devflare 1.0.0-next.2 → 1.0.0-next.21
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.
- package/LLM.md +15743 -1751
- package/README.md +383 -605
- package/bin/devflare.js +14 -4
- package/dist/account-05zgta47.js +475 -0
- package/dist/account-0evqkmnc.js +475 -0
- package/dist/account-0v11zbzk.js +475 -0
- package/dist/account-2w85efas.js +475 -0
- package/dist/account-61gw9s7t.js +475 -0
- package/dist/account-65sk9q34.js +475 -0
- package/dist/account-77vx7q2f.js +475 -0
- package/dist/account-7z38mgqf.js +475 -0
- package/dist/account-8wgprpbt.js +475 -0
- package/dist/account-9gt3tej1.js +475 -0
- package/dist/account-b2ag1esh.js +475 -0
- package/dist/account-bxtcz61a.js +475 -0
- package/dist/account-d43d1eqs.js +475 -0
- package/dist/account-dtndtbad.js +475 -0
- package/dist/account-e95v7f4s.js +475 -0
- package/dist/account-fbyyptg7.js +475 -0
- package/dist/account-gnzhz1c9.js +475 -0
- package/dist/account-gyfqg964.js +475 -0
- package/dist/account-m6jq6fnp.js +475 -0
- package/dist/account-mvnfagh9.js +475 -0
- package/dist/account-p0crq2gh.js +475 -0
- package/dist/account-q6pvs9d9.js +475 -0
- package/dist/account-qkcevx7a.js +475 -0
- package/dist/account-rp4zbvw1.js +475 -0
- package/dist/account-txwqg1rx.js +475 -0
- package/dist/account-w5n5bjh2.js +475 -0
- package/dist/account-zd61t0ey.js +475 -0
- package/dist/api-11gkk043.js +33 -0
- package/dist/api-2n6dd2ws.js +33 -0
- package/dist/api-dbxvch9h.js +33 -0
- package/dist/api-p9hf69kr.js +33 -0
- package/dist/api-r10j00ev.js +33 -0
- package/dist/api-senf2569.js +33 -0
- package/dist/api-swcgnp72.js +33 -0
- package/dist/api-th82mps4.js +27 -0
- package/dist/bridge/client.d.ts +34 -6
- package/dist/bridge/client.d.ts.map +1 -1
- package/dist/bridge/gateway-runtime.d.ts +8 -0
- package/dist/bridge/gateway-runtime.d.ts.map +1 -0
- package/dist/bridge/index.d.ts +3 -3
- package/dist/bridge/index.d.ts.map +1 -1
- package/dist/bridge/log.d.ts +5 -0
- package/dist/bridge/log.d.ts.map +1 -0
- package/dist/bridge/miniflare.d.ts +73 -0
- package/dist/bridge/miniflare.d.ts.map +1 -1
- package/dist/bridge/protocol.d.ts +1 -1
- package/dist/bridge/protocol.d.ts.map +1 -1
- package/dist/bridge/proxy.d.ts +9 -11
- package/dist/bridge/proxy.d.ts.map +1 -1
- package/dist/bridge/serialization.d.ts +36 -16
- package/dist/bridge/serialization.d.ts.map +1 -1
- package/dist/bridge/server.d.ts +2 -1
- package/dist/bridge/server.d.ts.map +1 -1
- package/dist/bridge/v2/body-streams.d.ts +53 -0
- package/dist/bridge/v2/body-streams.d.ts.map +1 -0
- package/dist/bridge/v2/codec.d.ts +106 -0
- package/dist/bridge/v2/codec.d.ts.map +1 -0
- package/dist/bridge/v2/control-messages.d.ts +51 -0
- package/dist/bridge/v2/control-messages.d.ts.map +1 -0
- package/dist/bridge/v2/frames.d.ts +109 -0
- package/dist/bridge/v2/frames.d.ts.map +1 -0
- package/dist/bridge/v2/index.d.ts +11 -0
- package/dist/bridge/v2/index.d.ts.map +1 -0
- package/dist/bridge/v2/legacy-protocol.d.ts +146 -0
- package/dist/bridge/v2/legacy-protocol.d.ts.map +1 -0
- package/dist/bridge/v2/legacy-serialization.d.ts +103 -0
- package/dist/bridge/v2/legacy-serialization.d.ts.map +1 -0
- package/dist/bridge/v2/serialization.d.ts +48 -0
- package/dist/bridge/v2/serialization.d.ts.map +1 -0
- package/dist/bridge/v2/transport.d.ts +31 -0
- package/dist/bridge/v2/transport.d.ts.map +1 -0
- package/dist/bridge/v2/value-codec.d.ts +61 -0
- package/dist/bridge/v2/value-codec.d.ts.map +1 -0
- package/dist/bridge/v2/value-serialization.d.ts +103 -0
- package/dist/bridge/v2/value-serialization.d.ts.map +1 -0
- package/dist/bridge/v2/wire.d.ts +170 -0
- package/dist/bridge/v2/wire.d.ts.map +1 -0
- package/dist/bridge/v2/ws-relay.d.ts +53 -0
- package/dist/bridge/v2/ws-relay.d.ts.map +1 -0
- package/dist/browser-shim/handler.d.ts +1 -1
- package/dist/browser-shim/handler.d.ts.map +1 -1
- package/dist/browser-shim/server.d.ts +63 -0
- package/dist/browser-shim/server.d.ts.map +1 -1
- package/dist/browser.d.ts +4505 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +145 -0
- package/dist/build-07mjrvsn.js +53 -0
- package/dist/build-0rfr8k1s.js +53 -0
- package/dist/build-1313sg73.js +52 -0
- package/dist/build-1hnntbks.js +53 -0
- package/dist/build-1r3myv0k.js +53 -0
- package/dist/build-28561w5v.js +54 -0
- package/dist/build-2s5paw5p.js +54 -0
- package/dist/build-4c350cp7.js +54 -0
- package/dist/build-50yqpwb3.js +54 -0
- package/dist/build-55z8psd6.js +53 -0
- package/dist/build-5b9wj2p0.js +53 -0
- package/dist/build-5frhzh1m.js +53 -0
- package/dist/build-6ye9vnab.js +52 -0
- package/dist/build-8gv18jrq.js +52 -0
- package/dist/build-9bgqypy6.js +53 -0
- package/dist/build-9em4ymqn.js +52 -0
- package/dist/build-b1z6wqet.js +54 -0
- package/dist/build-e7wym63t.js +54 -0
- package/dist/build-eh3x12qc.js +52 -0
- package/dist/build-emhyycp7.js +53 -0
- package/dist/build-f1m9jn7b.js +53 -0
- package/dist/build-ffe72jjv.js +54 -0
- package/dist/build-fx90y6za.js +53 -0
- package/dist/build-ge6qp3t4.js +54 -0
- package/dist/build-h31egsze.js +53 -0
- package/dist/build-hpczwagb.js +53 -0
- package/dist/build-hpkbanpw.js +54 -0
- package/dist/build-j2re91xe.js +54 -0
- package/dist/build-j78389rg.js +52 -0
- package/dist/build-m3kckn5g.js +53 -0
- package/dist/build-mf3thcjm.js +53 -0
- package/dist/build-n3n5bv37.js +52 -0
- package/dist/build-ngfy4v5x.js +53 -0
- package/dist/build-pr843h0d.js +52 -0
- package/dist/build-qyg2b45p.js +52 -0
- package/dist/build-r6884kyt.js +53 -0
- package/dist/build-re370wkf.js +53 -0
- package/dist/build-ta8c6t11.js +54 -0
- package/dist/build-vvndgwg0.js +52 -0
- package/dist/build-wp559jkf.js +54 -0
- package/dist/build-wvjj8f28.js +54 -0
- package/dist/build-x4r67275.js +52 -0
- package/dist/build-x7maz3eb.js +54 -0
- package/dist/build-xp4pfhvv.js +52 -0
- package/dist/build-y489r2h9.js +54 -0
- package/dist/build-y6g4bvx5.js +52 -0
- package/dist/build-ypg6f2kw.js +54 -0
- package/dist/build-yts8wwgf.js +54 -0
- package/dist/build-yvn0yhwx.js +52 -0
- package/dist/build-yzkdqexs.js +54 -0
- package/dist/build-z4vmhs5c.js +53 -0
- package/dist/bundler/defaults.d.ts +23 -0
- package/dist/bundler/defaults.d.ts.map +1 -0
- package/dist/bundler/do-bundler.d.ts +7 -0
- package/dist/bundler/do-bundler.d.ts.map +1 -1
- package/dist/bundler/index.d.ts +2 -0
- package/dist/bundler/index.d.ts.map +1 -1
- package/dist/bundler/rolldown-shared.d.ts +53 -0
- package/dist/bundler/rolldown-shared.d.ts.map +1 -0
- package/dist/bundler/worker-bundler.d.ts +13 -0
- package/dist/bundler/worker-bundler.d.ts.map +1 -0
- package/dist/bundler/worker-compat.d.ts +4 -0
- package/dist/bundler/worker-compat.d.ts.map +1 -0
- package/dist/cli/build-manifest.d.ts +66 -0
- package/dist/cli/build-manifest.d.ts.map +1 -0
- package/dist/cli/command-utils.d.ts +18 -0
- package/dist/cli/command-utils.d.ts.map +1 -0
- package/dist/cli/commands/account.d.ts +1 -1
- package/dist/cli/commands/account.d.ts.map +1 -1
- package/dist/cli/commands/build-artifacts.d.ts +40 -0
- package/dist/cli/commands/build-artifacts.d.ts.map +1 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/config.d.ts +4 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/deploy/metadata.d.ts +18 -0
- package/dist/cli/commands/deploy/metadata.d.ts.map +1 -0
- package/dist/cli/commands/deploy/prepare.d.ts +23 -0
- package/dist/cli/commands/deploy/prepare.d.ts.map +1 -0
- package/dist/cli/commands/deploy/runtime.d.ts +4 -0
- package/dist/cli/commands/deploy/runtime.d.ts.map +1 -0
- package/dist/cli/commands/deploy/verification.d.ts +36 -0
- package/dist/cli/commands/deploy/verification.d.ts.map +1 -0
- package/dist/cli/commands/deploy.d.ts +2 -2
- package/dist/cli/commands/deploy.d.ts.map +1 -1
- package/dist/cli/commands/dev.d.ts +1 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/login.d.ts +4 -0
- package/dist/cli/commands/login.d.ts.map +1 -0
- package/dist/cli/commands/previews-support/cleanup.d.ts +9 -0
- package/dist/cli/commands/previews-support/cleanup.d.ts.map +1 -0
- package/dist/cli/commands/previews-support/family.d.ts +10 -0
- package/dist/cli/commands/previews-support/family.d.ts.map +1 -0
- package/dist/cli/commands/previews-support/render.d.ts +8 -0
- package/dist/cli/commands/previews-support/render.d.ts.map +1 -0
- package/dist/cli/commands/previews-support/theme.d.ts +10 -0
- package/dist/cli/commands/previews-support/theme.d.ts.map +1 -0
- package/dist/cli/commands/previews-support/types.d.ts +70 -0
- package/dist/cli/commands/previews-support/types.d.ts.map +1 -0
- package/dist/cli/commands/previews.d.ts +4 -0
- package/dist/cli/commands/previews.d.ts.map +1 -0
- package/dist/cli/commands/productions.d.ts +4 -0
- package/dist/cli/commands/productions.d.ts.map +1 -0
- package/dist/cli/commands/secrets.d.ts +4 -0
- package/dist/cli/commands/secrets.d.ts.map +1 -0
- package/dist/cli/commands/token.d.ts +4 -0
- package/dist/cli/commands/token.d.ts.map +1 -0
- package/dist/cli/commands/type-generation/discovery.d.ts +7 -0
- package/dist/cli/commands/type-generation/discovery.d.ts.map +1 -0
- package/dist/cli/commands/type-generation/generator.d.ts +110 -0
- package/dist/cli/commands/type-generation/generator.d.ts.map +1 -0
- package/dist/cli/commands/type-generation/models.d.ts +27 -0
- package/dist/cli/commands/type-generation/models.d.ts.map +1 -0
- package/dist/cli/commands/types.d.ts.map +1 -1
- package/dist/cli/commands/worker.d.ts +4 -0
- package/dist/cli/commands/worker.d.ts.map +1 -0
- package/dist/cli/config-path.d.ts +6 -0
- package/dist/cli/config-path.d.ts.map +1 -0
- package/dist/cli/dependencies.d.ts +1 -0
- package/dist/cli/dependencies.d.ts.map +1 -1
- package/dist/cli/deploy-strategy.d.ts +17 -0
- package/dist/cli/deploy-strategy.d.ts.map +1 -0
- package/dist/cli/deploy-target.d.ts +17 -0
- package/dist/cli/deploy-target.d.ts.map +1 -0
- package/dist/cli/generated-artifacts.d.ts +12 -0
- package/dist/cli/generated-artifacts.d.ts.map +1 -0
- package/dist/cli/help-pages/pages/account.d.ts +3 -0
- package/dist/cli/help-pages/pages/account.d.ts.map +1 -0
- package/dist/cli/help-pages/pages/core.d.ts +4 -0
- package/dist/cli/help-pages/pages/core.d.ts.map +1 -0
- package/dist/cli/help-pages/pages/index.d.ts +3 -0
- package/dist/cli/help-pages/pages/index.d.ts.map +1 -0
- package/dist/cli/help-pages/pages/misc.d.ts +3 -0
- package/dist/cli/help-pages/pages/misc.d.ts.map +1 -0
- package/dist/cli/help-pages/pages/previews.d.ts +3 -0
- package/dist/cli/help-pages/pages/previews.d.ts.map +1 -0
- package/dist/cli/help-pages/pages/productions.d.ts +3 -0
- package/dist/cli/help-pages/pages/productions.d.ts.map +1 -0
- package/dist/cli/help-pages/render.d.ts +12 -0
- package/dist/cli/help-pages/render.d.ts.map +1 -0
- package/dist/cli/help-pages/shared.d.ts +15 -0
- package/dist/cli/help-pages/shared.d.ts.map +1 -0
- package/dist/cli/help-pages/types.d.ts +23 -0
- package/dist/cli/help-pages/types.d.ts.map +1 -0
- package/dist/cli/help.d.ts +6 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +12 -0
- package/dist/cli/package-metadata.d.ts +16 -0
- package/dist/cli/package-metadata.d.ts.map +1 -0
- package/dist/cli/preview-bindings.d.ts +49 -0
- package/dist/cli/preview-bindings.d.ts.map +1 -0
- package/dist/cli/preview.d.ts +10 -0
- package/dist/cli/preview.d.ts.map +1 -0
- package/dist/cli/ui.d.ts +37 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/workspace-build-guard.d.ts +14 -0
- package/dist/cli/workspace-build-guard.d.ts.map +1 -0
- package/dist/cloudflare/account-core.d.ts +6 -0
- package/dist/cloudflare/account-core.d.ts.map +1 -0
- package/dist/cloudflare/account-resources.d.ts +40 -0
- package/dist/cloudflare/account-resources.d.ts.map +1 -0
- package/dist/cloudflare/account-status.d.ts +11 -0
- package/dist/cloudflare/account-status.d.ts.map +1 -0
- package/dist/cloudflare/account-workers.d.ts +14 -0
- package/dist/cloudflare/account-workers.d.ts.map +1 -0
- package/dist/cloudflare/account.d.ts +7 -64
- package/dist/cloudflare/account.d.ts.map +1 -1
- package/dist/cloudflare/api.d.ts +71 -3
- package/dist/cloudflare/api.d.ts.map +1 -1
- package/dist/cloudflare/auth.d.ts.map +1 -1
- package/dist/cloudflare/index.d.ts +57 -2
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/index.js +49 -2
- package/dist/cloudflare/known-permission-group-ids.generated.d.ts +10 -0
- package/dist/cloudflare/known-permission-group-ids.generated.d.ts.map +1 -0
- package/dist/cloudflare/kv-namespace.d.ts +4 -0
- package/dist/cloudflare/kv-namespace.d.ts.map +1 -0
- package/dist/cloudflare/preferences.d.ts +7 -0
- package/dist/cloudflare/preferences.d.ts.map +1 -1
- package/dist/cloudflare/preview-registry-cache.d.ts +6 -0
- package/dist/cloudflare/preview-registry-cache.d.ts.map +1 -0
- package/dist/cloudflare/preview-registry-inference.d.ts +14 -0
- package/dist/cloudflare/preview-registry-inference.d.ts.map +1 -0
- package/dist/cloudflare/preview-registry-records.d.ts +4 -0
- package/dist/cloudflare/preview-registry-records.d.ts.map +1 -0
- package/dist/cloudflare/preview-registry-shape.d.ts +47 -0
- package/dist/cloudflare/preview-registry-shape.d.ts.map +1 -0
- package/dist/cloudflare/preview-registry-store.d.ts +14 -0
- package/dist/cloudflare/preview-registry-store.d.ts.map +1 -0
- package/dist/cloudflare/preview-registry-transport.d.ts +4 -0
- package/dist/cloudflare/preview-registry-transport.d.ts.map +1 -0
- package/dist/cloudflare/preview-registry-types.d.ts +103 -0
- package/dist/cloudflare/preview-registry-types.d.ts.map +1 -0
- package/dist/cloudflare/preview-registry.d.ts +42 -0
- package/dist/cloudflare/preview-registry.d.ts.map +1 -0
- package/dist/cloudflare/preview-urls.d.ts +3 -0
- package/dist/cloudflare/preview-urls.d.ts.map +1 -0
- package/dist/cloudflare/registry-schema.d.ts +253 -0
- package/dist/cloudflare/registry-schema.d.ts.map +1 -0
- package/dist/cloudflare/tokens.d.ts +41 -0
- package/dist/cloudflare/tokens.d.ts.map +1 -0
- package/dist/cloudflare/types.d.ts +124 -7
- package/dist/cloudflare/types.d.ts.map +1 -1
- package/dist/cloudflare/usage.d.ts +30 -2
- package/dist/cloudflare/usage.d.ts.map +1 -1
- package/dist/config/binding-resolution-helpers.d.ts +41 -0
- package/dist/config/binding-resolution-helpers.d.ts.map +1 -0
- package/dist/config/compatibility.d.ts +3 -0
- package/dist/config/compatibility.d.ts.map +1 -0
- package/dist/config/compiler/bindings.d.ts +14 -0
- package/dist/config/compiler/bindings.d.ts.map +1 -0
- package/dist/config/compiler/core-helpers.d.ts +6 -0
- package/dist/config/compiler/core-helpers.d.ts.map +1 -0
- package/dist/config/compiler/do-workers.d.ts +34 -0
- package/dist/config/compiler/do-workers.d.ts.map +1 -0
- package/dist/config/compiler/paths.d.ts +18 -0
- package/dist/config/compiler/paths.d.ts.map +1 -0
- package/dist/config/compiler/types.d.ts +267 -0
- package/dist/config/compiler/types.d.ts.map +1 -0
- package/dist/config/compiler.d.ts +22 -134
- package/dist/config/compiler.d.ts.map +1 -1
- package/dist/config/deploy-resources.d.ts +58 -0
- package/dist/config/deploy-resources.d.ts.map +1 -0
- package/dist/config/framework-providers.d.ts +9 -0
- package/dist/config/framework-providers.d.ts.map +1 -0
- package/dist/config/index.d.ts +10 -4
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/loader.d.ts +1 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/local-dev-vars.d.ts +15 -0
- package/dist/config/local-dev-vars.d.ts.map +1 -0
- package/dist/config/preview-resources.d.ts +83 -0
- package/dist/config/preview-resources.d.ts.map +1 -0
- package/dist/config/preview.d.ts +31 -0
- package/dist/config/preview.d.ts.map +1 -0
- package/dist/config/ref.d.ts +14 -23
- package/dist/config/ref.d.ts.map +1 -1
- package/dist/config/resolve-phased.d.ts +72 -0
- package/dist/config/resolve-phased.d.ts.map +1 -0
- package/dist/config/resolve.d.ts +11 -0
- package/dist/config/resolve.d.ts.map +1 -0
- package/dist/config/resource-resolution.d.ts +80 -0
- package/dist/config/resource-resolution.d.ts.map +1 -0
- package/dist/config/schema-bindings.d.ts +1495 -0
- package/dist/config/schema-bindings.d.ts.map +1 -0
- package/dist/config/schema-build.d.ts +67 -0
- package/dist/config/schema-build.d.ts.map +1 -0
- package/dist/config/schema-env.d.ts +2949 -0
- package/dist/config/schema-env.d.ts.map +1 -0
- package/dist/config/schema-normalization.d.ts +184 -0
- package/dist/config/schema-normalization.d.ts.map +1 -0
- package/dist/config/schema-runtime.d.ts +468 -0
- package/dist/config/schema-runtime.d.ts.map +1 -0
- package/dist/config/schema.d.ts +4056 -1875
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/service-bindings-validation.d.ts +36 -0
- package/dist/config/service-bindings-validation.d.ts.map +1 -0
- package/dist/config-1japnjmx.js +59 -0
- package/dist/config-287wjn1m.js +59 -0
- package/dist/config-4x6vq82e.js +59 -0
- package/dist/config-5n0avhcx.js +59 -0
- package/dist/config-6m0n7d84.js +59 -0
- package/dist/config-70s2ap5j.js +59 -0
- package/dist/config-7cf004ag.js +59 -0
- package/dist/config-7hge8ze6.js +59 -0
- package/dist/config-7rbp7aek.js +59 -0
- package/dist/config-b8xbbmta.js +59 -0
- package/dist/config-b98dp58n.js +59 -0
- package/dist/config-baee5690.js +59 -0
- package/dist/config-bczpc1ey.js +59 -0
- package/dist/config-cf3djhqy.js +59 -0
- package/dist/config-entry.d.ts +5 -0
- package/dist/config-entry.d.ts.map +1 -0
- package/dist/config-entry.js +14 -0
- package/dist/config-fhmwzaw6.js +59 -0
- package/dist/config-g9vkfa4x.js +59 -0
- package/dist/config-gpexkzth.js +59 -0
- package/dist/config-nspw7sgv.js +59 -0
- package/dist/config-ntm4x9wt.js +59 -0
- package/dist/config-nv6hfpg6.js +59 -0
- package/dist/config-pjc33wew.js +59 -0
- package/dist/config-pnz2e8s3.js +59 -0
- package/dist/config-qj5jw8km.js +93 -0
- package/dist/config-qts6nh6g.js +59 -0
- package/dist/config-sg2xrdt4.js +59 -0
- package/dist/config-vzwckat3.js +59 -0
- package/dist/config-wa7hm0w9.js +59 -0
- package/dist/config-x90dkmm7.js +59 -0
- package/dist/decorators/durable-object.d.ts.map +1 -1
- package/dist/deploy-1jfagtn9.js +1055 -0
- package/dist/deploy-1s6nv5rp.js +921 -0
- package/dist/deploy-2afw0jfg.js +1055 -0
- package/dist/deploy-2fzj68kq.js +1055 -0
- package/dist/deploy-30ph1rrd.js +921 -0
- package/dist/deploy-39dmfn9d.js +921 -0
- package/dist/deploy-44n2pshy.js +1016 -0
- package/dist/deploy-45qxpsfe.js +921 -0
- package/dist/deploy-57nzn9wj.js +1045 -0
- package/dist/deploy-64e57w1g.js +1040 -0
- package/dist/deploy-82j43vta.js +1040 -0
- package/dist/deploy-99m8qhpa.js +1041 -0
- package/dist/deploy-9twkd32f.js +1040 -0
- package/dist/deploy-a2eq2c1n.js +1040 -0
- package/dist/deploy-a3pb1qs5.js +921 -0
- package/dist/deploy-agbtfm04.js +921 -0
- package/dist/deploy-aswbtbf3.js +921 -0
- package/dist/deploy-asyryrvm.js +1055 -0
- package/dist/deploy-bqf3kxy5.js +972 -0
- package/dist/deploy-fv4z648h.js +921 -0
- package/dist/deploy-gsybda2f.js +1038 -0
- package/dist/deploy-h17nzysm.js +921 -0
- package/dist/deploy-hc89h4wg.js +1045 -0
- package/dist/deploy-hc927rw6.js +1045 -0
- package/dist/deploy-jey5pnqn.js +1040 -0
- package/dist/deploy-jf3yczsz.js +1055 -0
- package/dist/deploy-jv0yfqvk.js +1040 -0
- package/dist/deploy-kn3z6spd.js +941 -0
- package/dist/deploy-m22xmz9w.js +921 -0
- package/dist/deploy-nefp2njy.js +1038 -0
- package/dist/deploy-pnnf8tgy.js +1045 -0
- package/dist/deploy-ppp8pg6w.js +1038 -0
- package/dist/deploy-q33bw715.js +1055 -0
- package/dist/deploy-qah2eag6.js +1041 -0
- package/dist/deploy-qdq2gpad.js +1045 -0
- package/dist/deploy-r1yf8913.js +1038 -0
- package/dist/deploy-r8nam9rr.js +1038 -0
- package/dist/deploy-rrm4a0xw.js +921 -0
- package/dist/deploy-s0tfnc12.js +1040 -0
- package/dist/deploy-s636ks9k.js +1041 -0
- package/dist/deploy-sany8t54.js +1045 -0
- package/dist/deploy-skqn61j3.js +1038 -0
- package/dist/deploy-smv2870h.js +921 -0
- package/dist/deploy-sp16nxs5.js +972 -0
- package/dist/deploy-sr805bvq.js +921 -0
- package/dist/deploy-t9dehmn0.js +1041 -0
- package/dist/deploy-taxt7njz.js +1041 -0
- package/dist/deploy-tmdgecs3.js +1055 -0
- package/dist/deploy-tnfvfh9m.js +1040 -0
- package/dist/deploy-v0y8kczr.js +1055 -0
- package/dist/deploy-w5f8db4s.js +1040 -0
- package/dist/deploy-wsxcectr.js +921 -0
- package/dist/deploy-xhj6zbcx.js +1055 -0
- package/dist/deploy-xqm869nf.js +1055 -0
- package/dist/deploy-xyfbhj3v.js +1038 -0
- package/dist/deploy-yv1y5xaa.js +921 -0
- package/dist/deploy-z3ejw37k.js +1040 -0
- package/dist/dev-02tgdc4f.js +2160 -0
- package/dist/dev-0tqhveye.js +2314 -0
- package/dist/dev-1mvcts8w.js +2515 -0
- package/dist/dev-2a283xts.js +2515 -0
- package/dist/dev-3dj2gkfq.js +2314 -0
- package/dist/dev-3e0etsww.js +2314 -0
- package/dist/dev-47dk7c13.js +2314 -0
- package/dist/dev-4a2a31cm.js +2314 -0
- package/dist/dev-4w1stcn8.js +2427 -0
- package/dist/dev-5wkh1z4a.js +2188 -0
- package/dist/dev-62nhytf8.js +2505 -0
- package/dist/dev-6t22zmey.js +2350 -0
- package/dist/dev-75acm2xj.js +2478 -0
- package/dist/dev-802rg9dp.js +2515 -0
- package/dist/dev-89p1sdv7.js +2395 -0
- package/dist/dev-8d1gxjky.js +2427 -0
- package/dist/dev-8yawx3cv.js +2314 -0
- package/dist/dev-9h4p8a0c.js +2393 -0
- package/dist/dev-9xmttj4c.js +2314 -0
- package/dist/dev-at6gy2td.js +2427 -0
- package/dist/dev-bgpxrwms.js +2551 -0
- package/dist/dev-bkbz97z0.js +2314 -0
- package/dist/dev-ct3344kv.js +2160 -0
- package/dist/dev-d1bb2t0f.js +2515 -0
- package/dist/{dev-qnxet3j9.js → dev-dg8dh5c1.js} +947 -809
- package/dist/dev-dr5jb965.js +2427 -0
- package/dist/dev-dwry8494.js +2489 -0
- package/dist/dev-e4sjqtbq.js +2314 -0
- package/dist/dev-ecq26mg4.js +2350 -0
- package/dist/dev-ef237vgp.js +2324 -0
- package/dist/dev-enbdg4st.js +2314 -0
- package/dist/dev-g6112y4w.js +2515 -0
- package/dist/dev-h2kneh95.js +2496 -0
- package/dist/dev-jcrxg8hq.js +2160 -0
- package/dist/dev-jk0gqges.js +2432 -0
- package/dist/dev-k62936am.js +2314 -0
- package/dist/dev-k8ptbvrs.js +2160 -0
- package/dist/dev-kqm107h4.js +2422 -0
- package/dist/dev-kybq3mwr.js +2489 -0
- package/dist/dev-kzs65xcr.js +2551 -0
- package/dist/dev-n0gx7eya.js +2350 -0
- package/dist/dev-n23m6rtb.js +2314 -0
- package/dist/dev-n8qndkyg.js +2512 -0
- package/dist/dev-ny8fmkzg.js +2432 -0
- package/dist/dev-p1c65cga.js +2386 -0
- package/dist/dev-p32fkbwf.js +2489 -0
- package/dist/dev-p4fjbb4h.js +2395 -0
- package/dist/dev-pnfcv79z.js +2160 -0
- package/dist/dev-qm9d4mfh.js +2478 -0
- package/dist/dev-qnrrv5ey.js +2393 -0
- package/dist/dev-qqysg50t.js +2427 -0
- package/dist/dev-r9f3n11q.js +2410 -0
- package/dist/dev-rcthnse5.js +2473 -0
- package/dist/dev-s0815dw4.js +2427 -0
- package/dist/dev-s4tpkg60.js +2314 -0
- package/dist/dev-s82fcesg.js +2314 -0
- package/dist/dev-s9sz53me.js +2401 -0
- package/dist/dev-sdka7z0d.js +2427 -0
- package/dist/dev-server/d1-migrations.d.ts +36 -0
- package/dist/dev-server/d1-migrations.d.ts.map +1 -0
- package/dist/dev-server/dev-server-state.d.ts +59 -0
- package/dist/dev-server/dev-server-state.d.ts.map +1 -0
- package/dist/dev-server/gateway-script.d.ts +21 -0
- package/dist/dev-server/gateway-script.d.ts.map +1 -0
- package/dist/dev-server/miniflare-bindings.d.ts +59 -0
- package/dist/dev-server/miniflare-bindings.d.ts.map +1 -0
- package/dist/dev-server/miniflare-dev-config.d.ts +35 -0
- package/dist/dev-server/miniflare-dev-config.d.ts.map +1 -0
- package/dist/dev-server/miniflare-log.d.ts +20 -0
- package/dist/dev-server/miniflare-log.d.ts.map +1 -0
- package/dist/dev-server/miniflare-worker-config.d.ts +74 -0
- package/dist/dev-server/miniflare-worker-config.d.ts.map +1 -0
- package/dist/dev-server/reload-queue.d.ts +13 -0
- package/dist/dev-server/reload-queue.d.ts.map +1 -0
- package/dist/dev-server/runtime-stdio.d.ts +8 -0
- package/dist/dev-server/runtime-stdio.d.ts.map +1 -0
- package/dist/dev-server/server-startup-helpers.d.ts +84 -0
- package/dist/dev-server/server-startup-helpers.d.ts.map +1 -0
- package/dist/dev-server/server.d.ts +2 -0
- package/dist/dev-server/server.d.ts.map +1 -1
- package/dist/dev-server/vite-process.d.ts +15 -0
- package/dist/dev-server/vite-process.d.ts.map +1 -0
- package/dist/dev-server/vite-utils.d.ts +55 -0
- package/dist/dev-server/vite-utils.d.ts.map +1 -0
- package/dist/dev-server/worker-source-watcher.d.ts +17 -0
- package/dist/dev-server/worker-source-watcher.d.ts.map +1 -0
- package/dist/dev-server/worker-surface-paths.d.ts +6 -0
- package/dist/dev-server/worker-surface-paths.d.ts.map +1 -0
- package/dist/dev-t3tjscah.js +2350 -0
- package/dist/dev-t50kdb1j.js +2338 -0
- package/dist/dev-t5xf55pt.js +2314 -0
- package/dist/dev-t63c44bb.js +2314 -0
- package/dist/dev-tgwja5mz.js +2496 -0
- package/dist/dev-wg3d38sy.js +2433 -0
- package/dist/dev-wh4rfgsh.js +2427 -0
- package/dist/dev-wjh8gdd1.js +2284 -0
- package/dist/dev-xzxy9qvq.js +2314 -0
- package/dist/dev-yymaq4ph.js +2392 -0
- package/dist/dev-zg2w2ve3.js +2422 -0
- package/dist/dev-zgx7fhe9.js +2553 -0
- package/dist/dev-zsb2xk1x.js +2427 -0
- package/dist/dev-zty3yhzg.js +2324 -0
- package/dist/doctor-0a2brpyz.js +259 -0
- package/dist/doctor-2shhdak6.js +245 -0
- package/dist/doctor-3mqavqg3.js +245 -0
- package/dist/doctor-5g73w40j.js +245 -0
- package/dist/doctor-5nek32fz.js +245 -0
- package/dist/doctor-7ra1w0sk.js +245 -0
- package/dist/doctor-ak830a0k.js +245 -0
- package/dist/doctor-bye3cs8a.js +245 -0
- package/dist/doctor-c5m9pmwq.js +245 -0
- package/dist/doctor-cqfkgxvw.js +245 -0
- package/dist/doctor-e1h9mhzp.js +245 -0
- package/dist/doctor-fmqsrafk.js +245 -0
- package/dist/doctor-gamefzcs.js +245 -0
- package/dist/doctor-hgs7wr5g.js +245 -0
- package/dist/doctor-j8ytsy94.js +245 -0
- package/dist/doctor-mcmkgp6e.js +245 -0
- package/dist/doctor-qb6m2exc.js +245 -0
- package/dist/doctor-qq4tsh8q.js +245 -0
- package/dist/doctor-rn53ctfs.js +245 -0
- package/dist/doctor-tecv869a.js +245 -0
- package/dist/doctor-wg0245pe.js +245 -0
- package/dist/doctor-z3jfqxtz.js +245 -0
- package/dist/{durable-object-t4kbb0yt.js → durable-object-4hey8fgy.js} +1 -1
- package/dist/durable-object-v3gsnybk.js +13 -0
- package/dist/durable-object-yt8v1dyn.js +13 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/index-01kehw41.js +348 -0
- package/dist/index-05pbj4hy.js +1193 -0
- package/dist/index-06bg0z9y.js +185 -0
- package/dist/index-0d7tw5r4.js +136 -0
- package/dist/index-0krb65s3.js +215 -0
- package/dist/index-0m6e4mxz.js +133 -0
- package/dist/index-0mantew0.js +513 -0
- package/dist/index-0mezg6ar.js +1372 -0
- package/dist/index-0tk2rs0q.js +185 -0
- package/dist/index-0vah20er.js +1410 -0
- package/dist/index-0wa0ebm1.js +68 -0
- package/dist/index-124e9t4t.js +232 -0
- package/dist/index-1546yrn0.js +74 -0
- package/dist/index-15fpa5tx.js +232 -0
- package/dist/index-1714y3cz.js +1410 -0
- package/dist/index-18mp5v50.js +538 -0
- package/dist/index-195tc6qh.js +1372 -0
- package/dist/index-1bg8w2gf.js +52 -0
- package/dist/index-1d4jg11n.js +542 -0
- package/dist/index-1e2qtrmj.js +188 -0
- package/dist/index-1ghhgb9d.js +74 -0
- package/dist/index-1nhd9nkd.js +232 -0
- package/dist/index-1p814k7s.js +227 -0
- package/dist/index-1qs5gcm7.js +895 -0
- package/dist/index-1wn8mjep.js +808 -0
- package/dist/index-1xhd5czz.js +185 -0
- package/dist/index-29k04v43.js +574 -0
- package/dist/index-2a2g4fwp.js +993 -0
- package/dist/index-2bmtj21z.js +192 -0
- package/dist/index-2dpmd14d.js +52 -0
- package/dist/index-2fa0afdp.js +1372 -0
- package/dist/index-2jywf4pz.js +1372 -0
- package/dist/index-2qhk9nbx.js +1372 -0
- package/dist/index-2rcrefqd.js +1372 -0
- package/dist/index-2t2m5148.js +1372 -0
- package/dist/index-2vbzamev.js +437 -0
- package/dist/index-2vdep9h0.js +993 -0
- package/dist/index-2vq6bveq.js +574 -0
- package/dist/index-2y90j9cz.js +74 -0
- package/dist/index-33ep0z9x.js +385 -0
- package/dist/index-36h8gkhb.js +1088 -0
- package/dist/index-37jgbaw6.js +1372 -0
- package/dist/index-380ncsqc.js +977 -0
- package/dist/index-38fq7pww.js +560 -0
- package/dist/index-3bxqn033.js +1410 -0
- package/dist/index-3e1x39fc.js +365 -0
- package/dist/index-3e7by9sy.js +223 -0
- package/dist/index-3edvz3hs.js +124 -0
- package/dist/index-3egad6wm.js +538 -0
- package/dist/index-3f8rzmjw.js +232 -0
- package/dist/index-3jme4hgw.js +1234 -0
- package/dist/index-3p7s9mk9.js +360 -0
- package/dist/index-3vd4r37r.js +1372 -0
- package/dist/index-3vgn5e89.js +1682 -0
- package/dist/index-3x7k3t0f.js +513 -0
- package/dist/index-3xfe2dst.js +399 -0
- package/dist/index-464j1zb4.js +513 -0
- package/dist/index-47w35sft.js +244 -0
- package/dist/index-48rxsj95.js +365 -0
- package/dist/index-4bezcdd1.js +385 -0
- package/dist/index-4by4c7rm.js +52 -0
- package/dist/index-4fp11z9e.js +973 -0
- package/dist/index-4knecthc.js +70 -0
- package/dist/index-4phjwd6h.js +412 -0
- package/dist/index-4z5jrw0j.js +594 -0
- package/dist/index-502y7w13.js +538 -0
- package/dist/index-50em8s6c.js +898 -0
- package/dist/index-50mxxb4q.js +185 -0
- package/dist/index-51mzqy0d.js +895 -0
- package/dist/index-53pqqpq9.js +74 -0
- package/dist/index-5achzspr.js +185 -0
- package/dist/index-5ef3jmv7.js +1372 -0
- package/dist/index-5enq8ntr.js +1766 -0
- package/dist/index-5eswe8yv.js +844 -0
- package/dist/index-5fnq9r9m.js +1410 -0
- package/dist/index-5gevxfgs.js +513 -0
- package/dist/index-5gmf048z.js +817 -0
- package/dist/index-5jtkxpjf.js +971 -0
- package/dist/index-5m85bd9x.js +168 -0
- package/dist/index-5n5nn0a8.js +264 -0
- package/dist/index-5p3bgjwr.js +808 -0
- package/dist/index-5vzcszr2.js +1230 -0
- package/dist/index-5w77r4w9.js +513 -0
- package/dist/index-5w9f2b17.js +695 -0
- package/dist/index-627srx16.js +45 -0
- package/dist/index-62b3gt2g.js +12 -0
- package/dist/index-62srkqcm.js +168 -0
- package/dist/index-63cgqe43.js +1372 -0
- package/dist/index-65e7xx1a.js +19 -0
- package/dist/index-65qrje14.js +185 -0
- package/dist/index-666tdx14.js +895 -0
- package/dist/index-66sx3b3v.js +385 -0
- package/dist/index-678j0n13.js +74 -0
- package/dist/index-6bqgf5x8.js +227 -0
- package/dist/index-6ftrth7n.js +1218 -0
- package/dist/index-6g90pvxa.js +215 -0
- package/dist/index-6h8xbs75.js +44 -0
- package/dist/index-6j4m5x2d.js +1372 -0
- package/dist/index-6jwpke14.js +385 -0
- package/dist/index-6knsjvxh.js +1372 -0
- package/dist/index-6kwq6yf8.js +979 -0
- package/dist/index-6mg4yvc6.js +74 -0
- package/dist/index-6phry941.js +133 -0
- package/dist/index-6r59wbek.js +232 -0
- package/dist/index-6sf815ps.js +74 -0
- package/dist/index-6xknvbyk.js +1088 -0
- package/dist/index-6zd89k6y.js +1230 -0
- package/dist/index-71rwg0cn.js +264 -0
- package/dist/index-75ex2m0x.js +695 -0
- package/dist/index-79exzs3q.js +1218 -0
- package/dist/index-7bxj118m.js +264 -0
- package/dist/index-7c6np4mq.js +397 -0
- package/dist/index-7cm339qz.js +133 -0
- package/dist/index-7e359eb5.js +895 -0
- package/dist/index-7ef3ktz5.js +1372 -0
- package/dist/index-7eqyr4j8.js +70 -0
- package/dist/index-7hpjfdzh.js +185 -0
- package/dist/index-7k278fgz.js +273 -0
- package/dist/index-7kazxkjt.js +1372 -0
- package/dist/index-7m0x8m9r.js +844 -0
- package/dist/index-7p74smg8.js +1372 -0
- package/dist/index-7q8fke3k.js +188 -0
- package/dist/index-7qk6eafn.js +1682 -0
- package/dist/index-7r7dfpcm.js +133 -0
- package/dist/index-7vbjt6ba.js +805 -0
- package/dist/index-7z4b0vgd.js +470 -0
- package/dist/index-8052df4m.js +627 -0
- package/dist/index-82epjzrr.js +1410 -0
- package/dist/index-82f1z98k.js +41 -0
- package/dist/index-82xn4azv.js +74 -0
- package/dist/index-82z7rvz6.js +1238 -0
- package/dist/index-83wns7cz.js +70 -0
- package/dist/index-852945d4.js +413 -0
- package/dist/index-86rvj79f.js +133 -0
- package/dist/index-88tcq0t4.js +542 -0
- package/dist/index-8atc1yb9.js +68 -0
- package/dist/index-8evn52g1.js +1218 -0
- package/dist/index-8jkf4nsd.js +695 -0
- package/dist/index-8nx4ce3e.js +133 -0
- package/dist/index-8p7rxkbs.js +1426 -0
- package/dist/index-8q1bcnrh.js +1372 -0
- package/dist/index-8t5gynct.js +971 -0
- package/dist/index-8tj0awnv.js +476 -0
- package/dist/index-8x745h59.js +1069 -0
- package/dist/index-905svbb6.js +470 -0
- package/dist/index-94cb0sac.js +1372 -0
- package/dist/index-95mmvbw7.js +538 -0
- package/dist/index-97x94h0m.js +438 -0
- package/dist/index-9an9qqr1.js +728 -0
- package/dist/index-9bawzcny.js +574 -0
- package/dist/index-9bjjqdfc.js +236 -0
- package/dist/index-9d125myk.js +1372 -0
- package/dist/index-9d7x3vfr.js +236 -0
- package/dist/index-9fbtk7gv.js +817 -0
- package/dist/index-9gg0kqw0.js +470 -0
- package/dist/index-9nf8zs4p.js +1069 -0
- package/dist/index-9ryw7mcw.js +971 -0
- package/dist/{index-gz1gndna.js → index-9wt9x09k.js} +42 -62
- package/dist/index-a3e13a7m.js +478 -0
- package/dist/index-a6j44jmr.js +1372 -0
- package/dist/index-a75ejnvt.js +1711 -0
- package/dist/index-aabgympv.js +39 -0
- package/dist/index-ab2nck1h.js +1372 -0
- package/dist/index-abrqnq3e.js +488 -0
- package/dist/index-acwbmagz.js +412 -0
- package/dist/index-afjbpgb1.js +1218 -0
- package/dist/index-aqjdaem7.js +74 -0
- package/dist/index-aqrwyy57.js +288 -0
- package/dist/index-at89fpde.js +470 -0
- package/dist/index-awr0tp6p.js +313 -0
- package/dist/index-b1be9bdh.js +1372 -0
- package/dist/index-b1xs3wc4.js +513 -0
- package/dist/index-b50x7seh.js +513 -0
- package/dist/index-b5ra8w3q.js +52 -0
- package/dist/index-b6448fd0.js +133 -0
- package/dist/index-b64sbzgv.js +884 -0
- package/dist/index-b7r8ntpm.js +1372 -0
- package/dist/index-b9j55r7q.js +240 -0
- package/dist/index-ba9gyy13.js +513 -0
- package/dist/index-bb66mn7p.js +52 -0
- package/dist/index-bc5pvbbd.js +1193 -0
- package/dist/index-bdatd1za.js +1372 -0
- package/dist/index-bj0mk3dw.js +74 -0
- package/dist/index-bj3a8dr0.js +1372 -0
- package/dist/index-bj5avaba.js +109 -0
- package/dist/index-bk3eez1g.js +513 -0
- package/dist/index-btjx137w.js +774 -0
- package/dist/index-btrfqp9h.js +133 -0
- package/dist/index-bx6gangy.js +402 -0
- package/dist/index-c0tc0yts.js +471 -0
- package/dist/index-c0whkev9.js +200 -0
- package/dist/index-c3nxftnp.js +699 -0
- package/dist/index-c643s0gv.js +488 -0
- package/dist/index-ca96qkva.js +1372 -0
- package/dist/index-cdtzkedk.js +993 -0
- package/dist/index-cejgenma.js +1372 -0
- package/dist/index-cp8jt5pr.js +52 -0
- package/dist/index-cpz69pek.js +1372 -0
- package/dist/index-crrdegk5.js +456 -0
- package/dist/index-cskszwkx.js +1372 -0
- package/dist/index-cxf22a0s.js +74 -0
- package/dist/index-cy3c3w81.js +270 -0
- package/dist/index-d2md1j3d.js +185 -0
- package/dist/index-d5dgb1j6.js +74 -0
- package/dist/index-da397xcx.js +1372 -0
- package/dist/index-damyy8bw.js +1372 -0
- package/dist/index-dbr6bfz6.js +528 -0
- package/dist/index-dd1g0g7e.js +360 -0
- package/dist/index-dd6fwwmq.js +456 -0
- package/dist/index-dgww0ewn.js +574 -0
- package/dist/index-dh1954bp.js +52 -0
- package/dist/index-djp2wd1f.js +1372 -0
- package/dist/index-dktb9az5.js +1372 -0
- package/dist/index-dm9q84c7.js +360 -0
- package/dist/index-dmbdj6va.js +805 -0
- package/dist/index-dmf76tac.js +1372 -0
- package/dist/index-dphr32gc.js +420 -0
- package/dist/index-dxh2qyyw.js +52 -0
- package/dist/index-dzayfxjy.js +492 -0
- package/dist/index-e05q77kt.js +397 -0
- package/dist/index-e11b1fb2.js +495 -0
- package/dist/index-e3zvspqj.js +968 -0
- package/dist/index-e4tzzbn9.js +1372 -0
- package/dist/index-e5ct1gwb.js +1372 -0
- package/dist/index-e7ptcz1a.js +1372 -0
- package/dist/index-e7wt0203.js +513 -0
- package/dist/index-ebwsvf9t.js +365 -0
- package/dist/index-ed73jn3d.js +185 -0
- package/dist/index-ed7n5d07.js +185 -0
- package/dist/index-ejs2a4kd.js +513 -0
- package/dist/index-esjrgt3y.js +467 -0
- package/dist/index-ev03khsd.js +513 -0
- package/dist/index-f1gsqm54.js +168 -0
- package/dist/index-f1yshy4s.js +412 -0
- package/dist/index-f51mkh13.js +1088 -0
- package/dist/index-f86n1fpd.js +55 -0
- package/dist/index-f90314kt.js +52 -0
- package/dist/index-f9mhsept.js +185 -0
- package/dist/index-fa6zwffw.js +70 -0
- package/dist/index-fama7kpt.js +52 -0
- package/dist/index-fgk87mk2.js +1067 -0
- package/dist/index-fkyvxe4b.js +968 -0
- package/dist/index-fnk0tkw7.js +412 -0
- package/dist/index-g0h40xke.js +513 -0
- package/dist/index-g0rqh52c.js +385 -0
- package/dist/index-g170d619.js +385 -0
- package/dist/index-g3qtxkms.js +881 -0
- package/dist/index-g5aq66bj.js +1534 -0
- package/dist/index-gactvqcs.js +467 -0
- package/dist/index-gaew61v4.js +470 -0
- package/dist/index-gba371j7.js +542 -0
- package/dist/index-gegyyfnw.js +1372 -0
- package/dist/index-get7he2s.js +133 -0
- package/dist/index-gj5qh491.js +54 -0
- package/dist/index-gn5wy09x.js +205 -0
- package/dist/index-gq39t0rx.js +895 -0
- package/dist/index-gtbgzwfs.js +264 -0
- package/dist/index-gtpz0gkz.js +185 -0
- package/dist/index-h1s66pmp.js +52 -0
- package/dist/index-h5dqna7q.js +1410 -0
- package/dist/index-h8r2d8y5.js +366 -0
- package/dist/index-heeqwrfd.js +385 -0
- package/dist/index-hheb5dyt.js +192 -0
- package/dist/index-hjs9j2g9.js +895 -0
- package/dist/index-hjz2x9nq.js +441 -0
- package/dist/index-hkex7ns5.js +513 -0
- package/dist/index-hmbctwv6.js +517 -0
- package/dist/index-hn5nbxbt.js +147 -0
- package/dist/index-hpjh0qjx.js +1723 -0
- package/dist/index-hpwa6vsw.js +239 -0
- package/dist/index-hpzasdhw.js +513 -0
- package/dist/index-hqdr9wdm.js +74 -0
- package/dist/index-hrvz8d26.js +1372 -0
- package/dist/index-hs6ekcfs.js +412 -0
- package/dist/index-hyvhnf6r.js +971 -0
- package/dist/index-j2kc5rbv.js +895 -0
- package/dist/index-j2xdxwsz.js +133 -0
- package/dist/index-j47hhb3r.js +694 -0
- package/dist/index-j5aphr5x.js +185 -0
- package/dist/index-j68v36q1.js +188 -0
- package/dist/index-j7cf22x7.js +968 -0
- package/dist/index-j7xxrbnr.js +694 -0
- package/dist/index-jdzrvnfj.js +52 -0
- package/dist/index-jg720mq7.js +476 -0
- package/dist/index-jptqxkew.js +470 -0
- package/dist/index-jrzddxvt.js +2167 -0
- package/dist/index-jsymy3mb.js +781 -0
- package/dist/index-jwvy32xk.js +188 -0
- package/dist/index-jxe1b19v.js +133 -0
- package/dist/index-jz96ry5g.js +513 -0
- package/dist/index-k6wtsgs1.js +971 -0
- package/dist/index-k7m5f1dg.js +200 -0
- package/dist/index-kawa49m8.js +371 -0
- package/dist/index-key4e5c9.js +168 -0
- package/dist/index-kgstnk6g.js +239 -0
- package/dist/index-khbmdrn2.js +884 -0
- package/dist/index-khnw972v.js +117 -0
- package/dist/index-kpzfwex6.js +264 -0
- package/dist/index-kse4gn99.js +74 -0
- package/dist/index-ktscz0r2.js +264 -0
- package/dist/index-kwqff3ba.js +1410 -0
- package/dist/index-kxc4gtyt.js +574 -0
- package/dist/index-kybmjvad.js +1212 -0
- package/dist/index-kzm9cea8.js +185 -0
- package/dist/index-m2d74gqg.js +808 -0
- package/dist/index-m2v0fj08.js +74 -0
- package/dist/index-m56w7mjt.js +513 -0
- package/dist/index-m95ha3n4.js +264 -0
- package/dist/index-ma151p8g.js +264 -0
- package/dist/index-mg8vwqxf.js +514 -0
- package/dist/index-mhdsywhs.js +264 -0
- package/dist/index-mjgx910r.js +895 -0
- package/dist/index-mjve6tqn.js +447 -0
- package/dist/index-mkrmaea3.js +185 -0
- package/dist/index-mkxnyyjn.js +264 -0
- package/dist/index-mkxzgn0q.js +1372 -0
- package/dist/index-mp5mrst6.js +1372 -0
- package/dist/index-ms15cr86.js +513 -0
- package/dist/index-mt99xkc6.js +133 -0
- package/dist/index-mttv6k25.js +881 -0
- package/dist/index-myxfx52v.js +385 -0
- package/dist/index-mzmq3v0d.js +1088 -0
- package/dist/index-mzw7732t.js +74 -0
- package/dist/index-n3mjwz22.js +188 -0
- package/dist/index-n3yksgz0.js +1372 -0
- package/dist/index-n4jr3dmk.js +484 -0
- package/dist/index-n5drt64b.js +74 -0
- package/dist/index-n8frmffg.js +264 -0
- package/dist/index-n8k8chde.js +694 -0
- package/dist/index-na796ka6.js +994 -0
- package/dist/index-nbs4q0qg.js +1218 -0
- package/dist/index-newpq2zf.js +968 -0
- package/dist/index-nexg9p7j.js +1271 -0
- package/dist/index-nfm8tb5n.js +74 -0
- package/dist/index-ng9n3znd.js +1372 -0
- package/dist/index-nga2tg9z.js +1372 -0
- package/dist/index-ngbzr9gz.js +805 -0
- package/dist/index-nh90nk7j.js +380 -0
- package/dist/index-nhbkm2ba.js +467 -0
- package/dist/index-njnmfxm5.js +1372 -0
- package/dist/index-nqma24j3.js +385 -0
- package/dist/index-nrfhk0k5.js +1088 -0
- package/dist/index-ntddf5d9.js +993 -0
- package/dist/index-nte9n8w9.js +538 -0
- package/dist/index-ntxp7ke1.js +264 -0
- package/dist/index-nwxnmw41.js +264 -0
- package/dist/index-nxkesg55.js +68 -0
- package/dist/index-p0zppqxm.js +467 -0
- package/dist/index-p1xe5eem.js +513 -0
- package/dist/index-p296ban8.js +191 -0
- package/dist/index-p477xm52.js +385 -0
- package/dist/index-p7q23nce.js +1031 -0
- package/dist/index-p8nmkrgw.js +470 -0
- package/dist/index-p97s12vf.js +904 -0
- package/dist/index-p9hvv0yd.js +401 -0
- package/dist/index-pac7wv88.js +307 -0
- package/dist/index-pkxf6h87.js +895 -0
- package/dist/index-pqp4312v.js +52 -0
- package/dist/index-pt49cgjv.js +1426 -0
- package/dist/index-ptp2pfq1.js +52 -0
- package/dist/index-pw9jn6kz.js +574 -0
- package/dist/index-pyeycp3f.js +74 -0
- package/dist/index-q23xf3vr.js +479 -0
- package/dist/index-q31ne0xa.js +412 -0
- package/dist/index-q34rvh5a.js +247 -0
- package/dist/index-q5errwrj.js +904 -0
- package/dist/index-q5nhzc9m.js +1217 -0
- package/dist/index-q8f4kawk.js +204 -0
- package/dist/index-qer8zv2r.js +997 -0
- package/dist/index-qf2dkqxh.js +249 -0
- package/dist/index-qhyy018y.js +467 -0
- package/dist/index-qmhdydwy.js +70 -0
- package/dist/index-qmtdf7k5.js +639 -0
- package/dist/index-qpmvca5j.js +470 -0
- package/dist/index-qr96vaj1.js +52 -0
- package/dist/index-qtyhg3dn.js +904 -0
- package/dist/index-qwgr4q7s.js +37 -0
- package/dist/index-qye17gq5.js +264 -0
- package/dist/index-r0839f5k.js +133 -0
- package/dist/index-r1rqg72v.js +1372 -0
- package/dist/index-rab2dfh3.js +494 -0
- package/dist/index-rbckga8p.js +971 -0
- package/dist/index-rbxb5x2w.js +1372 -0
- package/dist/index-rck779bj.js +739 -0
- package/dist/index-rfn2gt5f.js +1658 -0
- package/dist/index-rg21w26a.js +470 -0
- package/dist/index-rgcz45t0.js +1372 -0
- package/dist/index-rhnd376q.js +133 -0
- package/dist/index-rjc5z3w6.js +971 -0
- package/dist/index-rkhtayd6.js +385 -0
- package/dist/index-rm8srzmv.js +371 -0
- package/dist/index-rmhk5j03.js +968 -0
- package/dist/index-rp0aye39.js +1426 -0
- package/dist/index-rp4t86bt.js +993 -0
- package/dist/index-rpq5ktdy.js +264 -0
- package/dist/index-rpsmhdd4.js +1218 -0
- package/dist/index-rqpakjyr.js +470 -0
- package/dist/index-rttk33x6.js +232 -0
- package/dist/index-rwa82e89.js +385 -0
- package/dist/index-rz185crv.js +695 -0
- package/dist/index-rz7rx80s.js +1410 -0
- package/dist/index-s22verzj.js +1218 -0
- package/dist/index-s2s3g1nt.js +133 -0
- package/dist/index-s37h3jgk.js +572 -0
- package/dist/index-s7ytytth.js +781 -0
- package/dist/index-sb705m7d.js +52 -0
- package/dist/index-sefnpzwa.js +185 -0
- package/dist/index-sgxw0xzd.js +513 -0
- package/dist/index-stc0caq6.js +1009 -0
- package/dist/index-stgn34cr.js +148 -0
- package/dist/index-svz82w9h.js +513 -0
- package/dist/index-sx49e8x9.js +884 -0
- package/dist/index-sxadwvw9.js +185 -0
- package/dist/index-sxn223fz.js +1372 -0
- package/dist/index-sy0c2mh0.js +1372 -0
- package/dist/index-syeh1hca.js +264 -0
- package/dist/index-syscwrjp.js +1576 -0
- package/dist/index-t14zr0ys.js +1063 -0
- package/dist/index-t18wyhgf.js +418 -0
- package/dist/index-t4fhcx1n.js +71 -0
- package/dist/index-td4hbgj5.js +1372 -0
- package/dist/index-tjc99447.js +68 -0
- package/dist/index-tknbyxzn.js +2202 -0
- package/dist/index-tkvt9mmq.js +484 -0
- package/dist/index-tmmcf4m6.js +808 -0
- package/dist/index-tpgs5v64.js +366 -0
- package/dist/index-tt4fsv91.js +412 -0
- package/dist/{index-07q6yxyc.js → index-tte89s31.js} +30 -29
- package/dist/index-tvjg7swe.js +1372 -0
- package/dist/index-txndxr4w.js +513 -0
- package/dist/index-tyh5jc55.js +990 -0
- package/dist/index-tyt0wa52.js +470 -0
- package/dist/index-tyt3wcfr.js +1372 -0
- package/dist/index-v35460hf.js +574 -0
- package/dist/index-v3znz08m.js +968 -0
- package/dist/index-v78xbj6n.js +808 -0
- package/dist/index-v7q00d1e.js +1410 -0
- package/dist/index-v915brjk.js +1372 -0
- package/dist/index-v922fmsx.js +52 -0
- package/dist/index-v9w1bbfe.js +842 -0
- package/dist/index-vbbgtm8p.js +513 -0
- package/dist/index-vcjgrmzj.js +456 -0
- package/dist/{index-pf5s73n9.js → index-vdewcg1r.js} +243 -385
- package/dist/index-vhqww6tt.js +307 -0
- package/dist/index-vkkmx4xe.js +1372 -0
- package/dist/index-vrps1gky.js +2202 -0
- package/dist/index-vse8620z.js +1372 -0
- package/dist/index-vt803j3b.js +1372 -0
- package/dist/index-vtjp46c3.js +52 -0
- package/dist/index-vxhxk8mf.js +904 -0
- package/dist/index-w3azfsa0.js +695 -0
- package/dist/index-w4c9vmvg.js +1517 -0
- package/dist/index-w4tq35dn.js +52 -0
- package/dist/index-w5qxvxzq.js +991 -0
- package/dist/index-w5sedpqn.js +986 -0
- package/dist/index-waha3chv.js +1230 -0
- package/dist/index-wdxww0kj.js +1006 -0
- package/dist/index-wdy5y07x.js +1372 -0
- package/dist/index-wf9pnh6j.js +385 -0
- package/dist/{index-z14anrqp.js → index-wfbfz02q.js} +14 -15
- package/dist/index-wnnbbbkt.js +1192 -0
- package/dist/index-wqd8n2qk.js +574 -0
- package/dist/index-wvcmvtjf.js +1372 -0
- package/dist/index-x0jbntp0.js +470 -0
- package/dist/index-x12e6fzy.js +476 -0
- package/dist/index-x6scc3nx.js +70 -0
- package/dist/index-x8e2wsbv.js +971 -0
- package/dist/index-xagpz645.js +2199 -0
- package/dist/index-xbth1r6e.js +572 -0
- package/dist/index-xec4p3v5.js +560 -0
- package/dist/index-xgfyxpfs.js +1218 -0
- package/dist/index-xm9fqhcb.js +447 -0
- package/dist/index-xm9hp9xc.js +1372 -0
- package/dist/index-xp7zkxcx.js +1372 -0
- package/dist/index-xrbj0ebk.js +185 -0
- package/dist/index-xskax7r3.js +50 -0
- package/dist/index-xvk5n96q.js +694 -0
- package/dist/index-xy1jvt4w.js +1229 -0
- package/dist/index-xzg9hhf3.js +470 -0
- package/dist/index-xzpg0kaz.js +70 -0
- package/dist/index-y1d8za14.js +196 -0
- package/dist/index-y3q6qvv3.js +316 -0
- package/dist/index-y44ts1dk.js +52 -0
- package/dist/index-y59hnmd0.js +132 -0
- package/dist/index-y5nt1wjb.js +971 -0
- package/dist/index-y7mkb00x.js +133 -0
- package/dist/index-y82b8w14.js +1372 -0
- package/dist/index-yb07t3c5.js +1372 -0
- package/dist/index-yc3xz3vm.js +431 -0
- package/dist/index-yeaqrevg.js +1372 -0
- package/dist/index-ygwsd1nn.js +438 -0
- package/dist/index-yhdavjk7.js +371 -0
- package/dist/index-yjhns95b.js +188 -0
- package/dist/index-yk23p7he.js +1372 -0
- package/dist/index-ykfgpsmm.js +190 -0
- package/dist/index-ymz3cfab.js +968 -0
- package/dist/index-yq9g1sq7.js +264 -0
- package/dist/index-ys5q9ch9.js +366 -0
- package/dist/index-yt0n4p46.js +973 -0
- package/dist/index-ytq1t9jx.js +232 -0
- package/dist/index-ywmtmf3b.js +513 -0
- package/dist/index-yzb44q6d.js +1372 -0
- package/dist/index-z043xk61.js +133 -0
- package/dist/index-z0gsf674.js +431 -0
- package/dist/index-z32n0ndp.js +1372 -0
- package/dist/index-z40mjts9.js +212 -0
- package/dist/index-z4qgk76w.js +1372 -0
- package/dist/index-z5k5bjc7.js +1218 -0
- package/dist/index-z73sytma.js +895 -0
- package/dist/index-z8njfj2g.js +385 -0
- package/dist/index-z9fjnwa8.js +397 -0
- package/dist/{index-1xpj0m4r.js → index-z9gy8w6b.js} +1 -1
- package/dist/index-zapkc14c.js +264 -0
- package/dist/index-zes6w8yr.js +185 -0
- package/dist/index-zgrqest7.js +695 -0
- package/dist/index-zjv6apef.js +1410 -0
- package/dist/index-zsngz131.js +1372 -0
- package/dist/index-zt22fe2j.js +54 -0
- package/dist/index-zxhc6sb0.js +192 -0
- package/dist/index.d.ts +2 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -269
- package/dist/{init-f9mgmew3.js → init-cwpergap.js} +63 -69
- package/dist/init-wfh63bfz.js +180 -0
- package/dist/login-1amyp1jy.js +77 -0
- package/dist/login-1cdwn1dj.js +77 -0
- package/dist/login-456h8jk3.js +77 -0
- package/dist/login-4n470c4j.js +77 -0
- package/dist/login-4sh7s849.js +77 -0
- package/dist/login-5dmkf0pz.js +77 -0
- package/dist/login-7yex6ppq.js +77 -0
- package/dist/login-83bjfhvz.js +77 -0
- package/dist/login-88tp4gbb.js +77 -0
- package/dist/login-b51faqpc.js +77 -0
- package/dist/login-bxyjqmbj.js +77 -0
- package/dist/login-c4649qss.js +77 -0
- package/dist/login-d80a2qxd.js +77 -0
- package/dist/login-ddw888xb.js +77 -0
- package/dist/login-dp0napaz.js +77 -0
- package/dist/login-e7pytkdc.js +77 -0
- package/dist/login-fe0brfcr.js +77 -0
- package/dist/login-h7sm5trm.js +77 -0
- package/dist/login-j3r6z2t4.js +77 -0
- package/dist/login-jkfkdjg4.js +77 -0
- package/dist/login-kch6f2yj.js +77 -0
- package/dist/login-pmqh94ws.js +77 -0
- package/dist/login-rc82x5j8.js +77 -0
- package/dist/login-tvppwyx2.js +77 -0
- package/dist/login-vd0m3xr4.js +77 -0
- package/dist/login-w76jx22q.js +77 -0
- package/dist/login-ze7y36rc.js +77 -0
- package/dist/previews-02jmcwft.js +1214 -0
- package/dist/previews-2wfvsjfy.js +1337 -0
- package/dist/previews-31bq82km.js +1225 -0
- package/dist/previews-31feb8r3.js +1337 -0
- package/dist/previews-3w4pxqby.js +1337 -0
- package/dist/previews-62pcvvpe.js +1214 -0
- package/dist/previews-6fepv94a.js +1225 -0
- package/dist/previews-76fxkp59.js +1214 -0
- package/dist/previews-844bp6kf.js +1214 -0
- package/dist/previews-8amn46qv.js +1214 -0
- package/dist/previews-93ttrf5f.js +1337 -0
- package/dist/previews-a5xbkksy.js +1214 -0
- package/dist/previews-bdrefjzx.js +1337 -0
- package/dist/previews-bgnryzav.js +1214 -0
- package/dist/previews-bkhv9dnr.js +1214 -0
- package/dist/previews-c2c64z5m.js +1214 -0
- package/dist/previews-cfcn56b4.js +1337 -0
- package/dist/previews-e4wgscsb.js +1214 -0
- package/dist/previews-ew4reetn.js +1214 -0
- package/dist/previews-hzqke1fg.js +1214 -0
- package/dist/previews-j8wa3sge.js +1214 -0
- package/dist/previews-k5n6vhpm.js +1214 -0
- package/dist/previews-mssq1hrm.js +1337 -0
- package/dist/previews-n4sdyxa5.js +1214 -0
- package/dist/previews-npwrwb0b.js +1214 -0
- package/dist/previews-q8ph9wbe.js +1214 -0
- package/dist/previews-rfswrh7q.js +1214 -0
- package/dist/previews-s0y5yp8s.js +1214 -0
- package/dist/previews-s5b6mnz5.js +1214 -0
- package/dist/previews-sdte4984.js +1200 -0
- package/dist/previews-t5tdm6t0.js +1225 -0
- package/dist/previews-tcaz1gt8.js +1337 -0
- package/dist/previews-tk7vpzj9.js +1214 -0
- package/dist/previews-tqt4pk27.js +1214 -0
- package/dist/previews-vqg4psk2.js +1225 -0
- package/dist/productions-2t9q8f57.js +505 -0
- package/dist/productions-4abj58kx.js +505 -0
- package/dist/productions-4dpec71r.js +505 -0
- package/dist/productions-4h80j2c7.js +505 -0
- package/dist/productions-4nedsanh.js +505 -0
- package/dist/productions-5c58yafp.js +505 -0
- package/dist/productions-5xq5cjhx.js +505 -0
- package/dist/productions-6dr0bmd7.js +505 -0
- package/dist/productions-86jaqt7m.js +505 -0
- package/dist/productions-8f6s8dqr.js +505 -0
- package/dist/productions-a2hcwwzf.js +505 -0
- package/dist/productions-b7svyjp7.js +505 -0
- package/dist/productions-bf2cnr05.js +505 -0
- package/dist/productions-bn2q31my.js +505 -0
- package/dist/productions-dkgry3gv.js +505 -0
- package/dist/productions-dv8g7f6g.js +505 -0
- package/dist/productions-e2m9s4tr.js +505 -0
- package/dist/productions-eqh105tk.js +505 -0
- package/dist/productions-etvcm7yf.js +505 -0
- package/dist/productions-f1zpbmjz.js +505 -0
- package/dist/productions-fgshs1m7.js +505 -0
- package/dist/productions-hphmt68n.js +505 -0
- package/dist/productions-j14pvx93.js +505 -0
- package/dist/productions-j7s1ywvg.js +505 -0
- package/dist/productions-jp3v3q3p.js +505 -0
- package/dist/productions-m9p42amj.js +505 -0
- package/dist/productions-mxbneawa.js +505 -0
- package/dist/productions-np4exh9p.js +505 -0
- package/dist/productions-rr1hqgda.js +505 -0
- package/dist/productions-rtf9ksgg.js +505 -0
- package/dist/productions-vhq7yx86.js +505 -0
- package/dist/productions-w2nzftcz.js +505 -0
- package/dist/productions-wbks5h7e.js +505 -0
- package/dist/productions-y99ayk6h.js +505 -0
- package/dist/productions-zpqyw5gm.js +505 -0
- package/dist/router/types.d.ts +24 -0
- package/dist/router/types.d.ts.map +1 -0
- package/dist/runtime/context-events.d.ts +13 -0
- package/dist/runtime/context-events.d.ts.map +1 -0
- package/dist/runtime/context-types.d.ts +82 -0
- package/dist/runtime/context-types.d.ts.map +1 -0
- package/dist/runtime/context.d.ts +27 -40
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/exports.d.ts +47 -55
- package/dist/runtime/exports.d.ts.map +1 -1
- package/dist/runtime/index.d.ts +8 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +108 -99
- package/dist/runtime/middleware.d.ts +102 -60
- package/dist/runtime/middleware.d.ts.map +1 -1
- package/dist/runtime/router/index.d.ts +7 -0
- package/dist/runtime/router/index.d.ts.map +1 -0
- package/dist/runtime/router/types.d.ts +24 -0
- package/dist/runtime/router/types.d.ts.map +1 -0
- package/dist/runtime/router.d.ts +7 -0
- package/dist/runtime/router.d.ts.map +1 -0
- package/dist/runtime/validation.d.ts +10 -2
- package/dist/runtime/validation.d.ts.map +1 -1
- package/dist/secrets/local-secrets.d.ts +46 -0
- package/dist/secrets/local-secrets.d.ts.map +1 -0
- package/dist/secrets-8wcj47nh.js +91 -0
- package/dist/secrets-b2ww34ta.js +91 -0
- package/dist/secrets-b7g4z621.js +91 -0
- package/dist/shims/local-media-bindings.d.ts +19 -0
- package/dist/shims/local-media-bindings.d.ts.map +1 -0
- package/dist/shims/local-worker-loader.d.ts +3 -0
- package/dist/shims/local-worker-loader.d.ts.map +1 -0
- package/dist/sveltekit/index.js +187 -75
- package/dist/sveltekit/local-bindings.d.ts +4 -0
- package/dist/sveltekit/local-bindings.d.ts.map +1 -0
- package/dist/sveltekit/platform.d.ts +19 -1
- package/dist/sveltekit/platform.d.ts.map +1 -1
- package/dist/test/ai-search.d.ts +39 -0
- package/dist/test/ai-search.d.ts.map +1 -0
- package/dist/test/binding-hints.d.ts +11 -0
- package/dist/test/binding-hints.d.ts.map +1 -0
- package/dist/test/bridge-context.d.ts +12 -2
- package/dist/test/bridge-context.d.ts.map +1 -1
- package/dist/test/cf.d.ts +35 -21
- package/dist/test/cf.d.ts.map +1 -1
- package/dist/test/containers.d.ts +87 -0
- package/dist/test/containers.d.ts.map +1 -0
- package/dist/test/email.d.ts +16 -7
- package/dist/test/email.d.ts.map +1 -1
- package/dist/test/index.d.ts +5 -8
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +4595 -43
- package/dist/test/local-worker-loader.d.ts +3 -0
- package/dist/test/local-worker-loader.d.ts.map +1 -0
- package/dist/test/offline-bindings.d.ts +65 -0
- package/dist/test/offline-bindings.d.ts.map +1 -0
- package/dist/test/queue.d.ts.map +1 -1
- package/dist/test/remote-ai.d.ts.map +1 -1
- package/dist/test/remote-cloudflare.d.ts +13 -0
- package/dist/test/remote-cloudflare.d.ts.map +1 -0
- package/dist/test/remote-vectorize.d.ts.map +1 -1
- package/dist/test/resolve-service-bindings.d.ts +63 -3
- package/dist/test/resolve-service-bindings.d.ts.map +1 -1
- package/dist/test/scheduled.d.ts.map +1 -1
- package/dist/test/should-skip.d.ts +14 -18
- package/dist/test/should-skip.d.ts.map +1 -1
- package/dist/test/simple-context-bindings.d.ts +13 -0
- package/dist/test/simple-context-bindings.d.ts.map +1 -0
- package/dist/test/simple-context-durable-objects.d.ts +6 -0
- package/dist/test/simple-context-durable-objects.d.ts.map +1 -0
- package/dist/test/simple-context-env.d.ts +35 -0
- package/dist/test/simple-context-env.d.ts.map +1 -0
- package/dist/test/simple-context-gateway-script.d.ts +2 -0
- package/dist/test/simple-context-gateway-script.d.ts.map +1 -0
- package/dist/test/simple-context-handlers.d.ts +12 -0
- package/dist/test/simple-context-handlers.d.ts.map +1 -0
- package/dist/test/simple-context-lifecycle.d.ts +32 -0
- package/dist/test/simple-context-lifecycle.d.ts.map +1 -0
- package/dist/test/simple-context-mfconfig.d.ts +10 -0
- package/dist/test/simple-context-mfconfig.d.ts.map +1 -0
- package/dist/test/simple-context-multi-worker.d.ts +16 -0
- package/dist/test/simple-context-multi-worker.d.ts.map +1 -0
- package/dist/test/simple-context-paths.d.ts +40 -0
- package/dist/test/simple-context-paths.d.ts.map +1 -0
- package/dist/test/simple-context-runtime.d.ts +19 -0
- package/dist/test/simple-context-runtime.d.ts.map +1 -0
- package/dist/test/simple-context-startup.d.ts +11 -0
- package/dist/test/simple-context-startup.d.ts.map +1 -0
- package/dist/test/simple-context-transport.d.ts +14 -0
- package/dist/test/simple-context-transport.d.ts.map +1 -0
- package/dist/test/simple-context.d.ts +7 -26
- package/dist/test/simple-context.d.ts.map +1 -1
- package/dist/test/tail.d.ts +2 -1
- package/dist/test/tail.d.ts.map +1 -1
- package/dist/test/utilities/artifacts.d.ts +11 -0
- package/dist/test/utilities/artifacts.d.ts.map +1 -0
- package/dist/test/utilities/context.d.ts +39 -0
- package/dist/test/utilities/context.d.ts.map +1 -0
- package/dist/test/utilities/d1.d.ts +21 -0
- package/dist/test/utilities/d1.d.ts.map +1 -0
- package/dist/test/utilities/env.d.ts +40 -0
- package/dist/test/utilities/env.d.ts.map +1 -0
- package/dist/test/utilities/kv.d.ts +11 -0
- package/dist/test/utilities/kv.d.ts.map +1 -0
- package/dist/test/utilities/media.d.ts +16 -0
- package/dist/test/utilities/media.d.ts.map +1 -0
- package/dist/test/utilities/platform.d.ts +38 -0
- package/dist/test/utilities/platform.d.ts.map +1 -0
- package/dist/test/utilities/queue.d.ts +5 -0
- package/dist/test/utilities/queue.d.ts.map +1 -0
- package/dist/test/utilities/r2.d.ts +12 -0
- package/dist/test/utilities/r2.d.ts.map +1 -0
- package/dist/test/utilities/workflows.d.ts +26 -0
- package/dist/test/utilities/workflows.d.ts.map +1 -0
- package/dist/test/utilities.d.ts +10 -98
- package/dist/test/utilities.d.ts.map +1 -1
- package/dist/test/worker.d.ts +6 -0
- package/dist/test/worker.d.ts.map +1 -1
- package/dist/token-3b9wws58.js +419 -0
- package/dist/token-47kcz18j.js +419 -0
- package/dist/token-a2b38w0z.js +419 -0
- package/dist/token-exz78pth.js +419 -0
- package/dist/token-fcthx92c.js +419 -0
- package/dist/token-grwp8z4q.js +419 -0
- package/dist/token-jkmm44z2.js +419 -0
- package/dist/token-qxtvzm3b.js +419 -0
- package/dist/token-rq8mvtz9.js +419 -0
- package/dist/token-sct51r47.js +419 -0
- package/dist/token-sfgxcgen.js +419 -0
- package/dist/transform/durable-object.d.ts.map +1 -1
- package/dist/transform/worker-entrypoint.d.ts +5 -0
- package/dist/transform/worker-entrypoint.d.ts.map +1 -1
- package/dist/{types-5nyrz1sz.js → types-0qv4xces.js} +266 -148
- package/dist/types-2ejrbba1.js +695 -0
- package/dist/types-2nvrs9jg.js +572 -0
- package/dist/types-2pbe8kzw.js +572 -0
- package/dist/types-2zyt0m43.js +572 -0
- package/dist/types-4xxer9ep.js +572 -0
- package/dist/types-5t5y7a5n.js +572 -0
- package/dist/types-5wd2rygw.js +572 -0
- package/dist/types-7j3ykgx7.js +572 -0
- package/dist/types-7jkbm95a.js +695 -0
- package/dist/types-8g78x34n.js +572 -0
- package/dist/types-a2fk9yns.js +695 -0
- package/dist/types-dyb3c6zw.js +695 -0
- package/dist/types-e2n9f3pd.js +695 -0
- package/dist/types-e6d336q3.js +572 -0
- package/dist/types-hqh2fx0x.js +572 -0
- package/dist/types-j12xmdnd.js +572 -0
- package/dist/types-j4s6qcrc.js +695 -0
- package/dist/types-jqn26et3.js +572 -0
- package/dist/types-n0mxbq26.js +572 -0
- package/dist/types-naz9hvw5.js +572 -0
- package/dist/types-qjcd1jks.js +572 -0
- package/dist/types-r9zb8sw1.js +572 -0
- package/dist/types-rv5xwgsn.js +572 -0
- package/dist/types-t39njdqx.js +572 -0
- package/dist/types-tkgch3xv.js +572 -0
- package/dist/types-vhvt4hvm.js +693 -0
- package/dist/types-vvtb7rrh.js +572 -0
- package/dist/types-w1grncdj.js +572 -0
- package/dist/types-wmw49exb.js +572 -0
- package/dist/types-ymxz9jga.js +572 -0
- package/dist/utils/entrypoint-discovery.d.ts +6 -3
- package/dist/utils/entrypoint-discovery.d.ts.map +1 -1
- package/dist/utils/resolve-package.d.ts.map +1 -1
- package/dist/utils/send-email.d.ts +15 -0
- package/dist/utils/send-email.d.ts.map +1 -0
- package/dist/utils/send-email.js +19 -0
- package/dist/vite/config-file.d.ts +25 -0
- package/dist/vite/config-file.d.ts.map +1 -0
- package/dist/vite/index.d.ts +1 -0
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +25 -331
- package/dist/vite/plugin-config-hook.d.ts +28 -0
- package/dist/vite/plugin-config-hook.d.ts.map +1 -0
- package/dist/vite/plugin-context.d.ts +27 -0
- package/dist/vite/plugin-context.d.ts.map +1 -0
- package/dist/vite/plugin-durable-objects.d.ts +18 -0
- package/dist/vite/plugin-durable-objects.d.ts.map +1 -0
- package/dist/vite/plugin-programmatic.d.ts +44 -0
- package/dist/vite/plugin-programmatic.d.ts.map +1 -0
- package/dist/vite/plugin-service-bindings.d.ts +13 -0
- package/dist/vite/plugin-service-bindings.d.ts.map +1 -0
- package/dist/vite/plugin-transform.d.ts +41 -0
- package/dist/vite/plugin-transform.d.ts.map +1 -0
- package/dist/vite/plugin.d.ts +10 -41
- package/dist/vite/plugin.d.ts.map +1 -1
- package/dist/worker-18ceqscc.js +513 -0
- package/dist/worker-1yqpwte2.js +513 -0
- package/dist/worker-2aw27zxy.js +513 -0
- package/dist/worker-45tp4a8f.js +513 -0
- package/dist/worker-663em30d.js +513 -0
- package/dist/worker-68zttchg.js +513 -0
- package/dist/worker-729cf3fh.js +513 -0
- package/dist/worker-99tew196.js +513 -0
- package/dist/worker-argxc7fb.js +513 -0
- package/dist/worker-aydzxt4f.js +513 -0
- package/dist/worker-ejdxma7k.js +513 -0
- package/dist/worker-entry/composed-worker.d.ts +7 -0
- package/dist/worker-entry/composed-worker.d.ts.map +1 -0
- package/dist/worker-entry/durable-object-discovery.d.ts +20 -0
- package/dist/worker-entry/durable-object-discovery.d.ts.map +1 -0
- package/dist/worker-entry/extensions.d.ts +5 -0
- package/dist/worker-entry/extensions.d.ts.map +1 -0
- package/dist/worker-entry/routes.d.ts +22 -0
- package/dist/worker-entry/routes.d.ts.map +1 -0
- package/dist/worker-entry/surface-paths.d.ts +26 -0
- package/dist/worker-entry/surface-paths.d.ts.map +1 -0
- package/dist/worker-entrypoint-3rmzd4c1.js +15 -0
- package/dist/worker-entrypoint-4xp7msd7.js +16 -0
- package/dist/{worker-entrypoint-m9th0rg0.js → worker-entrypoint-c259fmfs.js} +1 -1
- package/dist/worker-entrypoint-f1v9y4s8.js +15 -0
- package/dist/worker-f6xqjg65.js +513 -0
- package/dist/worker-fcdsnj14.js +513 -0
- package/dist/worker-fdnn62de.js +513 -0
- package/dist/worker-fk42rzse.js +513 -0
- package/dist/worker-frnh95rg.js +513 -0
- package/dist/worker-jkemk8d2.js +513 -0
- package/dist/worker-k55nfrmp.js +513 -0
- package/dist/worker-kqc6w7ry.js +513 -0
- package/dist/worker-ksvcq8be.js +513 -0
- package/dist/worker-m4ze8djx.js +513 -0
- package/dist/worker-mjzfstv5.js +513 -0
- package/dist/worker-p4rtsndj.js +513 -0
- package/dist/worker-p6d53qan.js +513 -0
- package/dist/worker-qh2qbv97.js +513 -0
- package/dist/worker-r234vnv9.js +513 -0
- package/dist/worker-r47bv7gt.js +513 -0
- package/dist/worker-s4nvbqsm.js +513 -0
- package/dist/worker-ttj7vwce.js +513 -0
- package/dist/worker-wnan5dca.js +513 -0
- package/dist/worker-wv4jdneg.js +513 -0
- package/dist/worker-xcbscr75.js +513 -0
- package/dist/worker-yw3atfb1.js +513 -0
- package/dist/worker-ywwkf3cp.js +513 -0
- package/dist/worker-z8wtk0sh.js +513 -0
- package/dist/workflows/local-workflow-entrypoints.d.ts +7 -0
- package/dist/workflows/local-workflow-entrypoints.d.ts.map +1 -0
- package/package.json +140 -115
- package/dist/account-rvrj687w.js +0 -397
- package/dist/browser-shim/worker.d.ts +0 -14
- package/dist/browser-shim/worker.d.ts.map +0 -1
- package/dist/build-mnf6v8gd.js +0 -53
- package/dist/deploy-nhceck39.js +0 -70
- package/dist/doctor-e8fy6fj5.js +0 -186
- package/dist/index-67qcae0f.js +0 -183
- package/dist/index-ep3445yc.js +0 -2225
- package/dist/index-hcex3rgh.js +0 -266
- package/dist/index-m2q41jwa.js +0 -462
- package/dist/index-n7rs26ft.js +0 -77
- package/dist/index-tfyxa77h.js +0 -850
- package/dist/index-tk6ej9dj.js +0 -94
- package/dist/test/multi-worker-context.d.ts +0 -114
- package/dist/test/multi-worker-context.d.ts.map +0 -1
package/dist/test/index.js
CHANGED
|
@@ -1,71 +1,4623 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
getRemoteModeStatus,
|
|
3
|
+
isRemoteModeActive
|
|
4
|
+
} from "../index-d8bdkx2h.js";
|
|
5
|
+
import {
|
|
6
|
+
canProceedWithTest
|
|
7
|
+
} from "../index-15fpa5tx.js";
|
|
8
|
+
import {
|
|
9
|
+
bundleWorkflowEntrypointScript
|
|
10
|
+
} from "../index-bj5avaba.js";
|
|
11
|
+
import"../index-666tdx14.js";
|
|
12
|
+
import {
|
|
13
|
+
createRouteResolve,
|
|
14
|
+
invokeFetchModule,
|
|
15
|
+
matchFetchRoute,
|
|
16
|
+
resolveFetchHandler
|
|
17
|
+
} from "../index-f1yshy4s.js";
|
|
18
|
+
import {
|
|
19
|
+
__clearTestContext,
|
|
20
|
+
__setTestContext,
|
|
21
|
+
env
|
|
22
|
+
} from "../index-nxkesg55.js";
|
|
23
|
+
import"../index-a855bdsx.js";
|
|
24
|
+
import {
|
|
25
|
+
buildHyperdrivesConfig,
|
|
3
26
|
clearBundleCache,
|
|
4
|
-
|
|
5
|
-
createEntrypointScript,
|
|
6
|
-
createMockD1,
|
|
7
|
-
createMockEnv,
|
|
8
|
-
createMockKV,
|
|
9
|
-
createMockQueue,
|
|
10
|
-
createMockR2,
|
|
11
|
-
createMockTestContext,
|
|
12
|
-
createMultiWorkerContext,
|
|
13
|
-
createTestContext,
|
|
14
|
-
email,
|
|
15
|
-
env,
|
|
16
|
-
getBridgeTestContext,
|
|
27
|
+
discoverRoutes,
|
|
17
28
|
hasCrossWorkerDOs,
|
|
18
29
|
hasServiceBindings,
|
|
19
|
-
isRemoteModeEnabled,
|
|
20
|
-
queue,
|
|
21
30
|
resolveDOBindings,
|
|
22
|
-
resolveServiceBindings
|
|
31
|
+
resolveServiceBindings
|
|
32
|
+
} from "../index-p7q23nce.js";
|
|
33
|
+
import"../index-3edvz3hs.js";
|
|
34
|
+
import {
|
|
35
|
+
DEFAULT_DO_PATTERN,
|
|
36
|
+
findFiles
|
|
37
|
+
} from "../index-qwgr4q7s.js";
|
|
38
|
+
import"../index-aqrwyy57.js";
|
|
39
|
+
import"../index-vhqww6tt.js";
|
|
40
|
+
import {
|
|
41
|
+
createLocalWorkerLoaderBinding,
|
|
42
|
+
disposeLocalWorkerLoaderBindings,
|
|
43
|
+
extractBindingHints
|
|
44
|
+
} from "../index-y59hnmd0.js";
|
|
45
|
+
import {
|
|
46
|
+
buildLocalBindingShimServiceConfig
|
|
47
|
+
} from "../index-dm9q84c7.js";
|
|
48
|
+
import {
|
|
49
|
+
buildLocalSecretWrappedBindingConfig,
|
|
50
|
+
resolveLocalSecretValuesForBindings
|
|
51
|
+
} from "../index-hn5nbxbt.js";
|
|
52
|
+
import {
|
|
53
|
+
BridgeClient,
|
|
54
|
+
createEmailEvent,
|
|
55
|
+
createEnvProxy,
|
|
56
|
+
createFetchEvent,
|
|
57
|
+
createQueueEvent,
|
|
58
|
+
createScheduledEvent,
|
|
59
|
+
createTailEvent,
|
|
60
|
+
runWithContext,
|
|
61
|
+
runWithEventContext,
|
|
62
|
+
setBindingHints
|
|
63
|
+
} from "../index-tknbyxzn.js";
|
|
64
|
+
import {
|
|
65
|
+
createLocalSendEmailBinding,
|
|
66
|
+
wrapEnvSendEmailBindings
|
|
67
|
+
} from "../index-hpwa6vsw.js";
|
|
68
|
+
import {
|
|
69
|
+
applyLocalDevVarsToConfig
|
|
70
|
+
} from "../index-c3nxftnp.js";
|
|
71
|
+
import {
|
|
72
|
+
getLocalD1DatabaseIdentifier,
|
|
73
|
+
loadConfig,
|
|
74
|
+
normalizeArtifactsBinding,
|
|
75
|
+
normalizeDOBinding,
|
|
76
|
+
normalizeDispatchNamespaceBinding,
|
|
77
|
+
normalizeHyperdriveBinding,
|
|
78
|
+
normalizeImagesBinding,
|
|
79
|
+
normalizeMediaBinding,
|
|
80
|
+
normalizeMtlsCertificateBinding,
|
|
81
|
+
normalizePipelineBinding,
|
|
82
|
+
normalizeSecretsStoreBinding,
|
|
83
|
+
normalizeWorkflowBinding,
|
|
84
|
+
resolveConfigPath
|
|
85
|
+
} from "../index-syscwrjp.js";
|
|
86
|
+
import {
|
|
87
|
+
getEffectiveAccountId,
|
|
88
|
+
getPrimaryAccount
|
|
89
|
+
} from "../index-1d4jg11n.js";
|
|
90
|
+
import {
|
|
91
|
+
getApiToken,
|
|
92
|
+
isAuthenticated
|
|
93
|
+
} from "../index-mg8vwqxf.js";
|
|
94
|
+
import"../index-z40mjts9.js";
|
|
95
|
+
import"../index-q8f4kawk.js";
|
|
96
|
+
import {
|
|
97
|
+
__require
|
|
98
|
+
} from "../index-37x76zdn.js";
|
|
99
|
+
|
|
100
|
+
// src/test/simple-context-durable-objects.ts
|
|
101
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
102
|
+
import { readFile } from "fs/promises";
|
|
103
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
104
|
+
|
|
105
|
+
// src/test/simple-context-gateway-script.ts
|
|
106
|
+
function buildGatewayScript(bundledCode, wrappers, nativeRpcBindingNames = []) {
|
|
107
|
+
const nativeRpcBindingsLiteral = JSON.stringify(nativeRpcBindingNames);
|
|
108
|
+
return `
|
|
109
|
+
// Bundled transport + DO classes
|
|
110
|
+
${bundledCode}
|
|
111
|
+
|
|
112
|
+
// DO Wrappers with RPC
|
|
113
|
+
${wrappers}
|
|
114
|
+
|
|
115
|
+
const __nativeRpcBindings = new Set(${nativeRpcBindingsLiteral})
|
|
116
|
+
|
|
117
|
+
// Transport encoding helper
|
|
118
|
+
const __transportEncoders = typeof transport !== 'undefined' ? transport : {}
|
|
119
|
+
|
|
120
|
+
function __encodeTransport(value) {
|
|
121
|
+
if (value === null || value === undefined) return value
|
|
122
|
+
|
|
123
|
+
// Try each encoder
|
|
124
|
+
for (const [typeName, transporter] of Object.entries(__transportEncoders)) {
|
|
125
|
+
const encoded = transporter.encode(value)
|
|
126
|
+
if (encoded !== false && encoded !== undefined) {
|
|
127
|
+
return { __transport: typeName, value: encoded }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Recursively encode arrays and objects
|
|
132
|
+
if (Array.isArray(value)) {
|
|
133
|
+
return value.map(__encodeTransport)
|
|
134
|
+
}
|
|
135
|
+
if (typeof value === 'object') {
|
|
136
|
+
const result = {}
|
|
137
|
+
for (const [k, v] of Object.entries(value)) {
|
|
138
|
+
result[k] = __encodeTransport(v)
|
|
139
|
+
}
|
|
140
|
+
return result
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return value
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Gateway with WebSocket RPC
|
|
147
|
+
export default {
|
|
148
|
+
async fetch(request, env) {
|
|
149
|
+
if (request.headers.get('Upgrade') === 'websocket') {
|
|
150
|
+
const { 0: client, 1: server } = new WebSocketPair()
|
|
151
|
+
server.accept()
|
|
152
|
+
server.addEventListener('message', async (e) => {
|
|
153
|
+
try {
|
|
154
|
+
const m = JSON.parse(e.data)
|
|
155
|
+
if (m.t === 'rpc.call') {
|
|
156
|
+
const result = await executeRpc(env, m.method, m.params)
|
|
157
|
+
server.send(JSON.stringify({ t: 'rpc.ok', id: m.id, result }))
|
|
158
|
+
}
|
|
159
|
+
} catch (error) {
|
|
160
|
+
server.send(JSON.stringify({ t: 'rpc.err', id: 'unknown', error: { code: 'RPC_ERROR', message: error.message } }))
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
return new Response(null, { status: 101, webSocket: client })
|
|
164
|
+
}
|
|
165
|
+
return new Response('Gateway')
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function executeRpc(env, method, params) {
|
|
170
|
+
const [bindingName, ...rest] = method.split('.')
|
|
171
|
+
let op = rest.join('.')
|
|
172
|
+
const binding = env[bindingName]
|
|
173
|
+
const RAW_EMAIL = 'EmailMessage::raw'
|
|
174
|
+
if (!binding) throw new Error('Binding not found: ' + bindingName)
|
|
175
|
+
|
|
176
|
+
// Normalize namespaced op names (kv.*, r2.*, d1.*, do.*, queue.*, ai.*, var.*)
|
|
177
|
+
// down to the legacy verbs this dispatcher historically used. The bridge
|
|
178
|
+
// proxy always emits namespaced forms (see src/bridge/proxy.ts and B3 in
|
|
179
|
+
// REMAINING.md); this keeps the dispatcher backwards-compatible while the
|
|
180
|
+
// rest of the codebase converges on the namespaced convention.
|
|
181
|
+
if (op.indexOf('kv.') === 0) op = op.slice(3)
|
|
182
|
+
else if (op.indexOf('do.') === 0) {
|
|
183
|
+
const tail = op.slice(3)
|
|
184
|
+
if (tail === 'fetch') op = 'stub.fetch'
|
|
185
|
+
else if (tail === 'rpc') op = 'stub.rpc'
|
|
186
|
+
else op = tail
|
|
187
|
+
}
|
|
188
|
+
else if (op.indexOf('queue.') === 0) op = op.slice(6)
|
|
189
|
+
else if (op.indexOf('ai.') === 0) op = op.slice(3)
|
|
190
|
+
else if (op.indexOf('var.') === 0) op = op.slice(4)
|
|
191
|
+
else if (op.indexOf('d1.stmt.') === 0) op = 'prepare.' + op.slice('d1.stmt.'.length)
|
|
192
|
+
else if (op.indexOf('d1.') === 0) op = op.slice(3)
|
|
193
|
+
// r2.* and email.* keep their existing prefixes
|
|
194
|
+
|
|
195
|
+
// KV operations
|
|
196
|
+
if (op === 'get') return binding.get(params[0], params[1])
|
|
197
|
+
if (op === 'put') return binding.put(params[0], params[1], params[2])
|
|
198
|
+
if (op === 'delete') return binding.delete(params[0])
|
|
199
|
+
if (op === 'list') return binding.list(params[0])
|
|
200
|
+
if (op === 'getWithMetadata') return binding.getWithMetadata(params[0], params[1])
|
|
201
|
+
|
|
202
|
+
// R2 operations
|
|
203
|
+
if (op === 'r2.get') return binding.get(params[0], params[1])
|
|
204
|
+
if (op === 'r2.put') return binding.put(params[0], params[1], params[2])
|
|
205
|
+
if (op === 'r2.delete') return binding.delete(params[0])
|
|
206
|
+
if (op === 'r2.list') return binding.list(params[0])
|
|
207
|
+
if (op === 'r2.head' || op === 'head') return binding.head(params[0])
|
|
208
|
+
|
|
209
|
+
// D1 operations
|
|
210
|
+
if (op === 'exec') return binding.exec(params[0])
|
|
211
|
+
if (op === 'dump') return binding.dump()
|
|
212
|
+
if (op === 'batch') {
|
|
213
|
+
const stmts = params[0].map(s => {
|
|
214
|
+
const stmt = binding.prepare(s.sql)
|
|
215
|
+
return s.bindings?.length ? stmt.bind(...s.bindings) : stmt
|
|
216
|
+
})
|
|
217
|
+
return binding.batch(stmts)
|
|
218
|
+
}
|
|
219
|
+
if (op === 'prepare.run') return binding.prepare(params[0]).bind(...(params[1] || [])).run()
|
|
220
|
+
if (op === 'prepare.all') return binding.prepare(params[0]).bind(...(params[1] || [])).all()
|
|
221
|
+
if (op === 'prepare.first') return binding.prepare(params[0]).bind(...(params[1] || [])).first(params[2])
|
|
222
|
+
if (op === 'prepare.raw') return binding.prepare(params[0]).bind(...(params[1] || [])).raw({ columnNames: params[2] })
|
|
223
|
+
|
|
224
|
+
// Send email operations
|
|
225
|
+
if (op === 'email.send') {
|
|
226
|
+
return binding.send(__normalizeEmailMessage(params[0]))
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// DO operations
|
|
230
|
+
if (op === 'idFromName') {
|
|
231
|
+
return { __type: 'DOId', hex: binding.idFromName(params[0]).toString() }
|
|
232
|
+
}
|
|
233
|
+
if (op === 'stub.rpc') {
|
|
234
|
+
const [, idSerialized, rpcMethod, rpcParams] = params
|
|
235
|
+
const stub = binding.get(binding.idFromString(idSerialized.hex))
|
|
236
|
+
|
|
237
|
+
if (__nativeRpcBindings.has(bindingName) && typeof stub[rpcMethod] === 'function') {
|
|
238
|
+
let result = await stub[rpcMethod](...(rpcParams || []))
|
|
239
|
+
result = __encodeTransport(result)
|
|
240
|
+
return result
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const response = await stub.fetch(new Request('http://do/_rpc', {
|
|
244
|
+
method: 'POST',
|
|
245
|
+
headers: { 'Content-Type': 'application/json' },
|
|
246
|
+
body: JSON.stringify({ method: rpcMethod, params: rpcParams || [] })
|
|
247
|
+
}))
|
|
248
|
+
|
|
249
|
+
const payload = await response.json()
|
|
250
|
+
if (!response.ok || !payload?.ok) {
|
|
251
|
+
throw new Error(payload?.error?.message || ('DO RPC failed with status ' + response.status))
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return payload.result
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
throw new Error('Unknown operation: ' + method)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function __createEmailMessageRaw(raw) {
|
|
261
|
+
if (typeof raw === 'string' || raw instanceof ReadableStream) {
|
|
262
|
+
return raw
|
|
263
|
+
}
|
|
264
|
+
if (raw instanceof Uint8Array || raw instanceof ArrayBuffer) {
|
|
265
|
+
return new Response(raw).body
|
|
266
|
+
}
|
|
267
|
+
throw new Error('Unsupported EmailMessage raw payload')
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function __buildRawEmail(message) {
|
|
271
|
+
const lines = []
|
|
272
|
+
const messageId = '<' + Date.now() + '-' + Math.random().toString(36).slice(2) + '@devflare.dev>'
|
|
273
|
+
|
|
274
|
+
lines.push('From: ' + message.from)
|
|
275
|
+
lines.push('To: ' + (Array.isArray(message.to) ? message.to.join(', ') : message.to))
|
|
276
|
+
lines.push('Date: ' + new Date().toUTCString())
|
|
277
|
+
lines.push('Message-ID: ' + messageId)
|
|
278
|
+
|
|
279
|
+
if (message.subject) lines.push('Subject: ' + message.subject)
|
|
280
|
+
if (message.replyTo) lines.push('Reply-To: ' + String(message.replyTo))
|
|
281
|
+
if (message.cc) lines.push('Cc: ' + (Array.isArray(message.cc) ? message.cc.join(', ') : message.cc))
|
|
282
|
+
if (message.bcc) lines.push('Bcc: ' + (Array.isArray(message.bcc) ? message.bcc.join(', ') : message.bcc))
|
|
283
|
+
|
|
284
|
+
for (const [key, value] of Object.entries(message.headers || {})) {
|
|
285
|
+
lines.push(key + ': ' + value)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
lines.push('MIME-Version: 1.0')
|
|
289
|
+
lines.push('Content-Type: ' + (message.html ? 'text/html' : 'text/plain') + '; charset=UTF-8')
|
|
290
|
+
lines.push('')
|
|
291
|
+
lines.push(String(message.html ?? message.text ?? '').replace(/\\r?\\n/g, '\\r\\n'))
|
|
292
|
+
|
|
293
|
+
return lines.join('\\r\\n')
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function __normalizeEmailMessage(message) {
|
|
297
|
+
if (!message || typeof message !== 'object' || !('from' in message) || !('to' in message)) {
|
|
298
|
+
return message
|
|
299
|
+
}
|
|
300
|
+
if ('EmailMessage::raw' in message) {
|
|
301
|
+
return message
|
|
302
|
+
}
|
|
303
|
+
if ('raw' in message && message.raw !== undefined) {
|
|
304
|
+
return {
|
|
305
|
+
from: message.from,
|
|
306
|
+
to: message.to,
|
|
307
|
+
[RAW_EMAIL]: __createEmailMessageRaw(message.raw)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
from: message.from,
|
|
312
|
+
to: message.to,
|
|
313
|
+
[RAW_EMAIL]: __createEmailMessageRaw(__buildRawEmail(message))
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
`;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// src/test/simple-context-paths.ts
|
|
320
|
+
import { existsSync } from "fs";
|
|
321
|
+
import { createServer } from "net";
|
|
322
|
+
import { dirname, join } from "path";
|
|
323
|
+
import { fileURLToPath } from "url";
|
|
324
|
+
var DEFAULT_TRANSPORT_ENTRY_FILES = [
|
|
325
|
+
"src/transport.ts",
|
|
326
|
+
"src/transport.js",
|
|
327
|
+
"src/transport.mts",
|
|
328
|
+
"src/transport.mjs"
|
|
329
|
+
];
|
|
330
|
+
var CURRENT_PACKAGE_ROOT = findPackageRoot(dirname(fileURLToPath(import.meta.url)));
|
|
331
|
+
function getBunRuntime() {
|
|
332
|
+
const g = globalThis;
|
|
333
|
+
if (typeof g.Bun === "object" && g.Bun !== null) {
|
|
334
|
+
return g.Bun;
|
|
335
|
+
}
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
function getCallerDirectory() {
|
|
339
|
+
const stackCallerDirectory = getStackCallerDirectory();
|
|
340
|
+
if (stackCallerDirectory) {
|
|
341
|
+
return stackCallerDirectory;
|
|
342
|
+
}
|
|
343
|
+
return process.cwd();
|
|
344
|
+
}
|
|
345
|
+
function getStackCallerDirectory() {
|
|
346
|
+
const originalPrepare = Error.prepareStackTrace;
|
|
347
|
+
Error.prepareStackTrace = (_, stack) => stack;
|
|
348
|
+
try {
|
|
349
|
+
const err = new Error;
|
|
350
|
+
const stack = err.stack;
|
|
351
|
+
for (const site of stack ?? []) {
|
|
352
|
+
const filename = site.getFileName?.();
|
|
353
|
+
if (filename && !isInsideCurrentPackage(filename) && !filename.includes("simple-context") && !filename.includes("node_modules") && !filename.includes("[") && existsSync(filename)) {
|
|
354
|
+
return dirname(filename);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
} finally {
|
|
358
|
+
Error.prepareStackTrace = originalPrepare;
|
|
359
|
+
}
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
function findPackageRoot(startDir) {
|
|
363
|
+
let currentDir = startDir;
|
|
364
|
+
while (true) {
|
|
365
|
+
if (existsSync(join(currentDir, "package.json"))) {
|
|
366
|
+
return currentDir;
|
|
367
|
+
}
|
|
368
|
+
const parentDir = dirname(currentDir);
|
|
369
|
+
if (parentDir === currentDir) {
|
|
370
|
+
return startDir;
|
|
371
|
+
}
|
|
372
|
+
currentDir = parentDir;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function isInsideCurrentPackage(filePath) {
|
|
376
|
+
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
377
|
+
const normalizedPackageRoot = CURRENT_PACKAGE_ROOT.replace(/\\/g, "/");
|
|
378
|
+
return normalizedFilePath === normalizedPackageRoot || normalizedFilePath.startsWith(`${normalizedPackageRoot}/`);
|
|
379
|
+
}
|
|
380
|
+
async function findNearestConfig(startDir) {
|
|
381
|
+
let currentDir = startDir;
|
|
382
|
+
while (true) {
|
|
383
|
+
const configPath = await resolveConfigPath(currentDir);
|
|
384
|
+
if (configPath) {
|
|
385
|
+
return configPath;
|
|
386
|
+
}
|
|
387
|
+
const parentDir = dirname(currentDir);
|
|
388
|
+
if (parentDir === currentDir) {
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
currentDir = parentDir;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
async function getAvailablePort() {
|
|
395
|
+
return await new Promise((resolvePort, reject) => {
|
|
396
|
+
const server = createServer();
|
|
397
|
+
server.once("error", reject);
|
|
398
|
+
server.listen(0, "127.0.0.1", () => {
|
|
399
|
+
const address = server.address();
|
|
400
|
+
if (!address || typeof address === "string") {
|
|
401
|
+
server.close(() => reject(new Error("Could not determine an available port")));
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const { port } = address;
|
|
405
|
+
server.close((error) => {
|
|
406
|
+
if (error) {
|
|
407
|
+
reject(error);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
resolvePort(port);
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
function resolveTransportFile(configDir, configuredPath) {
|
|
416
|
+
if (typeof configuredPath === "string") {
|
|
417
|
+
return configuredPath;
|
|
418
|
+
}
|
|
419
|
+
if (configuredPath === null) {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
for (const defaultEntry of DEFAULT_TRANSPORT_ENTRY_FILES) {
|
|
423
|
+
if (existsSync(join(configDir, defaultEntry))) {
|
|
424
|
+
return defaultEntry;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/test/simple-context-durable-objects.ts
|
|
431
|
+
function findExportedClasses(code) {
|
|
432
|
+
const classes = [];
|
|
433
|
+
const classPattern = /export\s+class\s+(\w+)/g;
|
|
434
|
+
let match;
|
|
435
|
+
while ((match = classPattern.exec(code)) !== null) {
|
|
436
|
+
classes.push(match[1]);
|
|
437
|
+
}
|
|
438
|
+
return classes;
|
|
439
|
+
}
|
|
440
|
+
function classSupportsNativeDurableObjectRpc(code, className) {
|
|
441
|
+
const nativeRpcPattern = new RegExp(`export\\s+class\\s+${className}\\s+extends\\s+DurableObject\\b`);
|
|
442
|
+
return nativeRpcPattern.test(code);
|
|
443
|
+
}
|
|
444
|
+
function toGeneratedIdentifier(value) {
|
|
445
|
+
const normalized = value.replace(/[^A-Za-z0-9_$]/g, "_");
|
|
446
|
+
return /^[A-Za-z_$]/.test(normalized) ? normalized : `_${normalized}`;
|
|
447
|
+
}
|
|
448
|
+
async function discoverLocalDurableObjectClasses(config, configDir) {
|
|
449
|
+
const classToFilePath = new Map;
|
|
450
|
+
const doPatternConfig = config.files?.durableObjects;
|
|
451
|
+
const doPattern = typeof doPatternConfig === "string" ? doPatternConfig : DEFAULT_DO_PATTERN;
|
|
452
|
+
if (doPatternConfig === false) {
|
|
453
|
+
return classToFilePath;
|
|
454
|
+
}
|
|
455
|
+
const doFiles = await findFiles(doPattern, { cwd: configDir });
|
|
456
|
+
for (const filePath of doFiles) {
|
|
457
|
+
try {
|
|
458
|
+
const code = await readFile(filePath, "utf-8");
|
|
459
|
+
const classNames = findExportedClasses(code);
|
|
460
|
+
for (const className of classNames) {
|
|
461
|
+
classToFilePath.set(className, {
|
|
462
|
+
filePath,
|
|
463
|
+
nativeRpc: classSupportsNativeDurableObjectRpc(code, className)
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
} catch {}
|
|
467
|
+
}
|
|
468
|
+
return classToFilePath;
|
|
469
|
+
}
|
|
470
|
+
async function resolveLocalDurableObjects(config, configDir) {
|
|
471
|
+
const doConfig = {};
|
|
472
|
+
const doInfos = [];
|
|
473
|
+
const classToFilePath = await discoverLocalDurableObjectClasses(config, configDir);
|
|
474
|
+
for (const [name, rawDoInfo] of Object.entries(config.bindings?.durableObjects ?? {})) {
|
|
475
|
+
const doInfo = normalizeDOBinding(rawDoInfo);
|
|
476
|
+
if (doInfo.__ref) {
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
let scriptPath;
|
|
480
|
+
let nativeRpc = false;
|
|
481
|
+
if (doInfo.kind === "cross-worker" && doInfo.scriptName) {
|
|
482
|
+
scriptPath = join2(configDir, "src", doInfo.scriptName);
|
|
483
|
+
try {
|
|
484
|
+
const code = await readFile(scriptPath, "utf-8");
|
|
485
|
+
nativeRpc = classSupportsNativeDurableObjectRpc(code, doInfo.className);
|
|
486
|
+
} catch {
|
|
487
|
+
nativeRpc = false;
|
|
488
|
+
}
|
|
489
|
+
} else {
|
|
490
|
+
const discoveredClass = classToFilePath.get(doInfo.className);
|
|
491
|
+
if (!discoveredClass) {
|
|
492
|
+
throw new Error(`Durable object ${name} (className: '${doInfo.className}') not found.
|
|
493
|
+
` + `Either:
|
|
494
|
+
` + ` 1. Set files.durableObjects pattern in config (e.g., 'src/do.*.ts')
|
|
495
|
+
` + ` 2. Use explicit scriptName: { className: '${doInfo.className}', scriptName: 'do.file.ts' }`);
|
|
496
|
+
}
|
|
497
|
+
scriptPath = discoveredClass.filePath;
|
|
498
|
+
nativeRpc = discoveredClass.nativeRpc;
|
|
499
|
+
}
|
|
500
|
+
const runtimeClassName = nativeRpc ? doInfo.className : `__Devflare${toGeneratedIdentifier(name)}RpcWrapper`;
|
|
501
|
+
doConfig[name] = runtimeClassName;
|
|
502
|
+
doInfos.push({
|
|
503
|
+
name,
|
|
504
|
+
className: doInfo.className,
|
|
505
|
+
scriptPath,
|
|
506
|
+
nativeRpc,
|
|
507
|
+
runtimeClassName
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
return {
|
|
511
|
+
doConfig,
|
|
512
|
+
doInfos
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
function buildWrapperCode(doInfos) {
|
|
516
|
+
return doInfos.filter((info) => !info.nativeRpc).map((info) => `
|
|
517
|
+
export class ${info.runtimeClassName} {
|
|
518
|
+
constructor(state, env) {
|
|
519
|
+
this.__instance = new ${info.className}(state, env)
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
async fetch(request) {
|
|
523
|
+
const url = new URL(request.url)
|
|
524
|
+
if (request.method !== 'POST' || url.pathname !== '/_rpc') {
|
|
525
|
+
return new Response('Not found', { status: 404 })
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
try {
|
|
529
|
+
const payload = await request.json()
|
|
530
|
+
const method = payload?.method
|
|
531
|
+
const params = Array.isArray(payload?.params) ? payload.params : []
|
|
532
|
+
const target = this.__instance?.[method]
|
|
533
|
+
|
|
534
|
+
if (typeof target !== 'function') {
|
|
535
|
+
return new Response(JSON.stringify({
|
|
536
|
+
ok: false,
|
|
537
|
+
error: { message: 'Method not found: ' + String(method) }
|
|
538
|
+
}), {
|
|
539
|
+
status: 404,
|
|
540
|
+
headers: { 'Content-Type': 'application/json' }
|
|
541
|
+
})
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
let result = await target.apply(this.__instance, params)
|
|
545
|
+
result = __encodeTransport(result)
|
|
546
|
+
|
|
547
|
+
return new Response(JSON.stringify({ ok: true, result }), {
|
|
548
|
+
headers: { 'Content-Type': 'application/json' }
|
|
549
|
+
})
|
|
550
|
+
} catch (error) {
|
|
551
|
+
return new Response(JSON.stringify({
|
|
552
|
+
ok: false,
|
|
553
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
554
|
+
}), {
|
|
555
|
+
status: 500,
|
|
556
|
+
headers: { 'Content-Type': 'application/json' }
|
|
557
|
+
})
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}`.trim()).join(`
|
|
561
|
+
|
|
562
|
+
`);
|
|
563
|
+
}
|
|
564
|
+
async function bundleDurableObjectModules(configDir, doInfos, transportFile) {
|
|
565
|
+
const virtualImports = [];
|
|
566
|
+
const virtualExports = [];
|
|
567
|
+
if (transportFile) {
|
|
568
|
+
const transportPath = join2(configDir, transportFile);
|
|
569
|
+
virtualImports.push(`import { transport } from '${transportPath.replace(/\\/g, "/")}'`);
|
|
570
|
+
virtualExports.push("export { transport }");
|
|
571
|
+
}
|
|
572
|
+
for (const info of doInfos) {
|
|
573
|
+
virtualImports.push(`import { ${info.className} } from '${info.scriptPath.replace(/\\/g, "/")}'`);
|
|
574
|
+
virtualExports.push(`export { ${info.className} }`);
|
|
575
|
+
}
|
|
576
|
+
if (virtualImports.length === 0) {
|
|
577
|
+
return "";
|
|
578
|
+
}
|
|
579
|
+
const virtualEntry = [...virtualImports, "", ...virtualExports].join(`
|
|
580
|
+
`);
|
|
581
|
+
const virtualPath = join2(configDir, ".devflare", "__test_entry.ts");
|
|
582
|
+
mkdirSync(dirname2(virtualPath), { recursive: true });
|
|
583
|
+
writeFileSync(virtualPath, virtualEntry);
|
|
584
|
+
const bun = getBunRuntime();
|
|
585
|
+
if (!bun) {
|
|
586
|
+
throw new Error("Bun runtime is required for createTestContext with Durable Objects");
|
|
587
|
+
}
|
|
588
|
+
const result = await bun.build({
|
|
589
|
+
entrypoints: [virtualPath],
|
|
590
|
+
target: "browser",
|
|
591
|
+
format: "esm",
|
|
592
|
+
minify: false,
|
|
593
|
+
external: ["cloudflare:workers", "cloudflare:*"]
|
|
594
|
+
});
|
|
595
|
+
if (!result.success) {
|
|
596
|
+
throw new Error(`Failed to bundle test entry: ${result.logs.join(`
|
|
597
|
+
`)}`);
|
|
598
|
+
}
|
|
599
|
+
return await result.outputs[0].text();
|
|
600
|
+
}
|
|
601
|
+
async function buildDurableObjectGateway(config, configDir, transportFile) {
|
|
602
|
+
const workflowEntrypointScript = await bundleWorkflowEntrypointScript(config, configDir);
|
|
603
|
+
if (!config.bindings?.durableObjects) {
|
|
604
|
+
return {
|
|
605
|
+
script: buildGatewayScript(workflowEntrypointScript, "")
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
const { doConfig, doInfos } = await resolveLocalDurableObjects(config, configDir);
|
|
609
|
+
const bundledCode = await bundleDurableObjectModules(configDir, doInfos, transportFile);
|
|
610
|
+
const wrapperCode = buildWrapperCode(doInfos);
|
|
611
|
+
const entrypointCode = [workflowEntrypointScript, bundledCode].filter(Boolean).join(`
|
|
612
|
+
|
|
613
|
+
`);
|
|
614
|
+
const nativeRpcBindingNames = doInfos.filter((info) => info.nativeRpc).map((info) => info.name);
|
|
615
|
+
return {
|
|
616
|
+
durableObjects: doConfig,
|
|
617
|
+
script: buildGatewayScript(entrypointCode, wrapperCode, nativeRpcBindingNames)
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// src/test/remote-cloudflare.ts
|
|
622
|
+
function createRemoteCloudflareClient(accountId) {
|
|
623
|
+
let resolvedAccountId = null;
|
|
624
|
+
async function getAccountId() {
|
|
625
|
+
if (accountId) {
|
|
626
|
+
return accountId;
|
|
627
|
+
}
|
|
628
|
+
if (resolvedAccountId) {
|
|
629
|
+
return resolvedAccountId;
|
|
630
|
+
}
|
|
631
|
+
const primary = await getPrimaryAccount();
|
|
632
|
+
if (!primary) {
|
|
633
|
+
throw new Error("No Cloudflare account found. Run: bunx wrangler login");
|
|
634
|
+
}
|
|
635
|
+
const { accountId: effectiveId } = await getEffectiveAccountId(primary.id);
|
|
636
|
+
resolvedAccountId = effectiveId;
|
|
637
|
+
return effectiveId;
|
|
638
|
+
}
|
|
639
|
+
async function getToken() {
|
|
640
|
+
const token = await getApiToken();
|
|
641
|
+
if (!token) {
|
|
642
|
+
throw new Error("Not authenticated. Run: bunx wrangler login");
|
|
643
|
+
}
|
|
644
|
+
return token;
|
|
645
|
+
}
|
|
646
|
+
async function jsonRequest(options) {
|
|
647
|
+
const [acctId, token] = await Promise.all([getAccountId(), getToken()]);
|
|
648
|
+
const response = await fetch(`https://api.cloudflare.com/client/v4/accounts/${acctId}${options.path}`, {
|
|
649
|
+
method: options.method,
|
|
650
|
+
headers: {
|
|
651
|
+
Authorization: `Bearer ${token}`,
|
|
652
|
+
"Content-Type": options.contentType ?? "application/json"
|
|
653
|
+
},
|
|
654
|
+
body: options.body
|
|
655
|
+
});
|
|
656
|
+
if (!response.ok) {
|
|
657
|
+
const errorText = await response.text();
|
|
658
|
+
throw new Error(`${options.serviceLabel} API error (${response.status}): ${errorText}`);
|
|
659
|
+
}
|
|
660
|
+
const result = await response.json();
|
|
661
|
+
if (!result.success) {
|
|
662
|
+
const message = result.errors?.[0]?.message || `Unknown ${options.serviceLabel} error`;
|
|
663
|
+
throw new Error(`${options.serviceLabel} API error: ${message}`);
|
|
664
|
+
}
|
|
665
|
+
return result.result;
|
|
666
|
+
}
|
|
667
|
+
return {
|
|
668
|
+
getAccountId,
|
|
669
|
+
getToken,
|
|
670
|
+
jsonRequest
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// src/test/remote-ai.ts
|
|
675
|
+
function encodePathSegment(value) {
|
|
676
|
+
return encodeURIComponent(value);
|
|
677
|
+
}
|
|
678
|
+
function applyExtraHeaders(headers, extraHeaders) {
|
|
679
|
+
if (!extraHeaders) {
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
for (const [key, value] of Object.entries(extraHeaders)) {
|
|
683
|
+
headers.set(key, String(value));
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
function createRemoteAIGateway(cloudflare, gatewayId, owner) {
|
|
687
|
+
const encodedGatewayId = encodePathSegment(gatewayId);
|
|
688
|
+
const gateway = {
|
|
689
|
+
async patchLog(logId, data) {
|
|
690
|
+
await cloudflare.jsonRequest({
|
|
691
|
+
method: "PATCH",
|
|
692
|
+
path: `/ai-gateway/gateways/${encodedGatewayId}/logs/${encodePathSegment(logId)}`,
|
|
693
|
+
serviceLabel: "AI Gateway",
|
|
694
|
+
body: JSON.stringify(data)
|
|
695
|
+
});
|
|
696
|
+
},
|
|
697
|
+
async getLog(logId) {
|
|
698
|
+
return cloudflare.jsonRequest({
|
|
699
|
+
method: "GET",
|
|
700
|
+
path: `/ai-gateway/gateways/${encodedGatewayId}/logs/${encodePathSegment(logId)}`,
|
|
701
|
+
serviceLabel: "AI Gateway"
|
|
702
|
+
});
|
|
703
|
+
},
|
|
704
|
+
async getUrl(provider) {
|
|
705
|
+
const accountId = await cloudflare.getAccountId();
|
|
706
|
+
const baseUrl = `https://gateway.ai.cloudflare.com/v1/${encodePathSegment(accountId)}/${encodedGatewayId}`;
|
|
707
|
+
return provider ? `${baseUrl}/${encodePathSegment(provider)}` : `${baseUrl}/`;
|
|
708
|
+
},
|
|
709
|
+
async run(data, options) {
|
|
710
|
+
const [url, token] = await Promise.all([gateway.getUrl(), cloudflare.getToken()]);
|
|
711
|
+
const headers = new Headers({
|
|
712
|
+
Authorization: `Bearer ${token}`,
|
|
713
|
+
"Content-Type": "application/json"
|
|
714
|
+
});
|
|
715
|
+
applyExtraHeaders(headers, options?.extraHeaders);
|
|
716
|
+
const response = await fetch(url, {
|
|
717
|
+
method: "POST",
|
|
718
|
+
headers,
|
|
719
|
+
body: JSON.stringify(data),
|
|
720
|
+
signal: options?.signal
|
|
721
|
+
});
|
|
722
|
+
const logId = response.headers.get("cf-aig-log-id") ?? response.headers.get("cf-ai-gateway-log-id");
|
|
723
|
+
if (logId) {
|
|
724
|
+
owner.aiGatewayLogId = logId;
|
|
725
|
+
}
|
|
726
|
+
if (!response.ok) {
|
|
727
|
+
const errorText = await response.text();
|
|
728
|
+
throw new Error(`AI Gateway API error (${response.status}): ${errorText}`);
|
|
729
|
+
}
|
|
730
|
+
return response;
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
return gateway;
|
|
734
|
+
}
|
|
735
|
+
function createRemoteAI(accountId) {
|
|
736
|
+
const cloudflare = createRemoteCloudflareClient(accountId);
|
|
737
|
+
const ai = {
|
|
738
|
+
aiGatewayLogId: null,
|
|
739
|
+
async run(model, inputs) {
|
|
740
|
+
return cloudflare.jsonRequest({
|
|
741
|
+
method: "POST",
|
|
742
|
+
path: `/ai/run/${model}`,
|
|
743
|
+
serviceLabel: "AI",
|
|
744
|
+
body: JSON.stringify(inputs)
|
|
745
|
+
});
|
|
746
|
+
},
|
|
747
|
+
gateway(gatewayId) {
|
|
748
|
+
return createRemoteAIGateway(cloudflare, gatewayId, ai);
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
return ai;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// src/test/remote-vectorize.ts
|
|
755
|
+
function createRemoteVectorize(indexName, accountId) {
|
|
756
|
+
const cloudflare = createRemoteCloudflareClient(accountId);
|
|
757
|
+
async function apiRequest(method, endpoint, body) {
|
|
758
|
+
return cloudflare.jsonRequest({
|
|
759
|
+
method,
|
|
760
|
+
path: `/vectorize/v2/indexes/${indexName}${endpoint}`,
|
|
761
|
+
serviceLabel: "Vectorize",
|
|
762
|
+
body: body ? JSON.stringify(body) : undefined
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
async function ndjsonRequest(endpoint, vectors) {
|
|
766
|
+
const ndjson = vectors.map((v) => JSON.stringify(v)).join(`
|
|
767
|
+
`);
|
|
768
|
+
return cloudflare.jsonRequest({
|
|
769
|
+
method: "POST",
|
|
770
|
+
path: `/vectorize/v2/indexes/${indexName}${endpoint}`,
|
|
771
|
+
serviceLabel: "Vectorize",
|
|
772
|
+
contentType: "application/x-ndjson",
|
|
773
|
+
body: ndjson
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
const vectorize = {
|
|
777
|
+
async describe() {
|
|
778
|
+
return apiRequest("GET", "");
|
|
779
|
+
},
|
|
780
|
+
async query(vector, options) {
|
|
781
|
+
const vectorArray = Array.isArray(vector) ? vector : Array.from(vector);
|
|
782
|
+
return apiRequest("POST", "/query", {
|
|
783
|
+
vector: vectorArray,
|
|
784
|
+
topK: options?.topK ?? 10,
|
|
785
|
+
returnValues: options?.returnValues ?? false,
|
|
786
|
+
returnMetadata: options?.returnMetadata ?? "none",
|
|
787
|
+
namespace: options?.namespace,
|
|
788
|
+
filter: options?.filter
|
|
789
|
+
});
|
|
790
|
+
},
|
|
791
|
+
async insert(vectors) {
|
|
792
|
+
const result = await ndjsonRequest("/insert", vectors);
|
|
793
|
+
return {
|
|
794
|
+
count: result.count,
|
|
795
|
+
ids: result.ids || vectors.map((v) => v.id)
|
|
796
|
+
};
|
|
797
|
+
},
|
|
798
|
+
async upsert(vectors) {
|
|
799
|
+
const result = await ndjsonRequest("/upsert", vectors);
|
|
800
|
+
return {
|
|
801
|
+
count: result.count,
|
|
802
|
+
ids: result.ids || vectors.map((v) => v.id)
|
|
803
|
+
};
|
|
804
|
+
},
|
|
805
|
+
async deleteByIds(ids) {
|
|
806
|
+
const result = await apiRequest("POST", "/delete-by-ids", { ids });
|
|
807
|
+
return {
|
|
808
|
+
count: result.count,
|
|
809
|
+
ids
|
|
810
|
+
};
|
|
811
|
+
},
|
|
812
|
+
async getByIds(ids) {
|
|
813
|
+
return apiRequest("POST", "/get-by-ids", { ids });
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
return vectorize;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// src/test/utilities/context.ts
|
|
820
|
+
function createMockTestContext(options = {}) {
|
|
821
|
+
const waitUntilPromises = [];
|
|
822
|
+
const ctx = {
|
|
823
|
+
waitUntil(promise) {
|
|
824
|
+
waitUntilPromises.push(promise);
|
|
825
|
+
},
|
|
826
|
+
passThroughOnException() {},
|
|
827
|
+
props: {}
|
|
828
|
+
};
|
|
829
|
+
return {
|
|
830
|
+
env: options.env ?? {},
|
|
831
|
+
ctx,
|
|
832
|
+
request: options.request ?? null,
|
|
833
|
+
waitUntilPromises
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
async function withTestContext(options, handler) {
|
|
837
|
+
const testCtx = createMockTestContext(options);
|
|
838
|
+
return runWithContext(testCtx.env, testCtx.ctx, options.request ?? null, handler, options.type ?? "fetch");
|
|
839
|
+
}
|
|
840
|
+
// src/test/utilities/kv.ts
|
|
841
|
+
function createMockKV(initialData = {}) {
|
|
842
|
+
const store = new Map;
|
|
843
|
+
const metadata = new Map;
|
|
844
|
+
const encoder = new TextEncoder;
|
|
845
|
+
const decoder = new TextDecoder;
|
|
846
|
+
for (const [key, value] of Object.entries(initialData)) {
|
|
847
|
+
store.set(key, encoder.encode(value));
|
|
848
|
+
}
|
|
849
|
+
const toBytes = async (value) => {
|
|
850
|
+
if (typeof value === "string") {
|
|
851
|
+
return encoder.encode(value);
|
|
852
|
+
}
|
|
853
|
+
if (value instanceof ArrayBuffer) {
|
|
854
|
+
return new Uint8Array(value.slice(0));
|
|
855
|
+
}
|
|
856
|
+
if (ArrayBuffer.isView(value)) {
|
|
857
|
+
const view = value;
|
|
858
|
+
const copy = new Uint8Array(view.byteLength);
|
|
859
|
+
copy.set(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
|
|
860
|
+
return copy;
|
|
861
|
+
}
|
|
862
|
+
const reader = value.getReader();
|
|
863
|
+
const chunks = [];
|
|
864
|
+
let total = 0;
|
|
865
|
+
while (true) {
|
|
866
|
+
const result = await reader.read();
|
|
867
|
+
if (result.done)
|
|
868
|
+
break;
|
|
869
|
+
if (result.value) {
|
|
870
|
+
const chunk = result.value instanceof Uint8Array ? result.value : new Uint8Array(result.value);
|
|
871
|
+
chunks.push(chunk);
|
|
872
|
+
total += chunk.length;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
const combined = new Uint8Array(total);
|
|
876
|
+
let offset = 0;
|
|
877
|
+
for (const chunk of chunks) {
|
|
878
|
+
combined.set(chunk, offset);
|
|
879
|
+
offset += chunk.length;
|
|
880
|
+
}
|
|
881
|
+
return combined;
|
|
882
|
+
};
|
|
883
|
+
const decodeBytes = (bytes, type) => {
|
|
884
|
+
switch (type) {
|
|
885
|
+
case "json":
|
|
886
|
+
return JSON.parse(decoder.decode(bytes));
|
|
887
|
+
case "arrayBuffer": {
|
|
888
|
+
const copy = new Uint8Array(bytes.length);
|
|
889
|
+
copy.set(bytes);
|
|
890
|
+
return copy.buffer;
|
|
891
|
+
}
|
|
892
|
+
case "stream": {
|
|
893
|
+
const copy = new Uint8Array(bytes.length);
|
|
894
|
+
copy.set(bytes);
|
|
895
|
+
return new ReadableStream({
|
|
896
|
+
start(controller) {
|
|
897
|
+
controller.enqueue(copy);
|
|
898
|
+
controller.close();
|
|
899
|
+
}
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
default:
|
|
903
|
+
return decoder.decode(bytes);
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
const resolveType = (options) => {
|
|
907
|
+
const type = typeof options === "string" ? options : options?.type ?? "text";
|
|
908
|
+
return type;
|
|
909
|
+
};
|
|
910
|
+
return {
|
|
911
|
+
async get(key, options) {
|
|
912
|
+
const bytes = store.get(key);
|
|
913
|
+
if (bytes === undefined)
|
|
914
|
+
return null;
|
|
915
|
+
return decodeBytes(bytes, resolveType(options));
|
|
916
|
+
},
|
|
917
|
+
async put(key, value, _options) {
|
|
918
|
+
const bytes = await toBytes(value);
|
|
919
|
+
store.set(key, bytes);
|
|
920
|
+
},
|
|
921
|
+
async delete(key) {
|
|
922
|
+
store.delete(key);
|
|
923
|
+
metadata.delete(key);
|
|
924
|
+
},
|
|
925
|
+
async list(options) {
|
|
926
|
+
const prefix = options?.prefix ?? "";
|
|
927
|
+
const limit = options?.limit ?? 1000;
|
|
928
|
+
const keys = Array.from(store.keys()).filter((key) => key.startsWith(prefix)).slice(0, limit).map((name) => ({ name }));
|
|
929
|
+
return {
|
|
930
|
+
keys,
|
|
931
|
+
list_complete: keys.length < limit,
|
|
932
|
+
cursor: undefined
|
|
933
|
+
};
|
|
934
|
+
},
|
|
935
|
+
async getWithMetadata(key, options) {
|
|
936
|
+
const bytes = store.get(key);
|
|
937
|
+
return {
|
|
938
|
+
value: bytes === undefined ? null : decodeBytes(bytes, resolveType(options)),
|
|
939
|
+
metadata: metadata.get(key) ?? null
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
// src/test/utilities/d1.ts
|
|
945
|
+
var TABLE_NAME_RE = /(?:from|into|update)\s+["'`]?([a-zA-Z_][a-zA-Z0-9_]*)["'`]?/i;
|
|
946
|
+
var extractTable = (sql) => {
|
|
947
|
+
const match = TABLE_NAME_RE.exec(sql);
|
|
948
|
+
return match ? match[1] : null;
|
|
949
|
+
};
|
|
950
|
+
var detectOp = (sql) => {
|
|
951
|
+
const trimmed = sql.trimStart().toLowerCase();
|
|
952
|
+
if (trimmed.startsWith("select"))
|
|
953
|
+
return "select";
|
|
954
|
+
if (trimmed.startsWith("insert"))
|
|
955
|
+
return "insert";
|
|
956
|
+
if (trimmed.startsWith("update"))
|
|
957
|
+
return "update";
|
|
958
|
+
if (trimmed.startsWith("delete"))
|
|
959
|
+
return "delete";
|
|
960
|
+
return "other";
|
|
961
|
+
};
|
|
962
|
+
function createMockD1(mockResultsOrOptions = []) {
|
|
963
|
+
const options = Array.isArray(mockResultsOrOptions) ? { results: mockResultsOrOptions } : mockResultsOrOptions;
|
|
964
|
+
const tables = new Map;
|
|
965
|
+
for (const [name, rows] of Object.entries(options.fixtures ?? {})) {
|
|
966
|
+
tables.set(name, [...rows]);
|
|
967
|
+
}
|
|
968
|
+
const fallback = options.results ?? [];
|
|
969
|
+
const resolveRows = (sql) => {
|
|
970
|
+
const op = detectOp(sql);
|
|
971
|
+
const table = extractTable(sql);
|
|
972
|
+
if (table && tables.has(table)) {
|
|
973
|
+
return { rows: tables.get(table) ?? [], op, table };
|
|
974
|
+
}
|
|
975
|
+
return { rows: [...fallback], op, table };
|
|
976
|
+
};
|
|
977
|
+
const createStatement = (sql) => {
|
|
978
|
+
let boundValues = [];
|
|
979
|
+
const statement = {
|
|
980
|
+
bind(...values) {
|
|
981
|
+
boundValues = values;
|
|
982
|
+
return statement;
|
|
983
|
+
},
|
|
984
|
+
async first(column) {
|
|
985
|
+
const { rows } = resolveRows(sql);
|
|
986
|
+
const row = rows[0];
|
|
987
|
+
if (!row)
|
|
988
|
+
return null;
|
|
989
|
+
if (column)
|
|
990
|
+
return row[column];
|
|
991
|
+
return row;
|
|
992
|
+
},
|
|
993
|
+
async all() {
|
|
994
|
+
const { rows } = resolveRows(sql);
|
|
995
|
+
return {
|
|
996
|
+
results: rows,
|
|
997
|
+
success: true,
|
|
998
|
+
meta: { duration: 0, changes: 0, last_row_id: 0 }
|
|
999
|
+
};
|
|
1000
|
+
},
|
|
1001
|
+
async run() {
|
|
1002
|
+
const { op, table } = resolveRows(sql);
|
|
1003
|
+
let changes = 0;
|
|
1004
|
+
let lastRowId = 0;
|
|
1005
|
+
if (op === "insert" && table) {
|
|
1006
|
+
const rows = tables.get(table) ?? [];
|
|
1007
|
+
const bound = boundValues.length > 0 ? Object.fromEntries(boundValues.map((v, i) => [`col${i}`, v])) : {};
|
|
1008
|
+
rows.push(bound);
|
|
1009
|
+
tables.set(table, rows);
|
|
1010
|
+
changes = 1;
|
|
1011
|
+
lastRowId = rows.length;
|
|
1012
|
+
} else if (op === "delete" && table) {
|
|
1013
|
+
const rows = tables.get(table) ?? [];
|
|
1014
|
+
changes = rows.length;
|
|
1015
|
+
tables.set(table, []);
|
|
1016
|
+
} else if (op === "update" && table) {
|
|
1017
|
+
changes = (tables.get(table) ?? []).length;
|
|
1018
|
+
}
|
|
1019
|
+
return {
|
|
1020
|
+
results: [],
|
|
1021
|
+
success: true,
|
|
1022
|
+
meta: { duration: 0, changes, last_row_id: lastRowId }
|
|
1023
|
+
};
|
|
1024
|
+
},
|
|
1025
|
+
async raw(_options) {
|
|
1026
|
+
const { rows } = resolveRows(sql);
|
|
1027
|
+
return rows.map((row) => Object.values(row));
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
return statement;
|
|
1031
|
+
};
|
|
1032
|
+
return {
|
|
1033
|
+
prepare(query) {
|
|
1034
|
+
return createStatement(query);
|
|
1035
|
+
},
|
|
1036
|
+
async exec(_query) {
|
|
1037
|
+
return {
|
|
1038
|
+
results: [],
|
|
1039
|
+
success: true,
|
|
1040
|
+
meta: { duration: 0, changes: 0, last_row_id: 0 }
|
|
1041
|
+
};
|
|
1042
|
+
},
|
|
1043
|
+
async batch(statements) {
|
|
1044
|
+
return statements.map(() => ({
|
|
1045
|
+
results: [],
|
|
1046
|
+
success: true,
|
|
1047
|
+
meta: { duration: 0, changes: 0, last_row_id: 0 }
|
|
1048
|
+
}));
|
|
1049
|
+
},
|
|
1050
|
+
async dump() {
|
|
1051
|
+
return new ArrayBuffer(0);
|
|
1052
|
+
},
|
|
1053
|
+
withSession(_constraintOrBookmark) {
|
|
1054
|
+
return this;
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
// src/test/utilities/r2.ts
|
|
1059
|
+
function createMockR2() {
|
|
1060
|
+
const store = new Map;
|
|
1061
|
+
const createR2Object = (key, content, metadata) => {
|
|
1062
|
+
const encoder = new TextEncoder;
|
|
1063
|
+
const data = encoder.encode(content);
|
|
1064
|
+
return {
|
|
1065
|
+
key,
|
|
1066
|
+
version: "1",
|
|
1067
|
+
size: data.length,
|
|
1068
|
+
etag: `"${key}-etag"`,
|
|
1069
|
+
httpEtag: `"${key}-etag"`,
|
|
1070
|
+
uploaded: new Date,
|
|
1071
|
+
httpMetadata: metadata ?? {},
|
|
1072
|
+
customMetadata: {},
|
|
1073
|
+
checksums: {},
|
|
1074
|
+
storageClass: "Standard",
|
|
1075
|
+
body: new ReadableStream({
|
|
1076
|
+
start(controller) {
|
|
1077
|
+
controller.enqueue(data);
|
|
1078
|
+
controller.close();
|
|
1079
|
+
}
|
|
1080
|
+
}),
|
|
1081
|
+
bodyUsed: false,
|
|
1082
|
+
async arrayBuffer() {
|
|
1083
|
+
return new Uint8Array(data).buffer;
|
|
1084
|
+
},
|
|
1085
|
+
async text() {
|
|
1086
|
+
return content;
|
|
1087
|
+
},
|
|
1088
|
+
async json() {
|
|
1089
|
+
return JSON.parse(content);
|
|
1090
|
+
},
|
|
1091
|
+
async blob() {
|
|
1092
|
+
return new Blob([data]);
|
|
1093
|
+
},
|
|
1094
|
+
writeHttpMetadata(headers) {}
|
|
1095
|
+
};
|
|
1096
|
+
};
|
|
1097
|
+
return {
|
|
1098
|
+
async put(key, value, options) {
|
|
1099
|
+
let content;
|
|
1100
|
+
if (typeof value === "string") {
|
|
1101
|
+
content = value;
|
|
1102
|
+
} else if (value instanceof ArrayBuffer) {
|
|
1103
|
+
content = new TextDecoder().decode(value);
|
|
1104
|
+
} else if (value instanceof Blob) {
|
|
1105
|
+
content = await value.text();
|
|
1106
|
+
} else if (value === null) {
|
|
1107
|
+
content = "";
|
|
1108
|
+
} else {
|
|
1109
|
+
const reader = value.getReader();
|
|
1110
|
+
const chunks = [];
|
|
1111
|
+
let done = false;
|
|
1112
|
+
while (!done) {
|
|
1113
|
+
const result = await reader.read();
|
|
1114
|
+
done = result.done;
|
|
1115
|
+
if (result.value) {
|
|
1116
|
+
chunks.push(new TextDecoder().decode(result.value));
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
content = chunks.join("");
|
|
1120
|
+
}
|
|
1121
|
+
store.set(key, { content });
|
|
1122
|
+
return createR2Object(key, content);
|
|
1123
|
+
},
|
|
1124
|
+
async get(key, options) {
|
|
1125
|
+
const item = store.get(key);
|
|
1126
|
+
if (!item)
|
|
1127
|
+
return null;
|
|
1128
|
+
return createR2Object(key, item.content, item.metadata);
|
|
1129
|
+
},
|
|
1130
|
+
async head(key) {
|
|
1131
|
+
const item = store.get(key);
|
|
1132
|
+
if (!item)
|
|
1133
|
+
return null;
|
|
1134
|
+
return {
|
|
1135
|
+
key,
|
|
1136
|
+
version: "1",
|
|
1137
|
+
size: new TextEncoder().encode(item.content).length,
|
|
1138
|
+
etag: `"${key}-etag"`,
|
|
1139
|
+
httpEtag: `"${key}-etag"`,
|
|
1140
|
+
uploaded: new Date,
|
|
1141
|
+
httpMetadata: item.metadata ?? {},
|
|
1142
|
+
customMetadata: {},
|
|
1143
|
+
checksums: {},
|
|
1144
|
+
storageClass: "Standard",
|
|
1145
|
+
writeHttpMetadata(headers) {}
|
|
1146
|
+
};
|
|
1147
|
+
},
|
|
1148
|
+
async delete(keys) {
|
|
1149
|
+
const keyArray = Array.isArray(keys) ? keys : [keys];
|
|
1150
|
+
for (const key of keyArray) {
|
|
1151
|
+
store.delete(key);
|
|
1152
|
+
}
|
|
1153
|
+
},
|
|
1154
|
+
async list(options) {
|
|
1155
|
+
const objects = Array.from(store.entries()).map(([key, { content, metadata }]) => createR2Object(key, content, metadata));
|
|
1156
|
+
return {
|
|
1157
|
+
objects,
|
|
1158
|
+
truncated: false,
|
|
1159
|
+
delimitedPrefixes: []
|
|
1160
|
+
};
|
|
1161
|
+
},
|
|
1162
|
+
async createMultipartUpload(key, options) {
|
|
1163
|
+
throw new Error("Multipart upload not implemented in mock");
|
|
1164
|
+
},
|
|
1165
|
+
async resumeMultipartUpload(key, uploadId) {
|
|
1166
|
+
throw new Error("Multipart upload not implemented in mock");
|
|
1167
|
+
}
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
// src/test/utilities/queue.ts
|
|
1171
|
+
function createMockQueue() {
|
|
1172
|
+
const messages = [];
|
|
1173
|
+
const metrics = {
|
|
1174
|
+
backlogCount: 0,
|
|
1175
|
+
backlogBytes: 0
|
|
1176
|
+
};
|
|
1177
|
+
const response = { metadata: { metrics } };
|
|
1178
|
+
return {
|
|
1179
|
+
async metrics() {
|
|
1180
|
+
return metrics;
|
|
1181
|
+
},
|
|
1182
|
+
async send(message, options) {
|
|
1183
|
+
messages.push({ body: message, options });
|
|
1184
|
+
return response;
|
|
1185
|
+
},
|
|
1186
|
+
async sendBatch(batch, options) {
|
|
1187
|
+
for (const message of batch) {
|
|
1188
|
+
messages.push({
|
|
1189
|
+
body: message.body,
|
|
1190
|
+
options: {
|
|
1191
|
+
contentType: message.contentType,
|
|
1192
|
+
delaySeconds: message.delaySeconds ?? options?.delaySeconds
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
return response;
|
|
1197
|
+
},
|
|
1198
|
+
_getMessages() {
|
|
1199
|
+
return messages;
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
// src/test/utilities/platform.ts
|
|
1204
|
+
function createMockRateLimit(options = {}) {
|
|
1205
|
+
const limit = options.limit ?? Number.MAX_SAFE_INTEGER;
|
|
1206
|
+
const periodMs = (options.period ?? 60) * 1000;
|
|
1207
|
+
const windows = new Map;
|
|
1208
|
+
return {
|
|
1209
|
+
async limit({ key }) {
|
|
1210
|
+
const now = Date.now();
|
|
1211
|
+
const existing = windows.get(key);
|
|
1212
|
+
if (!existing || existing.resetAt <= now) {
|
|
1213
|
+
windows.set(key, { count: 1, resetAt: now + periodMs });
|
|
1214
|
+
return { success: limit >= 1 };
|
|
1215
|
+
}
|
|
1216
|
+
existing.count += 1;
|
|
1217
|
+
return { success: existing.count <= limit };
|
|
1218
|
+
}
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
function createMockVersionMetadata(metadata = {}) {
|
|
1222
|
+
return {
|
|
1223
|
+
id: metadata.id ?? "devflare-local-version",
|
|
1224
|
+
tag: metadata.tag ?? "local",
|
|
1225
|
+
timestamp: metadata.timestamp ?? "1970-01-01T00:00:00.000Z"
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
function createMockSecretsStoreSecret(value) {
|
|
1229
|
+
return {
|
|
1230
|
+
async get() {
|
|
1231
|
+
return value;
|
|
1232
|
+
}
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
function defaultPortForDatabaseUrl(url) {
|
|
1236
|
+
if (url.port) {
|
|
1237
|
+
return Number(url.port);
|
|
1238
|
+
}
|
|
1239
|
+
return url.protocol === "mysql:" ? 3306 : 5432;
|
|
1240
|
+
}
|
|
1241
|
+
function createMockHyperdrive(connectionString) {
|
|
1242
|
+
const url = new URL(connectionString);
|
|
1243
|
+
return {
|
|
1244
|
+
connectionString,
|
|
1245
|
+
host: url.hostname,
|
|
1246
|
+
port: defaultPortForDatabaseUrl(url),
|
|
1247
|
+
user: decodeURIComponent(url.username),
|
|
1248
|
+
password: decodeURIComponent(url.password),
|
|
1249
|
+
database: decodeURIComponent(url.pathname.replace(/^\//, "")),
|
|
1250
|
+
connect() {
|
|
1251
|
+
throw new Error("Mock Hyperdrive connect() is not implemented. Use connectionString with your database client, or run a Miniflare-backed test for socket behavior.");
|
|
1252
|
+
}
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
function createDefaultWorkerStub() {
|
|
1256
|
+
return {
|
|
1257
|
+
getEntrypoint() {
|
|
1258
|
+
throw new Error("Mock WorkerLoader stub has no entrypoint. Pass createMockWorkerLoader({ stub }) for behavior.");
|
|
1259
|
+
},
|
|
1260
|
+
getDurableObjectClass() {
|
|
1261
|
+
throw new Error("Mock WorkerLoader stub has no Durable Object class. Pass createMockWorkerLoader({ stub }) for behavior.");
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
function createMockWorkerLoader(options = {}) {
|
|
1266
|
+
const stub = options.stub ?? createDefaultWorkerStub();
|
|
1267
|
+
return {
|
|
1268
|
+
get(_name, _getCode) {
|
|
1269
|
+
return stub;
|
|
1270
|
+
},
|
|
1271
|
+
load(_code) {
|
|
1272
|
+
return stub;
|
|
1273
|
+
}
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
function defaultMTLSCertificateHandler() {
|
|
1277
|
+
throw new Error("Mock mTLS Certificate Fetcher has no handler. Pass createMockMTLSCertificate(handler) for behavior.");
|
|
1278
|
+
}
|
|
1279
|
+
function createMockMTLSCertificate(handler = defaultMTLSCertificateHandler) {
|
|
1280
|
+
return {
|
|
1281
|
+
async fetch(input, init) {
|
|
1282
|
+
return handler(input, init);
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
function createMockDispatchNamespace(options = {}) {
|
|
1287
|
+
return {
|
|
1288
|
+
get(name) {
|
|
1289
|
+
const worker = options.workers?.[name];
|
|
1290
|
+
if (!worker) {
|
|
1291
|
+
throw new Error(`Mock DispatchNamespace has no worker named "${name}".`);
|
|
1292
|
+
}
|
|
1293
|
+
return typeof worker === "function" ? createMockMTLSCertificate(worker) : worker;
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
// src/test/utilities/workflows.ts
|
|
1298
|
+
function createMockWorkflowInstance(id, options = {}) {
|
|
1299
|
+
let status = options.status ?? "queued";
|
|
1300
|
+
let output = options.output;
|
|
1301
|
+
let error = options.error;
|
|
1302
|
+
return {
|
|
1303
|
+
id,
|
|
1304
|
+
async pause() {
|
|
1305
|
+
status = "paused";
|
|
1306
|
+
},
|
|
1307
|
+
async resume() {
|
|
1308
|
+
status = "running";
|
|
1309
|
+
},
|
|
1310
|
+
async terminate() {
|
|
1311
|
+
status = "terminated";
|
|
1312
|
+
},
|
|
1313
|
+
async restart() {
|
|
1314
|
+
status = "queued";
|
|
1315
|
+
error = undefined;
|
|
1316
|
+
output = undefined;
|
|
1317
|
+
},
|
|
1318
|
+
async status() {
|
|
1319
|
+
return {
|
|
1320
|
+
status,
|
|
1321
|
+
...error && { error },
|
|
1322
|
+
...output !== undefined && { output }
|
|
1323
|
+
};
|
|
1324
|
+
},
|
|
1325
|
+
async sendEvent(_event) {}
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
function createMockWorkflow(options = {}) {
|
|
1329
|
+
const instances = new Map;
|
|
1330
|
+
let sequence = 0;
|
|
1331
|
+
for (const [id, instanceOptions] of Object.entries(options.instances ?? {})) {
|
|
1332
|
+
instances.set(id, createMockWorkflowInstance(id, instanceOptions));
|
|
1333
|
+
}
|
|
1334
|
+
const createInstance = (id) => {
|
|
1335
|
+
if (instances.has(id)) {
|
|
1336
|
+
throw new Error(`Mock Workflow already has an instance named "${id}".`);
|
|
1337
|
+
}
|
|
1338
|
+
const instance = createMockWorkflowInstance(id);
|
|
1339
|
+
instances.set(id, instance);
|
|
1340
|
+
return instance;
|
|
1341
|
+
};
|
|
1342
|
+
return {
|
|
1343
|
+
async get(id) {
|
|
1344
|
+
const instance = instances.get(id);
|
|
1345
|
+
if (!instance) {
|
|
1346
|
+
throw new Error(`Mock Workflow has no instance named "${id}".`);
|
|
1347
|
+
}
|
|
1348
|
+
return instance;
|
|
1349
|
+
},
|
|
1350
|
+
async create(options2) {
|
|
1351
|
+
const id = options2?.id ?? `mock-workflow-${++sequence}`;
|
|
1352
|
+
return createInstance(id);
|
|
1353
|
+
},
|
|
1354
|
+
async createBatch(batch) {
|
|
1355
|
+
return batch.map((options2) => {
|
|
1356
|
+
const id = options2.id ?? `mock-workflow-${++sequence}`;
|
|
1357
|
+
return createInstance(id);
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
function createMockPipeline() {
|
|
1363
|
+
const records = [];
|
|
1364
|
+
return {
|
|
1365
|
+
async send(batch) {
|
|
1366
|
+
records.push(...batch);
|
|
1367
|
+
},
|
|
1368
|
+
_getRecords() {
|
|
1369
|
+
return [...records];
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
// src/test/utilities/media.ts
|
|
1374
|
+
function createEmptyImageStream() {
|
|
1375
|
+
return new ReadableStream({
|
|
1376
|
+
start(controller) {
|
|
1377
|
+
controller.close();
|
|
1378
|
+
}
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
function createMockImageTransformationResult(response) {
|
|
1382
|
+
return {
|
|
1383
|
+
response() {
|
|
1384
|
+
return response.clone();
|
|
1385
|
+
},
|
|
1386
|
+
contentType() {
|
|
1387
|
+
return response.headers.get("Content-Type") ?? "image/png";
|
|
1388
|
+
},
|
|
1389
|
+
image() {
|
|
1390
|
+
const cloned = response.clone();
|
|
1391
|
+
return cloned.body ?? createEmptyImageStream();
|
|
1392
|
+
}
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1395
|
+
function createMockImageTransformer(response) {
|
|
1396
|
+
const transformer = {
|
|
1397
|
+
transform(_transform) {
|
|
1398
|
+
return transformer;
|
|
1399
|
+
},
|
|
1400
|
+
draw(_image, _options) {
|
|
1401
|
+
return transformer;
|
|
1402
|
+
},
|
|
1403
|
+
async output(_options) {
|
|
1404
|
+
return createMockImageTransformationResult(response);
|
|
1405
|
+
}
|
|
1406
|
+
};
|
|
1407
|
+
return transformer;
|
|
1408
|
+
}
|
|
1409
|
+
function createMockHostedImagesBinding() {
|
|
1410
|
+
const unsupported = () => {
|
|
1411
|
+
throw new Error("Mock Images hosted API is not implemented. Pass a custom ImagesBinding through createMockEnv({ images }) if your test needs hosted image behavior.");
|
|
1412
|
+
};
|
|
1413
|
+
return {
|
|
1414
|
+
image(_imageId) {
|
|
1415
|
+
return {
|
|
1416
|
+
details: unsupported,
|
|
1417
|
+
bytes: unsupported,
|
|
1418
|
+
update: unsupported,
|
|
1419
|
+
delete: unsupported
|
|
1420
|
+
};
|
|
1421
|
+
},
|
|
1422
|
+
upload: unsupported,
|
|
1423
|
+
list: unsupported
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
function createMockImagesBinding(options = {}) {
|
|
1427
|
+
const response = options.response ?? new Response("", {
|
|
1428
|
+
headers: { "Content-Type": "image/png" }
|
|
1429
|
+
});
|
|
1430
|
+
const info = options.info ?? {
|
|
1431
|
+
format: response.headers.get("Content-Type") ?? "image/png",
|
|
1432
|
+
fileSize: 0,
|
|
1433
|
+
width: 0,
|
|
1434
|
+
height: 0
|
|
1435
|
+
};
|
|
1436
|
+
return {
|
|
1437
|
+
async info(_stream, _options) {
|
|
1438
|
+
return info;
|
|
1439
|
+
},
|
|
1440
|
+
input(_stream, _options) {
|
|
1441
|
+
return createMockImageTransformer(response);
|
|
1442
|
+
},
|
|
1443
|
+
hosted: createMockHostedImagesBinding()
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
function createEmptyMediaStream() {
|
|
1447
|
+
return new ReadableStream({
|
|
1448
|
+
start(controller) {
|
|
1449
|
+
controller.close();
|
|
1450
|
+
}
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
function createMockMediaTransformationResult(response) {
|
|
1454
|
+
return {
|
|
1455
|
+
async media() {
|
|
1456
|
+
const cloned = response.clone();
|
|
1457
|
+
return cloned.body ?? createEmptyMediaStream();
|
|
1458
|
+
},
|
|
1459
|
+
async response() {
|
|
1460
|
+
return response.clone();
|
|
1461
|
+
},
|
|
1462
|
+
async contentType() {
|
|
1463
|
+
return response.headers.get("Content-Type") ?? "video/mp4";
|
|
1464
|
+
}
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
function createMockMediaTransformer(response) {
|
|
1468
|
+
const transformer = {
|
|
1469
|
+
transform(_transform) {
|
|
1470
|
+
return {
|
|
1471
|
+
output(_output) {
|
|
1472
|
+
return createMockMediaTransformationResult(response);
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
},
|
|
1476
|
+
output(_output) {
|
|
1477
|
+
return createMockMediaTransformationResult(response);
|
|
1478
|
+
}
|
|
1479
|
+
};
|
|
1480
|
+
return transformer;
|
|
1481
|
+
}
|
|
1482
|
+
function createMockMediaBinding(options = {}) {
|
|
1483
|
+
const response = options.response ?? new Response("", {
|
|
1484
|
+
headers: { "Content-Type": "video/mp4" }
|
|
1485
|
+
});
|
|
1486
|
+
return {
|
|
1487
|
+
input(_media) {
|
|
1488
|
+
return createMockMediaTransformer(response);
|
|
1489
|
+
}
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
// src/test/utilities/artifacts.ts
|
|
1493
|
+
function createArtifactTimestamp() {
|
|
1494
|
+
return new Date("2026-04-26T00:00:00.000Z").toISOString();
|
|
1495
|
+
}
|
|
1496
|
+
function createArtifactsRepoInfo(name, options = {}) {
|
|
1497
|
+
const now = createArtifactTimestamp();
|
|
1498
|
+
return {
|
|
1499
|
+
id: `repo-${name}`,
|
|
1500
|
+
name,
|
|
1501
|
+
description: options.description ?? null,
|
|
1502
|
+
defaultBranch: options.defaultBranch ?? "main",
|
|
1503
|
+
createdAt: now,
|
|
1504
|
+
updatedAt: now,
|
|
1505
|
+
lastPushAt: null,
|
|
1506
|
+
source: options.source ?? null,
|
|
1507
|
+
readOnly: options.readOnly ?? false,
|
|
1508
|
+
remote: `https://example.com/artifacts/default/${name}.git`
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
function isArtifactsBinding(value) {
|
|
1512
|
+
return typeof value.create === "function";
|
|
1513
|
+
}
|
|
1514
|
+
function createMockArtifacts(options = {}) {
|
|
1515
|
+
const repos = new Map;
|
|
1516
|
+
const tokens = new Map;
|
|
1517
|
+
const addRepo = (info) => {
|
|
1518
|
+
repos.set(info.name, info);
|
|
1519
|
+
if (!tokens.has(info.name)) {
|
|
1520
|
+
tokens.set(info.name, []);
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
for (const repo of options.repos ?? []) {
|
|
1524
|
+
addRepo({
|
|
1525
|
+
...createArtifactsRepoInfo(repo.name),
|
|
1526
|
+
...repo
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
const createToken = (repoName, scope = "write", ttl = 86400) => {
|
|
1530
|
+
const existing = tokens.get(repoName) ?? [];
|
|
1531
|
+
const id = `token-${repoName}-${existing.length + 1}`;
|
|
1532
|
+
const expiresAt = new Date(Date.parse(createArtifactTimestamp()) + ttl * 1000).toISOString();
|
|
1533
|
+
const token = {
|
|
1534
|
+
id,
|
|
1535
|
+
scope,
|
|
1536
|
+
state: "active",
|
|
1537
|
+
createdAt: createArtifactTimestamp(),
|
|
1538
|
+
expiresAt
|
|
1539
|
+
};
|
|
1540
|
+
tokens.set(repoName, [...existing, token]);
|
|
1541
|
+
return {
|
|
1542
|
+
id,
|
|
1543
|
+
plaintext: `${id}-plaintext`,
|
|
1544
|
+
scope,
|
|
1545
|
+
expiresAt
|
|
1546
|
+
};
|
|
1547
|
+
};
|
|
1548
|
+
const createRepoHandle = (info) => ({
|
|
1549
|
+
...info,
|
|
1550
|
+
async createToken(scope, ttl) {
|
|
1551
|
+
return createToken(info.name, scope, ttl);
|
|
1552
|
+
},
|
|
1553
|
+
async listTokens() {
|
|
1554
|
+
const repoTokens = tokens.get(info.name) ?? [];
|
|
1555
|
+
return {
|
|
1556
|
+
tokens: repoTokens,
|
|
1557
|
+
total: repoTokens.length
|
|
1558
|
+
};
|
|
1559
|
+
},
|
|
1560
|
+
async revokeToken(tokenOrId) {
|
|
1561
|
+
const repoTokens = tokens.get(info.name) ?? [];
|
|
1562
|
+
const index = repoTokens.findIndex((token) => token.id === tokenOrId);
|
|
1563
|
+
if (index === -1) {
|
|
1564
|
+
return false;
|
|
1565
|
+
}
|
|
1566
|
+
repoTokens[index] = {
|
|
1567
|
+
...repoTokens[index],
|
|
1568
|
+
state: "revoked"
|
|
1569
|
+
};
|
|
1570
|
+
tokens.set(info.name, repoTokens);
|
|
1571
|
+
return true;
|
|
1572
|
+
},
|
|
1573
|
+
async fork(name, forkOptions) {
|
|
1574
|
+
return createRepo(name, {
|
|
1575
|
+
description: forkOptions?.description ?? info.description ?? undefined,
|
|
1576
|
+
readOnly: forkOptions?.readOnly ?? info.readOnly,
|
|
1577
|
+
setDefaultBranch: info.defaultBranch,
|
|
1578
|
+
source: `artifacts:default/${info.name}`
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
});
|
|
1582
|
+
const createRepo = async (name, createOptions = {}) => {
|
|
1583
|
+
const info = createArtifactsRepoInfo(name, {
|
|
1584
|
+
description: createOptions.description,
|
|
1585
|
+
readOnly: createOptions.readOnly,
|
|
1586
|
+
defaultBranch: createOptions.setDefaultBranch,
|
|
1587
|
+
source: createOptions.source
|
|
1588
|
+
});
|
|
1589
|
+
addRepo(info);
|
|
1590
|
+
const token = createToken(name);
|
|
1591
|
+
return {
|
|
1592
|
+
id: info.id,
|
|
1593
|
+
name: info.name,
|
|
1594
|
+
description: info.description,
|
|
1595
|
+
defaultBranch: info.defaultBranch,
|
|
1596
|
+
remote: info.remote,
|
|
1597
|
+
token: token.plaintext,
|
|
1598
|
+
tokenExpiresAt: token.expiresAt
|
|
1599
|
+
};
|
|
1600
|
+
};
|
|
1601
|
+
return {
|
|
1602
|
+
create: createRepo,
|
|
1603
|
+
async get(name) {
|
|
1604
|
+
const repo = repos.get(name);
|
|
1605
|
+
return repo ? createRepoHandle(repo) : null;
|
|
1606
|
+
},
|
|
1607
|
+
async import(params) {
|
|
1608
|
+
return createRepo(params.target.name, {
|
|
1609
|
+
description: params.target.opts?.description,
|
|
1610
|
+
readOnly: params.target.opts?.readOnly,
|
|
1611
|
+
source: params.source.url
|
|
1612
|
+
});
|
|
1613
|
+
},
|
|
1614
|
+
async list(opts) {
|
|
1615
|
+
const limit = opts?.limit ?? 50;
|
|
1616
|
+
const repoList = Array.from(repos.values()).slice(0, limit).map((repo) => {
|
|
1617
|
+
const { remote: _remote, ...rest } = repo;
|
|
1618
|
+
return rest;
|
|
1619
|
+
});
|
|
1620
|
+
return {
|
|
1621
|
+
repos: repoList,
|
|
1622
|
+
total: repos.size,
|
|
1623
|
+
...repos.size > repoList.length && { cursor: String(repoList.length) }
|
|
1624
|
+
};
|
|
1625
|
+
},
|
|
1626
|
+
async delete(name) {
|
|
1627
|
+
tokens.delete(name);
|
|
1628
|
+
return repos.delete(name);
|
|
1629
|
+
}
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
// src/test/utilities/env.ts
|
|
1633
|
+
function createMockEnv(options = {}) {
|
|
1634
|
+
const env2 = {};
|
|
1635
|
+
if (options.kv) {
|
|
1636
|
+
for (const name of options.kv) {
|
|
1637
|
+
env2[name] = createMockKV();
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
if (options.d1) {
|
|
1641
|
+
for (const name of options.d1) {
|
|
1642
|
+
env2[name] = createMockD1();
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
if (options.r2) {
|
|
1646
|
+
for (const name of options.r2) {
|
|
1647
|
+
env2[name] = createMockR2();
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
if (options.queues) {
|
|
1651
|
+
for (const name of options.queues) {
|
|
1652
|
+
env2[name] = createMockQueue();
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
if (options.rateLimits) {
|
|
1656
|
+
for (const [name, rateLimitOptions] of Object.entries(options.rateLimits)) {
|
|
1657
|
+
env2[name] = createMockRateLimit(rateLimitOptions);
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
if (options.versionMetadata) {
|
|
1661
|
+
env2[options.versionMetadata] = createMockVersionMetadata();
|
|
1662
|
+
}
|
|
1663
|
+
if (options.hyperdrive) {
|
|
1664
|
+
for (const [name, binding] of Object.entries(options.hyperdrive)) {
|
|
1665
|
+
env2[name] = typeof binding === "string" ? createMockHyperdrive(binding) : binding;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
if (Array.isArray(options.workerLoaders)) {
|
|
1669
|
+
for (const name of options.workerLoaders) {
|
|
1670
|
+
env2[name] = createMockWorkerLoader();
|
|
1671
|
+
}
|
|
1672
|
+
} else if (options.workerLoaders) {
|
|
1673
|
+
for (const [name, workerLoaderOptions] of Object.entries(options.workerLoaders)) {
|
|
1674
|
+
env2[name] = createMockWorkerLoader(workerLoaderOptions);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
if (Array.isArray(options.mtlsCertificates)) {
|
|
1678
|
+
for (const name of options.mtlsCertificates) {
|
|
1679
|
+
env2[name] = createMockMTLSCertificate();
|
|
1680
|
+
}
|
|
1681
|
+
} else if (options.mtlsCertificates) {
|
|
1682
|
+
for (const [name, handler] of Object.entries(options.mtlsCertificates)) {
|
|
1683
|
+
env2[name] = createMockMTLSCertificate(handler);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
if (Array.isArray(options.dispatchNamespaces)) {
|
|
1687
|
+
for (const name of options.dispatchNamespaces) {
|
|
1688
|
+
env2[name] = createMockDispatchNamespace();
|
|
1689
|
+
}
|
|
1690
|
+
} else if (options.dispatchNamespaces) {
|
|
1691
|
+
for (const [name, dispatchNamespaceOptions] of Object.entries(options.dispatchNamespaces)) {
|
|
1692
|
+
env2[name] = createMockDispatchNamespace(dispatchNamespaceOptions);
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
if (Array.isArray(options.workflows)) {
|
|
1696
|
+
for (const name of options.workflows) {
|
|
1697
|
+
env2[name] = createMockWorkflow();
|
|
1698
|
+
}
|
|
1699
|
+
} else if (options.workflows) {
|
|
1700
|
+
for (const [name, workflowOptions] of Object.entries(options.workflows)) {
|
|
1701
|
+
env2[name] = "create" in workflowOptions ? workflowOptions : createMockWorkflow(workflowOptions);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
if (Array.isArray(options.pipelines)) {
|
|
1705
|
+
for (const name of options.pipelines) {
|
|
1706
|
+
env2[name] = createMockPipeline();
|
|
1707
|
+
}
|
|
1708
|
+
} else if (options.pipelines) {
|
|
1709
|
+
for (const [name, pipeline] of Object.entries(options.pipelines)) {
|
|
1710
|
+
env2[name] = pipeline;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
if (typeof options.images === "string") {
|
|
1714
|
+
env2[options.images] = createMockImagesBinding();
|
|
1715
|
+
} else if (options.images) {
|
|
1716
|
+
env2.IMAGES = options.images;
|
|
1717
|
+
}
|
|
1718
|
+
if (typeof options.media === "string") {
|
|
1719
|
+
env2[options.media] = createMockMediaBinding();
|
|
1720
|
+
} else if (options.media) {
|
|
1721
|
+
env2.MEDIA = options.media;
|
|
1722
|
+
}
|
|
1723
|
+
if (Array.isArray(options.artifacts)) {
|
|
1724
|
+
for (const name of options.artifacts) {
|
|
1725
|
+
env2[name] = createMockArtifacts();
|
|
1726
|
+
}
|
|
1727
|
+
} else if (options.artifacts) {
|
|
1728
|
+
for (const [name, artifactsOptions] of Object.entries(options.artifacts)) {
|
|
1729
|
+
env2[name] = isArtifactsBinding(artifactsOptions) ? artifactsOptions : createMockArtifacts(artifactsOptions);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
if (options.secretsStore) {
|
|
1733
|
+
for (const [name, value] of Object.entries(options.secretsStore)) {
|
|
1734
|
+
env2[name] = createMockSecretsStoreSecret(value);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
if (options.vars) {
|
|
1738
|
+
Object.assign(env2, options.vars);
|
|
1739
|
+
}
|
|
1740
|
+
if (options.secrets) {
|
|
1741
|
+
Object.assign(env2, options.secrets);
|
|
1742
|
+
}
|
|
1743
|
+
if (options.custom) {
|
|
1744
|
+
Object.assign(env2, options.custom);
|
|
1745
|
+
}
|
|
1746
|
+
return env2;
|
|
1747
|
+
}
|
|
1748
|
+
// src/test/simple-context-bindings.ts
|
|
1749
|
+
function buildRemoteAndStaticBindings(config) {
|
|
1750
|
+
const remoteBindings = {};
|
|
1751
|
+
if (isRemoteModeActive()) {
|
|
1752
|
+
if (config.bindings?.ai) {
|
|
1753
|
+
const aiBindingName = config.bindings.ai.binding || "AI";
|
|
1754
|
+
remoteBindings[aiBindingName] = createRemoteAI(config.accountId);
|
|
1755
|
+
}
|
|
1756
|
+
if (config.bindings?.vectorize) {
|
|
1757
|
+
for (const [name, vectorConfig] of Object.entries(config.bindings.vectorize)) {
|
|
1758
|
+
remoteBindings[name] = createRemoteVectorize(vectorConfig.indexName, config.accountId);
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
if (config.vars) {
|
|
1763
|
+
for (const [key, value] of Object.entries(config.vars)) {
|
|
1764
|
+
remoteBindings[key] = value;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
if (config.bindings?.sendEmail) {
|
|
1768
|
+
for (const [name, binding] of Object.entries(config.bindings.sendEmail)) {
|
|
1769
|
+
remoteBindings[name] = createLocalSendEmailBinding(binding);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
if (config.bindings?.workerLoaders) {
|
|
1773
|
+
for (const name of Object.keys(config.bindings.workerLoaders)) {
|
|
1774
|
+
remoteBindings[name] = createLocalWorkerLoaderBinding();
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
if (config.bindings?.versionMetadata) {
|
|
1778
|
+
remoteBindings[config.bindings.versionMetadata.binding] = createMockVersionMetadata();
|
|
1779
|
+
}
|
|
1780
|
+
return remoteBindings;
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
// src/test/email.ts
|
|
1784
|
+
import { join as join3 } from "path";
|
|
1785
|
+
var miniflarePort = 8787;
|
|
1786
|
+
var emailListeners = [];
|
|
1787
|
+
var sentEmails = [];
|
|
1788
|
+
var emailHandlerPath = null;
|
|
1789
|
+
var configDir = null;
|
|
1790
|
+
var testEnvGetter = null;
|
|
1791
|
+
function configureEmail(options = {}) {
|
|
1792
|
+
if (options.port) {
|
|
1793
|
+
miniflarePort = options.port;
|
|
1794
|
+
}
|
|
1795
|
+
emailHandlerPath = options.handlerPath ?? emailHandlerPath;
|
|
1796
|
+
configDir = options.configDir ?? configDir;
|
|
1797
|
+
testEnvGetter = options.getEnv ?? testEnvGetter;
|
|
1798
|
+
}
|
|
1799
|
+
function buildRawEmail(options) {
|
|
1800
|
+
if (options.raw) {
|
|
1801
|
+
return options.raw;
|
|
1802
|
+
}
|
|
1803
|
+
const lines = [];
|
|
1804
|
+
const messageId = `<${Date.now()}-${Math.random().toString(36).slice(2)}@devflare.dev>`;
|
|
1805
|
+
const date = new Date().toUTCString();
|
|
1806
|
+
lines.push(`From: ${options.from}`);
|
|
1807
|
+
lines.push(`To: ${options.to}`);
|
|
1808
|
+
lines.push(`Date: ${date}`);
|
|
1809
|
+
lines.push(`Message-ID: ${messageId}`);
|
|
1810
|
+
if (options.subject) {
|
|
1811
|
+
lines.push(`Subject: ${options.subject}`);
|
|
1812
|
+
}
|
|
1813
|
+
if (options.headers) {
|
|
1814
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
1815
|
+
lines.push(`${key}: ${value}`);
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
lines.push("MIME-Version: 1.0");
|
|
1819
|
+
lines.push("Content-Type: text/plain; charset=UTF-8");
|
|
1820
|
+
lines.push("");
|
|
1821
|
+
lines.push(options.body ?? "");
|
|
1822
|
+
return lines.join(`\r
|
|
1823
|
+
`);
|
|
1824
|
+
}
|
|
1825
|
+
function createEmailHeaders(rawEmail) {
|
|
1826
|
+
const headers = new Headers;
|
|
1827
|
+
const lines = rawEmail.split(/\r?\n/);
|
|
1828
|
+
for (const line of lines) {
|
|
1829
|
+
if (!line.trim()) {
|
|
1830
|
+
break;
|
|
1831
|
+
}
|
|
1832
|
+
const colonIndex = line.indexOf(":");
|
|
1833
|
+
if (colonIndex <= 0) {
|
|
1834
|
+
continue;
|
|
1835
|
+
}
|
|
1836
|
+
headers.append(line.slice(0, colonIndex).trim(), line.slice(colonIndex + 1).trim());
|
|
1837
|
+
}
|
|
1838
|
+
return headers;
|
|
1839
|
+
}
|
|
1840
|
+
function createRawEmailStream(rawEmail) {
|
|
1841
|
+
return new ReadableStream({
|
|
1842
|
+
start(controller) {
|
|
1843
|
+
controller.enqueue(new TextEncoder().encode(rawEmail));
|
|
1844
|
+
controller.close();
|
|
1845
|
+
}
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
function resolveEmailHandler(module) {
|
|
1849
|
+
if (typeof module.default === "function") {
|
|
1850
|
+
return module.default;
|
|
1851
|
+
}
|
|
1852
|
+
if (module.default && typeof module.default.email === "function") {
|
|
1853
|
+
return module.default.email.bind(module.default);
|
|
1854
|
+
}
|
|
1855
|
+
if (typeof module.email === "function") {
|
|
1856
|
+
return module.email;
|
|
1857
|
+
}
|
|
1858
|
+
return null;
|
|
1859
|
+
}
|
|
1860
|
+
function getRecordedRawContent(raw) {
|
|
1861
|
+
if (typeof raw === "string") {
|
|
1862
|
+
return raw;
|
|
1863
|
+
}
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
async function send(options) {
|
|
1867
|
+
const raw = buildRawEmail(options);
|
|
1868
|
+
if (emailHandlerPath && configDir && testEnvGetter) {
|
|
1869
|
+
const absolutePath = join3(configDir, emailHandlerPath);
|
|
1870
|
+
const handlerModule = await import(absolutePath);
|
|
1871
|
+
const emailHandler = resolveEmailHandler(handlerModule);
|
|
1872
|
+
if (!emailHandler) {
|
|
1873
|
+
throw new Error(`Email handler at "${emailHandlerPath}" must export a default function or named "email" export.
|
|
1874
|
+
NaN`);
|
|
1875
|
+
}
|
|
1876
|
+
const waitUntilPromises = [];
|
|
1877
|
+
const ctx = {
|
|
1878
|
+
waitUntil(promise) {
|
|
1879
|
+
waitUntilPromises.push(promise);
|
|
1880
|
+
},
|
|
1881
|
+
passThroughOnException() {},
|
|
1882
|
+
props: {}
|
|
1883
|
+
};
|
|
1884
|
+
const runtimeEnv = testEnvGetter();
|
|
1885
|
+
const timestamp = new Date;
|
|
1886
|
+
const message = {
|
|
1887
|
+
from: options.from,
|
|
1888
|
+
to: options.to,
|
|
1889
|
+
headers: createEmailHeaders(raw),
|
|
1890
|
+
raw: createRawEmailStream(raw),
|
|
1891
|
+
rawSize: raw.length,
|
|
1892
|
+
setReject(reason) {
|
|
1893
|
+
throw new Error(`Email rejected: ${reason}`);
|
|
1894
|
+
},
|
|
1895
|
+
async forward(rcptTo) {
|
|
1896
|
+
recordSentEmail({
|
|
1897
|
+
type: "forward",
|
|
1898
|
+
from: options.from,
|
|
1899
|
+
to: rcptTo,
|
|
1900
|
+
raw,
|
|
1901
|
+
timestamp
|
|
1902
|
+
});
|
|
1903
|
+
},
|
|
1904
|
+
async reply(message2) {
|
|
1905
|
+
recordSentEmail({
|
|
1906
|
+
type: "reply",
|
|
1907
|
+
from: message2.from ?? options.to,
|
|
1908
|
+
to: message2.to ?? options.from,
|
|
1909
|
+
raw: getRecordedRawContent(message2.raw),
|
|
1910
|
+
timestamp
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
};
|
|
1914
|
+
const emailEvent = createEmailEvent(message, runtimeEnv, ctx);
|
|
1915
|
+
await runWithEventContext(emailEvent, () => emailHandler(emailEvent));
|
|
1916
|
+
await Promise.all(waitUntilPromises);
|
|
1917
|
+
return new Response(JSON.stringify({ ok: true, from: options.from, to: options.to }), {
|
|
1918
|
+
headers: { "Content-Type": "application/json" }
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1921
|
+
const url = new URL(`http://localhost:${miniflarePort}/cdn-cgi/handler/email`);
|
|
1922
|
+
url.searchParams.set("from", options.from);
|
|
1923
|
+
url.searchParams.set("to", options.to);
|
|
1924
|
+
const response = await fetch(url.toString(), {
|
|
1925
|
+
method: "POST",
|
|
1926
|
+
headers: {
|
|
1927
|
+
"Content-Type": "text/plain"
|
|
1928
|
+
},
|
|
1929
|
+
body: raw
|
|
1930
|
+
});
|
|
1931
|
+
return response;
|
|
1932
|
+
}
|
|
1933
|
+
function onReceive(callback) {
|
|
1934
|
+
emailListeners.push(callback);
|
|
1935
|
+
return () => {
|
|
1936
|
+
emailListeners = emailListeners.filter((cb) => cb !== callback);
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
function getSentEmails() {
|
|
1940
|
+
return [...sentEmails];
|
|
1941
|
+
}
|
|
1942
|
+
function clearSentEmails() {
|
|
1943
|
+
sentEmails = [];
|
|
1944
|
+
}
|
|
1945
|
+
function recordSentEmail(email) {
|
|
1946
|
+
sentEmails.push(email);
|
|
1947
|
+
for (const listener of emailListeners) {
|
|
1948
|
+
try {
|
|
1949
|
+
listener(email);
|
|
1950
|
+
} catch (error) {
|
|
1951
|
+
console.error("[devflare/test] Email listener error:", error);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
function resetEmailState() {
|
|
1956
|
+
miniflarePort = 8787;
|
|
1957
|
+
emailHandlerPath = null;
|
|
1958
|
+
configDir = null;
|
|
1959
|
+
testEnvGetter = null;
|
|
1960
|
+
emailListeners = [];
|
|
1961
|
+
sentEmails = [];
|
|
1962
|
+
}
|
|
1963
|
+
var email = {
|
|
1964
|
+
send,
|
|
1965
|
+
onReceive,
|
|
1966
|
+
getSentEmails,
|
|
1967
|
+
clearSentEmails
|
|
1968
|
+
};
|
|
1969
|
+
|
|
1970
|
+
// src/test/queue.ts
|
|
1971
|
+
import { join as join4 } from "path";
|
|
1972
|
+
var queueHandlerPath = null;
|
|
1973
|
+
var configDir2 = null;
|
|
1974
|
+
var testEnvGetter2 = null;
|
|
1975
|
+
function configureQueue(options) {
|
|
1976
|
+
queueHandlerPath = options.handlerPath;
|
|
1977
|
+
configDir2 = options.configDir;
|
|
1978
|
+
testEnvGetter2 = options.getEnv;
|
|
1979
|
+
}
|
|
1980
|
+
function resetQueueState() {
|
|
1981
|
+
queueHandlerPath = null;
|
|
1982
|
+
configDir2 = null;
|
|
1983
|
+
testEnvGetter2 = null;
|
|
1984
|
+
}
|
|
1985
|
+
var EMPTY_QUEUE_METADATA = {
|
|
1986
|
+
metrics: {
|
|
1987
|
+
backlogCount: 0,
|
|
1988
|
+
backlogBytes: 0
|
|
1989
|
+
}
|
|
1990
|
+
};
|
|
1991
|
+
function createMessage(options) {
|
|
1992
|
+
const id = options.id ?? crypto.randomUUID();
|
|
1993
|
+
const timestamp = options.timestamp ?? new Date;
|
|
1994
|
+
const attempts = options.attempts ?? 1;
|
|
1995
|
+
let state = "pending";
|
|
1996
|
+
return {
|
|
1997
|
+
id,
|
|
1998
|
+
timestamp,
|
|
1999
|
+
body: options.body,
|
|
2000
|
+
attempts,
|
|
2001
|
+
ack() {
|
|
2002
|
+
state = "acked";
|
|
2003
|
+
},
|
|
2004
|
+
retry(opts) {
|
|
2005
|
+
state = "retried";
|
|
2006
|
+
},
|
|
2007
|
+
retryAll() {
|
|
2008
|
+
state = "retried";
|
|
2009
|
+
},
|
|
2010
|
+
get _state() {
|
|
2011
|
+
return state;
|
|
2012
|
+
}
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
function createMessageBatch(messages) {
|
|
2016
|
+
return {
|
|
2017
|
+
queue: "test-queue",
|
|
2018
|
+
metadata: EMPTY_QUEUE_METADATA,
|
|
2019
|
+
messages,
|
|
2020
|
+
ackAll() {
|
|
2021
|
+
for (const msg of messages) {
|
|
2022
|
+
msg.ack();
|
|
2023
|
+
}
|
|
2024
|
+
},
|
|
2025
|
+
retryAll() {
|
|
2026
|
+
for (const msg of messages) {
|
|
2027
|
+
msg.retry();
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
async function trigger(messages) {
|
|
2033
|
+
if (!queueHandlerPath) {
|
|
2034
|
+
throw new Error("Queue handler not configured. Make sure your devflare.config.ts has files.queue set, " + "and the file exists at the specified path (default: src/queue.ts)");
|
|
2035
|
+
}
|
|
2036
|
+
if (!configDir2 || !testEnvGetter2) {
|
|
2037
|
+
throw new Error("Queue helper not initialized. Call createTestContext() before using cf.queue.trigger()");
|
|
2038
|
+
}
|
|
2039
|
+
const absolutePath = join4(configDir2, queueHandlerPath);
|
|
2040
|
+
const handlerModule = await import(absolutePath);
|
|
2041
|
+
const queueHandler = handlerModule.default ?? handlerModule.queue;
|
|
2042
|
+
if (typeof queueHandler !== "function") {
|
|
2043
|
+
throw new Error(`Queue handler at "${queueHandlerPath}" must export a default function or named "queue" export.
|
|
2044
|
+
NaN`);
|
|
2045
|
+
}
|
|
2046
|
+
const normalizedMessages = messages.map((msg) => {
|
|
2047
|
+
if (typeof msg === "object" && msg !== null && "body" in msg) {
|
|
2048
|
+
return msg;
|
|
2049
|
+
}
|
|
2050
|
+
return { body: msg };
|
|
2051
|
+
});
|
|
2052
|
+
const mockMessages = normalizedMessages.map((opts) => createMessage(opts));
|
|
2053
|
+
const batch = createMessageBatch(mockMessages);
|
|
2054
|
+
const waitUntilPromises = [];
|
|
2055
|
+
const ctx = {
|
|
2056
|
+
waitUntil(promise) {
|
|
2057
|
+
waitUntilPromises.push(promise);
|
|
2058
|
+
},
|
|
2059
|
+
passThroughOnException() {},
|
|
2060
|
+
props: {}
|
|
2061
|
+
};
|
|
2062
|
+
const env3 = testEnvGetter2();
|
|
2063
|
+
const queueEvent = createQueueEvent(batch, env3, ctx);
|
|
2064
|
+
await runWithEventContext(queueEvent, () => queueHandler(queueEvent));
|
|
2065
|
+
await Promise.all(waitUntilPromises);
|
|
2066
|
+
const acked = [];
|
|
2067
|
+
const retried = [];
|
|
2068
|
+
const failed = [];
|
|
2069
|
+
for (const msg of mockMessages) {
|
|
2070
|
+
switch (msg._state) {
|
|
2071
|
+
case "acked":
|
|
2072
|
+
acked.push(msg.id);
|
|
2073
|
+
break;
|
|
2074
|
+
case "retried":
|
|
2075
|
+
retried.push(msg.id);
|
|
2076
|
+
break;
|
|
2077
|
+
case "failed":
|
|
2078
|
+
failed.push(msg.id);
|
|
2079
|
+
break;
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
return {
|
|
2083
|
+
acked,
|
|
2084
|
+
retried,
|
|
2085
|
+
failed,
|
|
2086
|
+
total: mockMessages.length
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
async function send2(message) {
|
|
2090
|
+
return trigger([message]);
|
|
2091
|
+
}
|
|
2092
|
+
var queue2 = {
|
|
2093
|
+
trigger,
|
|
2094
|
+
send: send2
|
|
2095
|
+
};
|
|
2096
|
+
|
|
2097
|
+
// src/test/scheduled.ts
|
|
2098
|
+
import { join as join5 } from "path";
|
|
2099
|
+
var scheduledHandlerPath = null;
|
|
2100
|
+
var configDir3 = null;
|
|
2101
|
+
var testEnvGetter3 = null;
|
|
2102
|
+
function configureScheduled(options) {
|
|
2103
|
+
scheduledHandlerPath = options.handlerPath;
|
|
2104
|
+
configDir3 = options.configDir;
|
|
2105
|
+
testEnvGetter3 = options.getEnv;
|
|
2106
|
+
}
|
|
2107
|
+
function resetScheduledState() {
|
|
2108
|
+
scheduledHandlerPath = null;
|
|
2109
|
+
configDir3 = null;
|
|
2110
|
+
testEnvGetter3 = null;
|
|
2111
|
+
}
|
|
2112
|
+
async function trigger2(cronOrOptions) {
|
|
2113
|
+
if (!scheduledHandlerPath) {
|
|
2114
|
+
throw new Error("Scheduled handler not configured. Make sure your devflare.config.ts has files.scheduled set, " + "and the file exists at the specified path (default: src/scheduled.ts)");
|
|
2115
|
+
}
|
|
2116
|
+
if (!configDir3 || !testEnvGetter3) {
|
|
2117
|
+
throw new Error("Scheduled helper not initialized. Call createTestContext() before using cf.scheduled.trigger()");
|
|
2118
|
+
}
|
|
2119
|
+
const options = typeof cronOrOptions === "string" ? { cron: cronOrOptions } : cronOrOptions ?? {};
|
|
2120
|
+
const cron = options.cron ?? "* * * * *";
|
|
2121
|
+
const scheduledTime = options.scheduledTime instanceof Date ? options.scheduledTime.getTime() : options.scheduledTime ?? Date.now();
|
|
2122
|
+
const absolutePath = join5(configDir3, scheduledHandlerPath);
|
|
2123
|
+
const handlerModule = await import(absolutePath);
|
|
2124
|
+
const scheduledHandler = handlerModule.default ?? handlerModule.scheduled;
|
|
2125
|
+
if (typeof scheduledHandler !== "function") {
|
|
2126
|
+
throw new Error(`Scheduled handler at "${scheduledHandlerPath}" must export a default function or named "scheduled" export.
|
|
2127
|
+
NaN`);
|
|
2128
|
+
}
|
|
2129
|
+
const controller = {
|
|
2130
|
+
scheduledTime,
|
|
2131
|
+
cron,
|
|
2132
|
+
noRetry() {}
|
|
2133
|
+
};
|
|
2134
|
+
const waitUntilPromises = [];
|
|
2135
|
+
const ctx = {
|
|
2136
|
+
waitUntil(promise) {
|
|
2137
|
+
waitUntilPromises.push(promise);
|
|
2138
|
+
},
|
|
2139
|
+
passThroughOnException() {},
|
|
2140
|
+
props: {}
|
|
2141
|
+
};
|
|
2142
|
+
const env3 = testEnvGetter3();
|
|
2143
|
+
const scheduledEvent = createScheduledEvent(controller, env3, ctx);
|
|
2144
|
+
try {
|
|
2145
|
+
await runWithEventContext(scheduledEvent, () => scheduledHandler(scheduledEvent));
|
|
2146
|
+
await Promise.all(waitUntilPromises);
|
|
2147
|
+
return {
|
|
2148
|
+
success: true,
|
|
2149
|
+
cron,
|
|
2150
|
+
scheduledTime
|
|
2151
|
+
};
|
|
2152
|
+
} catch (error) {
|
|
2153
|
+
return {
|
|
2154
|
+
success: false,
|
|
2155
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2156
|
+
cron,
|
|
2157
|
+
scheduledTime
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
var scheduled = {
|
|
2162
|
+
trigger: trigger2
|
|
2163
|
+
};
|
|
2164
|
+
|
|
2165
|
+
// src/test/tail.ts
|
|
2166
|
+
import { join as join6 } from "path";
|
|
2167
|
+
var tailHandlerPath = null;
|
|
2168
|
+
var configDir4 = null;
|
|
2169
|
+
var testEnvGetter4 = null;
|
|
2170
|
+
function configureTail(options) {
|
|
2171
|
+
tailHandlerPath = options.handlerPath;
|
|
2172
|
+
configDir4 = options.configDir;
|
|
2173
|
+
testEnvGetter4 = options.getEnv;
|
|
2174
|
+
}
|
|
2175
|
+
function resetTailState() {
|
|
2176
|
+
tailHandlerPath = null;
|
|
2177
|
+
configDir4 = null;
|
|
2178
|
+
testEnvGetter4 = null;
|
|
2179
|
+
}
|
|
2180
|
+
function createTraceItem(options) {
|
|
2181
|
+
return {
|
|
2182
|
+
scriptName: options.scriptName ?? "test-worker",
|
|
2183
|
+
outcome: options.outcome ?? "ok",
|
|
2184
|
+
eventTimestamp: options.eventTimestamp ?? Date.now(),
|
|
2185
|
+
event: options.event ?? {
|
|
2186
|
+
request: {
|
|
2187
|
+
url: "https://example.com/",
|
|
2188
|
+
method: "GET"
|
|
2189
|
+
}
|
|
2190
|
+
},
|
|
2191
|
+
logs: options.logs ?? [],
|
|
2192
|
+
exceptions: options.exceptions ?? [],
|
|
2193
|
+
diagnosticsChannelEvents: options.diagnosticsChannelEvents ?? [],
|
|
2194
|
+
scriptVersion: options.scriptVersion ?? { id: "test-version" },
|
|
2195
|
+
dispatchNamespace: options.dispatchNamespace,
|
|
2196
|
+
scriptTags: options.scriptTags ?? []
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
async function trigger3(items) {
|
|
2200
|
+
if (!tailHandlerPath) {
|
|
2201
|
+
throw new Error("Tail handler not configured. Add a src/tail.ts file exporting tail(), " + "or configure a tail handler before calling cf.tail.trigger().");
|
|
2202
|
+
}
|
|
2203
|
+
if (!configDir4 || !testEnvGetter4) {
|
|
2204
|
+
throw new Error("Tail helper not initialized. Call createTestContext() before using cf.tail.trigger()");
|
|
2205
|
+
}
|
|
2206
|
+
const traceItems = items.map((item) => {
|
|
2207
|
+
if ("eventTimestamp" in item && "outcome" in item && "scriptName" in item) {
|
|
2208
|
+
return item;
|
|
2209
|
+
}
|
|
2210
|
+
return createTraceItem(item);
|
|
2211
|
+
});
|
|
2212
|
+
const absolutePath = join6(configDir4, tailHandlerPath);
|
|
2213
|
+
const handlerModule = await import(absolutePath);
|
|
2214
|
+
const defaultExport = handlerModule.default;
|
|
2215
|
+
const tailHandler = typeof defaultExport === "function" ? defaultExport : defaultExport && typeof defaultExport.tail === "function" ? defaultExport.tail.bind(defaultExport) : handlerModule.tail;
|
|
2216
|
+
if (typeof tailHandler !== "function") {
|
|
2217
|
+
throw new Error(`Tail handler at "${tailHandlerPath}" must export a default function or named "tail" export.
|
|
2218
|
+
NaN`);
|
|
2219
|
+
}
|
|
2220
|
+
const waitUntilPromises = [];
|
|
2221
|
+
const ctx = {
|
|
2222
|
+
waitUntil(promise) {
|
|
2223
|
+
waitUntilPromises.push(promise);
|
|
2224
|
+
},
|
|
2225
|
+
passThroughOnException() {},
|
|
2226
|
+
props: {}
|
|
2227
|
+
};
|
|
2228
|
+
const env3 = testEnvGetter4();
|
|
2229
|
+
const tailEvent = createTailEvent(traceItems, env3, ctx);
|
|
2230
|
+
try {
|
|
2231
|
+
await runWithEventContext(tailEvent, () => tailHandler.length >= 2 ? tailHandler(traceItems, env3, ctx) : tailHandler(tailEvent, env3, ctx));
|
|
2232
|
+
await Promise.all(waitUntilPromises);
|
|
2233
|
+
return {
|
|
2234
|
+
success: true,
|
|
2235
|
+
itemCount: traceItems.length
|
|
2236
|
+
};
|
|
2237
|
+
} catch (error) {
|
|
2238
|
+
return {
|
|
2239
|
+
success: false,
|
|
2240
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2241
|
+
itemCount: traceItems.length
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
function create(options = {}) {
|
|
2246
|
+
return createTraceItem(options);
|
|
2247
|
+
}
|
|
2248
|
+
var tail = {
|
|
2249
|
+
trigger: trigger3,
|
|
2250
|
+
create
|
|
2251
|
+
};
|
|
2252
|
+
|
|
2253
|
+
// src/test/worker.ts
|
|
2254
|
+
import { join as join7 } from "path";
|
|
2255
|
+
var fetchHandlerPath = null;
|
|
2256
|
+
var configDir5 = null;
|
|
2257
|
+
var testEnvGetter5 = null;
|
|
2258
|
+
var fileRoutes = [];
|
|
2259
|
+
function configureWorker(options) {
|
|
2260
|
+
fetchHandlerPath = options.handlerPath;
|
|
2261
|
+
fileRoutes = options.routes ?? [];
|
|
2262
|
+
configDir5 = options.configDir;
|
|
2263
|
+
testEnvGetter5 = options.getEnv;
|
|
2264
|
+
}
|
|
2265
|
+
function resetWorkerState() {
|
|
2266
|
+
fetchHandlerPath = null;
|
|
2267
|
+
fileRoutes = [];
|
|
2268
|
+
configDir5 = null;
|
|
2269
|
+
testEnvGetter5 = null;
|
|
2270
|
+
}
|
|
2271
|
+
async function fetch2(request, options) {
|
|
2272
|
+
if (!fetchHandlerPath && fileRoutes.length === 0) {
|
|
2273
|
+
throw new Error("Fetch handler not configured. Make sure your devflare.config.ts has files.fetch set or a routes directory is available, " + "and that the corresponding files exist (defaults: src/fetch.ts and src/routes/**).");
|
|
2274
|
+
}
|
|
2275
|
+
if (!configDir5 || !testEnvGetter5) {
|
|
2276
|
+
throw new Error("Worker helper not initialized. Call createTestContext() before using cf.worker.fetch()");
|
|
2277
|
+
}
|
|
2278
|
+
const workerConfigDir = configDir5;
|
|
2279
|
+
const getEnv = testEnvGetter5;
|
|
2280
|
+
let req;
|
|
2281
|
+
if (typeof request === "string") {
|
|
2282
|
+
const url = request.startsWith("http") ? request : `http://localhost${request.startsWith("/") ? "" : "/"}${request}`;
|
|
2283
|
+
const headers = new Headers(options?.headers);
|
|
2284
|
+
let body;
|
|
2285
|
+
if (options?.body !== undefined) {
|
|
2286
|
+
if (typeof options.body === "string") {
|
|
2287
|
+
body = options.body;
|
|
2288
|
+
} else {
|
|
2289
|
+
body = JSON.stringify(options.body);
|
|
2290
|
+
if (!headers.has("Content-Type")) {
|
|
2291
|
+
headers.set("Content-Type", "application/json");
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
req = new Request(url, {
|
|
2296
|
+
method: options?.method ?? "GET",
|
|
2297
|
+
headers,
|
|
2298
|
+
body
|
|
2299
|
+
});
|
|
2300
|
+
} else {
|
|
2301
|
+
req = request;
|
|
2302
|
+
}
|
|
2303
|
+
const handlerModule = fetchHandlerPath ? await import(join7(workerConfigDir, fetchHandlerPath)) : {};
|
|
2304
|
+
const routeModules = await Promise.all(fileRoutes.map(async (route) => {
|
|
2305
|
+
return {
|
|
2306
|
+
...route,
|
|
2307
|
+
module: await import(join7(workerConfigDir, route.filePath))
|
|
2308
|
+
};
|
|
2309
|
+
}));
|
|
2310
|
+
const methodExports = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "ALL"];
|
|
2311
|
+
const hasMethodHandler = methodExports.some((method) => {
|
|
2312
|
+
return typeof handlerModule[method] === "function" || typeof handlerModule.default?.[method] === "function";
|
|
2313
|
+
});
|
|
2314
|
+
if (!resolveFetchHandler(handlerModule) && !hasMethodHandler && routeModules.length === 0) {
|
|
2315
|
+
throw new Error(`Fetch handler at "${fetchHandlerPath}" must export one of:
|
|
2316
|
+
- request-wide "handle" middleware
|
|
2317
|
+
- named "fetch"
|
|
2318
|
+
- default fetch handler
|
|
2319
|
+
- HTTP method exports such as "GET" or "POST"`);
|
|
2320
|
+
}
|
|
2321
|
+
const waitUntilPromises = [];
|
|
2322
|
+
const ctx = {
|
|
2323
|
+
waitUntil(promise) {
|
|
2324
|
+
waitUntilPromises.push(promise);
|
|
2325
|
+
},
|
|
2326
|
+
passThroughOnException() {},
|
|
2327
|
+
props: {}
|
|
2328
|
+
};
|
|
2329
|
+
const env3 = getEnv();
|
|
2330
|
+
const initialRouteMatch = routeModules.length > 0 ? matchFetchRoute(routeModules, req) : null;
|
|
2331
|
+
const fetchEvent = createFetchEvent(req, env3, ctx, {
|
|
2332
|
+
params: initialRouteMatch?.params ?? {}
|
|
2333
|
+
});
|
|
2334
|
+
const response = await runWithEventContext(fetchEvent, () => invokeFetchModule(handlerModule, fetchEvent, routeModules.length > 0 ? createRouteResolve(routeModules, fetchEvent) : undefined));
|
|
2335
|
+
return response;
|
|
2336
|
+
}
|
|
2337
|
+
async function get(path, headers) {
|
|
2338
|
+
return fetch2(path, { method: "GET", headers });
|
|
2339
|
+
}
|
|
2340
|
+
async function post(path, body, headers) {
|
|
2341
|
+
return fetch2(path, { method: "POST", body, headers });
|
|
2342
|
+
}
|
|
2343
|
+
async function put(path, body, headers) {
|
|
2344
|
+
return fetch2(path, { method: "PUT", body, headers });
|
|
2345
|
+
}
|
|
2346
|
+
async function del(path, headers) {
|
|
2347
|
+
return fetch2(path, { method: "DELETE", headers });
|
|
2348
|
+
}
|
|
2349
|
+
async function patch(path, body, headers) {
|
|
2350
|
+
return fetch2(path, { method: "PATCH", body, headers });
|
|
2351
|
+
}
|
|
2352
|
+
var worker = {
|
|
2353
|
+
fetch: fetch2,
|
|
2354
|
+
get,
|
|
2355
|
+
post,
|
|
2356
|
+
put,
|
|
2357
|
+
delete: del,
|
|
2358
|
+
patch
|
|
2359
|
+
};
|
|
2360
|
+
|
|
2361
|
+
// src/test/simple-context-env.ts
|
|
2362
|
+
function configureSurfaceHandlers(input) {
|
|
2363
|
+
const { handlerPaths, configDir: configDir6, activePort, getEnv } = input;
|
|
2364
|
+
configureQueue({
|
|
2365
|
+
handlerPath: handlerPaths.queue,
|
|
2366
|
+
configDir: configDir6,
|
|
2367
|
+
getEnv
|
|
2368
|
+
});
|
|
2369
|
+
configureScheduled({
|
|
2370
|
+
handlerPath: handlerPaths.scheduled,
|
|
2371
|
+
configDir: configDir6,
|
|
2372
|
+
getEnv
|
|
2373
|
+
});
|
|
2374
|
+
configureWorker({
|
|
2375
|
+
handlerPath: handlerPaths.fetch,
|
|
2376
|
+
routes: handlerPaths.routes?.routes.map((route) => ({
|
|
2377
|
+
filePath: route.filePath,
|
|
2378
|
+
routePath: route.routePath,
|
|
2379
|
+
segments: route.segments
|
|
2380
|
+
})) ?? [],
|
|
2381
|
+
configDir: configDir6,
|
|
2382
|
+
getEnv
|
|
2383
|
+
});
|
|
2384
|
+
configureTail({
|
|
2385
|
+
handlerPath: handlerPaths.tail,
|
|
2386
|
+
configDir: configDir6,
|
|
2387
|
+
getEnv
|
|
2388
|
+
});
|
|
2389
|
+
configureEmail({
|
|
2390
|
+
port: activePort,
|
|
2391
|
+
handlerPath: handlerPaths.email,
|
|
2392
|
+
configDir: configDir6,
|
|
2393
|
+
getEnv
|
|
2394
|
+
});
|
|
2395
|
+
}
|
|
2396
|
+
function createBridgeEnvAccessor(state, hints, shouldPreferBridgeBinding) {
|
|
2397
|
+
return new Proxy({}, {
|
|
2398
|
+
get(_, prop) {
|
|
2399
|
+
const hint = hints[prop];
|
|
2400
|
+
const prefersBridgeBinding = shouldPreferBridgeBinding(hint);
|
|
2401
|
+
if (state.remoteBindings && prop in state.remoteBindings) {
|
|
2402
|
+
return state.remoteBindings[prop];
|
|
2403
|
+
}
|
|
2404
|
+
if (!prefersBridgeBinding && state.miniflareBindings && prop in state.miniflareBindings) {
|
|
2405
|
+
return state.miniflareBindings[prop];
|
|
2406
|
+
}
|
|
2407
|
+
if (state.envProxy) {
|
|
2408
|
+
return state.envProxy[prop];
|
|
2409
|
+
}
|
|
2410
|
+
if (prefersBridgeBinding && state.miniflareBindings && prop in state.miniflareBindings) {
|
|
2411
|
+
return state.miniflareBindings[prop];
|
|
2412
|
+
}
|
|
2413
|
+
return;
|
|
2414
|
+
},
|
|
2415
|
+
has(_, prop) {
|
|
2416
|
+
return Boolean(state.remoteBindings && prop in state.remoteBindings || state.miniflareBindings && prop in state.miniflareBindings || state.envProxy !== null);
|
|
2417
|
+
}
|
|
2418
|
+
});
|
|
2419
|
+
}
|
|
2420
|
+
function createMultiWorkerEnvAccessor(state) {
|
|
2421
|
+
return new Proxy({}, {
|
|
2422
|
+
get(_, prop) {
|
|
2423
|
+
if (state.remoteBindings && prop in state.remoteBindings) {
|
|
2424
|
+
return state.remoteBindings[prop];
|
|
2425
|
+
}
|
|
2426
|
+
if (state.miniflareBindings && prop in state.miniflareBindings) {
|
|
2427
|
+
return state.miniflareBindings[prop];
|
|
2428
|
+
}
|
|
2429
|
+
return;
|
|
2430
|
+
},
|
|
2431
|
+
has(_, prop) {
|
|
2432
|
+
return Boolean(state.remoteBindings && prop in state.remoteBindings || state.miniflareBindings && prop in state.miniflareBindings);
|
|
2433
|
+
}
|
|
2434
|
+
});
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
// src/test/simple-context-handlers.ts
|
|
2438
|
+
import { join as join8 } from "path";
|
|
2439
|
+
var DEFAULT_FETCH_PATH = "src/fetch.ts";
|
|
2440
|
+
var DEFAULT_QUEUE_PATH = "src/queue.ts";
|
|
2441
|
+
var DEFAULT_SCHEDULED_PATH = "src/scheduled.ts";
|
|
2442
|
+
var DEFAULT_EMAIL_PATH = "src/email.ts";
|
|
2443
|
+
var DEFAULT_TAIL_PATH = "src/tail.ts";
|
|
2444
|
+
async function resolveHandlerPath(configDir6, configValue, defaultPath) {
|
|
2445
|
+
if (typeof configValue === "string") {
|
|
2446
|
+
return configValue;
|
|
2447
|
+
}
|
|
2448
|
+
if (configValue === false) {
|
|
2449
|
+
return null;
|
|
2450
|
+
}
|
|
2451
|
+
const defaultAbsolute = join8(configDir6, defaultPath);
|
|
2452
|
+
try {
|
|
2453
|
+
const fs = await import("fs/promises");
|
|
2454
|
+
await fs.access(defaultAbsolute);
|
|
2455
|
+
return defaultPath;
|
|
2456
|
+
} catch {
|
|
2457
|
+
return null;
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
async function resolveHandlerPaths(configDir6, config) {
|
|
2461
|
+
const [fetch3, queue3, scheduled2, email2, tail2, routes] = await Promise.all([
|
|
2462
|
+
resolveHandlerPath(configDir6, config.files?.fetch, DEFAULT_FETCH_PATH),
|
|
2463
|
+
resolveHandlerPath(configDir6, config.files?.queue, DEFAULT_QUEUE_PATH),
|
|
2464
|
+
resolveHandlerPath(configDir6, config.files?.scheduled, DEFAULT_SCHEDULED_PATH),
|
|
2465
|
+
resolveHandlerPath(configDir6, config.files?.email, DEFAULT_EMAIL_PATH),
|
|
2466
|
+
resolveHandlerPath(configDir6, config.files?.tail, DEFAULT_TAIL_PATH),
|
|
2467
|
+
discoverRoutes(configDir6, config)
|
|
2468
|
+
]);
|
|
2469
|
+
return { fetch: fetch3, queue: queue3, scheduled: scheduled2, email: email2, tail: tail2, routes };
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
// src/test/simple-context-lifecycle.ts
|
|
2473
|
+
import { dirname as dirname4, resolve as resolve2 } from "path";
|
|
2474
|
+
|
|
2475
|
+
// src/test/containers.ts
|
|
2476
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2477
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
2478
|
+
import { stat } from "node:fs/promises";
|
|
2479
|
+
import { createConnection } from "node:net";
|
|
2480
|
+
import { basename, dirname as dirname3, isAbsolute, join as join9, resolve } from "node:path";
|
|
2481
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
2482
|
+
import { execa } from "execa";
|
|
2483
|
+
var realContainerCommandRunner = {
|
|
2484
|
+
async exec(command, args, options = {}) {
|
|
2485
|
+
try {
|
|
2486
|
+
const result = await execa(command, args, {
|
|
2487
|
+
cwd: options.cwd,
|
|
2488
|
+
env: options.env,
|
|
2489
|
+
reject: false,
|
|
2490
|
+
timeout: options.timeoutMs ?? 15000
|
|
2491
|
+
});
|
|
2492
|
+
return {
|
|
2493
|
+
exitCode: result.exitCode ?? 0,
|
|
2494
|
+
stdout: String(result.stdout ?? ""),
|
|
2495
|
+
stderr: String(result.stderr ?? "")
|
|
2496
|
+
};
|
|
2497
|
+
} catch (error) {
|
|
2498
|
+
const err = error;
|
|
2499
|
+
return {
|
|
2500
|
+
exitCode: err.exitCode ?? 1,
|
|
2501
|
+
stdout: String(err.stdout ?? ""),
|
|
2502
|
+
stderr: String(err.stderr ?? err.message ?? "Container command failed")
|
|
2503
|
+
};
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
};
|
|
2507
|
+
async function detectContainerEngine(options = {}) {
|
|
2508
|
+
const runner = options.runner ?? realContainerCommandRunner;
|
|
2509
|
+
const candidates = options.engine && options.engine !== "auto" ? [options.engine] : ["docker", "podman"];
|
|
2510
|
+
const checked = [];
|
|
2511
|
+
for (const engine of candidates) {
|
|
2512
|
+
let result;
|
|
2513
|
+
try {
|
|
2514
|
+
result = await runner.exec(engine, ["info"], { timeoutMs: 1e4 });
|
|
2515
|
+
} catch (error) {
|
|
2516
|
+
result = {
|
|
2517
|
+
exitCode: 1,
|
|
2518
|
+
stdout: "",
|
|
2519
|
+
stderr: error instanceof Error ? error.message : String(error)
|
|
2520
|
+
};
|
|
2521
|
+
}
|
|
2522
|
+
if (result.exitCode === 0) {
|
|
2523
|
+
checked.push({ engine, available: true });
|
|
2524
|
+
return { available: true, engine, checked };
|
|
2525
|
+
}
|
|
2526
|
+
checked.push({
|
|
2527
|
+
engine,
|
|
2528
|
+
available: false,
|
|
2529
|
+
reason: formatCommandFailure(result)
|
|
2530
|
+
});
|
|
2531
|
+
}
|
|
2532
|
+
return {
|
|
2533
|
+
available: false,
|
|
2534
|
+
reason: checked.map((check) => `${check.engine}: ${check.reason ?? "not available"}`).join("; "),
|
|
2535
|
+
checked
|
|
2536
|
+
};
|
|
2537
|
+
}
|
|
2538
|
+
async function getContainerSkipReason(options = {}) {
|
|
2539
|
+
const env3 = options.env ?? process.env;
|
|
2540
|
+
if (!isTruthyEnvFlag(env3.DEVFLARE_CONTAINER_TESTS)) {
|
|
2541
|
+
return "Container tests require DEVFLARE_CONTAINER_TESTS=1 because they launch local Docker/Podman containers.";
|
|
2542
|
+
}
|
|
2543
|
+
const status = await detectContainerEngine(options);
|
|
2544
|
+
if (!status.available) {
|
|
2545
|
+
return `No reachable Docker or Podman engine found: ${status.reason}`;
|
|
2546
|
+
}
|
|
2547
|
+
return null;
|
|
2548
|
+
}
|
|
2549
|
+
function createContainerManager(options = {}) {
|
|
2550
|
+
const active = new Set;
|
|
2551
|
+
const getRunner = () => options.runner ?? realContainerCommandRunner;
|
|
2552
|
+
const manager = {
|
|
2553
|
+
detectEngine(engineOptions = {}) {
|
|
2554
|
+
return detectContainerEngine({
|
|
2555
|
+
engine: engineOptions.engine ?? options.engine,
|
|
2556
|
+
runner: engineOptions.runner ?? getRunner()
|
|
2557
|
+
});
|
|
2558
|
+
},
|
|
2559
|
+
async start(className, startOptions) {
|
|
2560
|
+
const cwd = options.cwd ?? getCallerDirectory();
|
|
2561
|
+
const resolved = await resolveContainerStart(className, startOptions, cwd);
|
|
2562
|
+
const status = await manager.detectEngine();
|
|
2563
|
+
const engine = getAvailableContainerEngine(status, className);
|
|
2564
|
+
const runner = getRunner();
|
|
2565
|
+
const offline = startOptions.offline ?? true;
|
|
2566
|
+
const prepared = await prepareImage({
|
|
2567
|
+
engine,
|
|
2568
|
+
runner,
|
|
2569
|
+
image: resolved.image,
|
|
2570
|
+
className,
|
|
2571
|
+
configDir: resolved.configDir,
|
|
2572
|
+
imageBuildContext: resolved.config?.imageBuildContext,
|
|
2573
|
+
offline
|
|
2574
|
+
});
|
|
2575
|
+
const host = startOptions.host ?? "127.0.0.1";
|
|
2576
|
+
const hostPort = startOptions.hostPort ?? await (options.allocatePort ?? getAvailablePort)();
|
|
2577
|
+
const containerName = makeContainerName(className, startOptions.instance);
|
|
2578
|
+
const runArgs = buildRunArgs({
|
|
2579
|
+
name: containerName,
|
|
2580
|
+
className,
|
|
2581
|
+
image: prepared.image,
|
|
2582
|
+
host,
|
|
2583
|
+
hostPort,
|
|
2584
|
+
containerPort: startOptions.port,
|
|
2585
|
+
envVars: startOptions.envVars,
|
|
2586
|
+
entrypoint: startOptions.entrypoint,
|
|
2587
|
+
command: startOptions.command
|
|
2588
|
+
});
|
|
2589
|
+
const runResult = await runner.exec(engine, runArgs, {
|
|
2590
|
+
cwd: resolved.configDir,
|
|
2591
|
+
env: options.env ?? process.env
|
|
2592
|
+
});
|
|
2593
|
+
if (runResult.exitCode !== 0) {
|
|
2594
|
+
throw new Error(`Failed to start Devflare container "${className}": ${formatCommandFailure(runResult)}`);
|
|
2595
|
+
}
|
|
2596
|
+
const instance = new LocalDevflareContainer({
|
|
2597
|
+
id: runResult.stdout.trim() || containerName,
|
|
2598
|
+
name: containerName,
|
|
2599
|
+
className,
|
|
2600
|
+
engine,
|
|
2601
|
+
host,
|
|
2602
|
+
hostPort,
|
|
2603
|
+
port: startOptions.port,
|
|
2604
|
+
runner,
|
|
2605
|
+
fetchImpl: options.fetch ?? fetch,
|
|
2606
|
+
onDispose: (container) => active.delete(container)
|
|
2607
|
+
});
|
|
2608
|
+
active.add(instance);
|
|
2609
|
+
await waitForContainerReadiness(instance, startOptions, options.waitForPort ?? waitForTcpPort);
|
|
2610
|
+
return instance;
|
|
2611
|
+
},
|
|
2612
|
+
async stopAll() {
|
|
2613
|
+
await Promise.all([...active].map((container) => container.stop()));
|
|
2614
|
+
}
|
|
2615
|
+
};
|
|
2616
|
+
return manager;
|
|
2617
|
+
}
|
|
2618
|
+
var defaultContainerManager = createContainerManager();
|
|
2619
|
+
var containers = defaultContainerManager;
|
|
2620
|
+
async function stopActiveContainers() {
|
|
2621
|
+
await defaultContainerManager.stopAll();
|
|
2622
|
+
}
|
|
2623
|
+
function getAvailableContainerEngine(status, className) {
|
|
2624
|
+
if (!status.available) {
|
|
2625
|
+
throw new Error(`Cannot start Devflare container "${className}": ${status.reason}`);
|
|
2626
|
+
}
|
|
2627
|
+
return status.engine;
|
|
2628
|
+
}
|
|
2629
|
+
async function waitForContainerReadiness(instance, options, waitForPort) {
|
|
2630
|
+
if (!(options.waitForReady ?? true)) {
|
|
2631
|
+
return;
|
|
2632
|
+
}
|
|
2633
|
+
try {
|
|
2634
|
+
await waitForPort(instance.host, instance.hostPort, options.readyTimeoutMs ?? 20000);
|
|
2635
|
+
} catch (error) {
|
|
2636
|
+
await instance.destroy();
|
|
2637
|
+
throw error;
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
async function resolveContainerStart(className, options, cwd) {
|
|
2641
|
+
if (options.image) {
|
|
2642
|
+
return {
|
|
2643
|
+
configDir: cwd,
|
|
2644
|
+
image: options.image
|
|
2645
|
+
};
|
|
2646
|
+
}
|
|
2647
|
+
const { config, configDir: configDir6 } = await loadContainerConfig(options.configPath, cwd);
|
|
2648
|
+
const container = config.containers?.find((candidate) => candidate.className === className || candidate.name === className);
|
|
2649
|
+
if (!container) {
|
|
2650
|
+
throw new Error(`Container "${className}" was not found in devflare config.`);
|
|
2651
|
+
}
|
|
2652
|
+
return {
|
|
2653
|
+
configDir: configDir6,
|
|
2654
|
+
image: container.image,
|
|
2655
|
+
config: container
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
async function loadContainerConfig(configPath, cwd) {
|
|
2659
|
+
const absolutePath = configPath ? resolve(cwd, configPath) : await findNearestConfig(cwd);
|
|
2660
|
+
if (!absolutePath) {
|
|
2661
|
+
throw new Error(`Could not find a devflare config file for container lookup. Searched upward from: ${cwd}`);
|
|
2662
|
+
}
|
|
2663
|
+
const configDir6 = dirname3(absolutePath);
|
|
2664
|
+
const loadedConfig = await loadConfig({
|
|
2665
|
+
cwd: configDir6,
|
|
2666
|
+
configFile: basename(absolutePath)
|
|
2667
|
+
});
|
|
2668
|
+
const config = await applyLocalDevVarsToConfig(loadedConfig, {
|
|
2669
|
+
cwd: configDir6,
|
|
2670
|
+
configPath: absolutePath
|
|
2671
|
+
});
|
|
2672
|
+
return { config, configDir: configDir6 };
|
|
2673
|
+
}
|
|
2674
|
+
async function prepareImage(options) {
|
|
2675
|
+
if (looksLikeLocalPath(options.image)) {
|
|
2676
|
+
return {
|
|
2677
|
+
image: await buildLocalImage(options)
|
|
2678
|
+
};
|
|
2679
|
+
}
|
|
2680
|
+
const inspect = await options.runner.exec(options.engine, ["image", "inspect", options.image], {
|
|
2681
|
+
cwd: options.configDir
|
|
2682
|
+
});
|
|
2683
|
+
if (inspect.exitCode === 0) {
|
|
2684
|
+
return { image: options.image };
|
|
2685
|
+
}
|
|
2686
|
+
if (options.offline) {
|
|
2687
|
+
throw new Error(`Container image "${options.image}" is not present locally. Devflare container tests are offline-first; pull/build the image ahead of time or pass offline: false.`);
|
|
2688
|
+
}
|
|
2689
|
+
const pull = await options.runner.exec(options.engine, ["pull", options.image], {
|
|
2690
|
+
cwd: options.configDir
|
|
2691
|
+
});
|
|
2692
|
+
if (pull.exitCode !== 0) {
|
|
2693
|
+
throw new Error(`Failed to pull container image "${options.image}": ${formatCommandFailure(pull)}`);
|
|
2694
|
+
}
|
|
2695
|
+
return { image: options.image };
|
|
2696
|
+
}
|
|
2697
|
+
async function buildLocalImage(options) {
|
|
2698
|
+
const imagePath = resolve(options.configDir, options.image);
|
|
2699
|
+
const imageStat = await stat(imagePath);
|
|
2700
|
+
const dockerfile = imageStat.isDirectory() ? join9(imagePath, "Dockerfile") : imagePath;
|
|
2701
|
+
const context2 = resolve(options.configDir, options.imageBuildContext ?? (imageStat.isDirectory() ? options.image : dirname3(options.image)));
|
|
2702
|
+
if (!existsSync2(dockerfile)) {
|
|
2703
|
+
throw new Error(`Container Dockerfile does not exist: ${dockerfile}`);
|
|
2704
|
+
}
|
|
2705
|
+
const tag = makeLocalImageTag(options.className, options.configDir, options.image);
|
|
2706
|
+
const args = [
|
|
2707
|
+
"build",
|
|
2708
|
+
...options.offline ? [getOfflineBuildPullArg(options.engine)] : [],
|
|
2709
|
+
"-t",
|
|
2710
|
+
tag,
|
|
2711
|
+
"-f",
|
|
2712
|
+
dockerfile,
|
|
2713
|
+
context2
|
|
2714
|
+
];
|
|
2715
|
+
const result = await options.runner.exec(options.engine, args, {
|
|
2716
|
+
cwd: options.configDir
|
|
2717
|
+
});
|
|
2718
|
+
if (result.exitCode !== 0) {
|
|
2719
|
+
throw new Error(`Failed to build local container image "${options.image}": ${formatCommandFailure(result)}`);
|
|
2720
|
+
}
|
|
2721
|
+
return tag;
|
|
2722
|
+
}
|
|
2723
|
+
function buildRunArgs(options) {
|
|
2724
|
+
const args = [
|
|
2725
|
+
"run",
|
|
2726
|
+
"-d",
|
|
2727
|
+
"--name",
|
|
2728
|
+
options.name,
|
|
2729
|
+
"--label",
|
|
2730
|
+
"devflare.managed=true",
|
|
2731
|
+
"--label",
|
|
2732
|
+
`devflare.container.class=${options.className}`,
|
|
2733
|
+
"-p",
|
|
2734
|
+
`${options.host}:${options.hostPort}:${options.containerPort}`
|
|
2735
|
+
];
|
|
2736
|
+
for (const [key, value] of Object.entries(options.envVars ?? {})) {
|
|
2737
|
+
args.push("-e", `${key}=${value}`);
|
|
2738
|
+
}
|
|
2739
|
+
const command = [...options.command ?? []];
|
|
2740
|
+
if (options.entrypoint && options.entrypoint.length > 0) {
|
|
2741
|
+
args.push("--entrypoint", options.entrypoint[0]);
|
|
2742
|
+
command.unshift(...options.entrypoint.slice(1));
|
|
2743
|
+
}
|
|
2744
|
+
args.push(options.image, ...command);
|
|
2745
|
+
return args;
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
class LocalDevflareContainer {
|
|
2749
|
+
id;
|
|
2750
|
+
name;
|
|
2751
|
+
className;
|
|
2752
|
+
engine;
|
|
2753
|
+
host;
|
|
2754
|
+
hostPort;
|
|
2755
|
+
port;
|
|
2756
|
+
runner;
|
|
2757
|
+
fetchImpl;
|
|
2758
|
+
onDispose;
|
|
2759
|
+
disposed = false;
|
|
2760
|
+
constructor(options) {
|
|
2761
|
+
this.id = options.id;
|
|
2762
|
+
this.name = options.name;
|
|
2763
|
+
this.className = options.className;
|
|
2764
|
+
this.engine = options.engine;
|
|
2765
|
+
this.host = options.host;
|
|
2766
|
+
this.hostPort = options.hostPort;
|
|
2767
|
+
this.port = options.port;
|
|
2768
|
+
this.runner = options.runner;
|
|
2769
|
+
this.fetchImpl = options.fetchImpl;
|
|
2770
|
+
this.onDispose = options.onDispose;
|
|
2771
|
+
}
|
|
2772
|
+
fetch(input, init) {
|
|
2773
|
+
return fetchWithStartupRetries(this.fetchImpl, this.toLocalRequest(input, init));
|
|
2774
|
+
}
|
|
2775
|
+
async logs() {
|
|
2776
|
+
const result = await this.runner.exec(this.engine, ["logs", this.name]);
|
|
2777
|
+
if (result.exitCode !== 0) {
|
|
2778
|
+
throw new Error(`Failed to read logs for Devflare container "${this.name}": ${formatCommandFailure(result)}`);
|
|
2779
|
+
}
|
|
2780
|
+
return result.stdout;
|
|
2781
|
+
}
|
|
2782
|
+
async getState() {
|
|
2783
|
+
const result = await this.runner.exec(this.engine, [
|
|
2784
|
+
"inspect",
|
|
2785
|
+
"--format",
|
|
2786
|
+
"{{json .State}}",
|
|
2787
|
+
this.name
|
|
2788
|
+
]);
|
|
2789
|
+
if (result.exitCode !== 0) {
|
|
2790
|
+
throw new Error(`Failed to inspect Devflare container "${this.name}": ${formatCommandFailure(result)}`);
|
|
2791
|
+
}
|
|
2792
|
+
const parsed = JSON.parse(result.stdout || "{}");
|
|
2793
|
+
return {
|
|
2794
|
+
status: parsed.Status ?? (parsed.Running ? "running" : "stopped"),
|
|
2795
|
+
running: Boolean(parsed.Running),
|
|
2796
|
+
...typeof parsed.ExitCode === "number" && { exitCode: parsed.ExitCode }
|
|
2797
|
+
};
|
|
2798
|
+
}
|
|
2799
|
+
async stop() {
|
|
2800
|
+
if (this.disposed)
|
|
2801
|
+
return;
|
|
2802
|
+
try {
|
|
2803
|
+
await this.runner.exec(this.engine, ["stop", this.name]);
|
|
2804
|
+
} finally {
|
|
2805
|
+
await this.runner.exec(this.engine, ["rm", "-f", this.name]);
|
|
2806
|
+
this.disposed = true;
|
|
2807
|
+
this.onDispose(this);
|
|
2808
|
+
}
|
|
2809
|
+
}
|
|
2810
|
+
async destroy() {
|
|
2811
|
+
if (this.disposed)
|
|
2812
|
+
return;
|
|
2813
|
+
await this.runner.exec(this.engine, ["rm", "-f", this.name]);
|
|
2814
|
+
this.disposed = true;
|
|
2815
|
+
this.onDispose(this);
|
|
2816
|
+
}
|
|
2817
|
+
toLocalRequest(input, init) {
|
|
2818
|
+
const base = `http://${this.host}:${this.hostPort}/`;
|
|
2819
|
+
if (input instanceof Request) {
|
|
2820
|
+
const source = init ? new Request(input, init) : input;
|
|
2821
|
+
const sourceUrl2 = new URL(source.url);
|
|
2822
|
+
const localUrl2 = new URL(`${sourceUrl2.pathname}${sourceUrl2.search}`, base);
|
|
2823
|
+
return new Request(localUrl2, {
|
|
2824
|
+
method: source.method,
|
|
2825
|
+
headers: source.headers,
|
|
2826
|
+
body: source.body,
|
|
2827
|
+
redirect: source.redirect,
|
|
2828
|
+
signal: source.signal
|
|
2829
|
+
});
|
|
2830
|
+
}
|
|
2831
|
+
const sourceUrl = new URL(String(input), base);
|
|
2832
|
+
const localUrl = new URL(`${sourceUrl.pathname}${sourceUrl.search}`, base);
|
|
2833
|
+
return new Request(localUrl, init);
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
async function fetchWithStartupRetries(fetchImpl, request) {
|
|
2837
|
+
if (!canRetryRequest(request)) {
|
|
2838
|
+
return fetchImpl(request);
|
|
2839
|
+
}
|
|
2840
|
+
const deadline = Date.now() + 5000;
|
|
2841
|
+
let lastError;
|
|
2842
|
+
while (Date.now() < deadline) {
|
|
2843
|
+
try {
|
|
2844
|
+
return await fetchImpl(request.clone());
|
|
2845
|
+
} catch (error) {
|
|
2846
|
+
if (!isTransientContainerFetchError(error)) {
|
|
2847
|
+
throw error;
|
|
2848
|
+
}
|
|
2849
|
+
lastError = error;
|
|
2850
|
+
await delay(100);
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
throw lastError;
|
|
2854
|
+
}
|
|
2855
|
+
function canRetryRequest(request) {
|
|
2856
|
+
return (request.method === "GET" || request.method === "HEAD") && request.body === null;
|
|
2857
|
+
}
|
|
2858
|
+
function isTransientContainerFetchError(error) {
|
|
2859
|
+
const value = error;
|
|
2860
|
+
const code = value.code ?? value.cause?.code;
|
|
2861
|
+
if (code === "ECONNRESET" || code === "ECONNREFUSED" || code === "EPIPE" || code === "UND_ERR_SOCKET") {
|
|
2862
|
+
return true;
|
|
2863
|
+
}
|
|
2864
|
+
return typeof value.message === "string" && (value.message.includes("socket connection was closed") || value.message.includes("fetch failed"));
|
|
2865
|
+
}
|
|
2866
|
+
async function waitForTcpPort(host, port, timeoutMs) {
|
|
2867
|
+
const start = Date.now();
|
|
2868
|
+
let lastError;
|
|
2869
|
+
while (Date.now() - start < timeoutMs) {
|
|
2870
|
+
try {
|
|
2871
|
+
await connectOnce(host, port);
|
|
2872
|
+
return;
|
|
2873
|
+
} catch (error) {
|
|
2874
|
+
lastError = error;
|
|
2875
|
+
await delay(100);
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
const message = lastError instanceof Error ? lastError.message : "timed out";
|
|
2879
|
+
throw new Error(`Timed out waiting for container port ${host}:${port}: ${message}`);
|
|
2880
|
+
}
|
|
2881
|
+
function connectOnce(host, port) {
|
|
2882
|
+
return new Promise((resolveConnection, rejectConnection) => {
|
|
2883
|
+
const socket = createConnection({ host, port });
|
|
2884
|
+
socket.once("connect", () => {
|
|
2885
|
+
socket.destroy();
|
|
2886
|
+
resolveConnection();
|
|
2887
|
+
});
|
|
2888
|
+
socket.once("error", rejectConnection);
|
|
2889
|
+
});
|
|
2890
|
+
}
|
|
2891
|
+
function looksLikeLocalPath(image) {
|
|
2892
|
+
return image === "Dockerfile" || image.startsWith(".") || image.startsWith("/") || image.startsWith("\\") || isAbsolute(image) || image.endsWith("/Dockerfile") || image.endsWith("\\Dockerfile");
|
|
2893
|
+
}
|
|
2894
|
+
function getOfflineBuildPullArg(engine) {
|
|
2895
|
+
return engine === "podman" ? "--pull=never" : "--pull=false";
|
|
2896
|
+
}
|
|
2897
|
+
function makeLocalImageTag(className, configDir6, image) {
|
|
2898
|
+
const hash = createHash("sha256").update(`${configDir6}:${image}`).digest("hex").slice(0, 12);
|
|
2899
|
+
return `devflare-local-${sanitizeName(className)}:${hash}`;
|
|
2900
|
+
}
|
|
2901
|
+
function makeContainerName(className, instance) {
|
|
2902
|
+
return `devflare-${sanitizeName(className)}-${sanitizeName(instance ?? "default")}-${randomUUID().slice(0, 8)}`;
|
|
2903
|
+
}
|
|
2904
|
+
function sanitizeName(value) {
|
|
2905
|
+
return value.toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "") || "container";
|
|
2906
|
+
}
|
|
2907
|
+
function formatCommandFailure(result) {
|
|
2908
|
+
return (result.stderr || result.stdout || `exit code ${result.exitCode}`).trim();
|
|
2909
|
+
}
|
|
2910
|
+
function isTruthyEnvFlag(value) {
|
|
2911
|
+
return value === "1" || value?.toLowerCase() === "true" || value?.toLowerCase() === "yes";
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
// src/test/simple-context-lifecycle.ts
|
|
2915
|
+
async function resolveTestContextConfig(configPath, callerDir = getCallerDirectory()) {
|
|
2916
|
+
let absolutePath;
|
|
2917
|
+
if (configPath) {
|
|
2918
|
+
absolutePath = resolve2(callerDir, configPath);
|
|
2919
|
+
} else {
|
|
2920
|
+
const found = await findNearestConfig(callerDir);
|
|
2921
|
+
if (!found) {
|
|
2922
|
+
throw new Error(`Could not find a devflare config file. Searched upward from: ${callerDir}
|
|
2923
|
+
` + `Expected one of: devflare.config.ts, devflare.config.mts, devflare.config.js, devflare.config.mjs
|
|
2924
|
+
` + `Either create a config file or provide an explicit path: createTestContext('./path/to/config.ts')`);
|
|
2925
|
+
}
|
|
2926
|
+
absolutePath = found;
|
|
2927
|
+
}
|
|
2928
|
+
const configDir6 = dirname4(absolutePath);
|
|
2929
|
+
const loadedConfig = await loadConfig({
|
|
2930
|
+
cwd: configDir6,
|
|
2931
|
+
configFile: absolutePath.split(/[/\\]/).pop()
|
|
2932
|
+
});
|
|
2933
|
+
const config = await applyLocalDevVarsToConfig(loadedConfig, {
|
|
2934
|
+
cwd: configDir6,
|
|
2935
|
+
configPath: absolutePath
|
|
2936
|
+
});
|
|
2937
|
+
return { absolutePath, configDir: configDir6, config };
|
|
2938
|
+
}
|
|
2939
|
+
function createDisposeContext(state) {
|
|
2940
|
+
return async () => {
|
|
2941
|
+
if (state.client) {
|
|
2942
|
+
await state.client.disconnect();
|
|
2943
|
+
state.client = null;
|
|
2944
|
+
}
|
|
2945
|
+
if (state.miniflare) {
|
|
2946
|
+
await state.miniflare.dispose();
|
|
2947
|
+
state.miniflare = null;
|
|
2948
|
+
}
|
|
2949
|
+
await disposeLocalWorkerLoaderBindings();
|
|
2950
|
+
await stopActiveContainers();
|
|
2951
|
+
state.envProxy = null;
|
|
2952
|
+
state.transportDecode = null;
|
|
2953
|
+
state.remoteBindings = null;
|
|
2954
|
+
state.miniflareBindings = null;
|
|
2955
|
+
resetQueueState();
|
|
2956
|
+
resetScheduledState();
|
|
2957
|
+
resetWorkerState();
|
|
2958
|
+
resetTailState();
|
|
2959
|
+
resetEmailState();
|
|
2960
|
+
__clearTestContext();
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2963
|
+
|
|
2964
|
+
// src/test/simple-context-startup.ts
|
|
2965
|
+
var TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS = 3;
|
|
2966
|
+
var TEST_CONTEXT_STARTUP_RETRY_DELAY_MS = 75;
|
|
2967
|
+
var TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS = 8;
|
|
2968
|
+
var TEST_CONTEXT_BRIDGE_CONNECT_RETRY_DELAY_MS = 150;
|
|
2969
|
+
function isRetriableTestContextStartupError(error) {
|
|
2970
|
+
if (!(error instanceof Error)) {
|
|
2971
|
+
return false;
|
|
2972
|
+
}
|
|
2973
|
+
const message = error.message.toLowerCase();
|
|
2974
|
+
return message.includes("websocket connection failed") || message.includes("connection timeout: ws://") || message.includes("econnrefused") || message.includes("eaddrinuse") || message.includes("address already in use");
|
|
2975
|
+
}
|
|
2976
|
+
async function waitForTestContextStartupRetry() {
|
|
2977
|
+
await new Promise((resolve3) => setTimeout(resolve3, TEST_CONTEXT_STARTUP_RETRY_DELAY_MS));
|
|
2978
|
+
}
|
|
2979
|
+
async function waitForBridgeClientRetry() {
|
|
2980
|
+
await new Promise((resolve3) => setTimeout(resolve3, TEST_CONTEXT_BRIDGE_CONNECT_RETRY_DELAY_MS));
|
|
2981
|
+
}
|
|
2982
|
+
async function connectBridgeClientWithRetry(url) {
|
|
2983
|
+
let lastError;
|
|
2984
|
+
for (let attempt = 1;attempt <= TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS; attempt++) {
|
|
2985
|
+
const client = new BridgeClient({ url });
|
|
2986
|
+
try {
|
|
2987
|
+
await client.connect();
|
|
2988
|
+
return client;
|
|
2989
|
+
} catch (error) {
|
|
2990
|
+
lastError = error;
|
|
2991
|
+
client.disconnect();
|
|
2992
|
+
if (attempt >= TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS || !isRetriableTestContextStartupError(error)) {
|
|
2993
|
+
throw error;
|
|
2994
|
+
}
|
|
2995
|
+
await waitForBridgeClientRetry();
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
throw lastError instanceof Error ? lastError : new Error("Bridge-backed test context could not connect to the WebSocket gateway.");
|
|
2999
|
+
}
|
|
3000
|
+
function expandLocalBindingWorkers(mfConfig) {
|
|
3001
|
+
const auxiliaryWorkers = [
|
|
3002
|
+
...mfConfig.__devflareLocalSecretWorkers ?? [],
|
|
3003
|
+
...mfConfig.__devflareLocalBindingWorkers ?? []
|
|
3004
|
+
];
|
|
3005
|
+
if (!Array.isArray(auxiliaryWorkers) || auxiliaryWorkers.length === 0) {
|
|
3006
|
+
return mfConfig;
|
|
3007
|
+
}
|
|
3008
|
+
const {
|
|
3009
|
+
__devflareLocalSecretWorkers,
|
|
3010
|
+
__devflareLocalBindingWorkers,
|
|
3011
|
+
port,
|
|
3012
|
+
host,
|
|
3013
|
+
log,
|
|
3014
|
+
kvPersist,
|
|
3015
|
+
r2Persist,
|
|
3016
|
+
d1Persist,
|
|
3017
|
+
durableObjectsPersist,
|
|
3018
|
+
workflowsPersist,
|
|
3019
|
+
imagesPersist,
|
|
3020
|
+
...primaryWorker
|
|
3021
|
+
} = mfConfig;
|
|
3022
|
+
const primaryWorkerName = typeof primaryWorker.name === "string" ? primaryWorker.name : "primary";
|
|
3023
|
+
return {
|
|
3024
|
+
...port !== undefined && { port },
|
|
3025
|
+
...host && { host },
|
|
3026
|
+
...log && { log },
|
|
3027
|
+
...kvPersist && { kvPersist },
|
|
3028
|
+
...r2Persist && { r2Persist },
|
|
3029
|
+
...d1Persist && { d1Persist },
|
|
3030
|
+
...durableObjectsPersist && { durableObjectsPersist },
|
|
3031
|
+
...workflowsPersist && { workflowsPersist },
|
|
3032
|
+
...imagesPersist && { imagesPersist },
|
|
3033
|
+
workers: [
|
|
3034
|
+
{
|
|
3035
|
+
...primaryWorker,
|
|
3036
|
+
name: primaryWorkerName
|
|
3037
|
+
},
|
|
3038
|
+
...auxiliaryWorkers
|
|
3039
|
+
]
|
|
3040
|
+
};
|
|
3041
|
+
}
|
|
3042
|
+
async function startBridgeBackedTestContext(mfConfig) {
|
|
3043
|
+
const { Miniflare } = await import("miniflare");
|
|
3044
|
+
for (let attempt = 1;attempt <= TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS; attempt++) {
|
|
3045
|
+
const port = await getAvailablePort();
|
|
3046
|
+
let miniflare = null;
|
|
3047
|
+
let client = null;
|
|
3048
|
+
try {
|
|
3049
|
+
miniflare = new Miniflare(expandLocalBindingWorkers({
|
|
3050
|
+
...mfConfig,
|
|
3051
|
+
port
|
|
3052
|
+
}));
|
|
3053
|
+
await miniflare.ready;
|
|
3054
|
+
const miniflareBindings = wrapEnvSendEmailBindings(await miniflare.getBindings());
|
|
3055
|
+
client = await connectBridgeClientWithRetry(`ws://localhost:${port}`);
|
|
3056
|
+
return {
|
|
3057
|
+
port,
|
|
3058
|
+
client,
|
|
3059
|
+
miniflare,
|
|
3060
|
+
miniflareBindings
|
|
3061
|
+
};
|
|
3062
|
+
} catch (error) {
|
|
3063
|
+
client?.disconnect();
|
|
3064
|
+
if (miniflare) {
|
|
3065
|
+
try {
|
|
3066
|
+
await miniflare.dispose();
|
|
3067
|
+
} catch {}
|
|
3068
|
+
}
|
|
3069
|
+
if (attempt >= TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS || !isRetriableTestContextStartupError(error)) {
|
|
3070
|
+
throw error;
|
|
3071
|
+
}
|
|
3072
|
+
await waitForTestContextStartupRetry();
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
throw new Error("Bridge-backed test context startup exhausted all retry attempts.");
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
// src/test/simple-context-runtime.ts
|
|
3079
|
+
async function bootTestRuntime(mfConfig, usesMultiWorker) {
|
|
3080
|
+
if (usesMultiWorker) {
|
|
3081
|
+
const { Miniflare } = await import("miniflare");
|
|
3082
|
+
const activePort = await getAvailablePort();
|
|
3083
|
+
const miniflare = new Miniflare({
|
|
3084
|
+
...mfConfig,
|
|
3085
|
+
port: activePort
|
|
3086
|
+
});
|
|
3087
|
+
await miniflare.ready;
|
|
3088
|
+
const miniflareBindings = wrapEnvSendEmailBindings(await miniflare.getBindings());
|
|
3089
|
+
return { activePort, miniflare, miniflareBindings, client: null };
|
|
3090
|
+
}
|
|
3091
|
+
const started = await startBridgeBackedTestContext(mfConfig);
|
|
3092
|
+
return {
|
|
3093
|
+
activePort: started.port,
|
|
3094
|
+
miniflare: started.miniflare,
|
|
3095
|
+
miniflareBindings: started.miniflareBindings,
|
|
3096
|
+
client: started.client
|
|
3097
|
+
};
|
|
3098
|
+
}
|
|
3099
|
+
|
|
3100
|
+
// src/test/simple-context-transport.ts
|
|
3101
|
+
import { join as join10 } from "path";
|
|
3102
|
+
async function loadTransportDecoders(configDir6, transportFile) {
|
|
3103
|
+
const transportPath = join10(configDir6, transportFile);
|
|
3104
|
+
const transportModule = await import(transportPath);
|
|
3105
|
+
if (!transportModule.transport) {
|
|
3106
|
+
console.warn(`[devflare] Warning: Transport file "${transportFile}" does not export a named "transport" object.
|
|
3107
|
+
Expected: export const transport = { ... }
|
|
3108
|
+
Transport encoding/decoding will be disabled.`);
|
|
3109
|
+
return null;
|
|
3110
|
+
}
|
|
3111
|
+
const decoders = new Map;
|
|
3112
|
+
for (const [typeName, transporter] of Object.entries(transportModule.transport)) {
|
|
3113
|
+
const t = transporter;
|
|
3114
|
+
decoders.set(typeName, t.decode);
|
|
3115
|
+
}
|
|
3116
|
+
return decoders;
|
|
3117
|
+
}
|
|
3118
|
+
function decodeTransportValue(decoders, value) {
|
|
3119
|
+
if (!decoders || value === null || typeof value !== "object") {
|
|
3120
|
+
return value;
|
|
3121
|
+
}
|
|
3122
|
+
if ("__transport" in value) {
|
|
3123
|
+
const encoded = value;
|
|
3124
|
+
const decoder = decoders.get(encoded.__transport);
|
|
3125
|
+
if (decoder) {
|
|
3126
|
+
return decoder(encoded.value);
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
if (Array.isArray(value)) {
|
|
3130
|
+
return value.map((item) => decodeTransportValue(decoders, item));
|
|
3131
|
+
}
|
|
3132
|
+
const result = {};
|
|
3133
|
+
for (const [k, v] of Object.entries(value)) {
|
|
3134
|
+
result[k] = decodeTransportValue(decoders, v);
|
|
3135
|
+
}
|
|
3136
|
+
return result;
|
|
3137
|
+
}
|
|
3138
|
+
|
|
3139
|
+
// src/test/simple-context-multi-worker.ts
|
|
3140
|
+
function applyMultiWorkerConfig(mfConfig, config, serviceBindingResolution, doBindingResolution) {
|
|
3141
|
+
const primaryDurableObjects = {
|
|
3142
|
+
...mfConfig.durableObjects || {},
|
|
3143
|
+
...doBindingResolution?.crossWorkerDOBindings || {}
|
|
3144
|
+
};
|
|
3145
|
+
const primaryWorker = {
|
|
3146
|
+
name: config.name ?? "primary",
|
|
3147
|
+
modules: true,
|
|
3148
|
+
script: mfConfig.script,
|
|
3149
|
+
compatibilityDate: config.compatibilityDate ?? "2025-01-01",
|
|
3150
|
+
...mfConfig.kvNamespaces && { kvNamespaces: mfConfig.kvNamespaces },
|
|
3151
|
+
...mfConfig.r2Buckets && { r2Buckets: mfConfig.r2Buckets },
|
|
3152
|
+
...mfConfig.d1Databases && { d1Databases: mfConfig.d1Databases },
|
|
3153
|
+
...mfConfig.ratelimits && { ratelimits: mfConfig.ratelimits },
|
|
3154
|
+
...mfConfig.versionMetadata && { versionMetadata: mfConfig.versionMetadata },
|
|
3155
|
+
...mfConfig.workerLoaders && { workerLoaders: mfConfig.workerLoaders },
|
|
3156
|
+
...mfConfig.mtlsCertificates && { mtlsCertificates: mfConfig.mtlsCertificates },
|
|
3157
|
+
...mfConfig.dispatchNamespaces && { dispatchNamespaces: mfConfig.dispatchNamespaces },
|
|
3158
|
+
...mfConfig.workflows && { workflows: mfConfig.workflows },
|
|
3159
|
+
...mfConfig.pipelines && { pipelines: mfConfig.pipelines },
|
|
3160
|
+
...mfConfig.images && { images: mfConfig.images },
|
|
3161
|
+
...mfConfig.media && { media: mfConfig.media },
|
|
3162
|
+
...mfConfig.artifacts && { artifacts: mfConfig.artifacts },
|
|
3163
|
+
...mfConfig.secretsStoreSecrets && { secretsStoreSecrets: mfConfig.secretsStoreSecrets },
|
|
3164
|
+
...mfConfig.wrappedBindings && { wrappedBindings: mfConfig.wrappedBindings },
|
|
3165
|
+
...mfConfig.email && { email: mfConfig.email },
|
|
3166
|
+
...Object.keys(primaryDurableObjects).length > 0 && { durableObjects: primaryDurableObjects },
|
|
3167
|
+
...mfConfig.serviceBindings || serviceBindingResolution?.primaryServiceBindings ? {
|
|
3168
|
+
serviceBindings: {
|
|
3169
|
+
...mfConfig.serviceBindings ?? {},
|
|
3170
|
+
...serviceBindingResolution?.primaryServiceBindings ?? {}
|
|
3171
|
+
}
|
|
3172
|
+
} : {}
|
|
3173
|
+
};
|
|
3174
|
+
const additionalWorkers = [
|
|
3175
|
+
...serviceBindingResolution?.workers || [],
|
|
3176
|
+
...doBindingResolution?.workers || [],
|
|
3177
|
+
...mfConfig.__devflareLocalSecretWorkers || [],
|
|
3178
|
+
...mfConfig.__devflareLocalBindingWorkers || []
|
|
3179
|
+
];
|
|
3180
|
+
const workersByName = new Map;
|
|
3181
|
+
for (const worker2 of additionalWorkers) {
|
|
3182
|
+
if (!workersByName.has(worker2.name)) {
|
|
3183
|
+
workersByName.set(worker2.name, worker2);
|
|
3184
|
+
continue;
|
|
3185
|
+
}
|
|
3186
|
+
const existing = workersByName.get(worker2.name);
|
|
3187
|
+
if (worker2.durableObjects) {
|
|
3188
|
+
existing.durableObjects = {
|
|
3189
|
+
...existing.durableObjects || {},
|
|
3190
|
+
...worker2.durableObjects
|
|
3191
|
+
};
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
const workers = [primaryWorker, ...workersByName.values()];
|
|
3195
|
+
delete mfConfig.script;
|
|
3196
|
+
delete mfConfig.modules;
|
|
3197
|
+
delete mfConfig.kvNamespaces;
|
|
3198
|
+
delete mfConfig.r2Buckets;
|
|
3199
|
+
delete mfConfig.d1Databases;
|
|
3200
|
+
delete mfConfig.ratelimits;
|
|
3201
|
+
delete mfConfig.versionMetadata;
|
|
3202
|
+
delete mfConfig.workerLoaders;
|
|
3203
|
+
delete mfConfig.mtlsCertificates;
|
|
3204
|
+
delete mfConfig.dispatchNamespaces;
|
|
3205
|
+
delete mfConfig.workflows;
|
|
3206
|
+
delete mfConfig.pipelines;
|
|
3207
|
+
delete mfConfig.images;
|
|
3208
|
+
delete mfConfig.media;
|
|
3209
|
+
delete mfConfig.artifacts;
|
|
3210
|
+
delete mfConfig.secretsStoreSecrets;
|
|
3211
|
+
delete mfConfig.wrappedBindings;
|
|
3212
|
+
delete mfConfig.serviceBindings;
|
|
3213
|
+
delete mfConfig.__devflareLocalSecretWorkers;
|
|
3214
|
+
delete mfConfig.__devflareLocalBindingWorkers;
|
|
3215
|
+
delete mfConfig.durableObjects;
|
|
3216
|
+
mfConfig.workers = workers;
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
// src/test/simple-context-mfconfig.ts
|
|
3220
|
+
function buildInlineBridgeMfConfig(config, options = {}) {
|
|
3221
|
+
const localWorkerBindings = config.vars ?? {};
|
|
3222
|
+
const localSecretWrappedBindingConfig = options.cwd ? buildLocalSecretWrappedBindingConfig(config, options.cwd) : undefined;
|
|
3223
|
+
const localBindingShimServiceConfig = buildLocalBindingShimServiceConfig(config);
|
|
3224
|
+
const localSecretBindingNames = new Set(localSecretWrappedBindingConfig?.localBindingNames ?? []);
|
|
3225
|
+
const mfConfig = {
|
|
3226
|
+
modules: true,
|
|
3227
|
+
compatibilityDate: config.compatibilityDate ?? "2025-01-01",
|
|
3228
|
+
...config.compatibilityFlags && { compatibilityFlags: config.compatibilityFlags }
|
|
3229
|
+
};
|
|
3230
|
+
if (config.bindings?.kv) {
|
|
3231
|
+
mfConfig.kvNamespaces = Object.keys(config.bindings.kv);
|
|
3232
|
+
}
|
|
3233
|
+
if (config.bindings?.r2) {
|
|
3234
|
+
mfConfig.r2Buckets = Object.keys(config.bindings.r2);
|
|
3235
|
+
}
|
|
3236
|
+
if (config.bindings?.d1) {
|
|
3237
|
+
mfConfig.d1Databases = Object.fromEntries(Object.entries(config.bindings.d1).map(([bindingName, bindingConfig]) => {
|
|
3238
|
+
return [bindingName, getLocalD1DatabaseIdentifier(bindingConfig)];
|
|
3239
|
+
}));
|
|
3240
|
+
}
|
|
3241
|
+
const hyperdrivesConfig = buildHyperdrivesConfig(config.bindings ?? {});
|
|
3242
|
+
if (hyperdrivesConfig) {
|
|
3243
|
+
mfConfig.hyperdrives = hyperdrivesConfig;
|
|
3244
|
+
}
|
|
3245
|
+
if (config.bindings?.queues?.producers) {
|
|
3246
|
+
const queueProducers = {};
|
|
3247
|
+
for (const [bindingName, queueName] of Object.entries(config.bindings.queues.producers)) {
|
|
3248
|
+
queueProducers[bindingName] = { queueName };
|
|
3249
|
+
}
|
|
3250
|
+
mfConfig.queueProducers = queueProducers;
|
|
3251
|
+
}
|
|
3252
|
+
if (config.bindings?.rateLimits) {
|
|
3253
|
+
mfConfig.ratelimits = Object.fromEntries(Object.entries(config.bindings.rateLimits).map(([bindingName, binding]) => [
|
|
3254
|
+
bindingName,
|
|
3255
|
+
{
|
|
3256
|
+
simple: {
|
|
3257
|
+
limit: binding.simple.limit,
|
|
3258
|
+
period: binding.simple.period
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
]));
|
|
3262
|
+
}
|
|
3263
|
+
if (config.bindings?.versionMetadata) {
|
|
3264
|
+
mfConfig.versionMetadata = config.bindings.versionMetadata.binding;
|
|
3265
|
+
}
|
|
3266
|
+
if (config.bindings?.workerLoaders) {
|
|
3267
|
+
mfConfig.workerLoaders = Object.fromEntries(Object.keys(config.bindings.workerLoaders).map((bindingName) => [bindingName, {}]));
|
|
3268
|
+
}
|
|
3269
|
+
if (config.bindings?.mtlsCertificates) {
|
|
3270
|
+
mfConfig.mtlsCertificates = Object.fromEntries(Object.entries(config.bindings.mtlsCertificates).map(([bindingName, binding]) => {
|
|
3271
|
+
const normalized = normalizeMtlsCertificateBinding(binding);
|
|
3272
|
+
return [
|
|
3273
|
+
bindingName,
|
|
3274
|
+
{
|
|
3275
|
+
certificate_id: normalized.certificateId
|
|
3276
|
+
}
|
|
3277
|
+
];
|
|
3278
|
+
}));
|
|
3279
|
+
}
|
|
3280
|
+
if (config.bindings?.dispatchNamespaces) {
|
|
3281
|
+
mfConfig.dispatchNamespaces = Object.fromEntries(Object.entries(config.bindings.dispatchNamespaces).map(([bindingName, binding]) => {
|
|
3282
|
+
const normalized = normalizeDispatchNamespaceBinding(binding);
|
|
3283
|
+
return [
|
|
3284
|
+
bindingName,
|
|
3285
|
+
{
|
|
3286
|
+
namespace: normalized.namespace
|
|
3287
|
+
}
|
|
3288
|
+
];
|
|
3289
|
+
}));
|
|
3290
|
+
}
|
|
3291
|
+
if (config.bindings?.workflows) {
|
|
3292
|
+
mfConfig.workflows = Object.fromEntries(Object.entries(config.bindings.workflows).map(([bindingName, binding]) => {
|
|
3293
|
+
const normalized = normalizeWorkflowBinding(binding);
|
|
3294
|
+
return [
|
|
3295
|
+
bindingName,
|
|
3296
|
+
{
|
|
3297
|
+
name: normalized.name,
|
|
3298
|
+
className: normalized.className,
|
|
3299
|
+
...normalized.scriptName && { scriptName: normalized.scriptName },
|
|
3300
|
+
...normalized.limits && { stepLimit: normalized.limits.steps }
|
|
3301
|
+
}
|
|
3302
|
+
];
|
|
3303
|
+
}));
|
|
3304
|
+
}
|
|
3305
|
+
if (config.bindings?.pipelines) {
|
|
3306
|
+
mfConfig.pipelines = Object.fromEntries(Object.entries(config.bindings.pipelines).map(([bindingName, binding]) => {
|
|
3307
|
+
const normalized = normalizePipelineBinding(binding);
|
|
3308
|
+
return [
|
|
3309
|
+
bindingName,
|
|
3310
|
+
typeof binding === "string" ? normalized.pipeline : { pipeline: normalized.pipeline }
|
|
3311
|
+
];
|
|
3312
|
+
}));
|
|
3313
|
+
}
|
|
3314
|
+
if (config.bindings?.images && localBindingShimServiceConfig.localBindingNames.length === 0) {
|
|
3315
|
+
const [entry] = Object.entries(config.bindings.images);
|
|
3316
|
+
if (entry) {
|
|
3317
|
+
const [bindingName, binding] = entry;
|
|
3318
|
+
const normalized = normalizeImagesBinding(bindingName, binding);
|
|
3319
|
+
mfConfig.images = {
|
|
3320
|
+
binding: normalized.binding
|
|
3321
|
+
};
|
|
3322
|
+
}
|
|
3323
|
+
}
|
|
3324
|
+
if (config.bindings?.media && localBindingShimServiceConfig.localBindingNames.length === 0) {
|
|
3325
|
+
const [entry] = Object.entries(config.bindings.media);
|
|
3326
|
+
if (entry) {
|
|
3327
|
+
const [bindingName, binding] = entry;
|
|
3328
|
+
const normalized = normalizeMediaBinding(bindingName, binding);
|
|
3329
|
+
mfConfig.media = {
|
|
3330
|
+
binding: normalized.binding
|
|
3331
|
+
};
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
if (config.bindings?.artifacts) {
|
|
3335
|
+
mfConfig.artifacts = Object.fromEntries(Object.entries(config.bindings.artifacts).map(([bindingName, binding]) => {
|
|
3336
|
+
const normalized = normalizeArtifactsBinding(binding);
|
|
3337
|
+
return [
|
|
3338
|
+
bindingName,
|
|
3339
|
+
{
|
|
3340
|
+
namespace: normalized.namespace
|
|
3341
|
+
}
|
|
3342
|
+
];
|
|
3343
|
+
}));
|
|
3344
|
+
}
|
|
3345
|
+
if (config.bindings?.aiSearchNamespaces) {
|
|
3346
|
+
mfConfig.aiSearchNamespaces = Object.fromEntries(Object.entries(config.bindings.aiSearchNamespaces).map(([bindingName, binding]) => [
|
|
3347
|
+
bindingName,
|
|
3348
|
+
{
|
|
3349
|
+
namespace: binding.namespace
|
|
3350
|
+
}
|
|
3351
|
+
]));
|
|
3352
|
+
}
|
|
3353
|
+
if (config.bindings?.aiSearch) {
|
|
3354
|
+
mfConfig.aiSearchInstances = Object.fromEntries(Object.entries(config.bindings.aiSearch).map(([bindingName, binding]) => [
|
|
3355
|
+
bindingName,
|
|
3356
|
+
{
|
|
3357
|
+
instance_name: binding.instanceName
|
|
3358
|
+
}
|
|
3359
|
+
]));
|
|
3360
|
+
}
|
|
3361
|
+
if (config.bindings?.secretsStore) {
|
|
3362
|
+
const secretsStoreEntries = Object.entries(config.bindings.secretsStore).flatMap(([bindingName, binding]) => {
|
|
3363
|
+
if (localSecretBindingNames.has(bindingName)) {
|
|
3364
|
+
return [];
|
|
3365
|
+
}
|
|
3366
|
+
const normalized = normalizeSecretsStoreBinding(binding, config.secretsStoreId, bindingName);
|
|
3367
|
+
return [[
|
|
3368
|
+
bindingName,
|
|
3369
|
+
{
|
|
3370
|
+
store_id: normalized.storeId,
|
|
3371
|
+
secret_name: normalized.secretName
|
|
3372
|
+
}
|
|
3373
|
+
]];
|
|
3374
|
+
});
|
|
3375
|
+
if (secretsStoreEntries.length > 0) {
|
|
3376
|
+
mfConfig.secretsStoreSecrets = Object.fromEntries(secretsStoreEntries);
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
const wrappedBindings = {
|
|
3380
|
+
...localSecretWrappedBindingConfig?.wrappedBindings ?? {}
|
|
3381
|
+
};
|
|
3382
|
+
const localBindingWorkers = [
|
|
3383
|
+
...localSecretWrappedBindingConfig?.workers ?? [],
|
|
3384
|
+
...localBindingShimServiceConfig.workers
|
|
3385
|
+
];
|
|
3386
|
+
if (Object.keys(wrappedBindings).length > 0) {
|
|
3387
|
+
mfConfig.wrappedBindings = wrappedBindings;
|
|
3388
|
+
}
|
|
3389
|
+
if (localBindingShimServiceConfig.localBindingNames.length > 0) {
|
|
3390
|
+
mfConfig.serviceBindings = {
|
|
3391
|
+
...mfConfig.serviceBindings ?? {},
|
|
3392
|
+
...localBindingShimServiceConfig.serviceBindings
|
|
3393
|
+
};
|
|
3394
|
+
}
|
|
3395
|
+
if (localBindingWorkers.length > 0) {
|
|
3396
|
+
mfConfig.__devflareLocalBindingWorkers = localBindingWorkers;
|
|
3397
|
+
}
|
|
3398
|
+
if (Object.keys(localWorkerBindings).length > 0) {
|
|
3399
|
+
mfConfig.bindings = localWorkerBindings;
|
|
3400
|
+
}
|
|
3401
|
+
if (config.bindings?.sendEmail) {
|
|
3402
|
+
mfConfig.email = {
|
|
3403
|
+
send_email: Object.entries(config.bindings.sendEmail).map(([name, binding]) => ({
|
|
3404
|
+
name,
|
|
3405
|
+
...binding.destinationAddress && {
|
|
3406
|
+
destination_address: binding.destinationAddress
|
|
3407
|
+
},
|
|
3408
|
+
...binding.allowedDestinationAddresses && {
|
|
3409
|
+
allowed_destination_addresses: binding.allowedDestinationAddresses
|
|
3410
|
+
},
|
|
3411
|
+
...binding.allowedSenderAddresses && {
|
|
3412
|
+
allowed_sender_addresses: binding.allowedSenderAddresses
|
|
3413
|
+
}
|
|
3414
|
+
}))
|
|
3415
|
+
};
|
|
3416
|
+
}
|
|
3417
|
+
return mfConfig;
|
|
3418
|
+
}
|
|
3419
|
+
|
|
3420
|
+
// src/test/simple-context.ts
|
|
3421
|
+
function createTestContextState() {
|
|
3422
|
+
return {
|
|
3423
|
+
client: null,
|
|
3424
|
+
miniflare: null,
|
|
3425
|
+
envProxy: null,
|
|
3426
|
+
transportDecode: null,
|
|
3427
|
+
remoteBindings: null,
|
|
3428
|
+
miniflareBindings: null
|
|
3429
|
+
};
|
|
3430
|
+
}
|
|
3431
|
+
function shouldPreferBridgeBinding(hint) {
|
|
3432
|
+
return hint === "do" || hint === "service";
|
|
3433
|
+
}
|
|
3434
|
+
async function createTestContext(configPath) {
|
|
3435
|
+
const state = createTestContextState();
|
|
3436
|
+
const { configDir: configDir6, config } = await resolveTestContextConfig(configPath);
|
|
3437
|
+
state.remoteBindings = buildRemoteAndStaticBindings(config);
|
|
3438
|
+
const hints = extractBindingHints(config);
|
|
3439
|
+
const decodeTransport = (value) => decodeTransportValue(state.transportDecode, value);
|
|
3440
|
+
const needsMultiWorkerForServices = hasServiceBindings(config);
|
|
3441
|
+
const needsMultiWorkerForDOs = hasCrossWorkerDOs(config);
|
|
3442
|
+
const needsMultiWorker = needsMultiWorkerForServices || needsMultiWorkerForDOs;
|
|
3443
|
+
let serviceBindingResolution = null;
|
|
3444
|
+
let doBindingResolution = null;
|
|
3445
|
+
if (needsMultiWorkerForServices) {
|
|
3446
|
+
serviceBindingResolution = await resolveServiceBindings(config, configDir6);
|
|
3447
|
+
}
|
|
3448
|
+
if (needsMultiWorkerForDOs) {
|
|
3449
|
+
doBindingResolution = await resolveDOBindings(config, configDir6);
|
|
3450
|
+
}
|
|
3451
|
+
const mfConfig = buildInlineBridgeMfConfig(config, { cwd: configDir6 });
|
|
3452
|
+
const transportFile = resolveTransportFile(configDir6, config.files?.transport);
|
|
3453
|
+
if (transportFile) {
|
|
3454
|
+
state.transportDecode = await loadTransportDecoders(configDir6, transportFile);
|
|
3455
|
+
}
|
|
3456
|
+
const gateway = await buildDurableObjectGateway(config, configDir6, transportFile);
|
|
3457
|
+
mfConfig.script = gateway.script;
|
|
3458
|
+
if (gateway.durableObjects) {
|
|
3459
|
+
mfConfig.durableObjects = gateway.durableObjects;
|
|
3460
|
+
}
|
|
3461
|
+
const hasMultiWorkerServices = serviceBindingResolution && serviceBindingResolution.workers.length > 0;
|
|
3462
|
+
const hasMultiWorkerDOs = doBindingResolution && doBindingResolution.workers.length > 0;
|
|
3463
|
+
if (hasMultiWorkerServices || hasMultiWorkerDOs) {
|
|
3464
|
+
applyMultiWorkerConfig(mfConfig, config, serviceBindingResolution, doBindingResolution);
|
|
3465
|
+
}
|
|
3466
|
+
const usesMultiWorker = Boolean(hasMultiWorkerServices || hasMultiWorkerDOs);
|
|
3467
|
+
const runtime = await bootTestRuntime(mfConfig, usesMultiWorker);
|
|
3468
|
+
const activePort = runtime.activePort;
|
|
3469
|
+
state.miniflare = runtime.miniflare;
|
|
3470
|
+
state.miniflareBindings = runtime.miniflareBindings;
|
|
3471
|
+
state.client = runtime.client;
|
|
3472
|
+
const disposeContext = createDisposeContext(state);
|
|
3473
|
+
const getTestEnv = () => {
|
|
3474
|
+
return new Proxy({}, {
|
|
3475
|
+
get(_, prop) {
|
|
3476
|
+
if (state.remoteBindings && prop in state.remoteBindings) {
|
|
3477
|
+
return state.remoteBindings[prop];
|
|
3478
|
+
}
|
|
3479
|
+
if (hints[prop] === "sendEmail" && state.envProxy && prop in state.envProxy) {
|
|
3480
|
+
return state.envProxy[prop];
|
|
3481
|
+
}
|
|
3482
|
+
if (state.miniflareBindings && prop in state.miniflareBindings) {
|
|
3483
|
+
return state.miniflareBindings[prop];
|
|
3484
|
+
}
|
|
3485
|
+
if (state.envProxy && prop in state.envProxy) {
|
|
3486
|
+
return state.envProxy[prop];
|
|
3487
|
+
}
|
|
3488
|
+
return;
|
|
3489
|
+
},
|
|
3490
|
+
has(_, prop) {
|
|
3491
|
+
return Boolean(state.remoteBindings && prop in state.remoteBindings || state.miniflareBindings && prop in state.miniflareBindings || state.envProxy && prop in state.envProxy);
|
|
3492
|
+
}
|
|
3493
|
+
});
|
|
3494
|
+
};
|
|
3495
|
+
const handlerPaths = await resolveHandlerPaths(configDir6, config);
|
|
3496
|
+
configureSurfaceHandlers({
|
|
3497
|
+
handlerPaths,
|
|
3498
|
+
configDir: configDir6,
|
|
3499
|
+
activePort,
|
|
3500
|
+
getEnv: getTestEnv
|
|
3501
|
+
});
|
|
3502
|
+
if (usesMultiWorker) {
|
|
3503
|
+
setBindingHints(hints);
|
|
3504
|
+
__setTestContext(createMultiWorkerEnvAccessor(state), disposeContext);
|
|
3505
|
+
return;
|
|
3506
|
+
}
|
|
3507
|
+
const bridgeClient = state.client;
|
|
3508
|
+
if (!bridgeClient) {
|
|
3509
|
+
throw new Error("Bridge-backed test context did not initialize a client.");
|
|
3510
|
+
}
|
|
3511
|
+
setBindingHints(hints);
|
|
3512
|
+
state.envProxy = createEnvProxy({
|
|
3513
|
+
client: bridgeClient,
|
|
3514
|
+
transformResult: (result) => decodeTransport(result)
|
|
3515
|
+
});
|
|
3516
|
+
__setTestContext(createBridgeEnvAccessor(state, hints, shouldPreferBridgeBinding), disposeContext);
|
|
3517
|
+
}
|
|
3518
|
+
// src/test/cf.ts
|
|
3519
|
+
var cf = {
|
|
3520
|
+
email,
|
|
3521
|
+
queue: queue2,
|
|
23
3522
|
scheduled,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
3523
|
+
worker,
|
|
3524
|
+
tail
|
|
3525
|
+
};
|
|
3526
|
+
// src/test/should-skip.ts
|
|
3527
|
+
var REMOTE_ONLY_SERVICES = new Set([
|
|
3528
|
+
"ai",
|
|
3529
|
+
"ai_search",
|
|
3530
|
+
"ai_gateway",
|
|
3531
|
+
"media",
|
|
3532
|
+
"mtls_certificates",
|
|
3533
|
+
"artifacts",
|
|
3534
|
+
"builds",
|
|
3535
|
+
"vectorize"
|
|
3536
|
+
]);
|
|
3537
|
+
var skipResults = new Map;
|
|
3538
|
+
var containerSkipResult = null;
|
|
3539
|
+
var EXPECTED_ERROR_PATTERNS = [
|
|
3540
|
+
"ECONNREFUSED",
|
|
3541
|
+
"ETIMEDOUT",
|
|
3542
|
+
"ENOTFOUND",
|
|
3543
|
+
"fetch failed",
|
|
3544
|
+
"network",
|
|
3545
|
+
"401",
|
|
3546
|
+
"403",
|
|
3547
|
+
"429",
|
|
3548
|
+
"500",
|
|
3549
|
+
"502",
|
|
3550
|
+
"503",
|
|
3551
|
+
"504",
|
|
3552
|
+
"rate limit",
|
|
3553
|
+
"unauthorized",
|
|
3554
|
+
"forbidden",
|
|
3555
|
+
"timeout"
|
|
3556
|
+
];
|
|
3557
|
+
function isExpectedError(error) {
|
|
3558
|
+
if (!(error instanceof Error))
|
|
3559
|
+
return false;
|
|
3560
|
+
const msg = error.message.toLowerCase();
|
|
3561
|
+
return EXPECTED_ERROR_PATTERNS.some((pattern) => msg.includes(pattern.toLowerCase()));
|
|
3562
|
+
}
|
|
3563
|
+
async function computeSkip(service) {
|
|
3564
|
+
try {
|
|
3565
|
+
if (REMOTE_ONLY_SERVICES.has(service) && !isRemoteModeActive()) {
|
|
3566
|
+
const status = getRemoteModeStatus();
|
|
3567
|
+
console.log(`⏭️ ${service.toUpperCase()} tests skipped: Remote-only service.
|
|
3568
|
+
` + ` Enable with: ${status.isEnabled ? "" : "devflare remote enable"}
|
|
3569
|
+
` + ` Or set: DEVFLARE_REMOTE=1
|
|
3570
|
+
` + ` See: https://github.com/ArthurvdVenne/devflare#remote-testing`);
|
|
3571
|
+
return true;
|
|
3572
|
+
}
|
|
3573
|
+
const isAuth = await isAuthenticated();
|
|
3574
|
+
if (!isAuth) {
|
|
3575
|
+
console.log(`⏭️ ${service.toUpperCase()} tests skipped: Not authenticated. Run: bunx wrangler login
|
|
3576
|
+
` + ` See: https://github.com/ArthurvdVenne/devflare#authentication`);
|
|
3577
|
+
return true;
|
|
3578
|
+
}
|
|
3579
|
+
const primary = await getPrimaryAccount();
|
|
3580
|
+
if (!primary) {
|
|
3581
|
+
console.log(`⏭️ ${service.toUpperCase()} tests skipped: No Cloudflare account found
|
|
3582
|
+
` + ` See: https://github.com/ArthurvdVenne/devflare#authentication`);
|
|
3583
|
+
return true;
|
|
3584
|
+
}
|
|
3585
|
+
const { accountId } = await getEffectiveAccountId(primary.id);
|
|
3586
|
+
try {
|
|
3587
|
+
const { allowed, reason } = await canProceedWithTest(accountId, service);
|
|
3588
|
+
if (!allowed) {
|
|
3589
|
+
console.log(`⏭️ ${service.toUpperCase()} tests skipped: ${reason}`);
|
|
3590
|
+
return true;
|
|
3591
|
+
}
|
|
3592
|
+
} catch {}
|
|
3593
|
+
return false;
|
|
3594
|
+
} catch (error) {
|
|
3595
|
+
if (isExpectedError(error)) {
|
|
3596
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3597
|
+
console.log(`⏭️ ${service.toUpperCase()} tests skipped: ${message}`);
|
|
3598
|
+
return true;
|
|
3599
|
+
}
|
|
3600
|
+
throw error;
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
function getSkipResult(service) {
|
|
3604
|
+
let result = skipResults.get(service);
|
|
3605
|
+
if (!result) {
|
|
3606
|
+
result = computeSkip(service);
|
|
3607
|
+
skipResults.set(service, result);
|
|
3608
|
+
}
|
|
3609
|
+
return result;
|
|
3610
|
+
}
|
|
3611
|
+
function getContainerSkipResult() {
|
|
3612
|
+
if (!containerSkipResult) {
|
|
3613
|
+
containerSkipResult = getContainerSkipReason().then((reason) => {
|
|
3614
|
+
if (reason) {
|
|
3615
|
+
console.log(`CONTAINERS tests skipped: ${reason}`);
|
|
3616
|
+
return true;
|
|
3617
|
+
}
|
|
3618
|
+
return false;
|
|
3619
|
+
});
|
|
3620
|
+
}
|
|
3621
|
+
return containerSkipResult;
|
|
3622
|
+
}
|
|
3623
|
+
var shouldSkip = {
|
|
3624
|
+
get ai() {
|
|
3625
|
+
return getSkipResult("ai");
|
|
3626
|
+
},
|
|
3627
|
+
get aiSearch() {
|
|
3628
|
+
return getSkipResult("ai_search");
|
|
3629
|
+
},
|
|
3630
|
+
get aiGateway() {
|
|
3631
|
+
return getSkipResult("ai_gateway");
|
|
3632
|
+
},
|
|
3633
|
+
get vectorize() {
|
|
3634
|
+
return getSkipResult("vectorize");
|
|
3635
|
+
},
|
|
3636
|
+
get workers() {
|
|
3637
|
+
return getSkipResult("workers");
|
|
3638
|
+
},
|
|
3639
|
+
get kv() {
|
|
3640
|
+
return getSkipResult("kv");
|
|
3641
|
+
},
|
|
3642
|
+
get d1() {
|
|
3643
|
+
return getSkipResult("d1");
|
|
3644
|
+
},
|
|
3645
|
+
get r2() {
|
|
3646
|
+
return getSkipResult("r2");
|
|
3647
|
+
},
|
|
3648
|
+
get queues() {
|
|
3649
|
+
return getSkipResult("queues");
|
|
3650
|
+
},
|
|
3651
|
+
get durableObjects() {
|
|
3652
|
+
return getSkipResult("durable_objects");
|
|
3653
|
+
},
|
|
3654
|
+
get media() {
|
|
3655
|
+
return getSkipResult("media");
|
|
3656
|
+
},
|
|
3657
|
+
get mtlsCertificates() {
|
|
3658
|
+
return getSkipResult("mtls_certificates");
|
|
3659
|
+
},
|
|
3660
|
+
get artifacts() {
|
|
3661
|
+
return getSkipResult("artifacts");
|
|
3662
|
+
},
|
|
3663
|
+
get builds() {
|
|
3664
|
+
return getSkipResult("builds");
|
|
3665
|
+
},
|
|
3666
|
+
get containers() {
|
|
3667
|
+
return getContainerSkipResult();
|
|
3668
|
+
}
|
|
3669
|
+
};
|
|
3670
|
+
// src/test/ai-search.ts
|
|
3671
|
+
var MOCK_TIMESTAMP = "2026-04-26T00:00:00.000Z";
|
|
3672
|
+
function cloneInfo(value) {
|
|
3673
|
+
return { ...value };
|
|
3674
|
+
}
|
|
3675
|
+
function extractSearchQuery(params) {
|
|
3676
|
+
if ("query" in params && typeof params.query === "string") {
|
|
3677
|
+
return params.query;
|
|
3678
|
+
}
|
|
3679
|
+
const messages = "messages" in params ? params.messages : [];
|
|
3680
|
+
return messages.filter((message) => message.role === "user" && message.content).map((message) => message.content).join(" ");
|
|
3681
|
+
}
|
|
3682
|
+
function tokenize(value) {
|
|
3683
|
+
return value.toLowerCase().split(/[^a-z0-9_]+/).filter((token) => token.length > 2);
|
|
3684
|
+
}
|
|
3685
|
+
function scoreText(query, text) {
|
|
3686
|
+
const tokens = tokenize(query);
|
|
3687
|
+
if (tokens.length === 0) {
|
|
3688
|
+
return 1;
|
|
3689
|
+
}
|
|
3690
|
+
const haystack = text.toLowerCase();
|
|
3691
|
+
const matches = tokens.filter((token) => haystack.includes(token)).length;
|
|
3692
|
+
return matches === 0 ? 0 : Math.max(0.1, matches / tokens.length);
|
|
3693
|
+
}
|
|
3694
|
+
function streamFromText(text) {
|
|
3695
|
+
const encoded = new TextEncoder().encode(text);
|
|
3696
|
+
return new ReadableStream({
|
|
3697
|
+
start(controller) {
|
|
3698
|
+
controller.enqueue(encoded);
|
|
3699
|
+
controller.close();
|
|
3700
|
+
}
|
|
3701
|
+
});
|
|
3702
|
+
}
|
|
3703
|
+
async function contentToText(content) {
|
|
3704
|
+
if (typeof content === "string") {
|
|
3705
|
+
return content;
|
|
3706
|
+
}
|
|
3707
|
+
if (content instanceof Blob) {
|
|
3708
|
+
return content.text();
|
|
3709
|
+
}
|
|
3710
|
+
return new Response(content).text();
|
|
3711
|
+
}
|
|
3712
|
+
function createItemInfo(id, key, content, options = {}) {
|
|
3713
|
+
return {
|
|
3714
|
+
id,
|
|
3715
|
+
key,
|
|
3716
|
+
status: options.status ?? "completed",
|
|
3717
|
+
next_action: null,
|
|
3718
|
+
namespace: options.namespace,
|
|
3719
|
+
chunks_count: content.length > 0 ? 1 : 0,
|
|
3720
|
+
file_size: new TextEncoder().encode(content).byteLength,
|
|
3721
|
+
source_id: options.sourceId ?? null,
|
|
3722
|
+
last_seen_at: MOCK_TIMESTAMP,
|
|
3723
|
+
created_at: MOCK_TIMESTAMP,
|
|
3724
|
+
metadata: options.metadata
|
|
3725
|
+
};
|
|
3726
|
+
}
|
|
3727
|
+
function createItemChunks(item, chunks) {
|
|
3728
|
+
let offset = 0;
|
|
3729
|
+
return chunks.map((text, index) => {
|
|
3730
|
+
const start = offset;
|
|
3731
|
+
const end = start + new TextEncoder().encode(text).byteLength;
|
|
3732
|
+
offset = end;
|
|
3733
|
+
return {
|
|
3734
|
+
id: `${item.id}:chunk-${index + 1}`,
|
|
3735
|
+
text,
|
|
3736
|
+
start_byte: start,
|
|
3737
|
+
end_byte: end,
|
|
3738
|
+
item: {
|
|
3739
|
+
timestamp: Date.parse(MOCK_TIMESTAMP),
|
|
3740
|
+
key: item.key,
|
|
3741
|
+
metadata: item.metadata
|
|
3742
|
+
}
|
|
3743
|
+
};
|
|
3744
|
+
});
|
|
3745
|
+
}
|
|
3746
|
+
function createStoredItem(sequence, fixture, namespace) {
|
|
3747
|
+
const content = fixture.content ?? fixture.chunks?.join(`
|
|
3748
|
+
`) ?? "";
|
|
3749
|
+
const id = fixture.id ?? `item-${sequence}`;
|
|
3750
|
+
const info = createItemInfo(id, fixture.key, content, {
|
|
3751
|
+
metadata: fixture.metadata,
|
|
3752
|
+
status: fixture.status,
|
|
3753
|
+
namespace
|
|
3754
|
+
});
|
|
3755
|
+
const chunks = createItemChunks(info, fixture.chunks ?? [content]);
|
|
3756
|
+
return {
|
|
3757
|
+
info,
|
|
3758
|
+
content,
|
|
3759
|
+
contentType: fixture.contentType ?? "text/plain",
|
|
3760
|
+
chunks,
|
|
3761
|
+
logs: [
|
|
3762
|
+
{
|
|
3763
|
+
timestamp: MOCK_TIMESTAMP,
|
|
3764
|
+
action: "index",
|
|
3765
|
+
message: `Indexed ${fixture.key}`,
|
|
3766
|
+
fileKey: fixture.key,
|
|
3767
|
+
chunkCount: chunks.length,
|
|
3768
|
+
processingTimeMs: 0
|
|
3769
|
+
}
|
|
3770
|
+
]
|
|
3771
|
+
};
|
|
3772
|
+
}
|
|
3773
|
+
function isAISearchInstance(value) {
|
|
3774
|
+
return typeof value.search === "function";
|
|
3775
|
+
}
|
|
3776
|
+
function createMockAISearchInstance(options = {}) {
|
|
3777
|
+
const instanceId = options.id ?? options.info?.id ?? "mock-ai-search";
|
|
3778
|
+
const namespace = options.namespace ?? options.info?.namespace;
|
|
3779
|
+
const items = new Map;
|
|
3780
|
+
const jobs = new Map;
|
|
3781
|
+
const searches = [];
|
|
3782
|
+
let itemSequence = 0;
|
|
3783
|
+
let jobSequence = 0;
|
|
3784
|
+
let info = {
|
|
3785
|
+
id: instanceId,
|
|
3786
|
+
type: "builtin",
|
|
3787
|
+
namespace,
|
|
3788
|
+
status: "ready",
|
|
3789
|
+
created_at: MOCK_TIMESTAMP,
|
|
3790
|
+
modified_at: MOCK_TIMESTAMP,
|
|
3791
|
+
index_method: {
|
|
3792
|
+
vector: true,
|
|
3793
|
+
keyword: true
|
|
3794
|
+
},
|
|
3795
|
+
fusion_method: "rrf",
|
|
3796
|
+
...options.info
|
|
3797
|
+
};
|
|
3798
|
+
for (const fixture of options.items ?? []) {
|
|
3799
|
+
const stored = createStoredItem(++itemSequence, fixture, namespace);
|
|
3800
|
+
items.set(stored.info.id, stored);
|
|
3801
|
+
}
|
|
3802
|
+
const findMatches = (params) => {
|
|
3803
|
+
searches.push(params);
|
|
3804
|
+
const query = extractSearchQuery(params);
|
|
3805
|
+
const maxResults = params.ai_search_options?.retrieval?.max_num_results ?? 10;
|
|
3806
|
+
const chunks = [];
|
|
3807
|
+
for (const item of items.values()) {
|
|
3808
|
+
for (const chunk of item.chunks) {
|
|
3809
|
+
const score = scoreText(query, `${item.info.key} ${chunk.text}`);
|
|
3810
|
+
if (score === 0) {
|
|
3811
|
+
continue;
|
|
3812
|
+
}
|
|
3813
|
+
chunks.push({
|
|
3814
|
+
id: chunk.id,
|
|
3815
|
+
type: "text",
|
|
3816
|
+
score,
|
|
3817
|
+
text: chunk.text,
|
|
3818
|
+
item: {
|
|
3819
|
+
timestamp: Date.parse(MOCK_TIMESTAMP),
|
|
3820
|
+
key: item.info.key,
|
|
3821
|
+
metadata: item.info.metadata
|
|
3822
|
+
},
|
|
3823
|
+
scoring_details: {
|
|
3824
|
+
keyword_score: score,
|
|
3825
|
+
vector_score: score,
|
|
3826
|
+
keyword_rank: chunks.length + 1,
|
|
3827
|
+
vector_rank: chunks.length + 1,
|
|
3828
|
+
fusion_method: "rrf"
|
|
3829
|
+
}
|
|
3830
|
+
});
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
return {
|
|
3834
|
+
search_query: query,
|
|
3835
|
+
chunks: chunks.slice(0, maxResults)
|
|
3836
|
+
};
|
|
3837
|
+
};
|
|
3838
|
+
const createItemHandle = (itemId) => ({
|
|
3839
|
+
async info() {
|
|
3840
|
+
const item = items.get(itemId);
|
|
3841
|
+
if (!item) {
|
|
3842
|
+
throw new Error(`Mock AI Search item "${itemId}" was not found.`);
|
|
3843
|
+
}
|
|
3844
|
+
return cloneInfo(item.info);
|
|
3845
|
+
},
|
|
3846
|
+
async download() {
|
|
3847
|
+
const item = items.get(itemId);
|
|
3848
|
+
if (!item) {
|
|
3849
|
+
throw new Error(`Mock AI Search item "${itemId}" was not found.`);
|
|
3850
|
+
}
|
|
3851
|
+
return {
|
|
3852
|
+
body: streamFromText(item.content),
|
|
3853
|
+
contentType: item.contentType,
|
|
3854
|
+
filename: item.info.key,
|
|
3855
|
+
size: new TextEncoder().encode(item.content).byteLength
|
|
3856
|
+
};
|
|
3857
|
+
},
|
|
3858
|
+
async sync() {
|
|
3859
|
+
const item = items.get(itemId);
|
|
3860
|
+
if (!item) {
|
|
3861
|
+
throw new Error(`Mock AI Search item "${itemId}" was not found.`);
|
|
3862
|
+
}
|
|
3863
|
+
item.info.status = "completed";
|
|
3864
|
+
item.info.last_seen_at = MOCK_TIMESTAMP;
|
|
3865
|
+
return cloneInfo(item.info);
|
|
3866
|
+
},
|
|
3867
|
+
async logs(params) {
|
|
3868
|
+
const item = items.get(itemId);
|
|
3869
|
+
if (!item) {
|
|
3870
|
+
throw new Error(`Mock AI Search item "${itemId}" was not found.`);
|
|
3871
|
+
}
|
|
3872
|
+
const limit = params?.limit ?? 50;
|
|
3873
|
+
const result = item.logs.slice(0, limit);
|
|
3874
|
+
return {
|
|
3875
|
+
result,
|
|
3876
|
+
result_info: {
|
|
3877
|
+
count: result.length,
|
|
3878
|
+
per_page: limit,
|
|
3879
|
+
cursor: null,
|
|
3880
|
+
truncated: item.logs.length > result.length
|
|
3881
|
+
}
|
|
3882
|
+
};
|
|
3883
|
+
},
|
|
3884
|
+
async chunks(params) {
|
|
3885
|
+
const item = items.get(itemId);
|
|
3886
|
+
if (!item) {
|
|
3887
|
+
throw new Error(`Mock AI Search item "${itemId}" was not found.`);
|
|
3888
|
+
}
|
|
3889
|
+
const offset = params?.offset ?? 0;
|
|
3890
|
+
const limit = params?.limit ?? 20;
|
|
3891
|
+
const result = item.chunks.slice(offset, offset + limit);
|
|
3892
|
+
return {
|
|
3893
|
+
result,
|
|
3894
|
+
result_info: {
|
|
3895
|
+
count: result.length,
|
|
3896
|
+
total: item.chunks.length,
|
|
3897
|
+
limit,
|
|
3898
|
+
offset
|
|
3899
|
+
}
|
|
3900
|
+
};
|
|
3901
|
+
}
|
|
3902
|
+
});
|
|
3903
|
+
const uploadItem = async (name, content, uploadOptions) => {
|
|
3904
|
+
const text = await contentToText(content);
|
|
3905
|
+
const existing = Array.from(items.values()).find((item) => item.info.key === name);
|
|
3906
|
+
const stored = createStoredItem(existing ? Number(existing.info.id.replace(/^item-/, "")) || ++itemSequence : ++itemSequence, {
|
|
3907
|
+
id: existing?.info.id,
|
|
3908
|
+
key: name,
|
|
3909
|
+
content: text,
|
|
3910
|
+
metadata: uploadOptions?.metadata
|
|
3911
|
+
}, namespace);
|
|
3912
|
+
items.set(stored.info.id, stored);
|
|
3913
|
+
return cloneInfo(stored.info);
|
|
3914
|
+
};
|
|
3915
|
+
const itemsApi = {
|
|
3916
|
+
async list(params) {
|
|
3917
|
+
let result = Array.from(items.values()).map((item) => cloneInfo(item.info));
|
|
3918
|
+
if (params?.search) {
|
|
3919
|
+
const search = params.search.toLowerCase();
|
|
3920
|
+
result = result.filter((item) => item.key.toLowerCase().includes(search));
|
|
3921
|
+
}
|
|
3922
|
+
if (params?.status) {
|
|
3923
|
+
result = result.filter((item) => item.status === params.status);
|
|
3924
|
+
}
|
|
3925
|
+
const page = params?.page ?? 1;
|
|
3926
|
+
const perPage = (params?.per_page ?? result.length) || 50;
|
|
3927
|
+
const start = (page - 1) * perPage;
|
|
3928
|
+
const paged = result.slice(start, start + perPage);
|
|
3929
|
+
return {
|
|
3930
|
+
result: paged,
|
|
3931
|
+
result_info: {
|
|
3932
|
+
count: paged.length,
|
|
3933
|
+
page,
|
|
3934
|
+
per_page: perPage,
|
|
3935
|
+
total_count: result.length
|
|
3936
|
+
}
|
|
3937
|
+
};
|
|
3938
|
+
},
|
|
3939
|
+
upload: uploadItem,
|
|
3940
|
+
async uploadAndPoll(name, content, uploadOptions) {
|
|
3941
|
+
return uploadItem(name, content, uploadOptions);
|
|
3942
|
+
},
|
|
3943
|
+
get(itemId) {
|
|
3944
|
+
return createItemHandle(itemId);
|
|
3945
|
+
},
|
|
3946
|
+
async delete(itemId) {
|
|
3947
|
+
items.delete(itemId);
|
|
3948
|
+
}
|
|
3949
|
+
};
|
|
3950
|
+
const createJobHandle = (jobId) => ({
|
|
3951
|
+
async info() {
|
|
3952
|
+
const job = jobs.get(jobId);
|
|
3953
|
+
if (!job) {
|
|
3954
|
+
throw new Error(`Mock AI Search job "${jobId}" was not found.`);
|
|
3955
|
+
}
|
|
3956
|
+
return cloneInfo(job);
|
|
3957
|
+
},
|
|
3958
|
+
async logs(params) {
|
|
3959
|
+
const perPage = params?.per_page ?? 50;
|
|
3960
|
+
return {
|
|
3961
|
+
result: [
|
|
3962
|
+
{
|
|
3963
|
+
id: 1,
|
|
3964
|
+
message: `Mock job ${jobId}`,
|
|
3965
|
+
message_type: 0,
|
|
3966
|
+
created_at: Date.parse(MOCK_TIMESTAMP)
|
|
3967
|
+
}
|
|
3968
|
+
].slice(0, perPage),
|
|
3969
|
+
result_info: {
|
|
3970
|
+
count: 1,
|
|
3971
|
+
page: params?.page ?? 1,
|
|
3972
|
+
per_page: perPage,
|
|
3973
|
+
total_count: 1
|
|
3974
|
+
}
|
|
3975
|
+
};
|
|
3976
|
+
},
|
|
3977
|
+
async cancel() {
|
|
3978
|
+
const job = jobs.get(jobId);
|
|
3979
|
+
if (!job) {
|
|
3980
|
+
throw new Error(`Mock AI Search job "${jobId}" was not found.`);
|
|
3981
|
+
}
|
|
3982
|
+
const updated = {
|
|
3983
|
+
...job,
|
|
3984
|
+
ended_at: MOCK_TIMESTAMP,
|
|
3985
|
+
end_reason: "cancelled"
|
|
3986
|
+
};
|
|
3987
|
+
jobs.set(jobId, updated);
|
|
3988
|
+
return cloneInfo(updated);
|
|
3989
|
+
}
|
|
3990
|
+
});
|
|
3991
|
+
const jobsApi = {
|
|
3992
|
+
async list(params) {
|
|
3993
|
+
const allJobs = Array.from(jobs.values()).map((job) => cloneInfo(job));
|
|
3994
|
+
const page = params?.page ?? 1;
|
|
3995
|
+
const perPage = (params?.per_page ?? allJobs.length) || 50;
|
|
3996
|
+
const start = (page - 1) * perPage;
|
|
3997
|
+
const result = allJobs.slice(start, start + perPage);
|
|
3998
|
+
return {
|
|
3999
|
+
result,
|
|
4000
|
+
result_info: {
|
|
4001
|
+
count: result.length,
|
|
4002
|
+
page,
|
|
4003
|
+
per_page: perPage,
|
|
4004
|
+
total_count: allJobs.length
|
|
4005
|
+
}
|
|
4006
|
+
};
|
|
4007
|
+
},
|
|
4008
|
+
async create(params) {
|
|
4009
|
+
const id = `job-${++jobSequence}`;
|
|
4010
|
+
const job = {
|
|
4011
|
+
id,
|
|
4012
|
+
source: "user",
|
|
4013
|
+
description: params?.description,
|
|
4014
|
+
started_at: MOCK_TIMESTAMP
|
|
4015
|
+
};
|
|
4016
|
+
jobs.set(id, job);
|
|
4017
|
+
return cloneInfo(job);
|
|
4018
|
+
},
|
|
4019
|
+
get(jobId) {
|
|
4020
|
+
return createJobHandle(jobId);
|
|
4021
|
+
}
|
|
4022
|
+
};
|
|
4023
|
+
return {
|
|
4024
|
+
async search(params) {
|
|
4025
|
+
return findMatches(params);
|
|
4026
|
+
},
|
|
4027
|
+
async chatCompletions(params) {
|
|
4028
|
+
const search = findMatches({
|
|
4029
|
+
messages: params.messages,
|
|
4030
|
+
ai_search_options: params.ai_search_options
|
|
4031
|
+
});
|
|
4032
|
+
const content = typeof options.chatMessage === "function" ? options.chatMessage(search.chunks, params) : options.chatMessage ?? (search.chunks.map((chunk) => chunk.text).join(`
|
|
4033
|
+
`) || "No matching offline AI Search chunks.");
|
|
4034
|
+
if (params.stream) {
|
|
4035
|
+
const payload = JSON.stringify({
|
|
4036
|
+
choices: [{ delta: { content } }],
|
|
4037
|
+
chunks: search.chunks
|
|
4038
|
+
});
|
|
4039
|
+
return streamFromText(`data: ${payload}
|
|
4040
|
+
|
|
4041
|
+
`);
|
|
4042
|
+
}
|
|
4043
|
+
return {
|
|
4044
|
+
id: "mock-ai-search-chat",
|
|
4045
|
+
object: "chat.completion",
|
|
4046
|
+
model: params.model ?? "mock-ai-search",
|
|
4047
|
+
choices: [
|
|
4048
|
+
{
|
|
4049
|
+
index: 0,
|
|
4050
|
+
message: {
|
|
4051
|
+
role: "assistant",
|
|
4052
|
+
content
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
],
|
|
4056
|
+
chunks: search.chunks
|
|
4057
|
+
};
|
|
4058
|
+
},
|
|
4059
|
+
async update(config) {
|
|
4060
|
+
info = {
|
|
4061
|
+
...info,
|
|
4062
|
+
...config,
|
|
4063
|
+
modified_at: MOCK_TIMESTAMP
|
|
4064
|
+
};
|
|
4065
|
+
return cloneInfo(info);
|
|
4066
|
+
},
|
|
4067
|
+
async info() {
|
|
4068
|
+
return cloneInfo(info);
|
|
4069
|
+
},
|
|
4070
|
+
async stats() {
|
|
4071
|
+
const stats = {
|
|
4072
|
+
queued: 0,
|
|
4073
|
+
running: 0,
|
|
4074
|
+
completed: 0,
|
|
4075
|
+
error: 0,
|
|
4076
|
+
skipped: 0,
|
|
4077
|
+
outdated: 0,
|
|
4078
|
+
last_activity: MOCK_TIMESTAMP,
|
|
4079
|
+
engine: {
|
|
4080
|
+
vectorize: {
|
|
4081
|
+
vectorsCount: items.size,
|
|
4082
|
+
dimensions: 0
|
|
4083
|
+
},
|
|
4084
|
+
r2: {
|
|
4085
|
+
payloadSizeBytes: Array.from(items.values()).reduce((sum, item) => sum + item.info.file_size, 0),
|
|
4086
|
+
metadataSizeBytes: 0,
|
|
4087
|
+
objectCount: items.size
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
};
|
|
4091
|
+
for (const item of items.values()) {
|
|
4092
|
+
stats[item.info.status] = (stats[item.info.status] ?? 0) + 1;
|
|
4093
|
+
}
|
|
4094
|
+
return stats;
|
|
4095
|
+
},
|
|
4096
|
+
get items() {
|
|
4097
|
+
return itemsApi;
|
|
4098
|
+
},
|
|
4099
|
+
get jobs() {
|
|
4100
|
+
return jobsApi;
|
|
4101
|
+
},
|
|
4102
|
+
_getSearches() {
|
|
4103
|
+
return [...searches];
|
|
4104
|
+
},
|
|
4105
|
+
_getItems() {
|
|
4106
|
+
return Array.from(items.values()).map((item) => cloneInfo(item.info));
|
|
4107
|
+
},
|
|
4108
|
+
_getJobs() {
|
|
4109
|
+
return Array.from(jobs.values()).map((job) => cloneInfo(job));
|
|
4110
|
+
}
|
|
4111
|
+
};
|
|
4112
|
+
}
|
|
4113
|
+
function createMockAISearchNamespace(options = {}) {
|
|
4114
|
+
const namespaceName = options.namespace ?? "default";
|
|
4115
|
+
const instances = new Map;
|
|
4116
|
+
for (const [name, instanceOptions] of Object.entries(options.instances ?? {})) {
|
|
4117
|
+
instances.set(name, isAISearchInstance(instanceOptions) ? instanceOptions : createMockAISearchInstance({
|
|
4118
|
+
id: name,
|
|
4119
|
+
namespace: namespaceName,
|
|
4120
|
+
...instanceOptions
|
|
4121
|
+
}));
|
|
4122
|
+
}
|
|
4123
|
+
const getInstance = (name) => {
|
|
4124
|
+
const instance = instances.get(name);
|
|
4125
|
+
if (!instance) {
|
|
4126
|
+
throw new Error(`Mock AI Search namespace has no instance named "${name}".`);
|
|
4127
|
+
}
|
|
4128
|
+
return instance;
|
|
4129
|
+
};
|
|
4130
|
+
return {
|
|
4131
|
+
get(name) {
|
|
4132
|
+
return getInstance(name);
|
|
4133
|
+
},
|
|
4134
|
+
async list(params) {
|
|
4135
|
+
let result = await Promise.all(Array.from(instances.entries()).map(async ([name, instance]) => ({
|
|
4136
|
+
...await instance.info(),
|
|
4137
|
+
namespace: namespaceName,
|
|
4138
|
+
id: name
|
|
4139
|
+
})));
|
|
4140
|
+
if (params?.search) {
|
|
4141
|
+
const search = params.search.toLowerCase();
|
|
4142
|
+
result = result.filter((instance) => instance.id.toLowerCase().includes(search));
|
|
4143
|
+
}
|
|
4144
|
+
const page = params?.page ?? 1;
|
|
4145
|
+
const perPage = (params?.per_page ?? result.length) || 50;
|
|
4146
|
+
const start = (page - 1) * perPage;
|
|
4147
|
+
const paged = result.slice(start, start + perPage);
|
|
4148
|
+
return {
|
|
4149
|
+
result: paged,
|
|
4150
|
+
result_info: {
|
|
4151
|
+
count: paged.length,
|
|
4152
|
+
page,
|
|
4153
|
+
per_page: perPage,
|
|
4154
|
+
total_count: result.length
|
|
4155
|
+
}
|
|
4156
|
+
};
|
|
4157
|
+
},
|
|
4158
|
+
async create(config) {
|
|
4159
|
+
const instance = createMockAISearchInstance({
|
|
4160
|
+
id: config.id,
|
|
4161
|
+
namespace: namespaceName,
|
|
4162
|
+
info: config
|
|
4163
|
+
});
|
|
4164
|
+
instances.set(config.id, instance);
|
|
4165
|
+
return instance;
|
|
4166
|
+
},
|
|
4167
|
+
async delete(name) {
|
|
4168
|
+
instances.delete(name);
|
|
4169
|
+
},
|
|
4170
|
+
async search(params) {
|
|
4171
|
+
const query = extractSearchQuery(params);
|
|
4172
|
+
const chunks = [];
|
|
4173
|
+
const errors = [];
|
|
4174
|
+
for (const instanceId of params.ai_search_options.instance_ids) {
|
|
4175
|
+
const instance = instances.get(instanceId);
|
|
4176
|
+
if (!instance) {
|
|
4177
|
+
errors.push({
|
|
4178
|
+
instance_id: instanceId,
|
|
4179
|
+
message: `Mock AI Search namespace has no instance named "${instanceId}".`
|
|
4180
|
+
});
|
|
4181
|
+
continue;
|
|
4182
|
+
}
|
|
4183
|
+
const response = "query" in params ? await instance.search({ query, ai_search_options: params.ai_search_options }) : await instance.search({
|
|
4184
|
+
messages: params.messages,
|
|
4185
|
+
ai_search_options: params.ai_search_options
|
|
4186
|
+
});
|
|
4187
|
+
chunks.push(...response.chunks.map((chunk) => ({
|
|
4188
|
+
...chunk,
|
|
4189
|
+
instance_id: instanceId
|
|
4190
|
+
})));
|
|
4191
|
+
}
|
|
4192
|
+
return {
|
|
4193
|
+
search_query: query,
|
|
4194
|
+
chunks,
|
|
4195
|
+
...errors.length > 0 && { errors }
|
|
4196
|
+
};
|
|
4197
|
+
},
|
|
4198
|
+
async chatCompletions(params) {
|
|
4199
|
+
const search = await this.search({
|
|
4200
|
+
messages: params.messages,
|
|
4201
|
+
ai_search_options: params.ai_search_options
|
|
4202
|
+
});
|
|
4203
|
+
const content = search.chunks.map((chunk) => chunk.text).join(`
|
|
4204
|
+
`) || "No matching offline AI Search chunks.";
|
|
4205
|
+
if (params.stream) {
|
|
4206
|
+
return streamFromText(`data: ${JSON.stringify({ chunks: search.chunks })}
|
|
4207
|
+
|
|
4208
|
+
`);
|
|
4209
|
+
}
|
|
4210
|
+
return {
|
|
4211
|
+
id: "mock-ai-search-namespace-chat",
|
|
4212
|
+
object: "chat.completion",
|
|
4213
|
+
model: params.model ?? "mock-ai-search",
|
|
4214
|
+
choices: [
|
|
4215
|
+
{
|
|
4216
|
+
index: 0,
|
|
4217
|
+
message: {
|
|
4218
|
+
role: "assistant",
|
|
4219
|
+
content
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
4222
|
+
],
|
|
4223
|
+
chunks: search.chunks,
|
|
4224
|
+
errors: search.errors
|
|
4225
|
+
};
|
|
4226
|
+
},
|
|
4227
|
+
_getInstances() {
|
|
4228
|
+
return Array.from(instances.keys());
|
|
4229
|
+
}
|
|
4230
|
+
};
|
|
4231
|
+
}
|
|
4232
|
+
|
|
4233
|
+
// src/test/offline-bindings.ts
|
|
4234
|
+
var SUPPORT_MATRIX = {
|
|
4235
|
+
rateLimits: {
|
|
4236
|
+
service: "rateLimits",
|
|
4237
|
+
tier: "offline-native",
|
|
4238
|
+
reason: "Miniflare and devflare/test can simulate fixed-window RateLimit bindings locally.",
|
|
4239
|
+
recommendation: "Use createOfflineEnv() or createMockRateLimit() for pure tests; use createTestContext() for Miniflare-backed tests."
|
|
4240
|
+
},
|
|
4241
|
+
versionMetadata: {
|
|
4242
|
+
service: "versionMetadata",
|
|
4243
|
+
tier: "offline-native",
|
|
4244
|
+
reason: "Version metadata can be deterministic in tests without Cloudflare state.",
|
|
4245
|
+
recommendation: "Use createOfflineEnv() or createMockVersionMetadata() when asserting version-aware code."
|
|
4246
|
+
},
|
|
4247
|
+
secretsStore: {
|
|
4248
|
+
service: "secretsStore",
|
|
4249
|
+
tier: "offline-native",
|
|
4250
|
+
reason: "Secrets Store binding shape is local-testable with local secret-store values or fixed fixtures.",
|
|
4251
|
+
recommendation: "Use devflare secrets --local for dev/test values, or pass fixtures.secretsStore values for pure tests."
|
|
4252
|
+
},
|
|
4253
|
+
hyperdrive: {
|
|
4254
|
+
service: "hyperdrive",
|
|
4255
|
+
tier: "offline-native",
|
|
4256
|
+
reason: "Hyperdrive can run locally through Miniflare when Devflare has a local connection string or test fixture for the target database.",
|
|
4257
|
+
recommendation: "Use bindings.hyperdrive.*.localConnectionString, CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING>, or fixtures.hyperdrive for offline tests."
|
|
4258
|
+
},
|
|
4259
|
+
workerLoaders: {
|
|
4260
|
+
service: "workerLoaders",
|
|
4261
|
+
tier: "offline-native",
|
|
4262
|
+
reason: "Worker Loader bindings run locally through Miniflare and can use explicit Worker stubs for pure tests.",
|
|
4263
|
+
recommendation: "Use createTestContext() for local WorkerLoader execution; pass fixtures.workerLoaders with a WorkerStub for pure tests."
|
|
4264
|
+
},
|
|
4265
|
+
mtlsCertificates: {
|
|
4266
|
+
service: "mtlsCertificates",
|
|
4267
|
+
tier: "offline-fixture",
|
|
4268
|
+
reason: "Fetcher call paths can be tested locally, but real certificate presentation is Cloudflare/Wrangler remote behavior.",
|
|
4269
|
+
recommendation: "Pass fixtures.mtlsCertificates handlers for unit tests; use remote/deployed tests for certificate presentation."
|
|
4270
|
+
},
|
|
4271
|
+
dispatchNamespaces: {
|
|
4272
|
+
service: "dispatchNamespaces",
|
|
4273
|
+
tier: "offline-fixture",
|
|
4274
|
+
reason: "Tenant dispatch can be backed by explicit test fetchers, but namespace uploads and lifecycle are Cloudflare-managed.",
|
|
4275
|
+
recommendation: "Pass fixtures.dispatchNamespaces workers for deterministic tenant routing tests."
|
|
4276
|
+
},
|
|
4277
|
+
workflows: {
|
|
4278
|
+
service: "workflows",
|
|
4279
|
+
tier: "offline-native",
|
|
4280
|
+
reason: "Workflow binding calls can run through Miniflare or a deterministic local Workflow mock for app-level tests.",
|
|
4281
|
+
recommendation: "Use createOfflineEnv() for pure tests and createTestContext() when Miniflare execution semantics matter."
|
|
4282
|
+
},
|
|
4283
|
+
pipelines: {
|
|
4284
|
+
service: "pipelines",
|
|
4285
|
+
tier: "offline-native",
|
|
4286
|
+
reason: "Pipeline sends can be recorded locally; Cloudflare owns production batching and sinks.",
|
|
4287
|
+
recommendation: "Use createOfflineEnv() or createMockPipeline() to assert records sent by application code."
|
|
4288
|
+
},
|
|
4289
|
+
images: {
|
|
4290
|
+
service: "images",
|
|
4291
|
+
tier: "offline-native",
|
|
4292
|
+
reason: "Images has local development support and Devflare provides a low-fidelity deterministic pure mock.",
|
|
4293
|
+
recommendation: "Use createOfflineEnv() for chain-shape tests; use Cloudflare for hosted image APIs and transform fidelity."
|
|
4294
|
+
},
|
|
4295
|
+
media: {
|
|
4296
|
+
service: "media",
|
|
4297
|
+
tier: "offline-native",
|
|
4298
|
+
reason: "Media Transformations can run through Miniflare wiring locally, and Devflare provides a deterministic pure mock for app-level chain tests.",
|
|
4299
|
+
recommendation: "Use createTestContext() for local Worker binding tests and createMockMediaBinding() for pure tests; use Cloudflare for codec/output fidelity."
|
|
4300
|
+
},
|
|
4301
|
+
artifacts: {
|
|
4302
|
+
service: "artifacts",
|
|
4303
|
+
tier: "offline-fixture",
|
|
4304
|
+
reason: "Artifacts repo metadata and token flows can be modeled in memory; real Git remotes are Cloudflare-managed.",
|
|
4305
|
+
recommendation: "Use createMockArtifacts() for unit tests; use Cloudflare for Git protocol and namespace access."
|
|
4306
|
+
},
|
|
4307
|
+
aiSearch: {
|
|
4308
|
+
service: "aiSearch",
|
|
4309
|
+
tier: "offline-fixture",
|
|
4310
|
+
reason: "AI Search application flows can use deterministic in-memory instances and namespaces, but indexing/ranking/crawling are hosted Cloudflare behavior.",
|
|
4311
|
+
recommendation: "Use createMockAISearchInstance(), createMockAISearchNamespace(), or createOfflineEnv(); use remote tests for real relevance behavior."
|
|
4312
|
+
},
|
|
4313
|
+
aiSearchNamespaces: {
|
|
4314
|
+
service: "aiSearchNamespaces",
|
|
4315
|
+
tier: "offline-fixture",
|
|
4316
|
+
reason: "AI Search namespace management can be backed by an explicit in-memory instance registry for tests.",
|
|
4317
|
+
recommendation: "Use fixtures.aiSearchNamespaces to model tenant instances and multi-instance searches."
|
|
4318
|
+
},
|
|
4319
|
+
containers: {
|
|
4320
|
+
service: "containers",
|
|
4321
|
+
tier: "offline-native",
|
|
4322
|
+
reason: "Devflare can launch local Docker/Podman containers in explicit tests when an engine and cached images are available.",
|
|
4323
|
+
recommendation: "Use devflare/test containers helpers and shouldSkip.containers; keep ordinary unit tests engine-free."
|
|
4324
|
+
},
|
|
4325
|
+
browser: {
|
|
4326
|
+
service: "browser",
|
|
4327
|
+
tier: "offline-native",
|
|
4328
|
+
reason: "Cloudflare lists Browser Run as locally simulatable, while live view, HITL, recordings, and external CDP remain hosted features.",
|
|
4329
|
+
recommendation: "Use Cloudflare/Wrangler Browser local development for browser execution and remote/deployed tests for hosted-only features."
|
|
4330
|
+
},
|
|
4331
|
+
ai: {
|
|
4332
|
+
service: "ai",
|
|
4333
|
+
tier: "remote-boundary",
|
|
4334
|
+
reason: "Workers AI inference has no local simulation in Cloudflare local development.",
|
|
4335
|
+
recommendation: "Use DEVFLARE_REMOTE=1/devflare remote enable for real calls, or inject a custom fake for pure tests."
|
|
4336
|
+
},
|
|
4337
|
+
aiGateway: {
|
|
4338
|
+
service: "aiGateway",
|
|
4339
|
+
tier: "remote-boundary",
|
|
4340
|
+
reason: "AI Gateway routing and logs are Cloudflare account resources reached through the Workers AI binding.",
|
|
4341
|
+
recommendation: "Use remote-mode AI Gateway helpers for integration tests and custom fakes for offline unit tests."
|
|
4342
|
+
},
|
|
4343
|
+
vectorize: {
|
|
4344
|
+
service: "vectorize",
|
|
4345
|
+
tier: "remote-boundary",
|
|
4346
|
+
reason: "Cloudflare lists Vectorize with no local simulation.",
|
|
4347
|
+
recommendation: "Use DEVFLARE_REMOTE=1/devflare remote enable for real indexes, or inject a fake Vectorize binding for pure tests."
|
|
4348
|
+
},
|
|
4349
|
+
builds: {
|
|
4350
|
+
service: "builds",
|
|
4351
|
+
tier: "remote-boundary",
|
|
4352
|
+
reason: "Cloudflare Builds/Git-connected Workers are CI/CD orchestration, not a Worker runtime binding.",
|
|
4353
|
+
recommendation: "Run Devflare commands inside your CI; validate Cloudflare build integration with Cloudflare/Wrangler tests."
|
|
4354
|
+
}
|
|
4355
|
+
};
|
|
4356
|
+
function copySupport(entry) {
|
|
4357
|
+
return { ...entry };
|
|
4358
|
+
}
|
|
4359
|
+
function getOfflineSupportMatrix() {
|
|
4360
|
+
return Object.fromEntries(Object.entries(SUPPORT_MATRIX).map(([service, entry]) => [service, copySupport(entry)]));
|
|
4361
|
+
}
|
|
4362
|
+
function describeOfflineSupport(service) {
|
|
4363
|
+
const entry = SUPPORT_MATRIX[service];
|
|
4364
|
+
if (entry) {
|
|
4365
|
+
return copySupport(entry);
|
|
4366
|
+
}
|
|
4367
|
+
return {
|
|
4368
|
+
service,
|
|
4369
|
+
tier: "remote-boundary",
|
|
4370
|
+
reason: `No offline support classification exists for "${service}".`,
|
|
4371
|
+
recommendation: "Treat this as a remote Cloudflare boundary until Devflare documents a local simulator or fixture."
|
|
4372
|
+
};
|
|
4373
|
+
}
|
|
4374
|
+
function createMissingSecret(binding) {
|
|
4375
|
+
return {
|
|
4376
|
+
async get() {
|
|
4377
|
+
throw new Error(`Offline Secrets Store binding "${binding}" has no value. Pass fixtures.secretsStore.${binding} or write a local secret with devflare secrets --local.`);
|
|
4378
|
+
}
|
|
4379
|
+
};
|
|
4380
|
+
}
|
|
4381
|
+
function isWorkflowBinding(value) {
|
|
4382
|
+
return typeof value.create === "function";
|
|
4383
|
+
}
|
|
4384
|
+
function isPipelineBinding(value) {
|
|
4385
|
+
return typeof value?.send === "function";
|
|
4386
|
+
}
|
|
4387
|
+
function isImagesBinding(value) {
|
|
4388
|
+
return typeof value?.input === "function";
|
|
4389
|
+
}
|
|
4390
|
+
function isHyperdriveBinding(value) {
|
|
4391
|
+
return typeof value?.connectionString === "string";
|
|
4392
|
+
}
|
|
4393
|
+
function isMediaBinding(value) {
|
|
4394
|
+
return typeof value?.input === "function";
|
|
4395
|
+
}
|
|
4396
|
+
function isArtifactsBinding2(value) {
|
|
4397
|
+
return typeof value?.create === "function";
|
|
4398
|
+
}
|
|
4399
|
+
function isAISearchInstance2(value) {
|
|
4400
|
+
return typeof value?.search === "function";
|
|
4401
|
+
}
|
|
4402
|
+
function isAISearchNamespace(value) {
|
|
4403
|
+
return typeof value?.get === "function";
|
|
4404
|
+
}
|
|
4405
|
+
function addBoundary(remoteBoundaries, service, binding, reason) {
|
|
4406
|
+
remoteBoundaries.push({
|
|
4407
|
+
service,
|
|
4408
|
+
binding,
|
|
4409
|
+
reason,
|
|
4410
|
+
recommendation: describeOfflineSupport(service).recommendation
|
|
4411
|
+
});
|
|
4412
|
+
}
|
|
4413
|
+
function addStaticBindings(env3, config) {
|
|
4414
|
+
if (config.vars) {
|
|
4415
|
+
Object.assign(env3, config.vars);
|
|
4416
|
+
}
|
|
4417
|
+
}
|
|
4418
|
+
function addRateLimitBindings(env3, bindings) {
|
|
4419
|
+
for (const [name, binding] of Object.entries(bindings?.rateLimits ?? {})) {
|
|
4420
|
+
env3[name] = createMockRateLimit(binding.simple);
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
function addVersionMetadataBinding(env3, bindings) {
|
|
4424
|
+
if (bindings?.versionMetadata) {
|
|
4425
|
+
env3[bindings.versionMetadata.binding] = createMockVersionMetadata();
|
|
4426
|
+
}
|
|
4427
|
+
}
|
|
4428
|
+
function getHyperdriveConnectionString(name, binding, fixture) {
|
|
4429
|
+
if (typeof fixture === "string") {
|
|
4430
|
+
return fixture;
|
|
4431
|
+
}
|
|
4432
|
+
const envValue = process.env[`CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_${name}`] ?? process.env[`WRANGLER_HYPERDRIVE_LOCAL_CONNECTION_STRING_${name}`];
|
|
4433
|
+
if (envValue?.trim()) {
|
|
4434
|
+
return envValue;
|
|
4435
|
+
}
|
|
4436
|
+
return normalizeHyperdriveBinding(binding).localConnectionString;
|
|
4437
|
+
}
|
|
4438
|
+
function addHyperdriveBindings(env3, bindings, fixtures, missingFixtures) {
|
|
4439
|
+
for (const [name, binding] of Object.entries(bindings?.hyperdrive ?? {})) {
|
|
4440
|
+
const fixture = fixtures.hyperdrive?.[name];
|
|
4441
|
+
if (isHyperdriveBinding(fixture)) {
|
|
4442
|
+
env3[name] = fixture;
|
|
4443
|
+
continue;
|
|
4444
|
+
}
|
|
4445
|
+
const connectionString = getHyperdriveConnectionString(name, binding, fixture);
|
|
4446
|
+
if (connectionString) {
|
|
4447
|
+
env3[name] = createMockHyperdrive(connectionString);
|
|
4448
|
+
continue;
|
|
4449
|
+
}
|
|
4450
|
+
missingFixtures.push({
|
|
4451
|
+
service: "hyperdrive",
|
|
4452
|
+
binding: name,
|
|
4453
|
+
reason: `Hyperdrive binding "${name}" has no local connection string. Configure bindings.hyperdrive.${name}.localConnectionString or pass fixtures.hyperdrive.${name}.`
|
|
4454
|
+
});
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
function addWorkerLoaderBindings(env3, bindings, fixtures) {
|
|
4458
|
+
for (const name of Object.keys(bindings?.workerLoaders ?? {})) {
|
|
4459
|
+
env3[name] = createMockWorkerLoader(fixtures.workerLoaders?.[name]);
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
function addMTLSCertificateBindings(env3, bindings, fixtures) {
|
|
4463
|
+
for (const name of Object.keys(bindings?.mtlsCertificates ?? {})) {
|
|
4464
|
+
env3[name] = createMockMTLSCertificate(fixtures.mtlsCertificates?.[name]);
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
4467
|
+
function addDispatchNamespaceBindings(env3, bindings, fixtures) {
|
|
4468
|
+
for (const name of Object.keys(bindings?.dispatchNamespaces ?? {})) {
|
|
4469
|
+
env3[name] = createMockDispatchNamespace(fixtures.dispatchNamespaces?.[name]);
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
function addWorkflowBindings(env3, bindings, fixtures) {
|
|
4473
|
+
for (const name of Object.keys(bindings?.workflows ?? {})) {
|
|
4474
|
+
const fixture = fixtures.workflows?.[name];
|
|
4475
|
+
env3[name] = fixture && isWorkflowBinding(fixture) ? fixture : createMockWorkflow(fixture);
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4478
|
+
function addPipelineBindings(env3, bindings, fixtures) {
|
|
4479
|
+
for (const name of Object.keys(bindings?.pipelines ?? {})) {
|
|
4480
|
+
const fixture = fixtures.pipelines?.[name];
|
|
4481
|
+
env3[name] = isPipelineBinding(fixture) ? fixture : createMockPipeline();
|
|
4482
|
+
}
|
|
4483
|
+
}
|
|
4484
|
+
function addImagesBindings(env3, bindings, fixtures) {
|
|
4485
|
+
for (const name of Object.keys(bindings?.images ?? {})) {
|
|
4486
|
+
const fixture = fixtures.images?.[name];
|
|
4487
|
+
env3[name] = isImagesBinding(fixture) ? fixture : createMockImagesBinding(fixture);
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
function addMediaBindings(env3, bindings, fixtures) {
|
|
4491
|
+
for (const name of Object.keys(bindings?.media ?? {})) {
|
|
4492
|
+
const fixture = fixtures.media?.[name];
|
|
4493
|
+
env3[name] = isMediaBinding(fixture) ? fixture : createMockMediaBinding(fixture);
|
|
4494
|
+
}
|
|
4495
|
+
}
|
|
4496
|
+
function addArtifactsBindings(env3, bindings, fixtures) {
|
|
4497
|
+
for (const name of Object.keys(bindings?.artifacts ?? {})) {
|
|
4498
|
+
const fixture = fixtures.artifacts?.[name];
|
|
4499
|
+
env3[name] = isArtifactsBinding2(fixture) ? fixture : createMockArtifacts(fixture);
|
|
4500
|
+
}
|
|
4501
|
+
}
|
|
4502
|
+
function addSecretsStoreBindings(env3, bindings, fixtures, localSecretValues, missingFixtures) {
|
|
4503
|
+
for (const name of Object.keys(bindings?.secretsStore ?? {})) {
|
|
4504
|
+
const value = fixtures.secretsStore?.[name] ?? localSecretValues[name];
|
|
4505
|
+
if (value === undefined) {
|
|
4506
|
+
missingFixtures.push({
|
|
4507
|
+
service: "secretsStore",
|
|
4508
|
+
binding: name,
|
|
4509
|
+
reason: `Secrets Store values are not present in fixtures or the local secret store; pass fixtures.secretsStore.${name} or run devflare secrets --local.`
|
|
4510
|
+
});
|
|
4511
|
+
env3[name] = createMissingSecret(name);
|
|
4512
|
+
} else {
|
|
4513
|
+
env3[name] = createMockSecretsStoreSecret(value);
|
|
4514
|
+
}
|
|
4515
|
+
}
|
|
4516
|
+
}
|
|
4517
|
+
function addAISearchBindings(env3, bindings, fixtures) {
|
|
4518
|
+
for (const [name, binding] of Object.entries(bindings?.aiSearch ?? {})) {
|
|
4519
|
+
const fixture = fixtures.aiSearch?.[name];
|
|
4520
|
+
env3[name] = isAISearchInstance2(fixture) ? fixture : createMockAISearchInstance({
|
|
4521
|
+
id: binding.instanceName,
|
|
4522
|
+
...fixture
|
|
4523
|
+
});
|
|
4524
|
+
}
|
|
4525
|
+
}
|
|
4526
|
+
function addAISearchNamespaceBindings(env3, bindings, fixtures) {
|
|
4527
|
+
for (const [name, binding] of Object.entries(bindings?.aiSearchNamespaces ?? {})) {
|
|
4528
|
+
const fixture = fixtures.aiSearchNamespaces?.[name];
|
|
4529
|
+
env3[name] = isAISearchNamespace(fixture) ? fixture : createMockAISearchNamespace({
|
|
4530
|
+
namespace: binding.namespace,
|
|
4531
|
+
...fixture
|
|
4532
|
+
});
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
4535
|
+
function addRemoteBoundaries(remoteBoundaries, bindings) {
|
|
4536
|
+
if (bindings?.ai) {
|
|
4537
|
+
addBoundary(remoteBoundaries, "ai", bindings.ai.binding || "AI", "Workers AI inference is not available in offline local simulations.");
|
|
4538
|
+
}
|
|
4539
|
+
for (const name of Object.keys(bindings?.vectorize ?? {})) {
|
|
4540
|
+
addBoundary(remoteBoundaries, "vectorize", name, "Vectorize has no offline local simulation in Cloudflare local development.");
|
|
4541
|
+
}
|
|
4542
|
+
}
|
|
4543
|
+
function createOfflineBindings(config, fixtures = {}, options = {}) {
|
|
4544
|
+
const env3 = {};
|
|
4545
|
+
const remoteBoundaries = [];
|
|
4546
|
+
const missingFixtures = [];
|
|
4547
|
+
const bindings = config.bindings;
|
|
4548
|
+
const localSecretValues = options.cwd && options.useLocalSecrets !== false ? resolveLocalSecretValuesForBindings(config, options.cwd) : {};
|
|
4549
|
+
addStaticBindings(env3, config);
|
|
4550
|
+
addRateLimitBindings(env3, bindings);
|
|
4551
|
+
addVersionMetadataBinding(env3, bindings);
|
|
4552
|
+
addHyperdriveBindings(env3, bindings, fixtures, missingFixtures);
|
|
4553
|
+
addWorkerLoaderBindings(env3, bindings, fixtures);
|
|
4554
|
+
addMTLSCertificateBindings(env3, bindings, fixtures);
|
|
4555
|
+
addDispatchNamespaceBindings(env3, bindings, fixtures);
|
|
4556
|
+
addWorkflowBindings(env3, bindings, fixtures);
|
|
4557
|
+
addPipelineBindings(env3, bindings, fixtures);
|
|
4558
|
+
addImagesBindings(env3, bindings, fixtures);
|
|
4559
|
+
addMediaBindings(env3, bindings, fixtures);
|
|
4560
|
+
addArtifactsBindings(env3, bindings, fixtures);
|
|
4561
|
+
addSecretsStoreBindings(env3, bindings, fixtures, localSecretValues, missingFixtures);
|
|
4562
|
+
addAISearchBindings(env3, bindings, fixtures);
|
|
4563
|
+
addAISearchNamespaceBindings(env3, bindings, fixtures);
|
|
4564
|
+
addRemoteBoundaries(remoteBoundaries, bindings);
|
|
4565
|
+
if (fixtures.custom) {
|
|
4566
|
+
Object.assign(env3, fixtures.custom);
|
|
4567
|
+
}
|
|
4568
|
+
return {
|
|
4569
|
+
env: env3,
|
|
4570
|
+
support: getOfflineSupportMatrix(),
|
|
4571
|
+
remoteBoundaries,
|
|
4572
|
+
missingFixtures
|
|
4573
|
+
};
|
|
4574
|
+
}
|
|
4575
|
+
function createOfflineEnv(config, fixtures = {}, options = {}) {
|
|
4576
|
+
return createOfflineBindings(config, fixtures, options).env;
|
|
4577
|
+
}
|
|
42
4578
|
export {
|
|
43
4579
|
worker,
|
|
44
4580
|
withTestContext,
|
|
45
|
-
testEnv,
|
|
46
4581
|
tail,
|
|
47
|
-
|
|
4582
|
+
stopActiveContainers,
|
|
48
4583
|
shouldSkip,
|
|
49
4584
|
scheduled,
|
|
50
4585
|
resolveServiceBindings,
|
|
51
4586
|
resolveDOBindings,
|
|
52
|
-
queue,
|
|
53
|
-
isRemoteModeEnabled,
|
|
4587
|
+
queue2 as queue,
|
|
54
4588
|
hasServiceBindings,
|
|
55
4589
|
hasCrossWorkerDOs,
|
|
56
|
-
|
|
4590
|
+
getOfflineSupportMatrix,
|
|
4591
|
+
getContainerSkipReason,
|
|
57
4592
|
env,
|
|
58
4593
|
email,
|
|
4594
|
+
detectContainerEngine,
|
|
4595
|
+
describeOfflineSupport,
|
|
59
4596
|
createTestContext,
|
|
60
|
-
|
|
4597
|
+
createOfflineEnv,
|
|
4598
|
+
createOfflineBindings,
|
|
4599
|
+
createMockWorkflow,
|
|
4600
|
+
createMockWorkerLoader,
|
|
4601
|
+
createMockVersionMetadata,
|
|
61
4602
|
createMockTestContext,
|
|
4603
|
+
createMockSecretsStoreSecret,
|
|
4604
|
+
createMockRateLimit,
|
|
62
4605
|
createMockR2,
|
|
63
4606
|
createMockQueue,
|
|
4607
|
+
createMockPipeline,
|
|
4608
|
+
createMockMediaBinding,
|
|
4609
|
+
createMockMTLSCertificate,
|
|
64
4610
|
createMockKV,
|
|
4611
|
+
createMockImagesBinding,
|
|
4612
|
+
createMockHyperdrive,
|
|
65
4613
|
createMockEnv,
|
|
4614
|
+
createMockDispatchNamespace,
|
|
66
4615
|
createMockD1,
|
|
67
|
-
|
|
68
|
-
|
|
4616
|
+
createMockArtifacts,
|
|
4617
|
+
createMockAISearchNamespace,
|
|
4618
|
+
createMockAISearchInstance,
|
|
4619
|
+
createContainerManager,
|
|
4620
|
+
containers,
|
|
69
4621
|
clearBundleCache,
|
|
70
4622
|
cf
|
|
71
4623
|
};
|