@xyo-network/event-kit-node 2.1.0 → 2.2.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/README.md +53 -34
- package/dist/node/bestEffortSnapshotWorker.d.ts +44 -0
- package/dist/node/bestEffortSnapshotWorker.d.ts.map +1 -0
- package/dist/node/bestEffortTypes.d.ts +55 -0
- package/dist/node/bestEffortTypes.d.ts.map +1 -0
- package/dist/node/filesystemCursorStore.d.ts.map +1 -1
- package/dist/node/filesystemJson.d.ts.map +1 -1
- package/dist/node/filesystemPublisherOutbox.d.ts.map +1 -1
- package/dist/node/filesystemWakeInbox.d.ts +3 -20
- package/dist/node/filesystemWakeInbox.d.ts.map +1 -1
- package/dist/node/filesystemWakeQueue.d.ts +10 -19
- package/dist/node/filesystemWakeQueue.d.ts.map +1 -1
- package/dist/node/handshake.mjs +12 -11
- package/dist/node/handshake.mjs.map +2 -2
- package/dist/node/index.d.ts +8 -1
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.mjs +1909 -609
- package/dist/node/index.mjs.map +4 -4
- package/dist/node/privateSqlite.d.ts +19 -0
- package/dist/node/privateSqlite.d.ts.map +1 -0
- package/dist/node/receiverAuthority.d.ts +102 -0
- package/dist/node/receiverAuthority.d.ts.map +1 -0
- package/dist/node/receiverMigration.d.ts +16 -0
- package/dist/node/receiverMigration.d.ts.map +1 -0
- package/dist/node/receiverMigrationInventory.d.ts +6 -0
- package/dist/node/receiverMigrationInventory.d.ts.map +1 -0
- package/dist/node/receiverMigrationSnapshot.d.ts +6 -0
- package/dist/node/receiverMigrationSnapshot.d.ts.map +1 -0
- package/dist/node/receiverMigrationTypes.d.ts +113 -0
- package/dist/node/receiverMigrationTypes.d.ts.map +1 -0
- package/dist/node/runtime.mjs.map +2 -2
- package/dist/node/sqliteBestEffortInbox.d.ts +20 -0
- package/dist/node/sqliteBestEffortInbox.d.ts.map +1 -0
- package/dist/node/sqliteWakeQueue.d.ts +83 -17
- package/dist/node/sqliteWakeQueue.d.ts.map +1 -1
- package/package.json +13 -13
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# `@xyo-network/event-kit-node`
|
|
2
2
|
|
|
3
|
-
Node.js
|
|
3
|
+
Node.js 26+ durability adapters for Event Kit. The repository pins Node 26.9.0:
|
|
4
4
|
|
|
5
5
|
- `createFilesystemCursorStore` — publisher cursor for the wake publisher actor
|
|
6
6
|
- `createFilesystemPublisherOutbox` — durable pending-wake slot for retry stability
|
|
7
|
-
- `
|
|
8
|
-
- `createSqliteWakeQueue` — `WakeQueue` over `node:sqlite
|
|
9
|
-
- `createFilesystemWakeInbox` —
|
|
7
|
+
- `createSqliteWakeReceiver` — atomic durable inbox/queue over one private FULL/WAL SQLite database
|
|
8
|
+
- `createSqliteWakeQueue` — standalone fenced `WakeQueue` over `node:sqlite`
|
|
9
|
+
- `createFilesystemWakeQueue` / `createFilesystemWakeInbox` — coordinated SQLite authority with inspectable JSON projections
|
|
10
|
+
- `createSqliteBestEffortInbox` / `createBestEffortSnapshotWorker` — persistent bounded hint security/coalescing and independent snapshot work
|
|
11
|
+
- `migrateReceiverStore` / `migrateIdOnlyReducerCheckpoint` — explicit stopped-owner offline migration and reports
|
|
10
12
|
|
|
11
13
|
## Handshake clients
|
|
12
14
|
|
|
@@ -63,43 +65,60 @@ the remaining hosted/Sequence gates.
|
|
|
63
65
|
|
|
64
66
|
## Durability model
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
68
|
+
The [2026-10-05 remediation](../../docs/CODE_REVIEW_REMEDIATION_PLAN_2026-10-05.md)
|
|
69
|
+
replaces split identity/enqueue commits with one transaction over scoped wake
|
|
70
|
+
and token bindings, a stable receipt, exact authorized work, lease state and
|
|
71
|
+
completion. Duplicate token binding/expiry extension is atomic. FULL/WAL SQLite
|
|
72
|
+
coordinates objects and processes on one local host; a bounded 100 ms busy wait
|
|
73
|
+
fails visibly under unresolved contention. Retrying retains logical identity.
|
|
74
|
+
|
|
75
|
+
The filesystem adapter uses the same authority in `receiver.sqlite`. Private
|
|
76
|
+
`pending/` and `inflight/` JSON files are projections, published exclusively with
|
|
77
|
+
`link` and synced directories. They do not decide admission or lease ownership.
|
|
78
|
+
Missing projections can be repaired; corrupt or mismatching bytes stop processing
|
|
79
|
+
and remain available for inspection. Claims validate shape and canonical hash
|
|
80
|
+
against committed authority. Completion removes the retained body while keeping
|
|
81
|
+
the stable identity/receipt tombstone; stale files cannot resurrect completed work.
|
|
82
|
+
`repairProjections()` scans active work and filesystem references explicitly.
|
|
83
|
+
|
|
84
|
+
Owned directories/files are `0700`/`0600`, with owner, regular-file and symlink
|
|
85
|
+
checks. Unsafe existing custody is refused instead of chmodding unrelated paths.
|
|
86
|
+
Publisher cursor/outbox replacement remains single-writer `rename`, with file
|
|
87
|
+
and publication-directory syncs. Read, parse, state and sync failures propagate;
|
|
88
|
+
retained retry bytes/IDs are never replaced because a read failed.
|
|
89
|
+
|
|
90
|
+
Legacy stores are refused before journal-mode mutation. Use the
|
|
91
|
+
[offline migration runbook](../../docs/CODE_REVIEW_RECEIVER_MIGRATION_2026-10-05.md)
|
|
92
|
+
on stopped-owner copies; retain backup, inventory and quarantine evidence. The
|
|
93
|
+
new source format is intentionally incompatible with old writers and runtime
|
|
94
|
+
dual readers are absent.
|
|
84
95
|
|
|
85
96
|
## Queue providers
|
|
86
97
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
98
|
+
All three providers assign a fresh lease fence on each claim. `ack()`/`release()`
|
|
99
|
+
compare the current entry/state/token atomically. A stale or opposite completion
|
|
100
|
+
throws `StaleWakeLeaseError`; repeating the same successful operation is inert.
|
|
101
|
+
Recovery requires `recoverInflight({ consumersStopped: true })` after every
|
|
102
|
+
consumer sharing the store has stopped. The assertion is an operator contract,
|
|
103
|
+
not a liveness detector. Startup alone does not establish quiescence.
|
|
104
|
+
|
|
105
|
+
Expanded shared conformance covers stale/repeated completion and successor
|
|
106
|
+
preservation. Independent-object/process admission races, real SIGKILL boundaries,
|
|
107
|
+
private custody and signed HTTP tests supplement it. Provider clauses requiring
|
|
108
|
+
`reopen`/cross-writer support remain explicitly skipped for memory.
|
|
90
109
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
of what the filesystem provider approximates with `link`. WAL lets several
|
|
94
|
-
processes on one host share the file; it does not coordinate across hosts.
|
|
110
|
+
Application effects remain at least once. Fence queue state and separately make
|
|
111
|
+
external effects idempotent or atomically commit effects/checkpoints in their owner.
|
|
95
112
|
|
|
96
|
-
`node:sqlite` is built in, so this adds no dependency. It does emit an
|
|
97
|
-
ExperimentalWarning.
|
|
113
|
+
`node:sqlite` is built in, so this adds no dependency. It does not emit an
|
|
114
|
+
ExperimentalWarning when loaded on the pinned Node 26.9.0 runtime.
|
|
98
115
|
|
|
99
116
|
## Boundary
|
|
100
117
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
118
|
+
Current recovery evidence covers local objects/processes and copied fixtures,
|
|
119
|
+
not host-loss, storage hardware failure, restore or cross-machine qualification.
|
|
120
|
+
Historical core Gate 5 evidence remains scoped to its original artifacts. See
|
|
121
|
+
the remediation plan for current checks and measured performance/retention costs.
|
|
122
|
+
The on-chain publisher roadmap
|
|
104
123
|
qualifies a single VM in Phases 4-7; a coordinated multi-VM deployment needs a
|
|
105
124
|
transactional shared store, leases, and fencing and is roadmap Phase 8.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { BestEffortWorkerLease, PersistentBestEffortInbox, VerifiedBestEffortSnapshot } from './bestEffortTypes.ts';
|
|
2
|
+
/** The port must enforce the byte bound while reading and independently verify source identity/finality/content. */
|
|
3
|
+
export interface BestEffortSnapshotSource<TSnapshot, TValue> {
|
|
4
|
+
readCurrent(input: {
|
|
5
|
+
readonly maxBytes: number;
|
|
6
|
+
readonly signal: AbortSignal;
|
|
7
|
+
readonly source: BestEffortWorkerLease['binding']['source'];
|
|
8
|
+
}): Promise<TSnapshot>;
|
|
9
|
+
verifySnapshot(input: {
|
|
10
|
+
readonly signal: AbortSignal;
|
|
11
|
+
readonly snapshot: TSnapshot;
|
|
12
|
+
readonly source: BestEffortWorkerLease['binding']['source'];
|
|
13
|
+
}): Promise<VerifiedBestEffortSnapshot<TValue>>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Host-owned effect/checkpoint commit. The host must atomically reject older
|
|
17
|
+
* binding/fence commits and make repeats safe through idempotence or its effect
|
|
18
|
+
* journal/checkpoint transaction. A process can die after this commit and before
|
|
19
|
+
* inbox completion, so it can be repeated. `lease.isCurrent()` is advisory and
|
|
20
|
+
* cannot replace the app's own atomic fence/effect commit.
|
|
21
|
+
* dapp-kit consumers implement this through their effect journal/checkpoint,
|
|
22
|
+
* rather than an independent app engine in Event Kit.
|
|
23
|
+
*/
|
|
24
|
+
export interface BestEffortSnapshotApplication<TValue> {
|
|
25
|
+
commitSnapshot(input: {
|
|
26
|
+
readonly lease: BestEffortWorkerLease;
|
|
27
|
+
readonly snapshot: VerifiedBestEffortSnapshot<TValue>;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
export interface BestEffortSnapshotWorkerOptions<TSnapshot, TValue> {
|
|
31
|
+
readonly application: BestEffortSnapshotApplication<TValue>;
|
|
32
|
+
readonly inbox: PersistentBestEffortInbox;
|
|
33
|
+
readonly leaseSeconds?: number;
|
|
34
|
+
readonly maxSnapshotBytes: number;
|
|
35
|
+
readonly nowSeconds?: () => number;
|
|
36
|
+
readonly source: BestEffortSnapshotSource<TSnapshot, TValue>;
|
|
37
|
+
}
|
|
38
|
+
export interface BestEffortSnapshotWorker {
|
|
39
|
+
/** Run at startup and periodically; durable scanning, not callbacks, chooses work. */
|
|
40
|
+
runOnce(signal?: AbortSignal): Promise<'idle' | 'completed' | 'superseded'>;
|
|
41
|
+
}
|
|
42
|
+
/** A bounded observer worker; no full-history replay or public-ingress app execution. */
|
|
43
|
+
export declare function createBestEffortSnapshotWorker<TSnapshot, TValue>(options: BestEffortSnapshotWorkerOptions<TSnapshot, TValue>): BestEffortSnapshotWorker;
|
|
44
|
+
//# sourceMappingURL=bestEffortSnapshotWorker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bestEffortSnapshotWorker.d.ts","sourceRoot":"","sources":["../../src/bestEffortSnapshotWorker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,qBAAqB,EAAE,yBAAyB,EAAE,0BAA0B,EAC7E,MAAM,sBAAsB,CAAA;AAE7B,oHAAoH;AACpH,MAAM,WAAW,wBAAwB,CAAC,SAAS,EAAE,MAAM;IACzD,WAAW,CAAC,KAAK,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;QACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;QAC5B,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAA;KAC5D,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA;IACtB,cAAc,CAAC,KAAK,EAAE;QACpB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;QAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;QAC5B,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAA;KAC5D,GAAG,OAAO,CAAC,0BAA0B,CAAC,MAAM,CAAC,CAAC,CAAA;CAChD;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,6BAA6B,CAAC,MAAM;IACnD,cAAc,CAAC,KAAK,EAAE;QACpB,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAA;QACrC,QAAQ,CAAC,QAAQ,EAAE,0BAA0B,CAAC,MAAM,CAAC,CAAA;KACtD,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClB;AAED,MAAM,WAAW,+BAA+B,CAAC,SAAS,EAAE,MAAM;IAChE,QAAQ,CAAC,WAAW,EAAE,6BAA6B,CAAC,MAAM,CAAC,CAAA;IAC3D,QAAQ,CAAC,KAAK,EAAE,yBAAyB,CAAA;IACzC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,MAAM,CAAA;IAClC,QAAQ,CAAC,MAAM,EAAE,wBAAwB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;CAC7D;AAED,MAAM,WAAW,wBAAwB;IACvC,sFAAsF;IACtF,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,WAAW,GAAG,YAAY,CAAC,CAAA;CAC5E;AAED,yFAAyF;AACzF,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,MAAM,EAC9D,OAAO,EAAE,+BAA+B,CAAC,SAAS,EAAE,MAAM,CAAC,GAC1D,wBAAwB,CAqE1B"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { AtomicBestEffortInbox } from '@xyo-network/event-kit-protocol';
|
|
2
|
+
import type { Hash, WakeSource } from '@xyo-network/event-kit-schemas';
|
|
3
|
+
/** The exact authenticated consumer/source/profile scope; filterHash commits mode and policy. */
|
|
4
|
+
export interface BestEffortConsumerBinding {
|
|
5
|
+
readonly audience: string;
|
|
6
|
+
readonly authorizationChainId: string;
|
|
7
|
+
readonly consumerId: string;
|
|
8
|
+
readonly controller: string;
|
|
9
|
+
readonly deploymentId: string;
|
|
10
|
+
readonly filterHash: string;
|
|
11
|
+
readonly publisher: string;
|
|
12
|
+
readonly queueId?: string;
|
|
13
|
+
readonly source: WakeSource;
|
|
14
|
+
readonly subscriptionHash: string;
|
|
15
|
+
}
|
|
16
|
+
/** Durable scheduling authority. A callback is only an acceleration signal. */
|
|
17
|
+
export interface BestEffortDirtyTarget {
|
|
18
|
+
readonly binding: BestEffortConsumerBinding;
|
|
19
|
+
readonly completedGeneration: number;
|
|
20
|
+
readonly generation: number;
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly requestedPosition: number;
|
|
23
|
+
}
|
|
24
|
+
/** A receiver-verified bounded snapshot, never evidence supplied by the hint publisher. */
|
|
25
|
+
export interface VerifiedBestEffortSnapshot<TValue = unknown> {
|
|
26
|
+
readonly hash?: Hash;
|
|
27
|
+
readonly position: number;
|
|
28
|
+
readonly source: WakeSource;
|
|
29
|
+
readonly value: TValue;
|
|
30
|
+
}
|
|
31
|
+
export interface BestEffortWorkerLease extends BestEffortDirtyTarget {
|
|
32
|
+
/** Monotonic per-target fence. Host app commits must fence their own effects/checkpoint. */
|
|
33
|
+
readonly fence: number;
|
|
34
|
+
readonly leaseToken: string;
|
|
35
|
+
readonly previousSnapshot?: Pick<VerifiedBestEffortSnapshot, 'hash' | 'position'>;
|
|
36
|
+
complete(snapshot: Pick<VerifiedBestEffortSnapshot, 'hash' | 'position'>): Promise<boolean>;
|
|
37
|
+
isCurrent(): Promise<boolean>;
|
|
38
|
+
release(): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
export interface PersistentBestEffortInbox extends AtomicBestEffortInbox {
|
|
41
|
+
claim(input: {
|
|
42
|
+
readonly leaseSeconds: number;
|
|
43
|
+
readonly nowSeconds: number;
|
|
44
|
+
}): Promise<BestEffortWorkerLease | undefined>;
|
|
45
|
+
close(): void;
|
|
46
|
+
dirtyTargets(): Promise<readonly BestEffortDirtyTarget[]>;
|
|
47
|
+
/** Expired work remains dirty; a reclaimed worker gets a new token and fence. */
|
|
48
|
+
recoverExpired(nowSeconds: number): Promise<number>;
|
|
49
|
+
/** Startup/periodic scanning repairs lost callback delivery. Failure cannot delete dirty work. */
|
|
50
|
+
repairDirty(): Promise<{
|
|
51
|
+
readonly failed: number;
|
|
52
|
+
readonly scheduled: number;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=bestEffortTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bestEffortTypes.d.ts","sourceRoot":"","sources":["../../src/bestEffortTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAA;AAC5E,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAA;AAEtE,iGAAiG;AACjG,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAA;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;CAClC;AAED,+EAA+E;AAC/E,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,yBAAyB,CAAA;IAC3C,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAA;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;CACnC;AAED,2FAA2F;AAC3F,MAAM,WAAW,0BAA0B,CAAC,MAAM,GAAG,OAAO;IAC1D,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAA;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,qBAAsB,SAAQ,qBAAqB;IAClE,4FAA4F;IAC5F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,IAAI,CAAC,0BAA0B,EAAE,MAAM,GAAG,UAAU,CAAC,CAAA;IACjF,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,0BAA0B,EAAE,MAAM,GAAG,UAAU,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3F,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAED,MAAM,WAAW,yBAA0B,SAAQ,qBAAqB;IACtE,KAAK,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAAA;IACxH,KAAK,IAAI,IAAI,CAAA;IACb,YAAY,IAAI,OAAO,CAAC,SAAS,qBAAqB,EAAE,CAAC,CAAA;IACzD,iFAAiF;IACjF,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACnD,kGAAkG;IAClG,WAAW,IAAI,OAAO,CAAC;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAChF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filesystemCursorStore.d.ts","sourceRoot":"","sources":["../../src/filesystemCursorStore.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"filesystemCursorStore.d.ts","sourceRoot":"","sources":["../../src/filesystemCursorStore.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AAQxE,MAAM,WAAW,4BAA4B;IAC3C,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAID;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,4BAA4B,GACpC,oBAAoB,CAuBtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filesystemJson.d.ts","sourceRoot":"","sources":["../../src/filesystemJson.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"filesystemJson.d.ts","sourceRoot":"","sources":["../../src/filesystemJson.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWvE;AAED,+EAA+E;AAC/E,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CAYf;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,OAAO,CAAC,CAelB;AAMD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEpD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filesystemPublisherOutbox.d.ts","sourceRoot":"","sources":["../../src/filesystemPublisherOutbox.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAe,eAAe,EAAE,MAAM,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"filesystemPublisherOutbox.d.ts","sourceRoot":"","sources":["../../src/filesystemPublisherOutbox.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAe,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAWhF,MAAM,WAAW,gCAAgC;IAC/C,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,gCAAgC,GACxC,eAAe,CA2DjB"}
|
|
@@ -1,30 +1,13 @@
|
|
|
1
1
|
import type { AtomicWakeInbox } from '@xyo-network/event-kit-protocol';
|
|
2
2
|
import type { FilesystemWakeQueue } from './filesystemWakeQueue.ts';
|
|
3
3
|
export interface FilesystemWakeInboxOptions {
|
|
4
|
-
/** Directory owning the `tokens/` and `wakes/` record sets. */
|
|
5
4
|
readonly path: string;
|
|
6
|
-
/**
|
|
5
|
+
/** Must be the coordinated queue created by createFilesystemWakeQueue. */
|
|
7
6
|
readonly queue: FilesystemWakeQueue;
|
|
8
7
|
}
|
|
9
8
|
export interface FilesystemWakeInbox extends AtomicWakeInbox {
|
|
10
|
-
|
|
11
|
-
pruneExpiredTokens: (nowSeconds: number) => Promise<number>;
|
|
9
|
+
pruneExpiredTokens(nowSeconds: number): Promise<number>;
|
|
12
10
|
}
|
|
13
|
-
/**
|
|
14
|
-
* Crash-safe single-node `AtomicWakeInbox`.
|
|
15
|
-
*
|
|
16
|
-
* Commit order is the correctness property: the wake reaches the durable queue
|
|
17
|
-
* **before** either identity record is written. A crash in the gap redelivers
|
|
18
|
-
* the wake, which the idempotent consumer absorbs; the reverse order could
|
|
19
|
-
* claim `wakeId` for a wake that was never queued and wedge it permanently.
|
|
20
|
-
* D-002 §6 forbids burning a valid `jti` ahead of a fallible enqueue.
|
|
21
|
-
*
|
|
22
|
-
* Identity records are published by `link`, which fails rather than overwrites,
|
|
23
|
-
* so a concurrent writer loses the race and re-reads the winner's receipt.
|
|
24
|
-
*
|
|
25
|
-
* This survives process death, not host loss, and does not coordinate across
|
|
26
|
-
* machines. A multi-node deployment needs a transactional store or a cloud
|
|
27
|
-
* queue with native deduplication.
|
|
28
|
-
*/
|
|
11
|
+
/** Both identity indexes, stable receipt and exact work share the queue's transaction. */
|
|
29
12
|
export declare function createFilesystemWakeInbox(options: FilesystemWakeInboxOptions): FilesystemWakeInbox;
|
|
30
13
|
//# sourceMappingURL=filesystemWakeInbox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filesystemWakeInbox.d.ts","sourceRoot":"","sources":["../../src/filesystemWakeInbox.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"filesystemWakeInbox.d.ts","sourceRoot":"","sources":["../../src/filesystemWakeInbox.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAEtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAInE,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAA;CACpC;AACD,MAAM,WAAW,mBAAoB,SAAQ,eAAe;IAC1D,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACxD;AACD,0FAA0F;AAC1F,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,GAAG,mBAAmB,CAiBlG"}
|
|
@@ -1,28 +1,19 @@
|
|
|
1
1
|
import type { WakeQueue } from '@xyo-network/event-kit-protocol';
|
|
2
2
|
import { ensureDurableDirectory, syncDirectory, writeDurableFile } from './filesystemDurability.ts';
|
|
3
|
+
import type { ReceiverAuthority } from './receiverAuthority.ts';
|
|
3
4
|
export { ensureDurableDirectory, syncDirectory, writeDurableFile, };
|
|
4
5
|
export interface FilesystemWakeQueueOptions {
|
|
5
|
-
/**
|
|
6
|
+
/** Private directory; receiver.sqlite owns state, JSON files are projections. */
|
|
6
7
|
readonly path: string;
|
|
7
8
|
}
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* fails with `EEXIST`, so exactly one claimer wins.
|
|
16
|
-
*
|
|
17
|
-
* A claim is `link(pending -> inflight)` followed by `unlink(pending)`. A crash
|
|
18
|
-
* between the two leaves the entry in both sets as one inode; recovery drops the
|
|
19
|
-
* inflight copy and the entry stays pending. A consumer that dies mid-handler
|
|
20
|
-
* leaves its entry inflight for `recoverInflight` rather than dropping it.
|
|
21
|
-
*
|
|
22
|
-
* Ordering is approximate FIFO by entry name. Single-node only: this survives
|
|
23
|
-
* process death, not host loss, and does not coordinate across machines.
|
|
24
|
-
*/
|
|
9
|
+
export interface FilesystemWakeQueue extends WakeQueue {
|
|
10
|
+
close(): void;
|
|
11
|
+
repairProjections(): Promise<void>;
|
|
12
|
+
repairRow(id: number): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare function filesystemReceiverAuthority(queue: FilesystemWakeQueue): ReceiverAuthority;
|
|
15
|
+
/** Filesystem-facing receiver with one SQLite admission/lease/completion authority. */
|
|
25
16
|
export declare function createFilesystemWakeQueue(options: FilesystemWakeQueueOptions): FilesystemWakeQueue;
|
|
26
|
-
/**
|
|
17
|
+
/** Only open-time absence means an empty optional record. */
|
|
27
18
|
export declare function readJsonFile<T>(path: string): Promise<T | undefined>;
|
|
28
19
|
//# sourceMappingURL=filesystemWakeQueue.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filesystemWakeQueue.d.ts","sourceRoot":"","sources":["../../src/filesystemWakeQueue.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"filesystemWakeQueue.d.ts","sourceRoot":"","sources":["../../src/filesystemWakeQueue.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAA;AAGhE,OAAO,EACL,sBAAsB,EAAE,aAAa,EAAE,gBAAgB,EACxD,MAAM,2BAA2B,CAAA;AAIlC,OAAO,KAAK,EAAE,iBAAiB,EAAoB,MAAM,wBAAwB,CAAA;AAGjF,OAAO,EACL,sBAAsB,EAAE,aAAa,EAAE,gBAAgB,GACxD,CAAA;AAED,MAAM,WAAW,0BAA0B;IACzC,iFAAiF;IACjF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AACD,MAAM,WAAW,mBAAoB,SAAQ,SAAS;IACpD,KAAK,IAAI,IAAI,CAAA;IACb,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAClC,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACrC;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,mBAAmB,GAAG,iBAAiB,CAIzF;AAED,uFAAuF;AACvF,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,GAAG,mBAAmB,CA4HlG;AAED,6DAA6D;AAC7D,wBAAsB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAY1E"}
|
package/dist/node/handshake.mjs
CHANGED
|
@@ -58,25 +58,26 @@ function errorCode(error) {
|
|
|
58
58
|
|
|
59
59
|
// src/filesystemJson.ts
|
|
60
60
|
async function readJsonFileStrict(path) {
|
|
61
|
+
let handle;
|
|
61
62
|
try {
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
return JSON.parse(await handle.readFile("utf8"), reviveJson);
|
|
65
|
-
} finally {
|
|
66
|
-
await handle.close();
|
|
67
|
-
}
|
|
63
|
+
handle = await open2(path, "r");
|
|
68
64
|
} catch (error) {
|
|
69
65
|
if (errorCode2(error) === "ENOENT") return void 0;
|
|
70
66
|
throw error;
|
|
71
67
|
}
|
|
68
|
+
try {
|
|
69
|
+
return JSON.parse(await handle.readFile("utf8"), reviveJson);
|
|
70
|
+
} finally {
|
|
71
|
+
await handle.close();
|
|
72
|
+
}
|
|
72
73
|
}
|
|
73
74
|
async function replaceJsonFile(path, value, options = {}) {
|
|
74
75
|
const directory = PATH2.dirname(path);
|
|
75
76
|
await ensureDurableDirectory(directory, options.directoryMode);
|
|
76
77
|
const tempPath = PATH2.join(directory, `.${PATH2.basename(path)}.${process.pid}.${suffix()}.tmp`);
|
|
77
|
-
await writeDurableFile(tempPath, `${stringifyJson(value)}
|
|
78
|
-
`, options.mode);
|
|
79
78
|
try {
|
|
79
|
+
await writeDurableFile(tempPath, `${stringifyJson(value)}
|
|
80
|
+
`, options.mode);
|
|
80
81
|
await rename(tempPath, path);
|
|
81
82
|
await syncDirectory(directory);
|
|
82
83
|
} catch (error) {
|
|
@@ -88,9 +89,9 @@ async function publishJsonFile(path, value, options = {}) {
|
|
|
88
89
|
const directory = PATH2.dirname(path);
|
|
89
90
|
await ensureDurableDirectory(directory, options.directoryMode);
|
|
90
91
|
const tempPath = PATH2.join(directory, `.${PATH2.basename(path)}.${process.pid}.${suffix()}.tmp`);
|
|
91
|
-
await writeDurableFile(tempPath, `${stringifyJson(value)}
|
|
92
|
-
`, options.mode);
|
|
93
92
|
try {
|
|
93
|
+
await writeDurableFile(tempPath, `${stringifyJson(value)}
|
|
94
|
+
`, options.mode);
|
|
94
95
|
await link(tempPath, path);
|
|
95
96
|
await syncDirectory(directory);
|
|
96
97
|
return true;
|
|
@@ -1197,7 +1198,7 @@ function createPublisherHandshakeClient(options) {
|
|
|
1197
1198
|
const reconciliation = await reconcileAcceptance(acceptanceHash, publication, intent.challenge.subscriber, intent.challenge.subscription);
|
|
1198
1199
|
return Object.freeze({
|
|
1199
1200
|
acceptance: Object.freeze({ body: intent.acceptance, objectHash: acceptanceHash }),
|
|
1200
|
-
challenge: intent.
|
|
1201
|
+
challenge: intent.acceptance.challenge,
|
|
1201
1202
|
intentDisposition,
|
|
1202
1203
|
operationId: intent.operationId,
|
|
1203
1204
|
publication,
|