stitchkit 0.102.0 → 0.103.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/CHANGELOG.md +43 -0
- package/dist/application/diagnostic-journal-contract.d.ts +46 -7
- package/dist/application/diagnostic-journal-contract.d.ts.map +1 -1
- package/dist/application/diagnostic-journal-frame.d.ts +9 -0
- package/dist/application/diagnostic-journal-frame.d.ts.map +1 -0
- package/dist/application/diagnostic-journal-manager.d.ts.map +1 -1
- package/dist/application/diagnostic-journal-read-contract.d.ts +110 -0
- package/dist/application/diagnostic-journal-read-contract.d.ts.map +1 -0
- package/dist/application/diagnostic-journal-reader.d.ts +13 -0
- package/dist/application/diagnostic-journal-reader.d.ts.map +1 -0
- package/dist/application/diagnostic-journal-recovery.d.ts +4 -0
- package/dist/application/diagnostic-journal-recovery.d.ts.map +1 -0
- package/dist/application/diagnostic-journal-storage.d.ts +2 -0
- package/dist/application/diagnostic-journal-storage.d.ts.map +1 -1
- package/dist/entrypoints/application/diagnostic-journal.d.ts +1 -0
- package/dist/entrypoints/application/diagnostic-journal.d.ts.map +1 -1
- package/dist/entrypoints/application/diagnostic-journal.js +213 -19
- package/dist/entrypoints/application.d.ts +1 -0
- package/dist/entrypoints/application.d.ts.map +1 -1
- package/dist/entrypoints/application.js +10 -2
- package/dist/index-z94wht4y.js +156 -0
- package/llms/application-diagnostic-journal.txt +2 -0
- package/llms/application.2.txt +157 -267
- package/llms/application.3.txt +278 -0
- package/llms/application.txt +43 -158
- package/llms/stitchkit.txt +4 -4
- package/llms/{upgrading-0.75.0-0.102.0.txt → upgrading-0.75.0-0.103.0.txt} +32 -3
- package/llms/upgrading.txt +1 -1
- package/llms-full.txt +80 -8
- package/llms.txt +4 -4
- package/package.json +1 -1
- package/dist/index-h124epcf.js +0 -103
package/llms/application.2.txt
CHANGED
|
@@ -1,16 +1,171 @@
|
|
|
1
|
-
# stitchkit/application — stitchkit docs slice, part 2 of
|
|
1
|
+
# stitchkit/application — stitchkit docs slice, part 2 of 3
|
|
2
2
|
|
|
3
3
|
> Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
|
|
4
4
|
> `llms.txt` in this package.
|
|
5
|
+
> Continues in `llms/application.3.txt`.
|
|
5
6
|
|
|
6
7
|
Parts of this slice:
|
|
7
8
|
- `llms/application.txt`: Guide: Managed application kernel (docs/guide/application-kernel.md)
|
|
8
|
-
- `llms/application.2.txt`: Guide: Managed application kernel (docs/guide/application-kernel.md) · Guide: Application migration recipes (docs/guide/application-migration-recipes.md)
|
|
9
|
+
- `llms/application.2.txt`: Guide: Managed application kernel (docs/guide/application-kernel.md) · Guide: Application migration recipes (docs/guide/application-migration-recipes.md)
|
|
10
|
+
- `llms/application.3.txt`: API reference: `stitchkit/application` (docs/api/reference.md)
|
|
9
11
|
|
|
10
12
|
==============================================================================
|
|
11
13
|
# Guide: Managed application kernel (docs/guide/application-kernel.md) (continued)
|
|
12
14
|
==============================================================================
|
|
13
15
|
|
|
16
|
+
## Durable process facts and owner notifications
|
|
17
|
+
|
|
18
|
+
`createProcessLifecycleLedger` records a bounded versioned list of process runs.
|
|
19
|
+
Start, readiness, draining and shutdown target both `runId` and `pid`, so hot
|
|
20
|
+
reload and PID reuse cannot close the wrong generation. `lifecycleLedgerResource`
|
|
21
|
+
drives it from the kernel's phases: `start` records the start, `activate` the
|
|
22
|
+
readiness, `stopAdmission` the drain — the run's `unavailableAt`, the moment it
|
|
23
|
+
stops answering new work — and `close` / `force` the stop. A run that stops
|
|
24
|
+
without ever becoming ready (a failed startup rolls back with `close` only)
|
|
25
|
+
ends as `startup-failed`, not `clean`.
|
|
26
|
+
|
|
27
|
+
A start classifies what happened to the newest run before it, and the
|
|
28
|
+
classification is decided by four facts — whether that run recorded its own
|
|
29
|
+
exit, whether the pid is the same, whether the version changed and whether it
|
|
30
|
+
ever became ready:
|
|
31
|
+
|
|
32
|
+
| newest run | same pid | version | `previousExit` | predecessor's `termination` |
|
|
33
|
+
|---|---|---|---|---|
|
|
34
|
+
| none | — | — | `first-boot` | — |
|
|
35
|
+
| recorded `stoppedAt` | — | — | `clean` / `forced` / `startup-failed` / `abnormal` as recorded | unchanged |
|
|
36
|
+
| still open | yes | — | `hot-reload` | `hot-reload`, closed at the new start |
|
|
37
|
+
| still open | no | changed | `handoff` (default) | stays open; it records its own stop later |
|
|
38
|
+
| still open | no | changed, `versionChangeOverlap: 'abnormal'` | `abnormal` / `startup-failed` | closed at the new start, as for the same version |
|
|
39
|
+
| still open, was ready | no | same | `abnormal` (default) | `abnormal`, closed at the new start — an upper bound, the crash time is unknown |
|
|
40
|
+
| still open, never ready | no | same | `startup-failed` (default) | `startup-failed`, closed at the new start |
|
|
41
|
+
|
|
42
|
+
Every other open run the start finds was abandoned too and is closed the same
|
|
43
|
+
way — two crashes in a row leave two open runs, and both are closed, not only
|
|
44
|
+
the newest. Open means `active` or `draining`.
|
|
45
|
+
|
|
46
|
+
A version of `unknown` on either side is never a version change, so a dev build
|
|
47
|
+
after a crashed release reads `abnormal`, not `handoff`. `forced` means the
|
|
48
|
+
process itself acknowledged a kill; `abnormal` means a successor found it dead.
|
|
49
|
+
|
|
50
|
+
### Downtime is the window nobody answered in
|
|
51
|
+
|
|
52
|
+
`ReadyFact.downtimeMs` is `readyAt` of the new run minus `unavailableAt` of the
|
|
53
|
+
last run that served — the predecessor's drain plus the successor's boot. A
|
|
54
|
+
110 s forced drain followed by a 4 s boot is 114 s of downtime; the gap between
|
|
55
|
+
the two processes alone would read 4 s before and after that defect was fixed.
|
|
56
|
+
Runs that never became ready answered nobody, so the count passes them: after a
|
|
57
|
+
failed start it still opens where the last serving run stopped answering. It is
|
|
58
|
+
`0` when that run still answers (a handoff whose predecessor has not drained
|
|
59
|
+
yet) and `null` on first boot and when the end was never timed — a crash or a
|
|
60
|
+
hot reload records no `unavailableAt`. `unavailableSince` names the moment it is
|
|
61
|
+
counted from. The gap between two processes is still there, under its own
|
|
62
|
+
name: `StartFact.processGapMs` is the start minus the predecessor's recorded
|
|
63
|
+
stop.
|
|
64
|
+
|
|
65
|
+
The ledger records what the process says while it can say it. stitchkit never
|
|
66
|
+
calls `process.exit` (ADR 0074), so a stop is recorded on the kernel's `close`
|
|
67
|
+
or `force` path, both awaited. An application that exits the process on its
|
|
68
|
+
own before its shutdown settles loses that last write; the successor then
|
|
69
|
+
finds the run open and closes it as `abnormal`, keeping the `unavailableAt`
|
|
70
|
+
already recorded — which is why the drain moment is written first, at
|
|
71
|
+
`stopAdmission`, and the downtime survives a stop that was never recorded.
|
|
72
|
+
|
|
73
|
+
The `abnormal` default is a choice, and it is the single-process deployment:
|
|
74
|
+
one process per build, a new pid of the same build means the old one stopped
|
|
75
|
+
answering. Where processes of **one** build overlap on purpose — a cluster, a
|
|
76
|
+
zero-downtime reload of the same build — pass `sameVersionOverlap: 'handoff'`
|
|
77
|
+
to the ledger, and every open run stays open until it records its own
|
|
78
|
+
shutdown. The cost of that setting is symmetric: a real crash under it is
|
|
79
|
+
reported as a handoff and the dead run stays open in the ledger until
|
|
80
|
+
retention drops it.
|
|
81
|
+
|
|
82
|
+
A version change is the other axis, and its default is the opposite:
|
|
83
|
+
`handoff`, because a release that starts the new build before stopping the
|
|
84
|
+
old one is the common zero-downtime shape. Where builds never overlap — one
|
|
85
|
+
process per deployment (a pm2 fork, a systemd unit, one container), the old
|
|
86
|
+
one stopped before the new one starts — pass `versionChangeOverlap:
|
|
87
|
+
'abnormal'`. An open predecessor of another build then died without
|
|
88
|
+
recording its exit, most often killed during the release itself: it is closed
|
|
89
|
+
as `abnormal` (or `startup-failed`) at the new start, `downtimeMs` is `null`
|
|
90
|
+
instead of `0`, and `previousVersion` / `versionChanged` still describe the
|
|
91
|
+
release. A predecessor that did record its stop is read as before, and its
|
|
92
|
+
downtime is measured. The two settings compose: `sameVersionOverlap:
|
|
93
|
+
'handoff'` keeps open only the runs of a build allowed to overlap the new one.
|
|
94
|
+
|
|
95
|
+
The list is kept in the order the transitions wrote it —
|
|
96
|
+
every write goes through one atomic update, so that order is the causal one,
|
|
97
|
+
and a successor whose clock lags its predecessor still finds it at the head;
|
|
98
|
+
`startedAt` is data, not the sort key. Retention (`retain`, default 20) drops
|
|
99
|
+
finished runs first and an open one — a live handoff predecessor — only when
|
|
100
|
+
nothing finished is left. Facts are published through the ledger's own
|
|
101
|
+
subscription and resource value; `ApplicationEventSink` remains the strict
|
|
102
|
+
application-state stream.
|
|
103
|
+
|
|
104
|
+
`createNotificationOutbox` is the transport-neutral durable delivery side. It
|
|
105
|
+
persists before send, claims an item with an expiring lease, carries one stable
|
|
106
|
+
idempotency key into the transport, retries under an injected clock and records
|
|
107
|
+
terminal drops. The guarantee is at-least-once: a crash after remote acceptance
|
|
108
|
+
but before the receipt is persisted may redeliver, so transports should use the
|
|
109
|
+
key when they support deduplication. The retry budget is sized for the outage
|
|
110
|
+
an owner notification has to outlive, not for a flaky call: the default backoff
|
|
111
|
+
doubles from one second and caps at sixty (`backoffDelay`, the one formula the
|
|
112
|
+
client's resumable streams also use, with jitter 0), and the 99 waits between
|
|
113
|
+
the default `maxAttempts` of 100 sum to 1+2+4+8+16+32 s plus 93 × 60 s — about
|
|
114
|
+
94 minutes — before `onDropped` sees `attempt-limit`.
|
|
115
|
+
`state()` is a read and never fails on the bounds a transition enforces — a file
|
|
116
|
+
that grew past `maxStateBytes` under an older limit is inspectable and is
|
|
117
|
+
trimmed by the next transition. Two bounds do fail loudly: `enqueue` past
|
|
118
|
+
`maxQueue` throws rather than dropping silently, and a `backoffMs` that returns
|
|
119
|
+
`NaN`, a negative or an infinite delay rejects the flush. `stop()` lets the
|
|
120
|
+
send in flight finish and claims nothing more; a `send` without its own
|
|
121
|
+
deadline holds `stop()` — and so the resource's `force()` — for that one call,
|
|
122
|
+
so give the transport a timeout. Superseding a key that is being sent right now
|
|
123
|
+
does not recall it: the send completes, and only its receipt is not written.
|
|
124
|
+
|
|
125
|
+
`createDirectoryInbox` is the receiving side for another program on the same
|
|
126
|
+
host: it drops entries — a release transition, say — into a directory, and
|
|
127
|
+
the application takes each one at least once. It is a managed resource of its
|
|
128
|
+
own (`createDirectoryInbox({ id, directory, schema, handle })`, imported from
|
|
129
|
+
`stitchkit/application/directory-inbox` because it touches files; its types
|
|
130
|
+
and schemas are in `stitchkit/application`) and publishes a handle with
|
|
131
|
+
`flush()` and `state()`. The producer writes each entry
|
|
132
|
+
atomically as `<name>.json` — a temporary name, then a rename; names that start
|
|
133
|
+
with a dot are never read. Entries are delivered in name order, and only after
|
|
134
|
+
`activate`: none reaches the application before it is ready. `stopAdmission`
|
|
135
|
+
stops taking new ones, `drain` waits for the one in flight, and `force` aborts
|
|
136
|
+
the `signal` its handler was given.
|
|
137
|
+
|
|
138
|
+
Taken and done are separate durable records. Before `handle` runs, the entry
|
|
139
|
+
is claimed with a lease (`leaseMs`, default five minutes); after it returns,
|
|
140
|
+
a receipt replaces the claim and only then is the file removed. A process that
|
|
141
|
+
died mid-delivery leaves a claim, and the entry is taken again once the lease
|
|
142
|
+
runs out — at least once, not exactly once, so a handler that sends somewhere
|
|
143
|
+
should carry the entry's `key` as its idempotency key. A process that died
|
|
144
|
+
after the receipt leaves only the file, which the next pass removes without
|
|
145
|
+
delivering it again. A handler that throws is retried after a backoff from one
|
|
146
|
+
second doubling to five minutes; an entry that fails `schema`, is over
|
|
147
|
+
`maxEntryBytes` or was taken `maxAttempts` times (default 20 — the count
|
|
148
|
+
survives restarts, so an entry whose handling kills the process is not taken
|
|
149
|
+
forever) is moved to `rejected/` with its reason and reported to `onRejected`,
|
|
150
|
+
and the entries after it keep flowing. The state lives in
|
|
151
|
+
`<directory>/.inbox-state.json` under `createFileStateStore`'s inter-process
|
|
152
|
+
lock unless a `store` is supplied.
|
|
153
|
+
|
|
154
|
+
These primitives depend on the structural `StateStore`. On a server,
|
|
155
|
+
`createFileStateStore` supplies the shared Zod-validated JSON adapter with an
|
|
156
|
+
inter-process lock, unique temporary file, fsync and atomic rename. The lock
|
|
157
|
+
is a file with a heartbeat: the holder refreshes its mtime every third of
|
|
158
|
+
`staleLockMs` (default 3 s), a contender waits up to `lockTimeoutMs` (default
|
|
159
|
+
10 s), and the stale bound must sit inside the timeout — otherwise a crashed
|
|
160
|
+
holder blocks every update until the lock ages out, which the constructor
|
|
161
|
+
refuses. A lock whose heartbeat is stale is reclaimed once its recorded pid is
|
|
162
|
+
gone; a live or unverifiable pid (a reused number, another user's process)
|
|
163
|
+
keeps it for ten stale bounds — and never fewer than thirty missed heartbeats,
|
|
164
|
+
so a scheduler stall on a loaded host cannot pull a live lock from under its
|
|
165
|
+
holder — after which the heartbeat wins and the lock is abandoned. Temporary files a crashed writer left beside the state are swept on
|
|
166
|
+
the store's first update. Ledger corruption may be declared reconstructable;
|
|
167
|
+
an outbox must fail closed rather than silently discard pending delivery.
|
|
168
|
+
|
|
14
169
|
## Recovering change subscriptions
|
|
15
170
|
|
|
16
171
|
`changeSubscriptionResource` is an optional resource over a consumer-owned driver.
|
|
@@ -330,268 +485,3 @@ Keep the product code that still owns durable state and policy:
|
|
|
330
485
|
Run the old and new lifecycle paths separately during development if needed,
|
|
331
486
|
but do not ship both. Before cutover, verify there is exactly one signal binding,
|
|
332
487
|
one timer owner, one admission gate and one resource cleanup chain.
|
|
333
|
-
|
|
334
|
-
==============================================================================
|
|
335
|
-
# API reference: `stitchkit/application` (docs/api/reference.md)
|
|
336
|
-
==============================================================================
|
|
337
|
-
|
|
338
|
-
## `stitchkit/application`
|
|
339
|
-
|
|
340
|
-
Server-only process-local application composition. See the
|
|
341
|
-
[application kernel guide](../guide/application-kernel.md) and
|
|
342
|
-
[architecture](../architecture/application-kernel.md). Complete consumer
|
|
343
|
-
cutovers are covered by the executable
|
|
344
|
-
[migration recipes](../guide/application-migration-recipes.md).
|
|
345
|
-
|
|
346
|
-
### Kernel and resources
|
|
347
|
-
|
|
348
|
-
| Export | Kind | Summary |
|
|
349
|
-
|--------|------|---------|
|
|
350
|
-
| `createApplication` | function | compose a validated resource DAG into one startup, readiness, admission and shutdown state machine |
|
|
351
|
-
| `StateStore` / `StateStoreUpdate` | _type_ | async atomic read/update boundary shared by durable application state machines |
|
|
352
|
-
| `createProcessLifecycleLedger` / `ProcessLifecycleLedger` / `ProcessLifecycleLedgerConfig` | function / _type_ | durable bounded run history and typed fact subscription |
|
|
353
|
-
| `LifecycleRunSchema` / `LifecycleRun` / `LifecycleStateSchema` / `LifecycleState` / `LifecycleTerminationSchema` / `LifecycleTermination` | schema / _type_ | versioned process-run state — open (`active`, `draining` with `unavailableAt`) and ended (`clean`, `forced`, `startup-failed`, `hot-reload`, `abnormal`) |
|
|
354
|
-
| `PreviousExitSchema` / `PreviousExit` / `StartFact` / `ReadyFact` / `DrainingFact` / `ShutdownFact` / `ProcessLifecycleFact` | schema / _type_ | first-boot, hot-reload, clean, forced, startup-failed, handoff and abnormal lifecycle facts; `processGapMs` on start, `downtimeMs` (unavailable → ready) on ready |
|
|
355
|
-
| `transitionProcessStart` / `transitionProcessReady` / `transitionProcessDraining` / `transitionProcessShutdown` | function | pure idempotent runId+pid-owned transitions |
|
|
356
|
-
| `LifecycleTransition` / `TransitionStartInput` / `TransitionRunInput` / `TransitionShutdownInput` / `SameVersionOverlap` / `VersionChangeOverlap` | _type_ | pure transition inputs (`TransitionRunInput` for ready and draining, extended with `forced` for shutdown), the same-build overlap policy (`abnormal` default / `handoff`), the version-change overlap policy (`handoff` default / `abnormal`) and state+fact result |
|
|
357
|
-
| `lifecycleLedgerResource` / `LifecycleLedgerResource` / `LifecycleLedgerResourceConfig` | function / _type_ | managed-resource adapter that records start, readiness, admission stop (draining) and shutdown |
|
|
358
|
-
| `createNotificationOutbox` / `NotificationOutbox` / `NotificationOutboxConfig` | function / _type_ | bounded at-least-once queue with durable claims, leases, retries and supersede |
|
|
359
|
-
| `NotificationOutboxItem` / `NotificationOutboxReceipt` / `NotificationOutboxState` / `NotificationSend` / `NotificationFailureClassification` / `DroppedNotification` / `EnqueueNotification` | _type_ | versioned queue records, stable send identity and delivery decisions |
|
|
360
|
-
| `notificationOutboxStateSchema` | function | strict persisted-state schema over an application payload schema |
|
|
361
|
-
| `notificationOutboxResource` / `NotificationOutboxResource` / `NotificationOutboxResourceConfig` | function / _type_ | thin managed-resource scheduler over an outbox handle |
|
|
362
|
-
| `createDirectoryInbox` (from `stitchkit/application/directory-inbox`) / `DirectoryInbox` / `DirectoryInboxConfig` / `DirectoryInboxDelivery` / `DirectoryInboxResource` | function / _type_ | managed resource delivering each `<name>.json` entry another program drops into a directory to the application at least once, after readiness; separate durable taken/done records, leases, bounded retry |
|
|
363
|
-
| `DirectoryInboxStateSchema` / `DirectoryInboxState` / `DirectoryInboxRejection` / `DirectoryInboxRejectionReasonSchema` / `DirectoryInboxRejectionReason` | schema / _type_ | the inbox's claims, receipts and rejections (`invalid`, `too-large`, `attempt-limit`) |
|
|
364
|
-
| `ApplicationHandle.restart` | method | replace one resource and everything that depends on it, leaving the rest of the graph running and the process epoch unchanged |
|
|
365
|
-
| `ApplicationRestartInputSchema` / `ApplicationRestartInput` | schema / _type_ | the resource to replace, by id, and optionally `gracePeriodMs` / `forceTimeoutMs` for this restart — the application's own shutdown budget otherwise |
|
|
366
|
-
| `ApplicationRestartResultSchema` / `ApplicationRestartResult` | schema / _type_ | the subtree that was actually taken down and brought back, in start order, with the outcome, the reason on anything but success, and how long it took |
|
|
367
|
-
| `ApplicationRestartOutcomeSchema` / `ApplicationRestartOutcome` | schema / _type_ | `restarted`, `failed` or `refused` — a refusal (unknown id, shutting down, not yet ready) is not a failure and touches nothing |
|
|
368
|
-
| `ApplicationResourceFailure` | _type_ | one resource failure with the cause its phase label cannot carry — delivered to `onResourceFailure` |
|
|
369
|
-
| `ApplicationResourcePhase` | _type_ | the phase a managed resource failed in — the vocabulary of `ApplicationResourceShutdown.failures` |
|
|
370
|
-
| `ApplicationShutdownOptionsSchema` / `ApplicationShutdownOptions` | schema / _type_ | the two shutdown budgets and an abort signal — without the HTTP-only `retryAfterSeconds` |
|
|
371
|
-
| `ApplicationShutdownBudgetSchema` / `ApplicationShutdownBudget` | schema / _type_ | the same two budgets without a signal — `ApplicationConfig.shutdown`, the default for `shutdown()` and the only budget a failed startup's rollback can read |
|
|
372
|
-
| `ActivityTokenBrand` | const | the brand symbol `ActivityToken` carries, exported so `ActivityProjection` is implementable |
|
|
373
|
-
| `defineManagedResource` | function | retain the exact typed resource declaration; every invoked start is rollback-eligible |
|
|
374
|
-
| `managedServerResource` | function | own a managed server's lifecycle without copying its HTTP/WebSocket shutdown machine — a thunk is called during `start`, and the handle is published to dependants |
|
|
375
|
-
| `managedResourceDependencyId` | function | the id of a dependency declared either as a string or as the resource itself |
|
|
376
|
-
| `createApplicationHealthHandler` | function | build a Fetch-clean liveness or readiness response from the sanitized application snapshot |
|
|
377
|
-
| `createApplicationOperationalHandlers` | function | compose always-readable status plus the canonical readiness/liveness handlers |
|
|
378
|
-
| `ApplicationAdmissionError` | class | stable `APPLICATION_NOT_ACCEPTING` rejection from `admission.run(...)` |
|
|
379
|
-
| `ApplicationConfig` / `ApplicationHandle` | _type_ | application declaration and its start/snapshot/subscription/admission/shutdown handle |
|
|
380
|
-
| `ApplicationAdmission` / `ApplicationOperationLease` | _type_ | atomic process-local admission and idempotent release primitive |
|
|
381
|
-
| `ManagedResource` / `ManagedResourceContext` / `ManagedResourceStartResult` | _type_ | resource lifecycle callbacks, shared deadlines, health reporting, separate readiness/completion promises and the value a resource publishes to its dependants |
|
|
382
|
-
| `ManagedResourceAdmission` | _type_ | `context.admission`: `acquire()` now or `null`, and `acquireWhenAccepting(signal)` for a resource that fetches work — a poller waits instead of fetching while the application is starting or degraded; rejects on abort and once the application can no longer admit |
|
|
383
|
-
| `ManagedResourceDependency` | _type_ | a dependency named by id or given as the resource itself — the second form is what `context.use(...)` can type |
|
|
384
|
-
| `ManagedResourcePublished` | _type_ | the value type `context.use(resource)` returns, recovered from that resource's own `start` |
|
|
385
|
-
| `ManagedResourcePublishesNoValue` | _type_ | what `context.use(...)` returns for a resource that publishes nothing — a branded refusal rather than `never`, so reading it does not silently compile |
|
|
386
|
-
| `ManagedServerResourceConfig` | _type_ | the server or a sync/async factory receiving `ManagedResourceContext`, plus stable ID, dependencies and server shutdown policy (`retryAfterSeconds`, `realtimeCloseTimeoutMs`) |
|
|
387
|
-
| `ManagedServerResource` | _type_ | the resource `managedServerResource` returns, whose `start` publishes the `ManagedServerHandle` |
|
|
388
|
-
| `ApplicationHealthHandlerOptions` / `ApplicationHealthHandlerOptionsSchema` | _type_ / schema | liveness/readiness selection and sanitized `Retry-After` policy |
|
|
389
|
-
| `ApplicationOperationalHandlers` / `ApplicationOperationalHandlersOptions` / `ApplicationOperationalHandlersOptionsSchema` | _type_ / schema | conventional status/readiness/liveness route surface and shared retry policy |
|
|
390
|
-
|
|
391
|
-
### Bounded admission
|
|
392
|
-
|
|
393
|
-
| Export | Kind | Summary |
|
|
394
|
-
|--------|------|---------|
|
|
395
|
-
| `createBoundedAdmission` | function | process-local no-queue global/per-key concurrency and rate leases, optionally composed with application admission |
|
|
396
|
-
| `BoundedAdmissionPolicySchema` / `BoundedAdmissionPolicy` | schema / _type_ | finite global budget and optional finite per-key budget with `maxKeys`, declared either as one flat ceiling or as a resolver — never both |
|
|
397
|
-
| `BoundedAdmissionPerKeyLimitsSchema` / `BoundedAdmissionPerKeyLimits` | schema / _type_ | one key's resolved ceiling: `maxConcurrent` and optional `rate` |
|
|
398
|
-
| `BoundedAdmissionPerKeyLimitResolver` | _type_ | `(key) => limits`, resolved on a key's first admission and cached until the key is evicted |
|
|
399
|
-
| `BoundedRateBudgetSchema` / `BoundedRateBudget` | schema / _type_ | `{ limit, intervalMs }` monotonic sliding-window budget |
|
|
400
|
-
| `BoundedAdmissionStateSchema` / `BoundedAdmissionState` | schema / _type_ | `accepting \| draining \| closed` |
|
|
401
|
-
| `BoundedAdmissionRefusalReasonSchema` / `BoundedAdmissionRefusalReason` | schema / _type_ | exact local/upstream refusal vocabulary |
|
|
402
|
-
| `BoundedAdmissionSnapshotSchema` / `BoundedAdmissionSnapshot` | schema / _type_ | absolute active/lifetime/refusal/rate-accounting counters |
|
|
403
|
-
| `BoundedAdmission` / `BoundedAdmissionConfig` / `BoundedAdmissionClock` | _type_ | handle, policy/upstream/clock configuration and monotonic clock seam |
|
|
404
|
-
| `BoundedAdmissionResult` / `BoundedAdmissionLeaseResult` / `BoundedAdmissionRefusedResult` | _type_ | explicit leased-or-refused acquisition result; retry time exists only for rate bounds |
|
|
405
|
-
| `BoundedOperationLease` | _type_ | idempotent release lease, optionally carrying its key |
|
|
406
|
-
| `BoundedOperationRunContext` / `BoundedOperationRunOptions` | _type_ | underlying work signal and caller abort/timeout wait budget |
|
|
407
|
-
| `BoundedAdmissionDrainOptions` / `BoundedAdmissionDrainResult` / `BoundedAdmissionForceResult` | _type_ | bounded drain inputs and honest remaining-work results |
|
|
408
|
-
| `BoundedAdmissionRefusalError` | class | `run()` refusal with reason and optional `retryAfterMs` |
|
|
409
|
-
| `BoundedOperationWaitError` | class | caller wait ended as `cancelled` or `timed-out`; underlying capacity remains leased until work settles |
|
|
410
|
-
|
|
411
|
-
### Revision wake-up
|
|
412
|
-
|
|
413
|
-
| Export | Kind | Summary |
|
|
414
|
-
|--------|------|---------|
|
|
415
|
-
| `createRevisionSignal` | function | finite broadcast wait for the next monotonic process-local revision |
|
|
416
|
-
| `RevisionSignal` / `RevisionSignalConfig` / `RevisionSignalClock` / `RevisionSignalTimer` | _type_ | signal handle, waiter bound and replaceable timer boundary |
|
|
417
|
-
| `RevisionSignalWaitOptions` | _type_ | optional caller `signal` and positive `timeoutMs`; absent options wait until advance or close |
|
|
418
|
-
| `RevisionSignalWaitOutcomeSchema` / `RevisionSignalWaitOutcome` | schema / _type_ | `changed \| timed-out \| aborted \| closed \| capacity` |
|
|
419
|
-
| `RevisionSignalWaitResultSchema` / `RevisionSignalWaitResult` | schema / _type_ | terminal wait outcome with the revision observed at settlement |
|
|
420
|
-
| `RevisionSignalAdvanceResultSchema` / `RevisionSignalAdvanceResult` | schema / _type_ | successful `advanced` or post-close `closed`, with the resulting revision |
|
|
421
|
-
| `RevisionSignalStateSchema` / `RevisionSignalState` | schema / _type_ | `open \| closed` lifecycle |
|
|
422
|
-
| `RevisionSignalSnapshotSchema` / `RevisionSignalSnapshot` | schema / _type_ | current revision, finite pending count and lifetime outcome counters |
|
|
423
|
-
|
|
424
|
-
### Bounded delivery
|
|
425
|
-
|
|
426
|
-
| Export | Kind | Summary |
|
|
427
|
-
|--------|------|---------|
|
|
428
|
-
| `createBoundedChannel` | function | finite single-reader async channel with explicit ordered or latest-value policy |
|
|
429
|
-
| `BoundedChannelPolicySchema` / `BoundedChannelPolicy` | schema / _type_ | `ordered \| latest` retention policy |
|
|
430
|
-
| `BoundedChannelStateSchema` / `BoundedChannelState` | schema / _type_ | `open \| draining \| closed \| failed` |
|
|
431
|
-
| `BoundedChannelSnapshotSchema` / `BoundedChannelSnapshot` | schema / _type_ | exact retained count/bytes, waiter and outcome counters |
|
|
432
|
-
| `BoundedChannel` / `BoundedChannelConfig` / `BoundedChannelCloseOptions` | _type_ | iterator/offer handle, explicit count/byte/size policy and drain/discard close mode |
|
|
433
|
-
| `BoundedChannelOfferResult` | _type_ | `delivered`, `queued`, `coalesced`, or reasoned `refused` outcome |
|
|
434
|
-
| `BoundedChannelReaderError` | class | refusal of a second concurrent pending `next()` |
|
|
435
|
-
| `createCreditWindow` | function | finite byte-credit lease window with exact once-only replenishment |
|
|
436
|
-
| `CreditWindow` / `CreditWindowSnapshot` / `CreditWindowSnapshotSchema` | _type_ / schema | byte-credit handle and absolute accounting record |
|
|
437
|
-
| `CreditAcquireResult` / `CreditLease` | _type_ | reasoned refusal or idempotently releasable byte-credit lease |
|
|
438
|
-
| `CreditAcquireWaitOptions` | _type_ | `signal` / `timeoutMs` for the waiting `acquire` overload; absent budget waits until credit, close or abort |
|
|
439
|
-
| `CreditWaitResult` / `CreditWaitRefusalReason` | _type_ | waiting-acquire outcome; `insufficient-credit` is absent by construction, `timed-out` and `aborted` replace it |
|
|
440
|
-
|
|
441
|
-
### Bounded diagnostic journal
|
|
442
|
-
|
|
443
|
-
| Export | Kind | Summary |
|
|
444
|
-
|--------|------|---------|
|
|
445
|
-
| `createDiagnosticJournal` | function | create one schema-owned FIFO JSONL writer with bounded retained memory, exclusive local path ownership and finite rotation |
|
|
446
|
-
| `DiagnosticJournalConfig` / `DiagnosticJournal` | _type_ | owner schema/path/limits/failure observer and the synchronous `submit`, bounded-wait `flush`/`close`, status handle |
|
|
447
|
-
| `DiagnosticJournalLimitsSchema` / `DiagnosticJournalLimits` | schema / _type_ | positive event, pending-item, pending-byte, file-byte and retained-file limits |
|
|
448
|
-
| `DiagnosticJournalLockPolicySchema` / `DiagnosticJournalLockPolicy` | schema / _type_ | `refuse` (default) or `reclaim-stale`, which reclaims only a lock whose recorded owner is provably gone |
|
|
449
|
-
| `readDiagnosticJournalLockDiagnosis` / `DiagnosticJournalLockDiagnosis` | function / _type_ | why a `reclaim-stale` acquisition refused — attribution, PID liveness and optional process `identity` evidence (`matched`, `different-boot`, `reused-pid`, `pid-gone`, `legacy`, `unavailable`) — read off the thrown `EEXIST` |
|
|
450
|
-
| `DiagnosticJournalSubmitResultSchema` / `DiagnosticJournalSubmitResult` | schema / _type_ | accepted epoch/sequence or explicit invalid, oversized, capacity, closed or failed refusal |
|
|
451
|
-
| `DiagnosticJournalStatusSchema` / `DiagnosticJournalStatus` | schema / _type_ | state, limits, exact admission/write/failure counters, pending ownership, rotations, partial tails and last safe sequences |
|
|
452
|
-
| `DiagnosticJournalFrameSchema` / `DiagnosticJournalFrame` | schema / _type_ | version-1 JSONL frame carrying process epoch, contiguous accepted sequence and schema-validated JSON event |
|
|
453
|
-
| `DiagnosticJournalWaitResultSchema` / `DiagnosticJournalWaitResult` | schema / _type_ | flush settlement boundary with truthful settled, timed-out or cancelled result |
|
|
454
|
-
| `DiagnosticJournalCloseResultSchema` / `DiagnosticJournalCloseResult` | schema / _type_ | physical close or caller timeout/cancellation without pretending an active write stopped |
|
|
455
|
-
| `DiagnosticJournalStateSchema` / `DiagnosticJournalState` | schema / _type_ | `open \| draining \| closed \| failed` |
|
|
456
|
-
| `DiagnosticJournalRefusalReasonSchema` / `DiagnosticJournalRefusalReason` | schema / _type_ | `closed \| failed \| invalid \| oversized \| item-capacity \| byte-capacity` |
|
|
457
|
-
| `DiagnosticJournalFailurePhaseSchema` / `DiagnosticJournalFailurePhase` | schema / _type_ | internal `write \| rotation \| close` failure phase exposed only to status and the isolated observer |
|
|
458
|
-
| `DiagnosticJournalWaitOptions` / `DiagnosticJournalFailure` | _type_ | caller wait signal/timeout and isolated internal failure callback record |
|
|
459
|
-
|
|
460
|
-
`accepted` is bounded in-memory admission and `written` is completed append, not `fsync` or durable
|
|
461
|
-
delivery. The journal has no reader/replay/upload API. See the
|
|
462
|
-
[guide](../guide/application-kernel.md#bounded-local-diagnostic-journal),
|
|
463
|
-
[architecture](../architecture/diagnostic-journal.md) and [ADR
|
|
464
|
-
0134](../decisions/0134-diagnostic-journal-is-bounded-local-evidence.md).
|
|
465
|
-
|
|
466
|
-
### Managed schedules
|
|
467
|
-
|
|
468
|
-
| Export | Kind | Summary |
|
|
469
|
-
|--------|------|---------|
|
|
470
|
-
| `createManagedSchedule` | function | fixed-rate process-local timer that activates after top-level readiness and participates in drain |
|
|
471
|
-
| `ManagedScheduleOverlapSchema` / `ManagedScheduleOverlap` | schema / _type_ | `skip`, `queue-one` or bounded `parallel` overlap policy |
|
|
472
|
-
| `ManagedScheduleErrorPolicySchema` / `ManagedScheduleErrorPolicy` | schema / _type_ | `continue` or `stop-schedule` after an observed callback failure |
|
|
473
|
-
| `ManagedScheduleDescriptorSchema` / `ManagedScheduleDescriptor` | schema / _type_ | immutable public schedule identity and cadence policy |
|
|
474
|
-
| `ManagedScheduleStatusSchema` / `ManagedScheduleStatus` | schema / _type_ | absolute schedule state, revision, counters and timestamps |
|
|
475
|
-
|
|
476
|
-
Schedule authoring additionally exports `ManagedSchedule`, `ManagedScheduleConfig`,
|
|
477
|
-
`ManagedScheduleRunContext`, `ManagedScheduleClock` and `ManagedScheduleTimer`.
|
|
478
|
-
The clock exposes monotonic cadence/deadline arithmetic and a wall-clock projection for portable
|
|
479
|
-
status timestamps; it is a deterministic test boundary, not a durable scheduler.
|
|
480
|
-
|
|
481
|
-
### Application and activity projection
|
|
482
|
-
|
|
483
|
-
| Export | Kind | Summary |
|
|
484
|
-
|--------|------|---------|
|
|
485
|
-
| `createActivityProjection` | function | aggregate anonymous process-local activity into declared bounded stages and absolute snapshots |
|
|
486
|
-
| `createApplicationSnapshotSink` | function | latest-value delivery with one write in flight and one replaceable pending revision |
|
|
487
|
-
| `applicationLifecycleEvent` | function | project a sanitized lifecycle fact from a canonical application snapshot |
|
|
488
|
-
| `createApplicationEventSink` | function | bounded failure-isolated lifecycle-event delivery; events are not canonical state |
|
|
489
|
-
| `ActivitySnapshotSchema` / `ActivitySnapshot` | schema / _type_ | epoch, monotonic revision, timestamps, declared stages and aggregate counts |
|
|
490
|
-
| `ApplicationSnapshotSinkStatusSchema` / `ApplicationSnapshotSinkStatus` | schema / _type_ | immutable latest-value sink delivery and coalescing counters |
|
|
491
|
-
| `ApplicationLifecycleEventSchema` / `ApplicationLifecycleEvent` | schema / _type_ | sanitized operator-facing application lifecycle fact |
|
|
492
|
-
|
|
493
|
-
Activity authoring additionally exports `ActivityId`, `ActivityIdSchema`,
|
|
494
|
-
`ActivityStageId`, `ActivityStageIdSchema`, `ActivityStageSnapshot`,
|
|
495
|
-
`ActivityStageSnapshotSchema`, `ActivityLiveState`, `ActivityLiveStateSchema`,
|
|
496
|
-
`ActivityProjection`, `ActivityProjectionConfig`,
|
|
497
|
-
`ActivityProjectionSubscriberError`, `ActivityToken` and `ActivityTransition`.
|
|
498
|
-
Latest-value delivery exports `ApplicationSnapshotSink`,
|
|
499
|
-
`ApplicationSnapshotSinkConfig`, `ApplicationSnapshotSinkError` and
|
|
500
|
-
`RevisionedApplicationSnapshot`; event delivery exports `ApplicationEventSink`
|
|
501
|
-
and `ApplicationEventSinkConfig`.
|
|
502
|
-
|
|
503
|
-
### Canonical application records
|
|
504
|
-
|
|
505
|
-
These are also published on their own as
|
|
506
|
-
[`stitchkit/application/schemas`](#stitchkitapplicationschemas), which carries
|
|
507
|
-
no server runtime and can therefore be imported from a browser bundle.
|
|
508
|
-
|
|
509
|
-
The entrypoint exports each Zod schema beside its inferred type:
|
|
510
|
-
`ApplicationIdSchema` / `ApplicationId`, `ApplicationLifecycleSchema` /
|
|
511
|
-
`ApplicationLifecycle`, `ApplicationHealthSchema` / `ApplicationHealth`,
|
|
512
|
-
`ManagedResourceStateSchema` / `ManagedResourceState`,
|
|
513
|
-
`ApplicationAdmissionSnapshotSchema` / `ApplicationAdmissionSnapshot`,
|
|
514
|
-
`ManagedResourceSnapshotSchema` / `ManagedResourceSnapshot`,
|
|
515
|
-
`ApplicationSnapshotSchema` / `ApplicationSnapshot`,
|
|
516
|
-
`ApplicationStatusProjectionSchema` / `ApplicationStatusProjection` with
|
|
517
|
-
`projectApplicationStatus` — what a status or probe endpoint may publish, and
|
|
518
|
-
the function that derives it from a snapshot,
|
|
519
|
-
`ApplicationResourceShutdownSchema` / `ApplicationResourceShutdown`, and
|
|
520
|
-
`ApplicationShutdownResultSchema` / `ApplicationShutdownResult`.
|
|
521
|
-
|
|
522
|
-
### Decisions
|
|
523
|
-
|
|
524
|
-
A pipeline of policies that each vote `allow`, `deny` or `defer`, sharing its
|
|
525
|
-
vocabulary with an event topic declared `mode: 'decision'`.
|
|
526
|
-
|
|
527
|
-
| Export | Kind | Summary |
|
|
528
|
-
|--------|------|---------|
|
|
529
|
-
| `createDecisionPipeline` | function | run policies in declaration order; the first terminal verdict wins and the rest do not run |
|
|
530
|
-
| `DecisionPipelineConfig` | _type_ | `policyTimeoutMs` — how long one policy has to answer. Required, no default: a policy that never settles hangs every caller of the operation it guards |
|
|
531
|
-
| `PolicyDecisionSchema` / `PolicyDecision` | schema / _type_ | `allow`, `deny` with a reason, or `defer` — a deny cannot be silent |
|
|
532
|
-
| `DecisionPolicy` / `DecisionPipeline` | _types_ | one named voter over the caller's subject, and the pipeline it composes into |
|
|
533
|
-
| `DecisionResult` / `DecisionTraceEntry` | _types_ | the verdict, and the trace of what actually ran — not what was declared |
|
|
534
|
-
| `DecisionUndecidedError` | class | every policy deferred and no undecided outcome was declared: an unanswered question, raised rather than guessed |
|
|
535
|
-
| `DecisionPolicyError` | class | a policy returned a non-decision, threw, or ran past `policyTimeoutMs` — one error for all three, carrying the policy id and the trace so far |
|
|
536
|
-
|
|
537
|
-
### Keyspace and watched reads
|
|
538
|
-
|
|
539
|
-
| Export | Kind | Summary |
|
|
540
|
-
|--------|------|---------|
|
|
541
|
-
| `defineKeyspace` / `keyspaceResource` | function | a named record set read synchronously from memory and written through one serialised chain; backend first, then memory, then the change event |
|
|
542
|
-
| `openKeyspace` / `OpenedKeyspace` | function / type | the same keyspace opened directly, for an application that owns its own lifecycle rather than declaring resources to a kernel |
|
|
543
|
-
| `KeyspaceBackend` / `KeyspaceDeclaration` / `KeyspaceChange` / `KeyspaceResourceConfig` / `OpenKeyspace` | _types_ | the four-method durability port, the declaration, the announced change, the resource's options and the published handle. `backend` takes a value or a **factory** — a factory is called once per generation, so a restarted keyspace gets a backend that was never closed |
|
|
544
|
-
| `memoryKeyspaceBackend` / `sqliteKeyspaceBackend` / `SqliteKeyspaceBackendConfig` | function / type | a disposable in-process backend, and one table with a key and a JSON payload over a caller-owned database |
|
|
545
|
-
| `SqliteDatabase` / `SqliteStatement` / `SqliteValue` | _types_ | the minimal synchronous SQLite boundary the framework types against |
|
|
546
|
-
| `createWatchHub` / `watchKey` | function | one read per question however many are asking: single-flight per key, re-read on a declared topic, publish only what changed |
|
|
547
|
-
| `WatchHub` / `WatchHubConfig` / `WatchOperation` / `WatchSubscriber` / `AttachedWatcher` | _types_ | the hub, its read/watchable/invalidation policy, the operation identity, and one connection's attachment |
|
|
548
|
-
|
|
549
|
-
→ [ADR 0152](../decisions/0152-a-keyspace-is-memory-that-nothing-reaches-before-it-is-durable.md),
|
|
550
|
-
[ADR 0153](../decisions/0153-a-watched-read-is-one-read-per-question.md).
|
|
551
|
-
|
|
552
|
-
### Managed change subscriptions and scoped watches
|
|
553
|
-
|
|
554
|
-
`changeSubscriptionResource` (`stitchkit/application`) accepts
|
|
555
|
-
`ChangeSubscriptionConfig`: `id`, unique `keys` (1–128, at most 256 characters each),
|
|
556
|
-
`connect(signal)`, `reconcile(key, context)`, required `reconcileIntervalMs` (10 ms–1 h),
|
|
557
|
-
optional `timeoutMs` (30000; 1 ms–1 h), `backoff`, and `onError`.
|
|
558
|
-
`ChangeConnection` supplies `subscribe({ signal, hint, disconnected })`, which completes subscription
|
|
559
|
-
before the first read; `close()` releases it. `ChangeReconciliation` exposes `signal`
|
|
560
|
-
and a synchronous `commit(callback)` guard against late publication. Drivers must
|
|
561
|
-
honor cancellation. Uncooperative work retains its slot until settled instead of
|
|
562
|
-
allowing overlapping connections/reads. Errors reach `onError`; no raw payload or
|
|
563
|
-
connection string is logged by this resource. Recovery runs after reconnect and
|
|
564
|
-
periodically even when no notification arrives.
|
|
565
|
-
|
|
566
|
-
`WatchHubConfig` adds `maxSources` (1024), `maxSubscribers` (1024), and optional
|
|
567
|
-
`reconcileIntervalMs` (10 ms–1 h). `hub.attach(subscriber, scope?)` accepts a
|
|
568
|
-
`WatchAdmissionScope` containing a **server-verified** `key` (1–512 characters) and
|
|
569
|
-
revocation `signal`. The key separates sharing and is the optional third argument
|
|
570
|
-
of `read(operation, args, scope)`; a key's `instance` routes frames and never
|
|
571
|
-
separates sources. Attached over `maxSubscribers`, a subscriber gets a watcher whose
|
|
572
|
-
`open` refuses with `Watch subscriber capacity exceeded`; `attach` does not throw
|
|
573
|
-
for capacity. It must represent account, session and permissions;
|
|
574
|
-
never trust a client-supplied owner ID as that scope.
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
### Notification action and projection receipts
|
|
579
|
-
|
|
580
|
-
`NotificationOutboxConfig.delivery` accepts `NotificationDeliveryConfig`:
|
|
581
|
-
immutable executor `version`, pure `actions(payload)` plan selector (1–128 unique
|
|
582
|
-
names, each at most 128 characters), and idempotent `project(notification, receipts)`.
|
|
583
|
-
`send` receives optional `action` and stable `idempotencyKey`, and returns a JSON
|
|
584
|
-
remote receipt (void becomes null). A result that is not JSON or does not fit the
|
|
585
|
-
byte budget is kept as `receipt: null` with `unrecorded: 'not-json' | 'too-large'`;
|
|
586
|
-
the action stays confirmed and is not sent again. `retiredVersions` maps earlier
|
|
587
|
-
plan versions to their `NotificationProjection` so queued items drain after an
|
|
588
|
-
executor upgrade; an unknown version is reported to `onError` as
|
|
589
|
-
`NotificationPlanVersionError` and waits, spending no attempt and not consulting
|
|
590
|
-
`classify`, for an executor that knows it. `NotificationDeliveryStateSchema` validates the
|
|
591
|
-
optional `NotificationDeliveryState` on queued items and completed receipts:
|
|
592
|
-
`version`, `actions`, `receipts` and optional `projectedAt`. Checkpoints share the
|
|
593
|
-
existing atomic StateStore and lease fence. Receipt retention and serialized byte
|
|
594
|
-
budgets still apply. Single-send configuration remains valid without `delivery`.
|
|
595
|
-
A remote result lost before its checkpoint may be sent again; provider idempotency
|
|
596
|
-
or reconciliation is required. A projection may repeat after a crash before its
|
|
597
|
-
checkpoint. Business-transaction enqueue and durable quarantine are not implied.
|