pactium 0.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.
Files changed (42) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +92 -0
  3. package/README.zh-CN.md +90 -0
  4. package/SECURITY.md +7 -0
  5. package/bin/pactium.mjs +121 -0
  6. package/docs/LICOLITE-ASPECT.md +57 -0
  7. package/docs/README.md +13 -0
  8. package/docs/TERM.md +289 -0
  9. package/docs/architecture/ARCHITECTURE.md +62 -0
  10. package/docs/protocols/PROFILE.md +124 -0
  11. package/docs/protocols/PROTOCOLS.md +62 -0
  12. package/examples/record-operation.mjs +26 -0
  13. package/package.json +69 -0
  14. package/src/README.md +13 -0
  15. package/src/aspects/licolite/aspect.js +278 -0
  16. package/src/aspects/licolite/constants.js +13 -0
  17. package/src/aspects/licolite/evidence.js +47 -0
  18. package/src/aspects/licolite/index.d.ts +51 -0
  19. package/src/aspects/licolite/index.js +19 -0
  20. package/src/aspects/licolite/signing.js +78 -0
  21. package/src/canonical/value.js +40 -0
  22. package/src/core/append-condition.js +102 -0
  23. package/src/core/pactium-core.js +1073 -0
  24. package/src/core/tracking-cursor.js +68 -0
  25. package/src/http.js +99 -0
  26. package/src/index-engine/snapshot-merkle-index.js +994 -0
  27. package/src/index.d.ts +244 -0
  28. package/src/index.js +73 -0
  29. package/src/ledger/signed-head.js +204 -0
  30. package/src/ledger/transparency-log.js +702 -0
  31. package/src/maintenance/task-engine.js +36 -0
  32. package/src/proof/bundle-format.js +265 -0
  33. package/src/proof/bundle.js +77 -0
  34. package/src/proof/envelope.js +548 -0
  35. package/src/proof/registry.js +18 -0
  36. package/src/protocol/constants.js +69 -0
  37. package/src/protocol/hashing.js +47 -0
  38. package/src/quality/profile-runner.js +291 -0
  39. package/src/repair/planner.js +62 -0
  40. package/src/shared/records.js +32 -0
  41. package/src/storage/local-json-storage-port.js +360 -0
  42. package/src/verification/failure.js +31 -0
@@ -0,0 +1,1073 @@
1
+ import crypto from "node:crypto";
2
+
3
+ import {
4
+ PACTIUM_BUNDLE_ENCODING,
5
+ PACTIUM_PROOF_BUNDLE_TYPE,
6
+ PACTIUM_PROOF_TYPES,
7
+ PACTIUM_PROTOCOL,
8
+ PACTIUM_SCHEMA_VERSION
9
+ } from "../protocol/constants.js";
10
+ import { canonicalEncode, normalizeCanonicalValue } from "../canonical/value.js";
11
+ import { createAppendCondition, assertAppendCondition } from "./append-condition.js";
12
+ import { createLedgerTransparencyLog } from "../ledger/transparency-log.js";
13
+ import { advanceTrustedHead as advanceTrustedLedgerHead } from "../ledger/signed-head.js";
14
+ import { createVerifiableIndexEngine } from "../index-engine/snapshot-merkle-index.js";
15
+ import { createId, protocolHash, protocolHashHex } from "../protocol/hashing.js";
16
+ import { createProofRef, finalizeEnvelope, materializeExtension, verifyProofEnvelope } from "../proof/envelope.js";
17
+ import { createRepairPlanner } from "../repair/planner.js";
18
+ import { createStoragePort } from "../storage/local-json-storage-port.js";
19
+ import { createTrackingCursor, verifyTrackingCursor } from "./tracking-cursor.js";
20
+ import { asArray, asRecord, nowIso, safeText } from "../shared/records.js";
21
+ import { createVerificationFailure, PactiumLifecycleError } from "../verification/failure.js";
22
+
23
+ function createEmptyCoreState() {
24
+ return {
25
+ protocol: PACTIUM_PROTOCOL,
26
+ schema: PACTIUM_SCHEMA_VERSION,
27
+ indexRoots: {
28
+ openIntent: "",
29
+ outcome: "",
30
+ intentIdempotency: "",
31
+ outcomeIdempotency: "",
32
+ causality: ""
33
+ },
34
+ workspace: {},
35
+ stateEntries: {},
36
+ checkpointEntries: {},
37
+ intents: {},
38
+ outcomes: {},
39
+ intentEnvelopes: {},
40
+ intentIdempotencyClaims: {},
41
+ outcomeEnvelopes: {},
42
+ envelopes: {},
43
+ proofBundles: {}
44
+ };
45
+ }
46
+
47
+ function idempotencyKeyFor(input) {
48
+ return [
49
+ safeText(input.workspaceId, "default"),
50
+ safeText(input.operationId),
51
+ safeText(input.idempotencyKey),
52
+ protocolHashHex("operation.intent", input.input ?? input.payload ?? {})
53
+ ].join("\u0000");
54
+ }
55
+
56
+ function intentIdempotencyClaimKeyFor(input) {
57
+ return [
58
+ safeText(input.workspaceId, "default"),
59
+ safeText(input.operationId),
60
+ safeText(input.idempotencyKey)
61
+ ].join("\u0000");
62
+ }
63
+
64
+ function outcomeIdempotencyKeyFor(input) {
65
+ return [
66
+ safeText(input.intentId),
67
+ safeText(input.outcomeIdempotencyKey || input.idempotencyKey),
68
+ protocolHashHex("operation.outcome", input.result ?? input.output ?? input.status ?? "succeeded")
69
+ ].join("\u0000");
70
+ }
71
+
72
+ function eventRefValue(ledgerAppend) {
73
+ return {
74
+ valueRef: ledgerAppend.entry.factCid,
75
+ valueHash: ledgerAppend.entry.factHash,
76
+ metadata: {
77
+ ledgerEventId: ledgerAppend.entry.eventId,
78
+ ledgerIndex: ledgerAppend.entry.index,
79
+ factType: ledgerAppend.entry.fact.factType
80
+ }
81
+ };
82
+ }
83
+
84
+ function lifecycleValueRef(id, extra = {}) {
85
+ return {
86
+ valueRef: `ref:${id}`,
87
+ valueHash: protocolHash("block", { id, ...extra }),
88
+ metadata: extra
89
+ };
90
+ }
91
+
92
+ function workspaceStateFor(state, workspaceId) {
93
+ const key = safeText(workspaceId, "default");
94
+ state.workspace[key] ||= {
95
+ nextOrdinal: 0,
96
+ orderRoot: "",
97
+ membershipRoot: "",
98
+ checkpointRoot: "",
99
+ stateRoot: ""
100
+ };
101
+ return state.workspace[key];
102
+ }
103
+
104
+ function stateEntriesFor(state, workspaceId) {
105
+ const key = safeText(workspaceId, "default");
106
+ state.stateEntries[key] ||= {};
107
+ return state.stateEntries[key];
108
+ }
109
+
110
+ function checkpointEntriesFor(state, workspaceId) {
111
+ const key = safeText(workspaceId, "default");
112
+ state.checkpointEntries[key] ||= {};
113
+ return state.checkpointEntries[key];
114
+ }
115
+
116
+ function padOrdinal(value) {
117
+ return String(value).padStart(16, "0");
118
+ }
119
+
120
+ function isMembershipProof(proof) {
121
+ return proof?.proofType === PACTIUM_PROOF_TYPES.indexMembership;
122
+ }
123
+
124
+ function knownCausalityRefsFor(state) {
125
+ return new Set([
126
+ ...Object.keys(asRecord(state.intents)),
127
+ ...Object.values(asRecord(state.intents)).map((record) => record?.ledgerEventId).filter(Boolean),
128
+ ...Object.keys(asRecord(state.outcomes)),
129
+ ...Object.values(asRecord(state.outcomes)).map((outcome) => outcome?.outcomeId).filter(Boolean)
130
+ ]);
131
+ }
132
+
133
+ async function applyIndexPut(indexEngine, root, key, value, domain) {
134
+ const result = await indexEngine.put(root, key, value, { domain });
135
+ return result.root;
136
+ }
137
+
138
+ async function applyIndexDelete(indexEngine, root, key, domain) {
139
+ const result = await indexEngine.delete(root, key, { domain });
140
+ return result.root;
141
+ }
142
+
143
+ function currentIndexRoots(state) {
144
+ const retained = new Set(Object.values(asRecord(state.indexRoots)).map(String).filter(Boolean));
145
+ for (const workspace of Object.values(asRecord(state.workspace))) {
146
+ for (const field of ["orderRoot", "membershipRoot", "checkpointRoot", "stateRoot"]) {
147
+ if (workspace?.[field]) retained.add(String(workspace[field]));
148
+ }
149
+ }
150
+ return [...retained];
151
+ }
152
+
153
+ function hashFromCid(cid) {
154
+ return String(cid || "").split(":").pop() || "";
155
+ }
156
+
157
+ async function updateWorkspaceProjection({ indexEngine, state, workspaceId, ledgerAppend }) {
158
+ const workspace = workspaceStateFor(state, workspaceId);
159
+ const ordinal = workspace.nextOrdinal;
160
+ workspace.nextOrdinal += 1;
161
+ const orderKey = padOrdinal(ordinal);
162
+ workspace.orderRoot = await applyIndexPut(
163
+ indexEngine,
164
+ workspace.orderRoot,
165
+ orderKey,
166
+ eventRefValue(ledgerAppend),
167
+ "workspace-order"
168
+ );
169
+ workspace.membershipRoot = await applyIndexPut(
170
+ indexEngine,
171
+ workspace.membershipRoot,
172
+ ledgerAppend.entry.eventId,
173
+ lifecycleValueRef(orderKey, { workspaceId, ordinal }),
174
+ "workspace-membership"
175
+ );
176
+ return {
177
+ workspaceId,
178
+ ordinal,
179
+ orderKey,
180
+ orderRoot: workspace.orderRoot,
181
+ membershipRoot: workspace.membershipRoot,
182
+ orderProof: await indexEngine.prove(workspace.orderRoot, orderKey),
183
+ membershipProof: await indexEngine.prove(workspace.membershipRoot, ledgerAppend.entry.eventId)
184
+ };
185
+ }
186
+
187
+ async function ensureWorkspaceStateRoot({ indexEngine, workspace, workspaceStateEntries }) {
188
+ if (workspace.stateRoot) return workspace.stateRoot;
189
+ const stateIndex = await indexEngine.createIndex(Object.values(asRecord(workspaceStateEntries)), { domain: "state" });
190
+ workspace.stateRoot = stateIndex.root;
191
+ return workspace.stateRoot;
192
+ }
193
+
194
+ export function createPactium({
195
+ dataDir = "",
196
+ userDataPath = "",
197
+ storage = null,
198
+ inMemory = false
199
+ } = {}) {
200
+ const resolvedStorage = storage || createStoragePort({ dataDir, userDataPath, inMemory });
201
+ const ledger = createLedgerTransparencyLog({ storage: resolvedStorage });
202
+ const indexEngine = createVerifiableIndexEngine({ storage: resolvedStorage, domain: "pactium" });
203
+ const repairPlanner = createRepairPlanner();
204
+ let state = null;
205
+ let mutationLane = Promise.resolve();
206
+ const useDurableWriteLock = !resolvedStorage.inMemory && typeof resolvedStorage.withWriteLock === "function";
207
+
208
+ async function reloadFromStorage() {
209
+ if (typeof resolvedStorage.clearCache === "function") resolvedStorage.clearCache();
210
+ if (typeof ledger.reload === "function") await ledger.reload();
211
+ state = null;
212
+ return ensureState();
213
+ }
214
+
215
+ async function prepareRead() {
216
+ await mutationLane.catch(() => null);
217
+ if (useDurableWriteLock) return reloadFromStorage();
218
+ return ensureState();
219
+ }
220
+
221
+ function enqueueMutation(task) {
222
+ const run = mutationLane.catch(() => null).then(() => {
223
+ if (!useDurableWriteLock) return task();
224
+ return resolvedStorage.withWriteLock(async () => {
225
+ await reloadFromStorage();
226
+ return task();
227
+ });
228
+ });
229
+ mutationLane = run;
230
+ return run;
231
+ }
232
+
233
+ async function ensureState() {
234
+ await resolvedStorage.initialize();
235
+ if (state) return state;
236
+ state = await resolvedStorage.getProtocolObject("core", "runtime-state", null);
237
+ if (!state) state = createEmptyCoreState();
238
+ state.intentIdempotencyClaims ||= {};
239
+ return state;
240
+ }
241
+
242
+ async function saveState() {
243
+ await resolvedStorage.putProtocolObject("core", "runtime-state", state);
244
+ }
245
+
246
+ async function createEnvelope({
247
+ envelopeKind,
248
+ fact,
249
+ ledgerAppend,
250
+ proofs = {},
251
+ appendCondition = null,
252
+ extensions = [],
253
+ replayed = false,
254
+ relatedEnvelopeIds = []
255
+ }) {
256
+ const material = {
257
+ protocol: PACTIUM_PROTOCOL,
258
+ materialType: "pactium.proof-material",
259
+ envelopeKind,
260
+ ledger: {
261
+ head: ledgerAppend.head,
262
+ previousHead: ledgerAppend.previousHead,
263
+ inclusionProof: ledgerAppend.inclusionProof,
264
+ consistencyProof: ledgerAppend.consistencyProof
265
+ },
266
+ appendCondition,
267
+ proofs
268
+ };
269
+ const materialRef = await createProofRef(resolvedStorage, "ledger-and-index-proofs", material);
270
+ const materializedExtensions = [];
271
+ for (const extension of extensions) {
272
+ const materialized = await materializeExtension(resolvedStorage, extension);
273
+ if (materialized) materializedExtensions.push(materialized);
274
+ }
275
+ const envelopeBase = {
276
+ protocol: PACTIUM_PROTOCOL,
277
+ schema: PACTIUM_SCHEMA_VERSION,
278
+ envelopeType: "pactium.proof-envelope",
279
+ envelopeKind,
280
+ factType: fact.factType,
281
+ factId: fact.intentId || fact.outcomeId || fact.repairId || ledgerAppend.entry.eventId,
282
+ factRef: {
283
+ ledgerEventId: ledgerAppend.entry.eventId,
284
+ ledgerIndex: ledgerAppend.entry.index,
285
+ factCid: ledgerAppend.entry.factCid,
286
+ factHash: ledgerAppend.entry.factHash
287
+ },
288
+ ledgerHead: ledgerAppend.head,
289
+ proofRefs: [materialRef],
290
+ extensions: materializedExtensions,
291
+ criticalExtensions: materializedExtensions.filter((extension) => extension.critical).map((extension) => extension.name),
292
+ relatedEnvelopeIds,
293
+ replayed,
294
+ createdAt: nowIso()
295
+ };
296
+ const envelope = finalizeEnvelope(envelopeBase);
297
+ await resolvedStorage.putBlock(envelope, {
298
+ kind: "proof-envelope",
299
+ refs: [
300
+ ...envelope.proofRefs.map((ref) => ref.cid),
301
+ ...envelope.extensions.map((extension) => extension.valueRef)
302
+ ]
303
+ });
304
+ const current = await ensureState();
305
+ current.envelopes[envelope.envelopeId] = envelope;
306
+ await saveState();
307
+ return envelope;
308
+ }
309
+
310
+ async function beginOperationIntent(input = {}) {
311
+ return enqueueMutation(() => beginOperationIntentCommitted(input));
312
+ }
313
+
314
+ async function beginOperationIntentCommitted(input = {}) {
315
+ const current = await ensureState();
316
+ const operationId = safeText(input.operationId);
317
+ if (!operationId) throw new Error("operationId is required for Operation Intent.");
318
+ const workspaceId = safeText(input.workspaceId || input.scope, "default");
319
+ const idempotencyKey = safeText(input.idempotencyKey);
320
+ const idKey = idempotencyKey ? idempotencyKeyFor({ ...input, operationId, workspaceId }) : "";
321
+ const idClaimKey = idempotencyKey ? intentIdempotencyClaimKeyFor({ operationId, workspaceId, idempotencyKey }) : "";
322
+ const inputHash = protocolHash("operation.intent", input.input ?? input.payload ?? {});
323
+ if (idKey && current.intentEnvelopes[idKey]) {
324
+ const envelope = { ...current.envelopes[current.intentEnvelopes[idKey]], replayed: true };
325
+ return envelope;
326
+ }
327
+ if (idClaimKey && current.intentIdempotencyClaims[idClaimKey] && current.intentIdempotencyClaims[idClaimKey].inputHash !== inputHash) {
328
+ throw new PactiumLifecycleError("Operation Intent idempotency key was reused with different input.", createVerificationFailure({
329
+ layer: "operation-lifecycle",
330
+ code: "idempotency_conflict",
331
+ message: "Intent idempotency key was reused with different input.",
332
+ evidenceRef: idClaimKey,
333
+ repairable: false
334
+ }));
335
+ }
336
+ const appendCondition = input.appendCondition
337
+ ? createAppendCondition({ workspaceId, ...asRecord(input.appendCondition) })
338
+ : null;
339
+ if (appendCondition) {
340
+ const requiredIntentId = appendCondition.requiredOpenIntentState?.intentId || "";
341
+ const openIntentState = requiredIntentId
342
+ ? {
343
+ intentId: requiredIntentId,
344
+ exists: current.intents[requiredIntentId]?.open === true
345
+ }
346
+ : { exists: false };
347
+ await assertAppendCondition(appendCondition, {
348
+ phase: "intent",
349
+ currentHead: await ledger.head(),
350
+ workspace: workspaceStateFor(current, workspaceId),
351
+ openIntentState,
352
+ knownCausalityRefs: knownCausalityRefsFor(current)
353
+ });
354
+ }
355
+ const intent = {
356
+ protocol: PACTIUM_PROTOCOL,
357
+ schema: PACTIUM_SCHEMA_VERSION,
358
+ factType: "operation.intent",
359
+ intentId: createId("operation_intent", {
360
+ operationId,
361
+ workspaceId,
362
+ idempotencyKey,
363
+ input: input.input ?? input.payload ?? {},
364
+ nonce: input.nonce || crypto.randomUUID()
365
+ }),
366
+ operationId,
367
+ workspaceId,
368
+ idempotencyKey,
369
+ inputHash,
370
+ subject: normalizeCanonicalValue(asRecord(input.subject)),
371
+ causalityRefs: asArray(input.causalityRefs).map(String),
372
+ appendConditionHash: appendCondition?.conditionHash || "",
373
+ createdAt: nowIso()
374
+ };
375
+ const ledgerAppend = await ledger.append(intent);
376
+ current.intents[intent.intentId] = {
377
+ intent,
378
+ ledgerEventId: ledgerAppend.entry.eventId,
379
+ open: true
380
+ };
381
+ current.indexRoots.openIntent = await applyIndexPut(
382
+ indexEngine,
383
+ current.indexRoots.openIntent,
384
+ intent.intentId,
385
+ eventRefValue(ledgerAppend),
386
+ "open-intent"
387
+ );
388
+ if (idKey) {
389
+ current.indexRoots.intentIdempotency = await applyIndexPut(
390
+ indexEngine,
391
+ current.indexRoots.intentIdempotency,
392
+ idKey,
393
+ lifecycleValueRef(intent.intentId, { intentId: intent.intentId }),
394
+ "intent-idempotency"
395
+ );
396
+ }
397
+ for (const ref of intent.causalityRefs) {
398
+ current.indexRoots.causality = await applyIndexPut(
399
+ indexEngine,
400
+ current.indexRoots.causality,
401
+ `${ref}\u0000${intent.intentId}`,
402
+ lifecycleValueRef(intent.intentId, { from: ref, to: intent.intentId, relation: "causes" }),
403
+ "operation-causality"
404
+ );
405
+ }
406
+ const projection = await updateWorkspaceProjection({ indexEngine, state: current, workspaceId, ledgerAppend });
407
+ const workspace = workspaceStateFor(current, workspaceId);
408
+ const checkpoints = checkpointEntriesFor(current, workspaceId);
409
+ const checkpointNodeId = createId("checkpoint_node", { intentId: intent.intentId, kind: "intent" });
410
+ const checkpointNode = {
411
+ checkpointNodeId,
412
+ checkpointKind: "intent",
413
+ parentId: "",
414
+ intentId: intent.intentId,
415
+ ledgerEventId: ledgerAppend.entry.eventId,
416
+ workspaceId
417
+ };
418
+ checkpoints[checkpointNodeId] = checkpointNode;
419
+ workspace.checkpointRoot = await applyIndexPut(
420
+ indexEngine,
421
+ workspace.checkpointRoot,
422
+ checkpointNodeId,
423
+ {
424
+ valueRef: `ref:${checkpointNodeId}`,
425
+ valueHash: protocolHash("checkpoint.node", checkpointNode),
426
+ metadata: checkpointNode
427
+ },
428
+ "checkpoint"
429
+ );
430
+ const envelope = await createEnvelope({
431
+ envelopeKind: "operation-intent",
432
+ fact: intent,
433
+ ledgerAppend,
434
+ proofs: {
435
+ openIntent: await indexEngine.prove(current.indexRoots.openIntent, intent.intentId),
436
+ intentIdempotency: idKey ? await indexEngine.prove(current.indexRoots.intentIdempotency, idKey) : null,
437
+ workspaceProjection: projection,
438
+ checkpoint: {
439
+ root: workspace.checkpointRoot,
440
+ proof: await indexEngine.prove(workspace.checkpointRoot, checkpointNodeId)
441
+ },
442
+ causality: {
443
+ root: current.indexRoots.causality,
444
+ proofs: await Promise.all(intent.causalityRefs.map((ref) =>
445
+ indexEngine.prove(current.indexRoots.causality, `${ref}\u0000${intent.intentId}`)
446
+ ))
447
+ }
448
+ },
449
+ appendCondition,
450
+ extensions: asArray(input.extensions),
451
+ replayed: false
452
+ });
453
+ if (idKey) current.intentEnvelopes[idKey] = envelope.envelopeId;
454
+ if (idClaimKey) current.intentIdempotencyClaims[idClaimKey] = { inputHash, envelopeId: envelope.envelopeId };
455
+ current.intents[intent.intentId].intentEnvelopeId = envelope.envelopeId;
456
+ await saveState();
457
+ return envelope;
458
+ }
459
+
460
+ async function appendOperationOutcome(input = {}) {
461
+ return enqueueMutation(() => appendOperationOutcomeCommitted(input));
462
+ }
463
+
464
+ async function appendOperationOutcomeCommitted(input = {}) {
465
+ const current = await ensureState();
466
+ const intentId = safeText(input.intentId);
467
+ if (!intentId) throw new Error("intentId is required for Operation Outcome.");
468
+ const intentRecord = current.intents[intentId];
469
+ if (!intentRecord) {
470
+ throw new PactiumLifecycleError("Operation Intent does not exist.", createVerificationFailure({
471
+ layer: "operation-lifecycle",
472
+ code: "intent_missing",
473
+ message: "Operation Outcome cannot be appended without a recorded Operation Intent.",
474
+ repairable: false
475
+ }));
476
+ }
477
+ const outcomeIdKey = safeText(input.outcomeIdempotencyKey || input.idempotencyKey)
478
+ ? outcomeIdempotencyKeyFor(input)
479
+ : "";
480
+ if (outcomeIdKey && current.outcomeEnvelopes[outcomeIdKey]) {
481
+ return { ...current.envelopes[current.outcomeEnvelopes[outcomeIdKey]], replayed: true };
482
+ }
483
+ if (current.outcomes[intentId]) {
484
+ throw new PactiumLifecycleError("Operation Intent already has a Terminal Outcome.", createVerificationFailure({
485
+ layer: "operation-lifecycle",
486
+ code: "terminal_outcome_exists",
487
+ message: "Pactium records exactly one Terminal Outcome per Operation Intent.",
488
+ evidenceRef: current.outcomes[intentId].outcomeId,
489
+ repairable: false
490
+ }));
491
+ }
492
+ const workspaceId = intentRecord.intent.workspaceId;
493
+ const appendCondition = input.appendCondition
494
+ ? createAppendCondition({ workspaceId, ...asRecord(input.appendCondition) })
495
+ : null;
496
+ if (appendCondition) {
497
+ await assertAppendCondition(appendCondition, {
498
+ phase: "outcome",
499
+ currentHead: await ledger.head(),
500
+ workspace: workspaceStateFor(current, workspaceId),
501
+ outcomeState: {
502
+ intentId,
503
+ exists: Boolean(current.outcomes[intentId]),
504
+ outcomeId: current.outcomes[intentId]?.outcomeId || ""
505
+ },
506
+ knownCausalityRefs: knownCausalityRefsFor(current)
507
+ });
508
+ }
509
+ const outcome = {
510
+ protocol: PACTIUM_PROTOCOL,
511
+ schema: PACTIUM_SCHEMA_VERSION,
512
+ factType: "operation.outcome",
513
+ outcomeId: createId("operation_outcome", {
514
+ intentId,
515
+ outcomeIdempotencyKey: outcomeIdKey,
516
+ status: input.status || "succeeded",
517
+ result: input.result ?? input.output ?? {},
518
+ nonce: input.nonce || crypto.randomUUID()
519
+ }),
520
+ intentId,
521
+ operationId: intentRecord.intent.operationId,
522
+ workspaceId,
523
+ status: safeText(input.status, "succeeded"),
524
+ resultHash: protocolHash("operation.outcome", input.result ?? input.output ?? {}),
525
+ hostEvidenceRefs: asArray(input.hostEvidenceRefs).map(String),
526
+ causalityRefs: asArray(input.causalityRefs).map(String),
527
+ appendConditionHash: appendCondition?.conditionHash || "",
528
+ createdAt: nowIso()
529
+ };
530
+ const ledgerAppend = await ledger.append(outcome);
531
+ current.outcomes[intentId] = outcome;
532
+ current.intents[intentId].open = false;
533
+ current.indexRoots.openIntent = await applyIndexDelete(indexEngine, current.indexRoots.openIntent, intentId, "open-intent");
534
+ current.indexRoots.outcome = await applyIndexPut(
535
+ indexEngine,
536
+ current.indexRoots.outcome,
537
+ intentId,
538
+ eventRefValue(ledgerAppend),
539
+ "outcome"
540
+ );
541
+ if (outcomeIdKey) {
542
+ current.indexRoots.outcomeIdempotency = await applyIndexPut(
543
+ indexEngine,
544
+ current.indexRoots.outcomeIdempotency,
545
+ outcomeIdKey,
546
+ lifecycleValueRef(outcome.outcomeId, { outcomeId: outcome.outcomeId }),
547
+ "outcome-idempotency"
548
+ );
549
+ }
550
+ for (const ref of outcome.causalityRefs) {
551
+ current.indexRoots.causality = await applyIndexPut(
552
+ indexEngine,
553
+ current.indexRoots.causality,
554
+ `${ref}\u0000${outcome.outcomeId}`,
555
+ lifecycleValueRef(outcome.outcomeId, { from: ref, to: outcome.outcomeId, relation: "causes" }),
556
+ "operation-causality"
557
+ );
558
+ }
559
+ const projection = await updateWorkspaceProjection({ indexEngine, state: current, workspaceId, ledgerAppend });
560
+ const workspace = workspaceStateFor(current, workspaceId);
561
+ const workspaceStateEntries = stateEntriesFor(current, workspaceId);
562
+ const mutations = asArray(input.stateMutations || input.state?.mutations);
563
+ const keyedMutations = mutations.filter((mutation) => mutation?.key);
564
+ await ensureWorkspaceStateRoot({ indexEngine, workspace, workspaceStateEntries });
565
+ for (const mutation of mutations) {
566
+ const key = String(mutation.key || "");
567
+ if (!key) continue;
568
+ if (mutation.action === "delete") {
569
+ delete workspaceStateEntries[key];
570
+ workspace.stateRoot = await applyIndexDelete(indexEngine, workspace.stateRoot, key, "state");
571
+ } else {
572
+ const valueBlock = mutation.valueRef
573
+ ? { cid: mutation.valueRef, payloadHash: mutation.valueHash || "" }
574
+ : await resolvedStorage.putBlock(mutation.value ?? mutation, { kind: "state-value" });
575
+ const stateEntry = {
576
+ key,
577
+ valueRef: valueBlock.cid,
578
+ valueHash: valueBlock.payloadHash || "",
579
+ metadata: normalizeCanonicalValue(asRecord(mutation.metadata))
580
+ };
581
+ workspaceStateEntries[key] = stateEntry;
582
+ workspace.stateRoot = await applyIndexPut(indexEngine, workspace.stateRoot, key, stateEntry, "state");
583
+ }
584
+ }
585
+ const stateRoot = workspace.stateRoot;
586
+ const stateCommit = {
587
+ protocol: PACTIUM_PROTOCOL,
588
+ schema: PACTIUM_SCHEMA_VERSION,
589
+ factType: "state.commit",
590
+ stateCommitId: createId("state_commit", {
591
+ outcomeId: outcome.outcomeId,
592
+ stateRoot,
593
+ mutations: keyedMutations
594
+ }),
595
+ outcomeId: outcome.outcomeId,
596
+ intentId,
597
+ workspaceId,
598
+ stateRoot,
599
+ mutationCount: keyedMutations.length,
600
+ mutationKeys: keyedMutations.map((mutation) => String(mutation.key || "")),
601
+ mutationActions: keyedMutations.map((mutation) => String(mutation.action || "put")),
602
+ touchedKeyCount: keyedMutations.slice(0, 32).length,
603
+ createdAt: nowIso()
604
+ };
605
+ const checkpointEntries = checkpointEntriesFor(current, workspaceId);
606
+ const outcomeCheckpointNodeId = createId("checkpoint_node", { outcomeId: outcome.outcomeId, kind: "outcome" });
607
+ const outcomeCheckpointNode = {
608
+ checkpointNodeId: outcomeCheckpointNodeId,
609
+ checkpointKind: "outcome",
610
+ parentId: createId("checkpoint_node", { intentId, kind: "intent" }),
611
+ intentId,
612
+ outcomeId: outcome.outcomeId,
613
+ stateCommitId: stateCommit.stateCommitId,
614
+ workspaceId,
615
+ ledgerEventId: ledgerAppend.entry.eventId
616
+ };
617
+ checkpointEntries[outcomeCheckpointNodeId] = outcomeCheckpointNode;
618
+ workspace.checkpointRoot = await applyIndexPut(
619
+ indexEngine,
620
+ workspace.checkpointRoot,
621
+ outcomeCheckpointNodeId,
622
+ {
623
+ valueRef: `ref:${outcomeCheckpointNodeId}`,
624
+ valueHash: protocolHash("checkpoint.node", outcomeCheckpointNode),
625
+ metadata: outcomeCheckpointNode
626
+ },
627
+ "checkpoint"
628
+ );
629
+ const touchedKeyProofs = [];
630
+ for (const mutation of keyedMutations.slice(0, 32)) {
631
+ touchedKeyProofs.push(await indexEngine.prove(stateRoot, String(mutation.key)));
632
+ }
633
+ const envelope = await createEnvelope({
634
+ envelopeKind: "operation-outcome",
635
+ fact: outcome,
636
+ ledgerAppend,
637
+ proofs: {
638
+ outcome: await indexEngine.prove(current.indexRoots.outcome, intentId),
639
+ openIntentRemoved: await indexEngine.prove(current.indexRoots.openIntent, intentId),
640
+ outcomeIdempotency: outcomeIdKey ? await indexEngine.prove(current.indexRoots.outcomeIdempotency, outcomeIdKey) : null,
641
+ workspaceProjection: projection,
642
+ stateCommit,
643
+ state: {
644
+ root: stateRoot,
645
+ touchedKeyProofs
646
+ },
647
+ checkpoint: {
648
+ root: workspace.checkpointRoot,
649
+ proof: await indexEngine.prove(workspace.checkpointRoot, outcomeCheckpointNodeId)
650
+ },
651
+ causality: {
652
+ root: current.indexRoots.causality,
653
+ proofs: await Promise.all(outcome.causalityRefs.map((ref) =>
654
+ indexEngine.prove(current.indexRoots.causality, `${ref}\u0000${outcome.outcomeId}`)
655
+ ))
656
+ }
657
+ },
658
+ appendCondition,
659
+ extensions: asArray(input.extensions),
660
+ replayed: false,
661
+ relatedEnvelopeIds: [intentRecord.intentEnvelopeId].filter(Boolean)
662
+ });
663
+ if (outcomeIdKey) current.outcomeEnvelopes[outcomeIdKey] = envelope.envelopeId;
664
+ await saveState();
665
+ return envelope;
666
+ }
667
+
668
+ async function recordOperation(input = {}) {
669
+ const intentEnvelope = await beginOperationIntent(input.intentAppendCondition
670
+ ? { ...input, appendCondition: input.intentAppendCondition }
671
+ : input);
672
+ if (intentEnvelope.replayed && input.returnIntentReplay) return intentEnvelope;
673
+ const intentId = intentEnvelope.factId;
674
+ return appendOperationOutcome({
675
+ ...input,
676
+ intentId,
677
+ appendCondition: input.outcomeAppendCondition || input.outcome?.appendCondition || null,
678
+ extensions: asArray(input.outcomeExtensions || input.extensions)
679
+ });
680
+ }
681
+
682
+ async function lookupOpenIntent(intentId) {
683
+ const current = await prepareRead();
684
+ const proof = await indexEngine.prove(current.indexRoots.openIntent, String(intentId || ""));
685
+ return {
686
+ protocol: PACTIUM_PROTOCOL,
687
+ intentId,
688
+ exists: isMembershipProof(proof),
689
+ proof,
690
+ intent: current.intents[intentId]?.intent || null
691
+ };
692
+ }
693
+
694
+ async function lookupOutcome(intentId) {
695
+ const current = await prepareRead();
696
+ const proof = await indexEngine.prove(current.indexRoots.outcome, String(intentId || ""));
697
+ return {
698
+ protocol: PACTIUM_PROTOCOL,
699
+ intentId,
700
+ exists: isMembershipProof(proof),
701
+ proof,
702
+ outcome: current.outcomes[intentId] || null
703
+ };
704
+ }
705
+
706
+ async function getWorkspaceProjection(workspaceId = "default") {
707
+ const current = await prepareRead();
708
+ const workspace = workspaceStateFor(current, workspaceId);
709
+ return {
710
+ protocol: PACTIUM_PROTOCOL,
711
+ workspaceId: safeText(workspaceId, "default"),
712
+ nextOrdinal: workspace.nextOrdinal,
713
+ orderRoot: workspace.orderRoot,
714
+ membershipRoot: workspace.membershipRoot,
715
+ order: workspace.orderRoot ? await indexEngine.scan(workspace.orderRoot, { limit: 100000 }) : [],
716
+ membership: workspace.membershipRoot ? await indexEngine.scan(workspace.membershipRoot, { limit: 100000 }) : []
717
+ };
718
+ }
719
+
720
+ async function proveWorkspaceMembership({ workspaceId = "default", ledgerEventId = "" } = {}) {
721
+ const current = await prepareRead();
722
+ const workspace = workspaceStateFor(current, workspaceId);
723
+ const proof = await indexEngine.prove(workspace.membershipRoot, ledgerEventId);
724
+ return {
725
+ protocol: PACTIUM_PROTOCOL,
726
+ workspaceId: safeText(workspaceId, "default"),
727
+ ledgerEventId,
728
+ member: isMembershipProof(proof),
729
+ proof
730
+ };
731
+ }
732
+
733
+ async function getLedgerCursor({ fromCursor = null, position = 0, limit = 100 } = {}) {
734
+ await prepareRead();
735
+ const start = Math.max(0, Number(fromCursor?.position ?? position ?? 0));
736
+ const pageLimit = Math.max(1, Math.min(Number(limit || 100), 10000));
737
+ const page = await ledger.pageEntries({ start, limit: pageLimit });
738
+ const currentHead = page.head;
739
+ const entries = page.entries;
740
+ const nextPosition = page.nextPosition;
741
+ const cursor = createTrackingCursor({
742
+ scope: "ledger",
743
+ position: nextPosition,
744
+ gaps: [],
745
+ headRef: currentHead.headId || currentHead.root || currentHead.rootHash
746
+ });
747
+ return {
748
+ protocol: PACTIUM_PROTOCOL,
749
+ pageType: "pactium.ledger-cursor-page",
750
+ entries,
751
+ cursor,
752
+ nextCursor: cursor,
753
+ head: currentHead
754
+ };
755
+ }
756
+
757
+ async function getWorkspaceCursor({ workspaceId = "default", fromCursor = null, position = 0, limit = 100 } = {}) {
758
+ const current = await prepareRead();
759
+ const workspace = workspaceStateFor(current, workspaceId);
760
+ const currentHead = await ledger.head();
761
+ const start = Math.max(0, Number(fromCursor?.position ?? position ?? 0));
762
+ const pageLimit = Math.max(1, Math.min(Number(limit || 100), 10000));
763
+ const entries = workspace.orderRoot
764
+ ? await indexEngine.scan(workspace.orderRoot, { min: padOrdinal(start), limit: pageLimit })
765
+ : [];
766
+ const nextPosition = entries.length > 0
767
+ ? Number.parseInt(entries[entries.length - 1].key, 10) + 1
768
+ : start;
769
+ const cursor = createTrackingCursor({
770
+ scope: "workspace",
771
+ workspaceId,
772
+ position: nextPosition,
773
+ gaps: [],
774
+ headRef: currentHead.headId || currentHead.root || currentHead.rootHash,
775
+ orderRoot: workspace.orderRoot
776
+ });
777
+ return {
778
+ protocol: PACTIUM_PROTOCOL,
779
+ pageType: "pactium.workspace-cursor-page",
780
+ workspaceId: safeText(workspaceId, "default"),
781
+ entries,
782
+ cursor,
783
+ nextCursor: cursor,
784
+ head: currentHead,
785
+ orderRoot: workspace.orderRoot,
786
+ orderProofs: await Promise.all(entries.map((entry) => indexEngine.prove(workspace.orderRoot, entry.key)))
787
+ };
788
+ }
789
+
790
+ function verifyCursor(cursor, context = {}) {
791
+ return verifyTrackingCursor(cursor, context);
792
+ }
793
+
794
+ function advanceTrustedHead(input = {}) {
795
+ return advanceTrustedLedgerHead(input);
796
+ }
797
+
798
+ function planRecovery(input = {}) {
799
+ return repairPlanner.planRecovery(input);
800
+ }
801
+
802
+ async function verifyEnvelope(envelope, options = {}) {
803
+ return verifyProofEnvelope(envelope, {
804
+ storage: resolvedStorage,
805
+ supportedCriticalExtensions: options.supportedCriticalExtensions || [],
806
+ proofVerifiers: options.proofVerifiers || {},
807
+ requireAllProofs: options.requireAllProofs !== false,
808
+ verifierManifest: options.verifierManifest || null,
809
+ ledgerHeadSignatures: options.ledgerHeadSignatures || []
810
+ });
811
+ }
812
+
813
+ function encodeVarint(value) {
814
+ const bytes = [];
815
+ let current = Number(value || 0);
816
+ do {
817
+ let byte = current & 0x7f;
818
+ current = Math.floor(current / 128);
819
+ if (current > 0) byte |= 0x80;
820
+ bytes.push(byte);
821
+ } while (current > 0);
822
+ return Buffer.from(bytes);
823
+ }
824
+
825
+ function indexedBundleRecords(blocks) {
826
+ let offset = 0;
827
+ const index = [];
828
+ const records = [];
829
+ for (const block of blocks) {
830
+ const header = {
831
+ protocol: block.protocol,
832
+ cid: block.cid,
833
+ codec: block.codec,
834
+ kind: block.kind,
835
+ refs: block.refs,
836
+ byteLength: block.byteLength,
837
+ payloadHash: block.payloadHash
838
+ };
839
+ const headerBytes = Buffer.from(canonicalEncode(header));
840
+ const payloadBytes = Buffer.from(String(block.payloadBase64 || ""), "base64");
841
+ const recordLength = headerBytes.length + payloadBytes.length;
842
+ const lengthBytes = encodeVarint(recordLength);
843
+ records.push(lengthBytes, headerBytes, payloadBytes);
844
+ index.push({
845
+ cid: block.cid,
846
+ offset,
847
+ recordLength,
848
+ headerLength: headerBytes.length,
849
+ byteLength: block.byteLength,
850
+ payloadHash: block.payloadHash,
851
+ codec: block.codec,
852
+ kind: block.kind,
853
+ refs: block.refs
854
+ });
855
+ offset += lengthBytes.length + recordLength;
856
+ }
857
+ return { index, binaryBase64: Buffer.concat(records).toString("base64"), byteLength: offset };
858
+ }
859
+
860
+ async function exportProofBundle(envelopeOrId, options = {}) {
861
+ return enqueueMutation(() => exportProofBundleCommitted(envelopeOrId, options));
862
+ }
863
+
864
+ async function exportProofBundleCommitted(envelopeOrId, options = {}) {
865
+ const current = await ensureState();
866
+ const envelope = typeof envelopeOrId === "string" ? current.envelopes[envelopeOrId] : envelopeOrId;
867
+ if (!envelope) throw new Error("Proof Envelope not found.");
868
+ const refs = [
869
+ ...asArray(envelope.proofRefs).map((ref) => ref.cid),
870
+ ...asArray(envelope.extensions).map((extension) => extension.valueRef)
871
+ ];
872
+ const blocks = [];
873
+ const seen = new Set();
874
+ for (const ref of refs) {
875
+ const walked = await resolvedStorage.walk(ref);
876
+ for (const block of walked.blocks) {
877
+ if (!seen.has(block.cid)) {
878
+ seen.add(block.cid);
879
+ blocks.push({
880
+ protocol: block.protocol,
881
+ cid: block.cid,
882
+ codec: block.codec,
883
+ kind: block.kind,
884
+ refs: block.refs,
885
+ byteLength: block.byteLength,
886
+ payloadHash: block.payloadHash,
887
+ payloadBase64: block.payloadBase64
888
+ });
889
+ }
890
+ }
891
+ }
892
+ if (options.format && options.format !== "indexed") {
893
+ throw new Error(`Unsupported proof bundle format: ${options.format}`);
894
+ }
895
+ const manifest = {
896
+ protocol: PACTIUM_PROTOCOL,
897
+ schema: PACTIUM_SCHEMA_VERSION,
898
+ bundleType: PACTIUM_PROOF_BUNDLE_TYPE,
899
+ envelopeId: envelope.envelopeId,
900
+ ledgerHead: envelope.ledgerHead,
901
+ blockCount: blocks.length,
902
+ requiredBlocks: blocks.map((block) => block.cid),
903
+ criticalExtensions: envelope.criticalExtensions,
904
+ createdAt: nowIso()
905
+ };
906
+ const indexed = indexedBundleRecords(blocks);
907
+ const bundle = {
908
+ protocol: PACTIUM_PROTOCOL,
909
+ schema: PACTIUM_SCHEMA_VERSION,
910
+ bundleType: PACTIUM_PROOF_BUNDLE_TYPE,
911
+ manifest,
912
+ envelope,
913
+ index: indexed.index,
914
+ blocksEncoding: PACTIUM_BUNDLE_ENCODING,
915
+ binaryBase64: indexed.binaryBase64,
916
+ byteLength: indexed.byteLength,
917
+ bundleHash: protocolHash("proof.bundle", {
918
+ manifest,
919
+ envelope,
920
+ index: indexed.index.map((item) => ({
921
+ cid: item.cid,
922
+ offset: item.offset,
923
+ recordLength: item.recordLength,
924
+ headerLength: item.headerLength,
925
+ byteLength: item.byteLength,
926
+ payloadHash: item.payloadHash
927
+ }))
928
+ })
929
+ };
930
+ current.proofBundles[envelope.envelopeId] = bundle;
931
+ await saveState();
932
+ return bundle;
933
+ }
934
+
935
+ async function createExtension(extension) {
936
+ return enqueueMutation(() => materializeExtension(resolvedStorage, extension));
937
+ }
938
+
939
+ async function storeEnvelope(envelope) {
940
+ return enqueueMutation(() => storeEnvelopeCommitted(envelope));
941
+ }
942
+
943
+ async function storeEnvelopeCommitted(envelope) {
944
+ const current = await ensureState();
945
+ const finalized = finalizeEnvelope(envelope);
946
+ await resolvedStorage.putBlock(finalized, {
947
+ kind: "proof-envelope",
948
+ refs: [
949
+ ...asArray(finalized.proofRefs).map((ref) => ref.cid),
950
+ ...asArray(finalized.extensions).map((extension) => extension.valueRef)
951
+ ]
952
+ });
953
+ current.envelopes[finalized.envelopeId] = finalized;
954
+ await saveState();
955
+ return finalized;
956
+ }
957
+
958
+ async function protocolCatalog() {
959
+ return {
960
+ protocol: PACTIUM_PROTOCOL,
961
+ schema: PACTIUM_SCHEMA_VERSION,
962
+ name: "Pactium",
963
+ packageName: "pactium",
964
+ rootExport: "latest-proof-first-only",
965
+ capabilities: [
966
+ "canonical-value",
967
+ "protocol-hash",
968
+ "storage-port",
969
+ "ledger-transparency-log",
970
+ "verifiable-index-engine",
971
+ "operation-lifecycle",
972
+ "append-condition",
973
+ "tracking-cursor",
974
+ "trusted-head-advancement",
975
+ "workspace-projection",
976
+ "merkle-state",
977
+ "checkpoint-tree",
978
+ "proof-envelope",
979
+ "proof-bundle",
980
+ "maintenance-task-engine",
981
+ "repair-planner"
982
+ ]
983
+ };
984
+ }
985
+
986
+ async function doctor() {
987
+ await prepareRead();
988
+ return {
989
+ protocol: PACTIUM_PROTOCOL,
990
+ schema: PACTIUM_SCHEMA_VERSION,
991
+ ok: true,
992
+ dataDir: resolvedStorage.dataDir,
993
+ latestSchemaOnly: true,
994
+ historicalMigration: false,
995
+ catalog: await protocolCatalog()
996
+ };
997
+ }
998
+
999
+ async function compactInMemoryCaches() {
1000
+ const current = await ensureState();
1001
+ if (!resolvedStorage.inMemory) {
1002
+ return {
1003
+ protocol: PACTIUM_PROTOCOL,
1004
+ inMemory: false,
1005
+ retainedRoots: 0,
1006
+ retainedNodeRoots: 0,
1007
+ prunedBlocks: 0,
1008
+ prunedProtocolObjects: 0
1009
+ };
1010
+ }
1011
+ const retainedRoots = currentIndexRoots(current);
1012
+ const cache = typeof indexEngine.pruneCache === "function"
1013
+ ? await indexEngine.pruneCache({ roots: retainedRoots })
1014
+ : { retainedRoots, retainedNodeRoots: retainedRoots };
1015
+ const retainedNodeRoots = new Set(asArray(cache.retainedNodeRoots || retainedRoots).map(String));
1016
+ const retainedRootHashes = new Set([
1017
+ ...asArray(cache.retainedRoots || retainedRoots),
1018
+ ...retainedNodeRoots
1019
+ ].map(hashFromCid).filter(Boolean));
1020
+ const prunedBlocks = typeof resolvedStorage.pruneBlocks === "function"
1021
+ ? resolvedStorage.pruneBlocks((block) => {
1022
+ const kind = String(block.kind || "");
1023
+ if (kind.startsWith("index-node:")) return !retainedNodeRoots.has(block.cid);
1024
+ return kind !== "state-value";
1025
+ })
1026
+ : 0;
1027
+ const prunedProtocolObjects = typeof resolvedStorage.pruneProtocolObjects === "function"
1028
+ ? resolvedStorage.pruneProtocolObjects((object) =>
1029
+ object.scope === "index" && !retainedRootHashes.has(String(object.key || "").split("-").pop())
1030
+ )
1031
+ : 0;
1032
+ return {
1033
+ protocol: PACTIUM_PROTOCOL,
1034
+ inMemory: true,
1035
+ retainedRoots: retainedRoots.length,
1036
+ retainedNodeRoots: retainedNodeRoots.size,
1037
+ prunedNodes: Number(cache.prunedNodes || 0),
1038
+ prunedRoots: Number(cache.prunedRoots || 0),
1039
+ prunedSnapshots: Number(cache.prunedSnapshots || 0),
1040
+ prunedBlocks,
1041
+ prunedProtocolObjects
1042
+ };
1043
+ }
1044
+
1045
+ return Object.freeze({
1046
+ protocol: PACTIUM_PROTOCOL,
1047
+ schema: PACTIUM_SCHEMA_VERSION,
1048
+ dataDir: resolvedStorage.dataDir,
1049
+ storage: resolvedStorage,
1050
+ ledger,
1051
+ indexEngine,
1052
+ beginOperationIntent,
1053
+ appendOperationOutcome,
1054
+ recordOperation,
1055
+ lookupOpenIntent,
1056
+ lookupOutcome,
1057
+ createAppendCondition,
1058
+ getLedgerCursor,
1059
+ getWorkspaceCursor,
1060
+ verifyCursor,
1061
+ advanceTrustedHead,
1062
+ planRecovery,
1063
+ getWorkspaceProjection,
1064
+ proveWorkspaceMembership,
1065
+ verifyEnvelope,
1066
+ exportProofBundle,
1067
+ createExtension,
1068
+ storeEnvelope,
1069
+ protocolCatalog,
1070
+ doctor,
1071
+ _compactInMemoryCaches: compactInMemoryCaches
1072
+ });
1073
+ }