@iworkforces/tracelattice 2.1.1 → 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 +20 -3
- package/dist/contracts/PersistenceBackend.d.ts +10 -1
- package/dist/contracts/PersistenceBackend.d.ts.map +1 -1
- package/dist/contracts/calibrator.d.ts +9 -6
- package/dist/contracts/calibrator.d.ts.map +1 -1
- package/dist/contracts/interfaces.d.ts +6 -0
- package/dist/contracts/interfaces.d.ts.map +1 -1
- package/dist/contracts/persistence-work.d.ts +13 -1
- package/dist/contracts/persistence-work.d.ts.map +1 -1
- package/dist/contracts/strategy.d.ts +14 -9
- package/dist/contracts/strategy.d.ts.map +1 -1
- package/dist/core/DurableThoughtIdentityIndex.d.ts +35 -0
- package/dist/core/DurableThoughtIdentityIndex.d.ts.map +1 -0
- package/dist/core/DurableThoughtIdentityIndex.js +94 -0
- package/dist/core/DurableThoughtIdentityIndex.js.map +1 -0
- package/dist/core/HistoryManager.d.ts +5 -0
- package/dist/core/HistoryManager.d.ts.map +1 -1
- package/dist/core/HistoryManager.js +37 -4
- package/dist/core/HistoryManager.js.map +1 -1
- package/dist/core/IHistoryManager.d.ts +2 -0
- package/dist/core/IHistoryManager.d.ts.map +1 -1
- package/dist/core/PersistenceBuffer.d.ts +7 -1
- package/dist/core/PersistenceBuffer.d.ts.map +1 -1
- package/dist/core/PersistenceBuffer.js +13 -1
- package/dist/core/PersistenceBuffer.js.map +1 -1
- package/dist/core/PersistenceRestore.d.ts.map +1 -1
- package/dist/core/PersistenceRestore.js +11 -3
- package/dist/core/PersistenceRestore.js.map +1 -1
- package/dist/core/PersistenceWorkQueue.d.ts +13 -1
- package/dist/core/PersistenceWorkQueue.d.ts.map +1 -1
- package/dist/core/PersistenceWorkQueue.js +86 -11
- package/dist/core/PersistenceWorkQueue.js.map +1 -1
- package/dist/core/PersistenceWriter.d.ts.map +1 -1
- package/dist/core/PersistenceWriter.js +3 -0
- package/dist/core/PersistenceWriter.js.map +1 -1
- package/dist/core/ThoughtEvaluator.d.ts +7 -4
- package/dist/core/ThoughtEvaluator.d.ts.map +1 -1
- package/dist/core/ThoughtEvaluator.js +15 -7
- package/dist/core/ThoughtEvaluator.js.map +1 -1
- package/dist/core/ThoughtProcessor.d.ts.map +1 -1
- package/dist/core/ThoughtProcessor.js +19 -7
- package/dist/core/ThoughtProcessor.js.map +1 -1
- package/dist/core/ThoughtReferenceIndex.d.ts +4 -0
- package/dist/core/ThoughtReferenceIndex.d.ts.map +1 -1
- package/dist/core/ThoughtReferenceIndex.js +28 -1
- package/dist/core/ThoughtReferenceIndex.js.map +1 -1
- package/dist/core/VerificationOutcomeAdmission.d.ts +8 -1
- package/dist/core/VerificationOutcomeAdmission.d.ts.map +1 -1
- package/dist/core/VerificationOutcomeAdmission.js.map +1 -1
- package/dist/core/VerificationTargetRestore.d.ts +5 -0
- package/dist/core/VerificationTargetRestore.d.ts.map +1 -0
- package/dist/core/VerificationTargetRestore.js +61 -0
- package/dist/core/VerificationTargetRestore.js.map +1 -0
- package/dist/core/evaluator/Aggregator.d.ts +2 -1
- package/dist/core/evaluator/Aggregator.d.ts.map +1 -1
- package/dist/core/evaluator/Aggregator.js +4 -12
- package/dist/core/evaluator/Aggregator.js.map +1 -1
- package/dist/core/evaluator/Calibrator.d.ts +13 -9
- package/dist/core/evaluator/Calibrator.d.ts.map +1 -1
- package/dist/core/evaluator/Calibrator.js +25 -11
- package/dist/core/evaluator/Calibrator.js.map +1 -1
- package/dist/core/evaluator/PatternDetector.d.ts +2 -1
- package/dist/core/evaluator/PatternDetector.d.ts.map +1 -1
- package/dist/core/evaluator/PatternDetector.js +13 -12
- package/dist/core/evaluator/PatternDetector.js.map +1 -1
- package/dist/core/evaluator/SignalComputer.d.ts +3 -2
- package/dist/core/evaluator/SignalComputer.d.ts.map +1 -1
- package/dist/core/evaluator/SignalComputer.js +4 -9
- package/dist/core/evaluator/SignalComputer.js.map +1 -1
- package/dist/core/evaluator/VerificationLinks.d.ts +19 -0
- package/dist/core/evaluator/VerificationLinks.d.ts.map +1 -0
- package/dist/core/evaluator/VerificationLinks.js +113 -0
- package/dist/core/evaluator/VerificationLinks.js.map +1 -0
- package/dist/core/evaluator/calibration-math.d.ts +12 -2
- package/dist/core/evaluator/calibration-math.d.ts.map +1 -1
- package/dist/core/evaluator/calibration-math.js +11 -2
- package/dist/core/evaluator/calibration-math.js.map +1 -1
- package/dist/core/graph/EdgeEmitter.d.ts.map +1 -1
- package/dist/core/graph/EdgeEmitter.js +2 -1
- package/dist/core/graph/EdgeEmitter.js.map +1 -1
- package/dist/core/graph/GraphView.d.ts +5 -5
- package/dist/core/graph/GraphView.d.ts.map +1 -1
- package/dist/core/graph/GraphView.js +11 -11
- package/dist/core/graph/GraphView.js.map +1 -1
- package/dist/core/reasoning/ActiveEvidenceProjection.d.ts +20 -0
- package/dist/core/reasoning/ActiveEvidenceProjection.d.ts.map +1 -0
- package/dist/core/reasoning/ActiveEvidenceProjection.js +100 -0
- package/dist/core/reasoning/ActiveEvidenceProjection.js.map +1 -0
- package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts +5 -4
- package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts.map +1 -1
- package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js +24 -14
- package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js.map +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +3 -1
- package/dist/lib.js.map +1 -1
- package/dist/persistence/BacktrackPersistence.d.ts +13 -0
- package/dist/persistence/BacktrackPersistence.d.ts.map +1 -0
- package/dist/persistence/BacktrackPersistence.js +90 -0
- package/dist/persistence/BacktrackPersistence.js.map +1 -0
- package/dist/persistence/FilePersistence.d.ts +2 -1
- package/dist/persistence/FilePersistence.d.ts.map +1 -1
- package/dist/persistence/FilePersistence.js +28 -0
- package/dist/persistence/FilePersistence.js.map +1 -1
- package/dist/persistence/FileSnapshotV2.d.ts +2 -0
- package/dist/persistence/FileSnapshotV2.d.ts.map +1 -1
- package/dist/persistence/FileSnapshotV2.js +6 -2
- package/dist/persistence/FileSnapshotV2.js.map +1 -1
- package/dist/persistence/MemoryPersistence.d.ts +2 -1
- package/dist/persistence/MemoryPersistence.d.ts.map +1 -1
- package/dist/persistence/MemoryPersistence.js +21 -1
- package/dist/persistence/MemoryPersistence.js.map +1 -1
- package/dist/persistence/PersistenceCodec.d.ts.map +1 -1
- package/dist/persistence/PersistenceCodec.js +5 -0
- package/dist/persistence/PersistenceCodec.js.map +1 -1
- package/dist/persistence/PersistenceErrors.d.ts +1 -1
- package/dist/persistence/PersistenceErrors.d.ts.map +1 -1
- package/dist/persistence/PersistenceErrors.js.map +1 -1
- package/dist/persistence/PersistenceScope.d.ts +1 -1
- package/dist/persistence/PersistenceScope.d.ts.map +1 -1
- package/dist/persistence/PersistenceScope.js.map +1 -1
- package/dist/persistence/SqlitePersistence.d.ts +2 -1
- package/dist/persistence/SqlitePersistence.d.ts.map +1 -1
- package/dist/persistence/SqlitePersistence.js +18 -0
- package/dist/persistence/SqlitePersistence.js.map +1 -1
- package/dist/registry/BaseRegistry.d.ts +10 -2
- package/dist/registry/BaseRegistry.d.ts.map +1 -1
- package/dist/registry/BaseRegistry.js +128 -81
- package/dist/registry/BaseRegistry.js.map +1 -1
- package/package.json +4 -3
|
@@ -6,7 +6,7 @@ export type PersistenceScope = {
|
|
|
6
6
|
readonly branchId?: BranchId;
|
|
7
7
|
};
|
|
8
8
|
/** Operations whose payload ownership is validated before mutation. */
|
|
9
|
-
export type PersistenceWriteOperation = 'saveThought' | 'saveThoughtForSession' | 'saveBranch' | 'saveBranchForSession' | 'saveEdges' | 'saveSummaries';
|
|
9
|
+
export type PersistenceWriteOperation = 'saveThought' | 'saveThoughtForSession' | 'saveBacktrackForSession' | 'saveBranch' | 'saveBranchForSession' | 'saveEdges' | 'saveSummaries';
|
|
10
10
|
/** Error raised before a write whose payload belongs to another namespace. */
|
|
11
11
|
export declare class PersistenceScopeMismatchError extends SequentialThinkingError {
|
|
12
12
|
readonly operation: PersistenceWriteOperation;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PersistenceErrors.d.ts","sourceRoot":"","sources":["../../src/persistence/PersistenceErrors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAe,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEpE,6DAA6D;AAC7D,MAAM,MAAM,gBAAgB,GAAG;IAC9B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CAC7B,CAAC;AAEF,uEAAuE;AACvE,MAAM,MAAM,yBAAyB,GAClC,aAAa,GACb,uBAAuB,GACvB,YAAY,GACZ,sBAAsB,GACtB,WAAW,GACX,eAAe,CAAC;AAEnB,8EAA8E;AAC9E,qBAAa,6BAA8B,SAAQ,uBAAuB;IACzE,SAAgB,SAAS,EAAE,yBAAyB,CAAC;IACrD,SAAgB,aAAa,EAAE,gBAAgB,CAAC;IAChD,SAAgB,YAAY,EAAE,SAAS,gBAAgB,EAAE,CAAC;gBAGzD,SAAS,EAAE,yBAAyB,EACpC,aAAa,EAAE,gBAAgB,EAC/B,YAAY,EAAE,SAAS,gBAAgB,EAAE;CAW1C"}
|
|
1
|
+
{"version":3,"file":"PersistenceErrors.d.ts","sourceRoot":"","sources":["../../src/persistence/PersistenceErrors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAe,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEpE,6DAA6D;AAC7D,MAAM,MAAM,gBAAgB,GAAG;IAC9B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CAC7B,CAAC;AAEF,uEAAuE;AACvE,MAAM,MAAM,yBAAyB,GAClC,aAAa,GACb,uBAAuB,GACvB,yBAAyB,GACzB,YAAY,GACZ,sBAAsB,GACtB,WAAW,GACX,eAAe,CAAC;AAEnB,8EAA8E;AAC9E,qBAAa,6BAA8B,SAAQ,uBAAuB;IACzE,SAAgB,SAAS,EAAE,yBAAyB,CAAC;IACrD,SAAgB,aAAa,EAAE,gBAAgB,CAAC;IAChD,SAAgB,YAAY,EAAE,SAAS,gBAAgB,EAAE,CAAC;gBAGzD,SAAS,EAAE,yBAAyB,EACpC,aAAa,EAAE,gBAAgB,EAC/B,YAAY,EAAE,SAAS,gBAAgB,EAAE;CAW1C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence/PersistenceErrors.js","sources":["../../src/persistence/PersistenceErrors.ts"],"sourcesContent":["import type { BranchId, SessionId } from '../contracts/ids.js';\nimport { ERROR_CODES, SequentialThinkingError } from '../errors.js';\n\n/** Durable namespace associated with a persistence write. */\nexport type PersistenceScope = {\n\treadonly sessionId: SessionId;\n\treadonly branchId?: BranchId;\n};\n\n/** Operations whose payload ownership is validated before mutation. */\nexport type PersistenceWriteOperation =\n\t| 'saveThought'\n\t| 'saveThoughtForSession'\n\t| 'saveBranch'\n\t| 'saveBranchForSession'\n\t| 'saveEdges'\n\t| 'saveSummaries';\n\n/** Error raised before a write whose payload belongs to another namespace. */\nexport class PersistenceScopeMismatchError extends SequentialThinkingError {\n\tpublic readonly operation: PersistenceWriteOperation;\n\tpublic readonly expectedScope: PersistenceScope;\n\tpublic readonly actualScopes: readonly PersistenceScope[];\n\n\tconstructor(\n\t\toperation: PersistenceWriteOperation,\n\t\texpectedScope: PersistenceScope,\n\t\tactualScopes: readonly PersistenceScope[]\n\t) {\n\t\tsuper(\n\t\t\t`Persistence payload scope does not match '${expectedScope.sessionId}' for ${operation}`,\n\t\t\tERROR_CODES.PERSISTENCE_SCOPE_MISMATCH\n\t\t);\n\t\tthis.name = 'PersistenceScopeMismatchError';\n\t\tthis.operation = operation;\n\t\tthis.expectedScope = expectedScope;\n\t\tthis.actualScopes = [...actualScopes];\n\t}\n}\n"],"names":["PersistenceScopeMismatchError","SequentialThinkingError","operation","expectedScope","actualScopes","ERROR_CODES"],"mappings":";
|
|
1
|
+
{"version":3,"file":"persistence/PersistenceErrors.js","sources":["../../src/persistence/PersistenceErrors.ts"],"sourcesContent":["import type { BranchId, SessionId } from '../contracts/ids.js';\nimport { ERROR_CODES, SequentialThinkingError } from '../errors.js';\n\n/** Durable namespace associated with a persistence write. */\nexport type PersistenceScope = {\n\treadonly sessionId: SessionId;\n\treadonly branchId?: BranchId;\n};\n\n/** Operations whose payload ownership is validated before mutation. */\nexport type PersistenceWriteOperation =\n\t| 'saveThought'\n\t| 'saveThoughtForSession'\n\t| 'saveBacktrackForSession'\n\t| 'saveBranch'\n\t| 'saveBranchForSession'\n\t| 'saveEdges'\n\t| 'saveSummaries';\n\n/** Error raised before a write whose payload belongs to another namespace. */\nexport class PersistenceScopeMismatchError extends SequentialThinkingError {\n\tpublic readonly operation: PersistenceWriteOperation;\n\tpublic readonly expectedScope: PersistenceScope;\n\tpublic readonly actualScopes: readonly PersistenceScope[];\n\n\tconstructor(\n\t\toperation: PersistenceWriteOperation,\n\t\texpectedScope: PersistenceScope,\n\t\tactualScopes: readonly PersistenceScope[]\n\t) {\n\t\tsuper(\n\t\t\t`Persistence payload scope does not match '${expectedScope.sessionId}' for ${operation}`,\n\t\t\tERROR_CODES.PERSISTENCE_SCOPE_MISMATCH\n\t\t);\n\t\tthis.name = 'PersistenceScopeMismatchError';\n\t\tthis.operation = operation;\n\t\tthis.expectedScope = expectedScope;\n\t\tthis.actualScopes = [...actualScopes];\n\t}\n}\n"],"names":["PersistenceScopeMismatchError","SequentialThinkingError","operation","expectedScope","actualScopes","ERROR_CODES"],"mappings":";AAoBO,MAAMA,sCAAsCC;IAClC,UAAqC;IACrC,cAAgC;IAChC,aAA0C;IAE1D,YACCC,SAAoC,EACpCC,aAA+B,EAC/BC,YAAyC,CACxC;QACD,KAAK,CACJ,CAAC,0CAA0C,EAAED,cAAc,SAAS,CAAC,MAAM,EAAED,WAAW,EACxFG,YAAY,0BAA0B;QAEvC,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,CAAC,SAAS,GAAGH;QACjB,IAAI,CAAC,aAAa,GAAGC;QACrB,IAAI,CAAC,YAAY,GAAG;eAAIC;SAAa;IACtC;AACD"}
|
|
@@ -23,7 +23,7 @@ export declare function assertPersistableThoughts(thoughts: readonly ThoughtData
|
|
|
23
23
|
* @returns Nothing when all collection identities are compatible.
|
|
24
24
|
*/
|
|
25
25
|
export declare function assertPersistableThoughtCollections(collections: readonly PersistedThoughtCollection[], sourcePath: string): void;
|
|
26
|
-
export declare function assertThoughtScope(operation: Extract<PersistenceWriteOperation, 'saveThought' | 'saveThoughtForSession'>, sessionId: SessionId, thought: ThoughtData): void;
|
|
26
|
+
export declare function assertThoughtScope(operation: Extract<PersistenceWriteOperation, 'saveThought' | 'saveThoughtForSession' | 'saveBacktrackForSession'>, sessionId: SessionId, thought: ThoughtData): void;
|
|
27
27
|
export declare function assertBranchScope(operation: Extract<PersistenceWriteOperation, 'saveBranch' | 'saveBranchForSession'>, sessionId: SessionId, branchId: BranchId, thoughts: readonly ThoughtData[]): void;
|
|
28
28
|
export declare function assertEdgeScopes(sessionId: SessionId, edges: readonly Edge[]): void;
|
|
29
29
|
export declare function assertSummaryScopes(sessionId: SessionId, summaries: readonly Summary[]): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PersistenceScope.d.ts","sourceRoot":"","sources":["../../src/persistence/PersistenceScope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAA2B,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAE7F,OAAO,EAGN,KAAK,yBAAyB,EAC9B,MAAM,wBAAwB,CAAC;AAIhC,KAAK,oBAAoB,GAAG,WAAW,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;CAC1C;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ,CAKpF;AAED,wBAAgB,yBAAyB,CACxC,QAAQ,EAAE,SAAS,WAAW,EAAE,EAChC,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,QAAQ,IAAI,SAAS,oBAAoB,EAAE,CAWrD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mCAAmC,CAClD,WAAW,EAAE,SAAS,0BAA0B,EAAE,EAClD,UAAU,EAAE,MAAM,GAChB,IAAI,CAqBN;AASD,wBAAgB,kBAAkB,CACjC,SAAS,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"PersistenceScope.d.ts","sourceRoot":"","sources":["../../src/persistence/PersistenceScope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAA2B,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAE7F,OAAO,EAGN,KAAK,yBAAyB,EAC9B,MAAM,wBAAwB,CAAC;AAIhC,KAAK,oBAAoB,GAAG,WAAW,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;CAC1C;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ,CAKpF;AAED,wBAAgB,yBAAyB,CACxC,QAAQ,EAAE,SAAS,WAAW,EAAE,EAChC,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,QAAQ,IAAI,SAAS,oBAAoB,EAAE,CAWrD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mCAAmC,CAClD,WAAW,EAAE,SAAS,0BAA0B,EAAE,EAClD,UAAU,EAAE,MAAM,GAChB,IAAI,CAqBN;AASD,wBAAgB,kBAAkB,CACjC,SAAS,EAAE,OAAO,CACjB,yBAAyB,EACzB,aAAa,GAAG,uBAAuB,GAAG,yBAAyB,CACnE,EACD,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,WAAW,GAClB,IAAI,CAKN;AAED,wBAAgB,iBAAiB,CAChC,SAAS,EAAE,OAAO,CAAC,yBAAyB,EAAE,YAAY,GAAG,sBAAsB,CAAC,EACpF,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,SAAS,WAAW,EAAE,GAC9B,IAAI,CAYN;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,IAAI,CAOnF;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI,CAU7F;AAED,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,SAAS;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,EAC3C,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC5B,IAAI,CAWN;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,oBAAoB,CACnC,IAAI,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EACzD,KAAK,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACxD,MAAM,CAER"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence/PersistenceScope.js","sources":["../../src/persistence/PersistenceScope.ts"],"sourcesContent":["import { isDeepStrictEqual } from 'node:util';\nimport type { ThoughtData } from '../core/thought.js';\nimport type { Edge } from '../core/graph/Edge.js';\nimport type { Summary } from '../core/compression/Summary.js';\nimport { asBranchId, asSessionId, type BranchId, type SessionId } from '../contracts/ids.js';\nimport { PersistenceCompatibilityError } from '../errors.js';\nimport {\n\tPersistenceScopeMismatchError,\n\ttype PersistenceScope,\n\ttype PersistenceWriteOperation,\n} from './PersistenceErrors.js';\n\nconst BRANCH_ID_PATTERN = /^[A-Za-z0-9_-]{1,50}$/;\n\ntype PersistedThoughtData = ThoughtData & { readonly id: string };\n\nexport interface PersistedThoughtCollection {\n\treadonly sessionId: SessionId;\n\treadonly thoughts: readonly ThoughtData[];\n}\n\nexport function parsePersistenceBranchId(value: string, sourcePath: string): BranchId {\n\tif (!BRANCH_ID_PATTERN.test(value)) {\n\t\tthrow new PersistenceCompatibilityError(sourcePath, `invalid branch id '${value}'`);\n\t}\n\treturn asBranchId(value);\n}\n\nexport function assertPersistableThoughts(\n\tthoughts: readonly ThoughtData[],\n\tsourcePath: string\n): asserts thoughts is readonly PersistedThoughtData[] {\n\tconst identifiers = new Set<string>();\n\tfor (const thought of thoughts) {\n\t\tif (thought.id === undefined || thought.id.length === 0) {\n\t\t\tthrow new PersistenceCompatibilityError(sourcePath, 'persisted thoughts require an id');\n\t\t}\n\t\tif (identifiers.has(thought.id)) {\n\t\t\tthrow new PersistenceCompatibilityError(sourcePath, `duplicate thought id '${thought.id}'`);\n\t\t}\n\t\tidentifiers.add(thought.id);\n\t}\n}\n\n/**\n * Enforces thought identity across session-tagged persisted arrays.\n *\n * Each array must contain unique IDs. Reuse between arrays is valid only for deeply equal\n * payloads in the same session; equal identifiers in different sessions are independent.\n *\n * @param collections - History and branch arrays tagged with their owning session.\n * @param sourcePath - Persistence location included in compatibility errors.\n * @returns Nothing when all collection identities are compatible.\n */\nexport function assertPersistableThoughtCollections(\n\tcollections: readonly PersistedThoughtCollection[],\n\tsourcePath: string\n): void {\n\tconst payloadsBySession = new Map<SessionId, Map<string, ThoughtData>>();\n\tfor (const collection of collections) {\n\t\tassertPersistableThoughts(collection.thoughts, sourcePath);\n\t\tlet sessionPayloads = payloadsBySession.get(collection.sessionId);\n\t\tif (sessionPayloads === undefined) {\n\t\t\tsessionPayloads = new Map<string, ThoughtData>();\n\t\t\tpayloadsBySession.set(collection.sessionId, sessionPayloads);\n\t\t}\n\t\tfor (const thought of collection.thoughts) {\n\t\t\tconst identifier = thought.id;\n\t\t\tconst existing = sessionPayloads.get(identifier);\n\t\t\tif (existing !== undefined && !isDeepStrictEqual(existing, thought)) {\n\t\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\t\tsourcePath,\n\t\t\t\t\t`conflicting thought id '${identifier}'`\n\t\t\t\t);\n\t\t\t}\n\t\t\tsessionPayloads.set(identifier, thought);\n\t\t}\n\t}\n}\n\nfunction thoughtScope(thought: ThoughtData): PersistenceScope {\n\treturn {\n\t\tsessionId: asSessionId(thought.session_id),\n\t\t...(thought.branch_id === undefined ? {} : { branchId: thought.branch_id }),\n\t};\n}\n\nexport function assertThoughtScope(\n\toperation: Extract
|
|
1
|
+
{"version":3,"file":"persistence/PersistenceScope.js","sources":["../../src/persistence/PersistenceScope.ts"],"sourcesContent":["import { isDeepStrictEqual } from 'node:util';\nimport type { ThoughtData } from '../core/thought.js';\nimport type { Edge } from '../core/graph/Edge.js';\nimport type { Summary } from '../core/compression/Summary.js';\nimport { asBranchId, asSessionId, type BranchId, type SessionId } from '../contracts/ids.js';\nimport { PersistenceCompatibilityError } from '../errors.js';\nimport {\n\tPersistenceScopeMismatchError,\n\ttype PersistenceScope,\n\ttype PersistenceWriteOperation,\n} from './PersistenceErrors.js';\n\nconst BRANCH_ID_PATTERN = /^[A-Za-z0-9_-]{1,50}$/;\n\ntype PersistedThoughtData = ThoughtData & { readonly id: string };\n\nexport interface PersistedThoughtCollection {\n\treadonly sessionId: SessionId;\n\treadonly thoughts: readonly ThoughtData[];\n}\n\nexport function parsePersistenceBranchId(value: string, sourcePath: string): BranchId {\n\tif (!BRANCH_ID_PATTERN.test(value)) {\n\t\tthrow new PersistenceCompatibilityError(sourcePath, `invalid branch id '${value}'`);\n\t}\n\treturn asBranchId(value);\n}\n\nexport function assertPersistableThoughts(\n\tthoughts: readonly ThoughtData[],\n\tsourcePath: string\n): asserts thoughts is readonly PersistedThoughtData[] {\n\tconst identifiers = new Set<string>();\n\tfor (const thought of thoughts) {\n\t\tif (thought.id === undefined || thought.id.length === 0) {\n\t\t\tthrow new PersistenceCompatibilityError(sourcePath, 'persisted thoughts require an id');\n\t\t}\n\t\tif (identifiers.has(thought.id)) {\n\t\t\tthrow new PersistenceCompatibilityError(sourcePath, `duplicate thought id '${thought.id}'`);\n\t\t}\n\t\tidentifiers.add(thought.id);\n\t}\n}\n\n/**\n * Enforces thought identity across session-tagged persisted arrays.\n *\n * Each array must contain unique IDs. Reuse between arrays is valid only for deeply equal\n * payloads in the same session; equal identifiers in different sessions are independent.\n *\n * @param collections - History and branch arrays tagged with their owning session.\n * @param sourcePath - Persistence location included in compatibility errors.\n * @returns Nothing when all collection identities are compatible.\n */\nexport function assertPersistableThoughtCollections(\n\tcollections: readonly PersistedThoughtCollection[],\n\tsourcePath: string\n): void {\n\tconst payloadsBySession = new Map<SessionId, Map<string, ThoughtData>>();\n\tfor (const collection of collections) {\n\t\tassertPersistableThoughts(collection.thoughts, sourcePath);\n\t\tlet sessionPayloads = payloadsBySession.get(collection.sessionId);\n\t\tif (sessionPayloads === undefined) {\n\t\t\tsessionPayloads = new Map<string, ThoughtData>();\n\t\t\tpayloadsBySession.set(collection.sessionId, sessionPayloads);\n\t\t}\n\t\tfor (const thought of collection.thoughts) {\n\t\t\tconst identifier = thought.id;\n\t\t\tconst existing = sessionPayloads.get(identifier);\n\t\t\tif (existing !== undefined && !isDeepStrictEqual(existing, thought)) {\n\t\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\t\tsourcePath,\n\t\t\t\t\t`conflicting thought id '${identifier}'`\n\t\t\t\t);\n\t\t\t}\n\t\t\tsessionPayloads.set(identifier, thought);\n\t\t}\n\t}\n}\n\nfunction thoughtScope(thought: ThoughtData): PersistenceScope {\n\treturn {\n\t\tsessionId: asSessionId(thought.session_id),\n\t\t...(thought.branch_id === undefined ? {} : { branchId: thought.branch_id }),\n\t};\n}\n\nexport function assertThoughtScope(\n\toperation: Extract<\n\t\tPersistenceWriteOperation,\n\t\t'saveThought' | 'saveThoughtForSession' | 'saveBacktrackForSession'\n\t>,\n\tsessionId: SessionId,\n\tthought: ThoughtData\n): void {\n\tconst actualScope = thoughtScope(thought);\n\tif (actualScope.sessionId !== sessionId) {\n\t\tthrow new PersistenceScopeMismatchError(operation, { sessionId }, [actualScope]);\n\t}\n}\n\nexport function assertBranchScope(\n\toperation: Extract<PersistenceWriteOperation, 'saveBranch' | 'saveBranchForSession'>,\n\tsessionId: SessionId,\n\tbranchId: BranchId,\n\tthoughts: readonly ThoughtData[]\n): void {\n\tconst expectedScope = { sessionId, branchId } satisfies PersistenceScope;\n\tconst mismatches = thoughts\n\t\t.map(thoughtScope)\n\t\t.filter(\n\t\t\t(scope) =>\n\t\t\t\tscope.sessionId !== expectedScope.sessionId ||\n\t\t\t\t(scope.branchId !== undefined && scope.branchId !== expectedScope.branchId)\n\t\t);\n\tif (mismatches.length > 0) {\n\t\tthrow new PersistenceScopeMismatchError(operation, expectedScope, mismatches);\n\t}\n}\n\nexport function assertEdgeScopes(sessionId: SessionId, edges: readonly Edge[]): void {\n\tconst actualScopes = edges\n\t\t.filter((edge) => edge.sessionId !== sessionId)\n\t\t.map((edge) => ({ sessionId: edge.sessionId }));\n\tif (actualScopes.length > 0) {\n\t\tthrow new PersistenceScopeMismatchError('saveEdges', { sessionId }, actualScopes);\n\t}\n}\n\nexport function assertSummaryScopes(sessionId: SessionId, summaries: readonly Summary[]): void {\n\tconst actualScopes = summaries\n\t\t.filter((summary) => summary.sessionId !== sessionId)\n\t\t.map((summary) => ({\n\t\t\tsessionId: summary.sessionId,\n\t\t\t...(summary.branchId === undefined ? {} : { branchId: summary.branchId }),\n\t\t}));\n\tif (actualScopes.length > 0) {\n\t\tthrow new PersistenceScopeMismatchError('saveSummaries', { sessionId }, actualScopes);\n\t}\n}\n\nexport function assertUniqueRecordIds(\n\trecords: readonly { readonly id: string }[],\n\tsourcePath: string,\n\trecordKind: 'edge' | 'summary'\n): void {\n\tconst identifiers = new Set<string>();\n\tfor (const record of records) {\n\t\tif (identifiers.has(record.id)) {\n\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\tsourcePath,\n\t\t\t\t`duplicate ${recordKind} id '${record.id}'`\n\t\t\t);\n\t\t}\n\t\tidentifiers.add(record.id);\n\t}\n}\n\nexport function compareCodePoint(left: string, right: string): number {\n\treturn left < right ? -1 : left > right ? 1 : 0;\n}\n\nexport function compareCreatedThenId(\n\tleft: { readonly createdAt: number; readonly id: string },\n\tright: { readonly createdAt: number; readonly id: string }\n): number {\n\treturn left.createdAt - right.createdAt || compareCodePoint(left.id, right.id);\n}\n"],"names":["BRANCH_ID_PATTERN","parsePersistenceBranchId","value","sourcePath","PersistenceCompatibilityError","asBranchId","assertPersistableThoughts","thoughts","identifiers","Set","thought","undefined","assertPersistableThoughtCollections","collections","payloadsBySession","Map","collection","sessionPayloads","identifier","existing","isDeepStrictEqual","thoughtScope","asSessionId","assertThoughtScope","operation","sessionId","actualScope","PersistenceScopeMismatchError","assertBranchScope","branchId","expectedScope","mismatches","scope","assertEdgeScopes","edges","actualScopes","edge","assertSummaryScopes","summaries","summary","assertUniqueRecordIds","records","recordKind","record","compareCodePoint","left","right","compareCreatedThenId"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PersistenceConfig, PersistenceBackend } from '../contracts/PersistenceBackend.js';
|
|
2
|
-
import { type BranchId, type SessionId } from '../contracts/ids.js';
|
|
2
|
+
import { type BranchId, type SessionId, type ThoughtId } from '../contracts/ids.js';
|
|
3
3
|
import type { Summary } from '../core/compression/Summary.js';
|
|
4
4
|
import type { Edge } from '../core/graph/Edge.js';
|
|
5
5
|
import type { ThoughtData } from '../core/thought.js';
|
|
@@ -14,6 +14,7 @@ export declare class SqlitePersistence implements PersistenceBackend {
|
|
|
14
14
|
static createWithDatabase(database: SqliteDatabase, options?: SqliteOptions, sourcePath?: string): SqlitePersistence;
|
|
15
15
|
static create(options?: SqliteOptions): Promise<SqlitePersistence>;
|
|
16
16
|
saveThoughtForSession(sessionId: SessionId, thought: ThoughtData): Promise<void>;
|
|
17
|
+
saveBacktrackForSession(sessionId: SessionId, thought: ThoughtData, targetThoughtId: ThoughtId): Promise<void>;
|
|
17
18
|
private _saveThought;
|
|
18
19
|
loadHistoryForSession(sessionId: SessionId): Promise<ThoughtData[]>;
|
|
19
20
|
saveBranchForSession(sessionId: SessionId, branchId: BranchId, thoughts: readonly ThoughtData[]): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SqlitePersistence.d.ts","sourceRoot":"","sources":["../../src/persistence/SqlitePersistence.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"SqlitePersistence.d.ts","sourceRoot":"","sources":["../../src/persistence/SqlitePersistence.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@ import { runSqliteReadTransaction, runSqliteTransaction } from "./SqliteDriver.j
|
|
|
7
7
|
import { decodeEdgeRow, decodeSummaryRow, decodeThoughtRow, numberField, stringField } from "./SqlitePayload.js";
|
|
8
8
|
import { assertBranchScope, assertEdgeScopes, assertPersistableThoughtCollections, assertPersistableThoughts, assertSummaryScopes, assertThoughtScope, compareCodePoint, parsePersistenceBranchId } from "./PersistenceScope.js";
|
|
9
9
|
import { initializeOrValidateSqliteV2 } from "./SqliteSchemaV2.js";
|
|
10
|
+
import { stageBacktrackPersistence } from "./BacktrackPersistence.js";
|
|
10
11
|
class SqlitePersistence {
|
|
11
12
|
_db;
|
|
12
13
|
_sourcePath;
|
|
@@ -52,6 +53,23 @@ class SqlitePersistence {
|
|
|
52
53
|
assertThoughtScope('saveThoughtForSession', validatedSessionId, thought);
|
|
53
54
|
this._saveThought(validatedSessionId, thought);
|
|
54
55
|
}
|
|
56
|
+
async saveBacktrackForSession(sessionId, thought, targetThoughtId) {
|
|
57
|
+
const validatedSessionId = asSessionId(sessionId);
|
|
58
|
+
runSqliteTransaction(this._db, ()=>{
|
|
59
|
+
const current = this._loadValidatedThoughtState(validatedSessionId);
|
|
60
|
+
const staged = stageBacktrackPersistence(validatedSessionId, current.history, [
|
|
61
|
+
...current.branches
|
|
62
|
+
].map(([branchId, thoughts])=>({
|
|
63
|
+
branchId,
|
|
64
|
+
thoughts
|
|
65
|
+
})), thought, targetThoughtId, this._maxHistorySize);
|
|
66
|
+
this._db.prepare('DELETE FROM thoughts WHERE session_id = ?').run(validatedSessionId);
|
|
67
|
+
const insertThought = this._db.prepare('INSERT INTO thoughts (session_id, data) VALUES (?, ?)');
|
|
68
|
+
for (const retained of staged.history)insertThought.run(validatedSessionId, JSON.stringify(retained));
|
|
69
|
+
const replaceBranch = this._db.prepare('INSERT OR REPLACE INTO branches (session_id, branch_id, data) VALUES (?, ?, ?)');
|
|
70
|
+
for (const branch of staged.branches)replaceBranch.run(validatedSessionId, branch.branchId, JSON.stringify(branch.thoughts));
|
|
71
|
+
});
|
|
72
|
+
}
|
|
55
73
|
_saveThought(sessionId, thought) {
|
|
56
74
|
runSqliteTransaction(this._db, ()=>{
|
|
57
75
|
this._loadValidatedThoughtState(sessionId, [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence/SqlitePersistence.js","sources":["../../src/persistence/SqlitePersistence.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type {\n\tPersistenceConfig,\n\tPersistenceBackend,\n} from '../contracts/PersistenceBackend.js';\nimport { asSessionId, type BranchId, type SessionId } from '../contracts/ids.js';\nimport type { Summary } from '../core/compression/Summary.js';\nimport type { Edge } from '../core/graph/Edge.js';\nimport type { ThoughtData } from '../core/thought.js';\nimport { PersistenceCompatibilityError, PersistenceCorruptionError } from '../errors.js';\nimport type { SqliteDatabase, SqliteDatabaseConstructor } from './SqliteDriver.js';\nimport { runSqliteReadTransaction, runSqliteTransaction } from './SqliteDriver.js';\nimport {\n\tdecodeEdgeRow,\n\tdecodeSummaryRow,\n\tdecodeThoughtRow,\n\tnumberField,\n\tstringField,\n} from './SqlitePayload.js';\nimport {\n\tassertBranchScope,\n\tassertEdgeScopes,\n\tassertPersistableThoughtCollections,\n\tassertPersistableThoughts,\n\tassertSummaryScopes,\n\tassertThoughtScope,\n\tcompareCodePoint,\n\tparsePersistenceBranchId,\n\ttype PersistedThoughtCollection,\n} from './PersistenceScope.js';\nimport { initializeOrValidateSqliteV2 } from './SqliteSchemaV2.js';\n\ntype SqliteOptions = PersistenceConfig['options'];\n\ntype ValidatedThoughtState = {\n\treadonly history: ThoughtData[];\n\treadonly branches: ReadonlyMap<BranchId, ThoughtData[]>;\n};\n\nexport class SqlitePersistence implements PersistenceBackend {\n\tprivate constructor(\n\t\tprivate readonly _db: SqliteDatabase,\n\t\tprivate readonly _sourcePath: string,\n\t\tprivate readonly _maxHistorySize: number,\n\t\tprivate readonly _persistBranches: boolean\n\t) {}\n\n\tpublic static createWithDatabase(\n\t\tdatabase: SqliteDatabase,\n\t\toptions: SqliteOptions = {},\n\t\tsourcePath = '<structural-sqlite>'\n\t): SqlitePersistence {\n\t\tinitializeOrValidateSqliteV2(database, sourcePath);\n\t\treturn new SqlitePersistence(\n\t\t\tdatabase,\n\t\t\tsourcePath,\n\t\t\toptions?.maxHistorySize ?? 10000,\n\t\t\toptions?.persistBranches ?? true\n\t\t);\n\t}\n\n\tpublic static async create(options?: SqliteOptions): Promise<SqlitePersistence> {\n\t\tconst defaultDataDir = existsSync('.claude/data')\n\t\t\t? '.claude/data'\n\t\t\t: join(homedir(), '.claude/data');\n\t\tconst dbPath = options?.dbPath ?? join(defaultDataDir, 'history.db');\n\t\tlet Database: SqliteDatabaseConstructor;\n\t\ttry {\n\t\t\tconst module = await import('better-sqlite3');\n\t\t\tDatabase = module.default;\n\t\t} catch (error) {\n\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\tdbPath,\n\t\t\t\t\"SQLite persistence requires the optional 'better-sqlite3' package\",\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\t\tconst database = new Database(dbPath);\n\t\ttry {\n\t\t\tconst persistence = SqlitePersistence.createWithDatabase(database, options, dbPath);\n\t\t\tif (options?.enableWAL !== false) database.pragma('journal_mode = WAL');\n\t\t\tdatabase.pragma('synchronous = NORMAL');\n\t\t\tdatabase.pragma('foreign_keys = ON');\n\t\t\tdatabase.pragma('busy_timeout = 5000');\n\t\t\tdatabase.pragma('cache_size = -64000');\n\t\t\tdatabase.pragma('temp_store = MEMORY');\n\t\t\treturn persistence;\n\t\t} catch (error) {\n\t\t\tdatabase.close();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic async saveThoughtForSession(sessionId: SessionId, thought: ThoughtData): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tassertThoughtScope('saveThoughtForSession', validatedSessionId, thought);\n\t\tthis._saveThought(validatedSessionId, thought);\n\t}\n\n\tprivate _saveThought(sessionId: SessionId, thought: ThoughtData): void {\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._loadValidatedThoughtState(sessionId, [thought]);\n\t\t\tthis._db\n\t\t\t\t.prepare('INSERT INTO thoughts (session_id, data) VALUES (?, ?)')\n\t\t\t\t.run(sessionId, JSON.stringify(thought));\n\t\t\tif (this._maxHistorySize > 0) {\n\t\t\t\tconst countRow = this._db\n\t\t\t\t\t.prepare('SELECT COUNT(*) AS count FROM thoughts WHERE session_id = ?')\n\t\t\t\t\t.get(sessionId);\n\t\t\t\tconst excess =\n\t\t\t\t\tnumberField(countRow, 'count', `${this._sourcePath}:thought-count`) -\n\t\t\t\t\tthis._maxHistorySize;\n\t\t\t\tif (excess > 0) {\n\t\t\t\t\tthis._db\n\t\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\t'DELETE FROM thoughts WHERE id IN (SELECT id FROM thoughts WHERE session_id = ? ORDER BY id ASC LIMIT ?)'\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.run(sessionId, excess);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic async loadHistoryForSession(sessionId: SessionId): Promise<ThoughtData[]> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\treturn runSqliteReadTransaction(\n\t\t\tthis._db,\n\t\t\t() => this._loadValidatedThoughtState(validatedSessionId).history\n\t\t);\n\t}\n\n\tpublic async saveBranchForSession(\n\t\tsessionId: SessionId,\n\t\tbranchId: BranchId,\n\t\tthoughts: readonly ThoughtData[]\n\t): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tconst validatedBranchId = parsePersistenceBranchId(branchId, `${this._sourcePath}:branches`);\n\t\tassertBranchScope('saveBranchForSession', validatedSessionId, validatedBranchId, thoughts);\n\t\tthis._saveBranch(validatedSessionId, validatedBranchId, thoughts);\n\t}\n\n\tprivate _saveBranch(\n\t\tsessionId: SessionId,\n\t\tbranchId: BranchId,\n\t\tthoughts: readonly ThoughtData[]\n\t): void {\n\t\tassertPersistableThoughts(thoughts, `${this._sourcePath}:branch:${branchId}`);\n\t\tif (!this._persistBranches) {\n\t\t\trunSqliteReadTransaction(this._db, () =>\n\t\t\t\tthis._loadValidatedThoughtState(sessionId, thoughts, branchId)\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._loadValidatedThoughtState(sessionId, thoughts, branchId);\n\t\t\tthis._db\n\t\t\t\t.prepare('INSERT OR REPLACE INTO branches (session_id, branch_id, data) VALUES (?, ?, ?)')\n\t\t\t\t.run(sessionId, branchId, JSON.stringify(thoughts));\n\t\t});\n\t}\n\n\tpublic async deleteBranchForSession(sessionId: SessionId, branchId: BranchId): Promise<void> {\n\t\tif (!this._persistBranches) return;\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tconst validatedBranchId = parsePersistenceBranchId(branchId, `${this._sourcePath}:branches`);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._db\n\t\t\t\t.prepare('DELETE FROM branches WHERE session_id = ? AND branch_id = ?')\n\t\t\t\t.run(validatedSessionId, validatedBranchId);\n\t\t});\n\t}\n\n\tpublic async loadBranchForSession(\n\t\tsessionId: SessionId,\n\t\tbranchId: BranchId\n\t): Promise<ThoughtData[] | undefined> {\n\t\tif (!this._persistBranches) return undefined;\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tconst validatedBranchId = parsePersistenceBranchId(branchId, `${this._sourcePath}:branches`);\n\t\treturn runSqliteReadTransaction(this._db, () =>\n\t\t\tthis._loadValidatedThoughtState(validatedSessionId).branches.get(validatedBranchId)\n\t\t);\n\t}\n\n\tpublic async listBranchesForSession(sessionId: SessionId): Promise<BranchId[]> {\n\t\tif (!this._persistBranches) return [];\n\t\treturn this._db\n\t\t\t.prepare('SELECT branch_id FROM branches WHERE session_id = ? ORDER BY branch_id ASC')\n\t\t\t.all(asSessionId(sessionId))\n\t\t\t.map((row) =>\n\t\t\t\tparsePersistenceBranchId(stringField(row, 'branch_id', this._sourcePath), this._sourcePath)\n\t\t\t);\n\t}\n\n\tpublic async listSessions(): Promise<SessionId[]> {\n\t\treturn this._db\n\t\t\t.prepare(\n\t\t\t\t'SELECT session_id FROM thoughts UNION SELECT session_id FROM branches UNION SELECT session_id FROM edges UNION SELECT session_id FROM summaries'\n\t\t\t)\n\t\t\t.all()\n\t\t\t.map((row) => asSessionId(stringField(row, 'session_id', this._sourcePath)))\n\t\t\t.sort(compareCodePoint);\n\t}\n\n\tpublic async clearAll(): Promise<void> {\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tfor (const table of ['thoughts', 'branches', 'edges', 'summaries'])\n\t\t\t\tthis._db.exec(`DELETE FROM ${table}`);\n\t\t});\n\t}\n\n\tpublic async clearSession(sessionId: SessionId): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tfor (const table of ['thoughts', 'branches', 'edges', 'summaries']) {\n\t\t\t\tthis._db.prepare(`DELETE FROM ${table} WHERE session_id = ?`).run(validatedSessionId);\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic async saveEdges(sessionId: SessionId, edges: readonly Edge[]): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tassertEdgeScopes(validatedSessionId, edges);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._db.prepare('DELETE FROM edges WHERE session_id = ?').run(validatedSessionId);\n\t\t\tconst insert = this._db.prepare(\n\t\t\t\t'INSERT INTO edges (id, session_id, from_id, to_id, kind, created_at, metadata) VALUES (?, ?, ?, ?, ?, ?, ?)'\n\t\t\t);\n\t\t\tfor (const edge of edges)\n\t\t\t\tinsert.run(\n\t\t\t\t\tedge.id,\n\t\t\t\t\tedge.sessionId,\n\t\t\t\t\tedge.from,\n\t\t\t\t\tedge.to,\n\t\t\t\t\tedge.kind,\n\t\t\t\t\tedge.createdAt,\n\t\t\t\t\tedge.metadata === undefined ? null : JSON.stringify(edge.metadata)\n\t\t\t\t);\n\t\t});\n\t}\n\n\tpublic async loadEdges(sessionId: SessionId): Promise<Edge[]> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\treturn this._db\n\t\t\t.prepare(\n\t\t\t\t'SELECT id, session_id, from_id, to_id, kind, created_at, metadata FROM edges WHERE session_id = ? ORDER BY created_at ASC, id ASC'\n\t\t\t)\n\t\t\t.all(validatedSessionId)\n\t\t\t.map((row, index) => decodeEdgeRow(row, `${this._sourcePath}:edges:${index}`));\n\t}\n\n\tpublic async saveSummaries(sessionId: SessionId, summaries: readonly Summary[]): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tassertSummaryScopes(validatedSessionId, summaries);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._db.prepare('DELETE FROM summaries WHERE session_id = ?').run(validatedSessionId);\n\t\t\tconst insert = this._db.prepare(\n\t\t\t\t'INSERT INTO summaries (id, session_id, branch_id, root_thought_id, covered_ids, covered_range_start, covered_range_end, topics, aggregate_confidence, created_at, meta) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'\n\t\t\t);\n\t\t\tfor (const summary of summaries)\n\t\t\t\tinsert.run(\n\t\t\t\t\tsummary.id,\n\t\t\t\t\tsummary.sessionId,\n\t\t\t\t\tsummary.branchId ?? null,\n\t\t\t\t\tsummary.rootThoughtId,\n\t\t\t\t\tJSON.stringify(summary.coveredIds),\n\t\t\t\t\tsummary.coveredRange[0],\n\t\t\t\t\tsummary.coveredRange[1],\n\t\t\t\t\tJSON.stringify(summary.topics),\n\t\t\t\t\tsummary.aggregateConfidence,\n\t\t\t\t\tsummary.createdAt,\n\t\t\t\t\tsummary.meta === undefined ? null : JSON.stringify(summary.meta)\n\t\t\t\t);\n\t\t});\n\t}\n\n\tpublic async loadSummaries(sessionId: SessionId): Promise<Summary[]> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\treturn this._db\n\t\t\t.prepare(\n\t\t\t\t'SELECT id, session_id, branch_id, root_thought_id, covered_ids, covered_range_start, covered_range_end, topics, aggregate_confidence, created_at, meta FROM summaries WHERE session_id = ? ORDER BY created_at ASC, id ASC'\n\t\t\t)\n\t\t\t.all(validatedSessionId)\n\t\t\t.map((row, index) => decodeSummaryRow(row, `${this._sourcePath}:summaries:${index}`));\n\t}\n\n\tpublic async healthy(): Promise<boolean> {\n\t\ttry {\n\t\t\tthis._db.prepare('SELECT 1').get();\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) return false;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic async close(): Promise<void> {\n\t\tthis._db.close();\n\t}\n\n\tpublic getStats(): {\n\t\treadonly thoughtCount: number;\n\t\treadonly branchCount: number;\n\t\treadonly dbSize: number;\n\t} {\n\t\tconst thoughtCount = numberField(\n\t\t\tthis._db.prepare('SELECT COUNT(*) AS count FROM thoughts').get(),\n\t\t\t'count',\n\t\t\tthis._sourcePath\n\t\t);\n\t\tconst branchCount = numberField(\n\t\t\tthis._db.prepare('SELECT COUNT(*) AS count FROM branches').get(),\n\t\t\t'count',\n\t\t\tthis._sourcePath\n\t\t);\n\t\treturn { thoughtCount, branchCount: this._persistBranches ? branchCount : 0, dbSize: 0 };\n\t}\n\n\tprivate _loadValidatedThoughtState(\n\t\tsessionId: SessionId,\n\t\tcandidates: readonly ThoughtData[] = [],\n\t\treplacingBranch?: BranchId\n\t): ValidatedThoughtState {\n\t\tconst history = this.loadHistoryRows(sessionId);\n\t\tfor (const thought of history) {\n\t\t\tassertThoughtScope('saveThoughtForSession', sessionId, thought);\n\t\t}\n\t\tconst collections: PersistedThoughtCollection[] = [\n\t\t\t{\n\t\t\t\tsessionId,\n\t\t\t\tthoughts: replacingBranch === undefined ? [...history, ...candidates] : history,\n\t\t\t},\n\t\t];\n\t\tconst branches = new Map<BranchId, ThoughtData[]>();\n\t\tfor (const row of this._db\n\t\t\t.prepare('SELECT branch_id, data FROM branches WHERE session_id = ?')\n\t\t\t.all(sessionId)) {\n\t\t\tconst branchId = parsePersistenceBranchId(\n\t\t\t\tstringField(row, 'branch_id', this._sourcePath),\n\t\t\t\tthis._sourcePath\n\t\t\t);\n\t\t\tconst data = stringField(row, 'data', this._sourcePath);\n\t\t\tlet values: unknown;\n\t\t\ttry {\n\t\t\t\tvalues = JSON.parse(data);\n\t\t\t} catch (error) {\n\t\t\t\tthrow new PersistenceCorruptionError(this._sourcePath, error);\n\t\t\t}\n\t\t\tif (!Array.isArray(values))\n\t\t\t\tthrow new PersistenceCorruptionError(\n\t\t\t\t\tthis._sourcePath,\n\t\t\t\t\tnew TypeError('branch data is not an array')\n\t\t\t\t);\n\t\t\tconst thoughts = values.map((value, index) =>\n\t\t\t\tdecodeThoughtRow(\n\t\t\t\t\t{ data: JSON.stringify(value) },\n\t\t\t\t\t`${this._sourcePath}:branch:${sessionId}:${branchId}:${index}`\n\t\t\t\t)\n\t\t\t);\n\t\t\tassertBranchScope('saveBranchForSession', sessionId, branchId, thoughts);\n\t\t\tbranches.set(branchId, thoughts);\n\t\t\tif (branchId !== replacingBranch) collections.push({ sessionId, thoughts });\n\t\t}\n\t\tif (replacingBranch !== undefined) {\n\t\t\tcollections.push({ sessionId, thoughts: candidates });\n\t\t}\n\t\tassertPersistableThoughtCollections(collections, this._sourcePath);\n\t\treturn { history, branches };\n\t}\n\n\tprivate loadHistoryRows(sessionId: SessionId): ThoughtData[] {\n\t\treturn this._db\n\t\t\t.prepare('SELECT data FROM thoughts WHERE session_id = ? ORDER BY id ASC')\n\t\t\t.all(sessionId)\n\t\t\t.map((row) => decodeThoughtRow(row, this._sourcePath));\n\t}\n}\n"],"names":["SqlitePersistence","_db","_sourcePath","_maxHistorySize","_persistBranches","database","options","sourcePath","initializeOrValidateSqliteV2","defaultDataDir","existsSync","join","homedir","dbPath","Database","module","error","PersistenceCompatibilityError","persistence","sessionId","thought","validatedSessionId","asSessionId","assertThoughtScope","runSqliteTransaction","JSON","countRow","excess","numberField","runSqliteReadTransaction","branchId","thoughts","validatedBranchId","parsePersistenceBranchId","assertBranchScope","assertPersistableThoughts","row","stringField","compareCodePoint","table","edges","assertEdgeScopes","insert","edge","undefined","index","decodeEdgeRow","summaries","assertSummaryScopes","summary","decodeSummaryRow","Error","thoughtCount","branchCount","candidates","replacingBranch","history","collections","branches","Map","data","values","PersistenceCorruptionError","Array","TypeError","value","decodeThoughtRow","assertPersistableThoughtCollections"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"persistence/SqlitePersistence.js","sources":["../../src/persistence/SqlitePersistence.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { PersistenceConfig, PersistenceBackend } from '../contracts/PersistenceBackend.js';\nimport { asSessionId, type BranchId, type SessionId, type ThoughtId } from '../contracts/ids.js';\nimport type { Summary } from '../core/compression/Summary.js';\nimport type { Edge } from '../core/graph/Edge.js';\nimport type { ThoughtData } from '../core/thought.js';\nimport { PersistenceCompatibilityError, PersistenceCorruptionError } from '../errors.js';\nimport type { SqliteDatabase, SqliteDatabaseConstructor } from './SqliteDriver.js';\nimport { runSqliteReadTransaction, runSqliteTransaction } from './SqliteDriver.js';\nimport {\n\tdecodeEdgeRow,\n\tdecodeSummaryRow,\n\tdecodeThoughtRow,\n\tnumberField,\n\tstringField,\n} from './SqlitePayload.js';\nimport {\n\tassertBranchScope,\n\tassertEdgeScopes,\n\tassertPersistableThoughtCollections,\n\tassertPersistableThoughts,\n\tassertSummaryScopes,\n\tassertThoughtScope,\n\tcompareCodePoint,\n\tparsePersistenceBranchId,\n\ttype PersistedThoughtCollection,\n} from './PersistenceScope.js';\nimport { initializeOrValidateSqliteV2 } from './SqliteSchemaV2.js';\nimport { stageBacktrackPersistence } from './BacktrackPersistence.js';\n\ntype SqliteOptions = PersistenceConfig['options'];\n\ntype ValidatedThoughtState = {\n\treadonly history: ThoughtData[];\n\treadonly branches: ReadonlyMap<BranchId, ThoughtData[]>;\n};\n\nexport class SqlitePersistence implements PersistenceBackend {\n\tprivate constructor(\n\t\tprivate readonly _db: SqliteDatabase,\n\t\tprivate readonly _sourcePath: string,\n\t\tprivate readonly _maxHistorySize: number,\n\t\tprivate readonly _persistBranches: boolean\n\t) {}\n\n\tpublic static createWithDatabase(\n\t\tdatabase: SqliteDatabase,\n\t\toptions: SqliteOptions = {},\n\t\tsourcePath = '<structural-sqlite>'\n\t): SqlitePersistence {\n\t\tinitializeOrValidateSqliteV2(database, sourcePath);\n\t\treturn new SqlitePersistence(\n\t\t\tdatabase,\n\t\t\tsourcePath,\n\t\t\toptions?.maxHistorySize ?? 10000,\n\t\t\toptions?.persistBranches ?? true\n\t\t);\n\t}\n\n\tpublic static async create(options?: SqliteOptions): Promise<SqlitePersistence> {\n\t\tconst defaultDataDir = existsSync('.claude/data')\n\t\t\t? '.claude/data'\n\t\t\t: join(homedir(), '.claude/data');\n\t\tconst dbPath = options?.dbPath ?? join(defaultDataDir, 'history.db');\n\t\tlet Database: SqliteDatabaseConstructor;\n\t\ttry {\n\t\t\tconst module = await import('better-sqlite3');\n\t\t\tDatabase = module.default;\n\t\t} catch (error) {\n\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\tdbPath,\n\t\t\t\t\"SQLite persistence requires the optional 'better-sqlite3' package\",\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\t\tconst database = new Database(dbPath);\n\t\ttry {\n\t\t\tconst persistence = SqlitePersistence.createWithDatabase(database, options, dbPath);\n\t\t\tif (options?.enableWAL !== false) database.pragma('journal_mode = WAL');\n\t\t\tdatabase.pragma('synchronous = NORMAL');\n\t\t\tdatabase.pragma('foreign_keys = ON');\n\t\t\tdatabase.pragma('busy_timeout = 5000');\n\t\t\tdatabase.pragma('cache_size = -64000');\n\t\t\tdatabase.pragma('temp_store = MEMORY');\n\t\t\treturn persistence;\n\t\t} catch (error) {\n\t\t\tdatabase.close();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic async saveThoughtForSession(sessionId: SessionId, thought: ThoughtData): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tassertThoughtScope('saveThoughtForSession', validatedSessionId, thought);\n\t\tthis._saveThought(validatedSessionId, thought);\n\t}\n\n\tpublic async saveBacktrackForSession(\n\t\tsessionId: SessionId,\n\t\tthought: ThoughtData,\n\t\ttargetThoughtId: ThoughtId\n\t): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tconst current = this._loadValidatedThoughtState(validatedSessionId);\n\t\t\tconst staged = stageBacktrackPersistence(\n\t\t\t\tvalidatedSessionId,\n\t\t\t\tcurrent.history,\n\t\t\t\t[...current.branches].map(([branchId, thoughts]) => ({ branchId, thoughts })),\n\t\t\t\tthought,\n\t\t\t\ttargetThoughtId,\n\t\t\t\tthis._maxHistorySize\n\t\t\t);\n\t\t\tthis._db.prepare('DELETE FROM thoughts WHERE session_id = ?').run(validatedSessionId);\n\t\t\tconst insertThought = this._db.prepare(\n\t\t\t\t'INSERT INTO thoughts (session_id, data) VALUES (?, ?)'\n\t\t\t);\n\t\t\tfor (const retained of staged.history) {\n\t\t\t\tinsertThought.run(validatedSessionId, JSON.stringify(retained));\n\t\t\t}\n\t\t\tconst replaceBranch = this._db.prepare(\n\t\t\t\t'INSERT OR REPLACE INTO branches (session_id, branch_id, data) VALUES (?, ?, ?)'\n\t\t\t);\n\t\t\tfor (const branch of staged.branches) {\n\t\t\t\treplaceBranch.run(validatedSessionId, branch.branchId, JSON.stringify(branch.thoughts));\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate _saveThought(sessionId: SessionId, thought: ThoughtData): void {\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._loadValidatedThoughtState(sessionId, [thought]);\n\t\t\tthis._db\n\t\t\t\t.prepare('INSERT INTO thoughts (session_id, data) VALUES (?, ?)')\n\t\t\t\t.run(sessionId, JSON.stringify(thought));\n\t\t\tif (this._maxHistorySize > 0) {\n\t\t\t\tconst countRow = this._db\n\t\t\t\t\t.prepare('SELECT COUNT(*) AS count FROM thoughts WHERE session_id = ?')\n\t\t\t\t\t.get(sessionId);\n\t\t\t\tconst excess =\n\t\t\t\t\tnumberField(countRow, 'count', `${this._sourcePath}:thought-count`) -\n\t\t\t\t\tthis._maxHistorySize;\n\t\t\t\tif (excess > 0) {\n\t\t\t\t\tthis._db\n\t\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\t'DELETE FROM thoughts WHERE id IN (SELECT id FROM thoughts WHERE session_id = ? ORDER BY id ASC LIMIT ?)'\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.run(sessionId, excess);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic async loadHistoryForSession(sessionId: SessionId): Promise<ThoughtData[]> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\treturn runSqliteReadTransaction(\n\t\t\tthis._db,\n\t\t\t() => this._loadValidatedThoughtState(validatedSessionId).history\n\t\t);\n\t}\n\n\tpublic async saveBranchForSession(\n\t\tsessionId: SessionId,\n\t\tbranchId: BranchId,\n\t\tthoughts: readonly ThoughtData[]\n\t): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tconst validatedBranchId = parsePersistenceBranchId(branchId, `${this._sourcePath}:branches`);\n\t\tassertBranchScope('saveBranchForSession', validatedSessionId, validatedBranchId, thoughts);\n\t\tthis._saveBranch(validatedSessionId, validatedBranchId, thoughts);\n\t}\n\n\tprivate _saveBranch(\n\t\tsessionId: SessionId,\n\t\tbranchId: BranchId,\n\t\tthoughts: readonly ThoughtData[]\n\t): void {\n\t\tassertPersistableThoughts(thoughts, `${this._sourcePath}:branch:${branchId}`);\n\t\tif (!this._persistBranches) {\n\t\t\trunSqliteReadTransaction(this._db, () =>\n\t\t\t\tthis._loadValidatedThoughtState(sessionId, thoughts, branchId)\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._loadValidatedThoughtState(sessionId, thoughts, branchId);\n\t\t\tthis._db\n\t\t\t\t.prepare('INSERT OR REPLACE INTO branches (session_id, branch_id, data) VALUES (?, ?, ?)')\n\t\t\t\t.run(sessionId, branchId, JSON.stringify(thoughts));\n\t\t});\n\t}\n\n\tpublic async deleteBranchForSession(sessionId: SessionId, branchId: BranchId): Promise<void> {\n\t\tif (!this._persistBranches) return;\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tconst validatedBranchId = parsePersistenceBranchId(branchId, `${this._sourcePath}:branches`);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._db\n\t\t\t\t.prepare('DELETE FROM branches WHERE session_id = ? AND branch_id = ?')\n\t\t\t\t.run(validatedSessionId, validatedBranchId);\n\t\t});\n\t}\n\n\tpublic async loadBranchForSession(\n\t\tsessionId: SessionId,\n\t\tbranchId: BranchId\n\t): Promise<ThoughtData[] | undefined> {\n\t\tif (!this._persistBranches) return undefined;\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tconst validatedBranchId = parsePersistenceBranchId(branchId, `${this._sourcePath}:branches`);\n\t\treturn runSqliteReadTransaction(this._db, () =>\n\t\t\tthis._loadValidatedThoughtState(validatedSessionId).branches.get(validatedBranchId)\n\t\t);\n\t}\n\n\tpublic async listBranchesForSession(sessionId: SessionId): Promise<BranchId[]> {\n\t\tif (!this._persistBranches) return [];\n\t\treturn this._db\n\t\t\t.prepare('SELECT branch_id FROM branches WHERE session_id = ? ORDER BY branch_id ASC')\n\t\t\t.all(asSessionId(sessionId))\n\t\t\t.map((row) =>\n\t\t\t\tparsePersistenceBranchId(stringField(row, 'branch_id', this._sourcePath), this._sourcePath)\n\t\t\t);\n\t}\n\n\tpublic async listSessions(): Promise<SessionId[]> {\n\t\treturn this._db\n\t\t\t.prepare(\n\t\t\t\t'SELECT session_id FROM thoughts UNION SELECT session_id FROM branches UNION SELECT session_id FROM edges UNION SELECT session_id FROM summaries'\n\t\t\t)\n\t\t\t.all()\n\t\t\t.map((row) => asSessionId(stringField(row, 'session_id', this._sourcePath)))\n\t\t\t.sort(compareCodePoint);\n\t}\n\n\tpublic async clearAll(): Promise<void> {\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tfor (const table of ['thoughts', 'branches', 'edges', 'summaries'])\n\t\t\t\tthis._db.exec(`DELETE FROM ${table}`);\n\t\t});\n\t}\n\n\tpublic async clearSession(sessionId: SessionId): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tfor (const table of ['thoughts', 'branches', 'edges', 'summaries']) {\n\t\t\t\tthis._db.prepare(`DELETE FROM ${table} WHERE session_id = ?`).run(validatedSessionId);\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic async saveEdges(sessionId: SessionId, edges: readonly Edge[]): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tassertEdgeScopes(validatedSessionId, edges);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._db.prepare('DELETE FROM edges WHERE session_id = ?').run(validatedSessionId);\n\t\t\tconst insert = this._db.prepare(\n\t\t\t\t'INSERT INTO edges (id, session_id, from_id, to_id, kind, created_at, metadata) VALUES (?, ?, ?, ?, ?, ?, ?)'\n\t\t\t);\n\t\t\tfor (const edge of edges)\n\t\t\t\tinsert.run(\n\t\t\t\t\tedge.id,\n\t\t\t\t\tedge.sessionId,\n\t\t\t\t\tedge.from,\n\t\t\t\t\tedge.to,\n\t\t\t\t\tedge.kind,\n\t\t\t\t\tedge.createdAt,\n\t\t\t\t\tedge.metadata === undefined ? null : JSON.stringify(edge.metadata)\n\t\t\t\t);\n\t\t});\n\t}\n\n\tpublic async loadEdges(sessionId: SessionId): Promise<Edge[]> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\treturn this._db\n\t\t\t.prepare(\n\t\t\t\t'SELECT id, session_id, from_id, to_id, kind, created_at, metadata FROM edges WHERE session_id = ? ORDER BY created_at ASC, id ASC'\n\t\t\t)\n\t\t\t.all(validatedSessionId)\n\t\t\t.map((row, index) => decodeEdgeRow(row, `${this._sourcePath}:edges:${index}`));\n\t}\n\n\tpublic async saveSummaries(sessionId: SessionId, summaries: readonly Summary[]): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tassertSummaryScopes(validatedSessionId, summaries);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._db.prepare('DELETE FROM summaries WHERE session_id = ?').run(validatedSessionId);\n\t\t\tconst insert = this._db.prepare(\n\t\t\t\t'INSERT INTO summaries (id, session_id, branch_id, root_thought_id, covered_ids, covered_range_start, covered_range_end, topics, aggregate_confidence, created_at, meta) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'\n\t\t\t);\n\t\t\tfor (const summary of summaries)\n\t\t\t\tinsert.run(\n\t\t\t\t\tsummary.id,\n\t\t\t\t\tsummary.sessionId,\n\t\t\t\t\tsummary.branchId ?? null,\n\t\t\t\t\tsummary.rootThoughtId,\n\t\t\t\t\tJSON.stringify(summary.coveredIds),\n\t\t\t\t\tsummary.coveredRange[0],\n\t\t\t\t\tsummary.coveredRange[1],\n\t\t\t\t\tJSON.stringify(summary.topics),\n\t\t\t\t\tsummary.aggregateConfidence,\n\t\t\t\t\tsummary.createdAt,\n\t\t\t\t\tsummary.meta === undefined ? null : JSON.stringify(summary.meta)\n\t\t\t\t);\n\t\t});\n\t}\n\n\tpublic async loadSummaries(sessionId: SessionId): Promise<Summary[]> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\treturn this._db\n\t\t\t.prepare(\n\t\t\t\t'SELECT id, session_id, branch_id, root_thought_id, covered_ids, covered_range_start, covered_range_end, topics, aggregate_confidence, created_at, meta FROM summaries WHERE session_id = ? ORDER BY created_at ASC, id ASC'\n\t\t\t)\n\t\t\t.all(validatedSessionId)\n\t\t\t.map((row, index) => decodeSummaryRow(row, `${this._sourcePath}:summaries:${index}`));\n\t}\n\n\tpublic async healthy(): Promise<boolean> {\n\t\ttry {\n\t\t\tthis._db.prepare('SELECT 1').get();\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) return false;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic async close(): Promise<void> {\n\t\tthis._db.close();\n\t}\n\n\tpublic getStats(): {\n\t\treadonly thoughtCount: number;\n\t\treadonly branchCount: number;\n\t\treadonly dbSize: number;\n\t} {\n\t\tconst thoughtCount = numberField(\n\t\t\tthis._db.prepare('SELECT COUNT(*) AS count FROM thoughts').get(),\n\t\t\t'count',\n\t\t\tthis._sourcePath\n\t\t);\n\t\tconst branchCount = numberField(\n\t\t\tthis._db.prepare('SELECT COUNT(*) AS count FROM branches').get(),\n\t\t\t'count',\n\t\t\tthis._sourcePath\n\t\t);\n\t\treturn { thoughtCount, branchCount: this._persistBranches ? branchCount : 0, dbSize: 0 };\n\t}\n\n\tprivate _loadValidatedThoughtState(\n\t\tsessionId: SessionId,\n\t\tcandidates: readonly ThoughtData[] = [],\n\t\treplacingBranch?: BranchId\n\t): ValidatedThoughtState {\n\t\tconst history = this.loadHistoryRows(sessionId);\n\t\tfor (const thought of history) {\n\t\t\tassertThoughtScope('saveThoughtForSession', sessionId, thought);\n\t\t}\n\t\tconst collections: PersistedThoughtCollection[] = [\n\t\t\t{\n\t\t\t\tsessionId,\n\t\t\t\tthoughts: replacingBranch === undefined ? [...history, ...candidates] : history,\n\t\t\t},\n\t\t];\n\t\tconst branches = new Map<BranchId, ThoughtData[]>();\n\t\tfor (const row of this._db\n\t\t\t.prepare('SELECT branch_id, data FROM branches WHERE session_id = ?')\n\t\t\t.all(sessionId)) {\n\t\t\tconst branchId = parsePersistenceBranchId(\n\t\t\t\tstringField(row, 'branch_id', this._sourcePath),\n\t\t\t\tthis._sourcePath\n\t\t\t);\n\t\t\tconst data = stringField(row, 'data', this._sourcePath);\n\t\t\tlet values: unknown;\n\t\t\ttry {\n\t\t\t\tvalues = JSON.parse(data);\n\t\t\t} catch (error) {\n\t\t\t\tthrow new PersistenceCorruptionError(this._sourcePath, error);\n\t\t\t}\n\t\t\tif (!Array.isArray(values))\n\t\t\t\tthrow new PersistenceCorruptionError(\n\t\t\t\t\tthis._sourcePath,\n\t\t\t\t\tnew TypeError('branch data is not an array')\n\t\t\t\t);\n\t\t\tconst thoughts = values.map((value, index) =>\n\t\t\t\tdecodeThoughtRow(\n\t\t\t\t\t{ data: JSON.stringify(value) },\n\t\t\t\t\t`${this._sourcePath}:branch:${sessionId}:${branchId}:${index}`\n\t\t\t\t)\n\t\t\t);\n\t\t\tassertBranchScope('saveBranchForSession', sessionId, branchId, thoughts);\n\t\t\tbranches.set(branchId, thoughts);\n\t\t\tif (branchId !== replacingBranch) collections.push({ sessionId, thoughts });\n\t\t}\n\t\tif (replacingBranch !== undefined) {\n\t\t\tcollections.push({ sessionId, thoughts: candidates });\n\t\t}\n\t\tassertPersistableThoughtCollections(collections, this._sourcePath);\n\t\treturn { history, branches };\n\t}\n\n\tprivate loadHistoryRows(sessionId: SessionId): ThoughtData[] {\n\t\treturn this._db\n\t\t\t.prepare('SELECT data FROM thoughts WHERE session_id = ? ORDER BY id ASC')\n\t\t\t.all(sessionId)\n\t\t\t.map((row) => decodeThoughtRow(row, this._sourcePath));\n\t}\n}\n"],"names":["SqlitePersistence","_db","_sourcePath","_maxHistorySize","_persistBranches","database","options","sourcePath","initializeOrValidateSqliteV2","defaultDataDir","existsSync","join","homedir","dbPath","Database","module","error","PersistenceCompatibilityError","persistence","sessionId","thought","validatedSessionId","asSessionId","assertThoughtScope","targetThoughtId","runSqliteTransaction","current","staged","stageBacktrackPersistence","branchId","thoughts","insertThought","retained","JSON","replaceBranch","branch","countRow","excess","numberField","runSqliteReadTransaction","validatedBranchId","parsePersistenceBranchId","assertBranchScope","assertPersistableThoughts","row","stringField","compareCodePoint","table","edges","assertEdgeScopes","insert","edge","undefined","index","decodeEdgeRow","summaries","assertSummaryScopes","summary","decodeSummaryRow","Error","thoughtCount","branchCount","candidates","replacingBranch","history","collections","branches","Map","data","values","PersistenceCorruptionError","Array","TypeError","value","decodeThoughtRow","assertPersistableThoughtCollections"],"mappings":"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"}
|
|
@@ -56,7 +56,10 @@ export declare abstract class BaseRegistry<T extends {
|
|
|
56
56
|
protected _discovered: boolean;
|
|
57
57
|
/** Promise for in-progress discovery (null if not in progress). */
|
|
58
58
|
protected _discoveryPromise: Promise<number> | null;
|
|
59
|
-
private
|
|
59
|
+
private _activeDiscoveryEpoch;
|
|
60
|
+
private _queuedRefreshEpoch;
|
|
61
|
+
private _replacementEpoch;
|
|
62
|
+
private _discoveredCount;
|
|
60
63
|
/** File extensions to match during discovery. */
|
|
61
64
|
protected abstract readonly _fileExtensions: string[];
|
|
62
65
|
/**
|
|
@@ -195,9 +198,14 @@ export declare abstract class BaseRegistry<T extends {
|
|
|
195
198
|
*
|
|
196
199
|
* @returns A Promise resolving to the number of items discovered
|
|
197
200
|
*/
|
|
198
|
-
protected _performDiscovery(): Promise<number>;
|
|
201
|
+
protected _performDiscovery(epoch?: number): Promise<number>;
|
|
202
|
+
private _scanDirectory;
|
|
203
|
+
private _scanFile;
|
|
199
204
|
private _retainLastKnownGood;
|
|
200
205
|
private _retainDirectory;
|
|
206
|
+
private _publishDiscovery;
|
|
207
|
+
private _rebuildVisibleItems;
|
|
208
|
+
private _beginReplacement;
|
|
201
209
|
/**
|
|
202
210
|
* Parses YAML frontmatter from file content.
|
|
203
211
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseRegistry.d.ts","sourceRoot":"","sources":["../../src/registry/BaseRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAG5D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,kCAAkC;IAClC,KAAK,CAAC,EAAE,cAAc,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEzC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"BaseRegistry.d.ts","sourceRoot":"","sources":["../../src/registry/BaseRegistry.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,7 +14,10 @@ class BaseRegistry {
|
|
|
14
14
|
_searchDirs;
|
|
15
15
|
_discovered = false;
|
|
16
16
|
_discoveryPromise = null;
|
|
17
|
-
|
|
17
|
+
_activeDiscoveryEpoch = null;
|
|
18
|
+
_queuedRefreshEpoch = null;
|
|
19
|
+
_replacementEpoch = 0;
|
|
20
|
+
_discoveredCount = 0;
|
|
18
21
|
constructor(options){
|
|
19
22
|
this._items = new Map();
|
|
20
23
|
this._manualItems = new Map();
|
|
@@ -41,9 +44,11 @@ class BaseRegistry {
|
|
|
41
44
|
}
|
|
42
45
|
remove(name) {
|
|
43
46
|
if (!this._items.has(name)) throw this._createNotFoundError(name, 'remove');
|
|
44
|
-
this.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
const removedManualItem = this._manualItems.delete(name);
|
|
48
|
+
if (!removedManualItem) {
|
|
49
|
+
for (const [filePath, item] of this._discoveredItemsByPath)if (item.name === name) this._discoveredItemsByPath.delete(filePath);
|
|
50
|
+
}
|
|
51
|
+
this._rebuildVisibleItems();
|
|
47
52
|
this.log(`Removed ${this._entityName}: ${name}`, {
|
|
48
53
|
[`${this._entityName}Name`]: name
|
|
49
54
|
});
|
|
@@ -52,14 +57,16 @@ class BaseRegistry {
|
|
|
52
57
|
}
|
|
53
58
|
update(name, updates) {
|
|
54
59
|
if (!this._items.has(name)) throw this._createNotFoundError(name, 'update');
|
|
60
|
+
const isManualItem = this._manualItems.has(name);
|
|
55
61
|
const existing = this._items.get(name);
|
|
56
62
|
const updated = {
|
|
57
63
|
...existing,
|
|
58
|
-
...updates
|
|
64
|
+
...updates,
|
|
65
|
+
name
|
|
59
66
|
};
|
|
60
67
|
this._items.set(name, updated);
|
|
61
|
-
if (
|
|
62
|
-
for (const [filePath, item] of this._discoveredItemsByPath)if (item.name === name) this._discoveredItemsByPath.set(filePath, updated);
|
|
68
|
+
if (isManualItem) this._manualItems.set(name, updated);
|
|
69
|
+
else for (const [filePath, item] of this._discoveredItemsByPath)if (item.name === name) this._discoveredItemsByPath.set(filePath, updated);
|
|
63
70
|
this.log(`Updated ${this._entityName}: ${name}`, {
|
|
64
71
|
[`${this._entityName}Name`]: name
|
|
65
72
|
});
|
|
@@ -85,110 +92,150 @@ class BaseRegistry {
|
|
|
85
92
|
return Array.from(this._items.keys());
|
|
86
93
|
}
|
|
87
94
|
clear() {
|
|
88
|
-
this.
|
|
89
|
-
this._manualItems.clear();
|
|
90
|
-
this._discoveredItemsByPath.clear();
|
|
95
|
+
this._beginReplacement();
|
|
91
96
|
this.log(`Cleared all ${this._entityName}s`);
|
|
92
|
-
this._cache?.clear();
|
|
93
97
|
}
|
|
94
98
|
size() {
|
|
95
99
|
return this._items.size;
|
|
96
100
|
}
|
|
97
101
|
async discoverAsync() {
|
|
98
|
-
if (this._discoveryPromise)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return cached?.length ?? 0;
|
|
102
|
+
if (this._discoveryPromise) {
|
|
103
|
+
if (!this._discovered && this._activeDiscoveryEpoch !== this._replacementEpoch) this._queuedRefreshEpoch = this._replacementEpoch;
|
|
104
|
+
return this._discoveryPromise;
|
|
102
105
|
}
|
|
106
|
+
if (this._discovered) return this._discoveredCount;
|
|
103
107
|
return this._beginDiscovery();
|
|
104
108
|
}
|
|
105
109
|
async refreshAsync() {
|
|
106
|
-
this.
|
|
107
|
-
|
|
110
|
+
if (this._discoveryPromise) {
|
|
111
|
+
this._queuedRefreshEpoch = this._replacementEpoch;
|
|
112
|
+
return this._discoveryPromise;
|
|
113
|
+
}
|
|
114
|
+
return this._beginDiscovery();
|
|
108
115
|
}
|
|
109
116
|
_beginDiscovery() {
|
|
110
117
|
const operation = this._runDiscoveryLoop();
|
|
111
118
|
this._discoveryPromise = operation;
|
|
112
|
-
const clearOperation = ()=>{
|
|
113
|
-
if (this._discoveryPromise === operation) this._discoveryPromise = null;
|
|
114
|
-
};
|
|
115
|
-
operation.then(clearOperation, clearOperation);
|
|
116
119
|
return operation;
|
|
117
120
|
}
|
|
118
121
|
async _runDiscoveryLoop() {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
let passEpoch = this._replacementEpoch;
|
|
123
|
+
try {
|
|
124
|
+
while(true){
|
|
125
|
+
this._activeDiscoveryEpoch = passEpoch;
|
|
126
|
+
let count;
|
|
127
|
+
try {
|
|
128
|
+
count = await this._performDiscovery(passEpoch);
|
|
129
|
+
} finally{
|
|
130
|
+
this._activeDiscoveryEpoch = null;
|
|
131
|
+
}
|
|
132
|
+
const queuedEpoch = this._queuedRefreshEpoch;
|
|
133
|
+
this._queuedRefreshEpoch = null;
|
|
134
|
+
if (null === queuedEpoch) return count;
|
|
135
|
+
passEpoch = queuedEpoch;
|
|
136
|
+
}
|
|
137
|
+
} finally{
|
|
138
|
+
this._discoveryPromise = null;
|
|
124
139
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
140
|
+
}
|
|
141
|
+
async _performDiscovery(epoch = this._replacementEpoch) {
|
|
142
|
+
const scan = {
|
|
143
|
+
itemsByPath: new Map(),
|
|
144
|
+
claimedNames: new Set(),
|
|
145
|
+
previousItemsByPath: new Map(this._discoveredItemsByPath),
|
|
146
|
+
warnings: []
|
|
147
|
+
};
|
|
148
|
+
for (const dir of this._searchDirs)await this._scanDirectory(dir, scan);
|
|
149
|
+
return this._publishDiscovery(epoch, scan);
|
|
150
|
+
}
|
|
151
|
+
async _scanDirectory(directory, scan) {
|
|
152
|
+
try {
|
|
153
|
+
if (!existsSync(directory)) return;
|
|
154
|
+
const entries = await readdir(directory, {
|
|
133
155
|
withFileTypes: true
|
|
134
156
|
});
|
|
135
|
-
for (const entry of entries)if (!this._shouldSkipFile(entry.name)) {
|
|
136
|
-
if (
|
|
137
|
-
const filePath = join(dir, entry.name);
|
|
138
|
-
try {
|
|
139
|
-
const content = await readFile(filePath, 'utf-8');
|
|
140
|
-
const parsed = this._parseFrontmatter(content);
|
|
141
|
-
if (parsed._error) {
|
|
142
|
-
this._retainLastKnownGood(filePath, parsed._error, discoveredItems, claimedNames);
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
if (parsed.name) {
|
|
146
|
-
const item = this._buildItem(parsed);
|
|
147
|
-
if (item && !claimedNames.has(item.name)) {
|
|
148
|
-
discoveredItems.set(filePath, item);
|
|
149
|
-
claimedNames.add(item.name);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
} catch (readError) {
|
|
153
|
-
this._retainLastKnownGood(filePath, getErrorMessage(readError), discoveredItems, claimedNames);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
157
|
+
for (const entry of entries)if (!this._shouldSkipFile(entry.name) && entry.isFile()) {
|
|
158
|
+
if (this._fileExtensions.some((extension)=>entry.name.endsWith(extension))) await this._scanFile(join(directory, entry.name), scan);
|
|
156
159
|
}
|
|
157
160
|
} catch (error) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
+
scan.warnings.push({
|
|
162
|
+
message: `Failed to scan ${this._entityName} directory`,
|
|
163
|
+
meta: {
|
|
164
|
+
directory,
|
|
165
|
+
error: getErrorMessage(error)
|
|
166
|
+
}
|
|
161
167
|
});
|
|
162
|
-
this._retainDirectory(
|
|
168
|
+
this._retainDirectory(directory, scan);
|
|
163
169
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
170
|
+
}
|
|
171
|
+
async _scanFile(filePath, scan) {
|
|
172
|
+
try {
|
|
173
|
+
const content = await readFile(filePath, 'utf-8');
|
|
174
|
+
const parsed = this._parseFrontmatter(content);
|
|
175
|
+
if (parsed._error) return void this._retainLastKnownGood(filePath, parsed._error, scan);
|
|
176
|
+
if (!parsed.name) return;
|
|
177
|
+
const item = this._buildItem(parsed);
|
|
178
|
+
if (item && !scan.claimedNames.has(item.name)) {
|
|
179
|
+
scan.itemsByPath.set(filePath, item);
|
|
180
|
+
scan.claimedNames.add(item.name);
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
this._retainLastKnownGood(filePath, getErrorMessage(error), scan);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
_retainLastKnownGood(filePath, reason, scan) {
|
|
187
|
+
const previous = scan.previousItemsByPath.get(filePath);
|
|
188
|
+
if (previous && !scan.claimedNames.has(previous.name)) {
|
|
189
|
+
scan.itemsByPath.set(filePath, previous);
|
|
190
|
+
scan.claimedNames.add(previous.name);
|
|
191
|
+
}
|
|
192
|
+
scan.warnings.push({
|
|
193
|
+
message: `Invalid ${this._entityName} discovery file`,
|
|
194
|
+
meta: {
|
|
195
|
+
filePath,
|
|
196
|
+
reason,
|
|
197
|
+
retainedLastKnownGood: void 0 !== previous
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
_retainDirectory(directory, scan) {
|
|
202
|
+
for (const [filePath, item] of scan.previousItemsByPath)if (dirname(filePath) === directory && !scan.claimedNames.has(item.name)) {
|
|
203
|
+
scan.itemsByPath.set(filePath, item);
|
|
204
|
+
scan.claimedNames.add(item.name);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
_publishDiscovery(epoch, scan) {
|
|
208
|
+
if (epoch !== this._replacementEpoch) return this._discoveredCount;
|
|
209
|
+
this._discoveredItemsByPath = scan.itemsByPath;
|
|
210
|
+
this._rebuildVisibleItems();
|
|
167
211
|
this._discovered = true;
|
|
168
212
|
this._cache.clear();
|
|
169
213
|
this._cache.set('all', Array.from(this._items.values()));
|
|
170
|
-
|
|
171
|
-
|
|
214
|
+
for (const warning of scan.warnings)this._logger.warn(warning.message, warning.meta);
|
|
215
|
+
this.log(`Discovery complete: found ${this._discoveredCount} ${this._entityName}s`, {
|
|
216
|
+
discoveredCount: this._discoveredCount
|
|
172
217
|
});
|
|
173
|
-
return
|
|
218
|
+
return this._discoveredCount;
|
|
174
219
|
}
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
220
|
+
_rebuildVisibleItems() {
|
|
221
|
+
const visibleItems = new Map(this._manualItems);
|
|
222
|
+
let visibleDiscoveredCount = 0;
|
|
223
|
+
for (const item of this._discoveredItemsByPath.values())if (!visibleItems.has(item.name)) {
|
|
224
|
+
visibleItems.set(item.name, item);
|
|
225
|
+
visibleDiscoveredCount++;
|
|
180
226
|
}
|
|
181
|
-
this.
|
|
182
|
-
|
|
183
|
-
reason,
|
|
184
|
-
retainedLastKnownGood: void 0 !== previous
|
|
185
|
-
});
|
|
227
|
+
this._items = visibleItems;
|
|
228
|
+
this._discoveredCount = visibleDiscoveredCount;
|
|
186
229
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
230
|
+
_beginReplacement() {
|
|
231
|
+
this._replacementEpoch++;
|
|
232
|
+
this._queuedRefreshEpoch = null;
|
|
233
|
+
this._items.clear();
|
|
234
|
+
this._manualItems.clear();
|
|
235
|
+
this._discoveredItemsByPath.clear();
|
|
236
|
+
this._discovered = false;
|
|
237
|
+
this._discoveredCount = 0;
|
|
238
|
+
this._cache.clear();
|
|
192
239
|
}
|
|
193
240
|
_extractFrontmatter(content) {
|
|
194
241
|
const match = content.match(/^---\n([\s\S]+?)\n---/);
|
|
@@ -196,7 +243,7 @@ class BaseRegistry {
|
|
|
196
243
|
return parse(match[1]);
|
|
197
244
|
}
|
|
198
245
|
setAll(items) {
|
|
199
|
-
this.
|
|
246
|
+
this._beginReplacement();
|
|
200
247
|
for (const item of items)try {
|
|
201
248
|
this.add(item);
|
|
202
249
|
} catch (error) {
|