@pithy-sh/cloudflare 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/dist/ai/aiManager.d.ts +99 -0
  2. package/dist/ai/aiManager.d.ts.map +1 -0
  3. package/dist/ai/aiManager.js +148 -0
  4. package/dist/ai/vectorizeManager.d.ts +67 -0
  5. package/dist/ai/vectorizeManager.d.ts.map +1 -0
  6. package/dist/ai/vectorizeManager.js +101 -0
  7. package/dist/ai/vectorizeProvisioner.d.ts +123 -0
  8. package/dist/ai/vectorizeProvisioner.d.ts.map +1 -0
  9. package/dist/ai/vectorizeProvisioner.js +195 -0
  10. package/dist/client/accounts.d.ts +48 -0
  11. package/dist/client/accounts.d.ts.map +1 -0
  12. package/dist/client/accounts.js +55 -0
  13. package/dist/client/clients.d.ts +122 -0
  14. package/dist/client/clients.d.ts.map +1 -0
  15. package/dist/client/clients.js +216 -0
  16. package/dist/client/errors.d.ts +73 -0
  17. package/dist/client/errors.d.ts.map +1 -0
  18. package/dist/client/errors.js +98 -0
  19. package/dist/client/manager.d.ts +45 -0
  20. package/dist/client/manager.d.ts.map +1 -0
  21. package/dist/client/manager.js +51 -0
  22. package/dist/d1/d1Manager.d.ts +74 -0
  23. package/dist/d1/d1Manager.d.ts.map +1 -0
  24. package/dist/d1/d1Manager.js +128 -0
  25. package/dist/d1/d1PreparedStatement.d.ts +24 -0
  26. package/dist/d1/d1PreparedStatement.d.ts.map +1 -0
  27. package/dist/d1/d1PreparedStatement.js +91 -0
  28. package/dist/d1/d1Provisioner.d.ts +28 -0
  29. package/dist/d1/d1Provisioner.d.ts.map +1 -0
  30. package/dist/d1/d1Provisioner.js +67 -0
  31. package/dist/email/emailRoutingManager.d.ts +36 -0
  32. package/dist/email/emailRoutingManager.d.ts.map +1 -0
  33. package/dist/email/emailRoutingManager.js +124 -0
  34. package/dist/email/emailSendManager.d.ts +45 -0
  35. package/dist/email/emailSendManager.d.ts.map +1 -0
  36. package/dist/email/emailSendManager.js +52 -0
  37. package/dist/env/devVars.d.ts +65 -0
  38. package/dist/env/devVars.d.ts.map +1 -0
  39. package/dist/env/devVars.js +84 -0
  40. package/dist/hostnames/customHostnamesManager.d.ts +51 -0
  41. package/dist/hostnames/customHostnamesManager.d.ts.map +1 -0
  42. package/dist/hostnames/customHostnamesManager.js +84 -0
  43. package/dist/kv/kvManager.d.ts +76 -0
  44. package/dist/kv/kvManager.d.ts.map +1 -0
  45. package/dist/kv/kvManager.js +135 -0
  46. package/dist/kv/kvProvisioner.d.ts +28 -0
  47. package/dist/kv/kvProvisioner.d.ts.map +1 -0
  48. package/dist/kv/kvProvisioner.js +71 -0
  49. package/dist/media/assetSeeder.d.ts +31 -0
  50. package/dist/media/assetSeeder.d.ts.map +1 -0
  51. package/dist/media/assetSeeder.js +70 -0
  52. package/dist/media/imageManager.d.ts +34 -0
  53. package/dist/media/imageManager.d.ts.map +1 -0
  54. package/dist/media/imageManager.js +103 -0
  55. package/dist/media/ownership.d.ts +40 -0
  56. package/dist/media/ownership.d.ts.map +1 -0
  57. package/dist/media/ownership.js +46 -0
  58. package/dist/media/streamManager.d.ts +83 -0
  59. package/dist/media/streamManager.d.ts.map +1 -0
  60. package/dist/media/streamManager.js +145 -0
  61. package/dist/queue/queueManager.d.ts +84 -0
  62. package/dist/queue/queueManager.d.ts.map +1 -0
  63. package/dist/queue/queueManager.js +114 -0
  64. package/dist/r2/r2Credentials.d.ts +12 -0
  65. package/dist/r2/r2Credentials.d.ts.map +1 -0
  66. package/dist/r2/r2Credentials.js +13 -0
  67. package/dist/r2/r2Manager.d.ts +226 -0
  68. package/dist/r2/r2Manager.d.ts.map +1 -0
  69. package/dist/r2/r2Manager.js +405 -0
  70. package/dist/r2/r2Provisioner.d.ts +39 -0
  71. package/dist/r2/r2Provisioner.d.ts.map +1 -0
  72. package/dist/r2/r2Provisioner.js +86 -0
  73. package/dist/secrets/secretsStoreManager.d.ts +82 -0
  74. package/dist/secrets/secretsStoreManager.d.ts.map +1 -0
  75. package/dist/secrets/secretsStoreManager.js +147 -0
  76. package/dist/secrets/secretsStores.d.ts +47 -0
  77. package/dist/secrets/secretsStores.d.ts.map +1 -0
  78. package/dist/secrets/secretsStores.js +65 -0
  79. package/dist/test-utils/emailRoutingRules.d.ts +39 -0
  80. package/dist/test-utils/emailRoutingRules.d.ts.map +1 -0
  81. package/dist/test-utils/emailRoutingRules.js +89 -0
  82. package/dist/test-utils/fixtureReportSetup.d.ts +25 -0
  83. package/dist/test-utils/fixtureReportSetup.d.ts.map +1 -0
  84. package/dist/test-utils/fixtureReportSetup.js +30 -0
  85. package/dist/test-utils/fixtures.d.ts +224 -0
  86. package/dist/test-utils/fixtures.d.ts.map +1 -0
  87. package/dist/test-utils/fixtures.js +277 -0
  88. package/dist/test-utils/harness.d.ts +192 -0
  89. package/dist/test-utils/harness.d.ts.map +1 -0
  90. package/dist/test-utils/harness.js +351 -0
  91. package/dist/test-utils/inboundRecorder.d.ts +91 -0
  92. package/dist/test-utils/inboundRecorder.d.ts.map +1 -0
  93. package/dist/test-utils/inboundRecorder.js +141 -0
  94. package/dist/test-utils/integrationSetup.d.ts +22 -0
  95. package/dist/test-utils/integrationSetup.d.ts.map +1 -0
  96. package/dist/test-utils/integrationSetup.js +37 -0
  97. package/dist/test-utils/reap.d.ts +112 -0
  98. package/dist/test-utils/reap.d.ts.map +1 -0
  99. package/dist/test-utils/reap.js +185 -0
  100. package/dist/tokens/accountTokensManager.d.ts +157 -0
  101. package/dist/tokens/accountTokensManager.d.ts.map +1 -0
  102. package/dist/tokens/accountTokensManager.js +280 -0
  103. package/dist/tokens/permissions.d.ts +36 -0
  104. package/dist/tokens/permissions.d.ts.map +1 -0
  105. package/dist/tokens/permissions.js +50 -0
  106. package/dist/tokens/profiles.d.ts +102 -0
  107. package/dist/tokens/profiles.d.ts.map +1 -0
  108. package/dist/tokens/profiles.js +137 -0
  109. package/dist/turnstile/turnstileManager.d.ts +75 -0
  110. package/dist/turnstile/turnstileManager.d.ts.map +1 -0
  111. package/dist/turnstile/turnstileManager.js +138 -0
  112. package/dist/user/userManager.d.ts +36 -0
  113. package/dist/user/userManager.d.ts.map +1 -0
  114. package/dist/user/userManager.js +58 -0
  115. package/dist/workers/buildsManager.d.ts +116 -0
  116. package/dist/workers/buildsManager.d.ts.map +1 -0
  117. package/dist/workers/buildsManager.js +237 -0
  118. package/dist/workers/buildsTypes.d.ts +102 -0
  119. package/dist/workers/buildsTypes.d.ts.map +1 -0
  120. package/dist/workers/buildsTypes.js +87 -0
  121. package/dist/workers/workersBuildEvent.d.ts +64 -0
  122. package/dist/workers/workersBuildEvent.d.ts.map +1 -0
  123. package/dist/workers/workersBuildEvent.js +47 -0
  124. package/dist/workers/workersManager.d.ts +136 -0
  125. package/dist/workers/workersManager.d.ts.map +1 -0
  126. package/dist/workers/workersManager.js +312 -0
  127. package/dist/workers/workersProvisioner.d.ts +80 -0
  128. package/dist/workers/workersProvisioner.d.ts.map +1 -0
  129. package/dist/workers/workersProvisioner.js +106 -0
  130. package/dist/workflows/stepFailure.d.ts +180 -0
  131. package/dist/workflows/stepFailure.d.ts.map +1 -0
  132. package/dist/workflows/stepFailure.js +227 -0
  133. package/dist/workflows/workflowsClient.d.ts +87 -0
  134. package/dist/workflows/workflowsClient.d.ts.map +1 -0
  135. package/dist/workflows/workflowsClient.js +156 -0
  136. package/dist/zones/zonesManager.d.ts +50 -0
  137. package/dist/zones/zonesManager.d.ts.map +1 -0
  138. package/dist/zones/zonesManager.js +74 -0
  139. package/package.json +18 -9
@@ -0,0 +1,195 @@
1
+ import { cloudflareRequest, decodeResponse, isNotFoundError } from "../client/errors.js";
2
+ import { CloudflareManager } from "../client/manager.js";
3
+ import { z } from "zod";
4
+ //#region src/ai/vectorizeProvisioner.ts
5
+ /** The distance metric an index scores nearest neighbors with. Fixed at creation — it cannot be changed later. */
6
+ const VectorizeMetric = z.enum([
7
+ "cosine",
8
+ "euclidean",
9
+ "dot-product"
10
+ ]).describe("The distance metric a Vectorize index scores nearest neighbors with. Fixed at index creation.");
11
+ /**
12
+ * A Cloudflare-managed embedding preset. Naming one lets Cloudflare pick the dimensions and metric that
13
+ * match the model, so an index and the model that fills it cannot drift apart.
14
+ */
15
+ const VectorizePreset = z.enum([
16
+ "@cf/baai/bge-small-en-v1.5",
17
+ "@cf/baai/bge-base-en-v1.5",
18
+ "@cf/baai/bge-large-en-v1.5",
19
+ "openai/text-embedding-ada-002",
20
+ "cohere/embed-multilingual-v2.0"
21
+ ]).describe("An embedding model preset Cloudflare maps to a fixed dimensions/metric pair at index creation.");
22
+ /** The shape of the vectors an index holds: how many components each carries, and how they are compared. */
23
+ const VectorizeIndexConfig = z.object({
24
+ dimensions: z.number().int().positive().describe("The number of components in every vector the index holds. Cloudflare allows 32 to 1536."),
25
+ metric: VectorizeMetric
26
+ }).describe("A Vectorize index's vector shape: its dimension count and the distance metric it scores with.");
27
+ /** A Vectorize index's identity and shape, decoded from the create/get/list responses. */
28
+ const VectorizeIndexInfo = z.object({
29
+ name: z.string().min(1).describe("The index name — Vectorize's sole address for an index (no separate uuid id)."),
30
+ config: VectorizeIndexConfig.describe("The dimensions and metric the index was created with."),
31
+ description: z.string().optional().describe("The operator-supplied description, when the index carries one.")
32
+ }).describe("A Cloudflare Vectorize index's identity and vector shape, as returned by the control-plane endpoints.");
33
+ /** The type of a metadata property an index can filter on. Chosen per property when the metadata index is created. */
34
+ const VectorizeMetadataIndexType = z.enum([
35
+ "string",
36
+ "number",
37
+ "boolean"
38
+ ]).describe("The value type of an indexed metadata property, which decides how filters compare against it.");
39
+ /**
40
+ * The type as the **list** endpoint reports it, normalized to the casing the **create** endpoint accepts.
41
+ *
42
+ * Live Cloudflare answers `metadataIndex.list` with `"String"` while `metadataIndex.create` takes `"string"`
43
+ * (verified against the account on 2026-07-28). Normalizing on decode is not cosmetic: every consumer compares
44
+ * a declared type against a live one — `@pithy-sh/vector`'s drift check does exactly that — and `"String" !==
45
+ * "string"` would report every correctly-provisioned index as mismatched, then send the operator to re-embed a
46
+ * corpus that was never wrong.
47
+ */
48
+ const ListedMetadataIndexType = z.string().transform((value) => value.toLowerCase()).pipe(VectorizeMetadataIndexType).describe("The value type of an indexed metadata property as the list endpoint reports it, lowercased.");
49
+ /** One indexed metadata property on a Vectorize index — the unit that makes a metadata filter possible. */
50
+ const VectorizeMetadataIndex = z.object({
51
+ propertyName: z.string().min(1).describe("The metadata property this index covers (e.g. `tenantId`)."),
52
+ indexType: ListedMetadataIndexType
53
+ }).describe("An indexed metadata property on a Vectorize index, as returned by the metadata-index list endpoint.");
54
+ /**
55
+ * Whether an error means "that index is not there".
56
+ *
57
+ * Vectorize answers a **deleted** index with `410 Gone` (`vectorize.index.deleted`), not `404` — verified live
58
+ * on 2026-07-28. For find-then-create provisioning the two are the same answer, and only one of them was being
59
+ * treated that way: `findIndexByName` threw on a deleted index, so `pithy vector provision` would fail on
60
+ * precisely the case it exists to repair — an index someone removed.
61
+ */
62
+ function isAbsentIndexError(error) {
63
+ if (isNotFoundError(error)) return true;
64
+ return typeof error === "object" && error !== null && error.status === 410;
65
+ }
66
+ /** Vectorize's error code for "that metadata index is not there" — verified live on 2026-07-28. */
67
+ const METADATA_INDEX_ABSENT_CODE = 40005;
68
+ /**
69
+ * Whether an error means "that metadata index is not there".
70
+ *
71
+ * A third spelling of absence, on top of the 404 and 410 {@link isAbsentIndexError} already covers.
72
+ * Deleting a metadata index that does not exist answers **`400` with error code 40005** — verified live
73
+ * on 2026-07-28 against `metadata index with name=tenantId does not exist`. So `deleteMetadataIndex`
74
+ * documented itself as idempotent while throwing on the one case idempotency is for, and teardown could
75
+ * not re-run.
76
+ *
77
+ * The check is deliberately narrow. A bare `status === 400` would swallow every malformed request this
78
+ * endpoint rejects — a bad property name, a missing account — and turn an author's mistake into a silent
79
+ * no-op. Matching the code as well keeps "absent" distinct from "wrong".
80
+ */
81
+ function isAbsentMetadataIndexError(error) {
82
+ if (isAbsentIndexError(error)) return true;
83
+ if (typeof error !== "object" || error === null) return false;
84
+ const { status, errors } = error;
85
+ if (status !== 400 || !Array.isArray(errors)) return false;
86
+ return errors.some((entry) => entry?.code === METADATA_INDEX_ABSENT_CODE);
87
+ }
88
+ /**
89
+ * Account-level Vectorize control plane: **create and delete indexes, and manage their metadata indexes**.
90
+ * `pithy vector provision` uses this to stand up a per-environment index and declare which metadata
91
+ * properties are filterable; teardown uses it to remove them. Unlike {@link CloudflareVectorizeManager},
92
+ * which operates *within* one already-provisioned index (insert/query/delete vectors), this manager is
93
+ * account-scoped and addresses indexes **by name** — Vectorize indexes carry no separate uuid id.
94
+ *
95
+ * Every mutation here is enqueued: Vectorize applies index and metadata-index changes asynchronously, so a
96
+ * successful call means "accepted", not "visible". Poll {@link listMetadataIndexes} or the data-plane
97
+ * `describe()` watermarks rather than writing and immediately reading back.
98
+ */
99
+ var CloudflareVectorizeProvisioner = class extends CloudflareManager {
100
+ getServiceType() {
101
+ return "Vectorize (control plane)";
102
+ }
103
+ /** Prove access by listing indexes — a read, never a destructive create/delete. Never throws. */
104
+ async validateServiceAccess() {
105
+ try {
106
+ await this.getClient().vectorize.indexes.list({ account_id: this.accountId });
107
+ return true;
108
+ } catch {
109
+ return false;
110
+ }
111
+ }
112
+ /** Create a Vectorize index by name with an explicit dimensions/metric pair or a Cloudflare preset. */
113
+ async createIndex(name, config, options = {}) {
114
+ const response = await cloudflareRequest(`create Vectorize index ${name}`, () => this.getClient().vectorize.indexes.create({
115
+ account_id: this.accountId,
116
+ name,
117
+ config,
118
+ ...options.description ? { description: options.description } : {}
119
+ }));
120
+ return decodeResponse(VectorizeIndexInfo, response, "Vectorize index create");
121
+ }
122
+ /** Delete a Vectorize index by name. Idempotent — a missing index is not an error, so teardown can re-run safely. */
123
+ async deleteIndex(name) {
124
+ await cloudflareRequest(`delete Vectorize index ${name}`, async () => {
125
+ try {
126
+ await this.getClient().vectorize.indexes.delete(name, { account_id: this.accountId });
127
+ } catch (error) {
128
+ if (isAbsentIndexError(error)) return;
129
+ throw error;
130
+ }
131
+ });
132
+ }
133
+ /** List every Vectorize index in the account — for prefix-scan reconcile teardown and drift checks. */
134
+ async listIndexes() {
135
+ return cloudflareRequest("list Vectorize indexes", async () => {
136
+ const raw = [];
137
+ for await (const index of this.getClient().vectorize.indexes.list({ account_id: this.accountId })) raw.push(index);
138
+ return decodeResponse(z.array(VectorizeIndexInfo), raw, "Vectorize index list");
139
+ });
140
+ }
141
+ /**
142
+ * Find an index by exact name, or `null` — for idempotent provisioning. Reads the one index rather than
143
+ * draining the account list, because a Vectorize index's name *is* its address (unlike a KV namespace,
144
+ * whose title is not addressable). A 404 — or a 410 for a deleted one — means absent, which is an answer,
145
+ * not a failure.
146
+ */
147
+ async findIndexByName(name) {
148
+ return cloudflareRequest(`find Vectorize index ${name}`, async () => {
149
+ try {
150
+ const response = await this.getClient().vectorize.indexes.get(name, { account_id: this.accountId });
151
+ return decodeResponse(VectorizeIndexInfo, response, "Vectorize index get");
152
+ } catch (error) {
153
+ if (isAbsentIndexError(error)) return null;
154
+ throw error;
155
+ }
156
+ });
157
+ }
158
+ /**
159
+ * Make a metadata property filterable. Cloudflare caps an index at 10 indexed properties, and only vectors
160
+ * written **after** the metadata index exists are covered — so declare these before the first insert.
161
+ * The change is enqueued asynchronously; the returned promise resolving means accepted, not live.
162
+ */
163
+ async createMetadataIndex(indexName, propertyName, indexType) {
164
+ await cloudflareRequest(`create Vectorize metadata index ${indexName}.${propertyName}`, () => this.getClient().vectorize.indexes.metadataIndex.create(indexName, {
165
+ account_id: this.accountId,
166
+ indexType,
167
+ propertyName
168
+ }));
169
+ }
170
+ /** List an index's indexed metadata properties — the read that proves a `createMetadataIndex` has landed. */
171
+ async listMetadataIndexes(indexName) {
172
+ const response = await cloudflareRequest(`list Vectorize metadata indexes for ${indexName}`, () => this.getClient().vectorize.indexes.metadataIndex.list(indexName, { account_id: this.accountId }));
173
+ return decodeResponse(z.array(VectorizeMetadataIndex), response?.metadataIndexes ?? [], "Vectorize metadata index list");
174
+ }
175
+ /**
176
+ * Stop filtering on a metadata property. Idempotent — a missing metadata index is not an error, matching
177
+ * {@link deleteIndex} so teardown can re-run safely. Absence here is `400`/40005, not `404`, which is why
178
+ * this uses {@link isAbsentMetadataIndexError} rather than the plain not-found check.
179
+ */
180
+ async deleteMetadataIndex(indexName, propertyName) {
181
+ await cloudflareRequest(`delete Vectorize metadata index ${indexName}.${propertyName}`, async () => {
182
+ try {
183
+ await this.getClient().vectorize.indexes.metadataIndex.delete(indexName, {
184
+ account_id: this.accountId,
185
+ propertyName
186
+ });
187
+ } catch (error) {
188
+ if (isAbsentMetadataIndexError(error)) return;
189
+ throw error;
190
+ }
191
+ });
192
+ }
193
+ };
194
+ //#endregion
195
+ export { CloudflareVectorizeProvisioner, VectorizeIndexConfig, VectorizeIndexInfo, VectorizeMetadataIndex, VectorizeMetadataIndexType, VectorizeMetric, VectorizePreset };
@@ -0,0 +1,48 @@
1
+ import { Cloudflare } from "cloudflare";
2
+ import { z } from "zod";
3
+ /**
4
+ * The accounts a bootstrap API token can see — `GET /accounts`.
5
+ *
6
+ * **The one Cloudflare read in this package that is not account-scoped**, which is why it is a function
7
+ * here rather than a manager: {@link CloudflareManager} takes an `accountId` in its config and refuses
8
+ * without one, and this is the call made *before* an account id exists. `pithy init` collects the token
9
+ * two prompts before it lists zones, and until now it took the account id on trust — pasted, unverified,
10
+ * and wrong often enough that "the token is for one account and the id is another" is a documented
11
+ * failure mode (`CLOUDFLARE_CREDENTIAL_KEYS`). Asking the token what it can see removes that class
12
+ * outright, and the account's own name is a better nickname than one invented at a prompt (#206).
13
+ *
14
+ * Read-only, and never required: a narrowly scoped token that cannot list accounts is a legitimate
15
+ * token, and every caller falls back to asking.
16
+ */
17
+ /** One Cloudflare account, as `GET /accounts` lists it. Two fields, because two is all anything here uses. */
18
+ export declare const CfAccount: z.ZodObject<{
19
+ id: z.ZodString;
20
+ name: z.ZodString;
21
+ }, z.core.$strip>;
22
+ export type CfAccount = z.output<typeof CfAccount>;
23
+ /** What {@link listCloudflareAccounts} needs: a token, and — for a test — somewhere other than Cloudflare. */
24
+ export interface ListCloudflareAccountsOptions {
25
+ /** The bootstrap API token. Never logged; it is only ever handed to the SDK. */
26
+ apiToken: string;
27
+ /**
28
+ * Seam: the raw account records. Defaults to the SDK's own paginated `accounts.list()`.
29
+ *
30
+ * A test passing this never reaches Cloudflare, which matters more here than anywhere else in the
31
+ * package: the default would list the operator's real accounts using whatever token their shell
32
+ * exports.
33
+ */
34
+ accounts?: (client: Cloudflare) => AsyncIterable<unknown>;
35
+ }
36
+ /**
37
+ * Every account this token can see, name-sorted so a picker is stable between runs.
38
+ *
39
+ * **Each record is validated, and one that does not parse is dropped rather than passed on.** This is a
40
+ * response from outside crossing into a value that becomes `cloudflare.accountId` in a repository and
41
+ * the account every later resource is created under; a half-record would put an `undefined` id into a
42
+ * config file and a `[object Object]` into an error message. The same rule `listZones` follows.
43
+ *
44
+ * Throws a `cloudflare/request_failed` when the account list could not be read at all — a token without
45
+ * the permission, or no network. Callers treat that as "no picker" and ask instead.
46
+ */
47
+ export declare function listCloudflareAccounts(options: ListCloudflareAccountsOptions): Promise<CfAccount[]>;
48
+ //# sourceMappingURL=accounts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../src/client/accounts.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;;;GAaG;AAEH,8GAA8G;AAC9G,eAAO,MAAM,SAAS;;;iBAS8C,CAAC;AACrE,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,SAAS,CAAC,CAAC;AASnD,8GAA8G;AAC9G,MAAM,WAAW,6BAA6B;IAC5C,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,aAAa,CAAC,OAAO,CAAC,CAAC;CAC3D;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAYzG"}
@@ -0,0 +1,55 @@
1
+ import { cloudflareRequest } from "./errors.js";
2
+ import { z } from "zod";
3
+ import { Cloudflare } from "cloudflare";
4
+ //#region src/client/accounts.ts
5
+ /**
6
+ * The accounts a bootstrap API token can see — `GET /accounts`.
7
+ *
8
+ * **The one Cloudflare read in this package that is not account-scoped**, which is why it is a function
9
+ * here rather than a manager: {@link CloudflareManager} takes an `accountId` in its config and refuses
10
+ * without one, and this is the call made *before* an account id exists. `pithy init` collects the token
11
+ * two prompts before it lists zones, and until now it took the account id on trust — pasted, unverified,
12
+ * and wrong often enough that "the token is for one account and the id is another" is a documented
13
+ * failure mode (`CLOUDFLARE_CREDENTIAL_KEYS`). Asking the token what it can see removes that class
14
+ * outright, and the account's own name is a better nickname than one invented at a prompt (#206).
15
+ *
16
+ * Read-only, and never required: a narrowly scoped token that cannot list accounts is a legitimate
17
+ * token, and every caller falls back to asking.
18
+ */
19
+ /** One Cloudflare account, as `GET /accounts` lists it. Two fields, because two is all anything here uses. */
20
+ const CfAccount = z.object({
21
+ id: z.string().describe("The account id every provisioned resource is created under, and the value a project pins."),
22
+ name: z.string().describe("The account's own name, as the operator set it — free text, e.g. `Leed, Inc.`, never a slug.")
23
+ }).describe("One Cloudflare account a bootstrap API token can see.");
24
+ /**
25
+ * More than this and a picker is not a picker. A token seeing hundreds of accounts is somebody's
26
+ * reseller credential, and walking every page of it to build a `select` helps nobody — the cap keeps one
27
+ * unusual token from turning `pithy init` into a paginated crawl.
28
+ */
29
+ const MAX_ACCOUNTS = 100;
30
+ /**
31
+ * Every account this token can see, name-sorted so a picker is stable between runs.
32
+ *
33
+ * **Each record is validated, and one that does not parse is dropped rather than passed on.** This is a
34
+ * response from outside crossing into a value that becomes `cloudflare.accountId` in a repository and
35
+ * the account every later resource is created under; a half-record would put an `undefined` id into a
36
+ * config file and a `[object Object]` into an error message. The same rule `listZones` follows.
37
+ *
38
+ * Throws a `cloudflare/request_failed` when the account list could not be read at all — a token without
39
+ * the permission, or no network. Callers treat that as "no picker" and ask instead.
40
+ */
41
+ async function listCloudflareAccounts(options) {
42
+ return cloudflareRequest("list accounts", async () => {
43
+ const client = new Cloudflare({ apiToken: options.apiToken });
44
+ const source = options.accounts ?? ((sdk) => sdk.accounts.list());
45
+ const accounts = [];
46
+ for await (const record of source(client)) {
47
+ const parsed = CfAccount.safeParse(record);
48
+ if (parsed.success) accounts.push(parsed.data);
49
+ if (accounts.length >= MAX_ACCOUNTS) break;
50
+ }
51
+ return accounts.sort((a, b) => a.name.localeCompare(b.name));
52
+ });
53
+ }
54
+ //#endregion
55
+ export { CfAccount, listCloudflareAccounts };
@@ -0,0 +1,122 @@
1
+ import { CloudflareAIManager } from "../ai/aiManager";
2
+ import { CloudflareVectorizeManager } from "../ai/vectorizeManager";
3
+ import { CloudflareVectorizeProvisioner } from "../ai/vectorizeProvisioner";
4
+ import { CloudflareD1Manager } from "../d1/d1Manager";
5
+ import { CloudflareD1Provisioner } from "../d1/d1Provisioner";
6
+ import { CloudflareEmailRoutingManager } from "../email/emailRoutingManager";
7
+ import { CloudflareEmailSendManager } from "../email/emailSendManager";
8
+ import { CloudflareCustomHostnamesManager } from "../hostnames/customHostnamesManager";
9
+ import { CloudflareKVManager } from "../kv/kvManager";
10
+ import { CloudflareKVProvisioner } from "../kv/kvProvisioner";
11
+ import { CloudflareImageManager } from "../media/imageManager";
12
+ import { CloudflareStreamManager } from "../media/streamManager";
13
+ import { CloudflareQueueManager } from "../queue/queueManager";
14
+ import type { R2Credentials } from "../r2/r2Credentials";
15
+ import { CloudflareR2Manager } from "../r2/r2Manager";
16
+ import { CloudflareR2Provisioner } from "../r2/r2Provisioner";
17
+ import { CloudflareSecretsStoreManager } from "../secrets/secretsStoreManager";
18
+ import { CloudflareSecretsStoresManager } from "../secrets/secretsStores";
19
+ import { CloudflareAccountTokensManager } from "../tokens/accountTokensManager";
20
+ import { CloudflareTurnstileManager } from "../turnstile/turnstileManager";
21
+ import { CloudflareUserManager } from "../user/userManager";
22
+ import { CloudflareBuildsManager } from "../workers/buildsManager";
23
+ import { CloudflareWorkersManager } from "../workers/workersManager";
24
+ import { WorkersProvisioner } from "../workers/workersProvisioner";
25
+ import { CloudflareZonesManager } from "../zones/zonesManager";
26
+ import type { CloudflareManagerConfig } from "./manager";
27
+ /**
28
+ * One configured entry point to every Cloudflare REST manager. This is a runtime aggregator — it
29
+ * constructs and memoizes managers from a single `{ apiToken, accountId }` config — **not** a
30
+ * re-export barrel (CLAUDE.md forbids those); the composition logic is the reason it exists.
31
+ *
32
+ * Resource-scoped managers (KV, D1, queues, Vectorize indexes, secret stores, hostname zones, R2
33
+ * buckets) are keyed by their resource id, so repeated calls for the same id return one shared
34
+ * instance. Account-scoped managers (AI, Images, Stream, Workers, Builds, Turnstile) are singletons.
35
+ */
36
+ export declare class CloudflareClients {
37
+ private readonly config;
38
+ private readonly kvByNamespace;
39
+ private readonly d1ByDatabase;
40
+ private readonly queueByName;
41
+ private readonly vectorizeByIndex;
42
+ private readonly secretsByStore;
43
+ private secretsStoresManager?;
44
+ private readonly hostnamesByZone;
45
+ private aiManager?;
46
+ private emailSendManager?;
47
+ private emailRoutingManager?;
48
+ private imageManager?;
49
+ private streamManager?;
50
+ private workersManager?;
51
+ private buildsManager?;
52
+ private turnstileManager?;
53
+ private accountTokensManager?;
54
+ private userManager?;
55
+ private zonesManager?;
56
+ private workersProvisioner?;
57
+ private d1ProvisionerManager?;
58
+ private kvProvisionerManager?;
59
+ private r2ProvisionerManager?;
60
+ private vectorizeProvisionerManager?;
61
+ constructor(config: CloudflareManagerConfig);
62
+ /** The KV manager for a namespace id. */
63
+ kv(namespaceId: string): CloudflareKVManager;
64
+ /** The D1 manager for a database id (also a Kysely `D1Dialect` database). */
65
+ d1(databaseId: string): CloudflareD1Manager;
66
+ /** The Queue manager for a queue name. */
67
+ queue(queueName: string): CloudflareQueueManager;
68
+ /** The Vectorize manager for an index name. */
69
+ vectorize(indexName: string): CloudflareVectorizeManager;
70
+ /** The Secrets Store manager for a store id. */
71
+ secrets(storeId: string): CloudflareSecretsStoreManager;
72
+ /**
73
+ * The **account-level** Secrets Store manager — which stores this account has.
74
+ *
75
+ * Separate from {@link secrets} because it is asked before any store id exists: `pithy add secrets`
76
+ * resolves the account's one store and records it, so nothing after that has to ask (#182).
77
+ */
78
+ secretsStores(): CloudflareSecretsStoresManager;
79
+ /** The Custom Hostnames manager for a zone id. */
80
+ hostnames(zoneId: string): CloudflareCustomHostnamesManager;
81
+ /**
82
+ * The R2 manager for a bucket. R2 needs S3 credentials beyond the API token, so they are supplied
83
+ * here per call — the manager is **not** memoized: caching by bucket name alone would hand back a
84
+ * stale instance after a credential rotation. Construction is cheap (the S3 client is lazy).
85
+ */
86
+ r2(bucket: R2Credentials & {
87
+ bucketName: string;
88
+ }): CloudflareR2Manager;
89
+ /** The account-scoped Workers AI manager. */
90
+ ai(): CloudflareAIManager;
91
+ /** The account-scoped Email Sending (REST) manager — out-of-Worker sends; in a Worker use the binding. */
92
+ email(): CloudflareEmailSendManager;
93
+ /** The Email Routing (inbound rules) manager — methods take a zone id. */
94
+ emailRouting(): CloudflareEmailRoutingManager;
95
+ /** The account-scoped Images manager. */
96
+ images(): CloudflareImageManager;
97
+ /** The account-scoped Stream manager. */
98
+ stream(): CloudflareStreamManager;
99
+ /** The account-scoped Workers (scripts/deployments) manager. */
100
+ workers(): CloudflareWorkersManager;
101
+ /** The account-scoped Workers Builds manager. */
102
+ builds(): CloudflareBuildsManager;
103
+ /** The account-scoped Turnstile manager. */
104
+ turnstile(): CloudflareTurnstileManager;
105
+ /** The Workers provisioner (orchestrates the Workers + Builds managers). */
106
+ provisioner(): WorkersProvisioner;
107
+ /** The account-scoped D1 control plane — create and delete databases. */
108
+ d1Provisioner(): CloudflareD1Provisioner;
109
+ /** The account-scoped KV control plane — create, find, list, and delete namespaces. */
110
+ kvProvisioner(): CloudflareKVProvisioner;
111
+ /** The account-scoped R2 control plane — create, find, list, and delete buckets. */
112
+ r2Provisioner(): CloudflareR2Provisioner;
113
+ /** The account-scoped Vectorize control plane — create, find, list, and delete indexes and their metadata indexes. */
114
+ vectorizeProvisioner(): CloudflareVectorizeProvisioner;
115
+ /** The account-scoped API-token control plane — mint, find, and delete account-owned tokens. */
116
+ accountTokens(): CloudflareAccountTokensManager;
117
+ /** The user/token identity reader — resolves the actor behind the calling API token (audit attribution). */
118
+ user(): CloudflareUserManager;
119
+ /** The account's zones, read-only — what a custom domain may attach to. Needs only `Zone:Read`. */
120
+ zones(): CloudflareZonesManager;
121
+ }
122
+ //# sourceMappingURL=clients.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clients.d.ts","sourceRoot":"","sources":["../../src/client/clients.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,216 @@
1
+ import { CloudflareAIManager } from "../ai/aiManager.js";
2
+ import { CloudflareVectorizeManager } from "../ai/vectorizeManager.js";
3
+ import { CloudflareVectorizeProvisioner } from "../ai/vectorizeProvisioner.js";
4
+ import { CloudflareD1Manager } from "../d1/d1Manager.js";
5
+ import { CloudflareD1Provisioner } from "../d1/d1Provisioner.js";
6
+ import { CloudflareEmailRoutingManager } from "../email/emailRoutingManager.js";
7
+ import { CloudflareEmailSendManager } from "../email/emailSendManager.js";
8
+ import { CloudflareCustomHostnamesManager } from "../hostnames/customHostnamesManager.js";
9
+ import { CloudflareKVManager } from "../kv/kvManager.js";
10
+ import { CloudflareKVProvisioner } from "../kv/kvProvisioner.js";
11
+ import { CloudflareImageManager } from "../media/imageManager.js";
12
+ import { CloudflareStreamManager } from "../media/streamManager.js";
13
+ import { CloudflareQueueManager } from "../queue/queueManager.js";
14
+ import { CloudflareR2Manager } from "../r2/r2Manager.js";
15
+ import { CloudflareR2Provisioner } from "../r2/r2Provisioner.js";
16
+ import { CloudflareSecretsStoreManager } from "../secrets/secretsStoreManager.js";
17
+ import { CloudflareSecretsStoresManager } from "../secrets/secretsStores.js";
18
+ import { CloudflareUserManager } from "../user/userManager.js";
19
+ import { CloudflareAccountTokensManager } from "../tokens/accountTokensManager.js";
20
+ import { CloudflareTurnstileManager } from "../turnstile/turnstileManager.js";
21
+ import { CloudflareBuildsManager } from "../workers/buildsManager.js";
22
+ import { CloudflareWorkersManager } from "../workers/workersManager.js";
23
+ import { WorkersProvisioner } from "../workers/workersProvisioner.js";
24
+ import { CloudflareZonesManager } from "../zones/zonesManager.js";
25
+ //#region src/client/clients.ts
26
+ /**
27
+ * One configured entry point to every Cloudflare REST manager. This is a runtime aggregator — it
28
+ * constructs and memoizes managers from a single `{ apiToken, accountId }` config — **not** a
29
+ * re-export barrel (CLAUDE.md forbids those); the composition logic is the reason it exists.
30
+ *
31
+ * Resource-scoped managers (KV, D1, queues, Vectorize indexes, secret stores, hostname zones, R2
32
+ * buckets) are keyed by their resource id, so repeated calls for the same id return one shared
33
+ * instance. Account-scoped managers (AI, Images, Stream, Workers, Builds, Turnstile) are singletons.
34
+ */
35
+ var CloudflareClients = class {
36
+ config;
37
+ kvByNamespace = /* @__PURE__ */ new Map();
38
+ d1ByDatabase = /* @__PURE__ */ new Map();
39
+ queueByName = /* @__PURE__ */ new Map();
40
+ vectorizeByIndex = /* @__PURE__ */ new Map();
41
+ secretsByStore = /* @__PURE__ */ new Map();
42
+ secretsStoresManager;
43
+ hostnamesByZone = /* @__PURE__ */ new Map();
44
+ aiManager;
45
+ emailSendManager;
46
+ emailRoutingManager;
47
+ imageManager;
48
+ streamManager;
49
+ workersManager;
50
+ buildsManager;
51
+ turnstileManager;
52
+ accountTokensManager;
53
+ userManager;
54
+ zonesManager;
55
+ workersProvisioner;
56
+ d1ProvisionerManager;
57
+ kvProvisionerManager;
58
+ r2ProvisionerManager;
59
+ vectorizeProvisionerManager;
60
+ constructor(config) {
61
+ this.config = config;
62
+ }
63
+ /** The KV manager for a namespace id. */
64
+ kv(namespaceId) {
65
+ return memo(this.kvByNamespace, namespaceId, () => new CloudflareKVManager({
66
+ ...this.config,
67
+ namespaceId
68
+ }));
69
+ }
70
+ /** The D1 manager for a database id (also a Kysely `D1Dialect` database). */
71
+ d1(databaseId) {
72
+ return memo(this.d1ByDatabase, databaseId, () => new CloudflareD1Manager({
73
+ ...this.config,
74
+ databaseId
75
+ }));
76
+ }
77
+ /** The Queue manager for a queue name. */
78
+ queue(queueName) {
79
+ return memo(this.queueByName, queueName, () => new CloudflareQueueManager({
80
+ ...this.config,
81
+ queueName
82
+ }));
83
+ }
84
+ /** The Vectorize manager for an index name. */
85
+ vectorize(indexName) {
86
+ return memo(this.vectorizeByIndex, indexName, () => new CloudflareVectorizeManager({
87
+ ...this.config,
88
+ indexName
89
+ }));
90
+ }
91
+ /** The Secrets Store manager for a store id. */
92
+ secrets(storeId) {
93
+ return memo(this.secretsByStore, storeId, () => new CloudflareSecretsStoreManager({
94
+ ...this.config,
95
+ storeId
96
+ }));
97
+ }
98
+ /**
99
+ * The **account-level** Secrets Store manager — which stores this account has.
100
+ *
101
+ * Separate from {@link secrets} because it is asked before any store id exists: `pithy add secrets`
102
+ * resolves the account's one store and records it, so nothing after that has to ask (#182).
103
+ */
104
+ secretsStores() {
105
+ this.secretsStoresManager ??= new CloudflareSecretsStoresManager(this.config);
106
+ return this.secretsStoresManager;
107
+ }
108
+ /** The Custom Hostnames manager for a zone id. */
109
+ hostnames(zoneId) {
110
+ return memo(this.hostnamesByZone, zoneId, () => new CloudflareCustomHostnamesManager({
111
+ ...this.config,
112
+ zoneId
113
+ }));
114
+ }
115
+ /**
116
+ * The R2 manager for a bucket. R2 needs S3 credentials beyond the API token, so they are supplied
117
+ * here per call — the manager is **not** memoized: caching by bucket name alone would hand back a
118
+ * stale instance after a credential rotation. Construction is cheap (the S3 client is lazy).
119
+ */
120
+ r2(bucket) {
121
+ return new CloudflareR2Manager({
122
+ ...this.config,
123
+ ...bucket
124
+ });
125
+ }
126
+ /** The account-scoped Workers AI manager. */
127
+ ai() {
128
+ this.aiManager ??= new CloudflareAIManager(this.config);
129
+ return this.aiManager;
130
+ }
131
+ /** The account-scoped Email Sending (REST) manager — out-of-Worker sends; in a Worker use the binding. */
132
+ email() {
133
+ this.emailSendManager ??= new CloudflareEmailSendManager(this.config);
134
+ return this.emailSendManager;
135
+ }
136
+ /** The Email Routing (inbound rules) manager — methods take a zone id. */
137
+ emailRouting() {
138
+ this.emailRoutingManager ??= new CloudflareEmailRoutingManager(this.config);
139
+ return this.emailRoutingManager;
140
+ }
141
+ /** The account-scoped Images manager. */
142
+ images() {
143
+ this.imageManager ??= new CloudflareImageManager(this.config);
144
+ return this.imageManager;
145
+ }
146
+ /** The account-scoped Stream manager. */
147
+ stream() {
148
+ this.streamManager ??= new CloudflareStreamManager(this.config);
149
+ return this.streamManager;
150
+ }
151
+ /** The account-scoped Workers (scripts/deployments) manager. */
152
+ workers() {
153
+ this.workersManager ??= new CloudflareWorkersManager(this.config);
154
+ return this.workersManager;
155
+ }
156
+ /** The account-scoped Workers Builds manager. */
157
+ builds() {
158
+ this.buildsManager ??= new CloudflareBuildsManager(this.config);
159
+ return this.buildsManager;
160
+ }
161
+ /** The account-scoped Turnstile manager. */
162
+ turnstile() {
163
+ this.turnstileManager ??= new CloudflareTurnstileManager(this.config);
164
+ return this.turnstileManager;
165
+ }
166
+ /** The Workers provisioner (orchestrates the Workers + Builds managers). */
167
+ provisioner() {
168
+ this.workersProvisioner ??= new WorkersProvisioner(this.config);
169
+ return this.workersProvisioner;
170
+ }
171
+ /** The account-scoped D1 control plane — create and delete databases. */
172
+ d1Provisioner() {
173
+ this.d1ProvisionerManager ??= new CloudflareD1Provisioner(this.config);
174
+ return this.d1ProvisionerManager;
175
+ }
176
+ /** The account-scoped KV control plane — create, find, list, and delete namespaces. */
177
+ kvProvisioner() {
178
+ this.kvProvisionerManager ??= new CloudflareKVProvisioner(this.config);
179
+ return this.kvProvisionerManager;
180
+ }
181
+ /** The account-scoped R2 control plane — create, find, list, and delete buckets. */
182
+ r2Provisioner() {
183
+ this.r2ProvisionerManager ??= new CloudflareR2Provisioner(this.config);
184
+ return this.r2ProvisionerManager;
185
+ }
186
+ /** The account-scoped Vectorize control plane — create, find, list, and delete indexes and their metadata indexes. */
187
+ vectorizeProvisioner() {
188
+ this.vectorizeProvisionerManager ??= new CloudflareVectorizeProvisioner(this.config);
189
+ return this.vectorizeProvisionerManager;
190
+ }
191
+ /** The account-scoped API-token control plane — mint, find, and delete account-owned tokens. */
192
+ accountTokens() {
193
+ this.accountTokensManager ??= new CloudflareAccountTokensManager(this.config);
194
+ return this.accountTokensManager;
195
+ }
196
+ /** The user/token identity reader — resolves the actor behind the calling API token (audit attribution). */
197
+ user() {
198
+ this.userManager ??= new CloudflareUserManager(this.config);
199
+ return this.userManager;
200
+ }
201
+ /** The account's zones, read-only — what a custom domain may attach to. Needs only `Zone:Read`. */
202
+ zones() {
203
+ this.zonesManager ??= new CloudflareZonesManager(this.config);
204
+ return this.zonesManager;
205
+ }
206
+ };
207
+ /** Return the cached value for `key`, constructing and storing it on first access. */
208
+ function memo(cache, key, make) {
209
+ const existing = cache.get(key);
210
+ if (existing !== void 0) return existing;
211
+ const created = make();
212
+ cache.set(key, created);
213
+ return created;
214
+ }
215
+ //#endregion
216
+ export { CloudflareClients };