@statewalker/cellb 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +323 -2
- package/dist/adapters/files-backup.d.ts +16 -0
- package/dist/adapters/files-backup.js +32 -0
- package/dist/adapters/files-backup.js.map +1 -0
- package/dist/adapters/files-changelog.d.ts +33 -0
- package/dist/adapters/files-changelog.js +96 -0
- package/dist/adapters/files-changelog.js.map +1 -0
- package/dist/adapters/files-ltx.d.ts +35 -0
- package/dist/adapters/files-ltx.js +9 -0
- package/dist/adapters/files-ltx.js.map +1 -0
- package/dist/adapters/ltx-vfs.d.ts +21 -0
- package/dist/adapters/ltx-vfs.js +286 -0
- package/dist/adapters/ltx-vfs.js.map +1 -0
- package/dist/adapters/node-sqlite.d.ts +45 -0
- package/dist/adapters/node-sqlite.js +254 -0
- package/dist/adapters/node-sqlite.js.map +1 -0
- package/dist/adapters/wasm-page-hook.d.ts +26 -0
- package/dist/adapters/wasm-page-hook.js +55 -0
- package/dist/adapters/wasm-page-hook.js.map +1 -0
- package/dist/adapters/wasm-session.d.ts +11 -0
- package/dist/adapters/wasm-session.js +76 -0
- package/dist/adapters/wasm-session.js.map +1 -0
- package/dist/adapters/wasm-sqlite.d.ts +89 -0
- package/dist/adapters/wasm-sqlite.js +258 -0
- package/dist/adapters/wasm-sqlite.js.map +1 -0
- package/dist/bindings/assets.d.ts +26 -0
- package/dist/bindings/assets.js +260 -0
- package/dist/bindings/assets.js.map +1 -0
- package/dist/bindings/cache.d.ts +16 -0
- package/dist/bindings/cache.js +34 -0
- package/dist/bindings/cache.js.map +1 -0
- package/dist/bindings/celld-kv.d.ts +26 -0
- package/dist/bindings/celld-kv.js +81 -0
- package/dist/bindings/celld-kv.js.map +1 -0
- package/dist/bindings/celld-queues.d.ts +56 -0
- package/dist/bindings/celld-queues.js +223 -0
- package/dist/bindings/celld-queues.js.map +1 -0
- package/dist/bindings/celld-workflows.d.ts +22 -0
- package/dist/bindings/celld-workflows.js +54 -0
- package/dist/bindings/celld-workflows.js.map +1 -0
- package/dist/bindings/cron.d.ts +27 -0
- package/dist/bindings/cron.js +232 -0
- package/dist/bindings/cron.js.map +1 -0
- package/dist/bindings/d1-migrations.d.ts +22 -0
- package/dist/bindings/d1-migrations.js +32 -0
- package/dist/bindings/d1-migrations.js.map +1 -0
- package/dist/bindings/html-rewriter.d.ts +8 -0
- package/dist/bindings/html-rewriter.js +75 -0
- package/dist/bindings/html-rewriter.js.map +1 -0
- package/dist/bindings/r2-celld.d.ts +3 -0
- package/dist/bindings/r2-celld.js +113 -0
- package/dist/bindings/r2-celld.js.map +1 -0
- package/dist/bindings/r2.d.ts +236 -0
- package/dist/bindings/r2.js +419 -0
- package/dist/bindings/r2.js.map +1 -0
- package/dist/bindings/worker-loader.d.ts +83 -0
- package/dist/bindings/worker-loader.js +183 -0
- package/dist/bindings/worker-loader.js.map +1 -0
- package/dist/bindings/workers-ai.d.ts +39 -0
- package/dist/bindings/workers-ai.js +175 -0
- package/dist/bindings/workers-ai.js.map +1 -0
- package/dist/browser.d.ts +7 -0
- package/dist/browser.js +8 -0
- package/dist/browser.js.map +1 -0
- package/dist/bucket/celld-cells.d.ts +86 -0
- package/dist/bucket/celld-cells.js +273 -0
- package/dist/bucket/celld-cells.js.map +1 -0
- package/dist/bucket/celld-format.d.ts +10 -0
- package/dist/bucket/celld-format.js +27 -0
- package/dist/bucket/celld-format.js.map +1 -0
- package/dist/bucket/idb-object-store.d.ts +9 -0
- package/dist/bucket/idb-object-store.js +73 -0
- package/dist/bucket/idb-object-store.js.map +1 -0
- package/dist/bucket/ltx-log.d.ts +34 -0
- package/dist/bucket/ltx-log.js +289 -0
- package/dist/bucket/ltx-log.js.map +1 -0
- package/dist/bucket/object-store.d.ts +51 -0
- package/dist/bucket/object-store.js +114 -0
- package/dist/bucket/object-store.js.map +1 -0
- package/dist/bucket/s3-object-store.d.ts +25 -0
- package/dist/bucket/s3-object-store.js +151 -0
- package/dist/bucket/s3-object-store.js.map +1 -0
- package/dist/bucket/sqlite-object-store.d.ts +10 -0
- package/dist/bucket/sqlite-object-store.js +107 -0
- package/dist/bucket/sqlite-object-store.js.map +1 -0
- package/dist/core/alarms.d.ts +27 -0
- package/dist/core/alarms.js +88 -0
- package/dist/core/alarms.js.map +1 -0
- package/dist/core/async-context.d.ts +34 -0
- package/dist/core/async-context.js +50 -0
- package/dist/core/async-context.js.map +1 -0
- package/dist/core/celld-schema.d.ts +14 -0
- package/dist/core/celld-schema.js +34 -0
- package/dist/core/celld-schema.js.map +1 -0
- package/dist/core/durable-object.d.ts +28 -0
- package/dist/core/durable-object.js +31 -0
- package/dist/core/durable-object.js.map +1 -0
- package/dist/core/gates.d.ts +33 -0
- package/dist/core/gates.js +89 -0
- package/dist/core/gates.js.map +1 -0
- package/dist/core/handles.d.ts +90 -0
- package/dist/core/handles.js +224 -0
- package/dist/core/handles.js.map +1 -0
- package/dist/core/id.d.ts +19 -0
- package/dist/core/id.js +64 -0
- package/dist/core/id.js.map +1 -0
- package/dist/core/namespace.d.ts +38 -0
- package/dist/core/namespace.js +141 -0
- package/dist/core/namespace.js.map +1 -0
- package/dist/core/object-host.d.ts +91 -0
- package/dist/core/object-host.js +616 -0
- package/dist/core/object-host.js.map +1 -0
- package/dist/core/request-body.d.ts +9 -0
- package/dist/core/request-body.js +55 -0
- package/dist/core/request-body.js.map +1 -0
- package/dist/core/runtime.d.ts +121 -0
- package/dist/core/runtime.js +366 -0
- package/dist/core/runtime.js.map +1 -0
- package/dist/core/shard.d.ts +7 -0
- package/dist/core/shard.js +10 -0
- package/dist/core/shard.js.map +1 -0
- package/dist/core/sql-split.d.ts +15 -0
- package/dist/core/sql-split.js +101 -0
- package/dist/core/sql-split.js.map +1 -0
- package/dist/core/sql-storage.d.ts +53 -0
- package/dist/core/sql-storage.js +407 -0
- package/dist/core/sql-storage.js.map +1 -0
- package/dist/core/state.d.ts +44 -0
- package/dist/core/state.js +93 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/storage.d.ts +103 -0
- package/dist/core/storage.js +351 -0
- package/dist/core/storage.js.map +1 -0
- package/dist/core/worker-rejections.d.ts +21 -0
- package/dist/core/worker-rejections.js +55 -0
- package/dist/core/worker-rejections.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/isolate/browser.d.ts +16 -0
- package/dist/isolate/browser.js +37 -0
- package/dist/isolate/browser.js.map +1 -0
- package/dist/isolate/host.d.ts +24 -0
- package/dist/isolate/host.js +74 -0
- package/dist/isolate/host.js.map +1 -0
- package/dist/isolate/isolate-worker.d.ts +1 -0
- package/dist/isolate/isolate-worker.js +53 -0
- package/dist/isolate/isolate-worker.js.map +1 -0
- package/dist/isolate/isolate.d.ts +10 -0
- package/dist/isolate/isolate.js +169 -0
- package/dist/isolate/isolate.js.map +1 -0
- package/dist/isolate/node-entry.d.ts +1 -0
- package/dist/isolate/node-entry.js +12 -0
- package/dist/isolate/node-entry.js.map +1 -0
- package/dist/isolate/protocol.d.ts +66 -0
- package/dist/isolate/protocol.js +205 -0
- package/dist/isolate/protocol.js.map +1 -0
- package/dist/ltx/crc64.d.ts +6 -0
- package/dist/ltx/crc64.js +46 -0
- package/dist/ltx/crc64.js.map +1 -0
- package/dist/ltx/ltx-page-server.d.ts +65 -0
- package/dist/ltx/ltx-page-server.js +309 -0
- package/dist/ltx/ltx-page-server.js.map +1 -0
- package/dist/ltx/ltx-pages.d.ts +81 -0
- package/dist/ltx/ltx-pages.js +315 -0
- package/dist/ltx/ltx-pages.js.map +1 -0
- package/dist/ltx/ltx-store-worker.d.ts +1 -0
- package/dist/ltx/ltx-store-worker.js +38 -0
- package/dist/ltx/ltx-store-worker.js.map +1 -0
- package/dist/ltx/ltx.d.ts +45 -0
- package/dist/ltx/ltx.js +203 -0
- package/dist/ltx/ltx.js.map +1 -0
- package/dist/ltx/lz4.d.ts +9 -0
- package/dist/ltx/lz4.js +156 -0
- package/dist/ltx/lz4.js.map +1 -0
- package/dist/ltx/opfs-sync-files.d.ts +23 -0
- package/dist/ltx/opfs-sync-files.js +191 -0
- package/dist/ltx/opfs-sync-files.js.map +1 -0
- package/dist/ltx/xxh32.d.ts +1 -0
- package/dist/ltx/xxh32.js +43 -0
- package/dist/ltx/xxh32.js.map +1 -0
- package/dist/miniflare-compat/blob-service.d.ts +13 -0
- package/dist/miniflare-compat/blob-service.js +66 -0
- package/dist/miniflare-compat/blob-service.js.map +1 -0
- package/dist/miniflare-compat/kv-binding.d.ts +44 -0
- package/dist/miniflare-compat/kv-binding.js +99 -0
- package/dist/miniflare-compat/kv-binding.js.map +1 -0
- package/dist/miniflare-compat/shims/assert.d.ts +15 -0
- package/dist/miniflare-compat/shims/assert.js +24 -0
- package/dist/miniflare-compat/shims/assert.js.map +1 -0
- package/dist/miniflare-compat/shims/workerd-cloudflare.d.ts +7 -0
- package/dist/miniflare-compat/shims/workerd-cloudflare.js +6 -0
- package/dist/miniflare-compat/shims/workerd-cloudflare.js.map +1 -0
- package/dist/miniflare-compat/shims/workerd-globals.d.ts +1 -0
- package/dist/miniflare-compat/shims/workerd-globals.js +30 -0
- package/dist/miniflare-compat/shims/workerd-globals.js.map +1 -0
- package/dist/miniflare-compat/shims/workerd-internal.d.ts +20 -0
- package/dist/miniflare-compat/shims/workerd-internal.js +23 -0
- package/dist/miniflare-compat/shims/workerd-internal.js.map +1 -0
- package/dist/miniflare-compat/shims/workerd-performance.d.ts +4 -0
- package/dist/miniflare-compat/shims/workerd-performance.js +17 -0
- package/dist/miniflare-compat/shims/workerd-performance.js.map +1 -0
- package/dist/miniflare-compat/simulator-fetcher.d.ts +9 -0
- package/dist/miniflare-compat/simulator-fetcher.js +14 -0
- package/dist/miniflare-compat/simulator-fetcher.js.map +1 -0
- package/dist/miniflare-compat/vendored.d.ts +15 -0
- package/dist/miniflare-compat/vendored.js +27 -0
- package/dist/miniflare-compat/vendored.js.map +1 -0
- package/dist/node-compat/async_hooks.d.ts +22 -0
- package/dist/node-compat/async_hooks.js +176 -0
- package/dist/node-compat/async_hooks.js.map +1 -0
- package/dist/node-compat/browser-registry.d.ts +3 -0
- package/dist/node-compat/browser-registry.js +39 -0
- package/dist/node-compat/browser-registry.js.map +1 -0
- package/dist/node-compat/crypto.d.ts +60 -0
- package/dist/node-compat/crypto.js +143 -0
- package/dist/node-compat/crypto.js.map +1 -0
- package/dist/node-compat/diagnostics_channel.d.ts +27 -0
- package/dist/node-compat/diagnostics_channel.js +46 -0
- package/dist/node-compat/diagnostics_channel.js.map +1 -0
- package/dist/node-compat/fs.d.ts +35 -0
- package/dist/node-compat/fs.js +40 -0
- package/dist/node-compat/fs.js.map +1 -0
- package/dist/node-compat/globals.d.ts +3 -0
- package/dist/node-compat/globals.js +4 -0
- package/dist/node-compat/globals.js.map +1 -0
- package/dist/node-compat/install.d.ts +5 -0
- package/dist/node-compat/install.js +12 -0
- package/dist/node-compat/install.js.map +1 -0
- package/dist/node-compat/module.d.ts +12 -0
- package/dist/node-compat/module.js +36 -0
- package/dist/node-compat/module.js.map +1 -0
- package/dist/node-compat/modules.d.ts +8 -0
- package/dist/node-compat/modules.js +23 -0
- package/dist/node-compat/modules.js.map +1 -0
- package/dist/node-compat/os.d.ts +36 -0
- package/dist/node-compat/os.js +36 -0
- package/dist/node-compat/os.js.map +1 -0
- package/dist/node-compat/process.d.ts +14 -0
- package/dist/node-compat/process.js +16 -0
- package/dist/node-compat/process.js.map +1 -0
- package/dist/node-compat/shims.d.ts +4 -0
- package/dist/node.d.ts +5 -0
- package/dist/node.js +6 -0
- package/dist/node.js.map +1 -0
- package/dist/peer/celld-peer.d.ts +53 -0
- package/dist/peer/celld-peer.js +555 -0
- package/dist/peer/celld-peer.js.map +1 -0
- package/dist/ports.d.ts +183 -0
- package/dist/ports.js +10 -0
- package/dist/ports.js.map +1 -0
- package/dist/project/browser-bundle.d.ts +17 -0
- package/dist/project/browser-bundle.js +125 -0
- package/dist/project/browser-bundle.js.map +1 -0
- package/dist/project/browser-cookies.d.ts +12 -0
- package/dist/project/browser-cookies.js +90 -0
- package/dist/project/browser-cookies.js.map +1 -0
- package/dist/project/browser-host.d.ts +173 -0
- package/dist/project/browser-host.js +200 -0
- package/dist/project/browser-host.js.map +1 -0
- package/dist/project/browser-modules.d.ts +17 -0
- package/dist/project/browser-modules.js +89 -0
- package/dist/project/browser-modules.js.map +1 -0
- package/dist/project/browser-shards.d.ts +62 -0
- package/dist/project/browser-shards.js +210 -0
- package/dist/project/browser-shards.js.map +1 -0
- package/dist/project/browser-tabs.d.ts +103 -0
- package/dist/project/browser-tabs.js +418 -0
- package/dist/project/browser-tabs.js.map +1 -0
- package/dist/project/cloudflare-email.d.ts +8 -0
- package/dist/project/cloudflare-email.js +11 -0
- package/dist/project/cloudflare-email.js.map +1 -0
- package/dist/project/cloudflare-workers.d.ts +12 -0
- package/dist/project/cloudflare-workers.js +16 -0
- package/dist/project/cloudflare-workers.js.map +1 -0
- package/dist/project/host-modules.d.ts +4 -0
- package/dist/project/host-modules.js +40 -0
- package/dist/project/host-modules.js.map +1 -0
- package/dist/project/jsonc.d.ts +2 -0
- package/dist/project/jsonc.js +27 -0
- package/dist/project/jsonc.js.map +1 -0
- package/dist/project/lower-await.d.ts +2 -0
- package/dist/project/lower-await.js +151 -0
- package/dist/project/lower-await.js.map +1 -0
- package/dist/project/node-project.d.ts +38 -0
- package/dist/project/node-project.js +362 -0
- package/dist/project/node-project.js.map +1 -0
- package/dist/project/own-module.d.ts +6 -0
- package/dist/project/own-module.js +13 -0
- package/dist/project/own-module.js.map +1 -0
- package/dist/project/scheduler.d.ts +2 -0
- package/dist/project/scheduler.js +3 -0
- package/dist/project/scheduler.js.map +1 -0
- package/dist/project/site.d.ts +74 -0
- package/dist/project/site.js +206 -0
- package/dist/project/site.js.map +1 -0
- package/dist/project/virtual-modules.d.ts +3 -0
- package/dist/project/worker-scope.d.ts +27 -0
- package/dist/project/worker-scope.js +46 -0
- package/dist/project/worker-scope.js.map +1 -0
- package/dist/project/workerd-message-channel.d.ts +13 -0
- package/dist/project/workerd-message-channel.js +137 -0
- package/dist/project/workerd-message-channel.js.map +1 -0
- package/dist/project/workers-global-names.d.ts +6 -0
- package/dist/project/workers-global-names.js +15 -0
- package/dist/project/workers-global-names.js.map +1 -0
- package/dist/project/workers-globals-module.d.ts +8 -0
- package/dist/project/workers-globals-module.js +9 -0
- package/dist/project/workers-globals-module.js.map +1 -0
- package/dist/project/workers-globals.d.ts +9 -0
- package/dist/project/workers-globals.js +19 -0
- package/dist/project/workers-globals.js.map +1 -0
- package/dist/project/wrangler.d.ts +116 -0
- package/dist/project/wrangler.js +342 -0
- package/dist/project/wrangler.js.map +1 -0
- package/dist/router/cell-router.d.ts +72 -0
- package/dist/router/cell-router.js +247 -0
- package/dist/router/cell-router.js.map +1 -0
- package/dist/router/shard-worker.d.ts +44 -0
- package/dist/router/shard-worker.js +163 -0
- package/dist/router/shard-worker.js.map +1 -0
- package/dist/transport/cells-http.d.ts +25 -0
- package/dist/transport/cells-http.js +141 -0
- package/dist/transport/cells-http.js.map +1 -0
- package/dist/transport/cells-link.d.ts +27 -0
- package/dist/transport/cells-link.js +71 -0
- package/dist/transport/cells-link.js.map +1 -0
- package/dist/transport/port.d.ts +26 -0
- package/dist/transport/port.js +100 -0
- package/dist/transport/port.js.map +1 -0
- package/dist/v8/codec.d.ts +6 -0
- package/dist/v8/codec.js +9 -0
- package/dist/v8/codec.js.map +1 -0
- package/dist/v8/serialize.d.ts +10 -0
- package/dist/v8/serialize.js +601 -0
- package/dist/v8/serialize.js.map +1 -0
- package/dist/ws/client.d.ts +25 -0
- package/dist/ws/client.js +203 -0
- package/dist/ws/client.js.map +1 -0
- package/dist/ws/control.d.ts +48 -0
- package/dist/ws/control.js +44 -0
- package/dist/ws/control.js.map +1 -0
- package/dist/ws/frames.d.ts +38 -0
- package/dist/ws/frames.js +39 -0
- package/dist/ws/frames.js.map +1 -0
- package/dist/ws/server.d.ts +99 -0
- package/dist/ws/server.js +295 -0
- package/dist/ws/server.js.map +1 -0
- package/dist/ws/sw-connector.d.ts +10 -0
- package/dist/ws/sw-connector.js +25 -0
- package/dist/ws/sw-connector.js.map +1 -0
- package/dist/ws/upgrade.d.ts +28 -0
- package/dist/ws/upgrade.js +75 -0
- package/dist/ws/upgrade.js.map +1 -0
- package/package.json +111 -3
- package/vendor/celld/LICENSE +202 -0
- package/vendor/celld/kv.js +1131 -0
- package/vendor/celld/queues.js +1656 -0
- package/vendor/celld/workflows.js +1610 -0
- package/vendor/html-rewriter-wasm/html_rewriter.js +925 -0
- package/vendor/html-rewriter-wasm/html_rewriter_bg.wasm +0 -0
- package/vendor/miniflare/d1-api.js +738 -0
- package/vendor/miniflare/d1-database.js +6978 -0
- package/vendor/miniflare/kv-namespace.js +3061 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// @ts-expect-error — vendored JS (scripts/vendor-celld.mjs)
|
|
2
|
+
import { queueService } from "../../vendor/celld/queues.js";
|
|
3
|
+
import { deserialize, serialize } from "../v8/serialize.js";
|
|
4
|
+
/**
|
|
5
|
+
* Queues exactly as celld implements them: celld's own queue cell, producer binding and
|
|
6
|
+
* consumer batch (harness.js, vendored unchanged, Apache-2.0) on a cellb host. The
|
|
7
|
+
* queue cell's SQLite layout (`__queue_messages`, `__queue_stats`, …) is therefore celld's, so
|
|
8
|
+
* a queue's pending messages move between celld and cellb with its cell.
|
|
9
|
+
*
|
|
10
|
+
* What the host supplies, as celld's engine does:
|
|
11
|
+
* - `__queue_policy`: celld_logic::queue (Rust) ported below — every deadline and lease rule;
|
|
12
|
+
* - `__queue_dispatch`: runs the consumer's `queue()` with celld's batch object, then posts the
|
|
13
|
+
* settlement to the queue cell (`/__qSettle`), after the alarm that leased the batch;
|
|
14
|
+
* - limits, consumer configuration, lease duration (celld's defaults).
|
|
15
|
+
*/
|
|
16
|
+
/** celld_logic::queue and queue_batching defaults (celld 0.6.2). */
|
|
17
|
+
export const CELLD_QUEUE_LIMITS = {
|
|
18
|
+
maxMessageBytes: 128_000,
|
|
19
|
+
maxBatchBytes: 256_000,
|
|
20
|
+
maxBatchMessages: 100,
|
|
21
|
+
producerGroupMs: 4,
|
|
22
|
+
maxConcurrency: 250,
|
|
23
|
+
maxDelaySeconds: 86_400,
|
|
24
|
+
retentionMs: 4 * 24 * 60 * 60 * 1000,
|
|
25
|
+
sweepBatchRows: 256,
|
|
26
|
+
};
|
|
27
|
+
/** admission wait (1 s) + handler budget (300 s) + operation deadline (15 s). */
|
|
28
|
+
const LEASE_DURATION_MS = 1_000 + 300_000 + 15_000;
|
|
29
|
+
const minDefined = (xs) => {
|
|
30
|
+
const v = xs.filter((x) => typeof x === "number");
|
|
31
|
+
return v.length ? Math.min(...v) : null;
|
|
32
|
+
};
|
|
33
|
+
const retryAt = (now, explicit, configured) => now + (explicit ?? configured ?? 0) * 1000;
|
|
34
|
+
const exhausted = (attempt, maxRetries) => attempt > maxRetries;
|
|
35
|
+
export function queuePolicy(request) {
|
|
36
|
+
switch (request.op) {
|
|
37
|
+
case "rearm": {
|
|
38
|
+
const d = minDefined([
|
|
39
|
+
request.batchDeadline,
|
|
40
|
+
request.earliestVisible,
|
|
41
|
+
request.earliestLeaseExpiry,
|
|
42
|
+
request.nextSweep,
|
|
43
|
+
]);
|
|
44
|
+
return d === null ? null : Math.max(d, request.now);
|
|
45
|
+
}
|
|
46
|
+
case "capacity":
|
|
47
|
+
return request.active < request.maximum;
|
|
48
|
+
case "retries":
|
|
49
|
+
return request.entries.map((e) => ({
|
|
50
|
+
at: retryAt(request.now, e.explicitSeconds, e.configuredSeconds),
|
|
51
|
+
exhausted: exhausted(e.attempt, e.maxRetries),
|
|
52
|
+
}));
|
|
53
|
+
case "expiry":
|
|
54
|
+
return request.entries.map((e) => {
|
|
55
|
+
const attempt = e.priorFailures + 1;
|
|
56
|
+
const action = e.purgeOnSettle
|
|
57
|
+
? { kind: "delete-purged" }
|
|
58
|
+
: exhausted(attempt, e.maxRetries)
|
|
59
|
+
? { kind: "exhausted" }
|
|
60
|
+
: { kind: "retry", at: retryAt(request.now, null, e.configuredSeconds) };
|
|
61
|
+
return { attempt, action };
|
|
62
|
+
});
|
|
63
|
+
case "batch": {
|
|
64
|
+
const leases = [];
|
|
65
|
+
const deletePurged = [];
|
|
66
|
+
for (const row of request.rows) {
|
|
67
|
+
const live = row.leasedUntil != null && row.leasedUntil > request.now;
|
|
68
|
+
if (row.purgeOnSettle) {
|
|
69
|
+
if (!live)
|
|
70
|
+
deletePurged.push(row.seq);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (leases.length >= request.maxBatchSize || row.visibleAt > request.now || live)
|
|
74
|
+
continue;
|
|
75
|
+
leases.push({
|
|
76
|
+
seq: row.seq,
|
|
77
|
+
generation: (BigInt(row.leaseGeneration) + 1n).toString(),
|
|
78
|
+
reclaimed: row.leasedUntil != null,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return { leases, deletePurged };
|
|
82
|
+
}
|
|
83
|
+
case "settlement": {
|
|
84
|
+
const current = request.current;
|
|
85
|
+
const submitted = request.submitted;
|
|
86
|
+
if (!current.length || current.length !== submitted.length)
|
|
87
|
+
return false;
|
|
88
|
+
if (current.some((m, i) => current.slice(0, i).some((p) => p.messageId === m.messageId)))
|
|
89
|
+
return false;
|
|
90
|
+
const matched = current.map(() => false);
|
|
91
|
+
for (const s of submitted) {
|
|
92
|
+
const i = current.findIndex((c, j) => !matched[j] &&
|
|
93
|
+
c.messageId === s.messageId &&
|
|
94
|
+
c.seq === s.seq &&
|
|
95
|
+
c.generation === s.generation);
|
|
96
|
+
if (i < 0)
|
|
97
|
+
return false;
|
|
98
|
+
matched[i] = true;
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
case "purge": {
|
|
103
|
+
const del = [];
|
|
104
|
+
const mark = [];
|
|
105
|
+
for (const row of request.rows)
|
|
106
|
+
(row.leaseIdPresent && row.leasedUntil != null && row.leasedUntil > request.now
|
|
107
|
+
? mark
|
|
108
|
+
: del).push(row.seq);
|
|
109
|
+
return { delete: del, markForSettle: mark };
|
|
110
|
+
}
|
|
111
|
+
default:
|
|
112
|
+
throw new Error(`unknown Queue policy operation ${JSON.stringify(request.op)}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const b64 = (bytes) => {
|
|
116
|
+
let s = "";
|
|
117
|
+
for (let i = 0; i < bytes.length; i += 0x8000)
|
|
118
|
+
s += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
|
|
119
|
+
return btoa(s);
|
|
120
|
+
};
|
|
121
|
+
const unb64 = (text) => Uint8Array.from(atob(text), (c) => c.charCodeAt(0));
|
|
122
|
+
export function celldQueues(opts) {
|
|
123
|
+
const consumers = {};
|
|
124
|
+
for (const c of opts.consumers)
|
|
125
|
+
consumers[c.queue] = {
|
|
126
|
+
script: "default",
|
|
127
|
+
maxBatchSize: c.max_batch_size ?? 10,
|
|
128
|
+
maxBatchTimeoutMs: (c.max_batch_timeout ?? 5) * 1000,
|
|
129
|
+
maxRetries: c.max_retries ?? 3,
|
|
130
|
+
...(c.dead_letter_queue ? { deadLetterQueue: c.dead_letter_queue } : {}),
|
|
131
|
+
...(c.max_concurrency !== undefined ? { maxConcurrency: c.max_concurrency } : {}),
|
|
132
|
+
...(c.retry_delay !== undefined ? { retryDelaySeconds: c.retry_delay } : {}),
|
|
133
|
+
};
|
|
134
|
+
const cell = {
|
|
135
|
+
queueLimits: CELLD_QUEUE_LIMITS,
|
|
136
|
+
queueConsumers: consumers,
|
|
137
|
+
queueLeaseDurationMs: LEASE_DURATION_MS,
|
|
138
|
+
compat: { queueJsonMessages: (opts.compatibilityDate ?? "2024-03-18") >= "2024-03-18" },
|
|
139
|
+
makeNamespace: () => opts.namespace(),
|
|
140
|
+
};
|
|
141
|
+
// biome-ignore lint/style/useConst: assigned after the service exists
|
|
142
|
+
let service;
|
|
143
|
+
const deliver = async (queue, batchJson) => {
|
|
144
|
+
const batch = JSON.parse(batchJson);
|
|
145
|
+
const incoming = {
|
|
146
|
+
queue,
|
|
147
|
+
messages: batch.messages.map((m) => ({
|
|
148
|
+
id: m.id,
|
|
149
|
+
timestamp: m.timestampMs,
|
|
150
|
+
body: unb64(m.bodyBase64),
|
|
151
|
+
contentType: m.contentType,
|
|
152
|
+
attempts: m.attempts,
|
|
153
|
+
})),
|
|
154
|
+
metrics: batch.metrics ?? {},
|
|
155
|
+
};
|
|
156
|
+
const result = await service.dispatchQueue("default", incoming);
|
|
157
|
+
const settlement = {
|
|
158
|
+
leaseId: batch.leaseId,
|
|
159
|
+
leases: batch.leases,
|
|
160
|
+
outcome: result.outcome,
|
|
161
|
+
error: result.error,
|
|
162
|
+
ackAll: result.ackAll,
|
|
163
|
+
retryBatch: result.retryBatch,
|
|
164
|
+
explicitAcks: result.explicitAcks,
|
|
165
|
+
retryMessages: result.retryMessages,
|
|
166
|
+
};
|
|
167
|
+
// celld: a refused settlement leaves the lease to expire (and the messages to retry).
|
|
168
|
+
await opts
|
|
169
|
+
.namespace()
|
|
170
|
+
.getByName(queue)
|
|
171
|
+
.fetch(new Request("https://queue.celld.internal/__qSettle", {
|
|
172
|
+
method: "POST",
|
|
173
|
+
headers: { "content-type": "application/json" },
|
|
174
|
+
body: JSON.stringify(settlement),
|
|
175
|
+
}));
|
|
176
|
+
};
|
|
177
|
+
service = queueService({
|
|
178
|
+
__cell: cell,
|
|
179
|
+
__queue_policy: (json) => JSON.stringify(queuePolicy(JSON.parse(json))),
|
|
180
|
+
// The cell awaits only the hand-off; the consumer runs after its alarm (celld: separate event).
|
|
181
|
+
__queue_dispatch: async (_script, queue, batchJson) => {
|
|
182
|
+
const work = new Promise((r) => setTimeout(r, 0)).then(() => deliver(queue, batchJson));
|
|
183
|
+
if (opts.defer)
|
|
184
|
+
opts.defer(work);
|
|
185
|
+
else
|
|
186
|
+
void work.catch((e) => console.error("Queue delivery failed:", e));
|
|
187
|
+
},
|
|
188
|
+
// celld: set the alarm and wait for its wake entry. Here the wake entry is published with
|
|
189
|
+
// the commit (output gate), so setting the alarm is all; the cell passes its storage's
|
|
190
|
+
// `_scope`, which the wrapper class below points at the storage itself.
|
|
191
|
+
__queue_alarm_set_wait: (storage, at) => storage.setAlarm(at),
|
|
192
|
+
__sc_encode: serialize,
|
|
193
|
+
__sc_decode: deserialize,
|
|
194
|
+
__cellSweepBatch: (storage, select, reclaim, limit) => {
|
|
195
|
+
let processed = 0;
|
|
196
|
+
storage.transactionSync(() => {
|
|
197
|
+
const rows = select(limit);
|
|
198
|
+
if (rows.length > limit)
|
|
199
|
+
throw new Error("a cell sweep exceeded its row bound");
|
|
200
|
+
for (const row of rows) {
|
|
201
|
+
reclaim(row);
|
|
202
|
+
processed++;
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
return processed;
|
|
206
|
+
},
|
|
207
|
+
__dispatchEntrypointMethodInCurrentEvent: async (_name, _method, batch) => opts.runConsumer(batch),
|
|
208
|
+
btoa: (bytes) => b64(bytes),
|
|
209
|
+
});
|
|
210
|
+
const Base = service.QueueCell;
|
|
211
|
+
class QueueCell extends Base {
|
|
212
|
+
constructor(ctx, env) {
|
|
213
|
+
Object.defineProperty(ctx.storage, "_scope", { value: ctx.storage, configurable: true });
|
|
214
|
+
super(ctx, env);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
QueueCell,
|
|
219
|
+
/** The producer binding (celld: `__makeQueue(queue, cellName, deliveryDelay)`). */
|
|
220
|
+
producer: (queue, deliveryDelay = 0) => service.makeQueue(queue, queue, deliveryDelay),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=celld-queues.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"celld-queues.js","sourceRoot":"","sources":["../../src/bindings/celld-queues.ts"],"names":[],"mappings":"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","sourcesContent":["// @ts-expect-error — vendored JS (scripts/vendor-celld.mjs)\nimport { queueService } from \"../../vendor/celld/queues.js\";\nimport { deserialize, serialize } from \"../v8/serialize.js\";\n\n/**\n * Queues exactly as celld implements them: celld's own queue cell, producer binding and\n * consumer batch (harness.js, vendored unchanged, Apache-2.0) on a cellb host. The\n * queue cell's SQLite layout (`__queue_messages`, `__queue_stats`, …) is therefore celld's, so\n * a queue's pending messages move between celld and cellb with its cell.\n *\n * What the host supplies, as celld's engine does:\n * - `__queue_policy`: celld_logic::queue (Rust) ported below — every deadline and lease rule;\n * - `__queue_dispatch`: runs the consumer's `queue()` with celld's batch object, then posts the\n * settlement to the queue cell (`/__qSettle`), after the alarm that leased the batch;\n * - limits, consumer configuration, lease duration (celld's defaults).\n */\n\n/** celld_logic::queue and queue_batching defaults (celld 0.6.2). */\nexport const CELLD_QUEUE_LIMITS = {\n maxMessageBytes: 128_000,\n maxBatchBytes: 256_000,\n maxBatchMessages: 100,\n producerGroupMs: 4,\n maxConcurrency: 250,\n maxDelaySeconds: 86_400,\n retentionMs: 4 * 24 * 60 * 60 * 1000,\n sweepBatchRows: 256,\n};\n/** admission wait (1 s) + handler budget (300 s) + operation deadline (15 s). */\nconst LEASE_DURATION_MS = 1_000 + 300_000 + 15_000;\n\nexport interface QueueConsumerConfig {\n queue: string;\n max_batch_size?: number;\n max_batch_timeout?: number;\n max_retries?: number;\n dead_letter_queue?: string;\n max_concurrency?: number;\n retry_delay?: number;\n}\n\n// ---- celld_logic::queue, ported (logic/queue.rs, celld/queue_policy.rs) ----\ntype J = Record<string, any>;\nconst minDefined = (xs: (number | null | undefined)[]) => {\n const v = xs.filter((x): x is number => typeof x === \"number\");\n return v.length ? Math.min(...v) : null;\n};\nconst retryAt = (now: number, explicit?: number | null, configured?: number | null) =>\n now + (explicit ?? configured ?? 0) * 1000;\nconst exhausted = (attempt: number, maxRetries: number) => attempt > maxRetries;\n\nexport function queuePolicy(request: J): unknown {\n switch (request.op) {\n case \"rearm\": {\n const d = minDefined([\n request.batchDeadline,\n request.earliestVisible,\n request.earliestLeaseExpiry,\n request.nextSweep,\n ]);\n return d === null ? null : Math.max(d, request.now);\n }\n case \"capacity\":\n return request.active < request.maximum;\n case \"retries\":\n return (request.entries as J[]).map((e) => ({\n at: retryAt(request.now, e.explicitSeconds, e.configuredSeconds),\n exhausted: exhausted(e.attempt, e.maxRetries),\n }));\n case \"expiry\":\n return (request.entries as J[]).map((e) => {\n const attempt = e.priorFailures + 1;\n const action = e.purgeOnSettle\n ? { kind: \"delete-purged\" }\n : exhausted(attempt, e.maxRetries)\n ? { kind: \"exhausted\" }\n : { kind: \"retry\", at: retryAt(request.now, null, e.configuredSeconds) };\n return { attempt, action };\n });\n case \"batch\": {\n const leases: J[] = [];\n const deletePurged: number[] = [];\n for (const row of request.rows as J[]) {\n const live = row.leasedUntil != null && row.leasedUntil > request.now;\n if (row.purgeOnSettle) {\n if (!live) deletePurged.push(row.seq);\n continue;\n }\n if (leases.length >= request.maxBatchSize || row.visibleAt > request.now || live) continue;\n leases.push({\n seq: row.seq,\n generation: (BigInt(row.leaseGeneration) + 1n).toString(),\n reclaimed: row.leasedUntil != null,\n });\n }\n return { leases, deletePurged };\n }\n case \"settlement\": {\n const current = request.current as J[];\n const submitted = request.submitted as J[];\n if (!current.length || current.length !== submitted.length) return false;\n if (current.some((m, i) => current.slice(0, i).some((p) => p.messageId === m.messageId)))\n return false;\n const matched = current.map(() => false);\n for (const s of submitted) {\n const i = current.findIndex(\n (c, j) =>\n !matched[j] &&\n c.messageId === s.messageId &&\n c.seq === s.seq &&\n c.generation === s.generation,\n );\n if (i < 0) return false;\n matched[i] = true;\n }\n return true;\n }\n case \"purge\": {\n const del: string[] = [];\n const mark: string[] = [];\n for (const row of request.rows as J[])\n (row.leaseIdPresent && row.leasedUntil != null && row.leasedUntil > request.now\n ? mark\n : del\n ).push(row.seq);\n return { delete: del, markForSettle: mark };\n }\n default:\n throw new Error(`unknown Queue policy operation ${JSON.stringify(request.op)}`);\n }\n}\n\nconst b64 = (bytes: Uint8Array) => {\n let s = \"\";\n for (let i = 0; i < bytes.length; i += 0x8000)\n s += String.fromCharCode(...bytes.subarray(i, i + 0x8000));\n return btoa(s);\n};\nconst unb64 = (text: string) => Uint8Array.from(atob(text), (c) => c.charCodeAt(0));\n\nexport function celldQueues(opts: {\n /** The namespace of the `__Queue` cells (celld's fleet-wide namespace). */\n namespace: () => { getByName(name: string): { fetch(req: Request): Promise<Response> } };\n consumers: QueueConsumerConfig[];\n /** Runs the Worker's `queue(batch)` handler (celld: the consumer script's entrypoint). */\n runConsumer: (batch: unknown) => Promise<void>;\n /** Wrangler `compatibility_date` decides the default content type (json from 2024-03-18). */\n compatibilityDate?: string;\n /** Background work after the leasing alarm (the consumer runs outside the cell's event). */\n defer?: (work: Promise<unknown>) => void;\n}) {\n const consumers: Record<string, J> = {};\n for (const c of opts.consumers)\n consumers[c.queue] = {\n script: \"default\",\n maxBatchSize: c.max_batch_size ?? 10,\n maxBatchTimeoutMs: (c.max_batch_timeout ?? 5) * 1000,\n maxRetries: c.max_retries ?? 3,\n ...(c.dead_letter_queue ? { deadLetterQueue: c.dead_letter_queue } : {}),\n ...(c.max_concurrency !== undefined ? { maxConcurrency: c.max_concurrency } : {}),\n ...(c.retry_delay !== undefined ? { retryDelaySeconds: c.retry_delay } : {}),\n };\n const cell = {\n queueLimits: CELLD_QUEUE_LIMITS,\n queueConsumers: consumers,\n queueLeaseDurationMs: LEASE_DURATION_MS,\n compat: { queueJsonMessages: (opts.compatibilityDate ?? \"2024-03-18\") >= \"2024-03-18\" },\n makeNamespace: () => opts.namespace(),\n };\n // biome-ignore lint/style/useConst: assigned after the service exists\n let service: {\n QueueCell: unknown;\n makeQueue: (q: string, cellName: string, delay: number) => unknown;\n dispatchQueue: (name: string, incoming: J) => Promise<J>;\n };\n const deliver = async (queue: string, batchJson: string) => {\n const batch = JSON.parse(batchJson) as J;\n const incoming = {\n queue,\n messages: (batch.messages as J[]).map((m) => ({\n id: m.id,\n timestamp: m.timestampMs,\n body: unb64(m.bodyBase64),\n contentType: m.contentType,\n attempts: m.attempts,\n })),\n metrics: batch.metrics ?? {},\n };\n const result = await service.dispatchQueue(\"default\", incoming);\n const settlement = {\n leaseId: batch.leaseId,\n leases: batch.leases,\n outcome: result.outcome,\n error: result.error,\n ackAll: result.ackAll,\n retryBatch: result.retryBatch,\n explicitAcks: result.explicitAcks,\n retryMessages: result.retryMessages,\n };\n // celld: a refused settlement leaves the lease to expire (and the messages to retry).\n await opts\n .namespace()\n .getByName(queue)\n .fetch(\n new Request(\"https://queue.celld.internal/__qSettle\", {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(settlement),\n }),\n );\n };\n service = queueService({\n __cell: cell,\n __queue_policy: (json: string) => JSON.stringify(queuePolicy(JSON.parse(json))),\n // The cell awaits only the hand-off; the consumer runs after its alarm (celld: separate event).\n __queue_dispatch: async (_script: string, queue: string, batchJson: string) => {\n const work = new Promise((r) => setTimeout(r, 0)).then(() => deliver(queue, batchJson));\n if (opts.defer) opts.defer(work);\n else void work.catch((e) => console.error(\"Queue delivery failed:\", e));\n },\n // celld: set the alarm and wait for its wake entry. Here the wake entry is published with\n // the commit (output gate), so setting the alarm is all; the cell passes its storage's\n // `_scope`, which the wrapper class below points at the storage itself.\n __queue_alarm_set_wait: (storage: { setAlarm(at: number): Promise<void> }, at: number) =>\n storage.setAlarm(at),\n __sc_encode: serialize,\n __sc_decode: deserialize,\n __cellSweepBatch: (\n storage: any,\n select: (n: number) => unknown[],\n reclaim: (row: unknown) => void,\n limit: number,\n ) => {\n let processed = 0;\n storage.transactionSync(() => {\n const rows = select(limit);\n if (rows.length > limit) throw new Error(\"a cell sweep exceeded its row bound\");\n for (const row of rows) {\n reclaim(row);\n processed++;\n }\n });\n return processed;\n },\n __dispatchEntrypointMethodInCurrentEvent: async (\n _name: string,\n _method: string,\n batch: unknown,\n ) => opts.runConsumer(batch),\n btoa: (bytes: Uint8Array) => b64(bytes),\n });\n const Base = service.QueueCell as new (ctx: any, env: any) => object;\n class QueueCell extends Base {\n constructor(ctx: any, env: any) {\n Object.defineProperty(ctx.storage, \"_scope\", { value: ctx.storage, configurable: true });\n super(ctx, env);\n }\n }\n return {\n QueueCell,\n /** The producer binding (celld: `__makeQueue(queue, cellName, deliveryDelay)`). */\n producer: (queue: string, deliveryDelay = 0) => service.makeQueue(queue, queue, deliveryDelay),\n };\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflows exactly as celld implements them: celld's own instance cell and binding
|
|
3
|
+
* (harness.js, vendored unchanged, Apache-2.0) on a cellb host. An instance is a cell
|
|
4
|
+
* `__Workflow.<script>:<id of "<workflow>/<instance>">` whose state is celld's KV records
|
|
5
|
+
* (`__wf.meta`, `__wf.<generation>.step.<n>.<name>`, V8-serialized) plus its alarm — so a
|
|
6
|
+
* running or sleeping instance moves between celld and cellb with its cell.
|
|
7
|
+
*/
|
|
8
|
+
export declare function celldWorkflows(opts: {
|
|
9
|
+
script: string;
|
|
10
|
+
/** workflow name → exported class name (wrangler `workflows`). */
|
|
11
|
+
workflows: Record<string, string>;
|
|
12
|
+
/** The Worker's exports (the workflow classes live there). */
|
|
13
|
+
exports: () => Record<string, unknown>;
|
|
14
|
+
env: () => unknown;
|
|
15
|
+
namespace: () => unknown;
|
|
16
|
+
waitUntil?: (p: Promise<unknown>) => void;
|
|
17
|
+
}): {
|
|
18
|
+
className: string;
|
|
19
|
+
WorkflowCell: any;
|
|
20
|
+
binding: (workflowName: string) => any;
|
|
21
|
+
NonRetryableError: any;
|
|
22
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// @ts-expect-error — vendored JS (scripts/vendor-celld.mjs)
|
|
2
|
+
import { workflowService } from "../../vendor/celld/workflows.js";
|
|
3
|
+
import { serialize } from "../v8/serialize.js";
|
|
4
|
+
/**
|
|
5
|
+
* Workflows exactly as celld implements them: celld's own instance cell and binding
|
|
6
|
+
* (harness.js, vendored unchanged, Apache-2.0) on a cellb host. An instance is a cell
|
|
7
|
+
* `__Workflow.<script>:<id of "<workflow>/<instance>">` whose state is celld's KV records
|
|
8
|
+
* (`__wf.meta`, `__wf.<generation>.step.<n>.<name>`, V8-serialized) plus its alarm — so a
|
|
9
|
+
* running or sleeping instance moves between celld and cellb with its cell.
|
|
10
|
+
*/
|
|
11
|
+
export function celldWorkflows(opts) {
|
|
12
|
+
// Async-context frame of the step being run (celld: a CPED-backed frame). A plain slot here:
|
|
13
|
+
// it serves celld's check that refuses a step started inside another step's callback.
|
|
14
|
+
let frame;
|
|
15
|
+
const service = workflowService({
|
|
16
|
+
__cell: {
|
|
17
|
+
get workflows() {
|
|
18
|
+
return opts.workflows;
|
|
19
|
+
},
|
|
20
|
+
get env() {
|
|
21
|
+
return opts.env();
|
|
22
|
+
},
|
|
23
|
+
script: opts.script,
|
|
24
|
+
makeNamespace: () => opts.namespace(),
|
|
25
|
+
},
|
|
26
|
+
__cf: {
|
|
27
|
+
get exports() {
|
|
28
|
+
return opts.exports();
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
__sc_encode: serialize,
|
|
32
|
+
__als_get: () => frame,
|
|
33
|
+
__als_set: (f) => {
|
|
34
|
+
frame = f;
|
|
35
|
+
},
|
|
36
|
+
// Inside the instance's transactionSync: the storage itself is the "scope".
|
|
37
|
+
__alarm_set: (storage, at) => storage.setAlarmSync(at),
|
|
38
|
+
__alarm_delete: (storage) => storage.clearAlarmSync(),
|
|
39
|
+
__registerWaitUntil: (p) => {
|
|
40
|
+
if (opts.waitUntil)
|
|
41
|
+
opts.waitUntil(p);
|
|
42
|
+
else
|
|
43
|
+
void Promise.resolve(p).catch(() => { });
|
|
44
|
+
},
|
|
45
|
+
__ctxRun: (_frame, fn) => fn(),
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
className: `__Workflow.${opts.script}`,
|
|
49
|
+
WorkflowCell: service.WorkflowCell,
|
|
50
|
+
binding: (workflowName) => service.makeWorkflow(workflowName),
|
|
51
|
+
NonRetryableError: service.NonRetryableError,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=celld-workflows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"celld-workflows.js","sourceRoot":"","sources":["../../src/bindings/celld-workflows.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAS9B;IACC,6FAA6F;IAC7F,sFAAsF;IACtF,IAAI,KAAc,CAAC;IACnB,MAAM,OAAO,GAAG,eAAe,CAAC;QAC9B,MAAM,EAAE;YACN,IAAI,SAAS;gBACX,OAAO,IAAI,CAAC,SAAS,CAAC;YACxB,CAAC;YACD,IAAI,GAAG;gBACL,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC;YACpB,CAAC;YACD,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,aAAa,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE;SACtC;QACD,IAAI,EAAE;YACJ,IAAI,OAAO;gBACT,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;YACxB,CAAC;SACF;QACD,WAAW,EAAE,SAAS;QACtB,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK;QACtB,SAAS,EAAE,CAAC,CAAU,EAAE,EAAE;YACxB,KAAK,GAAG,CAAC,CAAC;QACZ,CAAC;QACD,4EAA4E;QAC5E,WAAW,EAAE,CAAC,OAA2C,EAAE,EAAU,EAAE,EAAE,CACvE,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QAC1B,cAAc,EAAE,CAAC,OAAmC,EAAE,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE;QACjF,mBAAmB,EAAE,CAAC,CAAmB,EAAE,EAAE;YAC3C,IAAI,IAAI,CAAC,SAAS;gBAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;;gBACjC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC/C,CAAC;QACD,QAAQ,EAAE,CAAC,MAAe,EAAE,EAAiB,EAAE,EAAE,CAAC,EAAE,EAAE;KACvD,CAAC,CAAC;IACH,OAAO;QACL,SAAS,EAAE,cAAc,IAAI,CAAC,MAAM,EAAE;QACtC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,OAAO,EAAE,CAAC,YAAoB,EAAE,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC;QACrE,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;KAC7C,CAAC;AACJ,CAAC","sourcesContent":["// @ts-expect-error — vendored JS (scripts/vendor-celld.mjs)\nimport { workflowService } from \"../../vendor/celld/workflows.js\";\nimport { serialize } from \"../v8/serialize.js\";\n\n/**\n * Workflows exactly as celld implements them: celld's own instance cell and binding\n * (harness.js, vendored unchanged, Apache-2.0) on a cellb host. An instance is a cell\n * `__Workflow.<script>:<id of \"<workflow>/<instance>\">` whose state is celld's KV records\n * (`__wf.meta`, `__wf.<generation>.step.<n>.<name>`, V8-serialized) plus its alarm — so a\n * running or sleeping instance moves between celld and cellb with its cell.\n */\nexport function celldWorkflows(opts: {\n script: string;\n /** workflow name → exported class name (wrangler `workflows`). */\n workflows: Record<string, string>;\n /** The Worker's exports (the workflow classes live there). */\n exports: () => Record<string, unknown>;\n env: () => unknown;\n namespace: () => unknown;\n waitUntil?: (p: Promise<unknown>) => void;\n}) {\n // Async-context frame of the step being run (celld: a CPED-backed frame). A plain slot here:\n // it serves celld's check that refuses a step started inside another step's callback.\n let frame: unknown;\n const service = workflowService({\n __cell: {\n get workflows() {\n return opts.workflows;\n },\n get env() {\n return opts.env();\n },\n script: opts.script,\n makeNamespace: () => opts.namespace(),\n },\n __cf: {\n get exports() {\n return opts.exports();\n },\n },\n __sc_encode: serialize,\n __als_get: () => frame,\n __als_set: (f: unknown) => {\n frame = f;\n },\n // Inside the instance's transactionSync: the storage itself is the \"scope\".\n __alarm_set: (storage: { setAlarmSync(at: number): void }, at: number) =>\n storage.setAlarmSync(at),\n __alarm_delete: (storage: { clearAlarmSync(): void }) => storage.clearAlarmSync(),\n __registerWaitUntil: (p: Promise<unknown>) => {\n if (opts.waitUntil) opts.waitUntil(p);\n else void Promise.resolve(p).catch(() => {});\n },\n __ctxRun: (_frame: unknown, fn: () => unknown) => fn(),\n });\n return {\n className: `__Workflow.${opts.script}`,\n WorkflowCell: service.WorkflowCell,\n binding: (workflowName: string) => service.makeWorkflow(workflowName),\n NonRetryableError: service.NonRetryableError,\n };\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface CronSchedule {
|
|
2
|
+
readonly text: string;
|
|
3
|
+
matches(t: number): boolean;
|
|
4
|
+
/** First occurrence strictly after `t` (ms), at a whole minute. */
|
|
5
|
+
next(t: number): number;
|
|
6
|
+
}
|
|
7
|
+
export declare function parseCron(text: string): CronSchedule;
|
|
8
|
+
export interface CronClock {
|
|
9
|
+
now(): number;
|
|
10
|
+
setTimeout(fn: () => void, ms: number): unknown;
|
|
11
|
+
clearTimeout(h: unknown): void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Runs the Worker's `scheduled(controller, env, ctx)` for each expression, as celld does:
|
|
15
|
+
* one invocation at a time with its `waitUntil()` work drained before the next; a throwing
|
|
16
|
+
* handler retries after 4 s, doubling, until six failures, `controller.noRetry()` or the next
|
|
17
|
+
* occurrence; `scheduledTime` names the occurrence, not the attempt.
|
|
18
|
+
*/
|
|
19
|
+
/** Where the last fired occurrence of each expression is kept (for the downtime catch-up). */
|
|
20
|
+
export interface CronStateStore {
|
|
21
|
+
get(expression: string): number | undefined;
|
|
22
|
+
set(expression: string, scheduledTime: number): void;
|
|
23
|
+
}
|
|
24
|
+
export declare function startCronTriggers(expressions: string[], handler: (controller: unknown, ctx: {
|
|
25
|
+
waitUntil(p: Promise<unknown>): void;
|
|
26
|
+
passThroughOnException(): void;
|
|
27
|
+
}) => unknown, clock: CronClock, state?: CronStateStore): () => void;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cron Triggers in celld's dialect (docs/services/cron-triggers.md, = Cloudflare's):
|
|
3
|
+
* five fields — minute, hour, day of month, month, day of week — UTC, one-minute resolution.
|
|
4
|
+
* `*`, values, `a-b`, `a,b`, `/n`; month and weekday names; weekdays 1–7 from SUNDAY;
|
|
5
|
+
* day of month `L`, `L-<n>`, `LW`, `L-<n>W`, `<d>W`; day of week `<dow>L`, `<dow>#<n>`.
|
|
6
|
+
* When both day fields are restricted they form a union. Refused like celld: descending
|
|
7
|
+
* ranges, lists containing `*`, steps wider than the field.
|
|
8
|
+
*/
|
|
9
|
+
const MONTHS = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
|
|
10
|
+
const DAYS = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
|
|
11
|
+
function parseSimple(text, min, max, names, nameBase = 1) {
|
|
12
|
+
if (text === "*")
|
|
13
|
+
return { any: true, values: new Set(range(min, max)) };
|
|
14
|
+
const parts = text.split(",");
|
|
15
|
+
if (parts.length > 1 && parts.includes("*"))
|
|
16
|
+
throw new Error(`cron: a list cannot contain "*": ${text}`);
|
|
17
|
+
const values = new Set();
|
|
18
|
+
const value = (v) => {
|
|
19
|
+
const i = names?.indexOf(v.toUpperCase()) ?? -1;
|
|
20
|
+
const n = i >= 0 ? i + nameBase : Number(v);
|
|
21
|
+
if (!Number.isInteger(n) || n < min || n > max)
|
|
22
|
+
throw new Error(`cron: value out of range: ${v}`);
|
|
23
|
+
return n;
|
|
24
|
+
};
|
|
25
|
+
for (const part of parts) {
|
|
26
|
+
const [base, stepText] = part.split("/");
|
|
27
|
+
const step = stepText === undefined ? 1 : Number(stepText);
|
|
28
|
+
if (!Number.isInteger(step) || step < 1 || step > max - min)
|
|
29
|
+
throw new Error(`cron: bad step: ${part}`);
|
|
30
|
+
let lo;
|
|
31
|
+
let hi;
|
|
32
|
+
if (base === "*")
|
|
33
|
+
[lo, hi] = [min, max];
|
|
34
|
+
else if (base.includes("-")) {
|
|
35
|
+
const [a, b] = base.split("-");
|
|
36
|
+
[lo, hi] = [value(a), value(b)];
|
|
37
|
+
if (lo > hi)
|
|
38
|
+
throw new Error(`cron: descending range: ${base}`);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
lo = value(base);
|
|
42
|
+
hi = stepText === undefined ? lo : max;
|
|
43
|
+
}
|
|
44
|
+
for (let v = lo; v <= hi; v += step)
|
|
45
|
+
values.add(v);
|
|
46
|
+
}
|
|
47
|
+
return { any: false, values };
|
|
48
|
+
}
|
|
49
|
+
const range = (a, b) => Array.from({ length: b - a + 1 }, (_, i) => a + i);
|
|
50
|
+
const daysInMonth = (y, m) => new Date(Date.UTC(y, m + 1, 0)).getUTCDate();
|
|
51
|
+
function parseDom(text) {
|
|
52
|
+
if (text === "*")
|
|
53
|
+
return { any: true, match: () => true };
|
|
54
|
+
const rules = [];
|
|
55
|
+
for (const part of text.split(",")) {
|
|
56
|
+
const up = part.toUpperCase();
|
|
57
|
+
const last = /^L(?:-(\d+))?(W?)$/.exec(up);
|
|
58
|
+
const weekdayOf = /^(\d+)W$/.exec(up);
|
|
59
|
+
if (last) {
|
|
60
|
+
const m = last;
|
|
61
|
+
const back = Number(m[1] ?? 0);
|
|
62
|
+
const weekday = m[2] === "W";
|
|
63
|
+
rules.push((y, mo, d) => {
|
|
64
|
+
const target = daysInMonth(y, mo) - back;
|
|
65
|
+
return d === (weekday ? nearestWeekday(y, mo, target) : target);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
else if (weekdayOf) {
|
|
69
|
+
const day = Number(weekdayOf[1]);
|
|
70
|
+
rules.push((y, mo, d) => d === nearestWeekday(y, mo, Math.min(day, daysInMonth(y, mo))));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
const f = parseSimple(part, 1, 31);
|
|
74
|
+
rules.push((_y, _m, d) => f.values.has(d));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { any: false, match: (y, mo, d) => rules.some((r) => r(y, mo, d)) };
|
|
78
|
+
}
|
|
79
|
+
function nearestWeekday(y, m, day) {
|
|
80
|
+
const dow = new Date(Date.UTC(y, m, day)).getUTCDay();
|
|
81
|
+
if (dow === 6)
|
|
82
|
+
return day > 1 ? day - 1 : day + 2;
|
|
83
|
+
if (dow === 0)
|
|
84
|
+
return day < daysInMonth(y, m) ? day + 1 : day - 2;
|
|
85
|
+
return day;
|
|
86
|
+
}
|
|
87
|
+
function parseDow(text) {
|
|
88
|
+
if (text === "*")
|
|
89
|
+
return { any: true, match: () => true };
|
|
90
|
+
const rules = [];
|
|
91
|
+
const dowOf = (v) => {
|
|
92
|
+
const i = DAYS.indexOf(v.toUpperCase());
|
|
93
|
+
const n = i >= 0 ? i + 1 : Number(v);
|
|
94
|
+
if (!Number.isInteger(n) || n < 1 || n > 7)
|
|
95
|
+
throw new Error(`cron: weekday out of range: ${v}`);
|
|
96
|
+
return n - 1; // JS getUTCDay(): 0 = Sunday
|
|
97
|
+
};
|
|
98
|
+
for (const part of text.split(",")) {
|
|
99
|
+
const lastOf = /^(\w+)L$/i.exec(part);
|
|
100
|
+
const nthOf = /^(\w+)#(\d)$/.exec(part);
|
|
101
|
+
if (lastOf && !/^\d+$/.test(part)) {
|
|
102
|
+
const dow = dowOf(lastOf[1]);
|
|
103
|
+
rules.push((y, mo, d) => new Date(Date.UTC(y, mo, d)).getUTCDay() === dow && d + 7 > daysInMonth(y, mo));
|
|
104
|
+
}
|
|
105
|
+
else if (nthOf) {
|
|
106
|
+
const dow = dowOf(nthOf[1]);
|
|
107
|
+
const nth = Number(nthOf[2]);
|
|
108
|
+
rules.push((y, mo, d) => new Date(Date.UTC(y, mo, d)).getUTCDay() === dow && Math.ceil(d / 7) === nth);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
const f = parseSimple(part, 1, 7, DAYS, 1);
|
|
112
|
+
rules.push((y, mo, d) => f.values.has(new Date(Date.UTC(y, mo, d)).getUTCDay() + 1));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { any: false, match: (y, mo, d) => rules.some((r) => r(y, mo, d)) };
|
|
116
|
+
}
|
|
117
|
+
export function parseCron(text) {
|
|
118
|
+
const fields = text.trim().split(/\s+/);
|
|
119
|
+
if (fields.length !== 5)
|
|
120
|
+
throw new Error(`cron: expected 5 fields: ${text}`);
|
|
121
|
+
const minute = parseSimple(fields[0], 0, 59);
|
|
122
|
+
const hour = parseSimple(fields[1], 0, 23);
|
|
123
|
+
const dom = parseDom(fields[2]);
|
|
124
|
+
const month = parseSimple(fields[3], 1, 12, MONTHS, 1);
|
|
125
|
+
const dow = parseDow(fields[4]);
|
|
126
|
+
const dayOk = (y, m, d) => dom.any && dow.any
|
|
127
|
+
? true
|
|
128
|
+
: dom.any
|
|
129
|
+
? dow.match(y, m, d)
|
|
130
|
+
: dow.any
|
|
131
|
+
? dom.match(y, m, d)
|
|
132
|
+
: dom.match(y, m, d) || dow.match(y, m, d);
|
|
133
|
+
const matches = (t) => {
|
|
134
|
+
const x = new Date(t);
|
|
135
|
+
return (minute.values.has(x.getUTCMinutes()) &&
|
|
136
|
+
hour.values.has(x.getUTCHours()) &&
|
|
137
|
+
month.values.has(x.getUTCMonth() + 1) &&
|
|
138
|
+
dayOk(x.getUTCFullYear(), x.getUTCMonth(), x.getUTCDate()));
|
|
139
|
+
};
|
|
140
|
+
return {
|
|
141
|
+
text,
|
|
142
|
+
matches,
|
|
143
|
+
next(t) {
|
|
144
|
+
let c = Math.floor(t / 60_000) * 60_000 + 60_000;
|
|
145
|
+
const limit = c + 5 * 366 * 86_400_000;
|
|
146
|
+
while (c < limit) {
|
|
147
|
+
const x = new Date(c);
|
|
148
|
+
if (!month.values.has(x.getUTCMonth() + 1) ||
|
|
149
|
+
!dayOk(x.getUTCFullYear(), x.getUTCMonth(), x.getUTCDate())) {
|
|
150
|
+
c = Date.UTC(x.getUTCFullYear(), x.getUTCMonth(), x.getUTCDate() + 1); // next day, 00:00
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (matches(c))
|
|
154
|
+
return c;
|
|
155
|
+
c += 60_000;
|
|
156
|
+
}
|
|
157
|
+
throw new Error(`cron: no occurrence within 5 years: ${text}`);
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
export function startCronTriggers(expressions, handler, clock, state) {
|
|
162
|
+
const schedules = expressions.map(parseCron);
|
|
163
|
+
const timers = new Set();
|
|
164
|
+
let chain = Promise.resolve();
|
|
165
|
+
let stopped = false;
|
|
166
|
+
const at = (t, fn) => {
|
|
167
|
+
const h = clock.setTimeout(() => {
|
|
168
|
+
timers.delete(h);
|
|
169
|
+
if (!stopped)
|
|
170
|
+
fn();
|
|
171
|
+
}, Math.max(0, t - clock.now()));
|
|
172
|
+
timers.add(h);
|
|
173
|
+
};
|
|
174
|
+
const run = (s, scheduledTime, failures, nextOccurrence) => {
|
|
175
|
+
chain = chain.then(async () => {
|
|
176
|
+
let retry = true;
|
|
177
|
+
const pending = [];
|
|
178
|
+
const controller = {
|
|
179
|
+
cron: s.text,
|
|
180
|
+
scheduledTime,
|
|
181
|
+
type: "scheduled",
|
|
182
|
+
noRetry: () => {
|
|
183
|
+
retry = false;
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
try {
|
|
187
|
+
await handler(controller, {
|
|
188
|
+
waitUntil: (p) => pending.push(p),
|
|
189
|
+
passThroughOnException() { },
|
|
190
|
+
});
|
|
191
|
+
await Promise.allSettled(pending);
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
await Promise.allSettled(pending);
|
|
195
|
+
const delay = 4_000 * 2 ** failures;
|
|
196
|
+
if (retry && failures + 1 < 6 && clock.now() + delay < nextOccurrence)
|
|
197
|
+
at(clock.now() + delay, () => run(s, scheduledTime, failures + 1, nextOccurrence));
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
const arm = (s, from) => {
|
|
202
|
+
const t = s.next(from);
|
|
203
|
+
at(t, () => {
|
|
204
|
+
state?.set(s.text, t);
|
|
205
|
+
run(s, t, 0, s.next(t));
|
|
206
|
+
arm(s, t);
|
|
207
|
+
});
|
|
208
|
+
};
|
|
209
|
+
// After downtime: run ONE missed occurrence (the latest) and skip the rest, as celld does.
|
|
210
|
+
for (const s of schedules) {
|
|
211
|
+
const last = state?.get(s.text);
|
|
212
|
+
if (last === undefined)
|
|
213
|
+
continue;
|
|
214
|
+
let missed;
|
|
215
|
+
const now = clock.now();
|
|
216
|
+
for (let t = s.next(last), n = 0; t <= now && n < 600_000; t = s.next(t), n++)
|
|
217
|
+
missed = t;
|
|
218
|
+
if (missed !== undefined) {
|
|
219
|
+
state?.set(s.text, missed);
|
|
220
|
+
run(s, missed, 0, s.next(missed));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
for (const s of schedules)
|
|
224
|
+
arm(s, clock.now());
|
|
225
|
+
return () => {
|
|
226
|
+
stopped = true;
|
|
227
|
+
for (const h of timers)
|
|
228
|
+
clock.clearTimeout(h);
|
|
229
|
+
timers.clear();
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=cron.js.map
|