@lunora/platform-node 1.0.0-alpha.9 → 1.0.0-alpha.91
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/conformance/index.mjs +1 -1
- package/dist/index.d.mts +296 -75
- package/dist/index.d.ts +296 -75
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/createNodeGlobalStore-DiGxjmD7.mjs +1 -0
- package/dist/packem_shared/createNodePlatform-BJFsyOy5.mjs +1 -0
- package/dist/packem_shared/createNodeQueueHost-CttDb5sY.mjs +17 -0
- package/dist/packem_shared/createNodeR2Bucket-CajTvuOy.mjs +1 -0
- package/dist/packem_shared/createNodeSchedulerHost-BEAX6I4m.mjs +15 -0
- package/dist/packem_shared/createNodeShardHost-Da19DFX0.mjs +1 -0
- package/dist/packem_shared/createNodeShardKvStore-DVi1OsvH.mjs +1 -0
- package/dist/packem_shared/createNodeShardRegistry-D-KxwdUc.mjs +1 -0
- package/dist/packem_shared/createNodeShardState-D6XMWbFs.mjs +1 -0
- package/dist/packem_shared/createNodeSocketHost-DQs85G7W.mjs +2 -0
- package/dist/packem_shared/createNodeWorkflowHost-B5Aiwj3L.mjs +1 -0
- package/dist/packem_shared/{createNodeWorkflowStore-jlNR6Y3h.mjs → createNodeWorkflowStore-CL7RYP6H.mjs} +5 -5
- package/dist/packem_shared/{to-array-buffer-CP4NlF-y.mjs → to-array-buffer-DVyXU4Ep.mjs} +1 -1
- package/package.json +13 -10
- package/dist/packem_shared/createNodeGlobalStore-BvteXWRK.mjs +0 -1
- package/dist/packem_shared/createNodePlatform-BQimmgT9.mjs +0 -1
- package/dist/packem_shared/createNodeQueueHost-CLQ5hOBf.mjs +0 -17
- package/dist/packem_shared/createNodeR2Bucket-Wb8pyi2X.mjs +0 -1
- package/dist/packem_shared/createNodeSchedulerHost-DRjZhuxe.mjs +0 -15
- package/dist/packem_shared/createNodeShardHost-DEHe5Vcm.mjs +0 -1
- package/dist/packem_shared/createNodeShardKvStore-BfKCQTjE.mjs +0 -1
- package/dist/packem_shared/createNodeShardRegistry-0iSMdi9-.mjs +0 -1
- package/dist/packem_shared/createNodeShardState-DdOj9FzJ.mjs +0 -1
- package/dist/packem_shared/createNodeSocketHost-CytowrYN.mjs +0 -2
- package/dist/packem_shared/createNodeWorkflowHost-Dht_jgSv.mjs +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createNodeShardKvStore as
|
|
1
|
+
import{createNodeShardKvStore as N}from"../packem_shared/createNodeShardKvStore-DVi1OsvH.mjs";import{createNodeSchedulerHost as k}from"../packem_shared/createNodeSchedulerHost-BEAX6I4m.mjs";import{createNodeShardHost as D}from"../packem_shared/createNodeShardHost-Da19DFX0.mjs";import{createNodeShardRegistry as H}from"../packem_shared/createNodeShardRegistry-D-KxwdUc.mjs";import{createNodeSocketHost as T}from"../packem_shared/createNodeSocketHost-DQs85G7W.mjs";const P=()=>{const{database:o,dispose:m,host:p}=D(),h=N(o),c=H(),{directory:l}=c,{readFrames:w,restoreSocket:u,simulateRecycle:S,socket:y}=T(o),d=new Set,s=new Map,{dispose:f,scheduler:i,simulateDeadLetter:g}=k(o,{onDispatch:(e,a,t)=>{d.add(t.id),s.set(e,(s.get(e)??0)+1)}}),n=()=>{f(),m(),c.close()};return{awaitAlarmFired:async e=>{await new Promise(a=>{setTimeout(a,Math.max(0,e-Date.now())+30)})},awaitJobDispatched:async e=>{const t=(await i.list?.())?.find(r=>r.id===e);return t!==void 0&&await new Promise(r=>{setTimeout(r,Math.max(0,t.scheduledFor-Date.now())+30)}),d.has(e)},cleanup:n,cronTicks:e=>s.get(e)??0,directory:l,disposeTerminally:n,kv:h,readFrames:w,restoreSocket:u,scheduler:i,shard:p,simulateDeadLetter:g,simulateRecycle:S,socket:y}};export{P as createNodeConformanceHost};
|
package/dist/index.d.mts
CHANGED
|
@@ -14,13 +14,14 @@ type NodeGlobalContextDatabaseOptions = Omit<SqlCtxDbOptions, "dialect" | "exec"
|
|
|
14
14
|
/**
|
|
15
15
|
* Wrap a `better-sqlite3` connection as the async exec the store core consumes.
|
|
16
16
|
*
|
|
17
|
-
* `batch`
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
17
|
+
* `batch` runs the statements in order inside one `better-sqlite3`
|
|
18
|
+
* transaction, which is synchronous, so nothing else runs between them. There is
|
|
19
|
+
* no round trip to save; atomicity is the point. The store core's FTS5 search
|
|
20
|
+
* companion writes each document as an ordered list of statements, and without
|
|
21
|
+
* this two async writers interleaved at every `await` between them: one could
|
|
22
|
+
* move a document's mapping while the other's entry was mid-write, leaving an
|
|
23
|
+
* entry no later write or purge could reach. The `sqlite` dialect's contract
|
|
24
|
+
* requires an ordered, atomic `batch` for exactly that reason.
|
|
24
25
|
*/
|
|
25
26
|
declare const createNodeSqlExec: (database: Database.Database) => SqlCtxExec;
|
|
26
27
|
/** Options for {@link createNodeGlobalStore}. */
|
|
@@ -197,9 +198,12 @@ interface NodeShardHostOptions {
|
|
|
197
198
|
* `_lunora_alarm` when the host is constructed over an existing database,
|
|
198
199
|
* including an alarm whose time elapsed while nothing was running.
|
|
199
200
|
*
|
|
200
|
-
* A handler that throws is isolated to its own delivery
|
|
201
|
+
* A handler that throws is isolated to its own delivery — it never reaches
|
|
201
202
|
* the caller that set the alarm, because by then that call has long
|
|
202
|
-
* returned
|
|
203
|
+
* returned — and the wakeup is then re-delivered with backoff up to
|
|
204
|
+
* {@link ALARM_RETRY_LIMIT} times, the way workerd retries a throwing
|
|
205
|
+
* `alarm()`. Delivery is at-least-once, so the handler must tolerate
|
|
206
|
+
* running twice for one scheduled timestamp.
|
|
203
207
|
*/
|
|
204
208
|
onAlarm?: () => Promise<void> | void;
|
|
205
209
|
/**
|
|
@@ -218,25 +222,59 @@ interface NodeShardHostOptions {
|
|
|
218
222
|
/**
|
|
219
223
|
* Build a `ShardHost` over a real `better-sqlite3` database.
|
|
220
224
|
*
|
|
221
|
-
* `runSerialized`
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
225
|
+
* `runSerialized` and `transaction` share **one** boundary lock, because they
|
|
226
|
+
* write **one** `better-sqlite3` connection. `transaction` issues raw
|
|
227
|
+
* `BEGIN`/`COMMIT`/`ROLLBACK` — legal here (unlike inside a Cloudflare Durable
|
|
228
|
+
* Object, where the runtime forbids it and callers must use
|
|
229
|
+
* `storage.transaction`) because better-sqlite3 is a plain embedded database
|
|
230
|
+
* with no platform-level transaction primitive layered over it.
|
|
227
231
|
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* `
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
* `runSerialized
|
|
239
|
-
*
|
|
232
|
+
* ## Why one lane and not two
|
|
233
|
+
*
|
|
234
|
+
* This host shipped with two private tail chains, one per entry point, so each
|
|
235
|
+
* serialized against itself and neither against the other. `runSerialized`
|
|
236
|
+
* issues no `BEGIN` of its own, so one of its closures overlapping a bare
|
|
237
|
+
* `transaction()` wrote straight into that transaction's span. `assertOwnTurn`
|
|
238
|
+
* below catches the write — it is the only reason this host never silently lost
|
|
239
|
+
* one the way an unguarded connection would — but catching it is not the same
|
|
240
|
+
* as preventing it, and the refusal lands in the wrong place:
|
|
241
|
+
*
|
|
242
|
+
* 1. A `runSerialized` closure writes a row. Nothing is open, so it commits.
|
|
243
|
+
* 2. It awaits (a scheduler hop, an `onShardInit` read, any real I/O).
|
|
244
|
+
* 3. A bare `transaction()` runs `BEGIN` while it is parked. `transaction` is
|
|
245
|
+
* a public contract surface, and `./node-shard-state` re-exports it as the
|
|
246
|
+
* `storage.transaction` member `ShardDO` documents as "the platform
|
|
247
|
+
* primitive", so a subclass reaches one without going through the engine.
|
|
248
|
+
* 4. The closure resumes and its next write is refused with
|
|
249
|
+
* `SHARD_UNAVAILABLE`, so `runSerialized` **rejects** — with step 1 already
|
|
250
|
+
* durable.
|
|
251
|
+
*
|
|
252
|
+
* The boundary the contract promises is atomic-or-nothing to its caller tore in
|
|
253
|
+
* half: a rejection the caller will retry, on top of a write the retry now
|
|
254
|
+
* re-applies. That is `runSerialized`'s "no two closures interleave" guarantee
|
|
255
|
+
* broken by a `transaction` it was never serialized against.
|
|
256
|
+
*
|
|
257
|
+
* ## Why the lock is re-entrant, and why `AsyncLocalStorage` is what makes it so
|
|
258
|
+
*
|
|
259
|
+
* The engine composes the two, and stacks them: `ShardRunner.runInTransaction`
|
|
260
|
+
* is `runSerialized(() => transaction(work))` (`@lunora/shard-engine`), and
|
|
261
|
+
* `ShardDO.fetch` widens a mutation dispatch carrying `x-lunora-mutation-id`
|
|
262
|
+
* into a *further* `runSerialized` span around it (`@lunora/do`). Under two
|
|
263
|
+
* plain FIFO chains that outer span is already fatal on this host — the inner
|
|
264
|
+
* `runSerialized` waits on a `tail` that only settles when the outer closure it
|
|
265
|
+
* is running inside resolves, and because nothing ever resets that `tail`, the
|
|
266
|
+
* shard's write lane is wedged for the life of the process rather than for the
|
|
267
|
+
* life of the request.
|
|
268
|
+
*
|
|
269
|
+
* So the lock is skipped for a boundary opened from inside a boundary, and
|
|
270
|
+
* `AsyncLocalStorage` is the only thing that can tell that case from the one
|
|
271
|
+
* that must queue: its store follows a single call's own await chain without
|
|
272
|
+
* leaking into a sibling chain. A held boolean cannot — it reads `true` for an
|
|
273
|
+
* unrelated *second top-level* `transaction()` too, which would then nest a raw
|
|
274
|
+
* `BEGIN` on a connection already inside one. This is the same distinction
|
|
275
|
+
* workerd's `blockConcurrencyWhile` draws natively with "does not queue events
|
|
276
|
+
* initiated as part of the callback itself", which is what
|
|
277
|
+
* `@lunora/platform-cloudflare` leans on for the identical composition.
|
|
240
278
|
*
|
|
241
279
|
* The returned `dispose()` is this host's lifecycle owner: it clears the
|
|
242
280
|
* pending alarm `setTimeout` (so it can never fire against a connection this
|
|
@@ -310,22 +348,64 @@ interface NodeShardRegistry {
|
|
|
310
348
|
}
|
|
311
349
|
/** Build the in-process shard registry. */
|
|
312
350
|
declare const createNodeShardRegistry: (options?: NodeShardRegistryOptions) => NodeShardRegistry;
|
|
351
|
+
/** Options for {@link createNodeWorkflowHost}. */
|
|
352
|
+
interface NodeWorkflowHostOptions<Workflows extends Record<string, {
|
|
353
|
+
isLunoraWorkflow: true;
|
|
354
|
+
}>> {
|
|
355
|
+
/** Base env merged under the derived `WORKFLOW_*` bindings — surfaced to workflow bodies as `ctx.env` and used to resolve spawned children. */
|
|
356
|
+
env?: Record<string, unknown>;
|
|
357
|
+
/** How long (ms) the engine holds a cross-process lease while an activation runs, for stores that implement `acquire`. Defaults to 30000. */
|
|
358
|
+
leaseTtlMs?: number;
|
|
359
|
+
/** Where runs are persisted. Required — see the header for why there is no default. `createNodeWorkflowStore(database)` is the durable one. */
|
|
360
|
+
store: WorkflowStore;
|
|
361
|
+
/** The declared workflows keyed by their `lunora/workflows.ts` export name (e.g. `{ orderPipeline: orderPipeline }`). Values must be `defineWorkflow` results. */
|
|
362
|
+
workflows: Workflows;
|
|
363
|
+
}
|
|
364
|
+
/** A fully-wired Node workflow host. */
|
|
365
|
+
interface NodeWorkflowHost<Workflows extends Record<string, {
|
|
366
|
+
isLunoraWorkflow: true;
|
|
367
|
+
}>> {
|
|
368
|
+
/** Per-export-name `WorkflowBindingLike` — the map `ctx.workflows` consumes. */
|
|
369
|
+
readonly bindings: { [K in keyof Workflows]: WorkflowBindingLike; };
|
|
370
|
+
/**
|
|
371
|
+
* The caller's `env` plus one `WORKFLOW_<EXPORT>` binding per workflow —
|
|
372
|
+
* merge this into a worker env so `ctx.spawn`/`ctx.parallel` resolve
|
|
373
|
+
* children through the same runtime.
|
|
374
|
+
*/
|
|
375
|
+
readonly env: Record<string, unknown>;
|
|
376
|
+
/** The underlying visulima runtime — `sweep`/`signal` for a dev loop or tests. */
|
|
377
|
+
readonly runtime: WorkflowRuntime;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Create a Node workflow host: compile every declared Lunora workflow onto the
|
|
381
|
+
* visulima engine, derive the `WORKFLOW_*` env, and expose the per-workflow
|
|
382
|
+
* `WorkflowBindingLike` handles.
|
|
383
|
+
*/
|
|
384
|
+
declare const createNodeWorkflowHost: <Workflows extends Record<string, {
|
|
385
|
+
isLunoraWorkflow: true;
|
|
386
|
+
}>>(options: NodeWorkflowHostOptions<Workflows>) => NodeWorkflowHost<Workflows>;
|
|
313
387
|
/** Every contract this package provides, composed for one Node process. */
|
|
314
388
|
interface NodePlatform<Queues extends Record<string, {
|
|
315
389
|
isLunoraQueue: true;
|
|
390
|
+
}> = Record<string, never>, Workflows extends Record<string, {
|
|
391
|
+
isLunoraWorkflow: true;
|
|
316
392
|
}> = Record<string, never>> {
|
|
317
393
|
/** `using platform = createNodePlatform(...)` support — delegates to `close()`. */
|
|
318
394
|
[Symbol.dispose]: () => void;
|
|
319
395
|
/** What this target supports — see `NODE_CAPABILITIES` in `@lunora/platform`. */
|
|
320
396
|
capabilities: PlatformCapabilities;
|
|
321
397
|
/**
|
|
322
|
-
* Tear this platform instance down:
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
398
|
+
* Tear this platform instance down: every resource this root built, in
|
|
399
|
+
* reverse construction order — the global store's own connection, every
|
|
400
|
+
* armed scheduler job timer, the registry's live shards, and last the shard
|
|
401
|
+
* host's pending alarm timer and its `better-sqlite3` database (and, with
|
|
402
|
+
* it, `kv`'s table — both live on the same connection). Nothing in this
|
|
403
|
+
* package closes these resources on its own — a `NodePlatform` a caller
|
|
404
|
+
* stops using without calling `close()` leaks the open file handles (plus
|
|
405
|
+
* their WAL/SHM sidecar files) and keeps the process alive on outstanding
|
|
406
|
+
* timers. Safe to call more than once. A `createNodePlatform` that throws
|
|
407
|
+
* unwinds the same list itself, so a failed construction leaks nothing and
|
|
408
|
+
* leaves nothing to call this on.
|
|
329
409
|
*
|
|
330
410
|
* **`close()` is a terminal state, not merely a cleanup step.** After it
|
|
331
411
|
* runs: `scheduler.schedule()` throws instead of arming a fresh timer
|
|
@@ -335,8 +415,10 @@ interface NodePlatform<Queues extends Record<string, {
|
|
|
335
415
|
* benign); `shard.alarms.set()`/`delete()` throw before mutating any
|
|
336
416
|
* in-memory state (checked against the connection's own open/closed state,
|
|
337
417
|
* the single source of truth); `shard.alarms.get()` keeps answering
|
|
338
|
-
* whatever it last held.
|
|
339
|
-
*
|
|
418
|
+
* whatever it last held; `sockets.accept()`/`setTag()`/`removeTag()` throw
|
|
419
|
+
* the same way, checked against the same connection state, before touching
|
|
420
|
+
* any runtime socket map or durable row. A no-op instead of a throw would
|
|
421
|
+
* be indistinguishable from a working call — exactly the silent-vanishing
|
|
340
422
|
* this lifecycle exists to end.
|
|
341
423
|
*/
|
|
342
424
|
close: () => void;
|
|
@@ -351,8 +433,28 @@ interface NodePlatform<Queues extends Record<string, {
|
|
|
351
433
|
* shutdown is `await platform.drain()` then `platform.close()`.
|
|
352
434
|
*/
|
|
353
435
|
drain: () => Promise<void>;
|
|
436
|
+
/**
|
|
437
|
+
* The `.global()` backend, or `undefined` when the caller named no database
|
|
438
|
+
* file for it. There is no default path, because a global store silently
|
|
439
|
+
* rooted at `:memory:` loses every row when the process exits.
|
|
440
|
+
*
|
|
441
|
+
* **A building block, not a wiring.** Unlike its three siblings, nothing
|
|
442
|
+
* downstream of this composition root reads it: a `.global()` read or write
|
|
443
|
+
* reaches its backend through exactly one seam, `createShardCtxDb({ globalDb
|
|
444
|
+
* })`, and the only thing that passes `globalDb` is the generated `shard.ts`,
|
|
445
|
+
* from its `d1` / `hyperdriveGlobal` config thunks. `createNodePlatform`
|
|
446
|
+
* constructs no shard DO, so it cannot make that hop itself. A caller that
|
|
447
|
+
* wants `.global()` on this host makes it: after `migrate(schema)` has
|
|
448
|
+
* provisioned the tables, give the generated `createShardDO` a `d1` thunk
|
|
449
|
+
* returning `platform.globalTables.writer({ schema, … })` — `writer` builds
|
|
450
|
+
* the `createSqlCtxDb` facade that seam expects, and `node-platform.test.ts`
|
|
451
|
+
* round-trips a row through exactly that object.
|
|
452
|
+
*/
|
|
453
|
+
globalTables?: NodeGlobalStore;
|
|
354
454
|
/** Durable key-value storage backed by the same `better-sqlite3` database as `shard`. */
|
|
355
455
|
kv: ShardKvStore;
|
|
456
|
+
/** The local-filesystem bucket, or `undefined` when the caller named no bucket directory. */
|
|
457
|
+
objectStorage?: R2BucketLike;
|
|
356
458
|
/**
|
|
357
459
|
* The declared queues, or `undefined` when the caller declared none.
|
|
358
460
|
*
|
|
@@ -367,6 +469,12 @@ interface NodePlatform<Queues extends Record<string, {
|
|
|
367
469
|
shard: ShardHost;
|
|
368
470
|
/** Socket registry with mutable tags and SQLite-persisted attachments. */
|
|
369
471
|
sockets: SocketHost;
|
|
472
|
+
/**
|
|
473
|
+
* The declared workflows, or `undefined` when the caller declared none.
|
|
474
|
+
* Runs are persisted to the same `better-sqlite3` database as the shard, so
|
|
475
|
+
* they survive a restart without a second store to configure.
|
|
476
|
+
*/
|
|
477
|
+
workflows?: NodeWorkflowHost<Workflows>;
|
|
370
478
|
}
|
|
371
479
|
/**
|
|
372
480
|
* Options for {@link createNodePlatform} — the shard host's (`path`,
|
|
@@ -376,10 +484,29 @@ interface NodePlatform<Queues extends Record<string, {
|
|
|
376
484
|
* or job with nowhere to land is bookkeeping. `directory` makes the shards the
|
|
377
485
|
* directory resolves for fan-out file-backed too, and `onAlarm` gives their
|
|
378
486
|
* durable alarms somewhere to land.
|
|
487
|
+
*
|
|
488
|
+
* `queues`, `workflows`, `objectStorageDirectory` and `globalTablesPath` are the
|
|
489
|
+
* four declarations — each is absent from the returned platform when omitted.
|
|
379
490
|
*/
|
|
380
491
|
type NodePlatformOptions<Queues extends Record<string, {
|
|
381
492
|
isLunoraQueue: true;
|
|
493
|
+
}> = Record<string, never>, Workflows extends Record<string, {
|
|
494
|
+
isLunoraWorkflow: true;
|
|
382
495
|
}> = Record<string, never>> = {
|
|
496
|
+
/**
|
|
497
|
+
* Database file the `.global()` tables live in — its own file, never a
|
|
498
|
+
* shard's, because a table every shard reads must not be inside any one of
|
|
499
|
+
* them. Omit when the app declares no `.global()` table; there is no
|
|
500
|
+
* default, for the same reason the bucket has none.
|
|
501
|
+
*/
|
|
502
|
+
globalTablesPath?: string;
|
|
503
|
+
/**
|
|
504
|
+
* Directory the object-storage bucket keeps its objects in, one file per
|
|
505
|
+
* key. Omit when the app declares no buckets — there is no default,
|
|
506
|
+
* because a bucket silently rooted at the process's working directory is
|
|
507
|
+
* worse than an absent one.
|
|
508
|
+
*/
|
|
509
|
+
objectStorageDirectory?: string;
|
|
383
510
|
/**
|
|
384
511
|
* Deliver one assembled queue batch — wire this to `dispatchQueueBatch`.
|
|
385
512
|
* Required alongside `queues`; without it the messages would be stored
|
|
@@ -388,11 +515,15 @@ type NodePlatformOptions<Queues extends Record<string, {
|
|
|
388
515
|
onQueueBatch?: NodeQueueHostOptions<Queues>["onBatch"];
|
|
389
516
|
/** The app's `defineQueue` results, keyed by export name. Omit when the app declares no queues. */
|
|
390
517
|
queues?: Queues;
|
|
518
|
+
/** The app's `defineWorkflow` results, keyed by export name. Omit when the app declares no workflows. */
|
|
519
|
+
workflows?: Workflows;
|
|
391
520
|
} & NodeSchedulerHostOptions & NodeShardHostOptions & NodeShardRegistryOptions;
|
|
392
521
|
/** Compose every contract this package provides over one `better-sqlite3` database. */
|
|
393
522
|
declare const createNodePlatform: <Queues extends Record<string, {
|
|
394
523
|
isLunoraQueue: true;
|
|
395
|
-
}> = Record<string, never
|
|
524
|
+
}> = Record<string, never>, Workflows extends Record<string, {
|
|
525
|
+
isLunoraWorkflow: true;
|
|
526
|
+
}> = Record<string, never>>(options?: NodePlatformOptions<Queues, Workflows>) => NodePlatform<Queues, Workflows>;
|
|
396
527
|
/** Options for {@link createNodeR2Bucket}. */
|
|
397
528
|
interface NodeR2BucketOptions {
|
|
398
529
|
/** The bucket directory — created on first write. Objects live here, one file per key. */
|
|
@@ -400,7 +531,7 @@ interface NodeR2BucketOptions {
|
|
|
400
531
|
}
|
|
401
532
|
/**
|
|
402
533
|
* Create an `R2BucketLike` over the local filesystem. Any object shape
|
|
403
|
-
* `createStorage({ bucket })` accepts — `put`/`get`/`head`/`delete`/`list` —
|
|
534
|
+
* `createStorage({ bucket, bucketName })` accepts — `put`/`get`/`head`/`delete`/`list` —
|
|
404
535
|
* maps directly onto a file operation.
|
|
405
536
|
*/
|
|
406
537
|
declare const createNodeR2Bucket: (options: NodeR2BucketOptions) => R2BucketLike;
|
|
@@ -473,45 +604,135 @@ interface NodeSocketHost {
|
|
|
473
604
|
}
|
|
474
605
|
/** Build the socket registry, persisting attachments and tags to `database`. */
|
|
475
606
|
declare const createNodeSocketHost: (database: Database.Database) => NodeSocketHost;
|
|
476
|
-
/** Options for {@link createNodeWorkflowHost}. */
|
|
477
|
-
interface NodeWorkflowHostOptions<Workflows extends Record<string, {
|
|
478
|
-
isLunoraWorkflow: true;
|
|
479
|
-
}>> {
|
|
480
|
-
/** Base env merged under the derived `WORKFLOW_*` bindings — surfaced to workflow bodies as `ctx.env` and used to resolve spawned children. */
|
|
481
|
-
env?: Record<string, unknown>;
|
|
482
|
-
/** How long (ms) the engine holds a cross-process lease while an activation runs, for stores that implement `acquire`. Defaults to 30000. */
|
|
483
|
-
leaseTtlMs?: number;
|
|
484
|
-
/** Where runs are persisted. Required — see the header for why there is no default. `createNodeWorkflowStore(database)` is the durable one. */
|
|
485
|
-
store: WorkflowStore;
|
|
486
|
-
/** The declared workflows keyed by their `lunora/workflows.ts` export name (e.g. `{ orderPipeline: orderPipeline }`). Values must be `defineWorkflow` results. */
|
|
487
|
-
workflows: Workflows;
|
|
488
|
-
}
|
|
489
|
-
/** A fully-wired Node workflow host. */
|
|
490
|
-
interface NodeWorkflowHost<Workflows extends Record<string, {
|
|
491
|
-
isLunoraWorkflow: true;
|
|
492
|
-
}>> {
|
|
493
|
-
/** Per-export-name `WorkflowBindingLike` — the map `ctx.workflows` consumes. */
|
|
494
|
-
readonly bindings: { [K in keyof Workflows]: WorkflowBindingLike; };
|
|
495
|
-
/**
|
|
496
|
-
* The caller's `env` plus one `WORKFLOW_<EXPORT>` binding per workflow —
|
|
497
|
-
* merge this into a worker env so `ctx.spawn`/`ctx.parallel` resolve
|
|
498
|
-
* children through the same runtime.
|
|
499
|
-
*/
|
|
500
|
-
readonly env: Record<string, unknown>;
|
|
501
|
-
/** The underlying visulima runtime — `sweep`/`signal` for a dev loop or tests. */
|
|
502
|
-
readonly runtime: WorkflowRuntime;
|
|
503
|
-
}
|
|
504
|
-
/**
|
|
505
|
-
* Create a Node workflow host: compile every declared Lunora workflow onto the
|
|
506
|
-
* visulima engine, derive the `WORKFLOW_*` env, and expose the per-workflow
|
|
507
|
-
* `WorkflowBindingLike` handles.
|
|
508
|
-
*/
|
|
509
|
-
declare const createNodeWorkflowHost: <Workflows extends Record<string, {
|
|
510
|
-
isLunoraWorkflow: true;
|
|
511
|
-
}>>(options: NodeWorkflowHostOptions<Workflows>) => NodeWorkflowHost<Workflows>;
|
|
512
607
|
/**
|
|
513
608
|
* Build a durable {@link WorkflowStore} over a `better-sqlite3` connection.
|
|
514
609
|
* Pass the result as `createNodeWorkflowHost({ store })`.
|
|
515
610
|
*/
|
|
516
611
|
declare const createNodeWorkflowStore: (database: Database.Database) => WorkflowStore;
|
|
517
|
-
export {
|
|
612
|
+
export {
|
|
613
|
+
/**
|
|
614
|
+
* `@lunora/platform-node` — a Node implementation of the `@lunora/platform`
|
|
615
|
+
* host contracts (`ShardHost`, `SocketHost`, `ShardDirectory`, `ShardKvStore`,
|
|
616
|
+
* `SchedulerHost`) over `better-sqlite3` and an in-process socket/directory/
|
|
617
|
+
* scheduler registry.
|
|
618
|
+
*
|
|
619
|
+
* Promoted from `@lunora/platform`'s `node:sqlite` reference host
|
|
620
|
+
* (`src/conformance/reference-host.ts`) under plan 234, then hardened until the
|
|
621
|
+
* durability half of each contract actually holds: alarms and scheduler jobs
|
|
622
|
+
* are persisted **and re-armed on construction**, socket attachments and tags
|
|
623
|
+
* live in SQLite rather than a `Map`, and `.global()` tables run the real
|
|
624
|
+
* `@lunora/sql-store` core (`./node-global-store`). Each of those is pinned by
|
|
625
|
+
* a restart test — a second host over the same database file — not only by the
|
|
626
|
+
* TCK's simulated recycle.
|
|
627
|
+
*
|
|
628
|
+
* Three suites run against this package: `@lunora/platform/conformance`'s host
|
|
629
|
+
* TCK, `@lunora/shard-engine/conformance`'s engine suite, and its own
|
|
630
|
+
* lifecycle/global-store tests.
|
|
631
|
+
*
|
|
632
|
+
* **Emulated here**, and rated accordingly in `NODE_CAPABILITIES`
|
|
633
|
+
* (`@lunora/platform`): queues over a durable table (`./node-queue-host`), R2
|
|
634
|
+
* buckets over the local filesystem (`./node-r2-bucket`), workflows over the
|
|
635
|
+
* `@visulima/workflow` engine (`./node-workflow-host`), and cross-shard fan-out
|
|
636
|
+
* via `@lunora/runtime`'s query coordinator over the in-process shard registry.
|
|
637
|
+
* `createNodePlatform` binds all three declarations (`queues`, `workflows`,
|
|
638
|
+
* `objectStorageDirectory`) — a capability rated `emulated` with nothing bound
|
|
639
|
+
* is the one combination that fails at runtime with no diagnostic before it.
|
|
640
|
+
*
|
|
641
|
+
* **Still missing:** a dev server. There is no `lunora dev --target node`, and
|
|
642
|
+
* nothing here owns a timer, so queue delivery is driven by an explicit
|
|
643
|
+
* `poll()`. Also absent are the Cloudflare product bindings with no local
|
|
644
|
+
* equivalent — Vectorize, Workers AI, Browser Rendering, Containers, Analytics
|
|
645
|
+
* Engine, Pipelines, Secrets Store, Hyperdrive.
|
|
646
|
+
*
|
|
647
|
+
* `@lunora/config` ships a `node` **deploy** driver, so `--target node` resolves
|
|
648
|
+
* for `provision` — which reports, once, which declared features this target
|
|
649
|
+
* cannot serve, and writes nothing: there is no hosted control plane to deploy
|
|
650
|
+
* to and no `wrangler`-equivalent to shell out to. A deploy driver is not a dev
|
|
651
|
+
* server, which is why both statements above hold at once. See
|
|
652
|
+
* `plans/234-node-host-findings.md`.
|
|
653
|
+
*/
|
|
654
|
+
type NodeGlobalContextDatabaseOptions,
|
|
655
|
+
/**
|
|
656
|
+
* `@lunora/platform-node` — a Node implementation of the `@lunora/platform`
|
|
657
|
+
* host contracts (`ShardHost`, `SocketHost`, `ShardDirectory`, `ShardKvStore`,
|
|
658
|
+
* `SchedulerHost`) over `better-sqlite3` and an in-process socket/directory/
|
|
659
|
+
* scheduler registry.
|
|
660
|
+
*
|
|
661
|
+
* Promoted from `@lunora/platform`'s `node:sqlite` reference host
|
|
662
|
+
* (`src/conformance/reference-host.ts`) under plan 234, then hardened until the
|
|
663
|
+
* durability half of each contract actually holds: alarms and scheduler jobs
|
|
664
|
+
* are persisted **and re-armed on construction**, socket attachments and tags
|
|
665
|
+
* live in SQLite rather than a `Map`, and `.global()` tables run the real
|
|
666
|
+
* `@lunora/sql-store` core (`./node-global-store`). Each of those is pinned by
|
|
667
|
+
* a restart test — a second host over the same database file — not only by the
|
|
668
|
+
* TCK's simulated recycle.
|
|
669
|
+
*
|
|
670
|
+
* Three suites run against this package: `@lunora/platform/conformance`'s host
|
|
671
|
+
* TCK, `@lunora/shard-engine/conformance`'s engine suite, and its own
|
|
672
|
+
* lifecycle/global-store tests.
|
|
673
|
+
*
|
|
674
|
+
* **Emulated here**, and rated accordingly in `NODE_CAPABILITIES`
|
|
675
|
+
* (`@lunora/platform`): queues over a durable table (`./node-queue-host`), R2
|
|
676
|
+
* buckets over the local filesystem (`./node-r2-bucket`), workflows over the
|
|
677
|
+
* `@visulima/workflow` engine (`./node-workflow-host`), and cross-shard fan-out
|
|
678
|
+
* via `@lunora/runtime`'s query coordinator over the in-process shard registry.
|
|
679
|
+
* `createNodePlatform` binds all three declarations (`queues`, `workflows`,
|
|
680
|
+
* `objectStorageDirectory`) — a capability rated `emulated` with nothing bound
|
|
681
|
+
* is the one combination that fails at runtime with no diagnostic before it.
|
|
682
|
+
*
|
|
683
|
+
* **Still missing:** a dev server. There is no `lunora dev --target node`, and
|
|
684
|
+
* nothing here owns a timer, so queue delivery is driven by an explicit
|
|
685
|
+
* `poll()`. Also absent are the Cloudflare product bindings with no local
|
|
686
|
+
* equivalent — Vectorize, Workers AI, Browser Rendering, Containers, Analytics
|
|
687
|
+
* Engine, Pipelines, Secrets Store, Hyperdrive.
|
|
688
|
+
*
|
|
689
|
+
* `@lunora/config` ships a `node` **deploy** driver, so `--target node` resolves
|
|
690
|
+
* for `provision` — which reports, once, which declared features this target
|
|
691
|
+
* cannot serve, and writes nothing: there is no hosted control plane to deploy
|
|
692
|
+
* to and no `wrangler`-equivalent to shell out to. A deploy driver is not a dev
|
|
693
|
+
* server, which is why both statements above hold at once. See
|
|
694
|
+
* `plans/234-node-host-findings.md`.
|
|
695
|
+
*/
|
|
696
|
+
type NodeGlobalStore,
|
|
697
|
+
/**
|
|
698
|
+
* `@lunora/platform-node` — a Node implementation of the `@lunora/platform`
|
|
699
|
+
* host contracts (`ShardHost`, `SocketHost`, `ShardDirectory`, `ShardKvStore`,
|
|
700
|
+
* `SchedulerHost`) over `better-sqlite3` and an in-process socket/directory/
|
|
701
|
+
* scheduler registry.
|
|
702
|
+
*
|
|
703
|
+
* Promoted from `@lunora/platform`'s `node:sqlite` reference host
|
|
704
|
+
* (`src/conformance/reference-host.ts`) under plan 234, then hardened until the
|
|
705
|
+
* durability half of each contract actually holds: alarms and scheduler jobs
|
|
706
|
+
* are persisted **and re-armed on construction**, socket attachments and tags
|
|
707
|
+
* live in SQLite rather than a `Map`, and `.global()` tables run the real
|
|
708
|
+
* `@lunora/sql-store` core (`./node-global-store`). Each of those is pinned by
|
|
709
|
+
* a restart test — a second host over the same database file — not only by the
|
|
710
|
+
* TCK's simulated recycle.
|
|
711
|
+
*
|
|
712
|
+
* Three suites run against this package: `@lunora/platform/conformance`'s host
|
|
713
|
+
* TCK, `@lunora/shard-engine/conformance`'s engine suite, and its own
|
|
714
|
+
* lifecycle/global-store tests.
|
|
715
|
+
*
|
|
716
|
+
* **Emulated here**, and rated accordingly in `NODE_CAPABILITIES`
|
|
717
|
+
* (`@lunora/platform`): queues over a durable table (`./node-queue-host`), R2
|
|
718
|
+
* buckets over the local filesystem (`./node-r2-bucket`), workflows over the
|
|
719
|
+
* `@visulima/workflow` engine (`./node-workflow-host`), and cross-shard fan-out
|
|
720
|
+
* via `@lunora/runtime`'s query coordinator over the in-process shard registry.
|
|
721
|
+
* `createNodePlatform` binds all three declarations (`queues`, `workflows`,
|
|
722
|
+
* `objectStorageDirectory`) — a capability rated `emulated` with nothing bound
|
|
723
|
+
* is the one combination that fails at runtime with no diagnostic before it.
|
|
724
|
+
*
|
|
725
|
+
* **Still missing:** a dev server. There is no `lunora dev --target node`, and
|
|
726
|
+
* nothing here owns a timer, so queue delivery is driven by an explicit
|
|
727
|
+
* `poll()`. Also absent are the Cloudflare product bindings with no local
|
|
728
|
+
* equivalent — Vectorize, Workers AI, Browser Rendering, Containers, Analytics
|
|
729
|
+
* Engine, Pipelines, Secrets Store, Hyperdrive.
|
|
730
|
+
*
|
|
731
|
+
* `@lunora/config` ships a `node` **deploy** driver, so `--target node` resolves
|
|
732
|
+
* for `provision` — which reports, once, which declared features this target
|
|
733
|
+
* cannot serve, and writes nothing: there is no hosted control plane to deploy
|
|
734
|
+
* to and no `wrangler`-equivalent to shell out to. A deploy driver is not a dev
|
|
735
|
+
* server, which is why both statements above hold at once. See
|
|
736
|
+
* `plans/234-node-host-findings.md`.
|
|
737
|
+
*/
|
|
738
|
+
type NodeGlobalStoreOptions, type NodePlatform, type NodePlatformOptions, type NodeQueueHost, type NodeQueueHostOptions, type NodeR2BucketOptions, type NodeSchedulerHost, type NodeSchedulerHostOptions, type NodeShard, type NodeShardHostOptions, type NodeShardRegistry, type NodeShardRegistryOptions, type NodeShardState, type NodeSocketHost, type NodeWorkflowHost, type NodeWorkflowHostOptions, createNodeGlobalStore, createNodePlatform, createNodeQueueHost, createNodeR2Bucket, createNodeSchedulerHost, createNodeShardHost, createNodeShardKvStore, createNodeShardRegistry, createNodeShardState, createNodeSocketHost, createNodeSqlExec, createNodeWorkflowHost, createNodeWorkflowStore };
|