flingit 0.0.63 → 0.0.65
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/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +9 -5
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/workflow.d.ts +51 -0
- package/dist/cli/commands/workflow.d.ts.map +1 -0
- package/dist/cli/commands/workflow.js +479 -0
- package/dist/cli/commands/workflow.js.map +1 -0
- package/dist/cli/deploy/bundler.d.ts.map +1 -1
- package/dist/cli/deploy/bundler.js +17 -1
- package/dist/cli/deploy/bundler.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/entry.d.ts +7 -0
- package/dist/runtime/entry.d.ts.map +1 -1
- package/dist/runtime/entry.js +71 -2
- package/dist/runtime/entry.js.map +1 -1
- package/dist/types/workflow.d.ts +82 -0
- package/dist/types/workflow.d.ts.map +1 -0
- package/dist/types/workflow.js +8 -0
- package/dist/types/workflow.js.map +1 -0
- package/dist/worker-runtime/d1-event-store.d.ts +22 -0
- package/dist/worker-runtime/d1-event-store.d.ts.map +1 -0
- package/dist/worker-runtime/d1-event-store.js +227 -0
- package/dist/worker-runtime/d1-event-store.js.map +1 -0
- package/dist/worker-runtime/entry-extract.d.ts.map +1 -1
- package/dist/worker-runtime/entry-extract.js +6 -3
- package/dist/worker-runtime/entry-extract.js.map +1 -1
- package/dist/worker-runtime/entry.d.ts.map +1 -1
- package/dist/worker-runtime/entry.js +105 -3
- package/dist/worker-runtime/entry.js.map +1 -1
- package/dist/worker-runtime/index.d.ts +2 -0
- package/dist/worker-runtime/index.d.ts.map +1 -1
- package/dist/worker-runtime/index.js +5 -0
- package/dist/worker-runtime/index.js.map +1 -1
- package/dist/workflow/runtime.d.ts +56 -0
- package/dist/workflow/runtime.d.ts.map +1 -0
- package/dist/workflow/runtime.js +450 -0
- package/dist/workflow/runtime.js.map +1 -0
- package/node_modules/@glideapps/ts-necessities/LICENSE +21 -0
- package/node_modules/@glideapps/ts-necessities/README.md +16 -0
- package/node_modules/@glideapps/ts-necessities/dist/branded-strings.d.ts +39 -0
- package/node_modules/@glideapps/ts-necessities/dist/default-map.d.ts +53 -0
- package/node_modules/@glideapps/ts-necessities/dist/index.d.mts +113 -0
- package/node_modules/@glideapps/ts-necessities/dist/index.d.ts +113 -0
- package/node_modules/@glideapps/ts-necessities/dist/index.js +237 -0
- package/node_modules/@glideapps/ts-necessities/dist/index.mjs +189 -0
- package/node_modules/@glideapps/ts-necessities/package.json +52 -0
- package/node_modules/@rollup/rollup-linux-x64-gnu/README.md +3 -0
- package/node_modules/@rollup/rollup-linux-x64-gnu/package.json +25 -0
- package/node_modules/@rollup/rollup-linux-x64-gnu/rollup.linux-x64-gnu.node +0 -0
- package/node_modules/base64-js/LICENSE +21 -0
- package/node_modules/base64-js/README.md +34 -0
- package/node_modules/base64-js/base64js.min.js +1 -0
- package/node_modules/base64-js/index.d.ts +3 -0
- package/node_modules/base64-js/index.js +150 -0
- package/node_modules/base64-js/package.json +47 -0
- package/node_modules/better-sqlite3/LICENSE +21 -0
- package/node_modules/better-sqlite3/README.md +99 -0
- package/node_modules/better-sqlite3/binding.gyp +38 -0
- package/node_modules/better-sqlite3/deps/common.gypi +68 -0
- package/node_modules/better-sqlite3/deps/copy.js +31 -0
- package/node_modules/better-sqlite3/deps/defines.gypi +41 -0
- package/node_modules/better-sqlite3/deps/download.sh +122 -0
- package/node_modules/better-sqlite3/deps/patches/1208.patch +15 -0
- package/node_modules/better-sqlite3/deps/sqlite3/sqlite3.c +265994 -0
- package/node_modules/better-sqlite3/deps/sqlite3/sqlite3.h +13968 -0
- package/node_modules/better-sqlite3/deps/sqlite3/sqlite3ext.h +730 -0
- package/node_modules/better-sqlite3/deps/sqlite3.gyp +80 -0
- package/node_modules/better-sqlite3/deps/test_extension.c +21 -0
- package/node_modules/better-sqlite3/lib/database.js +90 -0
- package/node_modules/better-sqlite3/lib/index.js +3 -0
- package/node_modules/better-sqlite3/lib/methods/aggregate.js +43 -0
- package/node_modules/better-sqlite3/lib/methods/backup.js +67 -0
- package/node_modules/better-sqlite3/lib/methods/function.js +31 -0
- package/node_modules/better-sqlite3/lib/methods/inspect.js +7 -0
- package/node_modules/better-sqlite3/lib/methods/pragma.js +12 -0
- package/node_modules/better-sqlite3/lib/methods/serialize.js +16 -0
- package/node_modules/better-sqlite3/lib/methods/table.js +189 -0
- package/node_modules/better-sqlite3/lib/methods/transaction.js +78 -0
- package/node_modules/better-sqlite3/lib/methods/wrappers.js +54 -0
- package/node_modules/better-sqlite3/lib/sqlite-error.js +20 -0
- package/node_modules/better-sqlite3/lib/util.js +12 -0
- package/node_modules/better-sqlite3/package.json +59 -0
- package/node_modules/better-sqlite3/src/addon.cpp +47 -0
- package/node_modules/better-sqlite3/src/better_sqlite3.cpp +74 -0
- package/node_modules/better-sqlite3/src/objects/backup.cpp +120 -0
- package/node_modules/better-sqlite3/src/objects/backup.hpp +36 -0
- package/node_modules/better-sqlite3/src/objects/database.cpp +417 -0
- package/node_modules/better-sqlite3/src/objects/database.hpp +103 -0
- package/node_modules/better-sqlite3/src/objects/statement-iterator.cpp +113 -0
- package/node_modules/better-sqlite3/src/objects/statement-iterator.hpp +50 -0
- package/node_modules/better-sqlite3/src/objects/statement.cpp +383 -0
- package/node_modules/better-sqlite3/src/objects/statement.hpp +58 -0
- package/node_modules/better-sqlite3/src/util/bind-map.cpp +73 -0
- package/node_modules/better-sqlite3/src/util/binder.cpp +193 -0
- package/node_modules/better-sqlite3/src/util/constants.cpp +172 -0
- package/node_modules/better-sqlite3/src/util/custom-aggregate.cpp +121 -0
- package/node_modules/better-sqlite3/src/util/custom-function.cpp +59 -0
- package/node_modules/better-sqlite3/src/util/custom-table.cpp +409 -0
- package/node_modules/better-sqlite3/src/util/data-converter.cpp +17 -0
- package/node_modules/better-sqlite3/src/util/data.cpp +194 -0
- package/node_modules/better-sqlite3/src/util/helpers.cpp +109 -0
- package/node_modules/better-sqlite3/src/util/macros.cpp +83 -0
- package/node_modules/better-sqlite3/src/util/query-macros.cpp +71 -0
- package/node_modules/better-sqlite3/src/util/row-builder.cpp +49 -0
- package/node_modules/bindings/LICENSE.md +22 -0
- package/node_modules/bindings/README.md +98 -0
- package/node_modules/bindings/bindings.js +221 -0
- package/node_modules/bindings/package.json +28 -0
- package/node_modules/bl/.travis.yml +17 -0
- package/node_modules/bl/BufferList.js +396 -0
- package/node_modules/bl/LICENSE.md +13 -0
- package/node_modules/bl/README.md +247 -0
- package/node_modules/bl/bl.js +84 -0
- package/node_modules/bl/package.json +37 -0
- package/node_modules/bl/test/convert.js +21 -0
- package/node_modules/bl/test/indexOf.js +492 -0
- package/node_modules/bl/test/isBufferList.js +32 -0
- package/node_modules/bl/test/test.js +869 -0
- package/node_modules/buffer/AUTHORS.md +70 -0
- package/node_modules/buffer/LICENSE +21 -0
- package/node_modules/buffer/README.md +410 -0
- package/node_modules/buffer/index.d.ts +186 -0
- package/node_modules/buffer/index.js +1817 -0
- package/node_modules/buffer/package.json +96 -0
- package/node_modules/chownr/LICENSE +15 -0
- package/node_modules/chownr/README.md +3 -0
- package/node_modules/chownr/chownr.js +167 -0
- package/node_modules/chownr/package.json +29 -0
- package/node_modules/decompress-response/index.d.ts +22 -0
- package/node_modules/decompress-response/index.js +58 -0
- package/node_modules/decompress-response/license +9 -0
- package/node_modules/decompress-response/package.json +56 -0
- package/node_modules/decompress-response/readme.md +48 -0
- package/node_modules/deep-extend/LICENSE +20 -0
- package/node_modules/deep-extend/README.md +91 -0
- package/node_modules/deep-extend/index.js +1 -0
- package/node_modules/deep-extend/lib/deep-extend.js +150 -0
- package/node_modules/deep-extend/package.json +62 -0
- package/node_modules/detect-libc/LICENSE +201 -0
- package/node_modules/detect-libc/README.md +163 -0
- package/node_modules/detect-libc/index.d.ts +14 -0
- package/node_modules/detect-libc/lib/detect-libc.js +313 -0
- package/node_modules/detect-libc/lib/elf.js +39 -0
- package/node_modules/detect-libc/lib/filesystem.js +51 -0
- package/node_modules/detect-libc/lib/process.js +24 -0
- package/node_modules/detect-libc/package.json +44 -0
- package/node_modules/determined/LICENSE +18 -0
- package/node_modules/determined/README.md +317 -0
- package/node_modules/determined/dist/index.cjs +370 -0
- package/node_modules/determined/dist/index.d.cts +110 -0
- package/node_modules/determined/dist/index.d.ts +110 -0
- package/node_modules/determined/dist/index.js +332 -0
- package/node_modules/determined/package.json +45 -0
- package/node_modules/end-of-stream/LICENSE +21 -0
- package/node_modules/end-of-stream/README.md +54 -0
- package/node_modules/end-of-stream/index.js +96 -0
- package/node_modules/end-of-stream/package.json +37 -0
- package/node_modules/expand-template/.travis.yml +6 -0
- package/node_modules/expand-template/LICENSE +21 -0
- package/node_modules/expand-template/README.md +43 -0
- package/node_modules/expand-template/index.js +26 -0
- package/node_modules/expand-template/package.json +29 -0
- package/node_modules/expand-template/test.js +67 -0
- package/node_modules/file-uri-to-path/.npmignore +1 -0
- package/node_modules/file-uri-to-path/.travis.yml +30 -0
- package/node_modules/file-uri-to-path/History.md +21 -0
- package/node_modules/file-uri-to-path/LICENSE +20 -0
- package/node_modules/file-uri-to-path/README.md +74 -0
- package/node_modules/file-uri-to-path/index.d.ts +2 -0
- package/node_modules/file-uri-to-path/index.js +66 -0
- package/node_modules/file-uri-to-path/package.json +32 -0
- package/node_modules/file-uri-to-path/test/test.js +24 -0
- package/node_modules/file-uri-to-path/test/tests.json +13 -0
- package/node_modules/flingflow/.nvmrc +1 -0
- package/node_modules/flingflow/AGENTS.md +5 -0
- package/node_modules/flingflow/README.md +679 -0
- package/node_modules/flingflow/SPEC.md +554 -0
- package/node_modules/flingflow/TESTING.md +506 -0
- package/node_modules/flingflow/dist/backoff.d.ts +9 -0
- package/node_modules/flingflow/dist/backoff.js +14 -0
- package/node_modules/flingflow/dist/backoff.js.map +1 -0
- package/node_modules/flingflow/dist/clock.d.ts +13 -0
- package/node_modules/flingflow/dist/clock.js +21 -0
- package/node_modules/flingflow/dist/clock.js.map +1 -0
- package/node_modules/flingflow/dist/context.d.ts +8 -0
- package/node_modules/flingflow/dist/context.js +45 -0
- package/node_modules/flingflow/dist/context.js.map +1 -0
- package/node_modules/flingflow/dist/engine.d.ts +29 -0
- package/node_modules/flingflow/dist/engine.js +306 -0
- package/node_modules/flingflow/dist/engine.js.map +1 -0
- package/node_modules/flingflow/dist/index.d.ts +18 -0
- package/node_modules/flingflow/dist/index.js +14 -0
- package/node_modules/flingflow/dist/index.js.map +1 -0
- package/node_modules/flingflow/dist/recovery.d.ts +16 -0
- package/node_modules/flingflow/dist/recovery.js +118 -0
- package/node_modules/flingflow/dist/recovery.js.map +1 -0
- package/node_modules/flingflow/dist/registry.d.ts +24 -0
- package/node_modules/flingflow/dist/registry.js +29 -0
- package/node_modules/flingflow/dist/registry.js.map +1 -0
- package/node_modules/flingflow/dist/store-memory.d.ts +29 -0
- package/node_modules/flingflow/dist/store-memory.js +349 -0
- package/node_modules/flingflow/dist/store-memory.js.map +1 -0
- package/node_modules/flingflow/dist/store-sqlite.d.ts +30 -0
- package/node_modules/flingflow/dist/store-sqlite.js +400 -0
- package/node_modules/flingflow/dist/store-sqlite.js.map +1 -0
- package/node_modules/flingflow/dist/store.d.ts +39 -0
- package/node_modules/flingflow/dist/store.js +21 -0
- package/node_modules/flingflow/dist/store.js.map +1 -0
- package/node_modules/flingflow/dist/stress.d.ts +1 -0
- package/node_modules/flingflow/dist/stress.js +377 -0
- package/node_modules/flingflow/dist/stress.js.map +1 -0
- package/node_modules/flingflow/dist/transitions.d.ts +14 -0
- package/node_modules/flingflow/dist/transitions.js +28 -0
- package/node_modules/flingflow/dist/transitions.js.map +1 -0
- package/node_modules/flingflow/dist/types.d.ts +114 -0
- package/node_modules/flingflow/dist/types.js +10 -0
- package/node_modules/flingflow/dist/types.js.map +1 -0
- package/node_modules/flingflow/eslint.config.js +94 -0
- package/node_modules/flingflow/package.json +66 -0
- package/node_modules/flingflow/src/backoff.ts +14 -0
- package/node_modules/flingflow/src/clock.ts +29 -0
- package/node_modules/flingflow/src/context.ts +60 -0
- package/node_modules/flingflow/src/engine.ts +367 -0
- package/node_modules/flingflow/src/index.ts +52 -0
- package/node_modules/flingflow/src/recovery.ts +144 -0
- package/node_modules/flingflow/src/registry.ts +52 -0
- package/node_modules/flingflow/src/store-memory.ts +378 -0
- package/node_modules/flingflow/src/store-sqlite.ts +451 -0
- package/node_modules/flingflow/src/store.ts +55 -0
- package/node_modules/flingflow/src/stress.ts +423 -0
- package/node_modules/flingflow/src/transitions.ts +38 -0
- package/node_modules/flingflow/src/types.ts +84 -0
- package/node_modules/flingflow/test/backoff.test.ts +54 -0
- package/node_modules/flingflow/test/context.test.ts +94 -0
- package/node_modules/flingflow/test/engine.test.ts +362 -0
- package/node_modules/flingflow/test/fixtures.ts +58 -0
- package/node_modules/flingflow/test/recovery.test.ts +176 -0
- package/node_modules/flingflow/test/simulation.test.ts +395 -0
- package/node_modules/flingflow/test/store-conformance.ts +344 -0
- package/node_modules/flingflow/test/store-memory.test.ts +8 -0
- package/node_modules/flingflow/test/store-sqlite.test.ts +8 -0
- package/node_modules/flingflow/tsconfig.json +18 -0
- package/node_modules/flingflow/tsconfig.lint.json +5 -0
- package/node_modules/flingflow/tsconfig.typecheck.json +20 -0
- package/node_modules/flingflow/vitest.config.ts +10 -0
- package/node_modules/fs-constants/LICENSE +21 -0
- package/node_modules/fs-constants/README.md +26 -0
- package/node_modules/fs-constants/browser.js +1 -0
- package/node_modules/fs-constants/index.js +1 -0
- package/node_modules/fs-constants/package.json +19 -0
- package/node_modules/github-from-package/.travis.yml +4 -0
- package/node_modules/github-from-package/LICENSE +18 -0
- package/node_modules/github-from-package/example/package.json +8 -0
- package/node_modules/github-from-package/example/url.js +3 -0
- package/node_modules/github-from-package/index.js +17 -0
- package/node_modules/github-from-package/package.json +30 -0
- package/node_modules/github-from-package/readme.markdown +53 -0
- package/node_modules/github-from-package/test/a.json +8 -0
- package/node_modules/github-from-package/test/b.json +5 -0
- package/node_modules/github-from-package/test/c.json +5 -0
- package/node_modules/github-from-package/test/d.json +7 -0
- package/node_modules/github-from-package/test/e.json +5 -0
- package/node_modules/github-from-package/test/url.js +19 -0
- package/node_modules/ieee754/LICENSE +11 -0
- package/node_modules/ieee754/README.md +51 -0
- package/node_modules/ieee754/index.d.ts +10 -0
- package/node_modules/ieee754/index.js +85 -0
- package/node_modules/ieee754/package.json +52 -0
- package/node_modules/inherits/LICENSE +16 -0
- package/node_modules/inherits/README.md +42 -0
- package/node_modules/inherits/inherits.js +9 -0
- package/node_modules/inherits/inherits_browser.js +27 -0
- package/node_modules/inherits/package.json +29 -0
- package/node_modules/ini/LICENSE +15 -0
- package/node_modules/ini/README.md +102 -0
- package/node_modules/ini/ini.js +206 -0
- package/node_modules/ini/package.json +33 -0
- package/node_modules/mimic-response/index.d.ts +17 -0
- package/node_modules/mimic-response/index.js +77 -0
- package/node_modules/mimic-response/license +9 -0
- package/node_modules/mimic-response/package.json +42 -0
- package/node_modules/mimic-response/readme.md +78 -0
- package/node_modules/minimist/.eslintrc +29 -0
- package/node_modules/minimist/.github/FUNDING.yml +12 -0
- package/node_modules/minimist/.nycrc +14 -0
- package/node_modules/minimist/CHANGELOG.md +298 -0
- package/node_modules/minimist/LICENSE +18 -0
- package/node_modules/minimist/README.md +121 -0
- package/node_modules/minimist/example/parse.js +4 -0
- package/node_modules/minimist/index.js +263 -0
- package/node_modules/minimist/package.json +75 -0
- package/node_modules/minimist/test/all_bool.js +34 -0
- package/node_modules/minimist/test/bool.js +177 -0
- package/node_modules/minimist/test/dash.js +43 -0
- package/node_modules/minimist/test/default_bool.js +37 -0
- package/node_modules/minimist/test/dotted.js +24 -0
- package/node_modules/minimist/test/kv_short.js +32 -0
- package/node_modules/minimist/test/long.js +33 -0
- package/node_modules/minimist/test/num.js +38 -0
- package/node_modules/minimist/test/parse.js +209 -0
- package/node_modules/minimist/test/parse_modified.js +11 -0
- package/node_modules/minimist/test/proto.js +64 -0
- package/node_modules/minimist/test/short.js +69 -0
- package/node_modules/minimist/test/stop_early.js +17 -0
- package/node_modules/minimist/test/unknown.js +104 -0
- package/node_modules/minimist/test/whitespace.js +10 -0
- package/node_modules/mkdirp-classic/LICENSE +21 -0
- package/node_modules/mkdirp-classic/README.md +18 -0
- package/node_modules/mkdirp-classic/index.js +98 -0
- package/node_modules/mkdirp-classic/package.json +18 -0
- package/node_modules/napi-build-utils/.github/workflows/run-npm-tests.yml +31 -0
- package/node_modules/napi-build-utils/LICENSE +21 -0
- package/node_modules/napi-build-utils/README.md +52 -0
- package/node_modules/napi-build-utils/index.js +214 -0
- package/node_modules/napi-build-utils/index.md +0 -0
- package/node_modules/napi-build-utils/package.json +42 -0
- package/node_modules/neverthrow/LICENSE +22 -0
- package/node_modules/neverthrow/README.md +1683 -0
- package/node_modules/neverthrow/dist/index.cjs.js +510 -0
- package/node_modules/neverthrow/dist/index.d.ts +408 -0
- package/node_modules/neverthrow/dist/index.es.js +497 -0
- package/node_modules/neverthrow/package.json +64 -0
- package/node_modules/node-abi/LICENSE +21 -0
- package/node_modules/node-abi/README.md +54 -0
- package/node_modules/node-abi/abi_registry.json +425 -0
- package/node_modules/node-abi/index.js +179 -0
- package/node_modules/node-abi/package.json +45 -0
- package/node_modules/once/LICENSE +15 -0
- package/node_modules/once/README.md +79 -0
- package/node_modules/once/once.js +42 -0
- package/node_modules/once/package.json +33 -0
- package/node_modules/prebuild-install/CHANGELOG.md +131 -0
- package/node_modules/prebuild-install/CONTRIBUTING.md +6 -0
- package/node_modules/prebuild-install/LICENSE +21 -0
- package/node_modules/prebuild-install/README.md +163 -0
- package/node_modules/prebuild-install/asset.js +44 -0
- package/node_modules/prebuild-install/bin.js +78 -0
- package/node_modules/prebuild-install/download.js +142 -0
- package/node_modules/prebuild-install/error.js +14 -0
- package/node_modules/prebuild-install/help.txt +16 -0
- package/node_modules/prebuild-install/index.js +1 -0
- package/node_modules/prebuild-install/log.js +33 -0
- package/node_modules/prebuild-install/package.json +67 -0
- package/node_modules/prebuild-install/proxy.js +35 -0
- package/node_modules/prebuild-install/rc.js +64 -0
- package/node_modules/prebuild-install/util.js +143 -0
- package/node_modules/pump/.github/FUNDING.yml +2 -0
- package/node_modules/pump/.travis.yml +5 -0
- package/node_modules/pump/LICENSE +21 -0
- package/node_modules/pump/README.md +74 -0
- package/node_modules/pump/SECURITY.md +5 -0
- package/node_modules/pump/index.js +86 -0
- package/node_modules/pump/package.json +24 -0
- package/node_modules/pump/test-browser.js +66 -0
- package/node_modules/pump/test-node.js +53 -0
- package/node_modules/rc/LICENSE.APACHE2 +15 -0
- package/node_modules/rc/LICENSE.BSD +26 -0
- package/node_modules/rc/LICENSE.MIT +24 -0
- package/node_modules/rc/README.md +227 -0
- package/node_modules/rc/browser.js +7 -0
- package/node_modules/rc/cli.js +4 -0
- package/node_modules/rc/index.js +53 -0
- package/node_modules/rc/lib/utils.js +104 -0
- package/node_modules/rc/package.json +29 -0
- package/node_modules/rc/test/ini.js +16 -0
- package/node_modules/rc/test/nested-env-vars.js +50 -0
- package/node_modules/rc/test/test.js +59 -0
- package/node_modules/readable-stream/CONTRIBUTING.md +38 -0
- package/node_modules/readable-stream/GOVERNANCE.md +136 -0
- package/node_modules/readable-stream/LICENSE +47 -0
- package/node_modules/readable-stream/README.md +106 -0
- package/node_modules/readable-stream/errors-browser.js +127 -0
- package/node_modules/readable-stream/errors.js +116 -0
- package/node_modules/readable-stream/experimentalWarning.js +17 -0
- package/node_modules/readable-stream/lib/_stream_duplex.js +126 -0
- package/node_modules/readable-stream/lib/_stream_passthrough.js +37 -0
- package/node_modules/readable-stream/lib/_stream_readable.js +1027 -0
- package/node_modules/readable-stream/lib/_stream_transform.js +190 -0
- package/node_modules/readable-stream/lib/_stream_writable.js +641 -0
- package/node_modules/readable-stream/lib/internal/streams/async_iterator.js +180 -0
- package/node_modules/readable-stream/lib/internal/streams/buffer_list.js +183 -0
- package/node_modules/readable-stream/lib/internal/streams/destroy.js +96 -0
- package/node_modules/readable-stream/lib/internal/streams/end-of-stream.js +86 -0
- package/node_modules/readable-stream/lib/internal/streams/from-browser.js +3 -0
- package/node_modules/readable-stream/lib/internal/streams/from.js +52 -0
- package/node_modules/readable-stream/lib/internal/streams/pipeline.js +86 -0
- package/node_modules/readable-stream/lib/internal/streams/state.js +22 -0
- package/node_modules/readable-stream/lib/internal/streams/stream-browser.js +1 -0
- package/node_modules/readable-stream/lib/internal/streams/stream.js +1 -0
- package/node_modules/readable-stream/package.json +68 -0
- package/node_modules/readable-stream/readable-browser.js +9 -0
- package/node_modules/readable-stream/readable.js +16 -0
- package/node_modules/safe-buffer/LICENSE +21 -0
- package/node_modules/safe-buffer/README.md +584 -0
- package/node_modules/safe-buffer/index.d.ts +187 -0
- package/node_modules/safe-buffer/index.js +65 -0
- package/node_modules/safe-buffer/package.json +51 -0
- package/node_modules/semver/LICENSE +15 -0
- package/node_modules/semver/README.md +664 -0
- package/node_modules/semver/bin/semver.js +191 -0
- package/node_modules/semver/classes/comparator.js +143 -0
- package/node_modules/semver/classes/index.js +7 -0
- package/node_modules/semver/classes/range.js +557 -0
- package/node_modules/semver/classes/semver.js +333 -0
- package/node_modules/semver/functions/clean.js +8 -0
- package/node_modules/semver/functions/cmp.js +54 -0
- package/node_modules/semver/functions/coerce.js +62 -0
- package/node_modules/semver/functions/compare-build.js +9 -0
- package/node_modules/semver/functions/compare-loose.js +5 -0
- package/node_modules/semver/functions/compare.js +7 -0
- package/node_modules/semver/functions/diff.js +60 -0
- package/node_modules/semver/functions/eq.js +5 -0
- package/node_modules/semver/functions/gt.js +5 -0
- package/node_modules/semver/functions/gte.js +5 -0
- package/node_modules/semver/functions/inc.js +21 -0
- package/node_modules/semver/functions/lt.js +5 -0
- package/node_modules/semver/functions/lte.js +5 -0
- package/node_modules/semver/functions/major.js +5 -0
- package/node_modules/semver/functions/minor.js +5 -0
- package/node_modules/semver/functions/neq.js +5 -0
- package/node_modules/semver/functions/parse.js +18 -0
- package/node_modules/semver/functions/patch.js +5 -0
- package/node_modules/semver/functions/prerelease.js +8 -0
- package/node_modules/semver/functions/rcompare.js +5 -0
- package/node_modules/semver/functions/rsort.js +5 -0
- package/node_modules/semver/functions/satisfies.js +12 -0
- package/node_modules/semver/functions/sort.js +5 -0
- package/node_modules/semver/functions/valid.js +8 -0
- package/node_modules/semver/index.js +91 -0
- package/node_modules/semver/internal/constants.js +37 -0
- package/node_modules/semver/internal/debug.js +11 -0
- package/node_modules/semver/internal/identifiers.js +29 -0
- package/node_modules/semver/internal/lrucache.js +42 -0
- package/node_modules/semver/internal/parse-options.js +17 -0
- package/node_modules/semver/internal/re.js +223 -0
- package/node_modules/semver/package.json +78 -0
- package/node_modules/semver/preload.js +4 -0
- package/node_modules/semver/range.bnf +16 -0
- package/node_modules/semver/ranges/gtr.js +6 -0
- package/node_modules/semver/ranges/intersects.js +9 -0
- package/node_modules/semver/ranges/ltr.js +6 -0
- package/node_modules/semver/ranges/max-satisfying.js +27 -0
- package/node_modules/semver/ranges/min-satisfying.js +26 -0
- package/node_modules/semver/ranges/min-version.js +63 -0
- package/node_modules/semver/ranges/outside.js +82 -0
- package/node_modules/semver/ranges/simplify.js +49 -0
- package/node_modules/semver/ranges/subset.js +249 -0
- package/node_modules/semver/ranges/to-comparators.js +10 -0
- package/node_modules/semver/ranges/valid.js +13 -0
- package/node_modules/simple-concat/.travis.yml +3 -0
- package/node_modules/simple-concat/LICENSE +20 -0
- package/node_modules/simple-concat/README.md +44 -0
- package/node_modules/simple-concat/index.js +15 -0
- package/node_modules/simple-concat/package.json +47 -0
- package/node_modules/simple-concat/test/basic.js +41 -0
- package/node_modules/simple-get/.github/dependabot.yml +15 -0
- package/node_modules/simple-get/.github/workflows/ci.yml +23 -0
- package/node_modules/simple-get/LICENSE +20 -0
- package/node_modules/simple-get/README.md +333 -0
- package/node_modules/simple-get/index.js +108 -0
- package/node_modules/simple-get/package.json +67 -0
- package/node_modules/string_decoder/LICENSE +48 -0
- package/node_modules/string_decoder/README.md +47 -0
- package/node_modules/string_decoder/lib/string_decoder.js +296 -0
- package/node_modules/string_decoder/package.json +34 -0
- package/node_modules/strip-json-comments/index.js +70 -0
- package/node_modules/strip-json-comments/license +21 -0
- package/node_modules/strip-json-comments/package.json +42 -0
- package/node_modules/strip-json-comments/readme.md +64 -0
- package/node_modules/tar-fs/.travis.yml +6 -0
- package/node_modules/tar-fs/LICENSE +21 -0
- package/node_modules/tar-fs/README.md +165 -0
- package/node_modules/tar-fs/index.js +363 -0
- package/node_modules/tar-fs/package.json +41 -0
- package/node_modules/tar-fs/test/fixtures/a/hello.txt +1 -0
- package/node_modules/tar-fs/test/fixtures/b/a/test.txt +1 -0
- package/node_modules/tar-fs/test/fixtures/d/file1 +0 -0
- package/node_modules/tar-fs/test/fixtures/d/file2 +0 -0
- package/node_modules/tar-fs/test/fixtures/d/sub-dir/file5 +0 -0
- package/node_modules/tar-fs/test/fixtures/d/sub-files/file3 +0 -0
- package/node_modules/tar-fs/test/fixtures/d/sub-files/file4 +0 -0
- package/node_modules/tar-fs/test/fixtures/e/directory/.ignore +0 -0
- package/node_modules/tar-fs/test/fixtures/e/file +0 -0
- package/node_modules/tar-fs/test/fixtures/invalid.tar +0 -0
- package/node_modules/tar-fs/test/index.js +346 -0
- package/node_modules/tar-stream/LICENSE +21 -0
- package/node_modules/tar-stream/README.md +168 -0
- package/node_modules/tar-stream/extract.js +257 -0
- package/node_modules/tar-stream/headers.js +295 -0
- package/node_modules/tar-stream/index.js +2 -0
- package/node_modules/tar-stream/pack.js +255 -0
- package/node_modules/tar-stream/package.json +58 -0
- package/node_modules/tar-stream/sandbox.js +11 -0
- package/node_modules/tunnel-agent/LICENSE +55 -0
- package/node_modules/tunnel-agent/README.md +4 -0
- package/node_modules/tunnel-agent/index.js +244 -0
- package/node_modules/tunnel-agent/package.json +22 -0
- package/node_modules/util-deprecate/History.md +16 -0
- package/node_modules/util-deprecate/LICENSE +24 -0
- package/node_modules/util-deprecate/README.md +53 -0
- package/node_modules/util-deprecate/browser.js +67 -0
- package/node_modules/util-deprecate/node.js +6 -0
- package/node_modules/util-deprecate/package.json +27 -0
- package/node_modules/wrappy/LICENSE +15 -0
- package/node_modules/wrappy/README.md +36 -0
- package/node_modules/wrappy/package.json +29 -0
- package/node_modules/wrappy/wrappy.js +33 -0
- package/package.json +12 -2
- package/templates/default/dot-claude/skills/fling/.hash +1 -1
- package/templates/default/dot-claude/skills/fling/SKILL.md +56 -29
- package/templates/default/dot-claude/skills/fling/references/WORKFLOWS.md +368 -0
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
It seems the file system tools are having issues. Let me provide the document directly - you can copy it:
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Durable Execution Engine on Cloudflare
|
|
6
|
+
|
|
7
|
+
A design for a minimal viable durable execution engine built on Cloudflare Workers, D1, and Workers for Platforms. The goal is to support a subset of Temporal-like functionality, targeting only TypeScript, running entirely within customer projects.
|
|
8
|
+
|
|
9
|
+
## Goals
|
|
10
|
+
|
|
11
|
+
- **Durable execution**: Workflows survive worker restarts, timeouts, and failures
|
|
12
|
+
- **Per-project isolation**: All state lives in the project's own D1 database
|
|
13
|
+
- **No platform dependency**: The orchestration runs within the project itself, not in shared infrastructure
|
|
14
|
+
- **Simplicity**: Minimal concepts, minimal schema, easy to reason about
|
|
15
|
+
|
|
16
|
+
## Alternatives Considered
|
|
17
|
+
|
|
18
|
+
### Temporal-Style Replay
|
|
19
|
+
|
|
20
|
+
Temporal maintains an ordered event log and replays the entire workflow history to reconstruct state. The workflow function runs from the beginning, and the SDK intercepts each activity call, matching it against the event history by position.
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
async function onboard(ctx, input) {
|
|
24
|
+
const user = await ctx.activity("fetch-user", () => getUser(input.userId));
|
|
25
|
+
const plan = await ctx.activity("create-plan", () => createPlan(user.id));
|
|
26
|
+
return { planId: plan.id };
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
On replay, if `fetch-user` already completed, the SDK returns the stored result without executing. The workflow function stays alive in memory, blocked on the next await.
|
|
31
|
+
|
|
32
|
+
**Why we rejected it:**
|
|
33
|
+
|
|
34
|
+
- Workers have CPU time limits (30s on paid plans). Long histories become expensive to replay.
|
|
35
|
+
- Workers can't hold execution state between activity completions. There's no long-lived process—each invocation is ephemeral.
|
|
36
|
+
- Requires strict determinism. Any non-deterministic code (`Math.random`, `Date.now`, etc.) causes replay to diverge.
|
|
37
|
+
|
|
38
|
+
### Re-Execute with Memoization
|
|
39
|
+
|
|
40
|
+
This is what Inngest and Cloudflare Workflows do. The workflow function runs from the top on every invocation. Each `step.run()` call checks a key-value store: if the step already completed, return the result; otherwise, execute it, persist the result, and stop.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
async function onboard(step, input) {
|
|
44
|
+
const user = await step.run("fetch-user", () => getUser(input.userId));
|
|
45
|
+
const plan = await step.run("create-plan", () => createPlan(user.id));
|
|
46
|
+
return { planId: plan.id };
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Pros:**
|
|
51
|
+
- No long-lived process needed
|
|
52
|
+
- Works with arbitrary control flow
|
|
53
|
+
- Simpler than full replay
|
|
54
|
+
|
|
55
|
+
**Cons:**
|
|
56
|
+
- Still re-executes all code between steps on every invocation
|
|
57
|
+
- Code between steps must still be deterministic (affects control flow)
|
|
58
|
+
- Step ID collisions if you're not careful with naming
|
|
59
|
+
|
|
60
|
+
### Compile Workflows to State Machines
|
|
61
|
+
|
|
62
|
+
Transform the workflow code at build time into an explicit state machine. Each `await step.run()` becomes a case in a switch statement. Local variables that cross step boundaries are lifted into serializable state.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// Compiled output
|
|
66
|
+
function onboard_machine(state, input) {
|
|
67
|
+
switch (state.phase) {
|
|
68
|
+
case 0:
|
|
69
|
+
return { invoke: "fetch-user", fn: () => getUser(input.userId), next: 1 };
|
|
70
|
+
case 1:
|
|
71
|
+
const user = state.results["fetch-user"];
|
|
72
|
+
return { invoke: "create-plan", fn: () => createPlan(user.id), next: 2 };
|
|
73
|
+
// ...
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Pros:**
|
|
79
|
+
- O(1) per step—jump directly to the current phase, no replay
|
|
80
|
+
- No determinism constraints
|
|
81
|
+
|
|
82
|
+
**Cons:**
|
|
83
|
+
- Requires a source-to-source compiler (substantial engineering)
|
|
84
|
+
- Loops and try/catch become complex
|
|
85
|
+
- Non-serializable locals (closures, class instances) fail at runtime
|
|
86
|
+
|
|
87
|
+
We decided this could be a future optimization but is too complex for v1.
|
|
88
|
+
|
|
89
|
+
## Chosen Design: Explicit Continuations + Scratchpad
|
|
90
|
+
|
|
91
|
+
Instead of hiding the state machine inside a framework, make the user write it explicitly. The workflow is a function that takes the current step and returns a continuation describing what to do next.
|
|
92
|
+
|
|
93
|
+
### The Model
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
type Continuation =
|
|
97
|
+
| { step: string } // next step to run
|
|
98
|
+
| { done: true; result: any } // workflow complete
|
|
99
|
+
|
|
100
|
+
type Ctx = {
|
|
101
|
+
step: string; // current step name
|
|
102
|
+
get: (key: string) => Promise<any>; // read from scratchpad
|
|
103
|
+
set: (key: string, value: any) => void; // write to scratchpad (buffered)
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
type WorkflowFn = (ctx: Ctx) => Promise<Continuation>;
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Example Workflow
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
async function onboardingWorkflow(ctx: Ctx): Promise<Continuation> {
|
|
113
|
+
switch (ctx.step) {
|
|
114
|
+
case "start": {
|
|
115
|
+
const input = await ctx.get("input");
|
|
116
|
+
const user = await getUser(input.userId);
|
|
117
|
+
ctx.set("user", user);
|
|
118
|
+
return { step: "create-plan" };
|
|
119
|
+
}
|
|
120
|
+
case "create-plan": {
|
|
121
|
+
const user = await ctx.get("user");
|
|
122
|
+
const plan = await createPlan(user.id);
|
|
123
|
+
ctx.set("plan", plan);
|
|
124
|
+
return { step: "send-email" };
|
|
125
|
+
}
|
|
126
|
+
case "send-email": {
|
|
127
|
+
const user = await ctx.get("user");
|
|
128
|
+
const plan = await ctx.get("plan");
|
|
129
|
+
await sendWelcomeEmail(user.email, plan);
|
|
130
|
+
return { done: true, result: { planId: plan.id } };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Why This Design
|
|
137
|
+
|
|
138
|
+
**Engine simplicity**: The engine just calls the function, persists what it returns, and calls it again for the next step. No parsing, no memoization, no replay.
|
|
139
|
+
|
|
140
|
+
**No determinism constraints**: Each step is a fresh invocation. You can call `Math.random()`, `Date.now()`, fetch live data—whatever you want.
|
|
141
|
+
|
|
142
|
+
**State is explicit**: The scratchpad is a key-value store. Each step declares what it reads (`ctx.get`) and writes (`ctx.set`). No magic.
|
|
143
|
+
|
|
144
|
+
**Debuggable**: Query the database and see exactly where every workflow is and what it knows.
|
|
145
|
+
|
|
146
|
+
### The Scratchpad
|
|
147
|
+
|
|
148
|
+
The scratchpad is a per-workflow key-value store.
|
|
149
|
+
|
|
150
|
+
- `ctx.set(key, value)` buffers a write in memory. It's persisted when the step completes successfully.
|
|
151
|
+
- `ctx.get(key)` is async because it may need to fetch from the database.
|
|
152
|
+
|
|
153
|
+
If a step fails or times out, buffered writes are discarded. The step is retried with the scratchpad state from before it started.
|
|
154
|
+
|
|
155
|
+
## Event Sourcing
|
|
156
|
+
|
|
157
|
+
Rather than storing mutable "current state," we store an append-only event log. The current state is derived from the last event.
|
|
158
|
+
|
|
159
|
+
### Event Types
|
|
160
|
+
|
|
161
|
+
| Event Type | Payload |
|
|
162
|
+
|------------|---------|
|
|
163
|
+
| `workflow_created` | `{ writes }` |
|
|
164
|
+
| `step_started` | `{ step, attempt }` |
|
|
165
|
+
| `step_completed` | `{ writes, next_step? }` |
|
|
166
|
+
| `step_failed` | `{ reason, retryable }` |
|
|
167
|
+
| `workflow_completed` | `{ result }` |
|
|
168
|
+
| `workflow_failed` | `{ reason, cause }` |
|
|
169
|
+
|
|
170
|
+
- `writes` is a plain JSON object: `{ user: {...}, plan: {...} }`
|
|
171
|
+
- `next_step` is omitted when the workflow is done (next event will be `workflow_completed`)
|
|
172
|
+
- `retryable` indicates whether the failure can be retried
|
|
173
|
+
|
|
174
|
+
### State Machine
|
|
175
|
+
|
|
176
|
+
The last event determines the workflow's state and what can happen next.
|
|
177
|
+
|
|
178
|
+
```mermaid
|
|
179
|
+
stateDiagram-v2
|
|
180
|
+
[*] --> workflow_created
|
|
181
|
+
workflow_created --> step_started
|
|
182
|
+
step_started --> step_completed
|
|
183
|
+
step_started --> step_failed
|
|
184
|
+
step_completed --> step_started : has next_step
|
|
185
|
+
step_completed --> workflow_completed : no next_step
|
|
186
|
+
step_failed --> step_started : retryable, can retry
|
|
187
|
+
step_failed --> workflow_failed : not retryable or max attempts
|
|
188
|
+
workflow_completed --> [*]
|
|
189
|
+
workflow_failed --> [*]
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Valid Transitions
|
|
193
|
+
|
|
194
|
+
| Last Event | Can Be Followed By |
|
|
195
|
+
|------------|---------------------|
|
|
196
|
+
| `workflow_created` | `step_started` |
|
|
197
|
+
| `step_started` | `step_completed`, `step_failed` |
|
|
198
|
+
| `step_completed` (has `next_step`) | `step_started` |
|
|
199
|
+
| `step_completed` (no `next_step`) | `workflow_completed` |
|
|
200
|
+
| `step_failed` (retryable, under max) | `step_started` |
|
|
201
|
+
| `step_failed` (not retryable or max) | `workflow_failed` |
|
|
202
|
+
| `workflow_completed` | *(terminal)* |
|
|
203
|
+
| `workflow_failed` | *(terminal)* |
|
|
204
|
+
|
|
205
|
+
## Schema
|
|
206
|
+
|
|
207
|
+
```sql
|
|
208
|
+
CREATE TABLE workflow_events (
|
|
209
|
+
workflow_id TEXT NOT NULL,
|
|
210
|
+
seq INTEGER NOT NULL,
|
|
211
|
+
event_type TEXT NOT NULL,
|
|
212
|
+
payload JSON NOT NULL,
|
|
213
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
214
|
+
PRIMARY KEY (workflow_id, seq)
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
CREATE INDEX idx_workflow_events_last
|
|
218
|
+
ON workflow_events(workflow_id, seq DESC);
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The composite primary key `(workflow_id, seq)` enforces uniqueness. Two concurrent writers trying to insert `seq = 5` for the same workflow: one wins, one gets a constraint violation.
|
|
222
|
+
|
|
223
|
+
## Coordination Without Locks
|
|
224
|
+
|
|
225
|
+
D1 doesn't support interactive transactions—you can't SELECT then UPDATE atomically. We use optimistic concurrency via the unique constraint on `(workflow_id, seq)`.
|
|
226
|
+
|
|
227
|
+
### Claiming a Step
|
|
228
|
+
|
|
229
|
+
To start a step:
|
|
230
|
+
|
|
231
|
+
1. Query the last event for the workflow
|
|
232
|
+
2. Validate that `step_started` is a legal transition from that event
|
|
233
|
+
3. Insert `step_started` with `seq = last_seq + 1`
|
|
234
|
+
|
|
235
|
+
If the insert fails (unique constraint violation), someone else claimed it. No harm done—the loser just bails.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
async function claimStep(db: D1Database, workflowId: string): Promise<ClaimResult | null> {
|
|
239
|
+
// 1. Get last event
|
|
240
|
+
const last = await db.prepare(`
|
|
241
|
+
SELECT * FROM workflow_events
|
|
242
|
+
WHERE workflow_id = ?
|
|
243
|
+
ORDER BY seq DESC
|
|
244
|
+
LIMIT 1
|
|
245
|
+
`).bind(workflowId).first();
|
|
246
|
+
|
|
247
|
+
if (!last) return null;
|
|
248
|
+
|
|
249
|
+
// 2. Validate transition
|
|
250
|
+
const canStart =
|
|
251
|
+
last.event_type === "workflow_created" ||
|
|
252
|
+
(last.event_type === "step_completed" && last.payload.next_step) ||
|
|
253
|
+
(last.event_type === "step_failed" && last.payload.retryable);
|
|
254
|
+
|
|
255
|
+
if (!canStart) return null;
|
|
256
|
+
|
|
257
|
+
// 3. Determine step name and attempt
|
|
258
|
+
const stepName = last.event_type === "workflow_created"
|
|
259
|
+
? "start"
|
|
260
|
+
: last.event_type === "step_completed"
|
|
261
|
+
? last.payload.next_step
|
|
262
|
+
: last.payload.step; // retry same step
|
|
263
|
+
|
|
264
|
+
const attempt = last.event_type === "step_failed"
|
|
265
|
+
? last.payload.attempt + 1
|
|
266
|
+
: 1;
|
|
267
|
+
|
|
268
|
+
// 4. Try to insert (may fail if someone else got there)
|
|
269
|
+
try {
|
|
270
|
+
await db.prepare(`
|
|
271
|
+
INSERT INTO workflow_events (workflow_id, seq, event_type, payload)
|
|
272
|
+
VALUES (?, ?, 'step_started', ?)
|
|
273
|
+
`).bind(
|
|
274
|
+
workflowId,
|
|
275
|
+
last.seq + 1,
|
|
276
|
+
JSON.stringify({ step: stepName, attempt })
|
|
277
|
+
).run();
|
|
278
|
+
|
|
279
|
+
return { workflowId, step: stepName, attempt, seq: last.seq + 1 };
|
|
280
|
+
} catch (err) {
|
|
281
|
+
// Unique constraint violation = someone else claimed it
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## System Components
|
|
288
|
+
|
|
289
|
+
### Trigger Endpoint
|
|
290
|
+
|
|
291
|
+
Creates a new workflow:
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// POST /workflows
|
|
295
|
+
async function createWorkflow(db: D1Database, name: string, writes: Record<string, unknown>): Promise<string> {
|
|
296
|
+
const workflowId = generateId();
|
|
297
|
+
|
|
298
|
+
await db.prepare(`
|
|
299
|
+
INSERT INTO workflow_events (workflow_id, seq, event_type, payload)
|
|
300
|
+
VALUES (?, 0, 'workflow_created', ?)
|
|
301
|
+
`).bind(workflowId, JSON.stringify({ writes })).run();
|
|
302
|
+
|
|
303
|
+
// Kick off first step
|
|
304
|
+
await fetch(`${INTERNAL_URL}/internal/workflow/step`, {
|
|
305
|
+
method: "POST",
|
|
306
|
+
body: JSON.stringify({ workflowId }),
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
return workflowId;
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Step Endpoint
|
|
314
|
+
|
|
315
|
+
Executes a single step:
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
// POST /internal/workflow/step
|
|
319
|
+
async function handleStep(request: Request, env: Env) {
|
|
320
|
+
const { workflowId } = await request.json();
|
|
321
|
+
|
|
322
|
+
// 1. Try to claim
|
|
323
|
+
const claim = await claimStep(env.DB, workflowId);
|
|
324
|
+
if (!claim) {
|
|
325
|
+
return new Response("not claimable", { status: 409 });
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// 2. Build context
|
|
329
|
+
const ctx = await buildContext(env.DB, workflowId, claim);
|
|
330
|
+
|
|
331
|
+
// 3. Execute workflow function
|
|
332
|
+
const workflowFn = getWorkflowFunction(claim.workflowName);
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
const continuation = await workflowFn(ctx);
|
|
336
|
+
|
|
337
|
+
// 4. Commit
|
|
338
|
+
await commitStep(env.DB, workflowId, claim.seq, ctx.pendingWrites, continuation);
|
|
339
|
+
|
|
340
|
+
// 5. Continue if there's a next step
|
|
341
|
+
if (!continuation.done) {
|
|
342
|
+
env.ctx.waitUntil(
|
|
343
|
+
fetch(request.url, {
|
|
344
|
+
method: "POST",
|
|
345
|
+
body: JSON.stringify({ workflowId }),
|
|
346
|
+
})
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return new Response("ok");
|
|
351
|
+
|
|
352
|
+
} catch (err) {
|
|
353
|
+
// Step failed
|
|
354
|
+
await insertEvent(env.DB, workflowId, claim.seq + 1, "step_failed", {
|
|
355
|
+
step: claim.step,
|
|
356
|
+
attempt: claim.attempt,
|
|
357
|
+
reason: err.message,
|
|
358
|
+
retryable: true,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
return new Response("step failed", { status: 500 });
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Committing a Step
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
async function commitStep(
|
|
370
|
+
db: D1Database,
|
|
371
|
+
workflowId: string,
|
|
372
|
+
currentSeq: number,
|
|
373
|
+
writes: Record<string, any>,
|
|
374
|
+
continuation: Continuation
|
|
375
|
+
) {
|
|
376
|
+
if (continuation.done) {
|
|
377
|
+
// Insert step_completed then workflow_completed
|
|
378
|
+
await db.batch([
|
|
379
|
+
db.prepare(`
|
|
380
|
+
INSERT INTO workflow_events (workflow_id, seq, event_type, payload)
|
|
381
|
+
VALUES (?, ?, 'step_completed', ?)
|
|
382
|
+
`).bind(workflowId, currentSeq + 1, JSON.stringify({ writes })),
|
|
383
|
+
|
|
384
|
+
db.prepare(`
|
|
385
|
+
INSERT INTO workflow_events (workflow_id, seq, event_type, payload)
|
|
386
|
+
VALUES (?, ?, 'workflow_completed', ?)
|
|
387
|
+
`).bind(workflowId, currentSeq + 2, JSON.stringify({ result: continuation.result })),
|
|
388
|
+
]);
|
|
389
|
+
} else {
|
|
390
|
+
// Insert step_completed with next_step
|
|
391
|
+
await db.prepare(`
|
|
392
|
+
INSERT INTO workflow_events (workflow_id, seq, event_type, payload)
|
|
393
|
+
VALUES (?, ?, 'step_completed', ?)
|
|
394
|
+
`).bind(
|
|
395
|
+
workflowId,
|
|
396
|
+
currentSeq + 1,
|
|
397
|
+
JSON.stringify({ writes, next_step: continuation.step })
|
|
398
|
+
).run();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Cron Job
|
|
404
|
+
|
|
405
|
+
Runs every 5–10 seconds. Handles:
|
|
406
|
+
|
|
407
|
+
1. **Timeouts**: Steps that started but never completed
|
|
408
|
+
2. **Stuck workflows**: Workflows that should be running but aren't
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
async function handleCron(env: Env) {
|
|
412
|
+
// Get last event for all non-terminal workflows
|
|
413
|
+
const workflows = await env.DB.prepare(`
|
|
414
|
+
SELECT
|
|
415
|
+
e.workflow_id,
|
|
416
|
+
e.seq,
|
|
417
|
+
e.event_type,
|
|
418
|
+
e.payload,
|
|
419
|
+
e.created_at
|
|
420
|
+
FROM workflow_events e
|
|
421
|
+
INNER JOIN (
|
|
422
|
+
SELECT workflow_id, MAX(seq) as max_seq
|
|
423
|
+
FROM workflow_events
|
|
424
|
+
GROUP BY workflow_id
|
|
425
|
+
) latest ON e.workflow_id = latest.workflow_id AND e.seq = latest.max_seq
|
|
426
|
+
WHERE e.event_type NOT IN ('workflow_completed', 'workflow_failed')
|
|
427
|
+
`).all();
|
|
428
|
+
|
|
429
|
+
for (const wf of workflows.results) {
|
|
430
|
+
const payload = JSON.parse(wf.payload);
|
|
431
|
+
|
|
432
|
+
if (wf.event_type === "step_started") {
|
|
433
|
+
// Check for timeout
|
|
434
|
+
const startedAt = new Date(wf.created_at);
|
|
435
|
+
const timedOut = Date.now() - startedAt.getTime() > STEP_TIMEOUT_MS;
|
|
436
|
+
|
|
437
|
+
if (timedOut) {
|
|
438
|
+
// Insert step_failed
|
|
439
|
+
await env.DB.prepare(`
|
|
440
|
+
INSERT INTO workflow_events (workflow_id, seq, event_type, payload)
|
|
441
|
+
VALUES (?, ?, 'step_failed', ?)
|
|
442
|
+
`).bind(
|
|
443
|
+
wf.workflow_id,
|
|
444
|
+
wf.seq + 1,
|
|
445
|
+
JSON.stringify({
|
|
446
|
+
step: payload.step,
|
|
447
|
+
attempt: payload.attempt,
|
|
448
|
+
reason: "timeout",
|
|
449
|
+
retryable: true,
|
|
450
|
+
})
|
|
451
|
+
).run();
|
|
452
|
+
|
|
453
|
+
// Kick the workflow
|
|
454
|
+
env.ctx.waitUntil(triggerStep(wf.workflow_id));
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
// workflow_created, step_completed, step_failed (retryable)
|
|
458
|
+
// These should all have a step running, but don't
|
|
459
|
+
// Kick the workflow to make progress
|
|
460
|
+
env.ctx.waitUntil(triggerStep(wf.workflow_id));
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
async function triggerStep(workflowId: string) {
|
|
466
|
+
await fetch(`${INTERNAL_URL}/internal/workflow/step`, {
|
|
467
|
+
method: "POST",
|
|
468
|
+
body: JSON.stringify({ workflowId }),
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
## Reconstructing the Scratchpad
|
|
474
|
+
|
|
475
|
+
To get a key's current value, scan `step_completed` events from newest to oldest:
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
async function getScratchpadValue(db: D1Database, workflowId: string, key: string): Promise<any> {
|
|
479
|
+
const events = await db.prepare(`
|
|
480
|
+
SELECT payload FROM workflow_events
|
|
481
|
+
WHERE workflow_id = ?
|
|
482
|
+
AND event_type = 'step_completed'
|
|
483
|
+
ORDER BY seq DESC
|
|
484
|
+
`).bind(workflowId).all();
|
|
485
|
+
|
|
486
|
+
for (const event of events.results) {
|
|
487
|
+
const payload = JSON.parse(event.payload);
|
|
488
|
+
if (payload.writes && key in payload.writes) {
|
|
489
|
+
return payload.writes[key];
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return undefined;
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
For efficiency, the step endpoint can cache reads during execution so multiple `ctx.get()` calls for the same key don't hit the database repeatedly.
|
|
498
|
+
|
|
499
|
+
## Event Log Example
|
|
500
|
+
|
|
501
|
+
A successful workflow:
|
|
502
|
+
|
|
503
|
+
| seq | event_type | payload |
|
|
504
|
+
|-----|------------|---------|
|
|
505
|
+
| 0 | `workflow_created` | `{ writes: { userId: 123 } }` |
|
|
506
|
+
| 1 | `step_started` | `{ step: "start", attempt: 1 }` |
|
|
507
|
+
| 2 | `step_completed` | `{ writes: { user: {...} }, next_step: "create-plan" }` |
|
|
508
|
+
| 3 | `step_started` | `{ step: "create-plan", attempt: 1 }` |
|
|
509
|
+
| 4 | `step_completed` | `{ writes: { plan: {...} }, next_step: "send-email" }` |
|
|
510
|
+
| 5 | `step_started` | `{ step: "send-email", attempt: 1 }` |
|
|
511
|
+
| 6 | `step_completed` | `{ writes: {} }` |
|
|
512
|
+
| 7 | `workflow_completed` | `{ result: { planId: "abc" } }` |
|
|
513
|
+
|
|
514
|
+
A workflow with a retry:
|
|
515
|
+
|
|
516
|
+
| seq | event_type | payload |
|
|
517
|
+
|-----|------------|---------|
|
|
518
|
+
| 0 | `workflow_created` | `{ writes: { userId: 123 } }` |
|
|
519
|
+
| 1 | `step_started` | `{ step: "start", attempt: 1 }` |
|
|
520
|
+
| 2 | `step_failed` | `{ step: "start", attempt: 1, reason: "connection timeout", retryable: true }` |
|
|
521
|
+
| 3 | `step_started` | `{ step: "start", attempt: 2 }` |
|
|
522
|
+
| 4 | `step_completed` | `{ writes: { user: {...} }, next_step: "create-plan" }` |
|
|
523
|
+
| ... | | |
|
|
524
|
+
|
|
525
|
+
## Future Extensions
|
|
526
|
+
|
|
527
|
+
### Sleep / Delayed Execution
|
|
528
|
+
|
|
529
|
+
Add `run_at` to `step_completed`:
|
|
530
|
+
|
|
531
|
+
```typescript
|
|
532
|
+
return { step: "remind-user", runAt: new Date("2025-03-10") };
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
The cron job would check for `step_completed` events where `run_at <= now` and kick those workflows.
|
|
536
|
+
|
|
537
|
+
### Signals / External Triggers
|
|
538
|
+
|
|
539
|
+
Add a `wait_for` field to `step_completed` and a new event type `signal_received`. The workflow pauses until an external HTTP call delivers the signal.
|
|
540
|
+
|
|
541
|
+
### Child Workflows
|
|
542
|
+
|
|
543
|
+
A step can spawn a child workflow and wait for its completion. The parent's `step_completed` would reference the child workflow ID, and the step wouldn't complete until the child reaches `workflow_completed`.
|
|
544
|
+
|
|
545
|
+
## Summary
|
|
546
|
+
|
|
547
|
+
This design achieves durable execution with:
|
|
548
|
+
|
|
549
|
+
- **One table**: `workflow_events`
|
|
550
|
+
- **Append-only writes**: Events are immutable
|
|
551
|
+
- **Optimistic concurrency**: The unique constraint on `(workflow_id, seq)` is the lock
|
|
552
|
+
- **No external dependencies**: Everything runs in the project's Workers and D1
|
|
553
|
+
- **Self-healing**: The cron job catches timeouts and stuck workflows
|
|
554
|
+
- **Full audit trail**: The event log is the history
|