@powerhousedao/reactor 4.1.0-dev.81 → 4.1.0-dev.83

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.
Files changed (54) hide show
  1. package/dist/src/cache/buffer/ring-buffer.d.ts +37 -0
  2. package/dist/src/cache/buffer/ring-buffer.d.ts.map +1 -0
  3. package/dist/src/cache/buffer/ring-buffer.js +69 -0
  4. package/dist/src/cache/buffer/ring-buffer.js.map +1 -0
  5. package/dist/src/cache/kysely-write-cache.d.ts +99 -0
  6. package/dist/src/cache/kysely-write-cache.d.ts.map +1 -0
  7. package/dist/src/cache/kysely-write-cache.js +309 -0
  8. package/dist/src/cache/kysely-write-cache.js.map +1 -0
  9. package/dist/src/cache/lru/lru-tracker.d.ts +15 -0
  10. package/dist/src/cache/lru/lru-tracker.d.ts.map +1 -0
  11. package/dist/src/cache/lru/lru-tracker.js +96 -0
  12. package/dist/src/cache/lru/lru-tracker.js.map +1 -0
  13. package/dist/src/cache/types.d.ts +42 -0
  14. package/dist/src/cache/types.d.ts.map +1 -0
  15. package/dist/src/cache/types.js +2 -0
  16. package/dist/src/cache/types.js.map +1 -0
  17. package/dist/src/cache/write/interfaces.d.ts +85 -0
  18. package/dist/src/cache/write/interfaces.d.ts.map +1 -0
  19. package/dist/src/cache/write/interfaces.js +2 -0
  20. package/dist/src/cache/write/interfaces.js.map +1 -0
  21. package/dist/src/executor/simple-job-executor.d.ts +0 -14
  22. package/dist/src/executor/simple-job-executor.d.ts.map +1 -1
  23. package/dist/src/executor/simple-job-executor.js +23 -43
  24. package/dist/src/executor/simple-job-executor.js.map +1 -1
  25. package/dist/src/executor/types.d.ts +6 -0
  26. package/dist/src/executor/types.d.ts.map +1 -1
  27. package/dist/src/executor/types.js.map +1 -1
  28. package/dist/src/executor/util.d.ts +16 -0
  29. package/dist/src/executor/util.d.ts.map +1 -0
  30. package/dist/src/executor/util.js +31 -0
  31. package/dist/src/executor/util.js.map +1 -0
  32. package/dist/src/index.d.ts +4 -2
  33. package/dist/src/index.d.ts.map +1 -1
  34. package/dist/src/index.js +3 -2
  35. package/dist/src/index.js.map +1 -1
  36. package/dist/src/read-models/document-view.d.ts.map +1 -1
  37. package/dist/src/read-models/document-view.js +11 -9
  38. package/dist/src/read-models/document-view.js.map +1 -1
  39. package/dist/src/storage/interfaces.d.ts +13 -0
  40. package/dist/src/storage/interfaces.d.ts.map +1 -1
  41. package/dist/src/storage/interfaces.js.map +1 -1
  42. package/dist/src/storage/kysely/keyframe-store.d.ts +15 -0
  43. package/dist/src/storage/kysely/keyframe-store.d.ts.map +1 -0
  44. package/dist/src/storage/kysely/keyframe-store.js +71 -0
  45. package/dist/src/storage/kysely/keyframe-store.js.map +1 -0
  46. package/dist/src/storage/kysely/store.d.ts.map +1 -1
  47. package/dist/src/storage/kysely/store.js +0 -1
  48. package/dist/src/storage/kysely/store.js.map +1 -1
  49. package/dist/src/storage/kysely/types.d.ts +14 -1
  50. package/dist/src/storage/kysely/types.d.ts.map +1 -1
  51. package/dist/src/storage/txn.d.ts.map +1 -1
  52. package/dist/src/storage/txn.js +0 -1
  53. package/dist/src/storage/txn.js.map +1 -1
  54. package/package.json +3 -3
@@ -0,0 +1,37 @@
1
+ /**
2
+ * RingBuffer is a generic circular buffer implementation that stores a fixed number
3
+ * of items. When the buffer is full, new items overwrite the oldest items.
4
+ *
5
+ * This implementation maintains O(1) time complexity for push operations and provides
6
+ * items in chronological order (oldest to newest) via getAll().
7
+ *
8
+ * @template T - The type of items stored in the buffer
9
+ */
10
+ export declare class RingBuffer<T> {
11
+ private buffer;
12
+ private head;
13
+ private size;
14
+ private capacity;
15
+ constructor(capacity: number);
16
+ /**
17
+ * Adds an item to the buffer. If the buffer is full, overwrites the oldest item.
18
+ *
19
+ * @param item - The item to add
20
+ */
21
+ push(item: T): void;
22
+ /**
23
+ * Returns all items in the buffer in chronological order (oldest to newest).
24
+ *
25
+ * @returns Array of items in insertion order
26
+ */
27
+ getAll(): T[];
28
+ /**
29
+ * Clears all items from the buffer.
30
+ */
31
+ clear(): void;
32
+ /**
33
+ * Gets the current number of items in the buffer.
34
+ */
35
+ get length(): number;
36
+ }
37
+ //# sourceMappingURL=ring-buffer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ring-buffer.d.ts","sourceRoot":"","sources":["../../../../src/cache/buffer/ring-buffer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,qBAAa,UAAU,CAAC,CAAC;IACvB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,QAAQ,CAAS;gBAEb,QAAQ,EAAE,MAAM;IAQ5B;;;;OAIG;IACH,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI;IAYnB;;;;OAIG;IACH,MAAM,IAAI,CAAC,EAAE;IAab;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;CACF"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * RingBuffer is a generic circular buffer implementation that stores a fixed number
3
+ * of items. When the buffer is full, new items overwrite the oldest items.
4
+ *
5
+ * This implementation maintains O(1) time complexity for push operations and provides
6
+ * items in chronological order (oldest to newest) via getAll().
7
+ *
8
+ * @template T - The type of items stored in the buffer
9
+ */
10
+ export class RingBuffer {
11
+ buffer;
12
+ head = 0;
13
+ size = 0;
14
+ capacity;
15
+ constructor(capacity) {
16
+ if (capacity <= 0) {
17
+ throw new Error("Ring buffer capacity must be greater than 0");
18
+ }
19
+ this.capacity = capacity;
20
+ this.buffer = new Array(capacity);
21
+ }
22
+ /**
23
+ * Adds an item to the buffer. If the buffer is full, overwrites the oldest item.
24
+ *
25
+ * @param item - The item to add
26
+ */
27
+ push(item) {
28
+ const index = (this.head + this.size) % this.capacity;
29
+ if (this.size < this.capacity) {
30
+ this.buffer[index] = item;
31
+ this.size++;
32
+ }
33
+ else {
34
+ this.buffer[this.head] = item;
35
+ this.head = (this.head + 1) % this.capacity;
36
+ }
37
+ }
38
+ /**
39
+ * Returns all items in the buffer in chronological order (oldest to newest).
40
+ *
41
+ * @returns Array of items in insertion order
42
+ */
43
+ getAll() {
44
+ if (this.size === 0) {
45
+ return [];
46
+ }
47
+ const result = [];
48
+ for (let i = 0; i < this.size; i++) {
49
+ const index = (this.head + i) % this.capacity;
50
+ result.push(this.buffer[index]);
51
+ }
52
+ return result;
53
+ }
54
+ /**
55
+ * Clears all items from the buffer.
56
+ */
57
+ clear() {
58
+ this.buffer = new Array(this.capacity);
59
+ this.head = 0;
60
+ this.size = 0;
61
+ }
62
+ /**
63
+ * Gets the current number of items in the buffer.
64
+ */
65
+ get length() {
66
+ return this.size;
67
+ }
68
+ }
69
+ //# sourceMappingURL=ring-buffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ring-buffer.js","sourceRoot":"","sources":["../../../../src/cache/buffer/ring-buffer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,OAAO,UAAU;IACb,MAAM,CAAM;IACZ,IAAI,GAAW,CAAC,CAAC;IACjB,IAAI,GAAW,CAAC,CAAC;IACjB,QAAQ,CAAS;IAEzB,YAAY,QAAgB;QAC1B,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,IAAI,KAAK,CAAI,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED;;;;OAIG;IACH,IAAI,CAAC,IAAO;QACV,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;QAEtD,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;YAC1B,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;YAC9B,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9C,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,MAAM;QACJ,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACpB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,MAAM,GAAQ,EAAE,CAAC;QACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC9C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,IAAI,KAAK,CAAI,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QACd,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,99 @@
1
+ import type { PHDocument } from "document-model";
2
+ import type { IDocumentModelRegistry } from "../registry/interfaces.js";
3
+ import type { IKeyframeStore, IOperationStore } from "../storage/interfaces.js";
4
+ import { RingBuffer } from "./buffer/ring-buffer.js";
5
+ import type { CachedSnapshot, WriteCacheConfig } from "./types.js";
6
+ import type { IWriteCache } from "./write/interfaces.js";
7
+ type DocumentStream = {
8
+ key: string;
9
+ ringBuffer: RingBuffer<CachedSnapshot>;
10
+ };
11
+ export declare class KyselyWriteCache implements IWriteCache {
12
+ private streams;
13
+ private lruTracker;
14
+ private keyframeStore;
15
+ private operationStore;
16
+ private registry;
17
+ private config;
18
+ constructor(keyframeStore: IKeyframeStore, operationStore: IOperationStore, registry: IDocumentModelRegistry, config: WriteCacheConfig);
19
+ /**
20
+ * Initializes the write cache.
21
+ * Currently a no-op as keyframe store lifecycle is managed externally.
22
+ */
23
+ startup(): Promise<void>;
24
+ /**
25
+ * Shuts down the write cache.
26
+ * Currently a no-op as keyframe store lifecycle is managed externally.
27
+ */
28
+ shutdown(): Promise<void>;
29
+ /**
30
+ * Retrieves document state at a specific revision from cache or rebuilds it.
31
+ *
32
+ * Cache hit path: Returns cached snapshot if available (O(1))
33
+ * Warm miss path: Rebuilds from cached base revision + incremental ops
34
+ * Cold miss path: Rebuilds from keyframe or from scratch using all operations
35
+ *
36
+ * @param documentId - The document identifier
37
+ * @param documentType - The document type for reducer lookup
38
+ * @param scope - The operation scope
39
+ * @param branch - The operation branch
40
+ * @param targetRevision - The target revision, or undefined for newest
41
+ * @param signal - Optional abort signal to cancel the operation
42
+ * @returns The document at the target revision
43
+ * @throws {Error} "Operation aborted" if signal is aborted
44
+ * @throws {ModuleNotFoundError} If document type not registered in registry
45
+ * @throws {Error} "Failed to rebuild document" if operation store fails
46
+ * @throws {Error} If reducer throws during operation application
47
+ * @throws {Error} If document serialization (structuredClone) fails
48
+ */
49
+ getState(documentId: string, documentType: string, scope: string, branch: string, targetRevision?: number, signal?: AbortSignal): Promise<PHDocument>;
50
+ /**
51
+ * Stores a document snapshot in the cache at a specific revision.
52
+ *
53
+ * The document is deep-copied to prevent external mutations.
54
+ * Updates LRU tracker and may evict least recently used stream if at capacity.
55
+ * Asynchronously persists keyframes at configured intervals (fire-and-forget).
56
+ *
57
+ * @param documentId - The document identifier
58
+ * @param documentType - The document type
59
+ * @param scope - The operation scope
60
+ * @param branch - The operation branch
61
+ * @param revision - The revision number
62
+ * @param document - The document to cache
63
+ * @throws {Error} If document serialization (structuredClone) fails
64
+ */
65
+ putState(documentId: string, documentType: string, scope: string, branch: string, revision: number, document: PHDocument): void;
66
+ /**
67
+ * Invalidates cached document streams.
68
+ *
69
+ * Supports three invalidation scopes:
70
+ * - Document-level: invalidate(documentId) - removes all streams for document
71
+ * - Scope-level: invalidate(documentId, scope) - removes all branches for scope
72
+ * - Stream-level: invalidate(documentId, scope, branch) - removes specific stream
73
+ *
74
+ * @param documentId - The document identifier
75
+ * @param scope - Optional scope to narrow invalidation
76
+ * @param branch - Optional branch to narrow invalidation (requires scope)
77
+ * @returns The number of streams evicted
78
+ */
79
+ invalidate(documentId: string, scope?: string, branch?: string): number;
80
+ /**
81
+ * Clears the entire cache, removing all cached document streams.
82
+ * Resets LRU tracking state. This operation always succeeds.
83
+ */
84
+ clear(): void;
85
+ /**
86
+ * Retrieves a specific stream for a document. Exposed on the implementation
87
+ * for testing, but not on the interface.
88
+ */
89
+ getStream(documentId: string, scope: string, branch: string): DocumentStream | undefined;
90
+ private findNearestKeyframe;
91
+ private coldMissRebuild;
92
+ private warmMissRebuild;
93
+ private findNearestOlderSnapshot;
94
+ private makeStreamKey;
95
+ private getOrCreateStream;
96
+ private isKeyframeRevision;
97
+ }
98
+ export {};
99
+ //# sourceMappingURL=kysely-write-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kysely-write-cache.d.ts","sourceRoot":"","sources":["../../../src/cache/kysely-write-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD,KAAK,cAAc,GAAG;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC;CACxC,CAAC;AAEF,qBAAa,gBAAiB,YAAW,WAAW;IAClD,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,aAAa,CAAiB;IACtC,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,MAAM,CAA6B;gBAGzC,aAAa,EAAE,cAAc,EAC7B,cAAc,EAAE,eAAe,EAC/B,QAAQ,EAAE,sBAAsB,EAChC,MAAM,EAAE,gBAAgB;IAc1B;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;;OAGG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B;;;;;;;;;;;;;;;;;;;OAmBG;IACG,QAAQ,CACZ,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,cAAc,CAAC,EAAE,MAAM,EACvB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC;IA0EtB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CACN,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,UAAU,GACnB,IAAI;IAiCP;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM;IA+BvE;;;OAGG;IACH,KAAK,IAAI,IAAI;IAKb;;;OAGG;IACH,SAAS,CACP,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,cAAc,GAAG,SAAS;YAKf,mBAAmB;YAoBnB,eAAe;YA6Ff,eAAe;IAqD7B,OAAO,CAAC,wBAAwB;IAiBhC,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,kBAAkB;CAG3B"}
@@ -0,0 +1,309 @@
1
+ import { RingBuffer } from "./buffer/ring-buffer.js";
2
+ import { LRUTracker } from "./lru/lru-tracker.js";
3
+ export class KyselyWriteCache {
4
+ streams;
5
+ lruTracker;
6
+ keyframeStore;
7
+ operationStore;
8
+ registry;
9
+ config;
10
+ constructor(keyframeStore, operationStore, registry, config) {
11
+ this.keyframeStore = keyframeStore;
12
+ this.operationStore = operationStore;
13
+ this.registry = registry;
14
+ this.config = {
15
+ maxDocuments: config.maxDocuments,
16
+ ringBufferSize: config.ringBufferSize,
17
+ keyframeInterval: config.keyframeInterval,
18
+ };
19
+ this.streams = new Map();
20
+ this.lruTracker = new LRUTracker();
21
+ }
22
+ /**
23
+ * Initializes the write cache.
24
+ * Currently a no-op as keyframe store lifecycle is managed externally.
25
+ */
26
+ async startup() {
27
+ return Promise.resolve();
28
+ }
29
+ /**
30
+ * Shuts down the write cache.
31
+ * Currently a no-op as keyframe store lifecycle is managed externally.
32
+ */
33
+ async shutdown() {
34
+ return Promise.resolve();
35
+ }
36
+ /**
37
+ * Retrieves document state at a specific revision from cache or rebuilds it.
38
+ *
39
+ * Cache hit path: Returns cached snapshot if available (O(1))
40
+ * Warm miss path: Rebuilds from cached base revision + incremental ops
41
+ * Cold miss path: Rebuilds from keyframe or from scratch using all operations
42
+ *
43
+ * @param documentId - The document identifier
44
+ * @param documentType - The document type for reducer lookup
45
+ * @param scope - The operation scope
46
+ * @param branch - The operation branch
47
+ * @param targetRevision - The target revision, or undefined for newest
48
+ * @param signal - Optional abort signal to cancel the operation
49
+ * @returns The document at the target revision
50
+ * @throws {Error} "Operation aborted" if signal is aborted
51
+ * @throws {ModuleNotFoundError} If document type not registered in registry
52
+ * @throws {Error} "Failed to rebuild document" if operation store fails
53
+ * @throws {Error} If reducer throws during operation application
54
+ * @throws {Error} If document serialization (structuredClone) fails
55
+ */
56
+ async getState(documentId, documentType, scope, branch, targetRevision, signal) {
57
+ if (signal?.aborted) {
58
+ throw new Error("Operation aborted");
59
+ }
60
+ const streamKey = this.makeStreamKey(documentId, scope, branch);
61
+ const stream = this.streams.get(streamKey);
62
+ if (stream) {
63
+ const snapshots = stream.ringBuffer.getAll();
64
+ if (targetRevision === undefined) {
65
+ if (snapshots.length > 0) {
66
+ const newest = snapshots[snapshots.length - 1];
67
+ this.lruTracker.touch(streamKey);
68
+ return structuredClone(newest.document);
69
+ }
70
+ }
71
+ else {
72
+ const exactMatch = snapshots.find((s) => s.revision === targetRevision);
73
+ if (exactMatch) {
74
+ this.lruTracker.touch(streamKey);
75
+ return structuredClone(exactMatch.document);
76
+ }
77
+ const newestOlder = this.findNearestOlderSnapshot(snapshots, targetRevision);
78
+ if (newestOlder) {
79
+ const document = await this.warmMissRebuild(newestOlder.document, newestOlder.revision, documentId, documentType, scope, branch, targetRevision, signal);
80
+ this.putState(documentId, documentType, scope, branch, targetRevision, document);
81
+ this.lruTracker.touch(streamKey);
82
+ return structuredClone(document);
83
+ }
84
+ }
85
+ }
86
+ const document = await this.coldMissRebuild(documentId, documentType, scope, branch, targetRevision, signal);
87
+ let revision = targetRevision;
88
+ if (revision === undefined) {
89
+ revision = document.header.revision[scope] || 0;
90
+ }
91
+ this.putState(documentId, documentType, scope, branch, revision, document);
92
+ return structuredClone(document);
93
+ }
94
+ /**
95
+ * Stores a document snapshot in the cache at a specific revision.
96
+ *
97
+ * The document is deep-copied to prevent external mutations.
98
+ * Updates LRU tracker and may evict least recently used stream if at capacity.
99
+ * Asynchronously persists keyframes at configured intervals (fire-and-forget).
100
+ *
101
+ * @param documentId - The document identifier
102
+ * @param documentType - The document type
103
+ * @param scope - The operation scope
104
+ * @param branch - The operation branch
105
+ * @param revision - The revision number
106
+ * @param document - The document to cache
107
+ * @throws {Error} If document serialization (structuredClone) fails
108
+ */
109
+ putState(documentId, documentType, scope, branch, revision, document) {
110
+ const safeCopy = structuredClone(document);
111
+ const streamKey = this.makeStreamKey(documentId, scope, branch);
112
+ const stream = this.getOrCreateStream(streamKey);
113
+ const snapshot = {
114
+ revision,
115
+ document: safeCopy,
116
+ };
117
+ stream.ringBuffer.push(snapshot);
118
+ if (this.isKeyframeRevision(revision)) {
119
+ // Fire-and-forget keyframe persistence: errors are logged but don't fail putState
120
+ // This allows in-memory cache to continue working even if keyframe storage fails
121
+ this.keyframeStore
122
+ .putKeyframe(documentId, documentType, scope, branch, revision, safeCopy)
123
+ .catch((err) => {
124
+ console.error(`Failed to persist keyframe ${documentId}@${revision}:`, err);
125
+ });
126
+ }
127
+ }
128
+ /**
129
+ * Invalidates cached document streams.
130
+ *
131
+ * Supports three invalidation scopes:
132
+ * - Document-level: invalidate(documentId) - removes all streams for document
133
+ * - Scope-level: invalidate(documentId, scope) - removes all branches for scope
134
+ * - Stream-level: invalidate(documentId, scope, branch) - removes specific stream
135
+ *
136
+ * @param documentId - The document identifier
137
+ * @param scope - Optional scope to narrow invalidation
138
+ * @param branch - Optional branch to narrow invalidation (requires scope)
139
+ * @returns The number of streams evicted
140
+ */
141
+ invalidate(documentId, scope, branch) {
142
+ let evicted = 0;
143
+ if (scope === undefined && branch === undefined) {
144
+ for (const [key] of this.streams.entries()) {
145
+ if (key.startsWith(`${documentId}:`)) {
146
+ this.streams.delete(key);
147
+ this.lruTracker.remove(key);
148
+ evicted++;
149
+ }
150
+ }
151
+ }
152
+ else if (scope !== undefined && branch === undefined) {
153
+ for (const [key] of this.streams.entries()) {
154
+ if (key.startsWith(`${documentId}:${scope}:`)) {
155
+ this.streams.delete(key);
156
+ this.lruTracker.remove(key);
157
+ evicted++;
158
+ }
159
+ }
160
+ }
161
+ else if (scope !== undefined && branch !== undefined) {
162
+ const key = this.makeStreamKey(documentId, scope, branch);
163
+ if (this.streams.has(key)) {
164
+ this.streams.delete(key);
165
+ this.lruTracker.remove(key);
166
+ evicted = 1;
167
+ }
168
+ }
169
+ return evicted;
170
+ }
171
+ /**
172
+ * Clears the entire cache, removing all cached document streams.
173
+ * Resets LRU tracking state. This operation always succeeds.
174
+ */
175
+ clear() {
176
+ this.streams.clear();
177
+ this.lruTracker.clear();
178
+ }
179
+ /**
180
+ * Retrieves a specific stream for a document. Exposed on the implementation
181
+ * for testing, but not on the interface.
182
+ */
183
+ getStream(documentId, scope, branch) {
184
+ const key = this.makeStreamKey(documentId, scope, branch);
185
+ return this.streams.get(key);
186
+ }
187
+ async findNearestKeyframe(documentId, scope, branch, targetRevision, signal) {
188
+ if (targetRevision === Number.MAX_SAFE_INTEGER || targetRevision <= 0) {
189
+ return undefined;
190
+ }
191
+ return this.keyframeStore.findNearestKeyframe(documentId, scope, branch, targetRevision, signal);
192
+ }
193
+ async coldMissRebuild(documentId, documentType, scope, branch, targetRevision, signal) {
194
+ const effectiveTargetRevision = targetRevision || Number.MAX_SAFE_INTEGER;
195
+ const keyframe = await this.findNearestKeyframe(documentId, scope, branch, effectiveTargetRevision, signal);
196
+ let document;
197
+ let startRevision;
198
+ if (keyframe) {
199
+ document = structuredClone(keyframe.document);
200
+ startRevision = keyframe.revision;
201
+ }
202
+ else {
203
+ document = undefined;
204
+ startRevision = 0;
205
+ }
206
+ // Throws ModuleNotFoundError if document type not registered
207
+ const module = this.registry.getModule(documentType);
208
+ let cursor = undefined;
209
+ const pageSize = 100;
210
+ let hasMorePages;
211
+ do {
212
+ if (signal?.aborted) {
213
+ throw new Error("Operation aborted");
214
+ }
215
+ const paging = cursor ? { cursor, pageSize } : { pageSize };
216
+ try {
217
+ const result = await this.operationStore.getSince(documentId, scope, branch, startRevision, paging, signal);
218
+ for (const operation of result.items) {
219
+ if (targetRevision !== undefined &&
220
+ operation.index > targetRevision) {
221
+ break;
222
+ }
223
+ if (document === undefined) {
224
+ document = module.utils.createDocument();
225
+ }
226
+ // Fail-fast: if reducer throws, error propagates immediately without caching partial state
227
+ document = module.reducer(document, operation.action);
228
+ }
229
+ const reachedTarget = targetRevision !== undefined &&
230
+ result.items.some((op) => op.index >= targetRevision);
231
+ hasMorePages = Boolean(result.nextCursor) && !reachedTarget;
232
+ if (hasMorePages) {
233
+ cursor = result.nextCursor;
234
+ }
235
+ }
236
+ catch (err) {
237
+ // Wrap errors with context to include document ID for debugging
238
+ throw new Error(`Failed to rebuild document ${documentId}: ${err instanceof Error ? err.message : String(err)}`);
239
+ }
240
+ } while (hasMorePages);
241
+ if (!document) {
242
+ throw new Error(`Failed to rebuild document ${documentId}: no operations found`);
243
+ }
244
+ return document;
245
+ }
246
+ async warmMissRebuild(baseDocument, baseRevision, documentId, documentType, scope, branch, targetRevision, signal) {
247
+ // Throws ModuleNotFoundError if document type not registered
248
+ const module = this.registry.getModule(documentType);
249
+ let document = structuredClone(baseDocument);
250
+ try {
251
+ const pagedResults = await this.operationStore.getSince(documentId, scope, branch, baseRevision, undefined, signal);
252
+ for (const operation of pagedResults.items) {
253
+ if (signal?.aborted) {
254
+ throw new Error("Operation aborted");
255
+ }
256
+ if (targetRevision !== undefined && operation.index > targetRevision) {
257
+ break;
258
+ }
259
+ // Fail-fast: if reducer throws, error propagates immediately without caching partial state
260
+ document = module.reducer(document, operation.action);
261
+ if (targetRevision !== undefined &&
262
+ operation.index === targetRevision) {
263
+ break;
264
+ }
265
+ }
266
+ }
267
+ catch (err) {
268
+ // Wrap errors with context to include document ID for debugging
269
+ throw new Error(`Failed to rebuild document ${documentId}: ${err instanceof Error ? err.message : String(err)}`);
270
+ }
271
+ return document;
272
+ }
273
+ findNearestOlderSnapshot(snapshots, targetRevision) {
274
+ let nearest = undefined;
275
+ for (const snapshot of snapshots) {
276
+ if (snapshot.revision < targetRevision) {
277
+ if (!nearest || snapshot.revision > nearest.revision) {
278
+ nearest = snapshot;
279
+ }
280
+ }
281
+ }
282
+ return nearest;
283
+ }
284
+ makeStreamKey(documentId, scope, branch) {
285
+ return `${documentId}:${scope}:${branch}`;
286
+ }
287
+ getOrCreateStream(key) {
288
+ let stream = this.streams.get(key);
289
+ if (!stream) {
290
+ if (this.streams.size >= this.config.maxDocuments) {
291
+ const evictKey = this.lruTracker.evict();
292
+ if (evictKey) {
293
+ this.streams.delete(evictKey);
294
+ }
295
+ }
296
+ stream = {
297
+ key,
298
+ ringBuffer: new RingBuffer(this.config.ringBufferSize),
299
+ };
300
+ this.streams.set(key, stream);
301
+ }
302
+ this.lruTracker.touch(key);
303
+ return stream;
304
+ }
305
+ isKeyframeRevision(revision) {
306
+ return revision > 0 && revision % this.config.keyframeInterval === 0;
307
+ }
308
+ }
309
+ //# sourceMappingURL=kysely-write-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kysely-write-cache.js","sourceRoot":"","sources":["../../../src/cache/kysely-write-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export declare class LRUTracker<K> {
2
+ private map;
3
+ private head;
4
+ private tail;
5
+ constructor();
6
+ get size(): number;
7
+ touch(key: K): void;
8
+ evict(): K | undefined;
9
+ remove(key: K): void;
10
+ clear(): void;
11
+ private addToFront;
12
+ private moveToFront;
13
+ private removeNode;
14
+ }
15
+ //# sourceMappingURL=lru-tracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lru-tracker.d.ts","sourceRoot":"","sources":["../../../../src/cache/lru/lru-tracker.ts"],"names":[],"mappings":"AAYA,qBAAa,UAAU,CAAC,CAAC;IACvB,OAAO,CAAC,GAAG,CAAqB;IAChC,OAAO,CAAC,IAAI,CAAyB;IACrC,OAAO,CAAC,IAAI,CAAyB;;IAQrC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI;IAUnB,KAAK,IAAI,CAAC,GAAG,SAAS;IAUtB,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI;IAUpB,KAAK,IAAI,IAAI;IAMb,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,UAAU;CAanB"}
@@ -0,0 +1,96 @@
1
+ class LRUNode {
2
+ key;
3
+ prev;
4
+ next;
5
+ constructor(key) {
6
+ this.key = key;
7
+ this.prev = undefined;
8
+ this.next = undefined;
9
+ }
10
+ }
11
+ export class LRUTracker {
12
+ map;
13
+ head;
14
+ tail;
15
+ constructor() {
16
+ this.map = new Map();
17
+ this.head = undefined;
18
+ this.tail = undefined;
19
+ }
20
+ get size() {
21
+ return this.map.size;
22
+ }
23
+ touch(key) {
24
+ const node = this.map.get(key);
25
+ if (node) {
26
+ this.moveToFront(node);
27
+ }
28
+ else {
29
+ this.addToFront(key);
30
+ }
31
+ }
32
+ evict() {
33
+ if (!this.tail) {
34
+ return undefined;
35
+ }
36
+ const key = this.tail.key;
37
+ this.remove(key);
38
+ return key;
39
+ }
40
+ remove(key) {
41
+ const node = this.map.get(key);
42
+ if (!node) {
43
+ return;
44
+ }
45
+ this.removeNode(node);
46
+ this.map.delete(key);
47
+ }
48
+ clear() {
49
+ this.map.clear();
50
+ this.head = undefined;
51
+ this.tail = undefined;
52
+ }
53
+ addToFront(key) {
54
+ const node = new LRUNode(key);
55
+ this.map.set(key, node);
56
+ if (!this.head) {
57
+ this.head = node;
58
+ this.tail = node;
59
+ }
60
+ else {
61
+ node.next = this.head;
62
+ this.head.prev = node;
63
+ this.head = node;
64
+ }
65
+ }
66
+ moveToFront(node) {
67
+ if (node === this.head) {
68
+ return;
69
+ }
70
+ this.removeNode(node);
71
+ node.prev = undefined;
72
+ node.next = this.head;
73
+ if (this.head) {
74
+ this.head.prev = node;
75
+ }
76
+ this.head = node;
77
+ if (!this.tail) {
78
+ this.tail = node;
79
+ }
80
+ }
81
+ removeNode(node) {
82
+ if (node.prev) {
83
+ node.prev.next = node.next;
84
+ }
85
+ else {
86
+ this.head = node.next;
87
+ }
88
+ if (node.next) {
89
+ node.next.prev = node.prev;
90
+ }
91
+ else {
92
+ this.tail = node.prev;
93
+ }
94
+ }
95
+ }
96
+ //# sourceMappingURL=lru-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lru-tracker.js","sourceRoot":"","sources":["../../../../src/cache/lru/lru-tracker.ts"],"names":[],"mappings":"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"}