@zackbart/connecta 0.28.1 → 0.29.0

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 (319) hide show
  1. package/CHANGELOG.md +788 -5
  2. package/README.md +62 -42
  3. package/bin/connecta.mjs +110 -139
  4. package/bin/meta-tool-names.d.mts +2 -0
  5. package/bin/meta-tool-names.mjs +9 -0
  6. package/bin/version.mjs +3 -0
  7. package/dist/access-tokens.js +53 -63
  8. package/dist/activity-request.js +8 -0
  9. package/dist/activity.d.ts +76 -42
  10. package/dist/activity.js +110 -42
  11. package/dist/auth/clerk-sdk/client.d.ts +65 -0
  12. package/dist/auth/clerk-sdk/client.js +5 -0
  13. package/dist/auth/clerk-sdk/generated.js +1845 -0
  14. package/dist/auth/clerk-transport.js +19 -0
  15. package/dist/auth/clerk.d.ts +17 -7
  16. package/dist/auth/clerk.js +189 -107
  17. package/dist/auth/cloudflare-access.js +25 -26
  18. package/dist/auth/downstream-client-metadata.d.ts +29 -0
  19. package/dist/auth/downstream-client-metadata.js +133 -0
  20. package/dist/auth/downstream-oauth.js +1584 -1580
  21. package/dist/auth/downstream-revocation.js +106 -0
  22. package/dist/auth/oauth-token-response.js +231 -0
  23. package/dist/auth/oauth-v2-migration.js +116 -0
  24. package/dist/auth/static-oauth.js +93 -71
  25. package/dist/auth-elicitation.js +283 -0
  26. package/dist/branding.d.ts +8 -16
  27. package/dist/branding.js +26 -59
  28. package/dist/byte-read-response.js +39 -0
  29. package/dist/call-admission.js +12 -16
  30. package/dist/catalog-cache.js +739 -0
  31. package/dist/catalog-drift.d.ts +50 -15
  32. package/dist/catalog-drift.js +365 -128
  33. package/dist/catalog-fingerprint.js +2 -4
  34. package/dist/catalog-freshness.js +13 -0
  35. package/dist/catalog-limits.js +2 -2
  36. package/dist/catalog-service.js +368 -224
  37. package/dist/catalog.js +43 -84
  38. package/dist/concurrency.js +3 -4
  39. package/dist/config-defaults.d.ts +39 -0
  40. package/dist/config-defaults.js +46 -0
  41. package/dist/config-schema.d.ts +181 -0
  42. package/dist/config-schema.js +435 -0
  43. package/dist/config-value-sources.js +102 -0
  44. package/dist/config.d.ts +631 -0
  45. package/dist/config.js +503 -0
  46. package/dist/connector-access.d.ts +1 -0
  47. package/dist/connector-access.js +13 -15
  48. package/dist/connector-caller.d.ts +8 -0
  49. package/dist/connector-caller.js +15 -6
  50. package/dist/connector-scope.js +1 -1
  51. package/dist/connectors/api-connector.d.ts +59 -7
  52. package/dist/connectors/api-connector.js +106 -19
  53. package/dist/connectors/api.js +4 -1
  54. package/dist/connectors/guarded-fetch.d.ts +144 -0
  55. package/dist/connectors/guarded-fetch.js +84 -35
  56. package/dist/connectors/negotiation-cache.js +46 -0
  57. package/dist/connectors/option-shapes.js +27 -0
  58. package/dist/connectors/remote-mcp.d.ts +30 -4
  59. package/dist/connectors/remote-mcp.js +1774 -320
  60. package/dist/connectors/resource-uri.js +335 -0
  61. package/dist/credential-contract.d.ts +10 -0
  62. package/dist/credential-rules.js +1 -3
  63. package/dist/credential-url.js +108 -0
  64. package/dist/credentials.d.ts +6 -1
  65. package/dist/credentials.js +27 -18
  66. package/dist/d1.d.ts +130 -0
  67. package/dist/d1.js +290 -0
  68. package/dist/describe-config.d.ts +165 -0
  69. package/dist/describe-config.js +315 -0
  70. package/dist/described.js +106 -0
  71. package/dist/downstream-input-context.js +119 -0
  72. package/dist/downstream-input.js +323 -0
  73. package/dist/errors.d.ts +97 -20
  74. package/dist/errors.js +158 -80
  75. package/dist/execute.js +453 -305
  76. package/dist/executor-admission.d.ts +1 -0
  77. package/dist/executor-admission.js +11 -15
  78. package/dist/executor-contract.d.ts +20 -0
  79. package/dist/executor-contract.js +48 -0
  80. package/dist/executor-result.js +24 -9
  81. package/dist/executors/quickjs-child.js +3 -4
  82. package/dist/executors/quickjs-runtime.js +147 -59
  83. package/dist/executors/quickjs.js +29 -35
  84. package/dist/favicon.js +1 -2
  85. package/dist/fix-prompt.js +1 -3
  86. package/dist/guest-runtime.js +188 -0
  87. package/dist/guest-types.d.ts +176 -0
  88. package/dist/guest-types.js +1 -0
  89. package/dist/html-security.js +24 -0
  90. package/dist/identity.js +1 -3
  91. package/dist/inbound-credential.js +52 -0
  92. package/dist/index.d.ts +24 -256
  93. package/dist/index.js +65 -478
  94. package/dist/invocation-auth.js +69 -0
  95. package/dist/invocation.js +230 -73
  96. package/dist/json-schema.d.ts +1 -1
  97. package/dist/mcp-client-context.js +35 -0
  98. package/dist/mcp-result.js +11 -7
  99. package/dist/meta-output.js +76 -0
  100. package/dist/meta-tool-names.d.ts +2 -0
  101. package/dist/meta-tool-names.js +3 -0
  102. package/dist/meta-tools.js +285 -209
  103. package/dist/module-contracts.d.ts +8 -23
  104. package/dist/node.d.ts +0 -2
  105. package/dist/node.js +2 -2
  106. package/dist/oauth-callback-outcome.js +1 -3
  107. package/dist/oauth-handoff.js +117 -0
  108. package/dist/oauth-sealing.js +20 -0
  109. package/dist/operator-record.d.ts +137 -0
  110. package/dist/operator-record.js +420 -0
  111. package/dist/operator-ui/assets.js +31 -0
  112. package/dist/operator-ui/contract.d.ts +72 -0
  113. package/dist/operator-ui/contract.js +1 -0
  114. package/dist/operator-ui/fix-prompts.d.ts +18 -0
  115. package/dist/operator-ui/generated.js +5 -4
  116. package/dist/operator-ui/model.d.ts +12 -87
  117. package/dist/operator-ui/model.js +2 -9
  118. package/dist/operator-ui/setup-commands.js +1 -3
  119. package/dist/operator-ui/view.d.ts +323 -0
  120. package/dist/operator-ui/view.js +62 -189
  121. package/dist/page-styles.js +2 -3
  122. package/dist/program-source.js +14 -2
  123. package/dist/{exempt-writes.js → program-writes.js} +55 -18
  124. package/dist/provider.d.ts +142 -0
  125. package/dist/provider.js +187 -0
  126. package/dist/providers/_shared/google/result-size.js +57 -0
  127. package/dist/providers/_shared/google/service-account.d.ts +58 -0
  128. package/dist/providers/_shared/google/service-account.js +500 -0
  129. package/dist/providers/_shared/google/workspace.d.ts +276 -0
  130. package/dist/providers/_shared/google/workspace.js +518 -0
  131. package/dist/providers/{basecamp.d.ts → basecamp/index.d.ts} +6 -28
  132. package/dist/providers/basecamp/index.js +997 -0
  133. package/dist/providers/basecamp/skill.generated.js +9 -0
  134. package/dist/providers/{breeze.d.ts → breeze/index.d.ts} +2 -2
  135. package/dist/providers/{breeze.js → breeze/index.js} +184 -140
  136. package/dist/providers/breeze/skill.generated.js +9 -0
  137. package/dist/providers/{ccb.d.ts → ccb/index.d.ts} +2 -2
  138. package/dist/providers/{ccb.js → ccb/index.js} +271 -116
  139. package/dist/providers/ccb/skill.generated.js +9 -0
  140. package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
  141. package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
  142. package/dist/providers/cloudflare/skill.generated.js +10 -0
  143. package/dist/providers/docs/index.d.ts +54 -0
  144. package/dist/providers/docs/index.js +1237 -0
  145. package/dist/providers/docs/skill.generated.js +9 -0
  146. package/dist/providers/drive/index.d.ts +53 -0
  147. package/dist/providers/drive/index.js +1523 -0
  148. package/dist/providers/drive/skill.generated.js +9 -0
  149. package/dist/providers/forms/index.d.ts +54 -0
  150. package/dist/providers/forms/index.js +1217 -0
  151. package/dist/providers/forms/skill.generated.js +11 -0
  152. package/dist/providers/github/auth.d.ts +32 -0
  153. package/dist/providers/github/auth.js +361 -0
  154. package/dist/providers/github/hosted.js +218 -0
  155. package/dist/providers/github/index.d.ts +13 -0
  156. package/dist/providers/github/index.js +140 -0
  157. package/dist/providers/github/key.js +100 -0
  158. package/dist/providers/github/rest.js +321 -0
  159. package/dist/providers/github/scope.d.ts +29 -0
  160. package/dist/providers/github/scope.js +124 -0
  161. package/dist/providers/github/skill.generated.js +8 -0
  162. package/dist/providers/gmail/index.d.ts +51 -0
  163. package/dist/providers/gmail/index.js +1436 -0
  164. package/dist/providers/gmail/skill.generated.js +9 -0
  165. package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
  166. package/dist/providers/linear/index.js +352 -0
  167. package/dist/providers/linear/skill.generated.js +8 -0
  168. package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
  169. package/dist/providers/mixpanel/index.js +416 -0
  170. package/dist/providers/mixpanel/skill.generated.js +9 -0
  171. package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
  172. package/dist/providers/{notion.js → notion/index.js} +226 -239
  173. package/dist/providers/notion/skill.generated.js +10 -0
  174. package/dist/providers/{overflow.d.ts → overflow/index.d.ts} +2 -2
  175. package/dist/providers/{overflow.js → overflow/index.js} +74 -111
  176. package/dist/providers/overflow/skill.generated.js +8 -0
  177. package/dist/providers/{planning-center.d.ts → planning-center/index.d.ts} +2 -2
  178. package/dist/providers/{planning-center.js → planning-center/index.js} +264 -104
  179. package/dist/providers/planning-center/skill.generated.js +10 -0
  180. package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
  181. package/dist/providers/revenuecat/index.js +557 -0
  182. package/dist/providers/revenuecat/skill.generated.js +11 -0
  183. package/dist/providers/sheets/index.d.ts +54 -0
  184. package/dist/providers/sheets/index.js +1237 -0
  185. package/dist/providers/sheets/skill.generated.js +10 -0
  186. package/dist/providers/slides/index.d.ts +53 -0
  187. package/dist/providers/slides/index.js +2645 -0
  188. package/dist/providers/slides/skill.generated.js +9 -0
  189. package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
  190. package/dist/providers/stripe/index.js +266 -0
  191. package/dist/providers/stripe/skill.generated.js +11 -0
  192. package/dist/providers/{tithely.d.ts → tithely/index.d.ts} +2 -2
  193. package/dist/providers/{tithely.js → tithely/index.js} +60 -91
  194. package/dist/providers/tithely/skill.generated.js +8 -0
  195. package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
  196. package/dist/providers/vercel/index.js +1038 -0
  197. package/dist/providers/vercel/mcp-ownership.js +212 -0
  198. package/dist/providers/vercel/skill.generated.js +11 -0
  199. package/dist/registry.d.ts +108 -171
  200. package/dist/registry.js +513 -925
  201. package/dist/request-state.js +21 -0
  202. package/dist/resource-read.js +100 -0
  203. package/dist/result-shapes.js +17 -49
  204. package/dist/retired-toolkits.js +1 -1
  205. package/dist/routes/access-tokens.js +3 -6
  206. package/dist/routes/activity-context.js +21 -0
  207. package/dist/routes/activity-disclosure.js +40 -0
  208. package/dist/routes/activity.js +59 -21
  209. package/dist/routes/actor-labels.js +3 -50
  210. package/dist/routes/connect.js +221 -0
  211. package/dist/routes/credentials.js +15 -27
  212. package/dist/routes/mcp.js +356 -130
  213. package/dist/routes/oauth-client-metadata.js +15 -0
  214. package/dist/routes/oauth-management.js +45 -107
  215. package/dist/routes/oauth.js +110 -57
  216. package/dist/routes/operator.js +3 -3
  217. package/dist/routes/shared.d.ts +25 -64
  218. package/dist/routes/shared.js +160 -80
  219. package/dist/routes/skills.js +30 -0
  220. package/dist/routes/ui-config.js +225 -0
  221. package/dist/routes/ui-data.js +40 -35
  222. package/dist/routes/ui.js +66 -56
  223. package/dist/runtime/admission.js +4 -2
  224. package/dist/runtime/call-admission.js +3 -1
  225. package/dist/runtime/connector-scope.js +1 -4
  226. package/dist/runtime/run.js +10 -39
  227. package/dist/runtime/services.js +12 -117
  228. package/dist/runtime/shared-read.js +1 -3
  229. package/dist/runtime/storage.js +8 -1
  230. package/dist/sent-secrets.d.ts +52 -0
  231. package/dist/sent-secrets.js +474 -0
  232. package/dist/server.js +28 -71
  233. package/dist/skills.js +406 -174
  234. package/dist/sqlite.d.ts +48 -0
  235. package/dist/sqlite.js +166 -0
  236. package/dist/storage/keys.js +428 -0
  237. package/dist/storage/memory.js +7 -0
  238. package/dist/storage/sql.d.ts +66 -0
  239. package/dist/storage/sql.js +534 -0
  240. package/dist/timeout.js +4 -2
  241. package/dist/tool-name.js +17 -0
  242. package/dist/tool-safety.d.ts +9 -35
  243. package/dist/tool-safety.js +20 -40
  244. package/dist/types.d.ts +320 -54
  245. package/dist/types.js +0 -1
  246. package/dist/ui.d.ts +7 -19
  247. package/dist/ui.js +76 -90
  248. package/dist/url-safety.js +3 -6
  249. package/dist/usage-guide.js +137 -0
  250. package/dist/validate.d.ts +7 -0
  251. package/dist/validate.js +309 -47
  252. package/dist/version.d.ts +1 -7
  253. package/dist/version.js +3 -7
  254. package/dist/worker.d.ts +11 -0
  255. package/dist/worker.js +434 -0
  256. package/documentation/architecture.md +664 -231
  257. package/documentation/auth.md +936 -546
  258. package/documentation/code-mode.md +501 -312
  259. package/documentation/meta-tools.md +449 -200
  260. package/documentation/operator-tests.md +93 -0
  261. package/documentation/provider-migration-0.29.md +60 -0
  262. package/examples/worker/AGENTS.md +34 -12
  263. package/examples/worker/README.md +286 -255
  264. package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
  265. package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
  266. package/examples/worker/src/connecta.config.ts +151 -0
  267. package/examples/worker/src/index.ts +14 -197
  268. package/examples/worker/wrangler.jsonc +15 -35
  269. package/package.json +120 -54
  270. package/templates/node/.dockerignore +1 -2
  271. package/templates/node/.env.example +20 -21
  272. package/templates/node/AGENTS.md +21 -17
  273. package/templates/node/Dockerfile +3 -5
  274. package/templates/node/README.md +133 -58
  275. package/templates/node/docker-compose.yml +11 -12
  276. package/templates/node/package.json +4 -2
  277. package/templates/node/src/connecta.config.ts +119 -0
  278. package/templates/node/src/index.ts +9 -154
  279. package/templates/node/src/provision-token.ts +17 -0
  280. package/AGENTS.md +0 -170
  281. package/dist/artifacts/connector.d.ts +0 -58
  282. package/dist/artifacts/connector.js +0 -730
  283. package/dist/artifacts/document.js +0 -86
  284. package/dist/artifacts/guide.js +0 -140
  285. package/dist/artifacts/html-scan.js +0 -329
  286. package/dist/artifacts/json.js +0 -78
  287. package/dist/artifacts/kv-store.d.ts +0 -20
  288. package/dist/artifacts/kv-store.js +0 -140
  289. package/dist/artifacts/markdown.js +0 -478
  290. package/dist/artifacts/module.d.ts +0 -37
  291. package/dist/artifacts/module.js +0 -66
  292. package/dist/artifacts/operations.d.ts +0 -197
  293. package/dist/artifacts/operations.js +0 -1002
  294. package/dist/artifacts/refresh.d.ts +0 -55
  295. package/dist/artifacts/refresh.js +0 -236
  296. package/dist/artifacts/routes.js +0 -206
  297. package/dist/artifacts/types.d.ts +0 -214
  298. package/dist/artifacts/types.js +0 -22
  299. package/dist/artifacts/validate.d.ts +0 -62
  300. package/dist/artifacts/validate.js +0 -651
  301. package/dist/artifacts.d.ts +0 -6
  302. package/dist/artifacts.js +0 -9
  303. package/dist/auth/bearer.d.ts +0 -11
  304. package/dist/auth/bearer.js +0 -50
  305. package/dist/providers/basecamp.js +0 -445
  306. package/dist/providers/linear.js +0 -217
  307. package/dist/providers/mixpanel.js +0 -247
  308. package/dist/providers/revenuecat.js +0 -317
  309. package/dist/providers/stripe.js +0 -253
  310. package/dist/providers/vercel.js +0 -1253
  311. package/dist/storage/file.d.ts +0 -15
  312. package/dist/storage/file.js +0 -455
  313. package/ethos.md +0 -93
  314. package/examples/worker/src/cloudflare-kv.ts +0 -43
  315. package/examples/worker/src/d1-activity-row.ts +0 -155
  316. package/examples/worker/src/d1-activity.ts +0 -147
  317. package/examples/worker/src/d1-storage.ts +0 -138
  318. package/examples/worker/src/r2-artifact-blobs.ts +0 -29
  319. package/templates/node/src/file-activity.ts +0 -182
@@ -0,0 +1,26 @@
1
+ // Bindings for the one-shot Workers KV → D1 copy in scripts/copy-kv-to-d1.mjs.
2
+ // Never deployed: `wrangler deploy` reads wrangler.jsonc, which binds no KV.
3
+ // Both bindings are remote, so the script reads the real namespace and writes
4
+ // the real database through your `wrangler login` session. Fill in the ids,
5
+ // stop traffic/writers, wait for stable KV, copy and verify before deploying,
6
+ // then mark cutover before reopening traffic. README.md § "Upgrading from
7
+ // 0.28" has the steps.
8
+ {
9
+ "name": "connecta-kv-to-d1",
10
+ "compatibility_date": "2025-01-01",
11
+ "kv_namespaces": [
12
+ {
13
+ "binding": "CONNECTA_KV",
14
+ "id": "replace-with-kv-namespace-id",
15
+ "remote": true,
16
+ },
17
+ ],
18
+ "d1_databases": [
19
+ {
20
+ "binding": "CONNECTA_DB",
21
+ "database_name": "connecta",
22
+ "database_id": "replace-with-d1-database-id",
23
+ "remote": true,
24
+ },
25
+ ],
26
+ }
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env node
2
+ // Run only during the maintenance-window cutover. See README "Upgrading from 0.28".
3
+
4
+ export const HELP = `Usage: node scripts/copy-kv-to-d1.mjs [config] --maintenance [options]
5
+ Config defaults to kv-to-d1.wrangler.jsonc; source is its CONNECTA_KV namespace id.
6
+ 1. Bulk-export KV and export D1 with wrangler d1 export. Preserve the credentials key.
7
+ 2. Block traffic and all background writers, then drain in-flight requests.
8
+ 3. Wait at least 60 seconds, or the longest configured KV cacheTtl, whichever is
9
+ greater. Propagation can take longer. Two consecutive listing/hash passes
10
+ must match; this script checks them before copying or verifying.
11
+ 4. Copy BEFORE switching traffic. Resolve invalid/conflict counts under maintenance.
12
+ --overwrite-family <family> may be repeated; there is no blanket overwrite.
13
+ Token/OAuth/vault families also require --confirm-stale-d1 after the operator
14
+ confirms that D1's conflicting rows are stale. Run --verify for exact value
15
+ hashes and expiries; it reports mismatch counts by family, never payloads.
16
+ 5. Deploy D1 config under maintenance. Run doctor and check an existing OAuth
17
+ connector, cta_ token, vault credential, and visible activity. Set
18
+ CONNECTA_ACTIVITY="on"; config lives in src/connecta.config.ts.
19
+ 6. Run --mark-live before reopening traffic. It permanently seals this source
20
+ in D1. Never rerun afterward. --i-know-this-is-stale overrides the seal only
21
+ after assessing stale KV state, with traffic and writers stopped again.
22
+ Keep backups before deleting KV.
23
+ Options: --verify, --mark-live, --overwrite-family <family>, --confirm-stale-d1,
24
+ --i-know-this-is-stale, --maintenance, --help`;
25
+
26
+ export class MigrationUsageError extends Error {}
27
+
28
+ export function parseArgs(args) {
29
+ const options = { configPath: "kv-to-d1.wrangler.jsonc", overwriteFamilies: [] };
30
+ let hasConfig = false;
31
+ for (let i = 0; i < args.length; i++) {
32
+ const arg = args[i];
33
+ if (arg === "--overwrite-family") {
34
+ const family = args[++i];
35
+ if (!family || family.startsWith("--")) throw new MigrationUsageError("--overwrite-family requires a family");
36
+ options.overwriteFamilies.push(family);
37
+ } else if (arg === "--maintenance") options.maintenance = true;
38
+ else if (arg === "--verify") options.verify = true;
39
+ else if (arg === "--mark-live") options.markLive = true;
40
+ else if (arg === "--confirm-stale-d1") options.confirmStale = true;
41
+ else if (arg === "--i-know-this-is-stale") options.allowStale = true;
42
+ else if (arg === "--help") options.help = true;
43
+ else if (!arg.startsWith("--") && !hasConfig) {
44
+ options.configPath = arg;
45
+ hasConfig = true;
46
+ } else throw new MigrationUsageError("Unknown argument; use --help");
47
+ }
48
+ if (options.help) return options;
49
+ if (!options.maintenance)
50
+ throw new MigrationUsageError(
51
+ "Traffic and writers must be stopped; pass --maintenance after draining and waiting for KV propagation",
52
+ );
53
+ if (
54
+ options.overwriteFamilies.some(
55
+ (family) => family === "access-token" || family === "credential" || family.startsWith("oauth"),
56
+ ) &&
57
+ !options.confirmStale
58
+ ) {
59
+ throw new MigrationUsageError(
60
+ "Token/OAuth/vault overwrite requires --confirm-stale-d1 after confirming D1 rows are stale",
61
+ );
62
+ }
63
+ if ((options.verify || options.markLive) && options.overwriteFamilies.length)
64
+ throw new MigrationUsageError("Verification and cutover marking cannot overwrite families");
65
+ if (options.verify && options.markLive)
66
+ throw new MigrationUsageError("Verify before marking cutover in a separate command");
67
+ return options;
68
+ }
69
+
70
+ const encode = new TextEncoder();
71
+ const hash = async (value) => {
72
+ const bytes = await crypto.subtle.digest("SHA-256", encode.encode(value));
73
+ return Array.from(new Uint8Array(bytes), (n) => n.toString(16).padStart(2, "0")).join("");
74
+ };
75
+
76
+ // Keep only entry digests. Sorting makes the comparison independent of list order.
77
+ async function sourceHash(kv) {
78
+ const digests = [];
79
+ let cursor;
80
+ do {
81
+ const page = await kv.list({ limit: 1000, ...(cursor ? { cursor } : {}) });
82
+ for (const key of page.keys) {
83
+ if (key.expiration !== undefined && key.expiration * 1000 <= Date.now()) continue;
84
+ const value = await kv.get(key.name, "text");
85
+ if (value !== null) digests.push(await hash(JSON.stringify([key.name, key.expiration ?? null, value])));
86
+ }
87
+ cursor = page.list_complete ? undefined : page.cursor;
88
+ } while (cursor);
89
+ return hash(digests.sort().join(""));
90
+ }
91
+
92
+ /** Injected operations let the Workers suite exercise the real migration flow. */
93
+ export async function runMigration(kv, db, options, operations, output = console) {
94
+ if (!options.maintenance) throw new MigrationUsageError("Migration requires maintenance");
95
+ if (options.markLive) {
96
+ await operations.markKvToD1Live(db, options.source);
97
+ output.log("Cutover sealed in D1. Reopen traffic after deployment verification; never copy stale KV afterward.");
98
+ return 0;
99
+ }
100
+ if ((await sourceHash(kv)) !== (await sourceHash(kv))) {
101
+ throw new MigrationUsageError("Workers KV source is not stable; keep maintenance active, wait, and repeat");
102
+ }
103
+ const families = {};
104
+ let cursor;
105
+ do {
106
+ const result = await operations.copyKvToD1(kv, db, {
107
+ source: options.source,
108
+ cursor,
109
+ overwriteFamilies: options.overwriteFamilies,
110
+ verify: options.verify,
111
+ allowStale: options.allowStale,
112
+ });
113
+ for (const [family, counts] of Object.entries(result.families)) {
114
+ const into = (families[family] ??= {});
115
+ for (const [field, n] of Object.entries(counts)) into[field] = (into[field] ?? 0) + n;
116
+ }
117
+ cursor = result.cursor;
118
+ } while (cursor);
119
+ output.table(families);
120
+ if (Object.values(families).some((counts) => counts.invalid > 0 || counts.conflicts > 0 || counts.mismatches > 0)) {
121
+ output.error(
122
+ "Invalid entries, conflicts, or verification mismatches remain. Keep maintenance active and resolve the per-family counts.",
123
+ );
124
+ return 1;
125
+ }
126
+ return 0;
127
+ }
128
+
129
+ async function main() {
130
+ let proxy;
131
+ let operations;
132
+ try {
133
+ const options = parseArgs(process.argv.slice(2));
134
+ if (options.help) {
135
+ console.log(HELP);
136
+ return;
137
+ }
138
+ const wranglerModule = "wrangler";
139
+ const d1Module = "@zackbart/connecta/d1";
140
+ const { getPlatformProxy, unstable_readConfig } = await import(/* @vite-ignore */ wranglerModule);
141
+ operations = await import(/* @vite-ignore */ d1Module);
142
+ const config = unstable_readConfig({ config: options.configPath });
143
+ const source = config.kv_namespaces?.find((binding) => binding.binding === "CONNECTA_KV")?.id;
144
+ if (!source || source.startsWith("replace-"))
145
+ throw new MigrationUsageError("Configure the CONNECTA_KV namespace id before copying");
146
+ proxy = await getPlatformProxy({ configPath: options.configPath, persist: false });
147
+ const { CONNECTA_KV: kv, CONNECTA_DB: db } = proxy.env;
148
+ if (!kv || !db) throw new MigrationUsageError("Copy config must bind CONNECTA_KV and CONNECTA_DB");
149
+ process.exitCode = await runMigration(kv, db, { ...options, source }, operations);
150
+ } catch (error) {
151
+ console.error(
152
+ error instanceof MigrationUsageError || (operations && error instanceof operations.KvToD1CopyError)
153
+ ? error.message
154
+ : "Workers KV to D1 migration stopped; keep maintenance active and inspect the configuration",
155
+ );
156
+ process.exitCode = 1;
157
+ } finally {
158
+ await proxy?.dispose();
159
+ }
160
+ }
161
+
162
+ if (typeof process !== "undefined" && import.meta.url === `file://${process.argv?.[1]}`) await main();
@@ -0,0 +1,151 @@
1
+ /**
2
+ * connecta on Cloudflare Workers: this deployment's configuration.
3
+ *
4
+ * One MCP endpoint aggregating a downstream remote MCP and an HTTP API, guarded
5
+ * by Cloudflare Access. It needs three resources and nothing else: one D1
6
+ * database (CONNECTA_DB) holding every piece of state, the Worker Loader
7
+ * binding (LOADER) behind execute_code, and one secret
8
+ * (CREDENTIAL_ENCRYPTION_KEY) sealing credentials in that database. Access
9
+ * admits the request before this Worker runs and supplies trusted human
10
+ * identity through ctx.access. Machines use stored cta_ tokens inside connecta.
11
+ *
12
+ * Optional modules are type-checked code switched by the environment: the
13
+ * credential vault by the CREDENTIAL_ENCRYPTION_KEY secret, and activity
14
+ * history by the CONNECTA_ACTIVITY var ("on"), in the same database. README.md § "Select optional
15
+ * modules" walks through each.
16
+ *
17
+ * Setup (this example has no package.json of its own — it self-references the
18
+ * installed `@zackbart/connecta` package):
19
+ * 1. `npm install` in the connecta package root (../../ from here) so the
20
+ * package import and wrangler resolve. A copy in its own repository
21
+ * installs `@zackbart/connecta @cloudflare/codemode` instead. Codemode is
22
+ * an optional peer.
23
+ * 2. `wrangler d1 create connecta` and put its id in wrangler.jsonc under
24
+ * `d1_databases`. connecta creates its tables on first use.
25
+ * 3. Set secrets:
26
+ * wrangler secret put DOWNSTREAM_TOKEN
27
+ * wrangler secret put CREDENTIAL_ENCRYPTION_KEY
28
+ * and PUBLIC_URL as a plain var in wrangler.jsonc.
29
+ * 4. Attach Cloudflare Access to this Worker. Enable Managed OAuth and
30
+ * Dynamic Client Registration. Its Allowed redirect URIs must include
31
+ * Claude's https://claude.ai/api/mcp/auth_callback plus ChatGPT's
32
+ * https://chatgpt.com/connector_platform_oauth_redirect and
33
+ * https://chatgpt.com/connector/oauth/* forms (see ../AGENTS.md).
34
+ * 5. Use the Workers Paid plan required by the `worker_loaders` binding.
35
+ * 6. `wrangler deploy` from this folder (examples/worker), where wrangler.jsonc
36
+ * lives. Point your MCP client at `<PUBLIC_URL>/mcp`.
37
+ */
38
+ import { api, defineConfig, remoteMcp } from "@zackbart/connecta";
39
+ import { activityHistory } from "@zackbart/connecta/activity";
40
+ import { accessTokens } from "@zackbart/connecta/auth/access-tokens";
41
+ import { cloudflareAccessAuth } from "@zackbart/connecta/auth/cloudflare-access";
42
+ import { encryptedCredentialVault } from "@zackbart/connecta/credentials";
43
+ import { d1ActivityStore, d1Storage } from "@zackbart/connecta/d1";
44
+ import { operatorUi } from "@zackbart/connecta/ui";
45
+ import { workerExecutor } from "@zackbart/connecta/worker";
46
+
47
+ export interface Env {
48
+ /** The one D1 database: OAuth grants, sealed credentials, catalogs, tokens, activity. */
49
+ CONNECTA_DB: D1Database;
50
+ /**
51
+ * Base64 32-byte AES key encrypting operator-managed credentials in D1.
52
+ * Unset means no vault: credential management is unavailable in the operator
53
+ * UI. Never put it in D1, since it is what protects D1.
54
+ */
55
+ CREDENTIAL_ENCRYPTION_KEY?: string;
56
+ DOWNSTREAM_TOKEN: string;
57
+ PUBLIC_URL: string;
58
+ /** "on" switches on activity history, in CONNECTA_DB. */
59
+ CONNECTA_ACTIVITY?: string;
60
+ /**
61
+ * Worker Loader binding (wrangler.jsonc `worker_loaders`) powering
62
+ * execute_code. Dynamic Workers require the Workers Paid plan.
63
+ */
64
+ LOADER: WorkerLoader;
65
+ }
66
+
67
+ export default defineConfig((env: Env) => {
68
+ const storage = d1Storage(env.CONNECTA_DB);
69
+ return {
70
+ publicUrl: env.PUBLIC_URL,
71
+ storage,
72
+ // Required adapter owns each run's handles; direct upstream construction throws.
73
+ executor: workerExecutor({ loader: env.LOADER }),
74
+ // Access owns edge admission and proves human identity for MCP and the
75
+ // operator pages. Machines need Access service headers at the edge plus a
76
+ // stored cta_ bearer inside connecta; a service identity alone is refused.
77
+ auth: [cloudflareAccessAuth()],
78
+ // Always installed over the same D1 storage. Empty storage admits no
79
+ // machine client; an Access-authenticated operator can provision tokens.
80
+ accessTokens: accessTokens(storage),
81
+ // Code-owned roster. Access proves the identity; connecta derives
82
+ // connector visibility and management permissions from the stable id it
83
+ // supplies. Add `connectorAccess` and `activityAccess` to split members
84
+ // from operators; here every signed-in human may manage every connector
85
+ // and access token. Machine tokens never receive interactive authority.
86
+ identity: {
87
+ credentialAdministration: () => "all",
88
+ personalConnection: () => "all",
89
+ accessTokenManagement: () => true,
90
+ },
91
+ // Connectors that declare a `credential` slot become editable by every
92
+ // signed-in human who can see that connector, encrypted with this key
93
+ // before anything reaches D1. A saved replacement takes effect on the
94
+ // next call; credentials fail at use.
95
+ vault: env.CREDENTIAL_ENCRYPTION_KEY ? encryptedCredentialVault(storage, env.CREDENTIAL_ENCRYPTION_KEY) : undefined,
96
+ // Payload-free activity at /ui/api/activity in CONNECTA_DB. The shared
97
+ // D1 mapping persists the build version and validated client facts, and
98
+ // adds nullable columns to old tables. Each write prunes expired rows.
99
+ activity:
100
+ env.CONNECTA_ACTIVITY === "on"
101
+ ? activityHistory({
102
+ store: d1ActivityStore(env.CONNECTA_DB, { retentionDays: 90 }),
103
+ deploymentId: "production",
104
+ })
105
+ : undefined,
106
+ // Branding is code too: operatorUi({ branding }).
107
+ ui: operatorUi(),
108
+ connectors: [
109
+ remoteMcp("notion", {
110
+ url: "https://mcp.notion.com/mcp",
111
+ description: "Notion — pages, databases, comments (static token)",
112
+ auth: {
113
+ type: "headers",
114
+ headers: { Authorization: `Bearer ${env.DOWNSTREAM_TOKEN}` },
115
+ // The vault-backed alternative for a downstream that authenticates
116
+ // with a static key: the operator manages it inside its connection
117
+ // in the operator UI, so no Worker secret holds it.
118
+ // type: "credential",
119
+ // credential: { label: "Notion internal integration token" },
120
+ },
121
+ // Use `authScope: "personal"` with OAuth or credential auth when each
122
+ // Access user connects their own downstream account. Literal headers
123
+ // are deployment-owned and cannot be personal.
124
+ }),
125
+ api("echo", {
126
+ description: "Echo — text transforms",
127
+ // What a vault-backed connector adds — an operator edits this slot
128
+ // inside its connection and the handler reads it with
129
+ // `await ctx.credential?.get()`, so the secret never lives in source:
130
+ // credential: { label: "API token" },
131
+ tools: [
132
+ {
133
+ name: "shout",
134
+ description: "Uppercase the given text.",
135
+ inputSchema: {
136
+ type: "object",
137
+ properties: {
138
+ text: { type: "string", description: "Text to uppercase." },
139
+ },
140
+ required: ["text"],
141
+ },
142
+ annotations: { readOnlyHint: true },
143
+ handler: async (args: { text: string }) => ({
144
+ shouted: args.text.toUpperCase(),
145
+ }),
146
+ },
147
+ ],
148
+ }),
149
+ ],
150
+ };
151
+ });
@@ -1,205 +1,22 @@
1
- import { operatorUi } from "@zackbart/connecta/ui";
2
- // import { activityHistory } from "@zackbart/connecta/activity";
3
- import { encryptedCredentialVault } from "@zackbart/connecta/credentials";
4
1
  /**
5
- * connecta on Cloudflare Workers.
6
- *
7
- * One MCP endpoint aggregating a downstream remote MCP and an HTTP API, guarded
8
- * by Cloudflare Access, with OAuth/cache state in a KV namespace. Access
9
- * authenticates the request before this Worker runs and supplies the trusted
10
- * identity through ctx.access. The required Worker Loader binding in
11
- * wrangler.jsonc backs the seven-tool surface.
12
- *
13
- * The operator surface is wired here except for activity history, which needs
14
- * a database this example does not create for you: sign-in and the credential
15
- * vault are on, and activity is three commented lines below. README.md
16
- * § "Select optional modules" walks through all three.
17
- *
18
- * Setup (this example has no package.json of its own — it self-references the
19
- * installed `@zackbart/connecta` package):
20
- * 1. `npm install` in the connecta package root (../../ from here) so the
21
- * package import and wrangler resolve. A copy in its own repository
22
- * installs `@zackbart/connecta @cloudflare/codemode` instead. Codemode is
23
- * an optional peer.
24
- * 2. Create a KV namespace and put its id in wrangler.jsonc under `kv_namespaces`.
25
- * 3. Set secrets:
26
- * wrangler secret put DOWNSTREAM_TOKEN
27
- * wrangler secret put CREDENTIAL_ENCRYPTION_KEY
28
- * and PUBLIC_URL as a plain var in wrangler.jsonc.
29
- * 4. Attach Cloudflare Access to this Worker. Enable Managed OAuth and
30
- * Dynamic Client Registration. Its Allowed redirect URIs must include
31
- * Claude's https://claude.ai/api/mcp/auth_callback plus ChatGPT's
32
- * https://chatgpt.com/connector_platform_oauth_redirect and
33
- * https://chatgpt.com/connector/oauth/* forms (see ../AGENTS.md).
34
- * 5. Use the Workers Paid plan required by the `worker_loaders` binding.
35
- * 6. `wrangler deploy` from this folder (examples/worker), where wrangler.jsonc
36
- * lives. Point your MCP client at `<PUBLIC_URL>/mcp`.
2
+ * connecta on Cloudflare Workers. Configuration lives in connecta.config.ts,
3
+ * which also lists setup; this file only starts it.
37
4
  */
38
- import { DynamicWorkerExecutor } from "@cloudflare/codemode";
39
- import {
40
- api,
41
- createConnecta,
42
- remoteMcp,
43
- } from "@zackbart/connecta";
44
- import { cloudflareAccessAuth } from "@zackbart/connecta/auth/cloudflare-access";
45
- import { cloudflareKvStorage } from "./cloudflare-kv.js";
46
- // import { artifacts, kvArtifactStore } from "@zackbart/connecta/artifacts";
47
- // import { d1Storage } from "./d1-storage.js";
48
- // Activity history, off by default because it needs a D1 database.
49
- // import { d1ActivityStore } from "./d1-activity.js";
50
-
51
- interface Env {
52
- CONNECTA_KV: KVNamespace;
53
- /**
54
- * Base64 32-byte AES key encrypting operator-managed credentials in KV.
55
- * Unset means no vault: credential management is unavailable in the operator
56
- * UI. Never put it in KV, since it is what protects KV.
57
- */
58
- CREDENTIAL_ENCRYPTION_KEY: string;
59
- DOWNSTREAM_TOKEN: string;
60
- PUBLIC_URL: string;
61
- /** Uncomment with the `d1_databases` binding to enable activity history. */
62
- // ACTIVITY_DB: D1Database;
63
- /**
64
- * Worker Loader binding (wrangler.jsonc `worker_loaders`) powering
65
- * execute_code. Dynamic Workers require the Workers Paid plan.
66
- */
67
- LOADER: WorkerLoader;
68
- }
69
-
70
- function build(env: Env) {
71
- const storage = cloudflareKvStorage(env.CONNECTA_KV);
72
- // Optional refreshable pages need the D1 binding, not Workers KV:
73
- // const artifactModule = artifacts({ store: kvArtifactStore(d1Storage(env.STORAGE_DB)) });
74
- // scheduledArtifacts.module = artifactModule;
75
- return createConnecta({
76
- publicUrl: env.PUBLIC_URL,
77
- storage,
78
- executor: new DynamicWorkerExecutor({ loader: env.LOADER }),
79
- auth: [
80
- // Access owns admission policy. A human identity may use MCP and the
81
- // operator pages; a service token may use MCP but cannot mutate operator
82
- // state. Neither path asks connecta to parse a JWT.
83
- cloudflareAccessAuth(),
84
- ],
85
- // Optional code-owned roster. Access proves the identity; connecta derives
86
- // connector visibility and deployment-operator status from the stable id
87
- // it supplies. A signed-in human may manage auth for every connector this
88
- // view includes. Omit the block to keep every connector visible and every
89
- // human a deployment operator.
90
- // identity: {
91
- // connectorAccess: ({ principal }) =>
92
- // principal?.id === "ACCESS_USER_UUID"
93
- // ? ["notion", "echo"]
94
- // : ["echo"],
95
- // activityAccess: ({ id }) => id === "ACCESS_USER_UUID",
96
- // },
97
- // Connectors that declare a `credential` slot become editable by every
98
- // signed-in human who can see that connector, inside its connection on /,
99
- // encrypted with this key before anything reaches KV. A saved replacement takes
100
- // effect on the next call — no redeploy, and no liveness probe:
101
- // credentials fail at use.
102
- //
103
- // The key is the vault, not the form: a credential form appears only on a
104
- // connector that declares a slot. Neither connector below does — Notion
105
- // here carries a deployment-owned static header and echo has no secret at
106
- // all — so this example ships the vault ready and nothing to fill in.
107
- // Declare a slot (see the commented shape on `echo`, or use a provider
108
- // connector like `notion()`, which declares its own) and the form appears
109
- // on the next load.
110
- vault: encryptedCredentialVault(storage, env.CREDENTIAL_ENCRYPTION_KEY),
111
- // Branding is code too: name, owner, description, favicon, and five theme
112
- // tokens that every other color is mixed from. A value that fails its
113
- // check falls back to the default, and the startup warning names it.
114
- // ui: operatorUi({
115
- // branding: {
116
- // productName: "Acme Tools",
117
- // ownerName: "Acme",
118
- // theme: {
119
- // accent: "#2f5fe0", // hex only
120
- // radius: 10, // pixels, or a CSS length such as "0.5rem"
121
- // fontFamily: "Inter, system-ui, sans-serif",
122
- // monoFamily: "ui-monospace, monospace",
123
- // colorScheme: "system", // or "light" | "dark"
124
- // },
125
- // },
126
- // }),
127
- ui: operatorUi(),
128
- // artifacts: artifactModule,
129
- identity: { credentialAdministration: () => "all", personalConnection: () => "all" },
130
- // Payload-free activity at /activity, off until a database exists to hold
131
- // it. Uncomment the `d1_databases` binding in wrangler.jsonc, apply the
132
- // schema in README.md § "Activity history", then these three lines and the
133
- // import above.
134
- // activity: activityHistory({
135
- // store: d1ActivityStore(env.ACTIVITY_DB),
136
- // deploymentId: "production",
137
- // }),
138
- connectors: [
139
- remoteMcp("notion", {
140
- url: "https://mcp.notion.com/mcp",
141
- description: "Notion — pages, databases, comments (static token)",
142
- auth: {
143
- type: "headers",
144
- headers: { Authorization: `Bearer ${env.DOWNSTREAM_TOKEN}` },
145
- // The vault-backed alternative for a downstream that authenticates
146
- // with a static key: the operator manages it inside its connection
147
- // in the operator UI, so no Worker secret holds it.
148
- // type: "credential",
149
- // credential: { label: "Notion internal integration token" },
150
- },
151
- // Use `authScope: "personal"` with OAuth or credential auth when each
152
- // Access user connects their own downstream account. Literal headers
153
- // are deployment-owned and cannot be personal.
154
- }),
155
- api("echo", {
156
- description: "Echo — text transforms",
157
- // What a vault-backed connector adds — an operator edits this slot
158
- // inside its connection in the operator UI and the handler reads it with
159
- // `await ctx.credential?.get()`, so the secret never lives in source
160
- // or in a Worker variable:
161
- // credential: { label: "API token" },
162
- tools: [
163
- {
164
- name: "shout",
165
- description: "Uppercase the given text.",
166
- inputSchema: {
167
- type: "object",
168
- properties: {
169
- text: { type: "string", description: "Text to uppercase." },
170
- },
171
- required: ["text"],
172
- },
173
- annotations: { readOnlyHint: true },
174
- handler: async (args: { text: string }) => ({
175
- shouted: args.text.toUpperCase(),
176
- }),
177
- },
178
- ],
179
- }),
180
- ],
181
- });
182
- }
5
+ import { createConnecta, type Connecta } from "@zackbart/connecta";
6
+ import connectaConfig, { type Env } from "./connecta.config.js";
183
7
 
184
8
  // Lazy per-isolate singleton: reuses the plain-data tool cache. Downstream MCP
185
9
  // clients are request-scoped internally so Worker I/O never crosses requests.
186
- let connecta: ReturnType<typeof build> | undefined;
187
- const scheduledArtifacts: { module?: { runDue(): Promise<unknown> } } = {};
10
+ let started: Connecta | undefined;
11
+ function start(env: Env) {
12
+ if (!started) {
13
+ const config = connectaConfig(env);
14
+ started = createConnecta(config);
15
+ }
16
+ return started;
17
+ }
188
18
 
189
19
  export default {
190
- // Pass `ctx` through: connecta hands deferred work (activity sinks) to
191
- // ctx.waitUntil so it settles after the response is returned instead of
192
- // being cancelled with the request.
193
- async fetch(
194
- request: Request,
195
- env: Env,
196
- ctx: ExecutionContext,
197
- ): Promise<Response> {
198
- connecta ??= build(env);
199
- return connecta.fetch(request, env, ctx);
200
- },
201
- async scheduled(_event: ScheduledController, env: Env): Promise<void> {
202
- connecta ??= build(env);
203
- await scheduledArtifacts.module?.runDue();
204
- },
20
+ // Pass `ctx` through: deferred work (activity sinks) settles on ctx.waitUntil.
21
+ fetch: (request: Request, env: Env, ctx: ExecutionContext) => start(env).fetch(request, env, ctx),
205
22
  };
@@ -6,14 +6,13 @@
6
6
  "compatibility_flags": ["nodejs_compat", "enable_request_signal"],
7
7
  "observability": { "enabled": true },
8
8
 
9
- // Uncomment with artifacts and D1 in src/index.ts. Every hourly tick runs
10
- // at most 10 due pages; each page's own daily/weekly schedule lives in data.
11
- // "triggers": { "crons": ["0 * * * *"] },
12
-
13
9
  // Plain vars. Secrets (CREDENTIAL_ENCRYPTION_KEY, DOWNSTREAM_TOKEN) are set with
14
10
  // `wrangler secret put <NAME>`, not here.
11
+ // CONNECTA_ACTIVITY switches on activity history in src/connecta.config.ts
12
+ // when "on"; its data stays in CONNECTA_DB.
15
13
  "vars": {
16
- "PUBLIC_URL": "https://connecta.example.workers.dev"
14
+ // "CONNECTA_ACTIVITY": "on",
15
+ "PUBLIC_URL": "https://connecta.example.workers.dev",
17
16
  },
18
17
 
19
18
  // Local-only Access identity. Remove this block to exercise the
@@ -22,39 +21,20 @@
22
21
  "access": {
23
22
  "dev": {
24
23
  "aud": "connecta-local",
25
- "identity": { "email": "operator@example.com" }
26
- }
24
+ "identity": { "email": "operator@example.com" },
25
+ },
27
26
  },
28
27
 
29
- // Create with `wrangler kv namespace create CONNECTA_KV` and paste the id.
30
- "kv_namespaces": [
31
- { "binding": "CONNECTA_KV", "id": "replace-with-kv-namespace-id" }
28
+ // The one database. Create with `wrangler d1 create connecta` and paste
29
+ // the id. connecta creates its tables on first use; nothing to apply.
30
+ "d1_databases": [
31
+ {
32
+ "binding": "CONNECTA_DB",
33
+ "database_name": "connecta",
34
+ "database_id": "replace-with-d1-database-id",
35
+ },
32
36
  ],
33
37
 
34
- // D1 databases, off by default so this example deploys without one.
35
- // Uncomment the array and keep the entries you use.
36
- //
37
- // ACTIVITY_DB: activity history. Create with `wrangler d1 create
38
- // connecta-activity`, paste the id, apply the schema in README.md, then
39
- // uncomment the matching lines in src/index.ts.
40
- //
41
- // STORAGE_DB: strongly consistent storage with compare-and-set, in place of
42
- // CONNECTA_KV. Create with `wrangler d1 create connecta-storage`, paste the
43
- // id, apply the schema in README.md § "Strongly consistent storage", then
44
- // pass `d1Storage(env.STORAGE_DB)` as `storage` in src/index.ts.
45
- // "d1_databases": [
46
- // {
47
- // "binding": "ACTIVITY_DB",
48
- // "database_name": "connecta-activity",
49
- // "database_id": "replace-with-d1-database-id"
50
- // },
51
- // {
52
- // "binding": "STORAGE_DB",
53
- // "database_name": "connecta-storage",
54
- // "database_id": "replace-with-d1-database-id"
55
- // }
56
- // ],
57
-
58
38
  // Required by execute_code; Dynamic Workers use the Workers Paid plan.
59
- "worker_loaders": [{ "binding": "LOADER" }]
39
+ "worker_loaders": [{ "binding": "LOADER" }],
60
40
  }