cloudflare-client
Advanced tools
Comparing version 0.2.6 to 0.3.0
@@ -1,2 +0,2 @@ | ||
import { type Credentials, type DataResponse, type ListResponse } from "./fetch.js"; | ||
import { type Credentials } from "./fetch.js"; | ||
export declare type Zone = { | ||
@@ -21,2 +21,6 @@ /** | ||
/** | ||
* Field to order records by | ||
*/ | ||
order?: "type" | "name" | "content" | "ttl" | "proxied"; | ||
/** | ||
* Page number of paginated results | ||
@@ -30,3 +34,3 @@ * @default 1 | ||
*/ | ||
per_page?: number; | ||
perPage?: number; | ||
/** | ||
@@ -125,9 +129,6 @@ * DNS record content | ||
}; | ||
export declare type DnsRecordResponse = DataResponse<DnsRecord>; | ||
export declare type DnsRecordsResponse = ListResponse<DnsRecord>; | ||
export declare type DeleteDnsRecordResponse = { | ||
result: { | ||
id: string; | ||
} | null; | ||
}; | ||
/** | ||
* DNS Records for Zone | ||
* @see https://api.cloudflare.com/#dns-records-for-a-zone-properties | ||
*/ | ||
export declare function dnsRecords(options: Zone & Credentials): { | ||
@@ -138,13 +139,8 @@ /** | ||
*/ | ||
get: (id: string) => Promise<DnsRecordResponse>; | ||
get: (id: string) => Promise<DnsRecord>; | ||
/** | ||
* Find DNS Record | ||
* @see https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records | ||
*/ | ||
find: (params?: FindOptions | undefined) => Promise<DnsRecordResponse>; | ||
/** | ||
* List DNS Records | ||
* @see https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records | ||
*/ | ||
findMany: (params?: FindOptions | undefined) => Promise<DnsRecordsResponse>; | ||
find: (params?: FindOptions | undefined) => import("./fetch.js").Query<DnsRecord>; | ||
/** | ||
@@ -154,13 +150,13 @@ * Create DNS Record | ||
*/ | ||
create: (input: DnsRecordInput) => Promise<DnsRecordResponse>; | ||
create: (input: DnsRecordInput) => Promise<DnsRecord>; | ||
/** | ||
* Update DNS Record | ||
* Replace DNS Record | ||
* @see https://api.cloudflare.com/#dns-records-for-a-zone-update-dns-record | ||
*/ | ||
update: (id: string, input: DnsRecordInput) => Promise<DnsRecordResponse>; | ||
replace: (id: string, input: DnsRecordInput) => Promise<DnsRecord>; | ||
/** | ||
* Patch DNS Record | ||
* Update DNS Record | ||
* @see https://api.cloudflare.com/#dns-records-for-a-zone-patch-dns-record | ||
*/ | ||
patch: (id: string, input: DnsRecordInput) => Promise<DnsRecordResponse>; | ||
update: (id: string, input: DnsRecordInput) => Promise<DnsRecord>; | ||
/** | ||
@@ -170,4 +166,6 @@ * Delete DNS Record | ||
*/ | ||
delete: (id: string) => Promise<DeleteDnsRecordResponse>; | ||
delete: (id: string) => Promise<{ | ||
id: string; | ||
}>; | ||
}; | ||
export {}; |
/* SPDX-FileCopyrightText: 2022-present Kriasoft */ | ||
/* SPDX-License-Identifier: MIT */ | ||
import { baseUrl, createFetch, HttpMethod, } from "./fetch.js"; | ||
import { baseUrl, createFetch, HttpMethod } from "./fetch.js"; | ||
// #endregion | ||
/** | ||
* DNS Records for Zone | ||
* @see https://api.cloudflare.com/#dns-records-for-a-zone-properties | ||
*/ | ||
export function dnsRecords(options) { | ||
@@ -17,5 +21,5 @@ const { zoneId, ...credentials } = options; | ||
credentials, | ||
})).json(), | ||
})).response(), | ||
/** | ||
* Find DNS Record | ||
* List DNS Records | ||
* @see https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records | ||
@@ -26,17 +30,16 @@ */ | ||
url, | ||
searchParams: params, | ||
searchParams: { | ||
match: params?.match, | ||
name: params?.name, | ||
order: params?.order, | ||
page: params?.page, | ||
per_page: params?.perPage, | ||
content: params?.content, | ||
type: params?.type, | ||
proxied: params?.proxied, | ||
direction: params?.direction, | ||
}, | ||
credentials, | ||
single: true, | ||
})).json(), | ||
})).query(), | ||
/** | ||
* List DNS Records | ||
* @see https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records | ||
*/ | ||
findMany: createFetch((params) => ({ | ||
method: HttpMethod.GET, | ||
url, | ||
searchParams: params, | ||
credentials, | ||
})).json(), | ||
/** | ||
* Create DNS Record | ||
@@ -50,8 +53,8 @@ * @see https://api.cloudflare.com/#dns-records-for-a-zone-create-dns-record | ||
credentials, | ||
})).json(), | ||
})).response(), | ||
/** | ||
* Update DNS Record | ||
* Replace DNS Record | ||
* @see https://api.cloudflare.com/#dns-records-for-a-zone-update-dns-record | ||
*/ | ||
update: createFetch((id, input) => ({ | ||
replace: createFetch((id, input) => ({ | ||
method: HttpMethod.PUT, | ||
@@ -61,8 +64,8 @@ url: `${url}/${id}`, | ||
credentials, | ||
})).json(), | ||
})).response(), | ||
/** | ||
* Patch DNS Record | ||
* Update DNS Record | ||
* @see https://api.cloudflare.com/#dns-records-for-a-zone-patch-dns-record | ||
*/ | ||
patch: createFetch((id, input) => ({ | ||
update: createFetch((id, input) => ({ | ||
method: HttpMethod.PATCH, | ||
@@ -72,3 +75,3 @@ url: `${url}/${id}`, | ||
credentials, | ||
})).json(), | ||
})).response(), | ||
/** | ||
@@ -82,4 +85,4 @@ * Delete DNS Record | ||
credentials, | ||
})).json(), | ||
})).response(), | ||
}; | ||
} |
@@ -9,2 +9,5 @@ export declare enum HttpMethod { | ||
export declare type Credentials = { | ||
/** | ||
* Cloudflare API token | ||
*/ | ||
accessToken: string; | ||
@@ -30,3 +33,3 @@ } | { | ||
export declare type Message = { | ||
code: string; | ||
code: number; | ||
message: string; | ||
@@ -40,24 +43,23 @@ type?: string; | ||
} | ||
export interface DataResponse<T> extends Response { | ||
result: T | null; | ||
interface QueryResponse<T> extends AsyncIterable<T> { | ||
all: () => Promise<Array<T>>; | ||
first: () => Promise<T | undefined>; | ||
totalCount: number; | ||
} | ||
export interface ListResponse<T> extends DataResponse<T[]> { | ||
result_info: { | ||
count: number; | ||
page: number; | ||
per_page: number; | ||
total_count: number; | ||
total_pages: number; | ||
}; | ||
export interface Query<T> extends Promise<QueryResponse<T>>, Omit<QueryResponse<T>, "totalCount"> { | ||
all: () => Promise<Array<T>>; | ||
first: () => Promise<T | undefined>; | ||
} | ||
export interface CursorResponse<T> extends DataResponse<T[]> { | ||
result_info: { | ||
count: number; | ||
cursor: string; | ||
}; | ||
} | ||
export declare const baseUrl = "https://api.cloudflare.com/client/v4"; | ||
export declare function createFetch<I extends FetchInit>(init: I): { | ||
json: <R>() => (...args: Parameters<I>) => Promise<R>; | ||
response: <R>() => (...args: Parameters<I>) => Promise<R>; | ||
query: <R>() => (...args: Parameters<I>) => Query<R>; | ||
}; | ||
export declare class FetchError extends Error { | ||
readonly code: number; | ||
readonly errors: Message[]; | ||
readonly messages: Message[]; | ||
readonly response: globalThis.Response; | ||
constructor(data: Response, res: globalThis.Response); | ||
} | ||
export {}; |
@@ -15,28 +15,33 @@ /* SPDX-FileCopyrightText: 2022-present Kriasoft */ | ||
export function createFetch(init) { | ||
function createRequest(...args) { | ||
const options = init(...args); | ||
const { searchParams, credentials } = options; | ||
const url = new URL(options.url); | ||
// Append URL (search) arguments to the URL | ||
if (typeof searchParams === "object") { | ||
Object.keys(searchParams).forEach((key) => { | ||
if (searchParams[key] !== undefined) { | ||
url.searchParams.set(key, String(searchParams[key])); | ||
} | ||
}); | ||
} | ||
const req = new Request(url, { method: options.method }); | ||
const contentType = "contentType" in options ? options.contentType : "application/json"; | ||
if (contentType) { | ||
req.headers.set("Content-Type", contentType); | ||
} | ||
// Set authentication header(s) | ||
if ("accessToken" in credentials) { | ||
req.headers.set("Authorization", `Bearer ${credentials.accessToken}`); | ||
} | ||
else { | ||
req.headers.set("X-Auth-Key", credentials.authKey); | ||
req.headers.set("X-Auth-Email", credentials.authEmail); | ||
} | ||
return [req, options]; | ||
} | ||
return { | ||
json() { | ||
response() { | ||
return async function (...args) { | ||
const { searchParams, credentials, ...options } = init(...args); | ||
const url = new URL(options.url); | ||
// Append URL (search) arguments to the URL | ||
if (typeof searchParams === "object") { | ||
Object.keys(searchParams).forEach((key) => { | ||
if (searchParams[key] !== undefined) { | ||
url.searchParams.set(key, String(searchParams[key])); | ||
} | ||
}); | ||
} | ||
const req = new Request(url, { method: options.method }); | ||
const contentType = "contentType" in options ? options.contentType : "application/json"; | ||
if (contentType) { | ||
req.headers.set("Content-Type", contentType); | ||
} | ||
// Set authentication header(s) | ||
if ("accessToken" in credentials) { | ||
req.headers.set("Authorization", `Bearer ${credentials.accessToken}`); | ||
} | ||
else { | ||
req.headers.set("X-Auth-Key", credentials.authKey); | ||
req.headers.set("X-Auth-Email", credentials.authEmail); | ||
} | ||
const [req, options] = createRequest(...args); | ||
// Make an HTTP request | ||
@@ -49,11 +54,81 @@ const res = options.body | ||
} | ||
const data = options.type === "text" ? await res.text() : await res.json(); | ||
if (options.single && Array.isArray(data.result)) { | ||
data.result = data.result[0]; | ||
delete data.result_info; | ||
if (!res.ok) { | ||
const body = await res.json(); | ||
throw new FetchError(body, res); | ||
} | ||
return data; | ||
const data = options.type === "text" | ||
? await res.text() | ||
: options.type === "binary" | ||
? await res.arrayBuffer() | ||
: await res.json(); | ||
if (options.type) | ||
return data; | ||
if (data?.success === false) { | ||
throw new FetchError(data, res); | ||
} | ||
return data.result; | ||
}; | ||
}, | ||
query() { | ||
return function (...args) { | ||
const [req, options] = createRequest(...args); | ||
const promise = (async () => { | ||
// Make an HTTP request | ||
const res = options.body | ||
? await fetch(new Request(req, { body: options.body })) | ||
: await fetch(req); | ||
if (res.status === 404 && "notFoundResponse" in options) { | ||
return options.notFoundResponse; | ||
} | ||
const data = options.type === "text" ? await res.text() : await res.json(); | ||
if (options.single && Array.isArray(data.result)) { | ||
data.result = data.result[0]; | ||
delete data.result_info; | ||
} | ||
return data; | ||
})(); | ||
const result = { | ||
async *[Symbol.asyncIterator]() { | ||
const res = await promise; | ||
if (Array.isArray(res.result)) { | ||
for (const item of res.result) { | ||
yield item; | ||
} | ||
} | ||
}, | ||
async all() { | ||
const items = []; | ||
for await (const item of this) { | ||
items.push(item); | ||
} | ||
return items; | ||
}, | ||
async first() { | ||
for await (const item of this) { | ||
return item; | ||
} | ||
}, | ||
}; | ||
return Object.assign(promise.then(() => result), result); | ||
}; | ||
}, | ||
// TEMP | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
}; | ||
} | ||
export class FetchError extends Error { | ||
code; | ||
errors; | ||
messages; | ||
response; | ||
constructor(data, res) { | ||
super(data.errors[0]?.message ?? "HTTP request failed"); | ||
this.name = "FetchError"; | ||
this.code = data.errors[0]?.code ?? 0; | ||
this.errors = data.errors ?? []; | ||
this.messages = data.messages ?? []; | ||
this.response = res; | ||
// https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types | ||
Object.setPrototypeOf(this, FetchError.prototype); | ||
} | ||
} |
@@ -1,7 +0,6 @@ | ||
import { type Credentials, type CursorResponse, type DataResponse, type ListResponse, type Response } from "./fetch.js"; | ||
import { type Credentials } from "./fetch.js"; | ||
declare type Options = { | ||
accountId: string; | ||
namespaceId?: string; | ||
} & Credentials; | ||
declare type GetNamespacesOptions = { | ||
declare type FindOptions = { | ||
/** | ||
@@ -34,7 +33,14 @@ * Page number of paginated results | ||
}; | ||
export declare type NamespaceParams = { | ||
title: string; | ||
export declare type Key = { | ||
name: string; | ||
expiration: number; | ||
metadata: Record<string, string | number>; | ||
}; | ||
declare type GetKeys = { | ||
declare type KeysOptions = { | ||
/** | ||
* A string prefix used to filter down which keys will be returned. Exact | ||
* matches and any key names that begin with the prefix will be returned. | ||
*/ | ||
prefix?: string; | ||
/** | ||
* The number of keys to return. The cursor attribute may be used to iterate | ||
@@ -46,3 +52,3 @@ * over the next batch of keys if there are more than the limit. | ||
*/ | ||
limit?: number; | ||
first?: number; | ||
/** | ||
@@ -55,24 +61,22 @@ * Opaque token indicating the position from which to continue when requesting | ||
*/ | ||
cursor?: string; | ||
after?: string; | ||
}; | ||
declare type SetOptions = { | ||
expires?: number; | ||
expiresTtl?: number; | ||
/** | ||
* A string prefix used to filter down which keys will be returned. Exact | ||
* matches and any key names that begin with the prefix will be returned. | ||
* @default JSON.stringify | ||
*/ | ||
prefix?: string; | ||
encode?: ((value: any) => any) | false; | ||
/** | ||
* @default "text/plain; charset=utf-8" | ||
*/ | ||
contentType?: string; | ||
}; | ||
declare type Key = { | ||
name: string; | ||
expiration: number; | ||
metadata: Record<string, string | number>; | ||
}; | ||
declare type SetOptions = { | ||
expiration?: number; | ||
expiration_ttl?: number; | ||
json?: false; | ||
}; | ||
declare type GetOptions = { | ||
json?: false; | ||
/** | ||
* @default JSON.parse | ||
*/ | ||
decode?: ((value: ArrayBuffer) => any) | false; | ||
}; | ||
export declare type CreateNamespaceResponse = DataResponse<Namespace>; | ||
export declare type GetKeysResponse = CursorResponse<Key>; | ||
/** | ||
@@ -86,3 +90,3 @@ * Cloudflare KV Storage client | ||
*/ | ||
getNamespaces: (params?: GetNamespacesOptions | undefined) => Promise<ListResponse<Namespace>>; | ||
find: (params?: FindOptions | undefined) => import("./fetch.js").Query<Namespace>; | ||
/** | ||
@@ -92,3 +96,3 @@ * Create a Namespace | ||
*/ | ||
createNamespace: (title: string) => Promise<CreateNamespaceResponse>; | ||
create: (name: string) => Promise<Namespace>; | ||
/** | ||
@@ -98,3 +102,3 @@ * Rename a Namespace | ||
*/ | ||
updateNamespace: (id: string, title: string) => Promise<Response>; | ||
update: (id: string, title: string) => Promise<null>; | ||
/** | ||
@@ -104,24 +108,18 @@ * Remove a Namespace | ||
*/ | ||
deleteNamespace: (id: string) => Promise<Response>; | ||
/** | ||
* List a Namespace's Keys | ||
* @see https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys | ||
*/ | ||
getKeys: (params?: GetKeys | undefined) => Promise<GetKeysResponse>; | ||
/** | ||
* Read key-value pair | ||
* @see https://api.cloudflare.com/#workers-kv-namespace-read-key-value-pair | ||
*/ | ||
get: <T = any>(id: string, options?: GetOptions) => Promise<T>; | ||
/** | ||
* Write key-value pair | ||
* @see https://api.cloudflare.com/#workers-kv-namespace-write-key-value-pair | ||
*/ | ||
set: (id: string, value: any, options?: SetOptions | undefined) => Promise<Response>; | ||
/** | ||
* Delete key-value pair | ||
* @see https://api.cloudflare.com/#workers-kv-namespace-delete-key-value-pair | ||
*/ | ||
delete: (id: string) => Promise<Response>; | ||
delete: (id: string) => Promise<null>; | ||
namespace(id: string): { | ||
keys: (params?: KeysOptions | undefined) => import("./fetch.js").Query<Key>; | ||
/** | ||
* Read key-value pair | ||
* @see https://api.cloudflare.com/#workers-kv-namespace-read-key-value-pair | ||
*/ | ||
get: <T = any>(key: string, options?: GetOptions) => Promise<T>; | ||
/** | ||
* Write key-value pair | ||
* @see https://api.cloudflare.com/#workers-kv-namespace-write-key-value-pair | ||
*/ | ||
set: <T_1 = any>(key: string, value: T_1, options?: SetOptions) => Promise<undefined>; | ||
delete: (id: string) => Promise<void>; | ||
}; | ||
}; | ||
export {}; |
137
dist/kv.js
/* SPDX-FileCopyrightText: 2022-present Kriasoft */ | ||
/* SPDX-License-Identifier: MIT */ | ||
import { baseUrl, createFetch, HttpMethod, } from "./fetch.js"; | ||
import { baseUrl, createFetch, HttpMethod } from "./fetch.js"; | ||
// #endregion | ||
@@ -10,4 +10,16 @@ const { GET, PUT, POST, DELETE } = HttpMethod; | ||
export function kv(options) { | ||
const { accountId, namespaceId, ...credentials } = options; | ||
const { accountId, ...credentials } = options; | ||
const url = `${baseUrl}/accounts/${accountId}/storage/kv/namespaces`; | ||
if (!accountId) | ||
throw new TypeError(`options.accountId`); | ||
if ("accessToken" in credentials) { | ||
if (!credentials.accessToken) | ||
throw new TypeError(`options.accessToken`); | ||
} | ||
else { | ||
if (!credentials.authKey) | ||
throw new TypeError(`options.authKey`); | ||
if (!credentials.authEmail) | ||
throw new TypeError(`options.authEmail`); | ||
} | ||
return { | ||
@@ -18,3 +30,3 @@ /** | ||
*/ | ||
getNamespaces: createFetch((params) => ({ | ||
find: createFetch((params) => ({ | ||
method: GET, | ||
@@ -24,3 +36,3 @@ url, | ||
credentials, | ||
})).json(), | ||
})).query(), | ||
/** | ||
@@ -30,8 +42,8 @@ * Create a Namespace | ||
*/ | ||
createNamespace: createFetch((title) => ({ | ||
create: createFetch((name) => ({ | ||
method: POST, | ||
url, | ||
body: JSON.stringify({ title }), | ||
body: JSON.stringify({ title: name }), | ||
credentials, | ||
})).json(), | ||
})).response(), | ||
/** | ||
@@ -41,3 +53,3 @@ * Rename a Namespace | ||
*/ | ||
updateNamespace: createFetch((id, title) => ({ | ||
update: createFetch((id, title) => ({ | ||
method: PUT, | ||
@@ -47,3 +59,3 @@ url: `${url}/${id}`, | ||
credentials, | ||
})).json(), | ||
})).response(), | ||
/** | ||
@@ -53,54 +65,65 @@ * Remove a Namespace | ||
*/ | ||
deleteNamespace: createFetch((id) => ({ | ||
delete: createFetch((id) => ({ | ||
method: DELETE, | ||
url: `${url}/${id}`, | ||
credentials, | ||
})).json(), | ||
/** | ||
* List a Namespace's Keys | ||
* @see https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys | ||
*/ | ||
getKeys: createFetch((params) => ({ | ||
method: GET, | ||
url: `${url}/${namespaceId}/keys`, | ||
searchParams: { ...params, namespaceId: undefined }, | ||
credentials, | ||
})).json(), | ||
/** | ||
* Read key-value pair | ||
* @see https://api.cloudflare.com/#workers-kv-namespace-read-key-value-pair | ||
*/ | ||
get: createFetch((id, options) => ({ | ||
method: GET, | ||
url: `${url}/${namespaceId}/values/${id}`, | ||
credentials, | ||
type: options?.json === false ? "text" : undefined, | ||
notFoundResponse: null, | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
})).json(), | ||
/** | ||
* Write key-value pair | ||
* @see https://api.cloudflare.com/#workers-kv-namespace-write-key-value-pair | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
set: createFetch((id, value, options) => ({ | ||
method: PUT, | ||
url: `${url}/${namespaceId}/values/${id}`, | ||
searchParams: options, | ||
body: options?.json === false || | ||
(typeof options?.json === undefined && typeof value === "string") | ||
? value | ||
: JSON.stringify(value), | ||
credentials, | ||
})).json(), | ||
/** | ||
* Delete key-value pair | ||
* @see https://api.cloudflare.com/#workers-kv-namespace-delete-key-value-pair | ||
*/ | ||
delete: createFetch((id) => ({ | ||
method: DELETE, | ||
url: `${url}/${namespaceId}/values/${id}`, | ||
credentials, | ||
})).json(), | ||
})).response(), | ||
namespace(id) { | ||
const namespaceUrl = `${url}/${id}`; | ||
const namespaceId = id; | ||
return { | ||
keys: createFetch((params) => ({ | ||
method: GET, | ||
url: `${namespaceUrl}/keys`, | ||
searchParams: { | ||
prefix: params?.prefix, | ||
limit: params?.first, | ||
cursor: params?.after, | ||
}, | ||
credentials, | ||
})).query(), | ||
/** | ||
* Read key-value pair | ||
* @see https://api.cloudflare.com/#workers-kv-namespace-read-key-value-pair | ||
*/ | ||
get: createFetch((key, options) => ({ | ||
method: GET, | ||
url: `${url}/${namespaceId}/values/${key}`, | ||
credentials, | ||
type: options?.decode === false | ||
? "text" | ||
: options?.decode | ||
? "binary" | ||
: "json", | ||
notFoundResponse: undefined, | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
})).response(), | ||
/** | ||
* Write key-value pair | ||
* @see https://api.cloudflare.com/#workers-kv-namespace-write-key-value-pair | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
set: createFetch((key, value, options) => ({ | ||
method: PUT, | ||
url: `${namespaceUrl}/values/${key}`, | ||
searchParams: { | ||
expiration: options?.expires, | ||
expiration_ttl: options?.expiresTtl, | ||
}, | ||
body: options?.encode === false || | ||
(typeof options?.encode === undefined && typeof value === "string") | ||
? value | ||
: JSON.stringify(value), | ||
contentType: options?.contentType ?? "text/plain; charset=utf-8", | ||
credentials, | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
})).response(), | ||
delete: createFetch((id) => ({ | ||
method: DELETE, | ||
url: `${url}/${namespaceId}/values/${id}`, | ||
credentials, | ||
})).response(), | ||
}; | ||
}, | ||
}; | ||
} |
@@ -1,2 +0,2 @@ | ||
import { type Credentials, type DataResponse } from "./fetch.js"; | ||
import { type Credentials } from "./fetch.js"; | ||
export declare type User = { | ||
@@ -29,3 +29,2 @@ id: string; | ||
}; | ||
export declare type UserResponse = DataResponse<User>; | ||
/** | ||
@@ -40,3 +39,3 @@ * The currently logged in / authenticated User | ||
*/ | ||
get: () => Promise<UserResponse>; | ||
get: () => Promise<User>; | ||
}; |
/* SPDX-FileCopyrightText: 2022-present Kriasoft */ | ||
/* SPDX-License-Identifier: MIT */ | ||
import { baseUrl, createFetch, HttpMethod, } from "./fetch.js"; | ||
import { baseUrl, createFetch, HttpMethod } from "./fetch.js"; | ||
// #endregion | ||
@@ -20,4 +20,4 @@ /** | ||
credentials, | ||
})).json(), | ||
})).response(), | ||
}; | ||
} |
@@ -1,33 +0,99 @@ | ||
import { type Credentials, type DataResponse } from "./fetch.js"; | ||
export declare type VerifyResponse = DataResponse<{ | ||
id: string; | ||
status: "active" | string; | ||
not_before: string; | ||
expires_on: string; | ||
}>; | ||
import { type Credentials } from "./fetch.js"; | ||
export declare type Token = { | ||
id: string; | ||
name: string; | ||
status: "active" | string; | ||
issued_on: string; | ||
modified_on: string; | ||
not_before: string; | ||
expires_on: string; | ||
policies: { | ||
id: string; | ||
effect: "allow" | string; | ||
resources: string[]; | ||
permission_groups: { | ||
id: string; | ||
name: string; | ||
}; | ||
}[]; | ||
condition: { | ||
request_ip: { | ||
in: string[]; | ||
not_in: string[]; | ||
}; | ||
/** | ||
* Token identifier tag | ||
* @maximum 32 | ||
*/ | ||
readonly id: string; | ||
/** | ||
* Token name | ||
* @maximum 120 | ||
*/ | ||
readonly name: string; | ||
/** | ||
* Status of the token | ||
*/ | ||
readonly status: "active" | "disabled" | "expired"; | ||
/** | ||
* The time on which the token was created | ||
* @example "2018-07-01T05:20:00+00:00" | ||
*/ | ||
readonly issued_on: string; | ||
/** | ||
* @example "2018-07-01T05:20:00+00:00" | ||
*/ | ||
readonly last_used_on: string; | ||
/** | ||
* Last time the token was modified | ||
* @example "2018-07-02T05:20:00+00:00" | ||
*/ | ||
readonly modified_on: string; | ||
/** | ||
* The time before which the token MUST NOT be accepted for processing | ||
* @example "2018-07-01T05:20:00+00:00" | ||
*/ | ||
readonly not_before: string; | ||
/** | ||
* The expiration time on or after which the JWT MUST NOT be accepted for processing | ||
* @example "2020-01-01T00:00:00+00:00" | ||
*/ | ||
readonly expires_on: string; | ||
/** | ||
* List of access policies assigned to the token | ||
*/ | ||
readonly policies: TokenPolicy[]; | ||
readonly condition: TokenCondition; | ||
}; | ||
export declare type TokenPolicy = { | ||
/** | ||
* Policy identifier | ||
* @example "f267e341f3dd4697bd3b9f71dd96247f" | ||
*/ | ||
readonly id: string; | ||
/** | ||
* Allow or deny operations against the resources | ||
*/ | ||
readonly effect: "allow" | "deny"; | ||
/** | ||
* A list of resource names that the policy applies to | ||
* @example | ||
* { | ||
* "com.cloudflare.api.account.zone.eb78d65290b24279ba6f44721b3ea3c4": "*", | ||
* "com.cloudflare.api.account.zone.22b1de5f1c0e4b3ea97bb1e963b06a43": "*" | ||
* } | ||
*/ | ||
readonly resources: string[]; | ||
/** | ||
* A set of permission groups that are specified to the policy | ||
* @example | ||
* [ | ||
* { | ||
* "id": "c8fed203ed3043cba015a93ad1616f1f", | ||
* "name": "Zone Read" | ||
* }, | ||
* { | ||
* "id": "82e64a83756745bbbb1c9c2701bf816b", | ||
* "name": "DNS Read" | ||
* } | ||
* ] | ||
*/ | ||
readonly permission_groups: { | ||
/** | ||
* Identifier of the group | ||
* @example "6d7f2f5f5b1d4a0e9081fdc98d432fd1" | ||
*/ | ||
readonly id: string; | ||
/** | ||
* Name of the group | ||
* @example "Load Balancers Write" | ||
*/ | ||
readonly name: string; | ||
}; | ||
}; | ||
export declare type TokenResponse = DataResponse<Token>; | ||
declare type TokenCondition = { | ||
request_ip: { | ||
in: string[]; | ||
not_in: string[]; | ||
}; | ||
}; | ||
/** | ||
@@ -39,11 +105,13 @@ * User API Tokens. Tokens that cab be used to access Cloudflare v4 APIs. | ||
/** | ||
* Token Details | ||
* @see https://api.cloudflare.com/#user-api-tokens-token-details | ||
*/ | ||
get: (id: string) => Promise<Token>; | ||
/** | ||
* Verify Token | ||
* @see https://api.cloudflare.com/#user-api-tokens-verify-token | ||
* @throws {FetchError} | ||
*/ | ||
verify: () => Promise<VerifyResponse>; | ||
/** | ||
* Token Details | ||
* @see https://api.cloudflare.com/#user-api-tokens-token-details | ||
*/ | ||
get: (id: string) => Promise<TokenResponse>; | ||
verify: () => Promise<Pick<Token, "id" | "status">>; | ||
}; | ||
export {}; |
/* SPDX-FileCopyrightText: 2022-present Kriasoft */ | ||
/* SPDX-License-Identifier: MIT */ | ||
import { baseUrl, createFetch, HttpMethod, } from "./fetch.js"; | ||
import { baseUrl, createFetch, HttpMethod } from "./fetch.js"; | ||
// #endregion | ||
@@ -13,4 +13,14 @@ /** | ||
/** | ||
* Token Details | ||
* @see https://api.cloudflare.com/#user-api-tokens-token-details | ||
*/ | ||
get: createFetch((id) => ({ | ||
method: HttpMethod.GET, | ||
url: `${url}/${id}`, | ||
credentials, | ||
})).response(), | ||
/** | ||
* Verify Token | ||
* @see https://api.cloudflare.com/#user-api-tokens-verify-token | ||
* @throws {FetchError} | ||
*/ | ||
@@ -21,13 +31,4 @@ verify: createFetch(() => ({ | ||
credentials, | ||
})).json(), | ||
/** | ||
* Token Details | ||
* @see https://api.cloudflare.com/#user-api-tokens-token-details | ||
*/ | ||
get: createFetch((id) => ({ | ||
method: HttpMethod.GET, | ||
url: `${url}/${id}`, | ||
credentials, | ||
})).json(), | ||
})).response(), | ||
}; | ||
} |
{ | ||
"name": "cloudflare-client", | ||
"version": "0.2.6", | ||
"version": "0.3.0", | ||
"packageManager": "yarn@4.0.0-rc.9", | ||
@@ -5,0 +5,0 @@ "description": "Universal HTTP client for Cloudflare API", |
212
README.md
@@ -14,2 +14,4 @@ # Cloudflare API Client | ||
## Getting Started | ||
```bash | ||
@@ -23,46 +25,214 @@ # Install using NPM | ||
## Usage Example | ||
Once the library is installed, you can cherry pick and configure individual | ||
Cloudflare API endpoints that you need. If you bundle your code (e.g. with | ||
Rollup), only the selected modules will be included into the application bundle. | ||
```ts | ||
import * as cf from "cloudflare-client"; | ||
import * as Cloudflare from "cloudflare-client"; | ||
const settings = { zoneId: "xxx", accessToken: "<CLOUDFLARE_API_TOKEN>" }; | ||
// EXAMPLE 1: | ||
// Initialize an HTTP client for managing Cloudflare DNS | ||
// records using API token for authentication | ||
const dnsRecords = Cloudflare.dnsRecords({ | ||
zoneId: "<CLOUDFLARE_ZONE_ID>", | ||
accessToken: "<CLOUDFLARE_API_TOKEN>", | ||
}); | ||
// Get the currently logged in / authenticated user | ||
// EXAMPLE 2: | ||
// Initialize an HTTP client for managing Cloudflare Workers | ||
// KV store using API key and email for authentication | ||
const kv = Cloudflare.kv({ | ||
accountId: "<CLOUDFLARE_ZONE_ID>", | ||
authKey: "<CLOUDFLARE_AUTH_KEY>", | ||
authEmail: "<CLOUDFLARE_AUTH_EMAIL>", | ||
}); | ||
``` | ||
## User | ||
```ts | ||
// Initialize an HTTP client for the `user` API endpoint | ||
// using an API token for authentication | ||
const user = Cloudflare.user({ accessToken: "xxx" }); | ||
// Fetch the currently logged in / authenticated user details | ||
// https://api.cloudflare.com/#user-user-details | ||
await cf.user(settings).get(); | ||
const userDetails = await user.get(); | ||
// => { | ||
// id: "7c5dae5552338874e5053f2534d2767a", | ||
// email: "user@example.com", | ||
// ... | ||
// } | ||
``` | ||
## User Tokens | ||
```ts | ||
// Initialize an HTTP client for the `userTokens` API endpoint | ||
// using an API token for authentication | ||
const userTokens = Cloudflare.userTokens({ accessToken: "xxx" }); | ||
// Verify the user's token | ||
// https://api.cloudflare.com/#user-api-tokens-verify-token | ||
await cf.userTokens(settings).verify(); | ||
const token = await userTokens.verify(); | ||
// => { | ||
// id: "ed17574386854bf78a67040be0a770b0", | ||
// status: "active" | ||
// } | ||
``` | ||
// Find a single DNS Record matching the search parameters | ||
// https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records | ||
await cf.dnsRecords(settings).find({ type: "A" }); | ||
```ts | ||
// Initialize an HTTP client for the `userTokens` API endpoint | ||
// using an auth key and email | ||
const userTokens = Cloudflare.userTokens({ authKey: "xxx", authEmail: "xxx" }); | ||
// Get the list of DNS Records for the target zone | ||
// https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records | ||
await cf.dnsRecords(settings).findMany({ type: "A" }); | ||
// Get token details | ||
// https://api.cloudflare.com/#user-api-tokens-token-details | ||
const token = await userTokens.get("ed17574386854bf78a67040be0a770b0"); | ||
// => { | ||
// id: "ed17574386854bf78a67040be0a770b0", | ||
// name: "My Token", | ||
// status: "active", | ||
// policies: [...], | ||
// ... | ||
// } | ||
``` | ||
// Get DNS Record details | ||
## DNS Records | ||
```ts | ||
// Initialize an HTTP client for managing DNS records | ||
// within the target zone using API token for authentication | ||
const dnsRecords = Cloudflare.dnsRecords({ zoneId: "xxx", accessToken: "xxx" }); | ||
``` | ||
```ts | ||
// Find all DNS records of type "A" | ||
const records = await dnsRecords.find({ type: "A" }).all(); | ||
// Find the first DNS record with the specified name | ||
const record = await dnsRecords.find({ type: "A", name: "test" }).first(); | ||
// => { | ||
// id: "372e67954025e0ba6aaa6d586b9e0b59", | ||
// type: "A", | ||
// name: "test.example.com", | ||
// content: "192.0.2.1", | ||
// ... | ||
// } | ||
``` | ||
```ts | ||
// Fetch the list of DNS records and iterate through the result set using `for await` | ||
const records = await dnsRecords.find({ type: "A" }); | ||
for await (const record of records) { | ||
console.log(record); | ||
} | ||
``` | ||
```ts | ||
// Get a specific DNS record by its ID | ||
// https://api.cloudflare.com/#dns-records-for-a-zone-dns-record-details | ||
await cf.dnsRecords(settings).get("xxx"); | ||
const record = await dnsRecords.get("372e67954025e0ba6aaa6d586b9e0b59"); | ||
// Create DNS record | ||
// Create a new DNS record | ||
// https://api.cloudflare.com/#dns-records-for-a-zone-create-dns-record | ||
await cf.dnsRecords(settings).create({ type: "A", content: "127.0.0.1", ... }); | ||
const record = await dnsRecords.create({ | ||
type: "A", | ||
name: "test.example.com", | ||
content: "192.0.2.1", | ||
proxied: true, | ||
}); | ||
// Update DNS record | ||
// Replace DNS record | ||
// https://api.cloudflare.com/#dns-records-for-a-zone-update-dns-record | ||
await cf.dnsRecords(settings).update({ id: "xxx", content: "127.0.0.1", ... }); | ||
const record = await dnsRecords.replace("372e67954025e0ba6aaa6d586b9e0b59", { | ||
type: "A", | ||
name: "test.example.com", | ||
content: "192.0.2.1", | ||
proxied: true, | ||
}); | ||
// Patch DNS record | ||
// Update DNS record | ||
// https://api.cloudflare.com/#dns-records-for-a-zone-patch-dns-record | ||
await cf.dnsRecords(settings).patch({ id: "xxx", content: "127.0.0.1", ... }); | ||
const record = await dnsRecords.update("372e67954025e0ba6aaa6d586b9e0b59", { | ||
proxied: false, | ||
}); | ||
// Delete DNS record | ||
// https://api.cloudflare.com/#dns-records-for-a-zone-delete-dns-record | ||
await cf.dnsRecords(settings).delete(id); | ||
await dnsRecords.delete("372e67954025e0ba6aaa6d586b9e0b59"); | ||
``` | ||
## Workers KV | ||
```ts | ||
// Initialize an HTTP client for managing CF Workers KV store | ||
const kv = Cloudflare.kv({ | ||
accountId: "xxx", | ||
authKey: "xxx", | ||
authEmail: "xxx", | ||
}); | ||
``` | ||
#### KV Namespaces | ||
```ts | ||
// Fetch the list of all KV namespaces | ||
// https://api.cloudflare.com/#workers-kv-namespace-list-namespaces | ||
const namespaces = await kv.find().all(); | ||
// Create a new namespace named "Example" | ||
// https://api.cloudflare.com/#workers-kv-namespace-create-a-namespace | ||
const ns = await kv.create("Example"); | ||
// => { | ||
// id: "0f2ac74b498b48028cb68387c421e279", | ||
// title: "Example", | ||
// supports_url_encoding: true | ||
// } | ||
// Update/rename a namespace | ||
// https://api.cloudflare.com/#workers-kv-namespace-rename-a-namespace | ||
await kv.update("0f2ac74b498b48028cb68387c421e279", "New Name"); | ||
// Delete a namespace | ||
// https://api.cloudflare.com/#workers-kv-namespace-remove-a-namespace | ||
await kv.delete("0f2ac74b498b48028cb68387c421e279"); | ||
``` | ||
#### Key-Value Pairs | ||
```ts | ||
// Initialize the API endpoint client for managing key-value pairs | ||
const ns = kv.namespace("0f2ac74b498b48028cb68387c421e279"); | ||
// Fetch the list of all the keys | ||
const keys = await ns.keys().all(); | ||
// Fetch the list of all the keys prefixed "example" | ||
const keys = await ns.keys({ prefix: "example" }).all(); | ||
// Create or update a key-value pair in Cloudflare KV store | ||
// using JSON encoding by default (`JSON.stringify(value)`). | ||
await ns.set("key", { some: "value" }); | ||
// Read key-pair value from Cloudflare KV store | ||
const value = await ns.get("key"); | ||
// => { | ||
// some: "name" | ||
// } | ||
// Delete a key-pair | ||
await ns.delete("key"); | ||
``` | ||
```ts | ||
// Save a key-value pair as plain text (as opposed to JSON-serialized) | ||
await ns.set("όνομα", "José", { encode: false }); | ||
// Read a key-value pair as plain text | ||
const value = await ns.get("όνομα", { decode: false }); | ||
// => "José" | ||
``` | ||
## Source Code | ||
@@ -69,0 +239,0 @@ |
42242
900
278
4