devflare 1.0.0-next.15 → 1.0.0-next.16
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 +9360 -1819
- package/README.md +313 -39
- package/bin/devflare.js +17 -7
- package/dist/account-0w8wdzjv.js +475 -0
- package/dist/account-eygq6qx7.js +475 -0
- package/dist/account-fw8nafav.js +475 -0
- package/dist/account-pzq69nys.js +475 -0
- package/dist/account-s66jb15j.js +475 -0
- package/dist/api-d6ekexs5.js +25 -0
- package/dist/bridge/index.d.ts +1 -1
- package/dist/bridge/index.d.ts.map +1 -1
- 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 +0 -4
- package/dist/bridge/proxy.d.ts.map +1 -1
- package/dist/bridge/serialization.d.ts.map +1 -1
- package/dist/bridge/server.d.ts +1 -1
- package/dist/bridge/server.d.ts.map +1 -1
- package/dist/browser-shim/handler.d.ts +1 -1
- package/dist/browser-shim/handler.d.ts.map +1 -1
- package/dist/browser.d.ts +1648 -38
- package/dist/browser.d.ts.map +1 -1
- package/dist/build-1kmkwqgh.js +53 -0
- package/dist/build-506kjhcm.js +53 -0
- package/dist/build-66866ahs.js +53 -0
- package/dist/build-g1adm3ww.js +53 -0
- package/dist/build-p3r3117t.js +53 -0
- package/dist/bundler/do-bundler.d.ts.map +1 -1
- package/dist/bundler/rolldown-shared.d.ts +24 -0
- package/dist/bundler/rolldown-shared.d.ts.map +1 -0
- package/dist/bundler/worker-bundler.d.ts +0 -1
- package/dist/bundler/worker-bundler.d.ts.map +1 -1
- 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 +27 -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/deploy.d.ts.map +1 -1
- 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/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 +44 -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 +2 -1
- package/dist/cli/config-path.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/preview-bindings.d.ts +42 -0
- package/dist/cli/preview-bindings.d.ts.map +1 -0
- package/dist/cli/preview.d.ts +11 -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 +4 -0
- package/dist/cloudflare/api.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/kv-namespace.d.ts +3 -0
- package/dist/cloudflare/kv-namespace.d.ts.map +1 -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-records.d.ts +61 -0
- package/dist/cloudflare/preview-registry-records.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-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/registry-schema.d.ts +253 -0
- package/dist/cloudflare/registry-schema.d.ts.map +1 -0
- package/dist/cloudflare/tokens.d.ts +18 -0
- package/dist/cloudflare/tokens.d.ts.map +1 -0
- package/dist/cloudflare/types.d.ts +122 -5
- package/dist/cloudflare/types.d.ts.map +1 -1
- package/dist/cloudflare/usage.d.ts.map +1 -1
- package/dist/config/compiler.d.ts +4 -0
- package/dist/config/compiler.d.ts.map +1 -1
- 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 +4 -3
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/preview-resources.d.ts +77 -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 +0 -22
- package/dist/config/ref.d.ts.map +1 -1
- package/dist/config/resolve.d.ts +1 -0
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resource-resolution.d.ts +21 -5
- package/dist/config/resource-resolution.d.ts.map +1 -1
- package/dist/config/schema-bindings.d.ts +693 -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 +1341 -0
- package/dist/config/schema-env.d.ts.map +1 -0
- package/dist/config/schema-normalization.d.ts +64 -0
- package/dist/config/schema-normalization.d.ts.map +1 -0
- package/dist/config/schema-runtime.d.ts +230 -0
- package/dist/config/schema-runtime.d.ts.map +1 -0
- package/dist/config/schema.d.ts +542 -3736
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config-entry.d.ts +5 -0
- package/dist/config-entry.d.ts.map +1 -0
- package/dist/{config-v9tr4rts.js → config-fjwke42y.js} +6 -4
- package/dist/config-hwdqjse7.js +59 -0
- package/dist/config-pxvewrhv.js +59 -0
- package/dist/config-q0g5qdga.js +59 -0
- package/dist/decorators/durable-object.d.ts.map +1 -1
- package/dist/deploy-7nmzc9r8.js +609 -0
- package/dist/deploy-csfhdr64.js +691 -0
- package/dist/deploy-ex4g5avz.js +621 -0
- package/dist/deploy-jnb0bhka.js +609 -0
- package/dist/deploy-tp0g6qdp.js +609 -0
- package/dist/deploy-ykpcjkc2.js +690 -0
- package/dist/{dev-ymtphbkg.js → dev-2pd33m28.js} +386 -348
- package/dist/dev-7ef5e2j1.js +2409 -0
- package/dist/dev-8nssqatr.js +2409 -0
- package/dist/dev-grznx8fn.js +2409 -0
- package/dist/dev-server/d1-migrations.d.ts +14 -0
- package/dist/dev-server/d1-migrations.d.ts.map +1 -0
- package/dist/dev-server/gateway-script.d.ts +8 -0
- package/dist/dev-server/gateway-script.d.ts.map +1 -0
- package/dist/dev-server/runtime-stdio.d.ts.map +1 -1
- package/dist/dev-server/server.d.ts.map +1 -1
- package/dist/dev-server/vite-process.d.ts +14 -0
- package/dist/dev-server/vite-process.d.ts.map +1 -0
- package/dist/dev-server/vite-utils.d.ts +1 -1
- package/dist/dev-server/vite-utils.d.ts.map +1 -1
- package/dist/dev-server/worker-source-watcher.d.ts +11 -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/{doctor-xv4gm1h4.js → doctor-04ammrrh.js} +67 -32
- package/dist/doctor-fmjj65mc.js +245 -0
- package/dist/doctor-fzkznce1.js +245 -0
- package/dist/doctor-sa5xv1bz.js +245 -0
- package/dist/index-091sh1ma.js +1229 -0
- package/dist/index-0apbm26n.js +788 -0
- package/dist/index-0eqksag4.js +418 -0
- package/dist/{index-k8vh558d.js → index-0kfzdywd.js} +15 -2
- package/dist/index-0w826dsr.js +379 -0
- package/dist/{index-5s1bz1e0.js → index-11m5a8wd.js} +100 -22
- package/dist/{index-0rsa2c1t.js → index-1sp39f2f.js} +110 -57
- package/dist/index-2jnrqbny.js +1301 -0
- package/dist/index-2pb7b9mw.js +378 -0
- package/dist/{index-3a4mmn57.js → index-2x53aqjm.js} +1065 -890
- package/dist/index-3ke5d2vn.js +1229 -0
- package/dist/index-43dq8yx8.js +788 -0
- package/dist/index-4rrttqj5.js +378 -0
- package/dist/index-4v9bc2pc.js +1367 -0
- package/dist/index-61jsjnsv.js +280 -0
- package/dist/index-6jef5emv.js +176 -0
- package/dist/index-6psz1h4c.js +788 -0
- package/dist/index-72mve6vh.js +168 -0
- package/dist/{index-zvgc3e0c.js → index-74198nxd.js} +159 -63
- package/dist/{index-7bq4xq84.js → index-7g8zyws4.js} +7 -12
- package/dist/index-7kcxjhta.js +456 -0
- package/dist/index-7v583xan.js +418 -0
- package/dist/index-7x0ybbtx.js +133 -0
- package/dist/index-816krz9p.js +52 -0
- package/dist/index-82f1z98k.js +41 -0
- package/dist/index-8t5nb4qx.js +133 -0
- package/dist/index-9az6s7ad.js +52 -0
- package/dist/{index-59df49vn.js → index-9ba1etyz.js} +29 -51
- package/dist/{index-001mw014.js → index-9fbtk7gv.js} +134 -248
- package/dist/index-9n6djthj.js +490 -0
- package/dist/index-aabgympv.js +39 -0
- package/dist/index-b8m6883k.js +74 -0
- package/dist/{index-5yxg30va.js → index-cgbvmse6.js} +15 -6
- package/dist/index-d8etnfef.js +1229 -0
- package/dist/index-e9yw4d6y.js +133 -0
- package/dist/index-epw1jxz5.js +1204 -0
- package/dist/index-f85s8gj3.js +2649 -0
- package/dist/index-fe2ngvh7.js +1229 -0
- package/dist/index-fvsadj32.js +192 -0
- package/dist/index-gs4y9gdf.js +456 -0
- package/dist/{index-fef08w43.js → index-h18pxvzs.js} +7 -6
- package/dist/index-hfj1a2c4.js +2649 -0
- package/dist/{index-8gtqgb3q.js → index-hjy8ctpc.js} +14 -92
- package/dist/index-htzf0py1.js +1204 -0
- package/dist/index-j185x270.js +897 -0
- package/dist/index-jb75kwa4.js +519 -0
- package/dist/index-jwd8pcb2.js +897 -0
- package/dist/index-k29yjhv0.js +52 -0
- package/dist/index-k6vq6kkt.js +456 -0
- package/dist/{index-vky23txa.js → index-m3fmw6mx.js} +2 -2
- package/dist/index-maxpsfk8.js +402 -0
- package/dist/index-mbdmrner.js +402 -0
- package/dist/index-mea5bc45.js +418 -0
- package/dist/index-mqekt778.js +185 -0
- package/dist/index-na3mnm1k.js +74 -0
- package/dist/index-p03n4qet.js +1367 -0
- package/dist/index-p296ban8.js +191 -0
- package/dist/index-pnbs1b8k.js +280 -0
- package/dist/index-q4kaz181.js +1207 -0
- package/dist/index-ry131z23.js +378 -0
- package/dist/index-sgb7c8nm.js +402 -0
- package/dist/index-sqrksgb2.js +133 -0
- package/dist/index-stgn34cr.js +148 -0
- package/dist/{index-v8vvsn9x.js → index-t08te69w.js} +1 -18
- package/dist/index-thna1tkd.js +280 -0
- package/dist/index-v5nmqthy.js +74 -0
- package/dist/{index-n932ytmq.js → index-vt4yxkmf.js} +2 -2
- package/dist/index-wyq6c6yj.js +402 -0
- package/dist/index-wztc9stx.js +418 -0
- package/dist/index-x9cwdxw5.js +456 -0
- package/dist/index-xk9djfjp.js +519 -0
- package/dist/index-yc0gcchc.js +418 -0
- package/dist/index-yqbxjysa.js +897 -0
- package/dist/index-yzddwp02.js +788 -0
- package/dist/index-zfhq6s96.js +74 -0
- package/dist/index-zt22fe2j.js +54 -0
- package/dist/index-zyt5byt6.js +2649 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/{init-na2atvz2.js → init-r4hnxan3.js} +24 -17
- package/dist/login-2hnz4m4n.js +77 -0
- package/dist/login-5bsxxpvc.js +77 -0
- package/dist/login-6tzvczw2.js +77 -0
- package/dist/login-bhaw72zc.js +77 -0
- package/dist/login-x8tgckqm.js +77 -0
- package/dist/previews-3rn8mz2c.js +1168 -0
- package/dist/previews-d487qde5.js +1200 -0
- package/dist/previews-gm3z0syj.js +1168 -0
- package/dist/previews-j9ymq4ys.js +1169 -0
- package/dist/previews-q031mx34.js +1168 -0
- package/dist/productions-120xg0aq.js +505 -0
- package/dist/productions-5ev5qweg.js +505 -0
- package/dist/productions-me3tdvr9.js +505 -0
- package/dist/productions-p5rbgp2f.js +505 -0
- package/dist/productions-x9p0pym1.js +505 -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 +6 -267
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/exports.d.ts +3 -3
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/middleware.d.ts +8 -38
- package/dist/runtime/middleware.d.ts.map +1 -1
- package/dist/src/browser.js +9 -17
- package/dist/src/cli/index.js +3 -1
- package/dist/src/cloudflare/index.js +49 -3
- package/dist/src/config-entry.js +14 -0
- package/dist/src/index.js +24 -20
- package/dist/src/runtime/index.js +3 -9
- package/dist/src/sveltekit/index.js +10 -8
- package/dist/src/test/index.js +16 -19
- package/dist/src/vite/index.js +7 -5
- package/dist/sveltekit/platform.d.ts +1 -1
- package/dist/sveltekit/platform.d.ts.map +1 -1
- package/dist/test/cf.d.ts +10 -10
- package/dist/test/email.d.ts.map +1 -1
- package/dist/test/index.d.ts +1 -6
- package/dist/test/index.d.ts.map +1 -1
- 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.map +1 -1
- package/dist/test/scheduled.d.ts.map +1 -1
- package/dist/test/should-skip.d.ts +0 -18
- package/dist/test/should-skip.d.ts.map +1 -1
- 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-gateway-script.d.ts +2 -0
- package/dist/test/simple-context-gateway-script.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.d.ts +1 -23
- package/dist/test/simple-context.d.ts.map +1 -1
- package/dist/test/tail.d.ts.map +1 -1
- package/dist/test/worker.d.ts.map +1 -1
- package/dist/token-kedhcret.js +419 -0
- package/dist/token-m8jmnjwk.js +419 -0
- package/dist/{types-158m16vd.js → types-0sqwkp7x.js} +244 -140
- package/dist/types-1gwr2ex6.js +572 -0
- package/dist/types-6e5yx6km.js +572 -0
- package/dist/types-p0gckpn6.js +572 -0
- package/dist/utils/send-email.d.ts.map +1 -1
- package/dist/vite/config-file.d.ts.map +1 -1
- package/dist/vite/plugin.d.ts.map +1 -1
- package/dist/worker-0srh2jfr.js +513 -0
- package/dist/worker-4xrfd10a.js +513 -0
- package/dist/worker-entry/composed-worker.d.ts +0 -7
- package/dist/worker-entry/composed-worker.d.ts.map +1 -1
- package/dist/worker-entry/surface-paths.d.ts +15 -0
- package/dist/worker-entry/surface-paths.d.ts.map +1 -0
- package/dist/worker-qtam8grz.js +513 -0
- package/dist/worker-qzm0b7br.js +513 -0
- package/dist/worker-y9ha6g44.js +513 -0
- package/package.json +17 -10
- package/R2.md +0 -200
- package/dist/account-spa7gzsn.js +0 -421
- package/dist/build-zv25ke4s.js +0 -102
- package/dist/deploy-6xmqvv06.js +0 -118
- package/dist/index-2q3pmzrx.js +0 -90
- package/dist/index-6nb7w45m.js +0 -79
- package/dist/index-tksw7gpy.js +0 -503
- package/dist/index-v43z02tr.js +0 -205
- package/dist/index-xdq9ery1.js +0 -664
- package/dist/test/multi-worker-context.d.ts +0 -114
- package/dist/test/multi-worker-context.d.ts.map +0 -1
|
@@ -2,23 +2,28 @@ import {
|
|
|
2
2
|
getRemoteModeStatus,
|
|
3
3
|
isRemoteModeActive
|
|
4
4
|
} from "./index-d8bdkx2h.js";
|
|
5
|
+
import {
|
|
6
|
+
discoverEntrypointsSync
|
|
7
|
+
} from "./index-zt22fe2j.js";
|
|
8
|
+
import {
|
|
9
|
+
canProceedWithTest
|
|
10
|
+
} from "./index-7g8zyws4.js";
|
|
5
11
|
import {
|
|
6
12
|
transformWorkerEntrypoint
|
|
7
13
|
} from "./index-wfbfz02q.js";
|
|
8
14
|
import {
|
|
9
|
-
discoverEntrypointsSync,
|
|
10
15
|
resolvePackageSpecifier
|
|
11
|
-
} from "./index-
|
|
16
|
+
} from "./index-82f1z98k.js";
|
|
12
17
|
import {
|
|
13
18
|
__clearTestContext,
|
|
14
19
|
__setTestContext
|
|
15
|
-
} from "./index-
|
|
20
|
+
} from "./index-m3fmw6mx.js";
|
|
16
21
|
import {
|
|
17
22
|
createRouteResolve,
|
|
18
23
|
invokeFetchModule,
|
|
19
24
|
matchFetchRoute,
|
|
20
25
|
resolveFetchHandler
|
|
21
|
-
} from "./index-
|
|
26
|
+
} from "./index-hjy8ctpc.js";
|
|
22
27
|
import {
|
|
23
28
|
createEmailEvent,
|
|
24
29
|
createFetchEvent,
|
|
@@ -27,7 +32,7 @@ import {
|
|
|
27
32
|
createTailEvent,
|
|
28
33
|
runWithContext,
|
|
29
34
|
runWithEventContext
|
|
30
|
-
} from "./index-
|
|
35
|
+
} from "./index-cgbvmse6.js";
|
|
31
36
|
import {
|
|
32
37
|
discoverRoutes
|
|
33
38
|
} from "./index-1p814k7s.js";
|
|
@@ -42,47 +47,47 @@ import {
|
|
|
42
47
|
import {
|
|
43
48
|
startMiniflare,
|
|
44
49
|
startMiniflareFromConfig
|
|
45
|
-
} from "./index-
|
|
50
|
+
} from "./index-ry131z23.js";
|
|
46
51
|
import {
|
|
47
52
|
BridgeClient,
|
|
48
53
|
createEnvProxy,
|
|
49
54
|
setBindingHints
|
|
50
|
-
} from "./index-
|
|
55
|
+
} from "./index-9ba1etyz.js";
|
|
51
56
|
import {
|
|
52
57
|
createLocalSendEmailBinding,
|
|
53
58
|
wrapEnvSendEmailBindings
|
|
54
|
-
} from "./index-
|
|
59
|
+
} from "./index-h18pxvzs.js";
|
|
55
60
|
import {
|
|
56
61
|
getLocalD1DatabaseIdentifier,
|
|
57
62
|
loadConfig,
|
|
58
63
|
normalizeDOBinding,
|
|
59
64
|
resolveConfigPath
|
|
60
|
-
} from "./index-
|
|
65
|
+
} from "./index-6psz1h4c.js";
|
|
61
66
|
import {
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
getEffectiveAccountId,
|
|
68
|
+
getPrimaryAccount
|
|
69
|
+
} from "./index-xk9djfjp.js";
|
|
64
70
|
import {
|
|
65
71
|
getApiToken,
|
|
66
|
-
getEffectiveAccountId,
|
|
67
|
-
getPrimaryAccount,
|
|
68
72
|
isAuthenticated
|
|
69
|
-
} from "./index-
|
|
73
|
+
} from "./index-0w826dsr.js";
|
|
70
74
|
import {
|
|
71
75
|
__require
|
|
72
76
|
} from "./index-37x76zdn.js";
|
|
73
77
|
|
|
74
78
|
// src/test/simple-context.ts
|
|
75
|
-
import {
|
|
76
|
-
import { existsSync as existsSync2 } from "fs";
|
|
79
|
+
import { dirname as dirname4, join as join9, resolve as resolve2 } from "path";
|
|
77
80
|
|
|
78
|
-
// src/test/remote-
|
|
79
|
-
function
|
|
81
|
+
// src/test/remote-cloudflare.ts
|
|
82
|
+
function createRemoteCloudflareClient(accountId) {
|
|
80
83
|
let resolvedAccountId = null;
|
|
81
84
|
async function getAccountId() {
|
|
82
|
-
if (accountId)
|
|
85
|
+
if (accountId) {
|
|
83
86
|
return accountId;
|
|
84
|
-
|
|
87
|
+
}
|
|
88
|
+
if (resolvedAccountId) {
|
|
85
89
|
return resolvedAccountId;
|
|
90
|
+
}
|
|
86
91
|
const primary = await getPrimaryAccount();
|
|
87
92
|
if (!primary) {
|
|
88
93
|
throw new Error("No Cloudflare account found. Run: bunx wrangler login");
|
|
@@ -98,28 +103,45 @@ function createRemoteAI(accountId) {
|
|
|
98
103
|
}
|
|
99
104
|
return token;
|
|
100
105
|
}
|
|
106
|
+
async function jsonRequest(options) {
|
|
107
|
+
const [acctId, token] = await Promise.all([getAccountId(), getToken()]);
|
|
108
|
+
const response = await fetch(`https://api.cloudflare.com/client/v4/accounts/${acctId}${options.path}`, {
|
|
109
|
+
method: options.method,
|
|
110
|
+
headers: {
|
|
111
|
+
Authorization: `Bearer ${token}`,
|
|
112
|
+
"Content-Type": options.contentType ?? "application/json"
|
|
113
|
+
},
|
|
114
|
+
body: options.body
|
|
115
|
+
});
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
const errorText = await response.text();
|
|
118
|
+
throw new Error(`${options.serviceLabel} API error (${response.status}): ${errorText}`);
|
|
119
|
+
}
|
|
120
|
+
const result = await response.json();
|
|
121
|
+
if (!result.success) {
|
|
122
|
+
const message = result.errors?.[0]?.message || `Unknown ${options.serviceLabel} error`;
|
|
123
|
+
throw new Error(`${options.serviceLabel} API error: ${message}`);
|
|
124
|
+
}
|
|
125
|
+
return result.result;
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
getAccountId,
|
|
129
|
+
getToken,
|
|
130
|
+
jsonRequest
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/test/remote-ai.ts
|
|
135
|
+
function createRemoteAI(accountId) {
|
|
136
|
+
const cloudflare = createRemoteCloudflareClient(accountId);
|
|
101
137
|
const ai = {
|
|
102
138
|
async run(model, inputs) {
|
|
103
|
-
|
|
104
|
-
const url = `https://api.cloudflare.com/client/v4/accounts/${acctId}/ai/run/${model}`;
|
|
105
|
-
const response = await fetch(url, {
|
|
139
|
+
return cloudflare.jsonRequest({
|
|
106
140
|
method: "POST",
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
"Content-Type": "application/json"
|
|
110
|
-
},
|
|
141
|
+
path: `/ai/run/${model}`,
|
|
142
|
+
serviceLabel: "AI",
|
|
111
143
|
body: JSON.stringify(inputs)
|
|
112
144
|
});
|
|
113
|
-
if (!response.ok) {
|
|
114
|
-
const errorText = await response.text();
|
|
115
|
-
throw new Error(`AI API error (${response.status}): ${errorText}`);
|
|
116
|
-
}
|
|
117
|
-
const result = await response.json();
|
|
118
|
-
if (!result.success) {
|
|
119
|
-
const message = result.errors?.[0]?.message || "Unknown AI error";
|
|
120
|
-
throw new Error(`AI API error: ${message}`);
|
|
121
|
-
}
|
|
122
|
-
return result.result;
|
|
123
145
|
},
|
|
124
146
|
gateway(_gatewayId) {
|
|
125
147
|
console.warn("AI Gateway is not supported in remote test mode");
|
|
@@ -131,72 +153,25 @@ function createRemoteAI(accountId) {
|
|
|
131
153
|
|
|
132
154
|
// src/test/remote-vectorize.ts
|
|
133
155
|
function createRemoteVectorize(indexName, accountId) {
|
|
134
|
-
|
|
135
|
-
async function getAccountId() {
|
|
136
|
-
if (accountId)
|
|
137
|
-
return accountId;
|
|
138
|
-
if (resolvedAccountId)
|
|
139
|
-
return resolvedAccountId;
|
|
140
|
-
const primary = await getPrimaryAccount();
|
|
141
|
-
if (!primary) {
|
|
142
|
-
throw new Error("No Cloudflare account found. Run: bunx wrangler login");
|
|
143
|
-
}
|
|
144
|
-
const { accountId: effectiveId } = await getEffectiveAccountId(primary.id);
|
|
145
|
-
resolvedAccountId = effectiveId;
|
|
146
|
-
return effectiveId;
|
|
147
|
-
}
|
|
148
|
-
async function getToken() {
|
|
149
|
-
const token = await getApiToken();
|
|
150
|
-
if (!token) {
|
|
151
|
-
throw new Error("Not authenticated. Run: bunx wrangler login");
|
|
152
|
-
}
|
|
153
|
-
return token;
|
|
154
|
-
}
|
|
156
|
+
const cloudflare = createRemoteCloudflareClient(accountId);
|
|
155
157
|
async function apiRequest(method, endpoint, body) {
|
|
156
|
-
|
|
157
|
-
const url = `https://api.cloudflare.com/client/v4/accounts/${acctId}/vectorize/v2/indexes/${indexName}${endpoint}`;
|
|
158
|
-
const response = await fetch(url, {
|
|
158
|
+
return cloudflare.jsonRequest({
|
|
159
159
|
method,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
"Content-Type": "application/json"
|
|
163
|
-
},
|
|
160
|
+
path: `/vectorize/v2/indexes/${indexName}${endpoint}`,
|
|
161
|
+
serviceLabel: "Vectorize",
|
|
164
162
|
body: body ? JSON.stringify(body) : undefined
|
|
165
163
|
});
|
|
166
|
-
if (!response.ok) {
|
|
167
|
-
const errorText = await response.text();
|
|
168
|
-
throw new Error(`Vectorize API error (${response.status}): ${errorText}`);
|
|
169
|
-
}
|
|
170
|
-
const result = await response.json();
|
|
171
|
-
if (!result.success) {
|
|
172
|
-
const message = result.errors?.[0]?.message || "Unknown Vectorize error";
|
|
173
|
-
throw new Error(`Vectorize API error: ${message}`);
|
|
174
|
-
}
|
|
175
|
-
return result.result;
|
|
176
164
|
}
|
|
177
165
|
async function ndjsonRequest(endpoint, vectors) {
|
|
178
|
-
const [acctId, token] = await Promise.all([getAccountId(), getToken()]);
|
|
179
|
-
const url = `https://api.cloudflare.com/client/v4/accounts/${acctId}/vectorize/v2/indexes/${indexName}${endpoint}`;
|
|
180
166
|
const ndjson = vectors.map((v) => JSON.stringify(v)).join(`
|
|
181
167
|
`);
|
|
182
|
-
|
|
168
|
+
return cloudflare.jsonRequest({
|
|
183
169
|
method: "POST",
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
},
|
|
170
|
+
path: `/vectorize/v2/indexes/${indexName}${endpoint}`,
|
|
171
|
+
serviceLabel: "Vectorize",
|
|
172
|
+
contentType: "application/x-ndjson",
|
|
188
173
|
body: ndjson
|
|
189
174
|
});
|
|
190
|
-
if (!response.ok) {
|
|
191
|
-
const errorText = await response.text();
|
|
192
|
-
throw new Error(`Vectorize API error (${response.status}): ${errorText}`);
|
|
193
|
-
}
|
|
194
|
-
const result = await response.json();
|
|
195
|
-
if (!result.success) {
|
|
196
|
-
const message = result.errors?.[0]?.message || "Unknown Vectorize error";
|
|
197
|
-
throw new Error(`Vectorize API error: ${message}`);
|
|
198
|
-
}
|
|
199
|
-
return result.result;
|
|
200
175
|
}
|
|
201
176
|
const vectorize = {
|
|
202
177
|
async describe() {
|
|
@@ -613,75 +588,755 @@ export default {
|
|
|
613
588
|
}
|
|
614
589
|
}
|
|
615
590
|
|
|
616
|
-
// src/test/
|
|
617
|
-
import {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
591
|
+
// src/test/simple-context-durable-objects.ts
|
|
592
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
593
|
+
import { readFile } from "fs/promises";
|
|
594
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
595
|
+
|
|
596
|
+
// src/test/simple-context-gateway-script.ts
|
|
597
|
+
function buildGatewayScript(bundledCode, wrappers) {
|
|
598
|
+
return `
|
|
599
|
+
// Bundled transport + DO classes
|
|
600
|
+
${bundledCode}
|
|
601
|
+
|
|
602
|
+
// DO Wrappers with RPC
|
|
603
|
+
${wrappers}
|
|
604
|
+
|
|
605
|
+
// Transport encoding helper
|
|
606
|
+
const __transportEncoders = typeof transport !== 'undefined' ? transport : {}
|
|
607
|
+
|
|
608
|
+
function __encodeTransport(value) {
|
|
609
|
+
if (value === null || value === undefined) return value
|
|
610
|
+
|
|
611
|
+
// Try each encoder
|
|
612
|
+
for (const [typeName, transporter] of Object.entries(__transportEncoders)) {
|
|
613
|
+
const encoded = transporter.encode(value)
|
|
614
|
+
if (encoded !== false && encoded !== undefined) {
|
|
615
|
+
return { __transport: typeName, value: encoded }
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Recursively encode arrays and objects
|
|
620
|
+
if (Array.isArray(value)) {
|
|
621
|
+
return value.map(__encodeTransport)
|
|
622
|
+
}
|
|
623
|
+
if (typeof value === 'object') {
|
|
624
|
+
const result = {}
|
|
625
|
+
for (const [k, v] of Object.entries(value)) {
|
|
626
|
+
result[k] = __encodeTransport(v)
|
|
627
|
+
}
|
|
628
|
+
return result
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
return value
|
|
625
632
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
633
|
+
|
|
634
|
+
// Gateway with WebSocket RPC
|
|
635
|
+
export default {
|
|
636
|
+
async fetch(request, env) {
|
|
637
|
+
if (request.headers.get('Upgrade') === 'websocket') {
|
|
638
|
+
const { 0: client, 1: server } = new WebSocketPair()
|
|
639
|
+
server.accept()
|
|
640
|
+
server.addEventListener('message', async (e) => {
|
|
641
|
+
try {
|
|
642
|
+
const m = JSON.parse(e.data)
|
|
643
|
+
if (m.t === 'rpc.call') {
|
|
644
|
+
const result = await executeRpc(env, m.method, m.params)
|
|
645
|
+
server.send(JSON.stringify({ t: 'rpc.ok', id: m.id, result }))
|
|
646
|
+
}
|
|
647
|
+
} catch (error) {
|
|
648
|
+
server.send(JSON.stringify({ t: 'rpc.err', id: 'unknown', error: { code: 'RPC_ERROR', message: error.message } }))
|
|
649
|
+
}
|
|
650
|
+
})
|
|
651
|
+
return new Response(null, { status: 101, webSocket: client })
|
|
652
|
+
}
|
|
653
|
+
return new Response('Gateway')
|
|
654
|
+
}
|
|
630
655
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
656
|
+
|
|
657
|
+
async function executeRpc(env, method, params) {
|
|
658
|
+
const [bindingName, ...rest] = method.split('.')
|
|
659
|
+
const op = rest.join('.')
|
|
660
|
+
const binding = env[bindingName]
|
|
661
|
+
const RAW_EMAIL = 'EmailMessage::raw'
|
|
662
|
+
if (!binding) throw new Error('Binding not found: ' + bindingName)
|
|
663
|
+
|
|
664
|
+
// KV operations
|
|
665
|
+
if (op === 'get') return binding.get(params[0], params[1])
|
|
666
|
+
if (op === 'put') return binding.put(params[0], params[1], params[2])
|
|
667
|
+
if (op === 'delete') return binding.delete(params[0])
|
|
668
|
+
if (op === 'list') return binding.list(params[0])
|
|
669
|
+
if (op === 'getWithMetadata') return binding.getWithMetadata(params[0], params[1])
|
|
670
|
+
|
|
671
|
+
// R2 operations
|
|
672
|
+
if (op === 'r2.get') return binding.get(params[0], params[1])
|
|
673
|
+
if (op === 'r2.put') return binding.put(params[0], params[1], params[2])
|
|
674
|
+
if (op === 'r2.delete') return binding.delete(params[0])
|
|
675
|
+
if (op === 'r2.list') return binding.list(params[0])
|
|
676
|
+
if (op === 'head') return binding.head(params[0])
|
|
677
|
+
|
|
678
|
+
// D1 operations
|
|
679
|
+
if (op === 'exec') return binding.exec(params[0])
|
|
680
|
+
if (op === 'dump') return binding.dump()
|
|
681
|
+
if (op === 'batch') {
|
|
682
|
+
const stmts = params[0].map(s => {
|
|
683
|
+
const stmt = binding.prepare(s.sql)
|
|
684
|
+
return s.bindings?.length ? stmt.bind(...s.bindings) : stmt
|
|
685
|
+
})
|
|
686
|
+
return binding.batch(stmts)
|
|
687
|
+
}
|
|
688
|
+
if (op === 'prepare.run') return binding.prepare(params[0]).bind(...(params[1] || [])).run()
|
|
689
|
+
if (op === 'prepare.all') return binding.prepare(params[0]).bind(...(params[1] || [])).all()
|
|
690
|
+
if (op === 'prepare.first') return binding.prepare(params[0]).bind(...(params[1] || [])).first(params[2])
|
|
691
|
+
if (op === 'prepare.raw') return binding.prepare(params[0]).bind(...(params[1] || [])).raw({ columnNames: params[2] })
|
|
692
|
+
|
|
693
|
+
// Send email operations
|
|
694
|
+
if (op === 'email.send') {
|
|
695
|
+
return binding.send(__normalizeEmailMessage(params[0]))
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// DO operations
|
|
699
|
+
if (op === 'idFromName') {
|
|
700
|
+
return { __type: 'DOId', hex: binding.idFromName(params[0]).toString() }
|
|
701
|
+
}
|
|
702
|
+
if (op === 'stub.rpc') {
|
|
703
|
+
const [, idSerialized, rpcMethod, rpcParams] = params
|
|
704
|
+
const stub = binding.get(binding.idFromString(idSerialized.hex))
|
|
705
|
+
|
|
706
|
+
if (typeof stub[rpcMethod] === 'function') {
|
|
707
|
+
let result = await stub[rpcMethod](...(rpcParams || []))
|
|
708
|
+
result = __encodeTransport(result)
|
|
709
|
+
return result
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const response = await stub.fetch(new Request('http://do/_rpc', {
|
|
713
|
+
method: 'POST',
|
|
714
|
+
headers: { 'Content-Type': 'application/json' },
|
|
715
|
+
body: JSON.stringify({ method: rpcMethod, params: rpcParams || [] })
|
|
716
|
+
}))
|
|
717
|
+
|
|
718
|
+
const payload = await response.json()
|
|
719
|
+
if (!response.ok || !payload?.ok) {
|
|
720
|
+
throw new Error(payload?.error?.message || ('DO RPC failed with status ' + response.status))
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
return payload.result
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
throw new Error('Unknown operation: ' + method)
|
|
654
727
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
retryAll() {
|
|
665
|
-
for (const msg of messages) {
|
|
666
|
-
msg.retry();
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
};
|
|
728
|
+
|
|
729
|
+
function __createEmailMessageRaw(raw) {
|
|
730
|
+
if (typeof raw === 'string' || raw instanceof ReadableStream) {
|
|
731
|
+
return raw
|
|
732
|
+
}
|
|
733
|
+
if (raw instanceof Uint8Array || raw instanceof ArrayBuffer) {
|
|
734
|
+
return new Response(raw).body
|
|
735
|
+
}
|
|
736
|
+
throw new Error('Unsupported EmailMessage raw payload')
|
|
670
737
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
738
|
+
|
|
739
|
+
function __buildRawEmail(message) {
|
|
740
|
+
const lines = []
|
|
741
|
+
const messageId = '<' + Date.now() + '-' + Math.random().toString(36).slice(2) + '@devflare.dev>'
|
|
742
|
+
|
|
743
|
+
lines.push('From: ' + message.from)
|
|
744
|
+
lines.push('To: ' + (Array.isArray(message.to) ? message.to.join(', ') : message.to))
|
|
745
|
+
lines.push('Date: ' + new Date().toUTCString())
|
|
746
|
+
lines.push('Message-ID: ' + messageId)
|
|
747
|
+
|
|
748
|
+
if (message.subject) lines.push('Subject: ' + message.subject)
|
|
749
|
+
if (message.replyTo) lines.push('Reply-To: ' + String(message.replyTo))
|
|
750
|
+
if (message.cc) lines.push('Cc: ' + (Array.isArray(message.cc) ? message.cc.join(', ') : message.cc))
|
|
751
|
+
if (message.bcc) lines.push('Bcc: ' + (Array.isArray(message.bcc) ? message.bcc.join(', ') : message.bcc))
|
|
752
|
+
|
|
753
|
+
for (const [key, value] of Object.entries(message.headers || {})) {
|
|
754
|
+
lines.push(key + ': ' + value)
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
lines.push('MIME-Version: 1.0')
|
|
758
|
+
lines.push('Content-Type: ' + (message.html ? 'text/html' : 'text/plain') + '; charset=UTF-8')
|
|
759
|
+
lines.push('')
|
|
760
|
+
lines.push(String(message.html ?? message.text ?? '').replace(/\\r?\\n/g, '\\r\\n'))
|
|
761
|
+
|
|
762
|
+
return lines.join('\\r\\n')
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function __normalizeEmailMessage(message) {
|
|
766
|
+
if (!message || typeof message !== 'object' || !('from' in message) || !('to' in message)) {
|
|
767
|
+
return message
|
|
768
|
+
}
|
|
769
|
+
if ('EmailMessage::raw' in message) {
|
|
770
|
+
return message
|
|
771
|
+
}
|
|
772
|
+
if ('raw' in message && message.raw !== undefined) {
|
|
773
|
+
return {
|
|
774
|
+
from: message.from,
|
|
775
|
+
to: message.to,
|
|
776
|
+
[RAW_EMAIL]: __createEmailMessageRaw(message.raw)
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return {
|
|
780
|
+
from: message.from,
|
|
781
|
+
to: message.to,
|
|
782
|
+
[RAW_EMAIL]: __createEmailMessageRaw(__buildRawEmail(message))
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
`;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// src/test/simple-context-paths.ts
|
|
789
|
+
import { existsSync as existsSync2 } from "fs";
|
|
790
|
+
import { createServer } from "net";
|
|
791
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
792
|
+
import { fileURLToPath } from "url";
|
|
793
|
+
var DEFAULT_TRANSPORT_ENTRY_FILES = [
|
|
794
|
+
"src/transport.ts",
|
|
795
|
+
"src/transport.js",
|
|
796
|
+
"src/transport.mts",
|
|
797
|
+
"src/transport.mjs"
|
|
798
|
+
];
|
|
799
|
+
var CURRENT_PACKAGE_ROOT = findPackageRoot(dirname2(fileURLToPath(import.meta.url)));
|
|
800
|
+
function getBunRuntime2() {
|
|
801
|
+
const g = globalThis;
|
|
802
|
+
if (typeof g.Bun === "object" && g.Bun !== null) {
|
|
803
|
+
return g.Bun;
|
|
804
|
+
}
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
function getCallerDirectory() {
|
|
808
|
+
const stackCallerDirectory = getStackCallerDirectory();
|
|
809
|
+
if (stackCallerDirectory) {
|
|
810
|
+
return stackCallerDirectory;
|
|
811
|
+
}
|
|
812
|
+
return process.cwd();
|
|
813
|
+
}
|
|
814
|
+
function getStackCallerDirectory() {
|
|
815
|
+
const originalPrepare = Error.prepareStackTrace;
|
|
816
|
+
Error.prepareStackTrace = (_, stack) => stack;
|
|
817
|
+
try {
|
|
818
|
+
const err = new Error;
|
|
819
|
+
const stack = err.stack;
|
|
820
|
+
for (const site of stack ?? []) {
|
|
821
|
+
const filename = site.getFileName?.();
|
|
822
|
+
if (filename && !isInsideCurrentPackage(filename) && !filename.includes("simple-context") && !filename.includes("node_modules") && !filename.includes("[") && existsSync2(filename)) {
|
|
823
|
+
return dirname2(filename);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
} finally {
|
|
827
|
+
Error.prepareStackTrace = originalPrepare;
|
|
828
|
+
}
|
|
829
|
+
return null;
|
|
830
|
+
}
|
|
831
|
+
function findPackageRoot(startDir) {
|
|
832
|
+
let currentDir = startDir;
|
|
833
|
+
while (true) {
|
|
834
|
+
if (existsSync2(join2(currentDir, "package.json"))) {
|
|
835
|
+
return currentDir;
|
|
836
|
+
}
|
|
837
|
+
const parentDir = dirname2(currentDir);
|
|
838
|
+
if (parentDir === currentDir) {
|
|
839
|
+
return startDir;
|
|
840
|
+
}
|
|
841
|
+
currentDir = parentDir;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
function isInsideCurrentPackage(filePath) {
|
|
845
|
+
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
846
|
+
const normalizedPackageRoot = CURRENT_PACKAGE_ROOT.replace(/\\/g, "/");
|
|
847
|
+
return normalizedFilePath === normalizedPackageRoot || normalizedFilePath.startsWith(`${normalizedPackageRoot}/`);
|
|
848
|
+
}
|
|
849
|
+
async function findNearestConfig(startDir) {
|
|
850
|
+
let currentDir = startDir;
|
|
851
|
+
while (true) {
|
|
852
|
+
const configPath = await resolveConfigPath(currentDir);
|
|
853
|
+
if (configPath) {
|
|
854
|
+
return configPath;
|
|
855
|
+
}
|
|
856
|
+
const parentDir = dirname2(currentDir);
|
|
857
|
+
if (parentDir === currentDir) {
|
|
858
|
+
return null;
|
|
859
|
+
}
|
|
860
|
+
currentDir = parentDir;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
async function getAvailablePort() {
|
|
864
|
+
return await new Promise((resolvePort, reject) => {
|
|
865
|
+
const server = createServer();
|
|
866
|
+
server.once("error", reject);
|
|
867
|
+
server.listen(0, "127.0.0.1", () => {
|
|
868
|
+
const address = server.address();
|
|
869
|
+
if (!address || typeof address === "string") {
|
|
870
|
+
server.close(() => reject(new Error("Could not determine an available port")));
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
const { port } = address;
|
|
874
|
+
server.close((error) => {
|
|
875
|
+
if (error) {
|
|
876
|
+
reject(error);
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
resolvePort(port);
|
|
880
|
+
});
|
|
881
|
+
});
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
function resolveTransportFile(configDir, configuredPath) {
|
|
885
|
+
if (typeof configuredPath === "string") {
|
|
886
|
+
return configuredPath;
|
|
887
|
+
}
|
|
888
|
+
if (configuredPath === null) {
|
|
889
|
+
return null;
|
|
890
|
+
}
|
|
891
|
+
for (const defaultEntry of DEFAULT_TRANSPORT_ENTRY_FILES) {
|
|
892
|
+
if (existsSync2(join2(configDir, defaultEntry))) {
|
|
893
|
+
return defaultEntry;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
return null;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// src/test/simple-context-durable-objects.ts
|
|
900
|
+
function findExportedClasses(code) {
|
|
901
|
+
const classes = [];
|
|
902
|
+
const classPattern = /export\s+class\s+(\w+)/g;
|
|
903
|
+
let match;
|
|
904
|
+
while ((match = classPattern.exec(code)) !== null) {
|
|
905
|
+
classes.push(match[1]);
|
|
906
|
+
}
|
|
907
|
+
return classes;
|
|
908
|
+
}
|
|
909
|
+
function classSupportsNativeDurableObjectRpc(code, className) {
|
|
910
|
+
const nativeRpcPattern = new RegExp(`export\\s+class\\s+${className}\\s+extends\\s+DurableObject\\b`);
|
|
911
|
+
return nativeRpcPattern.test(code);
|
|
912
|
+
}
|
|
913
|
+
function toGeneratedIdentifier(value) {
|
|
914
|
+
const normalized = value.replace(/[^A-Za-z0-9_$]/g, "_");
|
|
915
|
+
return /^[A-Za-z_$]/.test(normalized) ? normalized : `_${normalized}`;
|
|
916
|
+
}
|
|
917
|
+
async function discoverLocalDurableObjectClasses(config, configDir) {
|
|
918
|
+
const classToFilePath = new Map;
|
|
919
|
+
const doPatternConfig = config.files?.durableObjects;
|
|
920
|
+
const doPattern = typeof doPatternConfig === "string" ? doPatternConfig : DEFAULT_DO_PATTERN;
|
|
921
|
+
if (doPatternConfig === false) {
|
|
922
|
+
return classToFilePath;
|
|
923
|
+
}
|
|
924
|
+
const doFiles = await findFiles(doPattern, { cwd: configDir });
|
|
925
|
+
for (const filePath of doFiles) {
|
|
926
|
+
try {
|
|
927
|
+
const code = await readFile(filePath, "utf-8");
|
|
928
|
+
const classNames = findExportedClasses(code);
|
|
929
|
+
for (const className of classNames) {
|
|
930
|
+
classToFilePath.set(className, {
|
|
931
|
+
filePath,
|
|
932
|
+
nativeRpc: classSupportsNativeDurableObjectRpc(code, className)
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
} catch {}
|
|
936
|
+
}
|
|
937
|
+
return classToFilePath;
|
|
938
|
+
}
|
|
939
|
+
async function resolveLocalDurableObjects(config, configDir) {
|
|
940
|
+
const doConfig = {};
|
|
941
|
+
const doInfos = [];
|
|
942
|
+
const classToFilePath = await discoverLocalDurableObjectClasses(config, configDir);
|
|
943
|
+
for (const [name, rawDoInfo] of Object.entries(config.bindings?.durableObjects ?? {})) {
|
|
944
|
+
const doInfo = normalizeDOBinding(rawDoInfo);
|
|
945
|
+
if (doInfo.__ref) {
|
|
946
|
+
continue;
|
|
947
|
+
}
|
|
948
|
+
let scriptPath;
|
|
949
|
+
let nativeRpc = false;
|
|
950
|
+
if (doInfo.scriptName) {
|
|
951
|
+
scriptPath = join3(configDir, "src", doInfo.scriptName);
|
|
952
|
+
try {
|
|
953
|
+
const code = await readFile(scriptPath, "utf-8");
|
|
954
|
+
nativeRpc = classSupportsNativeDurableObjectRpc(code, doInfo.className);
|
|
955
|
+
} catch {
|
|
956
|
+
nativeRpc = false;
|
|
957
|
+
}
|
|
958
|
+
} else {
|
|
959
|
+
const discoveredClass = classToFilePath.get(doInfo.className);
|
|
960
|
+
if (!discoveredClass) {
|
|
961
|
+
throw new Error(`Durable object ${name} (className: '${doInfo.className}') not found.
|
|
962
|
+
` + `Either:
|
|
963
|
+
` + ` 1. Set files.durableObjects pattern in config (e.g., 'src/do.*.ts')
|
|
964
|
+
` + ` 2. Use explicit scriptName: { className: '${doInfo.className}', scriptName: 'do.file.ts' }`);
|
|
965
|
+
}
|
|
966
|
+
scriptPath = discoveredClass.filePath;
|
|
967
|
+
nativeRpc = discoveredClass.nativeRpc;
|
|
968
|
+
}
|
|
969
|
+
const runtimeClassName = nativeRpc ? doInfo.className : `__Devflare${toGeneratedIdentifier(name)}RpcWrapper`;
|
|
970
|
+
doConfig[name] = runtimeClassName;
|
|
971
|
+
doInfos.push({
|
|
972
|
+
name,
|
|
973
|
+
className: doInfo.className,
|
|
974
|
+
scriptPath,
|
|
975
|
+
nativeRpc,
|
|
976
|
+
runtimeClassName
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
return {
|
|
980
|
+
doConfig,
|
|
981
|
+
doInfos
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
function buildWrapperCode(doInfos) {
|
|
985
|
+
return doInfos.filter((info) => !info.nativeRpc).map((info) => `
|
|
986
|
+
export class ${info.runtimeClassName} {
|
|
987
|
+
constructor(state, env) {
|
|
988
|
+
this.__instance = new ${info.className}(state, env)
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
async fetch(request) {
|
|
992
|
+
const url = new URL(request.url)
|
|
993
|
+
if (request.method !== 'POST' || url.pathname !== '/_rpc') {
|
|
994
|
+
return new Response('Not found', { status: 404 })
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
try {
|
|
998
|
+
const payload = await request.json()
|
|
999
|
+
const method = payload?.method
|
|
1000
|
+
const params = Array.isArray(payload?.params) ? payload.params : []
|
|
1001
|
+
const target = this.__instance?.[method]
|
|
1002
|
+
|
|
1003
|
+
if (typeof target !== 'function') {
|
|
1004
|
+
return new Response(JSON.stringify({
|
|
1005
|
+
ok: false,
|
|
1006
|
+
error: { message: 'Method not found: ' + String(method) }
|
|
1007
|
+
}), {
|
|
1008
|
+
status: 404,
|
|
1009
|
+
headers: { 'Content-Type': 'application/json' }
|
|
1010
|
+
})
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
let result = await target.apply(this.__instance, params)
|
|
1014
|
+
result = __encodeTransport(result)
|
|
1015
|
+
|
|
1016
|
+
return new Response(JSON.stringify({ ok: true, result }), {
|
|
1017
|
+
headers: { 'Content-Type': 'application/json' }
|
|
1018
|
+
})
|
|
1019
|
+
} catch (error) {
|
|
1020
|
+
return new Response(JSON.stringify({
|
|
1021
|
+
ok: false,
|
|
1022
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
1023
|
+
}), {
|
|
1024
|
+
status: 500,
|
|
1025
|
+
headers: { 'Content-Type': 'application/json' }
|
|
1026
|
+
})
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}`.trim()).join(`
|
|
1030
|
+
|
|
1031
|
+
`);
|
|
1032
|
+
}
|
|
1033
|
+
async function bundleDurableObjectModules(configDir, doInfos, transportFile) {
|
|
1034
|
+
const virtualImports = [];
|
|
1035
|
+
const virtualExports = [];
|
|
1036
|
+
if (transportFile) {
|
|
1037
|
+
const transportPath = join3(configDir, transportFile);
|
|
1038
|
+
virtualImports.push(`import { transport } from '${transportPath.replace(/\\/g, "/")}'`);
|
|
1039
|
+
virtualExports.push("export { transport }");
|
|
1040
|
+
}
|
|
1041
|
+
for (const info of doInfos) {
|
|
1042
|
+
virtualImports.push(`import { ${info.className} } from '${info.scriptPath.replace(/\\/g, "/")}'`);
|
|
1043
|
+
virtualExports.push(`export { ${info.className} }`);
|
|
1044
|
+
}
|
|
1045
|
+
if (virtualImports.length === 0) {
|
|
1046
|
+
return "";
|
|
1047
|
+
}
|
|
1048
|
+
const virtualEntry = [...virtualImports, "", ...virtualExports].join(`
|
|
1049
|
+
`);
|
|
1050
|
+
const virtualPath = join3(configDir, ".devflare", "__test_entry.ts");
|
|
1051
|
+
mkdirSync(dirname3(virtualPath), { recursive: true });
|
|
1052
|
+
writeFileSync(virtualPath, virtualEntry);
|
|
1053
|
+
const bun = getBunRuntime2();
|
|
1054
|
+
if (!bun) {
|
|
1055
|
+
throw new Error("Bun runtime is required for createTestContext with Durable Objects");
|
|
1056
|
+
}
|
|
1057
|
+
const result = await bun.build({
|
|
1058
|
+
entrypoints: [virtualPath],
|
|
1059
|
+
target: "browser",
|
|
1060
|
+
format: "esm",
|
|
1061
|
+
minify: false,
|
|
1062
|
+
external: ["cloudflare:workers", "cloudflare:*"]
|
|
1063
|
+
});
|
|
1064
|
+
if (!result.success) {
|
|
1065
|
+
throw new Error(`Failed to bundle test entry: ${result.logs.join(`
|
|
1066
|
+
`)}`);
|
|
1067
|
+
}
|
|
1068
|
+
return await result.outputs[0].text();
|
|
1069
|
+
}
|
|
1070
|
+
async function buildDurableObjectGateway(config, configDir, transportFile) {
|
|
1071
|
+
if (!config.bindings?.durableObjects) {
|
|
1072
|
+
return {
|
|
1073
|
+
script: buildGatewayScript("", "")
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
const { doConfig, doInfos } = await resolveLocalDurableObjects(config, configDir);
|
|
1077
|
+
const bundledCode = await bundleDurableObjectModules(configDir, doInfos, transportFile);
|
|
1078
|
+
const wrapperCode = buildWrapperCode(doInfos);
|
|
1079
|
+
return {
|
|
1080
|
+
durableObjects: doConfig,
|
|
1081
|
+
script: buildGatewayScript(bundledCode, wrapperCode)
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// src/test/email.ts
|
|
1086
|
+
import { join as join4 } from "path";
|
|
1087
|
+
var miniflarePort = 8787;
|
|
1088
|
+
var emailListeners = [];
|
|
1089
|
+
var sentEmails = [];
|
|
1090
|
+
var emailHandlerPath = null;
|
|
1091
|
+
var configDir = null;
|
|
1092
|
+
var testEnvGetter = null;
|
|
1093
|
+
function configureEmail(options = {}) {
|
|
1094
|
+
if (options.port) {
|
|
1095
|
+
miniflarePort = options.port;
|
|
1096
|
+
}
|
|
1097
|
+
emailHandlerPath = options.handlerPath ?? emailHandlerPath;
|
|
1098
|
+
configDir = options.configDir ?? configDir;
|
|
1099
|
+
testEnvGetter = options.getEnv ?? testEnvGetter;
|
|
1100
|
+
}
|
|
1101
|
+
function buildRawEmail(options) {
|
|
1102
|
+
if (options.raw) {
|
|
1103
|
+
return options.raw;
|
|
1104
|
+
}
|
|
1105
|
+
const lines = [];
|
|
1106
|
+
const messageId = `<${Date.now()}-${Math.random().toString(36).slice(2)}@devflare.dev>`;
|
|
1107
|
+
const date = new Date().toUTCString();
|
|
1108
|
+
lines.push(`From: ${options.from}`);
|
|
1109
|
+
lines.push(`To: ${options.to}`);
|
|
1110
|
+
lines.push(`Date: ${date}`);
|
|
1111
|
+
lines.push(`Message-ID: ${messageId}`);
|
|
1112
|
+
if (options.subject) {
|
|
1113
|
+
lines.push(`Subject: ${options.subject}`);
|
|
1114
|
+
}
|
|
1115
|
+
if (options.headers) {
|
|
1116
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
1117
|
+
lines.push(`${key}: ${value}`);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
lines.push("MIME-Version: 1.0");
|
|
1121
|
+
lines.push("Content-Type: text/plain; charset=UTF-8");
|
|
1122
|
+
lines.push("");
|
|
1123
|
+
lines.push(options.body ?? "");
|
|
1124
|
+
return lines.join(`\r
|
|
1125
|
+
`);
|
|
1126
|
+
}
|
|
1127
|
+
function createEmailHeaders(rawEmail) {
|
|
1128
|
+
const headers = new Headers;
|
|
1129
|
+
const lines = rawEmail.split(/\r?\n/);
|
|
1130
|
+
for (const line of lines) {
|
|
1131
|
+
if (!line.trim()) {
|
|
1132
|
+
break;
|
|
1133
|
+
}
|
|
1134
|
+
const colonIndex = line.indexOf(":");
|
|
1135
|
+
if (colonIndex <= 0) {
|
|
1136
|
+
continue;
|
|
1137
|
+
}
|
|
1138
|
+
headers.append(line.slice(0, colonIndex).trim(), line.slice(colonIndex + 1).trim());
|
|
1139
|
+
}
|
|
1140
|
+
return headers;
|
|
1141
|
+
}
|
|
1142
|
+
function createRawEmailStream(rawEmail) {
|
|
1143
|
+
return new ReadableStream({
|
|
1144
|
+
start(controller) {
|
|
1145
|
+
controller.enqueue(new TextEncoder().encode(rawEmail));
|
|
1146
|
+
controller.close();
|
|
1147
|
+
}
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
function resolveEmailHandler(module) {
|
|
1151
|
+
if (typeof module.default === "function") {
|
|
1152
|
+
return module.default;
|
|
1153
|
+
}
|
|
1154
|
+
if (module.default && typeof module.default.email === "function") {
|
|
1155
|
+
return module.default.email.bind(module.default);
|
|
1156
|
+
}
|
|
1157
|
+
if (typeof module.email === "function") {
|
|
1158
|
+
return module.email;
|
|
1159
|
+
}
|
|
1160
|
+
return null;
|
|
1161
|
+
}
|
|
1162
|
+
function getRecordedRawContent(raw) {
|
|
1163
|
+
if (typeof raw === "string") {
|
|
1164
|
+
return raw;
|
|
1165
|
+
}
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
async function send(options) {
|
|
1169
|
+
const raw = buildRawEmail(options);
|
|
1170
|
+
if (emailHandlerPath && configDir && testEnvGetter) {
|
|
1171
|
+
const absolutePath = join4(configDir, emailHandlerPath);
|
|
1172
|
+
const handlerModule = await import(absolutePath);
|
|
1173
|
+
const emailHandler = resolveEmailHandler(handlerModule);
|
|
1174
|
+
if (!emailHandler) {
|
|
1175
|
+
throw new Error(`Email handler at "${emailHandlerPath}" must export a default function or named "email" export.
|
|
1176
|
+
NaN`);
|
|
1177
|
+
}
|
|
1178
|
+
const waitUntilPromises = [];
|
|
1179
|
+
const ctx = {
|
|
1180
|
+
waitUntil(promise) {
|
|
1181
|
+
waitUntilPromises.push(promise);
|
|
1182
|
+
},
|
|
1183
|
+
passThroughOnException() {},
|
|
1184
|
+
props: {}
|
|
1185
|
+
};
|
|
1186
|
+
const runtimeEnv = testEnvGetter();
|
|
1187
|
+
const timestamp = new Date;
|
|
1188
|
+
const message = {
|
|
1189
|
+
from: options.from,
|
|
1190
|
+
to: options.to,
|
|
1191
|
+
headers: createEmailHeaders(raw),
|
|
1192
|
+
raw: createRawEmailStream(raw),
|
|
1193
|
+
rawSize: raw.length,
|
|
1194
|
+
setReject(reason) {
|
|
1195
|
+
throw new Error(`Email rejected: ${reason}`);
|
|
1196
|
+
},
|
|
1197
|
+
async forward(rcptTo) {
|
|
1198
|
+
recordSentEmail({
|
|
1199
|
+
type: "forward",
|
|
1200
|
+
from: options.from,
|
|
1201
|
+
to: rcptTo,
|
|
1202
|
+
raw,
|
|
1203
|
+
timestamp
|
|
1204
|
+
});
|
|
1205
|
+
},
|
|
1206
|
+
async reply(message2) {
|
|
1207
|
+
recordSentEmail({
|
|
1208
|
+
type: "reply",
|
|
1209
|
+
from: message2.from ?? options.to,
|
|
1210
|
+
to: message2.to ?? options.from,
|
|
1211
|
+
raw: getRecordedRawContent(message2.raw),
|
|
1212
|
+
timestamp
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
const emailEvent = createEmailEvent(message, runtimeEnv, ctx);
|
|
1217
|
+
await runWithEventContext(emailEvent, () => emailHandler(emailEvent));
|
|
1218
|
+
await Promise.all(waitUntilPromises);
|
|
1219
|
+
return new Response(JSON.stringify({ ok: true, from: options.from, to: options.to }), {
|
|
1220
|
+
headers: { "Content-Type": "application/json" }
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
const url = new URL(`http://localhost:${miniflarePort}/cdn-cgi/handler/email`);
|
|
1224
|
+
url.searchParams.set("from", options.from);
|
|
1225
|
+
url.searchParams.set("to", options.to);
|
|
1226
|
+
const response = await fetch(url.toString(), {
|
|
1227
|
+
method: "POST",
|
|
1228
|
+
headers: {
|
|
1229
|
+
"Content-Type": "text/plain"
|
|
1230
|
+
},
|
|
1231
|
+
body: raw
|
|
1232
|
+
});
|
|
1233
|
+
return response;
|
|
1234
|
+
}
|
|
1235
|
+
function onReceive(callback) {
|
|
1236
|
+
emailListeners.push(callback);
|
|
1237
|
+
return () => {
|
|
1238
|
+
emailListeners = emailListeners.filter((cb) => cb !== callback);
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
function getSentEmails() {
|
|
1242
|
+
return [...sentEmails];
|
|
1243
|
+
}
|
|
1244
|
+
function clearSentEmails() {
|
|
1245
|
+
sentEmails = [];
|
|
1246
|
+
}
|
|
1247
|
+
function recordSentEmail(email) {
|
|
1248
|
+
sentEmails.push(email);
|
|
1249
|
+
for (const listener of emailListeners) {
|
|
1250
|
+
try {
|
|
1251
|
+
listener(email);
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
console.error("[devflare/test] Email listener error:", error);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
function resetEmailState() {
|
|
1258
|
+
miniflarePort = 8787;
|
|
1259
|
+
emailHandlerPath = null;
|
|
1260
|
+
configDir = null;
|
|
1261
|
+
testEnvGetter = null;
|
|
1262
|
+
emailListeners = [];
|
|
1263
|
+
sentEmails = [];
|
|
1264
|
+
}
|
|
1265
|
+
var email = {
|
|
1266
|
+
send,
|
|
1267
|
+
onReceive,
|
|
1268
|
+
getSentEmails,
|
|
1269
|
+
clearSentEmails
|
|
1270
|
+
};
|
|
1271
|
+
|
|
1272
|
+
// src/test/queue.ts
|
|
1273
|
+
import { join as join5 } from "path";
|
|
1274
|
+
var queueHandlerPath = null;
|
|
1275
|
+
var configDir2 = null;
|
|
1276
|
+
var testEnvGetter2 = null;
|
|
1277
|
+
function configureQueue(options) {
|
|
1278
|
+
queueHandlerPath = options.handlerPath;
|
|
1279
|
+
configDir2 = options.configDir;
|
|
1280
|
+
testEnvGetter2 = options.getEnv;
|
|
1281
|
+
}
|
|
1282
|
+
function resetQueueState() {
|
|
1283
|
+
queueHandlerPath = null;
|
|
1284
|
+
configDir2 = null;
|
|
1285
|
+
testEnvGetter2 = null;
|
|
1286
|
+
}
|
|
1287
|
+
function createMessage(options) {
|
|
1288
|
+
const id = options.id ?? crypto.randomUUID();
|
|
1289
|
+
const timestamp = options.timestamp ?? new Date;
|
|
1290
|
+
const attempts = options.attempts ?? 1;
|
|
1291
|
+
let state = "pending";
|
|
1292
|
+
return {
|
|
1293
|
+
id,
|
|
1294
|
+
timestamp,
|
|
1295
|
+
body: options.body,
|
|
1296
|
+
attempts,
|
|
1297
|
+
ack() {
|
|
1298
|
+
state = "acked";
|
|
1299
|
+
},
|
|
1300
|
+
retry(opts) {
|
|
1301
|
+
state = "retried";
|
|
1302
|
+
},
|
|
1303
|
+
retryAll() {
|
|
1304
|
+
state = "retried";
|
|
1305
|
+
},
|
|
1306
|
+
get _state() {
|
|
1307
|
+
return state;
|
|
1308
|
+
}
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
function createMessageBatch(messages) {
|
|
1312
|
+
return {
|
|
1313
|
+
queue: "test-queue",
|
|
1314
|
+
messages,
|
|
1315
|
+
ackAll() {
|
|
1316
|
+
for (const msg of messages) {
|
|
1317
|
+
msg.ack();
|
|
1318
|
+
}
|
|
1319
|
+
},
|
|
1320
|
+
retryAll() {
|
|
1321
|
+
for (const msg of messages) {
|
|
1322
|
+
msg.retry();
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
async function trigger(messages) {
|
|
1328
|
+
if (!queueHandlerPath) {
|
|
1329
|
+
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)");
|
|
1330
|
+
}
|
|
1331
|
+
if (!configDir2 || !testEnvGetter2) {
|
|
1332
|
+
throw new Error("Queue helper not initialized. Call createTestContext() before using cf.queue.trigger()");
|
|
1333
|
+
}
|
|
1334
|
+
const absolutePath = join5(configDir2, queueHandlerPath);
|
|
1335
|
+
const handlerModule = await import(absolutePath);
|
|
1336
|
+
const queueHandler = handlerModule.default ?? handlerModule.queue;
|
|
681
1337
|
if (typeof queueHandler !== "function") {
|
|
682
1338
|
throw new Error(`Queue handler at "${queueHandlerPath}" must export a default function or named "queue" export.
|
|
683
|
-
|
|
684
|
-
Legacy compatibility is still supported for queue(batch, env, ctx).`);
|
|
1339
|
+
NaN`);
|
|
685
1340
|
}
|
|
686
1341
|
const normalizedMessages = messages.map((msg) => {
|
|
687
1342
|
if (typeof msg === "object" && msg !== null && "body" in msg) {
|
|
@@ -699,9 +1354,9 @@ Legacy compatibility is still supported for queue(batch, env, ctx).`);
|
|
|
699
1354
|
passThroughOnException() {},
|
|
700
1355
|
props: {}
|
|
701
1356
|
};
|
|
702
|
-
const env =
|
|
1357
|
+
const env = testEnvGetter2();
|
|
703
1358
|
const queueEvent = createQueueEvent(batch, env, ctx);
|
|
704
|
-
await runWithEventContext(queueEvent, () => queueHandler(queueEvent
|
|
1359
|
+
await runWithEventContext(queueEvent, () => queueHandler(queueEvent));
|
|
705
1360
|
await Promise.all(waitUntilPromises);
|
|
706
1361
|
const acked = [];
|
|
707
1362
|
const retried = [];
|
|
@@ -726,46 +1381,45 @@ Legacy compatibility is still supported for queue(batch, env, ctx).`);
|
|
|
726
1381
|
total: mockMessages.length
|
|
727
1382
|
};
|
|
728
1383
|
}
|
|
729
|
-
async function
|
|
1384
|
+
async function send2(message) {
|
|
730
1385
|
return trigger([message]);
|
|
731
1386
|
}
|
|
732
1387
|
var queue = {
|
|
733
1388
|
trigger,
|
|
734
|
-
send
|
|
1389
|
+
send: send2
|
|
735
1390
|
};
|
|
736
1391
|
|
|
737
1392
|
// src/test/scheduled.ts
|
|
738
|
-
import { join as
|
|
1393
|
+
import { join as join6 } from "path";
|
|
739
1394
|
var scheduledHandlerPath = null;
|
|
740
|
-
var
|
|
741
|
-
var
|
|
1395
|
+
var configDir3 = null;
|
|
1396
|
+
var testEnvGetter3 = null;
|
|
742
1397
|
function configureScheduled(options) {
|
|
743
1398
|
scheduledHandlerPath = options.handlerPath;
|
|
744
|
-
|
|
745
|
-
|
|
1399
|
+
configDir3 = options.configDir;
|
|
1400
|
+
testEnvGetter3 = options.getEnv;
|
|
746
1401
|
}
|
|
747
1402
|
function resetScheduledState() {
|
|
748
1403
|
scheduledHandlerPath = null;
|
|
749
|
-
|
|
750
|
-
|
|
1404
|
+
configDir3 = null;
|
|
1405
|
+
testEnvGetter3 = null;
|
|
751
1406
|
}
|
|
752
1407
|
async function trigger2(cronOrOptions) {
|
|
753
1408
|
if (!scheduledHandlerPath) {
|
|
754
1409
|
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)");
|
|
755
1410
|
}
|
|
756
|
-
if (!
|
|
1411
|
+
if (!configDir3 || !testEnvGetter3) {
|
|
757
1412
|
throw new Error("Scheduled helper not initialized. Call createTestContext() before using cf.scheduled.trigger()");
|
|
758
1413
|
}
|
|
759
1414
|
const options = typeof cronOrOptions === "string" ? { cron: cronOrOptions } : cronOrOptions ?? {};
|
|
760
1415
|
const cron = options.cron ?? "* * * * *";
|
|
761
1416
|
const scheduledTime = options.scheduledTime instanceof Date ? options.scheduledTime.getTime() : options.scheduledTime ?? Date.now();
|
|
762
|
-
const absolutePath =
|
|
1417
|
+
const absolutePath = join6(configDir3, scheduledHandlerPath);
|
|
763
1418
|
const handlerModule = await import(absolutePath);
|
|
764
1419
|
const scheduledHandler = handlerModule.default ?? handlerModule.scheduled;
|
|
765
1420
|
if (typeof scheduledHandler !== "function") {
|
|
766
1421
|
throw new Error(`Scheduled handler at "${scheduledHandlerPath}" must export a default function or named "scheduled" export.
|
|
767
|
-
|
|
768
|
-
Legacy compatibility is still supported for scheduled(controller, env, ctx).`);
|
|
1422
|
+
NaN`);
|
|
769
1423
|
}
|
|
770
1424
|
const controller = {
|
|
771
1425
|
scheduledTime,
|
|
@@ -780,10 +1434,10 @@ Legacy compatibility is still supported for scheduled(controller, env, ctx).`);
|
|
|
780
1434
|
passThroughOnException() {},
|
|
781
1435
|
props: {}
|
|
782
1436
|
};
|
|
783
|
-
const env =
|
|
1437
|
+
const env = testEnvGetter3();
|
|
784
1438
|
const scheduledEvent = createScheduledEvent(controller, env, ctx);
|
|
785
1439
|
try {
|
|
786
|
-
await runWithEventContext(scheduledEvent, () => scheduledHandler(scheduledEvent
|
|
1440
|
+
await runWithEventContext(scheduledEvent, () => scheduledHandler(scheduledEvent));
|
|
787
1441
|
await Promise.all(waitUntilPromises);
|
|
788
1442
|
return {
|
|
789
1443
|
success: true,
|
|
@@ -803,117 +1457,8 @@ var scheduled = {
|
|
|
803
1457
|
trigger: trigger2
|
|
804
1458
|
};
|
|
805
1459
|
|
|
806
|
-
// src/test/worker.ts
|
|
807
|
-
import { join as join4 } from "path";
|
|
808
|
-
var fetchHandlerPath = null;
|
|
809
|
-
var configDir3 = null;
|
|
810
|
-
var testEnvGetter3 = null;
|
|
811
|
-
var fileRoutes = [];
|
|
812
|
-
function configureWorker(options) {
|
|
813
|
-
fetchHandlerPath = options.handlerPath;
|
|
814
|
-
fileRoutes = options.routes ?? [];
|
|
815
|
-
configDir3 = options.configDir;
|
|
816
|
-
testEnvGetter3 = options.getEnv;
|
|
817
|
-
}
|
|
818
|
-
function resetWorkerState() {
|
|
819
|
-
fetchHandlerPath = null;
|
|
820
|
-
fileRoutes = [];
|
|
821
|
-
configDir3 = null;
|
|
822
|
-
testEnvGetter3 = null;
|
|
823
|
-
}
|
|
824
|
-
async function fetch2(request, options) {
|
|
825
|
-
if (!fetchHandlerPath && fileRoutes.length === 0) {
|
|
826
|
-
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/**).");
|
|
827
|
-
}
|
|
828
|
-
if (!configDir3 || !testEnvGetter3) {
|
|
829
|
-
throw new Error("Worker helper not initialized. Call createTestContext() before using cf.worker.fetch()");
|
|
830
|
-
}
|
|
831
|
-
const workerConfigDir = configDir3;
|
|
832
|
-
const getEnv = testEnvGetter3;
|
|
833
|
-
let req;
|
|
834
|
-
if (typeof request === "string") {
|
|
835
|
-
const url = request.startsWith("http") ? request : `http://localhost${request.startsWith("/") ? "" : "/"}${request}`;
|
|
836
|
-
const headers = new Headers(options?.headers);
|
|
837
|
-
let body;
|
|
838
|
-
if (options?.body !== undefined) {
|
|
839
|
-
if (typeof options.body === "string") {
|
|
840
|
-
body = options.body;
|
|
841
|
-
} else {
|
|
842
|
-
body = JSON.stringify(options.body);
|
|
843
|
-
if (!headers.has("Content-Type")) {
|
|
844
|
-
headers.set("Content-Type", "application/json");
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
req = new Request(url, {
|
|
849
|
-
method: options?.method ?? "GET",
|
|
850
|
-
headers,
|
|
851
|
-
body
|
|
852
|
-
});
|
|
853
|
-
} else {
|
|
854
|
-
req = request;
|
|
855
|
-
}
|
|
856
|
-
const handlerModule = fetchHandlerPath ? await import(join4(workerConfigDir, fetchHandlerPath)) : {};
|
|
857
|
-
const routeModules = await Promise.all(fileRoutes.map(async (route) => {
|
|
858
|
-
return {
|
|
859
|
-
...route,
|
|
860
|
-
module: await import(join4(workerConfigDir, route.filePath))
|
|
861
|
-
};
|
|
862
|
-
}));
|
|
863
|
-
const methodExports = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "ALL"];
|
|
864
|
-
const hasMethodHandler = methodExports.some((method) => {
|
|
865
|
-
return typeof handlerModule[method] === "function" || typeof handlerModule.default?.[method] === "function";
|
|
866
|
-
});
|
|
867
|
-
if (!resolveFetchHandler(handlerModule) && !hasMethodHandler && routeModules.length === 0) {
|
|
868
|
-
throw new Error(`Fetch handler at "${fetchHandlerPath}" must export one of:
|
|
869
|
-
- request-wide "handle" middleware
|
|
870
|
-
- named "fetch"
|
|
871
|
-
- default fetch handler
|
|
872
|
-
- HTTP method exports such as "GET" or "POST"
|
|
873
|
-
Legacy compatibility is still supported for fetch(request, env, ctx).`);
|
|
874
|
-
}
|
|
875
|
-
const waitUntilPromises = [];
|
|
876
|
-
const ctx = {
|
|
877
|
-
waitUntil(promise) {
|
|
878
|
-
waitUntilPromises.push(promise);
|
|
879
|
-
},
|
|
880
|
-
passThroughOnException() {},
|
|
881
|
-
props: {}
|
|
882
|
-
};
|
|
883
|
-
const env = getEnv();
|
|
884
|
-
const initialRouteMatch = routeModules.length > 0 ? matchFetchRoute(routeModules, req) : null;
|
|
885
|
-
const fetchEvent = createFetchEvent(req, env, ctx, {
|
|
886
|
-
params: initialRouteMatch?.params ?? {}
|
|
887
|
-
});
|
|
888
|
-
const response = await runWithEventContext(fetchEvent, () => invokeFetchModule(handlerModule, fetchEvent, routeModules.length > 0 ? createRouteResolve(routeModules, fetchEvent) : undefined));
|
|
889
|
-
return response;
|
|
890
|
-
}
|
|
891
|
-
async function get(path, headers) {
|
|
892
|
-
return fetch2(path, { method: "GET", headers });
|
|
893
|
-
}
|
|
894
|
-
async function post(path, body, headers) {
|
|
895
|
-
return fetch2(path, { method: "POST", body, headers });
|
|
896
|
-
}
|
|
897
|
-
async function put(path, body, headers) {
|
|
898
|
-
return fetch2(path, { method: "PUT", body, headers });
|
|
899
|
-
}
|
|
900
|
-
async function del(path, headers) {
|
|
901
|
-
return fetch2(path, { method: "DELETE", headers });
|
|
902
|
-
}
|
|
903
|
-
async function patch(path, body, headers) {
|
|
904
|
-
return fetch2(path, { method: "PATCH", body, headers });
|
|
905
|
-
}
|
|
906
|
-
var worker = {
|
|
907
|
-
fetch: fetch2,
|
|
908
|
-
get,
|
|
909
|
-
post,
|
|
910
|
-
put,
|
|
911
|
-
delete: del,
|
|
912
|
-
patch
|
|
913
|
-
};
|
|
914
|
-
|
|
915
1460
|
// src/test/tail.ts
|
|
916
|
-
import { join as
|
|
1461
|
+
import { join as join7 } from "path";
|
|
917
1462
|
var tailHandlerPath = null;
|
|
918
1463
|
var configDir4 = null;
|
|
919
1464
|
var testEnvGetter4 = null;
|
|
@@ -959,13 +1504,12 @@ async function trigger3(items) {
|
|
|
959
1504
|
}
|
|
960
1505
|
return createTraceItem(item);
|
|
961
1506
|
});
|
|
962
|
-
const absolutePath =
|
|
1507
|
+
const absolutePath = join7(configDir4, tailHandlerPath);
|
|
963
1508
|
const handlerModule = await import(absolutePath);
|
|
964
1509
|
const tailHandler = handlerModule.default ?? handlerModule.tail;
|
|
965
1510
|
if (typeof tailHandler !== "function") {
|
|
966
1511
|
throw new Error(`Tail handler at "${tailHandlerPath}" must export a default function or named "tail" export.
|
|
967
|
-
|
|
968
|
-
Legacy compatibility is still supported for tail(events, env, ctx).`);
|
|
1512
|
+
NaN`);
|
|
969
1513
|
}
|
|
970
1514
|
const waitUntilPromises = [];
|
|
971
1515
|
const ctx = {
|
|
@@ -978,7 +1522,7 @@ Legacy compatibility is still supported for tail(events, env, ctx).`);
|
|
|
978
1522
|
const env = testEnvGetter4();
|
|
979
1523
|
const tailEvent = createTailEvent(traceItems, env, ctx);
|
|
980
1524
|
try {
|
|
981
|
-
await runWithEventContext(tailEvent, () => tailHandler(tailEvent
|
|
1525
|
+
await runWithEventContext(tailEvent, () => tailHandler(tailEvent));
|
|
982
1526
|
await Promise.all(waitUntilPromises);
|
|
983
1527
|
return {
|
|
984
1528
|
success: true,
|
|
@@ -987,284 +1531,206 @@ Legacy compatibility is still supported for tail(events, env, ctx).`);
|
|
|
987
1531
|
} catch (error) {
|
|
988
1532
|
return {
|
|
989
1533
|
success: false,
|
|
990
|
-
error: error instanceof Error ? error.message : String(error),
|
|
991
|
-
itemCount: traceItems.length
|
|
992
|
-
};
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
function create(options = {}) {
|
|
996
|
-
return createTraceItem(options);
|
|
997
|
-
}
|
|
998
|
-
var tail = {
|
|
999
|
-
trigger: trigger3,
|
|
1000
|
-
create
|
|
1001
|
-
};
|
|
1002
|
-
|
|
1003
|
-
// src/test/email.ts
|
|
1004
|
-
import { join as join6 } from "path";
|
|
1005
|
-
var miniflarePort = 8787;
|
|
1006
|
-
var emailListeners = [];
|
|
1007
|
-
var sentEmails = [];
|
|
1008
|
-
var emailHandlerPath = null;
|
|
1009
|
-
var configDir5 = null;
|
|
1010
|
-
var testEnvGetter5 = null;
|
|
1011
|
-
function configureEmail(options = {}) {
|
|
1012
|
-
if (options.port) {
|
|
1013
|
-
miniflarePort = options.port;
|
|
1014
|
-
}
|
|
1015
|
-
emailHandlerPath = options.handlerPath ?? emailHandlerPath;
|
|
1016
|
-
configDir5 = options.configDir ?? configDir5;
|
|
1017
|
-
testEnvGetter5 = options.getEnv ?? testEnvGetter5;
|
|
1018
|
-
}
|
|
1019
|
-
function buildRawEmail(options) {
|
|
1020
|
-
if (options.raw) {
|
|
1021
|
-
return options.raw;
|
|
1022
|
-
}
|
|
1023
|
-
const lines = [];
|
|
1024
|
-
const messageId = `<${Date.now()}-${Math.random().toString(36).slice(2)}@devflare.dev>`;
|
|
1025
|
-
const date = new Date().toUTCString();
|
|
1026
|
-
lines.push(`From: ${options.from}`);
|
|
1027
|
-
lines.push(`To: ${options.to}`);
|
|
1028
|
-
lines.push(`Date: ${date}`);
|
|
1029
|
-
lines.push(`Message-ID: ${messageId}`);
|
|
1030
|
-
if (options.subject) {
|
|
1031
|
-
lines.push(`Subject: ${options.subject}`);
|
|
1032
|
-
}
|
|
1033
|
-
if (options.headers) {
|
|
1034
|
-
for (const [key, value] of Object.entries(options.headers)) {
|
|
1035
|
-
lines.push(`${key}: ${value}`);
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
lines.push("MIME-Version: 1.0");
|
|
1039
|
-
lines.push("Content-Type: text/plain; charset=UTF-8");
|
|
1040
|
-
lines.push("");
|
|
1041
|
-
lines.push(options.body ?? "");
|
|
1042
|
-
return lines.join(`\r
|
|
1043
|
-
`);
|
|
1044
|
-
}
|
|
1045
|
-
function createEmailHeaders(rawEmail) {
|
|
1046
|
-
const headers = new Headers;
|
|
1047
|
-
const lines = rawEmail.split(/\r?\n/);
|
|
1048
|
-
for (const line of lines) {
|
|
1049
|
-
if (!line.trim()) {
|
|
1050
|
-
break;
|
|
1051
|
-
}
|
|
1052
|
-
const colonIndex = line.indexOf(":");
|
|
1053
|
-
if (colonIndex <= 0) {
|
|
1054
|
-
continue;
|
|
1055
|
-
}
|
|
1056
|
-
headers.append(line.slice(0, colonIndex).trim(), line.slice(colonIndex + 1).trim());
|
|
1534
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1535
|
+
itemCount: traceItems.length
|
|
1536
|
+
};
|
|
1057
1537
|
}
|
|
1058
|
-
return headers;
|
|
1059
1538
|
}
|
|
1060
|
-
function
|
|
1061
|
-
return
|
|
1062
|
-
start(controller) {
|
|
1063
|
-
controller.enqueue(new TextEncoder().encode(rawEmail));
|
|
1064
|
-
controller.close();
|
|
1065
|
-
}
|
|
1066
|
-
});
|
|
1539
|
+
function create(options = {}) {
|
|
1540
|
+
return createTraceItem(options);
|
|
1067
1541
|
}
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1542
|
+
var tail = {
|
|
1543
|
+
trigger: trigger3,
|
|
1544
|
+
create
|
|
1545
|
+
};
|
|
1546
|
+
|
|
1547
|
+
// src/test/worker.ts
|
|
1548
|
+
import { join as join8 } from "path";
|
|
1549
|
+
var fetchHandlerPath = null;
|
|
1550
|
+
var configDir5 = null;
|
|
1551
|
+
var testEnvGetter5 = null;
|
|
1552
|
+
var fileRoutes = [];
|
|
1553
|
+
function configureWorker(options) {
|
|
1554
|
+
fetchHandlerPath = options.handlerPath;
|
|
1555
|
+
fileRoutes = options.routes ?? [];
|
|
1556
|
+
configDir5 = options.configDir;
|
|
1557
|
+
testEnvGetter5 = options.getEnv;
|
|
1079
1558
|
}
|
|
1080
|
-
function
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1559
|
+
function resetWorkerState() {
|
|
1560
|
+
fetchHandlerPath = null;
|
|
1561
|
+
fileRoutes = [];
|
|
1562
|
+
configDir5 = null;
|
|
1563
|
+
testEnvGetter5 = null;
|
|
1085
1564
|
}
|
|
1086
|
-
async function
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
const
|
|
1098
|
-
const
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
from: options.from,
|
|
1109
|
-
to: options.to,
|
|
1110
|
-
headers: createEmailHeaders(raw),
|
|
1111
|
-
raw: createRawEmailStream(raw),
|
|
1112
|
-
rawSize: raw.length,
|
|
1113
|
-
setReject(reason) {
|
|
1114
|
-
throw new Error(`Email rejected: ${reason}`);
|
|
1115
|
-
},
|
|
1116
|
-
async forward(rcptTo) {
|
|
1117
|
-
recordSentEmail({
|
|
1118
|
-
type: "forward",
|
|
1119
|
-
from: options.from,
|
|
1120
|
-
to: rcptTo,
|
|
1121
|
-
raw,
|
|
1122
|
-
timestamp
|
|
1123
|
-
});
|
|
1124
|
-
},
|
|
1125
|
-
async reply(message2) {
|
|
1126
|
-
recordSentEmail({
|
|
1127
|
-
type: "reply",
|
|
1128
|
-
from: message2.from ?? options.to,
|
|
1129
|
-
to: message2.to ?? options.from,
|
|
1130
|
-
raw: getRecordedRawContent(message2.raw),
|
|
1131
|
-
timestamp
|
|
1132
|
-
});
|
|
1565
|
+
async function fetch2(request, options) {
|
|
1566
|
+
if (!fetchHandlerPath && fileRoutes.length === 0) {
|
|
1567
|
+
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/**).");
|
|
1568
|
+
}
|
|
1569
|
+
if (!configDir5 || !testEnvGetter5) {
|
|
1570
|
+
throw new Error("Worker helper not initialized. Call createTestContext() before using cf.worker.fetch()");
|
|
1571
|
+
}
|
|
1572
|
+
const workerConfigDir = configDir5;
|
|
1573
|
+
const getEnv = testEnvGetter5;
|
|
1574
|
+
let req;
|
|
1575
|
+
if (typeof request === "string") {
|
|
1576
|
+
const url = request.startsWith("http") ? request : `http://localhost${request.startsWith("/") ? "" : "/"}${request}`;
|
|
1577
|
+
const headers = new Headers(options?.headers);
|
|
1578
|
+
let body;
|
|
1579
|
+
if (options?.body !== undefined) {
|
|
1580
|
+
if (typeof options.body === "string") {
|
|
1581
|
+
body = options.body;
|
|
1582
|
+
} else {
|
|
1583
|
+
body = JSON.stringify(options.body);
|
|
1584
|
+
if (!headers.has("Content-Type")) {
|
|
1585
|
+
headers.set("Content-Type", "application/json");
|
|
1586
|
+
}
|
|
1133
1587
|
}
|
|
1134
|
-
}
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
headers: { "Content-Type": "application/json" }
|
|
1588
|
+
}
|
|
1589
|
+
req = new Request(url, {
|
|
1590
|
+
method: options?.method ?? "GET",
|
|
1591
|
+
headers,
|
|
1592
|
+
body
|
|
1140
1593
|
});
|
|
1594
|
+
} else {
|
|
1595
|
+
req = request;
|
|
1141
1596
|
}
|
|
1142
|
-
const
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1597
|
+
const handlerModule = fetchHandlerPath ? await import(join8(workerConfigDir, fetchHandlerPath)) : {};
|
|
1598
|
+
const routeModules = await Promise.all(fileRoutes.map(async (route) => {
|
|
1599
|
+
return {
|
|
1600
|
+
...route,
|
|
1601
|
+
module: await import(join8(workerConfigDir, route.filePath))
|
|
1602
|
+
};
|
|
1603
|
+
}));
|
|
1604
|
+
const methodExports = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "ALL"];
|
|
1605
|
+
const hasMethodHandler = methodExports.some((method) => {
|
|
1606
|
+
return typeof handlerModule[method] === "function" || typeof handlerModule.default?.[method] === "function";
|
|
1607
|
+
});
|
|
1608
|
+
if (!resolveFetchHandler(handlerModule) && !hasMethodHandler && routeModules.length === 0) {
|
|
1609
|
+
throw new Error(`Fetch handler at "${fetchHandlerPath}" must export one of:
|
|
1610
|
+
- request-wide "handle" middleware
|
|
1611
|
+
- named "fetch"
|
|
1612
|
+
- default fetch handler
|
|
1613
|
+
- HTTP method exports such as "GET" or "POST"`);
|
|
1614
|
+
}
|
|
1615
|
+
const waitUntilPromises = [];
|
|
1616
|
+
const ctx = {
|
|
1617
|
+
waitUntil(promise) {
|
|
1618
|
+
waitUntilPromises.push(promise);
|
|
1149
1619
|
},
|
|
1150
|
-
|
|
1620
|
+
passThroughOnException() {},
|
|
1621
|
+
props: {}
|
|
1622
|
+
};
|
|
1623
|
+
const env = getEnv();
|
|
1624
|
+
const initialRouteMatch = routeModules.length > 0 ? matchFetchRoute(routeModules, req) : null;
|
|
1625
|
+
const fetchEvent = createFetchEvent(req, env, ctx, {
|
|
1626
|
+
params: initialRouteMatch?.params ?? {}
|
|
1151
1627
|
});
|
|
1628
|
+
const response = await runWithEventContext(fetchEvent, () => invokeFetchModule(handlerModule, fetchEvent, routeModules.length > 0 ? createRouteResolve(routeModules, fetchEvent) : undefined));
|
|
1152
1629
|
return response;
|
|
1153
1630
|
}
|
|
1154
|
-
function
|
|
1155
|
-
|
|
1156
|
-
return () => {
|
|
1157
|
-
emailListeners = emailListeners.filter((cb) => cb !== callback);
|
|
1158
|
-
};
|
|
1631
|
+
async function get(path, headers) {
|
|
1632
|
+
return fetch2(path, { method: "GET", headers });
|
|
1159
1633
|
}
|
|
1160
|
-
function
|
|
1161
|
-
return
|
|
1634
|
+
async function post(path, body, headers) {
|
|
1635
|
+
return fetch2(path, { method: "POST", body, headers });
|
|
1162
1636
|
}
|
|
1163
|
-
function
|
|
1164
|
-
|
|
1637
|
+
async function put(path, body, headers) {
|
|
1638
|
+
return fetch2(path, { method: "PUT", body, headers });
|
|
1165
1639
|
}
|
|
1166
|
-
function
|
|
1167
|
-
|
|
1168
|
-
for (const listener of emailListeners) {
|
|
1169
|
-
try {
|
|
1170
|
-
listener(email);
|
|
1171
|
-
} catch (error) {
|
|
1172
|
-
console.error("[devflare/test] Email listener error:", error);
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1640
|
+
async function del(path, headers) {
|
|
1641
|
+
return fetch2(path, { method: "DELETE", headers });
|
|
1175
1642
|
}
|
|
1176
|
-
function
|
|
1177
|
-
|
|
1178
|
-
emailHandlerPath = null;
|
|
1179
|
-
configDir5 = null;
|
|
1180
|
-
testEnvGetter5 = null;
|
|
1181
|
-
emailListeners = [];
|
|
1182
|
-
sentEmails = [];
|
|
1643
|
+
async function patch(path, body, headers) {
|
|
1644
|
+
return fetch2(path, { method: "PATCH", body, headers });
|
|
1183
1645
|
}
|
|
1184
|
-
var
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1646
|
+
var worker = {
|
|
1647
|
+
fetch: fetch2,
|
|
1648
|
+
get,
|
|
1649
|
+
post,
|
|
1650
|
+
put,
|
|
1651
|
+
delete: del,
|
|
1652
|
+
patch
|
|
1189
1653
|
};
|
|
1190
1654
|
|
|
1191
1655
|
// src/test/simple-context.ts
|
|
1192
|
-
function findExportedClasses(code) {
|
|
1193
|
-
const classes = [];
|
|
1194
|
-
const classPattern = /export\s+class\s+(\w+)/g;
|
|
1195
|
-
let match;
|
|
1196
|
-
while ((match = classPattern.exec(code)) !== null) {
|
|
1197
|
-
classes.push(match[1]);
|
|
1198
|
-
}
|
|
1199
|
-
return classes;
|
|
1200
|
-
}
|
|
1201
|
-
function getBunRuntime2() {
|
|
1202
|
-
const g = globalThis;
|
|
1203
|
-
if (typeof g.Bun === "object" && g.Bun !== null) {
|
|
1204
|
-
return g.Bun;
|
|
1205
|
-
}
|
|
1206
|
-
return;
|
|
1207
|
-
}
|
|
1208
1656
|
var globalClient = null;
|
|
1209
1657
|
var globalMiniflare = null;
|
|
1210
1658
|
var globalEnvProxy = null;
|
|
1211
1659
|
var globalTransportDecode = null;
|
|
1212
1660
|
var globalRemoteBindings = null;
|
|
1213
1661
|
var globalMiniflareBindings = null;
|
|
1214
|
-
var
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
const bun = getBunRuntime2();
|
|
1222
|
-
if (bun?.main) {
|
|
1223
|
-
const mainPath = bun.main;
|
|
1224
|
-
if (!mainPath.includes("[") && existsSync2(mainPath)) {
|
|
1225
|
-
return dirname2(mainPath);
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
const originalPrepare = Error.prepareStackTrace;
|
|
1229
|
-
Error.prepareStackTrace = (_, stack2) => stack2;
|
|
1230
|
-
const err = new Error;
|
|
1231
|
-
const stack = err.stack;
|
|
1232
|
-
Error.prepareStackTrace = originalPrepare;
|
|
1233
|
-
for (const site of stack) {
|
|
1234
|
-
const filename = site.getFileName?.();
|
|
1235
|
-
if (filename && !filename.includes("simple-context") && !filename.includes("node_modules") && !filename.includes("[") && existsSync2(filename)) {
|
|
1236
|
-
return dirname2(filename);
|
|
1237
|
-
}
|
|
1662
|
+
var TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS = 3;
|
|
1663
|
+
var TEST_CONTEXT_STARTUP_RETRY_DELAY_MS = 75;
|
|
1664
|
+
var TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS = 8;
|
|
1665
|
+
var TEST_CONTEXT_BRIDGE_CONNECT_RETRY_DELAY_MS = 150;
|
|
1666
|
+
function isRetriableTestContextStartupError(error) {
|
|
1667
|
+
if (!(error instanceof Error)) {
|
|
1668
|
+
return false;
|
|
1238
1669
|
}
|
|
1239
|
-
|
|
1670
|
+
const message = error.message.toLowerCase();
|
|
1671
|
+
return message.includes("websocket connection failed") || message.includes("connection timeout: ws://") || message.includes("econnrefused") || message.includes("eaddrinuse") || message.includes("address already in use");
|
|
1240
1672
|
}
|
|
1241
|
-
async function
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1673
|
+
async function waitForTestContextStartupRetry() {
|
|
1674
|
+
await new Promise((resolve3) => setTimeout(resolve3, TEST_CONTEXT_STARTUP_RETRY_DELAY_MS));
|
|
1675
|
+
}
|
|
1676
|
+
async function waitForBridgeClientRetry() {
|
|
1677
|
+
await new Promise((resolve3) => setTimeout(resolve3, TEST_CONTEXT_BRIDGE_CONNECT_RETRY_DELAY_MS));
|
|
1678
|
+
}
|
|
1679
|
+
function shouldPreferBridgeBinding(hint) {
|
|
1680
|
+
return hint === "do" || hint === "service";
|
|
1681
|
+
}
|
|
1682
|
+
async function connectBridgeClientWithRetry(url) {
|
|
1683
|
+
let lastError;
|
|
1684
|
+
for (let attempt = 1;attempt <= TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS; attempt++) {
|
|
1685
|
+
const client = new BridgeClient({ url });
|
|
1686
|
+
try {
|
|
1687
|
+
await client.connect();
|
|
1688
|
+
return client;
|
|
1689
|
+
} catch (error) {
|
|
1690
|
+
lastError = error;
|
|
1691
|
+
client.disconnect();
|
|
1692
|
+
if (attempt >= TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS || !isRetriableTestContextStartupError(error)) {
|
|
1693
|
+
throw error;
|
|
1694
|
+
}
|
|
1695
|
+
await waitForBridgeClientRetry();
|
|
1251
1696
|
}
|
|
1252
|
-
currentDir = parentDir;
|
|
1253
1697
|
}
|
|
1698
|
+
throw lastError instanceof Error ? lastError : new Error("Bridge-backed test context could not connect to the WebSocket gateway.");
|
|
1254
1699
|
}
|
|
1255
|
-
function
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1700
|
+
async function startBridgeBackedTestContext(mfConfig) {
|
|
1701
|
+
const { Miniflare } = await import("miniflare");
|
|
1702
|
+
for (let attempt = 1;attempt <= TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS; attempt++) {
|
|
1703
|
+
const port = await getAvailablePort();
|
|
1704
|
+
let miniflare = null;
|
|
1705
|
+
let client = null;
|
|
1706
|
+
try {
|
|
1707
|
+
miniflare = new Miniflare({
|
|
1708
|
+
...mfConfig,
|
|
1709
|
+
port
|
|
1710
|
+
});
|
|
1711
|
+
await miniflare.ready;
|
|
1712
|
+
const miniflareBindings = wrapEnvSendEmailBindings(await miniflare.getBindings());
|
|
1713
|
+
client = await connectBridgeClientWithRetry(`ws://localhost:${port}`);
|
|
1714
|
+
return {
|
|
1715
|
+
port,
|
|
1716
|
+
client,
|
|
1717
|
+
miniflare,
|
|
1718
|
+
miniflareBindings
|
|
1719
|
+
};
|
|
1720
|
+
} catch (error) {
|
|
1721
|
+
client?.disconnect();
|
|
1722
|
+
if (miniflare) {
|
|
1723
|
+
try {
|
|
1724
|
+
await miniflare.dispose();
|
|
1725
|
+
} catch {}
|
|
1726
|
+
}
|
|
1727
|
+
if (attempt >= TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS || !isRetriableTestContextStartupError(error)) {
|
|
1728
|
+
throw error;
|
|
1729
|
+
}
|
|
1730
|
+
await waitForTestContextStartupRetry();
|
|
1265
1731
|
}
|
|
1266
1732
|
}
|
|
1267
|
-
|
|
1733
|
+
throw new Error("Bridge-backed test context startup exhausted all retry attempts.");
|
|
1268
1734
|
}
|
|
1269
1735
|
async function createTestContext(configPath) {
|
|
1270
1736
|
const callerDir = getCallerDirectory();
|
|
@@ -1275,12 +1741,12 @@ async function createTestContext(configPath) {
|
|
|
1275
1741
|
const found = await findNearestConfig(callerDir);
|
|
1276
1742
|
if (!found) {
|
|
1277
1743
|
throw new Error(`Could not find a devflare config file. Searched upward from: ${callerDir}
|
|
1278
|
-
|
|
1279
|
-
|
|
1744
|
+
Expected one of: devflare.config.ts, devflare.config.mts, devflare.config.js, devflare.config.mjs
|
|
1745
|
+
Either create a config file or provide an explicit path: createTestContext('./path/to/config.ts')`);
|
|
1280
1746
|
}
|
|
1281
1747
|
absolutePath = found;
|
|
1282
1748
|
}
|
|
1283
|
-
const configDir6 =
|
|
1749
|
+
const configDir6 = dirname4(absolutePath);
|
|
1284
1750
|
const config = await loadConfig({
|
|
1285
1751
|
cwd: configDir6,
|
|
1286
1752
|
configFile: absolutePath.split(/[/\\]/).pop()
|
|
@@ -1309,28 +1775,34 @@ async function createTestContext(configPath) {
|
|
|
1309
1775
|
}
|
|
1310
1776
|
const hints = {};
|
|
1311
1777
|
if (config.bindings?.kv) {
|
|
1312
|
-
for (const name of Object.keys(config.bindings.kv))
|
|
1778
|
+
for (const name of Object.keys(config.bindings.kv)) {
|
|
1313
1779
|
hints[name] = "kv";
|
|
1780
|
+
}
|
|
1314
1781
|
}
|
|
1315
1782
|
if (config.bindings?.r2) {
|
|
1316
|
-
for (const name of Object.keys(config.bindings.r2))
|
|
1783
|
+
for (const name of Object.keys(config.bindings.r2)) {
|
|
1317
1784
|
hints[name] = "r2";
|
|
1785
|
+
}
|
|
1318
1786
|
}
|
|
1319
1787
|
if (config.bindings?.d1) {
|
|
1320
|
-
for (const name of Object.keys(config.bindings.d1))
|
|
1788
|
+
for (const name of Object.keys(config.bindings.d1)) {
|
|
1321
1789
|
hints[name] = "d1";
|
|
1790
|
+
}
|
|
1322
1791
|
}
|
|
1323
1792
|
if (config.bindings?.durableObjects) {
|
|
1324
|
-
for (const name of Object.keys(config.bindings.durableObjects))
|
|
1793
|
+
for (const name of Object.keys(config.bindings.durableObjects)) {
|
|
1325
1794
|
hints[name] = "do";
|
|
1795
|
+
}
|
|
1326
1796
|
}
|
|
1327
1797
|
if (config.bindings?.services) {
|
|
1328
|
-
for (const name of Object.keys(config.bindings.services))
|
|
1798
|
+
for (const name of Object.keys(config.bindings.services)) {
|
|
1329
1799
|
hints[name] = "service";
|
|
1800
|
+
}
|
|
1330
1801
|
}
|
|
1331
1802
|
if (config.bindings?.sendEmail) {
|
|
1332
|
-
for (const name of Object.keys(config.bindings.sendEmail))
|
|
1803
|
+
for (const name of Object.keys(config.bindings.sendEmail)) {
|
|
1333
1804
|
hints[name] = "sendEmail";
|
|
1805
|
+
}
|
|
1334
1806
|
}
|
|
1335
1807
|
const needsMultiWorkerForServices = hasServiceBindings(config);
|
|
1336
1808
|
const needsMultiWorkerForDOs = hasCrossWorkerDOs(config);
|
|
@@ -1343,16 +1815,16 @@ async function createTestContext(configPath) {
|
|
|
1343
1815
|
if (needsMultiWorkerForDOs) {
|
|
1344
1816
|
doBindingResolution = await resolveDOBindings(config, configDir6);
|
|
1345
1817
|
}
|
|
1346
|
-
const randomPort = 1e4 + Math.floor(Math.random() * 50000);
|
|
1347
1818
|
const localWorkerBindings = config.vars ?? {};
|
|
1348
1819
|
const mfConfig = {
|
|
1349
|
-
modules: true
|
|
1350
|
-
port: randomPort
|
|
1820
|
+
modules: true
|
|
1351
1821
|
};
|
|
1352
|
-
if (config.bindings?.kv)
|
|
1822
|
+
if (config.bindings?.kv) {
|
|
1353
1823
|
mfConfig.kvNamespaces = Object.keys(config.bindings.kv);
|
|
1354
|
-
|
|
1824
|
+
}
|
|
1825
|
+
if (config.bindings?.r2) {
|
|
1355
1826
|
mfConfig.r2Buckets = Object.keys(config.bindings.r2);
|
|
1827
|
+
}
|
|
1356
1828
|
if (config.bindings?.d1) {
|
|
1357
1829
|
mfConfig.d1Databases = Object.fromEntries(Object.entries(config.bindings.d1).map(([bindingName, bindingConfig]) => {
|
|
1358
1830
|
return [bindingName, getLocalD1DatabaseIdentifier(bindingConfig)];
|
|
@@ -1386,7 +1858,7 @@ async function createTestContext(configPath) {
|
|
|
1386
1858
|
}
|
|
1387
1859
|
const transportFile = resolveTransportFile(configDir6, config.files?.transport);
|
|
1388
1860
|
if (transportFile) {
|
|
1389
|
-
const transportPath =
|
|
1861
|
+
const transportPath = join9(configDir6, transportFile);
|
|
1390
1862
|
const transportModule = await import(transportPath);
|
|
1391
1863
|
if (!transportModule.transport) {
|
|
1392
1864
|
console.warn(`[devflare] Warning: Transport file "${transportFile}" does not export a named "transport" object.
|
|
@@ -1400,86 +1872,10 @@ Transport encoding/decoding will be disabled.`);
|
|
|
1400
1872
|
}
|
|
1401
1873
|
}
|
|
1402
1874
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
const doPatternConfig = config.files?.durableObjects;
|
|
1408
|
-
const doPattern = typeof doPatternConfig === "string" ? doPatternConfig : DEFAULT_DO_PATTERN;
|
|
1409
|
-
if (doPatternConfig !== false) {
|
|
1410
|
-
const fs = await import("fs/promises");
|
|
1411
|
-
const doFiles = await findFiles(doPattern, { cwd: configDir6 });
|
|
1412
|
-
for (const filePath of doFiles) {
|
|
1413
|
-
try {
|
|
1414
|
-
const code = await fs.readFile(filePath, "utf-8");
|
|
1415
|
-
const classNames = findExportedClasses(code);
|
|
1416
|
-
for (const className of classNames) {
|
|
1417
|
-
classToFilePath.set(className, filePath);
|
|
1418
|
-
}
|
|
1419
|
-
} catch {}
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
for (const [name, rawDoInfo] of Object.entries(config.bindings.durableObjects)) {
|
|
1423
|
-
const doInfo = normalizeDOBinding(rawDoInfo);
|
|
1424
|
-
if (doInfo.__ref) {
|
|
1425
|
-
continue;
|
|
1426
|
-
}
|
|
1427
|
-
let scriptPath;
|
|
1428
|
-
if (doInfo.scriptName) {
|
|
1429
|
-
scriptPath = join7(configDir6, "src", doInfo.scriptName);
|
|
1430
|
-
} else {
|
|
1431
|
-
const discoveredPath = classToFilePath.get(doInfo.className);
|
|
1432
|
-
if (!discoveredPath) {
|
|
1433
|
-
throw new Error(`Durable object ${name} (className: '${doInfo.className}') not found.
|
|
1434
|
-
Either:
|
|
1435
|
-
1. Set files.durableObjects pattern in config (e.g., 'src/do.*.ts')
|
|
1436
|
-
2. Use explicit scriptName: { className: '${doInfo.className}', scriptName: 'do.file.ts' }`);
|
|
1437
|
-
}
|
|
1438
|
-
scriptPath = discoveredPath;
|
|
1439
|
-
}
|
|
1440
|
-
doConfig[name] = doInfo.className;
|
|
1441
|
-
doInfos.push({ name, className: doInfo.className, scriptPath });
|
|
1442
|
-
}
|
|
1443
|
-
const virtualImports = [];
|
|
1444
|
-
const virtualExports = [];
|
|
1445
|
-
if (transportFile) {
|
|
1446
|
-
const transportPath = join7(configDir6, transportFile);
|
|
1447
|
-
virtualImports.push(`import { transport } from '${transportPath.replace(/\\/g, "/")}'`);
|
|
1448
|
-
virtualExports.push("export { transport }");
|
|
1449
|
-
}
|
|
1450
|
-
for (const info of doInfos) {
|
|
1451
|
-
virtualImports.push(`import { ${info.className} } from '${info.scriptPath.replace(/\\/g, "/")}'`);
|
|
1452
|
-
virtualExports.push(`export { ${info.className} }`);
|
|
1453
|
-
}
|
|
1454
|
-
let bundledCode = "";
|
|
1455
|
-
if (virtualImports.length > 0) {
|
|
1456
|
-
const virtualEntry = [...virtualImports, "", ...virtualExports].join(`
|
|
1457
|
-
`);
|
|
1458
|
-
const virtualPath = join7(configDir6, ".devflare", "__test_entry.ts");
|
|
1459
|
-
const { writeFileSync, mkdirSync } = await import("fs");
|
|
1460
|
-
mkdirSync(dirname2(virtualPath), { recursive: true });
|
|
1461
|
-
writeFileSync(virtualPath, virtualEntry);
|
|
1462
|
-
const bun = getBunRuntime2();
|
|
1463
|
-
if (!bun) {
|
|
1464
|
-
throw new Error("Bun runtime is required for createTestContext with Durable Objects");
|
|
1465
|
-
}
|
|
1466
|
-
const result = await bun.build({
|
|
1467
|
-
entrypoints: [virtualPath],
|
|
1468
|
-
target: "browser",
|
|
1469
|
-
format: "esm",
|
|
1470
|
-
minify: false,
|
|
1471
|
-
external: ["cloudflare:workers", "cloudflare:*"]
|
|
1472
|
-
});
|
|
1473
|
-
if (!result.success) {
|
|
1474
|
-
throw new Error(`Failed to bundle test entry: ${result.logs.join(`
|
|
1475
|
-
`)}`);
|
|
1476
|
-
}
|
|
1477
|
-
bundledCode = await result.outputs[0].text();
|
|
1478
|
-
}
|
|
1479
|
-
mfConfig.durableObjects = doConfig;
|
|
1480
|
-
mfConfig.script = buildGatewayScript(bundledCode, "");
|
|
1481
|
-
} else {
|
|
1482
|
-
mfConfig.script = buildGatewayScript("", "");
|
|
1875
|
+
const gateway = await buildDurableObjectGateway(config, configDir6, transportFile);
|
|
1876
|
+
mfConfig.script = gateway.script;
|
|
1877
|
+
if (gateway.durableObjects) {
|
|
1878
|
+
mfConfig.durableObjects = gateway.durableObjects;
|
|
1483
1879
|
}
|
|
1484
1880
|
const hasMultiWorkerServices = serviceBindingResolution && serviceBindingResolution.workers.length > 0;
|
|
1485
1881
|
const hasMultiWorkerDOs = doBindingResolution && doBindingResolution.workers.length > 0;
|
|
@@ -1508,14 +1904,14 @@ Either:
|
|
|
1508
1904
|
for (const worker2 of additionalWorkers) {
|
|
1509
1905
|
if (!workersByName.has(worker2.name)) {
|
|
1510
1906
|
workersByName.set(worker2.name, worker2);
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
}
|
|
1907
|
+
continue;
|
|
1908
|
+
}
|
|
1909
|
+
const existing = workersByName.get(worker2.name);
|
|
1910
|
+
if (worker2.durableObjects) {
|
|
1911
|
+
existing.durableObjects = {
|
|
1912
|
+
...existing.durableObjects || {},
|
|
1913
|
+
...worker2.durableObjects
|
|
1914
|
+
};
|
|
1519
1915
|
}
|
|
1520
1916
|
}
|
|
1521
1917
|
const workers = [primaryWorker, ...workersByName.values()];
|
|
@@ -1527,10 +1923,23 @@ Either:
|
|
|
1527
1923
|
delete mfConfig.durableObjects;
|
|
1528
1924
|
mfConfig.workers = workers;
|
|
1529
1925
|
}
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1926
|
+
let activePort;
|
|
1927
|
+
if (hasMultiWorkerServices || hasMultiWorkerDOs) {
|
|
1928
|
+
const { Miniflare } = await import("miniflare");
|
|
1929
|
+
activePort = await getAvailablePort();
|
|
1930
|
+
globalMiniflare = new Miniflare({
|
|
1931
|
+
...mfConfig,
|
|
1932
|
+
port: activePort
|
|
1933
|
+
});
|
|
1934
|
+
await globalMiniflare.ready;
|
|
1935
|
+
globalMiniflareBindings = wrapEnvSendEmailBindings(await globalMiniflare.getBindings());
|
|
1936
|
+
} else {
|
|
1937
|
+
const startedBridgeBackedTestContext = await startBridgeBackedTestContext(mfConfig);
|
|
1938
|
+
activePort = startedBridgeBackedTestContext.port;
|
|
1939
|
+
globalMiniflare = startedBridgeBackedTestContext.miniflare;
|
|
1940
|
+
globalMiniflareBindings = startedBridgeBackedTestContext.miniflareBindings;
|
|
1941
|
+
globalClient = startedBridgeBackedTestContext.client;
|
|
1942
|
+
}
|
|
1534
1943
|
const disposeContext = async () => {
|
|
1535
1944
|
if (globalClient) {
|
|
1536
1945
|
await globalClient.disconnect();
|
|
@@ -1583,11 +1992,13 @@ Either:
|
|
|
1583
1992
|
const DEFAULT_EMAIL_PATH = "src/email.ts";
|
|
1584
1993
|
const DEFAULT_TAIL_PATH = "src/tail.ts";
|
|
1585
1994
|
const resolvePath = async (configValue, defaultPath) => {
|
|
1586
|
-
if (typeof configValue === "string")
|
|
1995
|
+
if (typeof configValue === "string") {
|
|
1587
1996
|
return configValue;
|
|
1588
|
-
|
|
1997
|
+
}
|
|
1998
|
+
if (configValue === false) {
|
|
1589
1999
|
return null;
|
|
1590
|
-
|
|
2000
|
+
}
|
|
2001
|
+
const defaultAbsolute = join9(configDir6, defaultPath);
|
|
1591
2002
|
try {
|
|
1592
2003
|
const fs = await import("fs/promises");
|
|
1593
2004
|
await fs.access(defaultAbsolute);
|
|
@@ -1630,7 +2041,7 @@ Either:
|
|
|
1630
2041
|
getEnv: getTestEnv
|
|
1631
2042
|
});
|
|
1632
2043
|
configureEmail({
|
|
1633
|
-
port:
|
|
2044
|
+
port: activePort,
|
|
1634
2045
|
handlerPath: resolvedEmailPath,
|
|
1635
2046
|
configDir: configDir6,
|
|
1636
2047
|
getEnv: getTestEnv
|
|
@@ -1654,29 +2065,31 @@ Either:
|
|
|
1654
2065
|
__setTestContext(envAccessor2, disposeContext);
|
|
1655
2066
|
return;
|
|
1656
2067
|
}
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
2068
|
+
const bridgeClient = globalClient;
|
|
2069
|
+
if (!bridgeClient) {
|
|
2070
|
+
throw new Error("Bridge-backed test context did not initialize a client.");
|
|
2071
|
+
}
|
|
1661
2072
|
setBindingHints(hints);
|
|
1662
2073
|
globalEnvProxy = createEnvProxy({
|
|
1663
|
-
client:
|
|
2074
|
+
client: bridgeClient,
|
|
1664
2075
|
transformResult: (result) => decodeTransport(result)
|
|
1665
2076
|
});
|
|
1666
2077
|
const envAccessor = new Proxy({}, {
|
|
1667
2078
|
get(_, prop) {
|
|
2079
|
+
const hint = hints[prop];
|
|
2080
|
+
const prefersBridgeBinding = shouldPreferBridgeBinding(hint);
|
|
1668
2081
|
if (globalRemoteBindings && prop in globalRemoteBindings) {
|
|
1669
2082
|
return globalRemoteBindings[prop];
|
|
1670
2083
|
}
|
|
1671
|
-
if (
|
|
1672
|
-
return globalEnvProxy[prop];
|
|
1673
|
-
}
|
|
1674
|
-
if (globalMiniflareBindings && prop in globalMiniflareBindings) {
|
|
2084
|
+
if (!prefersBridgeBinding && globalMiniflareBindings && prop in globalMiniflareBindings) {
|
|
1675
2085
|
return globalMiniflareBindings[prop];
|
|
1676
2086
|
}
|
|
1677
2087
|
if (globalEnvProxy) {
|
|
1678
2088
|
return globalEnvProxy[prop];
|
|
1679
2089
|
}
|
|
2090
|
+
if (prefersBridgeBinding && globalMiniflareBindings && prop in globalMiniflareBindings) {
|
|
2091
|
+
return globalMiniflareBindings[prop];
|
|
2092
|
+
}
|
|
1680
2093
|
return;
|
|
1681
2094
|
},
|
|
1682
2095
|
has(_, prop) {
|
|
@@ -1705,184 +2118,6 @@ function decodeTransport(value) {
|
|
|
1705
2118
|
}
|
|
1706
2119
|
return result;
|
|
1707
2120
|
}
|
|
1708
|
-
function buildGatewayScript(bundledCode, wrappers) {
|
|
1709
|
-
return `
|
|
1710
|
-
// Bundled transport + DO classes
|
|
1711
|
-
${bundledCode}
|
|
1712
|
-
|
|
1713
|
-
// DO Wrappers with RPC
|
|
1714
|
-
${wrappers}
|
|
1715
|
-
|
|
1716
|
-
// Transport encoding helper
|
|
1717
|
-
const __transportEncoders = typeof transport !== 'undefined' ? transport : {}
|
|
1718
|
-
|
|
1719
|
-
function __encodeTransport(value) {
|
|
1720
|
-
if (value === null || value === undefined) return value
|
|
1721
|
-
|
|
1722
|
-
// Try each encoder
|
|
1723
|
-
for (const [typeName, transporter] of Object.entries(__transportEncoders)) {
|
|
1724
|
-
const encoded = transporter.encode(value)
|
|
1725
|
-
if (encoded !== false && encoded !== undefined) {
|
|
1726
|
-
return { __transport: typeName, value: encoded }
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
|
|
1730
|
-
// Recursively encode arrays and objects
|
|
1731
|
-
if (Array.isArray(value)) {
|
|
1732
|
-
return value.map(__encodeTransport)
|
|
1733
|
-
}
|
|
1734
|
-
if (typeof value === 'object') {
|
|
1735
|
-
const result = {}
|
|
1736
|
-
for (const [k, v] of Object.entries(value)) {
|
|
1737
|
-
result[k] = __encodeTransport(v)
|
|
1738
|
-
}
|
|
1739
|
-
return result
|
|
1740
|
-
}
|
|
1741
|
-
|
|
1742
|
-
return value
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1745
|
-
// Gateway with WebSocket RPC
|
|
1746
|
-
export default {
|
|
1747
|
-
async fetch(request, env) {
|
|
1748
|
-
if (request.headers.get('Upgrade') === 'websocket') {
|
|
1749
|
-
const { 0: client, 1: server } = new WebSocketPair()
|
|
1750
|
-
server.accept()
|
|
1751
|
-
server.addEventListener('message', async (e) => {
|
|
1752
|
-
try {
|
|
1753
|
-
const m = JSON.parse(e.data)
|
|
1754
|
-
if (m.t === 'rpc.call') {
|
|
1755
|
-
const result = await executeRpc(env, m.method, m.params)
|
|
1756
|
-
server.send(JSON.stringify({ t: 'rpc.ok', id: m.id, result }))
|
|
1757
|
-
}
|
|
1758
|
-
} catch (error) {
|
|
1759
|
-
server.send(JSON.stringify({ t: 'rpc.err', id: 'unknown', error: { code: 'RPC_ERROR', message: error.message } }))
|
|
1760
|
-
}
|
|
1761
|
-
})
|
|
1762
|
-
return new Response(null, { status: 101, webSocket: client })
|
|
1763
|
-
}
|
|
1764
|
-
return new Response('Gateway')
|
|
1765
|
-
}
|
|
1766
|
-
}
|
|
1767
|
-
|
|
1768
|
-
async function executeRpc(env, method, params) {
|
|
1769
|
-
const [bindingName, ...rest] = method.split('.')
|
|
1770
|
-
const op = rest.join('.')
|
|
1771
|
-
const binding = env[bindingName]
|
|
1772
|
-
const RAW_EMAIL = 'EmailMessage::raw'
|
|
1773
|
-
if (!binding) throw new Error('Binding not found: ' + bindingName)
|
|
1774
|
-
|
|
1775
|
-
// KV operations
|
|
1776
|
-
if (op === 'get') return binding.get(params[0], params[1])
|
|
1777
|
-
if (op === 'put') return binding.put(params[0], params[1], params[2])
|
|
1778
|
-
if (op === 'delete') return binding.delete(params[0])
|
|
1779
|
-
if (op === 'list') return binding.list(params[0])
|
|
1780
|
-
if (op === 'getWithMetadata') return binding.getWithMetadata(params[0], params[1])
|
|
1781
|
-
|
|
1782
|
-
// R2 operations
|
|
1783
|
-
if (op === 'r2.get') return binding.get(params[0], params[1])
|
|
1784
|
-
if (op === 'r2.put') return binding.put(params[0], params[1], params[2])
|
|
1785
|
-
if (op === 'r2.delete') return binding.delete(params[0])
|
|
1786
|
-
if (op === 'r2.list') return binding.list(params[0])
|
|
1787
|
-
if (op === 'head') return binding.head(params[0])
|
|
1788
|
-
|
|
1789
|
-
// D1 operations
|
|
1790
|
-
if (op === 'exec') return binding.exec(params[0])
|
|
1791
|
-
if (op === 'dump') return binding.dump()
|
|
1792
|
-
if (op === 'batch') {
|
|
1793
|
-
const stmts = params[0].map(s => {
|
|
1794
|
-
const stmt = binding.prepare(s.sql)
|
|
1795
|
-
return s.bindings?.length ? stmt.bind(...s.bindings) : stmt
|
|
1796
|
-
})
|
|
1797
|
-
return binding.batch(stmts)
|
|
1798
|
-
}
|
|
1799
|
-
if (op === 'prepare.run') return binding.prepare(params[0]).bind(...(params[1] || [])).run()
|
|
1800
|
-
if (op === 'prepare.all') return binding.prepare(params[0]).bind(...(params[1] || [])).all()
|
|
1801
|
-
if (op === 'prepare.first') return binding.prepare(params[0]).bind(...(params[1] || [])).first(params[2])
|
|
1802
|
-
if (op === 'prepare.raw') return binding.prepare(params[0]).bind(...(params[1] || [])).raw({ columnNames: params[2] })
|
|
1803
|
-
|
|
1804
|
-
// Send email operations
|
|
1805
|
-
if (op === 'email.send') {
|
|
1806
|
-
return binding.send(__normalizeEmailMessage(params[0]))
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
|
-
// DO operations
|
|
1810
|
-
if (op === 'idFromName') {
|
|
1811
|
-
return { __type: 'DOId', hex: binding.idFromName(params[0]).toString() }
|
|
1812
|
-
}
|
|
1813
|
-
if (op === 'stub.rpc') {
|
|
1814
|
-
const [, idSerialized, rpcMethod, rpcParams] = params
|
|
1815
|
-
const stub = binding.get(binding.idFromString(idSerialized.hex))
|
|
1816
|
-
|
|
1817
|
-
// Use native RPC: call method directly on stub
|
|
1818
|
-
// This works for DOs extending DurableObject from cloudflare:workers
|
|
1819
|
-
let result = await stub[rpcMethod](...(rpcParams || []))
|
|
1820
|
-
// Apply transport encoding
|
|
1821
|
-
result = __encodeTransport(result)
|
|
1822
|
-
return result
|
|
1823
|
-
}
|
|
1824
|
-
|
|
1825
|
-
throw new Error('Unknown operation: ' + method)
|
|
1826
|
-
}
|
|
1827
|
-
|
|
1828
|
-
function __createEmailMessageRaw(raw) {
|
|
1829
|
-
if (typeof raw === 'string' || raw instanceof ReadableStream) {
|
|
1830
|
-
return raw
|
|
1831
|
-
}
|
|
1832
|
-
if (raw instanceof Uint8Array || raw instanceof ArrayBuffer) {
|
|
1833
|
-
return new Response(raw).body
|
|
1834
|
-
}
|
|
1835
|
-
throw new Error('Unsupported EmailMessage raw payload')
|
|
1836
|
-
}
|
|
1837
|
-
|
|
1838
|
-
function __buildRawEmail(message) {
|
|
1839
|
-
const lines = []
|
|
1840
|
-
const messageId = '<' + Date.now() + '-' + Math.random().toString(36).slice(2) + '@devflare.dev>'
|
|
1841
|
-
|
|
1842
|
-
lines.push('From: ' + message.from)
|
|
1843
|
-
lines.push('To: ' + (Array.isArray(message.to) ? message.to.join(', ') : message.to))
|
|
1844
|
-
lines.push('Date: ' + new Date().toUTCString())
|
|
1845
|
-
lines.push('Message-ID: ' + messageId)
|
|
1846
|
-
|
|
1847
|
-
if (message.subject) lines.push('Subject: ' + message.subject)
|
|
1848
|
-
if (message.replyTo) lines.push('Reply-To: ' + String(message.replyTo))
|
|
1849
|
-
if (message.cc) lines.push('Cc: ' + (Array.isArray(message.cc) ? message.cc.join(', ') : message.cc))
|
|
1850
|
-
if (message.bcc) lines.push('Bcc: ' + (Array.isArray(message.bcc) ? message.bcc.join(', ') : message.bcc))
|
|
1851
|
-
|
|
1852
|
-
for (const [key, value] of Object.entries(message.headers || {})) {
|
|
1853
|
-
lines.push(key + ': ' + value)
|
|
1854
|
-
}
|
|
1855
|
-
|
|
1856
|
-
lines.push('MIME-Version: 1.0')
|
|
1857
|
-
lines.push('Content-Type: ' + (message.html ? 'text/html' : 'text/plain') + '; charset=UTF-8')
|
|
1858
|
-
lines.push('')
|
|
1859
|
-
lines.push(String(message.html ?? message.text ?? '').replace(/\\r?\\n/g, '\\r\\n'))
|
|
1860
|
-
|
|
1861
|
-
return lines.join('\\r\\n')
|
|
1862
|
-
}
|
|
1863
|
-
|
|
1864
|
-
function __normalizeEmailMessage(message) {
|
|
1865
|
-
if (!message || typeof message !== 'object' || !('from' in message) || !('to' in message)) {
|
|
1866
|
-
return message
|
|
1867
|
-
}
|
|
1868
|
-
if ('EmailMessage::raw' in message) {
|
|
1869
|
-
return message
|
|
1870
|
-
}
|
|
1871
|
-
if ('raw' in message && message.raw !== undefined) {
|
|
1872
|
-
return {
|
|
1873
|
-
from: message.from,
|
|
1874
|
-
to: message.to,
|
|
1875
|
-
[RAW_EMAIL]: __createEmailMessageRaw(message.raw)
|
|
1876
|
-
}
|
|
1877
|
-
}
|
|
1878
|
-
return {
|
|
1879
|
-
from: message.from,
|
|
1880
|
-
to: message.to,
|
|
1881
|
-
[RAW_EMAIL]: __createEmailMessageRaw(__buildRawEmail(message))
|
|
1882
|
-
}
|
|
1883
|
-
}
|
|
1884
|
-
`;
|
|
1885
|
-
}
|
|
1886
2121
|
// src/test/cf.ts
|
|
1887
2122
|
var cf = {
|
|
1888
2123
|
email,
|
|
@@ -1891,71 +2126,11 @@ var cf = {
|
|
|
1891
2126
|
worker,
|
|
1892
2127
|
tail
|
|
1893
2128
|
};
|
|
1894
|
-
// src/test/multi-worker-context.ts
|
|
1895
|
-
async function createMultiWorkerContext(options) {
|
|
1896
|
-
const { workers, primary } = options;
|
|
1897
|
-
const primaryName = primary ?? workers[0]?.name;
|
|
1898
|
-
if (!primaryName) {
|
|
1899
|
-
throw new Error("At least one worker must be configured");
|
|
1900
|
-
}
|
|
1901
|
-
const { Miniflare } = await import("miniflare");
|
|
1902
|
-
const mfWorkers = workers.map((worker2) => {
|
|
1903
|
-
const serviceBindings = {};
|
|
1904
|
-
if (worker2.serviceBindings) {
|
|
1905
|
-
for (const [bindingName, target] of Object.entries(worker2.serviceBindings)) {
|
|
1906
|
-
if (typeof target === "string") {
|
|
1907
|
-
serviceBindings[bindingName] = { name: target };
|
|
1908
|
-
} else {
|
|
1909
|
-
serviceBindings[bindingName] = target;
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
return {
|
|
1914
|
-
name: worker2.name,
|
|
1915
|
-
modules: worker2.modules ?? true,
|
|
1916
|
-
script: worker2.script,
|
|
1917
|
-
compatibilityDate: worker2.compatibilityDate ?? "2025-01-01",
|
|
1918
|
-
...Object.keys(serviceBindings).length > 0 ? { serviceBindings } : {}
|
|
1919
|
-
};
|
|
1920
|
-
});
|
|
1921
|
-
const mf = new Miniflare({
|
|
1922
|
-
workers: mfWorkers
|
|
1923
|
-
});
|
|
1924
|
-
const env2 = await mf.getBindings();
|
|
1925
|
-
const fetch3 = async (input, init) => {
|
|
1926
|
-
const response = await mf.dispatchFetch(input, init);
|
|
1927
|
-
return response;
|
|
1928
|
-
};
|
|
1929
|
-
const dispose = async () => {
|
|
1930
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
1931
|
-
await mf.dispose();
|
|
1932
|
-
};
|
|
1933
|
-
return {
|
|
1934
|
-
mf,
|
|
1935
|
-
env: env2,
|
|
1936
|
-
fetch: fetch3,
|
|
1937
|
-
dispose
|
|
1938
|
-
};
|
|
1939
|
-
}
|
|
1940
|
-
function createEntrypointScript(className, methods) {
|
|
1941
|
-
const methodDefs = Object.entries(methods).map(([name, body]) => ` ${name}${body}`).join(`
|
|
1942
|
-
|
|
1943
|
-
`);
|
|
1944
|
-
return `import { WorkerEntrypoint } from 'cloudflare:workers'
|
|
1945
|
-
|
|
1946
|
-
export class ${className} extends WorkerEntrypoint {
|
|
1947
|
-
${methodDefs}
|
|
1948
|
-
}
|
|
1949
|
-
`;
|
|
1950
|
-
}
|
|
1951
2129
|
// src/test/should-skip.ts
|
|
1952
2130
|
var REMOTE_ONLY_SERVICES = new Set([
|
|
1953
2131
|
"ai",
|
|
1954
2132
|
"vectorize"
|
|
1955
2133
|
]);
|
|
1956
|
-
function isRemoteModeEnabled() {
|
|
1957
|
-
return isRemoteModeActive();
|
|
1958
|
-
}
|
|
1959
2134
|
var skipResults = new Map;
|
|
1960
2135
|
var EXPECTED_ERROR_PATTERNS = [
|
|
1961
2136
|
"ECONNREFUSED",
|
|
@@ -2471,4 +2646,4 @@ var testEnv = new Proxy({}, {
|
|
|
2471
2646
|
function isKVNamespace(binding) {
|
|
2472
2647
|
return typeof binding === "object" && binding !== null && "get" in binding && "put" in binding && "delete" in binding && "list" in binding;
|
|
2473
2648
|
}
|
|
2474
|
-
export { clearBundleCache, hasServiceBindings, resolveServiceBindings, hasCrossWorkerDOs, resolveDOBindings, queue, scheduled,
|
|
2649
|
+
export { clearBundleCache, hasServiceBindings, resolveServiceBindings, hasCrossWorkerDOs, resolveDOBindings, email, queue, scheduled, tail, worker, createTestContext, cf, shouldSkip, createMockTestContext, withTestContext, createMockKV, createMockD1, createMockR2, createMockQueue, createMockEnv, createBridgeTestContext, stopBridgeTestContext, getBridgeTestContext, testEnv };
|