flingit 0.0.62 → 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/email.d.ts +2 -0
- package/dist/cli/commands/email.d.ts.map +1 -1
- package/dist/cli/commands/email.js +3 -0
- package/dist/cli/commands/email.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,317 @@
|
|
|
1
|
+
# determined
|
|
2
|
+
|
|
3
|
+
A deterministic simulation testing (DST) framework for TypeScript. It provides controlled scheduling of concurrent tasks, reproducible entropy, and concurrency primitives — all designed so that bugs found during simulation can be replayed exactly.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
In production, concurrent tasks run with real async scheduling, real randomness, and real concurrency primitives. During testing, `determined` replaces all of these with deterministic equivalents controlled by an entropy source. This means:
|
|
8
|
+
|
|
9
|
+
- **Every scheduling decision** (which task runs next) is driven by entropy, not the JS event loop.
|
|
10
|
+
- **Failpoints** can be injected probabilistically to test error paths.
|
|
11
|
+
- **Failures are reproducible**: record the entropy, replay it, get the exact same execution.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install determined
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Works with both ESM and CommonJS:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// ESM
|
|
23
|
+
import { SimulationImpl, noSimulation, Mutex, ConditionVariable } from "determined";
|
|
24
|
+
|
|
25
|
+
// CJS
|
|
26
|
+
const { SimulationImpl, noSimulation, Mutex, ConditionVariable } = require("determined");
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Modules
|
|
30
|
+
|
|
31
|
+
### simulation.ts
|
|
32
|
+
|
|
33
|
+
The core of the framework. Defines the `SimulationTask` interface and two implementations of the `Simulation` runner.
|
|
34
|
+
|
|
35
|
+
#### `SimulationTask`
|
|
36
|
+
|
|
37
|
+
The interface every task function receives. It extends `Logger` and `EntropySource` and provides:
|
|
38
|
+
|
|
39
|
+
- **`checkpoint(...log)`** — A yield point. The task suspends and the scheduler picks which task to resume next. Use this at every point where you want the simulation to explore different interleavings.
|
|
40
|
+
- **`failpoint(...log)`** — Like checkpoint, but may also inject a simulated failure (an `ApplicationFailure`) based on `failureProbability`. If the failpoint passes, it acts as a scheduling point. When `failureProbability` is 0, no entropy is consumed for the fail decision (important for replay determinism).
|
|
41
|
+
- **`blockpoint(...log)`** — Marks the task as blocked (waiting on an external condition like a mutex or condition variable). Unlike checkpoint, blocked tasks are excluded from scheduling until something unblocks them. If all tasks are blocked, the simulation detects deadlock.
|
|
42
|
+
- **`abortSimulation(error)`** — Immediately aborts the entire simulation run with the given error.
|
|
43
|
+
- **`random(reason)`** — Returns a random number in [0, 1) from the simulation's entropy source.
|
|
44
|
+
- **`log(...)`** / **`error(...)`** — Logging, routed through the simulation's logger.
|
|
45
|
+
|
|
46
|
+
#### `SimulationImpl`
|
|
47
|
+
|
|
48
|
+
The deterministic simulation runner. Constructed with:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
new SimulationImpl(logger: Logger, entropy: EntropySource, failureProbability: number)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Call `runTasks(specs)` with an array of `TaskSpec` objects. Each spec has a `name` and an async function `f` that receives a `SimulationTask`. All tasks start at an implicit `checkpoint("START")`, and the scheduler picks which one runs first.
|
|
55
|
+
|
|
56
|
+
Returns `Result<T[], Error>` — either the array of results (in spec order) or the first error that occurred.
|
|
57
|
+
|
|
58
|
+
**Scheduling algorithm**: When all running tasks have reached a checkpoint or blockpoint, the scheduler picks one of the checkpointed tasks using `sample()` (entropy-driven). Blocked tasks are excluded. If no tasks are checkpointed and all are blocked, a deadlock error is raised.
|
|
59
|
+
|
|
60
|
+
#### `NoSimulationTask` / `noSimulation`
|
|
61
|
+
|
|
62
|
+
Production-mode implementations where `checkpoint()` and `failpoint()` resolve immediately, `blockpoint()` is a no-op, and `random()` uses `Math.random()`. The `noSimulation` singleton runs all tasks concurrently via `Promise.all`.
|
|
63
|
+
|
|
64
|
+
### entropy.ts
|
|
65
|
+
|
|
66
|
+
Pluggable entropy for deterministic randomness.
|
|
67
|
+
|
|
68
|
+
#### `EntropySource`
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
interface EntropySource {
|
|
72
|
+
random(reason: string): number; // returns [0, 1)
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The `reason` parameter is a human-readable label used for recording and replay diagnostics.
|
|
77
|
+
|
|
78
|
+
#### Implementations
|
|
79
|
+
|
|
80
|
+
- **`SimpleEntropySource`** — Wraps `Math.random()`. Used in production.
|
|
81
|
+
- **`RecordingEntropySource`** — Wraps another source, records every `(name, value)` pair. Use during test runs to capture entropy for later replay.
|
|
82
|
+
- **`ReplayingEntropySource`** — Replays a recorded sequence. Throws on name mismatch (detects divergence from the recorded run) or exhaustion. Use to reproduce failures.
|
|
83
|
+
|
|
84
|
+
#### `sample(entropy, name, items)`
|
|
85
|
+
|
|
86
|
+
Picks a random element from an array using the entropy source. Returns `undefined` for empty arrays. For single-element arrays, returns the element without consuming entropy (important for replay: avoids spurious entropy consumption when the choice is forced).
|
|
87
|
+
|
|
88
|
+
### errors.ts
|
|
89
|
+
|
|
90
|
+
#### `ApplicationFailure`
|
|
91
|
+
|
|
92
|
+
Extends `Error` with:
|
|
93
|
+
|
|
94
|
+
- **`type?: ErrorType`** — A branded string for categorizing errors.
|
|
95
|
+
- **`nonRetryable: boolean`** — Defaults to `false`. When `true`, indicates the error should not be retried.
|
|
96
|
+
|
|
97
|
+
Used by failpoints to distinguish simulated failures from real bugs.
|
|
98
|
+
|
|
99
|
+
#### `isApplicationFailure(error)`
|
|
100
|
+
|
|
101
|
+
Type guard for `ApplicationFailure`.
|
|
102
|
+
|
|
103
|
+
### mutex.ts
|
|
104
|
+
|
|
105
|
+
An async mutex for use inside simulated tasks.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const mutex = new Mutex("my-lock");
|
|
109
|
+
|
|
110
|
+
// In a task:
|
|
111
|
+
await mutex.lock(task, "critical section");
|
|
112
|
+
try {
|
|
113
|
+
// ... exclusive access ...
|
|
114
|
+
} finally {
|
|
115
|
+
mutex.unlock(task, "critical section");
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- **`lock(task, reason)`** — If unlocked, acquires immediately. If locked, calls `blockpoint` (marking the task as blocked) and enqueues. When the lock is released, the first waiter is woken via `checkpoint`.
|
|
120
|
+
- **`unlock(task, reason)`** — Releases the lock. If waiters are queued, passes the lock to the first one (FIFO).
|
|
121
|
+
- **`isLocked`** — Read-only property.
|
|
122
|
+
|
|
123
|
+
### condition-variable.ts
|
|
124
|
+
|
|
125
|
+
A condition variable for signaling between simulated tasks. Unlike classical condition variables, this is not paired with a mutex — it's a simple waiter list.
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
const cv = new ConditionVariable("data-ready");
|
|
129
|
+
|
|
130
|
+
// Waiting task:
|
|
131
|
+
await cv.wait(task, "new data");
|
|
132
|
+
|
|
133
|
+
// Notifying task:
|
|
134
|
+
cv.notifyAll(task, "data arrived");
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- **`wait(task, reason)`** — Calls `blockpoint` (task is blocked), then parks. The task resumes via `checkpoint` when `notifyAll` is called.
|
|
138
|
+
- **`notifyAll(task, reason)`** — Wakes all waiting tasks. Does nothing if no waiters. Notifications are **not sticky** — if `notifyAll` is called before `wait`, the notification is lost and the waiter will block forever (deadlock).
|
|
139
|
+
|
|
140
|
+
## Usage Example
|
|
141
|
+
|
|
142
|
+
The sync engine uses `determined` to test concurrent sync and mutation operations. Here's a condensed version showing the key patterns:
|
|
143
|
+
|
|
144
|
+
### Writing code that works with both simulation and production
|
|
145
|
+
|
|
146
|
+
The `Simulation` interface abstracts over `SimulationImpl` (testing) and `noSimulation` (production). Your code takes a `SimulationTask` and uses its methods to yield control:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import {
|
|
150
|
+
type Simulation, type SimulationTask,
|
|
151
|
+
ConditionVariable, Mutex, sample, isApplicationFailure,
|
|
152
|
+
} from "determined";
|
|
153
|
+
|
|
154
|
+
const mutex = new Mutex("db-lock");
|
|
155
|
+
|
|
156
|
+
async function writer(task: SimulationTask, data: string[]) {
|
|
157
|
+
await mutex.lock(task, "write");
|
|
158
|
+
try {
|
|
159
|
+
// failpoint: may inject a simulated failure here during testing
|
|
160
|
+
await task.failpoint("before write");
|
|
161
|
+
data.push("written");
|
|
162
|
+
// checkpoint: allows the scheduler to switch to another task
|
|
163
|
+
await task.checkpoint("after write");
|
|
164
|
+
} finally {
|
|
165
|
+
mutex.unlock(task, "write");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function reader(task: SimulationTask, data: string[]) {
|
|
170
|
+
await mutex.lock(task, "read");
|
|
171
|
+
try {
|
|
172
|
+
task.log("current data:", data);
|
|
173
|
+
} finally {
|
|
174
|
+
mutex.unlock(task, "read");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Use a `ConditionVariable` to signal between tasks:
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
async function producer(task: SimulationTask, cv: ConditionVariable, done: { value: boolean }) {
|
|
183
|
+
await task.checkpoint("producing");
|
|
184
|
+
done.value = true;
|
|
185
|
+
cv.notifyAll(task, "data ready");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function consumer(task: SimulationTask, cv: ConditionVariable, done: { value: boolean }) {
|
|
189
|
+
if (!done.value) {
|
|
190
|
+
await cv.wait(task, "waiting for data");
|
|
191
|
+
}
|
|
192
|
+
task.log("consumed");
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Use `sample()` and `task.random()` for any random decisions, so they're captured in the entropy trace:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
async function pickAction(task: SimulationTask) {
|
|
200
|
+
const actions = ["insert", "update", "delete"] as const;
|
|
201
|
+
const action = sample(task, "pick action", actions);
|
|
202
|
+
// ...
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Running a simulation
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import {
|
|
210
|
+
SimulationImpl, RecordingEntropySource, ReplayingEntropySource,
|
|
211
|
+
SimpleEntropySource, type Logger,
|
|
212
|
+
} from "determined";
|
|
213
|
+
|
|
214
|
+
// A logger that captures output
|
|
215
|
+
class ConsoleLogger implements Logger {
|
|
216
|
+
log(...args: readonly unknown[]) { console.log(...args); }
|
|
217
|
+
error(...args: readonly unknown[]) { console.error(...args); }
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Run with recording
|
|
221
|
+
const recording = new RecordingEntropySource(new SimpleEntropySource());
|
|
222
|
+
const sim = new SimulationImpl(new ConsoleLogger(), recording, 0.05);
|
|
223
|
+
|
|
224
|
+
const result = await sim.runTasks([
|
|
225
|
+
{ name: "writer", f: (task) => writer(task, data) },
|
|
226
|
+
{ name: "reader", f: (task) => reader(task, data) },
|
|
227
|
+
]);
|
|
228
|
+
|
|
229
|
+
if (result.isErr()) {
|
|
230
|
+
// Save entropy for replay
|
|
231
|
+
const record = { config: { /* options */ }, record: recording.getRecords() };
|
|
232
|
+
await fs.writeFile("failure.json", JSON.stringify(record));
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Replaying a failure
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
const file = JSON.parse(await fs.readFile("failure.json", "utf-8"));
|
|
240
|
+
const replay = new ReplayingEntropySource(file.record);
|
|
241
|
+
const sim = new SimulationImpl(new ConsoleLogger(), replay, 0.05);
|
|
242
|
+
|
|
243
|
+
// Produces the exact same scheduling decisions and failpoint outcomes
|
|
244
|
+
const result = await sim.runTasks([
|
|
245
|
+
{ name: "writer", f: (task) => writer(task, data) },
|
|
246
|
+
{ name: "reader", f: (task) => reader(task, data) },
|
|
247
|
+
]);
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Running in production (no simulation)
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import { noSimulation } from "determined";
|
|
254
|
+
|
|
255
|
+
// Tasks run concurrently via Promise.all, checkpoints are no-ops
|
|
256
|
+
const result = await noSimulation.runTasks([
|
|
257
|
+
{ name: "writer", f: (task) => writer(task, data) },
|
|
258
|
+
{ name: "reader", f: (task) => reader(task, data) },
|
|
259
|
+
]);
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Iterating over many random interleavings
|
|
263
|
+
|
|
264
|
+
The playground pattern: run thousands of iterations with different random entropy, automatically saving failures for replay:
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
for (let i = 0; i < 1000; i++) {
|
|
268
|
+
const entropy = new RecordingEntropySource(new SimpleEntropySource());
|
|
269
|
+
const sim = new SimulationImpl(logger, entropy, failureProbability);
|
|
270
|
+
|
|
271
|
+
const result = await runMyTest(sim);
|
|
272
|
+
|
|
273
|
+
if (result.isErr()) {
|
|
274
|
+
// Save for later replay
|
|
275
|
+
await fs.writeFile(
|
|
276
|
+
`failure-${i}.json`,
|
|
277
|
+
JSON.stringify({ config: options, record: entropy.getRecords() }),
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Verify replay produces the same result
|
|
282
|
+
const replayEntropy = new ReplayingEntropySource(entropy.getRecords());
|
|
283
|
+
const replaySim = new SimulationImpl(logger, replayEntropy, failureProbability);
|
|
284
|
+
const replayResult = await runMyTest(replaySim);
|
|
285
|
+
assert(result.isOk() === replayResult.isOk(), "Replay must match original");
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Recording and Replaying Failures
|
|
290
|
+
|
|
291
|
+
The typical workflow:
|
|
292
|
+
|
|
293
|
+
1. **Run with recording**: Use `RecordingEntropySource` wrapping a `SimpleEntropySource`.
|
|
294
|
+
2. **On failure**: Save `recording.getRecords()` to a JSON file.
|
|
295
|
+
3. **Replay**: Load the records and pass them to `ReplayingEntropySource`. The simulation will make the exact same scheduling decisions, hit the exact same failpoints, and reproduce the failure.
|
|
296
|
+
|
|
297
|
+
The `ReplayingEntropySource` validates that each entropy request matches the recorded name. A mismatch means the code has changed in a way that alters the entropy consumption pattern, and it throws a descriptive error with position and both names.
|
|
298
|
+
|
|
299
|
+
## Commands
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
# Run all tests
|
|
303
|
+
npm test
|
|
304
|
+
|
|
305
|
+
# Run a single test file
|
|
306
|
+
node --experimental-strip-types --test simulation.test.ts
|
|
307
|
+
|
|
308
|
+
# Type check
|
|
309
|
+
npm run typecheck
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Design Notes
|
|
313
|
+
|
|
314
|
+
- All concurrency is cooperative, not preemptive. Tasks only yield control at explicit `checkpoint`, `failpoint`, or `blockpoint` calls.
|
|
315
|
+
- The simulation runs in a single JS event loop turn between scheduling decisions. There is no actual parallelism.
|
|
316
|
+
- `SimulationImpl` should be treated as single-use per `runTasks` call. After a failed run (error or deadlock), the instance is permanently poisoned (`abortedWithError` is never reset) and subsequent `runTasks` calls will immediately fail.
|
|
317
|
+
- The `sample()` function's "no entropy for single item" optimization is critical for replay correctness — it ensures the entropy consumption sequence doesn't depend on transient pool sizes.
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ApplicationFailure: () => ApplicationFailure,
|
|
24
|
+
ConditionVariable: () => ConditionVariable,
|
|
25
|
+
Mutex: () => Mutex,
|
|
26
|
+
NoSimulationTask: () => NoSimulationTask,
|
|
27
|
+
RecordingEntropySource: () => RecordingEntropySource,
|
|
28
|
+
ReplayingEntropySource: () => ReplayingEntropySource,
|
|
29
|
+
SimpleEntropySource: () => SimpleEntropySource,
|
|
30
|
+
SimulationImpl: () => SimulationImpl,
|
|
31
|
+
isApplicationFailure: () => isApplicationFailure,
|
|
32
|
+
makeErrorType: () => makeErrorType,
|
|
33
|
+
noSimulation: () => noSimulation,
|
|
34
|
+
sample: () => sample
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// simulation.ts
|
|
39
|
+
var import_ts_necessities2 = require("@glideapps/ts-necessities");
|
|
40
|
+
|
|
41
|
+
// entropy.ts
|
|
42
|
+
var SimpleEntropySource = class {
|
|
43
|
+
random() {
|
|
44
|
+
return Math.random();
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var RecordingEntropySource = class {
|
|
48
|
+
underlying;
|
|
49
|
+
records = [];
|
|
50
|
+
constructor(underlying) {
|
|
51
|
+
this.underlying = underlying;
|
|
52
|
+
}
|
|
53
|
+
random(name) {
|
|
54
|
+
const value = this.underlying.random(name);
|
|
55
|
+
this.records.push({ name, value });
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
getRecords() {
|
|
59
|
+
return this.records;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
var ReplayingEntropySource = class {
|
|
63
|
+
records;
|
|
64
|
+
position = 0;
|
|
65
|
+
constructor(records) {
|
|
66
|
+
this.records = records.slice();
|
|
67
|
+
}
|
|
68
|
+
random(name) {
|
|
69
|
+
const record = this.records[this.position];
|
|
70
|
+
if (record === void 0) {
|
|
71
|
+
throw new Error(`No more entropy records available when requesting "${name}"`);
|
|
72
|
+
}
|
|
73
|
+
if (record.name !== name) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Entropy record name mismatch at position ${this.position}: expected "${record.name}", got "${name}"`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
this.position++;
|
|
79
|
+
return record.value;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
function sample(entropy, name, items) {
|
|
83
|
+
if (items.length < 2) return items[0];
|
|
84
|
+
const index = Math.floor(entropy.random(name) * items.length);
|
|
85
|
+
return items[index];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// errors.ts
|
|
89
|
+
var import_ts_necessities = require("@glideapps/ts-necessities");
|
|
90
|
+
var makeErrorType = (0, import_ts_necessities.makeBrandString)();
|
|
91
|
+
var ApplicationFailure = class extends Error {
|
|
92
|
+
type;
|
|
93
|
+
nonRetryable;
|
|
94
|
+
constructor(message, type, nonRetryable) {
|
|
95
|
+
super(message);
|
|
96
|
+
this.type = type;
|
|
97
|
+
this.nonRetryable = nonRetryable ?? false;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
function isApplicationFailure(error) {
|
|
101
|
+
return error instanceof ApplicationFailure;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// simulation.ts
|
|
105
|
+
var import_neverthrow = require("neverthrow");
|
|
106
|
+
var NoSimulationTask = class {
|
|
107
|
+
taskName;
|
|
108
|
+
shouldLog;
|
|
109
|
+
constructor(taskName, shouldLog) {
|
|
110
|
+
this.taskName = taskName;
|
|
111
|
+
this.shouldLog = shouldLog;
|
|
112
|
+
}
|
|
113
|
+
log(...log) {
|
|
114
|
+
if (this.shouldLog) {
|
|
115
|
+
console.log(`${this.taskName}: `, ...log);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
error(...log) {
|
|
119
|
+
console.error(`${this.taskName}: `, ...log);
|
|
120
|
+
}
|
|
121
|
+
random() {
|
|
122
|
+
return Math.random();
|
|
123
|
+
}
|
|
124
|
+
checkpoint(...log) {
|
|
125
|
+
this.log(...log);
|
|
126
|
+
return Promise.resolve();
|
|
127
|
+
}
|
|
128
|
+
failpoint(...log) {
|
|
129
|
+
this.log(...log);
|
|
130
|
+
return Promise.resolve();
|
|
131
|
+
}
|
|
132
|
+
blockpoint(...log) {
|
|
133
|
+
this.log(...log);
|
|
134
|
+
}
|
|
135
|
+
abortSimulation(e) {
|
|
136
|
+
throw (0, import_ts_necessities2.exceptionToError)(e);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
var NoSimulation = class {
|
|
140
|
+
log;
|
|
141
|
+
constructor(log) {
|
|
142
|
+
this.log = log;
|
|
143
|
+
}
|
|
144
|
+
async runTasks(specs) {
|
|
145
|
+
const shouldLog = this.log;
|
|
146
|
+
try {
|
|
147
|
+
const results = await Promise.all(
|
|
148
|
+
specs.map(({ name: taskName, f }) => {
|
|
149
|
+
const task = new NoSimulationTask(taskName, shouldLog);
|
|
150
|
+
return f(task);
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
return (0, import_neverthrow.ok)(results);
|
|
154
|
+
} catch (e) {
|
|
155
|
+
return (0, import_neverthrow.err)((0, import_ts_necessities2.exceptionToError)(e));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
var noSimulation = new NoSimulation(false);
|
|
160
|
+
var SimulationImpl = class {
|
|
161
|
+
logger;
|
|
162
|
+
entropy;
|
|
163
|
+
failureProbability;
|
|
164
|
+
// FIXME: Should this just be a `TaskInfo[]`, since we never look at the task anyway?
|
|
165
|
+
taskInfos = /* @__PURE__ */ new Map();
|
|
166
|
+
abortedWithError;
|
|
167
|
+
constructor(logger, entropy, failureProbability) {
|
|
168
|
+
this.logger = logger;
|
|
169
|
+
this.entropy = entropy;
|
|
170
|
+
this.failureProbability = failureProbability;
|
|
171
|
+
}
|
|
172
|
+
abort(e) {
|
|
173
|
+
if (this.abortedWithError === void 0) {
|
|
174
|
+
this.abortedWithError = e;
|
|
175
|
+
this.logger.error(`Aborting simulation: ${(0, import_ts_necessities2.exceptionToString)(e)}`);
|
|
176
|
+
}
|
|
177
|
+
throw e;
|
|
178
|
+
}
|
|
179
|
+
pickTask(tasks) {
|
|
180
|
+
const task = sample(this.entropy, `Picking task out of ${tasks.map((t) => t.name).join(", ")}`, tasks);
|
|
181
|
+
(0, import_ts_necessities2.assert)(task !== void 0, "No tasks to pick from");
|
|
182
|
+
return task;
|
|
183
|
+
}
|
|
184
|
+
unlockIfNecessary() {
|
|
185
|
+
if (this.abortedWithError !== void 0) throw this.abortedWithError;
|
|
186
|
+
if (this.taskInfos.size === 0) return;
|
|
187
|
+
const entries = Array.from(this.taskInfos.entries());
|
|
188
|
+
if (entries.some(([, i]) => i.resolve === void 0)) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const checkpointEntries = entries.filter(([, i]) => i.resolve !== void 0 && i.resolve !== false);
|
|
192
|
+
(0, import_ts_necessities2.assert)(checkpointEntries.length > 0, "All tasks are blocked");
|
|
193
|
+
const info = this.pickTask(checkpointEntries.map(([, i]) => i));
|
|
194
|
+
this.logger.log(`${info.name} UNBLOCKED from ${entries.map(([, i]) => i.name).join(", ")}`);
|
|
195
|
+
const { resolve } = info;
|
|
196
|
+
(0, import_ts_necessities2.assert)(resolve !== void 0 && resolve !== false);
|
|
197
|
+
info.resolve = void 0;
|
|
198
|
+
resolve();
|
|
199
|
+
}
|
|
200
|
+
async runTasks(specs) {
|
|
201
|
+
const simulation = this;
|
|
202
|
+
const tasksAndInfos = specs.map((s) => {
|
|
203
|
+
const info = { name: s.name, resolve: void 0 };
|
|
204
|
+
const task = {
|
|
205
|
+
random(name) {
|
|
206
|
+
const r = simulation.entropy.random(`${s.name} random number: ${name}`);
|
|
207
|
+
simulation.logger.log(`${s.name} RANDOM ${name}: ${r}`);
|
|
208
|
+
return r;
|
|
209
|
+
},
|
|
210
|
+
log(...log) {
|
|
211
|
+
simulation.logger.log(`${s.name}:`, ...log);
|
|
212
|
+
},
|
|
213
|
+
error(...log) {
|
|
214
|
+
simulation.logger.error(`${s.name}:`, ...log);
|
|
215
|
+
},
|
|
216
|
+
checkpoint(...log) {
|
|
217
|
+
simulation.logger.log(`${s.name} CHECKPOINT:`, ...log);
|
|
218
|
+
(0, import_ts_necessities2.assert)(
|
|
219
|
+
simulation.taskInfos.has(task),
|
|
220
|
+
`Task ${s.name} wants to checkpoint, but doesn't exist anymore`
|
|
221
|
+
);
|
|
222
|
+
const promise = new Promise((resolve) => {
|
|
223
|
+
(0, import_ts_necessities2.assert)(info.resolve === void 0 || info.resolve === false);
|
|
224
|
+
info.resolve = resolve;
|
|
225
|
+
});
|
|
226
|
+
simulation.unlockIfNecessary();
|
|
227
|
+
return promise;
|
|
228
|
+
},
|
|
229
|
+
failpoint(...log) {
|
|
230
|
+
(0, import_ts_necessities2.assert)(
|
|
231
|
+
simulation.taskInfos.has(task),
|
|
232
|
+
`Task ${s.name} wants to failpoint, but doesn't exist anymore`
|
|
233
|
+
);
|
|
234
|
+
const shouldFail = simulation.failureProbability > 0 && simulation.entropy.random(`${s.name} failpoint: ${log.join(" ")}`) < simulation.failureProbability;
|
|
235
|
+
if (shouldFail) {
|
|
236
|
+
simulation.logger.log(`${s.name} FAILING:`, ...log);
|
|
237
|
+
return Promise.reject(
|
|
238
|
+
new ApplicationFailure(`Simulated failure at failpoint: ${log.join(" ")}`)
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
simulation.logger.log(`${s.name} FAILPOINT:`, ...log);
|
|
242
|
+
const promise = new Promise((resolve) => {
|
|
243
|
+
(0, import_ts_necessities2.assert)(
|
|
244
|
+
info.resolve === void 0 || info.resolve === false,
|
|
245
|
+
`Task ${s.name} already has a resolve`
|
|
246
|
+
);
|
|
247
|
+
info.resolve = resolve;
|
|
248
|
+
});
|
|
249
|
+
simulation.unlockIfNecessary();
|
|
250
|
+
return promise;
|
|
251
|
+
},
|
|
252
|
+
blockpoint(...log) {
|
|
253
|
+
simulation.logger.log(`${s.name} BLOCKPOINT:`, ...log);
|
|
254
|
+
(0, import_ts_necessities2.assert)(simulation.taskInfos.has(task));
|
|
255
|
+
(0, import_ts_necessities2.assert)(info.resolve === void 0, `Task ${s.name} already has a resolve`);
|
|
256
|
+
info.resolve = false;
|
|
257
|
+
simulation.unlockIfNecessary();
|
|
258
|
+
},
|
|
259
|
+
abortSimulation(e) {
|
|
260
|
+
return simulation.abort(e);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
return [s, task, info];
|
|
264
|
+
});
|
|
265
|
+
for (const [, task, info] of tasksAndInfos) {
|
|
266
|
+
this.taskInfos.set(task, info);
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
const results = await Promise.all(
|
|
270
|
+
tasksAndInfos.map(([s, task]) => {
|
|
271
|
+
return task.checkpoint("START").then(() => s.f(task)).catch((e) => this.abort(e)).finally(() => {
|
|
272
|
+
this.taskInfos.delete(task);
|
|
273
|
+
this.logger.log(
|
|
274
|
+
`FINISHED ${s.name}, still left ${Array.from(this.taskInfos.values()).map((i) => i.name).join(", ")}`
|
|
275
|
+
);
|
|
276
|
+
this.unlockIfNecessary();
|
|
277
|
+
});
|
|
278
|
+
})
|
|
279
|
+
);
|
|
280
|
+
return (0, import_neverthrow.ok)(results);
|
|
281
|
+
} catch (e) {
|
|
282
|
+
return (0, import_neverthrow.err)((0, import_ts_necessities2.exceptionToError)(e));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// mutex.ts
|
|
288
|
+
var import_ts_necessities3 = require("@glideapps/ts-necessities");
|
|
289
|
+
var Mutex = class {
|
|
290
|
+
/** This is `undefined` iff the mutex is unlocked. Otherwise it's an array of resolve functions, one for each waiter. */
|
|
291
|
+
waiters;
|
|
292
|
+
name;
|
|
293
|
+
constructor(name) {
|
|
294
|
+
this.name = name;
|
|
295
|
+
}
|
|
296
|
+
get isLocked() {
|
|
297
|
+
return this.waiters !== void 0;
|
|
298
|
+
}
|
|
299
|
+
lock(task, reason) {
|
|
300
|
+
if (this.waiters === void 0) {
|
|
301
|
+
task.log(`mutex "${this.name}" locked unopposed for "${reason}"`);
|
|
302
|
+
this.waiters = [];
|
|
303
|
+
return Promise.resolve();
|
|
304
|
+
} else {
|
|
305
|
+
task.blockpoint(`mutex "${this.name}" enqueue for "${reason}", ${this.waiters.length} other waiters`);
|
|
306
|
+
const p = new Promise((resolve) => {
|
|
307
|
+
(0, import_ts_necessities3.defined)(this.waiters, "Promise init function did not run inline").push(async () => {
|
|
308
|
+
await task.checkpoint(
|
|
309
|
+
`mutex "${this.name}" acquired by waiter for "${reason}", ${(0, import_ts_necessities3.defined)(this.waiters).length} other waiters`
|
|
310
|
+
);
|
|
311
|
+
resolve();
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
return p;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
unlock(task, reason) {
|
|
318
|
+
(0, import_ts_necessities3.assert)(this.waiters !== void 0, "Can't unlock a mutex that's not locked");
|
|
319
|
+
const resolve = this.waiters.shift();
|
|
320
|
+
if (resolve !== void 0) {
|
|
321
|
+
task.log(
|
|
322
|
+
`mutex "${this.name}" unlocked and passed to next waiter for "${reason}", ${this.waiters.length} waiters left`
|
|
323
|
+
);
|
|
324
|
+
resolve();
|
|
325
|
+
} else {
|
|
326
|
+
task.log(`mutex "${this.name}" unlocked for "${reason}"`);
|
|
327
|
+
this.waiters = void 0;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
// condition-variable.ts
|
|
333
|
+
var ConditionVariable = class {
|
|
334
|
+
waiters = [];
|
|
335
|
+
name;
|
|
336
|
+
constructor(name) {
|
|
337
|
+
this.name = name;
|
|
338
|
+
}
|
|
339
|
+
wait(task, reason) {
|
|
340
|
+
task.blockpoint(`condition "${this.name}" wait for "${reason}", ${this.waiters.length} other waiters`);
|
|
341
|
+
return new Promise((resolve) => {
|
|
342
|
+
this.waiters.push(async () => {
|
|
343
|
+
await task.checkpoint(`condition "${this.name}" woken up for "${reason}"`);
|
|
344
|
+
resolve();
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
notifyAll(task, reason) {
|
|
349
|
+
task.log(`condition "${this.name}" notifying ${this.waiters.length} for "${reason}"`);
|
|
350
|
+
for (const resolve of this.waiters) {
|
|
351
|
+
resolve();
|
|
352
|
+
}
|
|
353
|
+
this.waiters.length = 0;
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
357
|
+
0 && (module.exports = {
|
|
358
|
+
ApplicationFailure,
|
|
359
|
+
ConditionVariable,
|
|
360
|
+
Mutex,
|
|
361
|
+
NoSimulationTask,
|
|
362
|
+
RecordingEntropySource,
|
|
363
|
+
ReplayingEntropySource,
|
|
364
|
+
SimpleEntropySource,
|
|
365
|
+
SimulationImpl,
|
|
366
|
+
isApplicationFailure,
|
|
367
|
+
makeErrorType,
|
|
368
|
+
noSimulation,
|
|
369
|
+
sample
|
|
370
|
+
});
|