@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,73 @@
1
+ import { PithyError } from "@pithy-sh/core/src/error/pithyError";
2
+ import type { MessageParams } from "@pithy-sh/core/src/i18n/catalog";
3
+ import type { z } from "zod";
4
+ /**
5
+ * Cloudflare REST client throw sugar. The `cloudflare/*` codes live in core's closed
6
+ * `KitErrorPayload` union (CLAUDE.md §Errors: capabilities add their codes to the one union); these
7
+ * subclasses are the package-local vehicles that set one of those members — the same pattern as
8
+ * core's `NotFoundError`/`InternalError`, just owned here. Runtime code in this package throws
9
+ * one of these, never a plain `new Error`.
10
+ */
11
+ /** Variable parts each subclass accepts; `code`/`status` are fixed by the subclass. */
12
+ interface CloudflareErrorArgs {
13
+ /** Override the public, safe-to-expose message. */
14
+ message?: string;
15
+ /** A remediation hint (CLI action line). */
16
+ action?: string;
17
+ /** Internal context for logs + audit. Never serialized to clients. */
18
+ detail?: string;
19
+ /**
20
+ * Values a translating client interpolates into its own wording for this code. Client-facing, so —
21
+ * unlike `action` and `detail` — these cross the boundary with `message`.
22
+ */
23
+ params?: MessageParams;
24
+ }
25
+ /** A required piece of configuration (token, account id, resource id) is missing. */
26
+ export declare class CloudflareNotConfiguredError extends PithyError {
27
+ constructor(args?: CloudflareErrorArgs, options?: {
28
+ cause?: unknown;
29
+ });
30
+ }
31
+ /** A call to the Cloudflare REST API failed (network error, 4xx/5xx from the API). */
32
+ export declare class CloudflareRequestError extends PithyError {
33
+ constructor(args?: CloudflareErrorArgs, options?: {
34
+ cause?: unknown;
35
+ });
36
+ }
37
+ /** A Cloudflare REST API response did not match its expected shape (failed Zod validation). */
38
+ export declare class CloudflareInvalidResponseError extends PithyError {
39
+ constructor(args?: CloudflareErrorArgs, options?: {
40
+ cause?: unknown;
41
+ });
42
+ }
43
+ /**
44
+ * Run a Cloudflare SDK call and turn any failure into a `CloudflareRequestError`, preserving the
45
+ * original error as `cause` and its message as internal `detail`. `operation` names the call for
46
+ * the audit trail (`"KV get for key 'x'"`). A `PithyError` already in flight (e.g. a
47
+ * not-configured guard) passes through untouched — only foreign throws get wrapped.
48
+ */
49
+ export declare function cloudflareRequest<T>(operation: string, fn: () => Promise<T>): Promise<T>;
50
+ /** The raw message of an unknown throw, for use as a PithyError `detail`. One source of truth. */
51
+ export declare function messageOf(error: unknown): string;
52
+ /**
53
+ * The most specific failure reason available, for a partial-failure report's `error` field: a
54
+ * `PithyError`'s internal `detail` (the real cause `cloudflareRequest` captured), else its public
55
+ * message, else the raw throw. Centralized so the detail-vs-message precedence lives in one place.
56
+ */
57
+ export declare function reasonOf(error: unknown): string;
58
+ /** Whether a thrown SDK error is an HTTP 404 — the SDK throws on a missing resource, not null. */
59
+ export declare function isNotFoundError(error: unknown): boolean;
60
+ /**
61
+ * Whether a thrown SDK error is an HTTP 403 — the token reached the API but lacks the permission for
62
+ * the operation. The signal a caller uses to turn a raw "Unauthorized" into an actionable
63
+ * "grant this permission group" message instead of a generic request failure.
64
+ */
65
+ export declare function isAuthorizationError(error: unknown): boolean;
66
+ /**
67
+ * Decode a Cloudflare response against `schema`, throwing `cloudflare/invalid_response` on a shape
68
+ * mismatch. The decode counterpart to `cloudflareRequest`'s error wrapping — one seam so every
69
+ * manager validates the wire the same way instead of hand-rolling `safeParse` + throw.
70
+ */
71
+ export declare function decodeResponse<T extends z.ZodType>(schema: T, raw: unknown, context: string): z.output<T>;
72
+ export {};
73
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/client/errors.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B;;;;;;GAMG;AAEH,uFAAuF;AACvF,UAAU,mBAAmB;IAC3B,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,qFAAqF;AACrF,qBAAa,4BAA6B,SAAQ,UAAU;IAC1D,YAAY,IAAI,GAAE,mBAAwB,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAYxE;CACF;AAED,sFAAsF;AACtF,qBAAa,sBAAuB,SAAQ,UAAU;IACpD,YAAY,IAAI,GAAE,mBAAwB,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAYxE;CACF;AAED,+FAA+F;AAC/F,qBAAa,8BAA+B,SAAQ,UAAU;IAC5D,YAAY,IAAI,GAAE,mBAAwB,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAYxE;CACF;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAU9F;AAED,kGAAkG;AAClG,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAG/C;AAED,kGAAkG;AAClG,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CASzG"}
@@ -0,0 +1,98 @@
1
+ import { PithyError } from "@pithy-sh/core/src/error/pithyError";
2
+ //#region src/client/errors.ts
3
+ /** A required piece of configuration (token, account id, resource id) is missing. */
4
+ var CloudflareNotConfiguredError = class extends PithyError {
5
+ constructor(args = {}, options) {
6
+ super({
7
+ code: "cloudflare/not_configured",
8
+ status: 500,
9
+ message: args.message ?? "The Cloudflare REST client is not fully configured.",
10
+ action: args.action ?? "Provide apiToken, accountId, and any required resource id.",
11
+ detail: args.detail,
12
+ params: args.params
13
+ }, options);
14
+ }
15
+ };
16
+ /** A call to the Cloudflare REST API failed (network error, 4xx/5xx from the API). */
17
+ var CloudflareRequestError = class extends PithyError {
18
+ constructor(args = {}, options) {
19
+ super({
20
+ code: "cloudflare/request_failed",
21
+ status: 502,
22
+ message: args.message ?? "A Cloudflare REST API call failed.",
23
+ action: args.action,
24
+ detail: args.detail,
25
+ params: args.params
26
+ }, options);
27
+ }
28
+ };
29
+ /** A Cloudflare REST API response did not match its expected shape (failed Zod validation). */
30
+ var CloudflareInvalidResponseError = class extends PithyError {
31
+ constructor(args = {}, options) {
32
+ super({
33
+ code: "cloudflare/invalid_response",
34
+ status: 502,
35
+ message: args.message ?? "A Cloudflare REST API response had an unexpected shape.",
36
+ action: args.action,
37
+ detail: args.detail,
38
+ params: args.params
39
+ }, options);
40
+ }
41
+ };
42
+ /**
43
+ * Run a Cloudflare SDK call and turn any failure into a `CloudflareRequestError`, preserving the
44
+ * original error as `cause` and its message as internal `detail`. `operation` names the call for
45
+ * the audit trail (`"KV get for key 'x'"`). A `PithyError` already in flight (e.g. a
46
+ * not-configured guard) passes through untouched — only foreign throws get wrapped.
47
+ */
48
+ async function cloudflareRequest(operation, fn) {
49
+ try {
50
+ return await fn();
51
+ } catch (error) {
52
+ if (error instanceof PithyError) throw error;
53
+ throw new CloudflareRequestError({
54
+ message: `Cloudflare request failed: ${operation}.`,
55
+ detail: messageOf(error)
56
+ }, { cause: error });
57
+ }
58
+ }
59
+ /** The raw message of an unknown throw, for use as a PithyError `detail`. One source of truth. */
60
+ function messageOf(error) {
61
+ return error instanceof Error ? error.message : String(error);
62
+ }
63
+ /**
64
+ * The most specific failure reason available, for a partial-failure report's `error` field: a
65
+ * `PithyError`'s internal `detail` (the real cause `cloudflareRequest` captured), else its public
66
+ * message, else the raw throw. Centralized so the detail-vs-message precedence lives in one place.
67
+ */
68
+ function reasonOf(error) {
69
+ if (error instanceof PithyError) return error.payload.detail ?? error.payload.message;
70
+ return messageOf(error);
71
+ }
72
+ /** Whether a thrown SDK error is an HTTP 404 — the SDK throws on a missing resource, not null. */
73
+ function isNotFoundError(error) {
74
+ return typeof error === "object" && error !== null && error.status === 404;
75
+ }
76
+ /**
77
+ * Whether a thrown SDK error is an HTTP 403 — the token reached the API but lacks the permission for
78
+ * the operation. The signal a caller uses to turn a raw "Unauthorized" into an actionable
79
+ * "grant this permission group" message instead of a generic request failure.
80
+ */
81
+ function isAuthorizationError(error) {
82
+ return typeof error === "object" && error !== null && error.status === 403;
83
+ }
84
+ /**
85
+ * Decode a Cloudflare response against `schema`, throwing `cloudflare/invalid_response` on a shape
86
+ * mismatch. The decode counterpart to `cloudflareRequest`'s error wrapping — one seam so every
87
+ * manager validates the wire the same way instead of hand-rolling `safeParse` + throw.
88
+ */
89
+ function decodeResponse(schema, raw, context) {
90
+ const parsed = schema.safeParse(raw);
91
+ if (!parsed.success) throw new CloudflareInvalidResponseError({
92
+ message: `A Cloudflare response had an unexpected shape: ${context}.`,
93
+ detail: parsed.error.message
94
+ });
95
+ return parsed.data;
96
+ }
97
+ //#endregion
98
+ export { CloudflareInvalidResponseError, CloudflareNotConfiguredError, CloudflareRequestError, cloudflareRequest, decodeResponse, isAuthorizationError, isNotFoundError, messageOf, reasonOf };
@@ -0,0 +1,45 @@
1
+ import { Cloudflare } from "cloudflare";
2
+ import type { Account } from "cloudflare/resources/accounts/accounts";
3
+ /**
4
+ * Configuration every manager shares. This is the whole surface: a scoped CF API token and the
5
+ * account it targets. The CMS managers also accepted Worker bindings (dual REST/binding mode);
6
+ * `@pithy-sh/cloudflare` is the *out-of-Worker* client (CLI, CI, provisioning), so it is REST-only
7
+ * — inside a Worker you use the binding directly (CLAUDE.md §Cloudflare access). One token, one
8
+ * account, no env coupling.
9
+ */
10
+ export interface CloudflareManagerConfig {
11
+ /** A scoped, least-privilege CF API token (from `CLOUDFLARE_API_TOKEN`, minted per environment). */
12
+ apiToken: string;
13
+ /** The Cloudflare account id all operations target (`CLOUDFLARE_ACCOUNT_ID`). */
14
+ accountId: string;
15
+ }
16
+ /**
17
+ * Base for every Cloudflare resource manager. Holds the configured SDK client and account id, and
18
+ * carries the account-level operations shared across resources. Subclasses add one resource's REST
19
+ * operations and declare what they are (`getServiceType`) and how to prove access
20
+ * (`validateServiceAccess`).
21
+ */
22
+ export declare abstract class CloudflareManager {
23
+ private readonly client;
24
+ private readonly apiToken;
25
+ protected readonly accountId: string;
26
+ constructor(config: CloudflareManagerConfig);
27
+ /** The configured SDK client. Subclasses route typed calls through this. */
28
+ protected getClient(): Cloudflare;
29
+ /**
30
+ * The raw API token, for subclasses that fall back to direct `fetch()` for endpoints the typed
31
+ * SDK does not cover (e.g. Workers Builds, event subscriptions). The documented escape hatch.
32
+ */
33
+ protected getApiToken(): string;
34
+ /** The account id all operations target. */
35
+ protected getAccountId(): string;
36
+ /** Fetch the full account record. Wraps SDK failures as `cloudflare/request_failed`. */
37
+ getAccountInfo(): Promise<Account>;
38
+ /** Whether the credentials work, proved by a lightweight account read. Never throws. */
39
+ validateCredentials(): Promise<boolean>;
40
+ /** A human label for the resource this manager owns (e.g. "KV Storage"). */
41
+ abstract getServiceType(): string;
42
+ /** Prove this manager can reach its specific resource. Never throws; returns false on failure. */
43
+ abstract validateServiceAccess(): Promise<boolean>;
44
+ }
45
+ //# sourceMappingURL=manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/client/manager.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wCAAwC,CAAC;AAGtE;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC,oGAAoG;IACpG,QAAQ,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,8BAAsB,iBAAiB;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IAEpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAErC,YAAY,MAAM,EAAE,uBAAuB,EAU1C;IAED,4EAA4E;IAC5E,SAAS,CAAC,SAAS,IAAI,UAAU,CAEhC;IAED;;;OAGG;IACH,SAAS,CAAC,WAAW,IAAI,MAAM,CAE9B;IAED,4CAA4C;IAC5C,SAAS,CAAC,YAAY,IAAI,MAAM,CAE/B;IAED,wFAAwF;IAClF,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAEvC;IAED,wFAAwF;IAClF,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO5C;IAED,4EAA4E;IAC5E,QAAQ,CAAC,cAAc,IAAI,MAAM,CAAC;IAElC,kGAAkG;IAClG,QAAQ,CAAC,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACpD"}
@@ -0,0 +1,51 @@
1
+ import { CloudflareNotConfiguredError, cloudflareRequest } from "./errors.js";
2
+ import { Cloudflare } from "cloudflare";
3
+ //#region src/client/manager.ts
4
+ /**
5
+ * Base for every Cloudflare resource manager. Holds the configured SDK client and account id, and
6
+ * carries the account-level operations shared across resources. Subclasses add one resource's REST
7
+ * operations and declare what they are (`getServiceType`) and how to prove access
8
+ * (`validateServiceAccess`).
9
+ */
10
+ var CloudflareManager = class {
11
+ client;
12
+ apiToken;
13
+ accountId;
14
+ constructor(config) {
15
+ if (!config.apiToken) throw new CloudflareNotConfiguredError({ detail: "Missing apiToken in CloudflareManagerConfig." });
16
+ if (!config.accountId) throw new CloudflareNotConfiguredError({ detail: "Missing accountId in CloudflareManagerConfig." });
17
+ this.client = new Cloudflare({ apiToken: config.apiToken });
18
+ this.apiToken = config.apiToken;
19
+ this.accountId = config.accountId;
20
+ }
21
+ /** The configured SDK client. Subclasses route typed calls through this. */
22
+ getClient() {
23
+ return this.client;
24
+ }
25
+ /**
26
+ * The raw API token, for subclasses that fall back to direct `fetch()` for endpoints the typed
27
+ * SDK does not cover (e.g. Workers Builds, event subscriptions). The documented escape hatch.
28
+ */
29
+ getApiToken() {
30
+ return this.apiToken;
31
+ }
32
+ /** The account id all operations target. */
33
+ getAccountId() {
34
+ return this.accountId;
35
+ }
36
+ /** Fetch the full account record. Wraps SDK failures as `cloudflare/request_failed`. */
37
+ async getAccountInfo() {
38
+ return cloudflareRequest("get account info", () => this.getClient().accounts.get({ account_id: this.accountId }));
39
+ }
40
+ /** Whether the credentials work, proved by a lightweight account read. Never throws. */
41
+ async validateCredentials() {
42
+ try {
43
+ await this.getAccountInfo();
44
+ return true;
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+ };
50
+ //#endregion
51
+ export { CloudflareManager };
@@ -0,0 +1,74 @@
1
+ import type { D1Database, D1PreparedStatement, D1Result } from "@cloudflare/workers-types";
2
+ import type { D1 } from "cloudflare/resources/d1/d1";
3
+ import type { QueryResult } from "cloudflare/resources/d1/database";
4
+ import { CloudflareManager, type CloudflareManagerConfig } from "../client/manager";
5
+ /** Config for the D1 manager: the shared client config plus the database it targets. */
6
+ export interface D1ManagerConfig extends CloudflareManagerConfig {
7
+ /** The D1 database id (the REST API addresses databases by id). */
8
+ databaseId: string;
9
+ }
10
+ /** The per-query outcome of `batchQueries`: a partial-failure report, never a thrown batch. */
11
+ export interface D1BatchResult {
12
+ /** The SQL this result is for. */
13
+ query: string;
14
+ /** Whether the query returned at least one result set. */
15
+ success: boolean;
16
+ /** The result sets, present only on success. */
17
+ result?: QueryResult[];
18
+ /** The failure reason, present only when `success` is false. */
19
+ error?: string;
20
+ }
21
+ /**
22
+ * Out-of-Worker D1 access over the REST API. It `implements D1Database`, so the same query builder
23
+ * a Worker runs against a binding runs from a CLI/CI context against D1 over REST — most
24
+ * importantly as the database behind a Kysely `D1Dialect` (kysely-d1), which is how `pithy migrate`
25
+ * promotes/rolls back schema remotely (issue #31). Inside a Worker you use the `D1Database` binding
26
+ * directly; this is the REST counterpart, addressed by database id.
27
+ */
28
+ export declare class CloudflareD1Manager extends CloudflareManager implements D1Database {
29
+ private readonly databaseId;
30
+ constructor(config: D1ManagerConfig);
31
+ prepare(query: string): D1PreparedStatement;
32
+ /**
33
+ * Run each statement and collect its result. **Not atomic:** unlike the Worker binding's `batch()`
34
+ * (which runs the statements in one transaction), the D1 REST API exposes no batch/transaction
35
+ * endpoint, so these execute as independent sequential queries — a mid-batch failure leaves earlier
36
+ * statements committed. D1 has no interactive transactions, and Kysely drives migrations through
37
+ * `prepare().all()` per statement rather than this method, so this affects only direct callers.
38
+ */
39
+ batch<T = unknown>(statements: D1PreparedStatement[]): Promise<D1Result<T>[]>;
40
+ exec(query: string): Promise<D1ExecResult>;
41
+ /** `dump()` is deprecated and has no REST equivalent — calling it is a programming error. */
42
+ dump(): Promise<ArrayBuffer>;
43
+ /** Sessions are a binding-only feature; the REST client has no equivalent. */
44
+ withSession(): never;
45
+ /** Run one SQL statement and return its raw REST result sets. */
46
+ executeQuery(sql: string, params?: string[]): Promise<QueryResult[]>;
47
+ /**
48
+ * Run many independent queries, collecting a per-query result instead of failing the whole batch
49
+ * on the first error — the caller decides what to do with partial failure.
50
+ */
51
+ batchQueries(queries: Array<{
52
+ sql: string;
53
+ params?: string[];
54
+ }>): Promise<D1BatchResult[]>;
55
+ /** Fetch the D1 database record. */
56
+ getDatabaseInfo(): Promise<D1>;
57
+ /** List the user tables in the database. */
58
+ listTables(): Promise<string[]>;
59
+ /**
60
+ * Read a table's column schema via `PRAGMA table_info`. SQLite pragmas take no bind parameters, so
61
+ * the table name is interpolated — guard it against a strict identifier allowlist first, so the one
62
+ * un-parameterized SQL path in this client can never become an injection sink.
63
+ */
64
+ getTableSchema(tableName: string): Promise<unknown[]>;
65
+ /** The database this manager targets and the account it lives in. */
66
+ getD1Info(): {
67
+ databaseId: string;
68
+ accountId: string;
69
+ };
70
+ getServiceType(): string;
71
+ /** Prove access by reading the database record. Never throws. */
72
+ validateServiceAccess(): Promise<boolean>;
73
+ }
74
+ //# sourceMappingURL=d1Manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"d1Manager.d.ts","sourceRoot":"","sources":["../../src/d1/d1Manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAE3F,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAEpE,OAAO,EAAE,iBAAiB,EAAE,KAAK,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAGpF,wFAAwF;AACxF,MAAM,WAAW,eAAgB,SAAQ,uBAAuB;IAC9D,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,+FAA+F;AAC/F,MAAM,WAAW,aAAa;IAC5B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAC;IACjB,gDAAgD;IAChD,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,qBAAa,mBAAoB,SAAQ,iBAAkB,YAAW,UAAU;IAC9E,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,YAAY,MAAM,EAAE,eAAe,EAMlC;IAID,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAE1C;IAED;;;;;;OAMG;IACG,KAAK,CAAC,CAAC,GAAG,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAMlF;IAEK,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAM/C;IAED,6FAA6F;IACvF,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC,CAEjC;IAED,8EAA8E;IAC9E,WAAW,IAAI,KAAK,CAEnB;IAID,iEAAiE;IAC3D,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAKzE;IAED;;;OAGG;IACG,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAW/F;IAED,oCAAoC;IAC9B,eAAe,IAAI,OAAO,CAAC,EAAE,CAAC,CAInC;IAED,4CAA4C;IACtC,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAMpC;IAED;;;;OAIG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAS1D;IAED,qEAAqE;IACrE,SAAS,IAAI;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAErD;IAED,cAAc,IAAI,MAAM,CAEvB;IAED,iEAAiE;IAC3D,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO9C;CACF"}
@@ -0,0 +1,128 @@
1
+ import { CloudflareNotConfiguredError, cloudflareRequest, reasonOf } from "../client/errors.js";
2
+ import { CloudflareManager } from "../client/manager.js";
3
+ import { D1PreparedStatementREST } from "./d1PreparedStatement.js";
4
+ import { InternalError, ValidationError } from "@pithy-sh/core/src/error/pithyError";
5
+ //#region src/d1/d1Manager.ts
6
+ /**
7
+ * Out-of-Worker D1 access over the REST API. It `implements D1Database`, so the same query builder
8
+ * a Worker runs against a binding runs from a CLI/CI context against D1 over REST — most
9
+ * importantly as the database behind a Kysely `D1Dialect` (kysely-d1), which is how `pithy migrate`
10
+ * promotes/rolls back schema remotely (issue #31). Inside a Worker you use the `D1Database` binding
11
+ * directly; this is the REST counterpart, addressed by database id.
12
+ */
13
+ var CloudflareD1Manager = class extends CloudflareManager {
14
+ databaseId;
15
+ constructor(config) {
16
+ super(config);
17
+ if (!config.databaseId) throw new CloudflareNotConfiguredError({ detail: "Missing databaseId for D1 REST access." });
18
+ this.databaseId = config.databaseId;
19
+ }
20
+ prepare(query) {
21
+ return new D1PreparedStatementREST(query, this.getClient(), this.accountId, this.databaseId);
22
+ }
23
+ /**
24
+ * Run each statement and collect its result. **Not atomic:** unlike the Worker binding's `batch()`
25
+ * (which runs the statements in one transaction), the D1 REST API exposes no batch/transaction
26
+ * endpoint, so these execute as independent sequential queries — a mid-batch failure leaves earlier
27
+ * statements committed. D1 has no interactive transactions, and Kysely drives migrations through
28
+ * `prepare().all()` per statement rather than this method, so this affects only direct callers.
29
+ */
30
+ async batch(statements) {
31
+ const results = [];
32
+ for (const statement of statements) results.push(await statement.run());
33
+ return results;
34
+ }
35
+ async exec(query) {
36
+ const result = (await cloudflareRequest("D1 exec", () => this.getClient().d1.database.query(this.databaseId, {
37
+ account_id: this.accountId,
38
+ sql: query
39
+ }))).result[0];
40
+ return {
41
+ count: result?.results?.length ?? 0,
42
+ duration: result?.meta?.duration ?? 0
43
+ };
44
+ }
45
+ /** `dump()` is deprecated and has no REST equivalent — calling it is a programming error. */
46
+ async dump() {
47
+ throw new InternalError({ detail: "D1 dump() is not supported via the REST client." });
48
+ }
49
+ /** Sessions are a binding-only feature; the REST client has no equivalent. */
50
+ withSession() {
51
+ throw new InternalError({ detail: "D1 withSession() is not supported via the REST client." });
52
+ }
53
+ /** Run one SQL statement and return its raw REST result sets. */
54
+ async executeQuery(sql, params) {
55
+ return (await cloudflareRequest("D1 query", () => this.getClient().d1.database.query(this.databaseId, {
56
+ account_id: this.accountId,
57
+ sql,
58
+ params
59
+ }))).result;
60
+ }
61
+ /**
62
+ * Run many independent queries, collecting a per-query result instead of failing the whole batch
63
+ * on the first error — the caller decides what to do with partial failure.
64
+ */
65
+ async batchQueries(queries) {
66
+ return Promise.all(queries.map(async (query) => {
67
+ try {
68
+ const result = await this.executeQuery(query.sql, query.params);
69
+ return {
70
+ query: query.sql,
71
+ success: result.length > 0,
72
+ result
73
+ };
74
+ } catch (error) {
75
+ return {
76
+ query: query.sql,
77
+ success: false,
78
+ error: reasonOf(error)
79
+ };
80
+ }
81
+ }));
82
+ }
83
+ /** Fetch the D1 database record. */
84
+ async getDatabaseInfo() {
85
+ return cloudflareRequest("get D1 database info", () => this.getClient().d1.database.get(this.databaseId, { account_id: this.accountId }));
86
+ }
87
+ /** List the user tables in the database. */
88
+ async listTables() {
89
+ return (await this.executeQuery("SELECT name FROM sqlite_master WHERE type='table'")).flatMap((queryResult) => queryResult.results ?? []).map((row) => isNamedRow(row) ? row.name : void 0).filter((name) => typeof name === "string");
90
+ }
91
+ /**
92
+ * Read a table's column schema via `PRAGMA table_info`. SQLite pragmas take no bind parameters, so
93
+ * the table name is interpolated — guard it against a strict identifier allowlist first, so the one
94
+ * un-parameterized SQL path in this client can never become an injection sink.
95
+ */
96
+ async getTableSchema(tableName) {
97
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(tableName)) throw new ValidationError({
98
+ message: "Invalid table name.",
99
+ detail: `Table name must be a plain SQL identifier, got: ${tableName}`
100
+ });
101
+ return (await this.executeQuery(`PRAGMA table_info(${tableName})`)).flatMap((queryResult) => queryResult.results ?? []);
102
+ }
103
+ /** The database this manager targets and the account it lives in. */
104
+ getD1Info() {
105
+ return {
106
+ databaseId: this.databaseId,
107
+ accountId: this.accountId
108
+ };
109
+ }
110
+ getServiceType() {
111
+ return "D1 Database";
112
+ }
113
+ /** Prove access by reading the database record. Never throws. */
114
+ async validateServiceAccess() {
115
+ try {
116
+ await this.getDatabaseInfo();
117
+ return true;
118
+ } catch {
119
+ return false;
120
+ }
121
+ }
122
+ };
123
+ /** Narrow a result row to one carrying a string `name` column (the `sqlite_master` shape). */
124
+ function isNamedRow(row) {
125
+ return typeof row === "object" && row !== null && "name" in row && typeof row.name === "string";
126
+ }
127
+ //#endregion
128
+ export { CloudflareD1Manager };
@@ -0,0 +1,24 @@
1
+ import type { D1PreparedStatement, D1Result } from "@cloudflare/workers-types";
2
+ import type { Cloudflare } from "cloudflare";
3
+ export declare class D1PreparedStatementREST<T = Record<string, unknown>> implements D1PreparedStatement {
4
+ private readonly sql;
5
+ private readonly boundParams;
6
+ private readonly client;
7
+ private readonly accountId;
8
+ private readonly databaseId;
9
+ constructor(sql: string, client: Cloudflare, accountId: string, databaseId: string, boundParams?: unknown[]);
10
+ /** Bind positional parameters, returning a new statement (the binding's immutable contract). */
11
+ bind(...values: unknown[]): D1PreparedStatementREST<T>;
12
+ run<U = T>(): Promise<D1Result<U>>;
13
+ all<U = T>(): Promise<D1Result<U>>;
14
+ first<U = unknown>(colName: string): Promise<U | null>;
15
+ first<U = T>(): Promise<U | null>;
16
+ raw<U = unknown[]>(options: {
17
+ columnNames: true;
18
+ }): Promise<[string[], ...U[]]>;
19
+ raw<U = unknown[]>(options?: {
20
+ columnNames?: false;
21
+ }): Promise<U[]>;
22
+ private executeQuery;
23
+ }
24
+ //# sourceMappingURL=d1PreparedStatement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"d1PreparedStatement.d.ts","sourceRoot":"","sources":["../../src/d1/d1PreparedStatement.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAU,mBAAmB,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAuC7C,qBAAa,uBAAuB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAE,YAAW,mBAAmB;IAC9F,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAE7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAY;IAExC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IAEpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,YAAY,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAE,OAAO,EAAO,EAM9G;IAED,gGAAgG;IAChG,IAAI,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,uBAAuB,CAAC,CAAC,CAAC,CAErD;IAED,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAEjC;IAED,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAEjC;IAEK,KAAK,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACvD,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IASlC,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,EAAE,OAAO,EAAE;QAAE,WAAW,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAChF,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,KAAK,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;YAe5D,YAAY;CAY3B"}
@@ -0,0 +1,91 @@
1
+ import { cloudflareRequest } from "../client/errors.js";
2
+ //#region src/d1/d1PreparedStatement.ts
3
+ /**
4
+ * A `D1PreparedStatement` backed by the D1 REST API instead of a Worker binding. It mirrors the
5
+ * binding's prepared-statement surface (`bind`/`run`/`all`/`first`/`raw`) closely enough that a
6
+ * Kysely `D1Dialect` (kysely-d1) can drive it unchanged — which is how `pithy migrate` runs the
7
+ * same query builder from a CLI/CI context against D1 over REST (issue #31).
8
+ *
9
+ * `D1PreparedStatement` and `D1Meta` are imported by name from `@cloudflare/workers-types`, so this
10
+ * class is structurally a drop-in for the binding's statement. They used to be named off the global
11
+ * scope, which resolved only because this package's own `tsconfig.json` loads the types — and every
12
+ * package here ships raw TypeScript, so an adopter compiling this file got `Cannot find name` unless
13
+ * their program happened to pull the types in too. Importing them is what makes the dependency real
14
+ * (#431).
15
+ */
16
+ function mapMeta(restMeta) {
17
+ return {
18
+ duration: restMeta?.duration ?? 0,
19
+ size_after: restMeta?.size_after ?? 0,
20
+ rows_read: restMeta?.rows_read ?? 0,
21
+ rows_written: restMeta?.rows_written ?? 0,
22
+ last_row_id: restMeta?.last_row_id ?? 0,
23
+ changed_db: restMeta?.changed_db ?? false,
24
+ changes: restMeta?.changes ?? 0
25
+ };
26
+ }
27
+ /** The REST `params` array is strings; coerce each bound value the way the D1 REST API expects. */
28
+ function toStringParams(params) {
29
+ return params.map((value) => {
30
+ if (value === null || value === void 0) return "";
31
+ if (typeof value === "string") return value;
32
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
33
+ return JSON.stringify(value);
34
+ });
35
+ }
36
+ var D1PreparedStatementREST = class D1PreparedStatementREST {
37
+ sql;
38
+ boundParams;
39
+ client;
40
+ accountId;
41
+ databaseId;
42
+ constructor(sql, client, accountId, databaseId, boundParams = []) {
43
+ this.sql = sql;
44
+ this.boundParams = boundParams;
45
+ this.client = client;
46
+ this.accountId = accountId;
47
+ this.databaseId = databaseId;
48
+ }
49
+ /** Bind positional parameters, returning a new statement (the binding's immutable contract). */
50
+ bind(...values) {
51
+ return new D1PreparedStatementREST(this.sql, this.client, this.accountId, this.databaseId, values);
52
+ }
53
+ run() {
54
+ return this.executeQuery();
55
+ }
56
+ all() {
57
+ return this.executeQuery();
58
+ }
59
+ async first(colName) {
60
+ const row = (await this.executeQuery()).results[0] ?? null;
61
+ if (row === null) return null;
62
+ if (colName) return row[colName] ?? null;
63
+ return row;
64
+ }
65
+ async raw(options) {
66
+ const params = this.boundParams.length > 0 ? toStringParams(this.boundParams) : void 0;
67
+ const rawResult = (await cloudflareRequest("D1 raw query", () => this.client.d1.database.raw(this.databaseId, {
68
+ account_id: this.accountId,
69
+ sql: this.sql,
70
+ params
71
+ }))).result[0];
72
+ const rows = rawResult?.results?.rows ?? [];
73
+ if (options?.columnNames) return [rawResult?.results?.columns ?? [], ...rows];
74
+ return rows;
75
+ }
76
+ async executeQuery() {
77
+ const params = this.boundParams.length > 0 ? toStringParams(this.boundParams) : void 0;
78
+ const queryResult = (await cloudflareRequest("D1 query", () => this.client.d1.database.query(this.databaseId, {
79
+ account_id: this.accountId,
80
+ sql: this.sql,
81
+ params
82
+ }))).result[0];
83
+ return {
84
+ success: true,
85
+ meta: mapMeta(queryResult?.meta),
86
+ results: queryResult?.results ?? []
87
+ };
88
+ }
89
+ };
90
+ //#endregion
91
+ export { D1PreparedStatementREST };
@@ -0,0 +1,28 @@
1
+ import { z } from "zod";
2
+ import { CloudflareManager } from "../client/manager";
3
+ /** A D1 database's identity, decoded from the create response. */
4
+ export declare const D1DatabaseInfo: z.ZodObject<{
5
+ uuid: z.ZodString;
6
+ name: z.ZodString;
7
+ }, z.core.$strip>;
8
+ export type D1DatabaseInfo = z.output<typeof D1DatabaseInfo>;
9
+ /**
10
+ * Account-level D1 control plane: **create and delete databases**. Customers use this to stand up
11
+ * and tear down per-environment databases (e.g. ephemeral staging), and `pithy add secrets` uses it
12
+ * to provision each environment's secrets D1. Addressed by account — unlike {@link CloudflareD1Manager},
13
+ * which targets one database id for queries.
14
+ */
15
+ export declare class CloudflareD1Provisioner extends CloudflareManager {
16
+ getServiceType(): string;
17
+ /** Prove access by listing databases — a read, never a destructive create/delete. Never throws. */
18
+ validateServiceAccess(): Promise<boolean>;
19
+ /** Every D1 database in the account — for prefix-scan reconcile teardown (`pithy feature destroy`). */
20
+ listDatabases(): Promise<D1DatabaseInfo[]>;
21
+ /** Find a database by exact name in the account, or `null` — for idempotent provisioning. */
22
+ findDatabaseByName(name: string): Promise<D1DatabaseInfo | null>;
23
+ /** Create a D1 database by name; returns its id and name. */
24
+ createDatabase(name: string): Promise<D1DatabaseInfo>;
25
+ /** Delete a D1 database by id. */
26
+ deleteDatabase(databaseId: string): Promise<void>;
27
+ }
28
+ //# sourceMappingURL=d1Provisioner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"d1Provisioner.d.ts","sourceRoot":"","sources":["../../src/d1/d1Provisioner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,kEAAkE;AAClE,eAAO,MAAM,cAAc;;;iBAK4D,CAAC;AACxF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AAE7D;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,iBAAiB;IAC5D,cAAc,IAAI,MAAM,CAEvB;IAED,mGAAmG;IAC7F,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO9C;IAED,uGAAuG;IACjG,aAAa,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAS/C;IAED,6FAA6F;IACvF,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAQrE;IAED,6DAA6D;IACvD,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAK1D;IAED,kCAAkC;IAC5B,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAItD;CACF"}