@superbee/core 0.2.0-pre.16 → 0.2.0-pre.19
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 +622 -5
- package/dist/blob-journal.d.ts +187 -2
- package/dist/blob-journal.d.ts.map +1 -1
- package/dist/blob-journal.js +377 -5
- package/dist/blob-journal.js.map +1 -1
- package/dist/body-replace-guards.d.ts +50 -0
- package/dist/body-replace-guards.d.ts.map +1 -0
- package/dist/body-replace-guards.js +158 -0
- package/dist/body-replace-guards.js.map +1 -0
- package/dist/builtin-recipes/content.d.ts +93 -0
- package/dist/builtin-recipes/content.d.ts.map +1 -0
- package/dist/builtin-recipes/content.js +288 -0
- package/dist/builtin-recipes/content.js.map +1 -0
- package/dist/builtin-recipes.d.ts +10 -0
- package/dist/builtin-recipes.d.ts.map +1 -0
- package/dist/builtin-recipes.js +97 -0
- package/dist/builtin-recipes.js.map +1 -0
- package/dist/bundle-health/index.d.ts +14 -0
- package/dist/bundle-health/index.d.ts.map +1 -0
- package/dist/bundle-health/index.js +12 -0
- package/dist/bundle-health/index.js.map +1 -0
- package/dist/bundle-health/kind-findings.d.ts +44 -0
- package/dist/bundle-health/kind-findings.d.ts.map +1 -0
- package/dist/bundle-health/kind-findings.js +68 -0
- package/dist/bundle-health/kind-findings.js.map +1 -0
- package/dist/bundle-health/legacy-page.d.ts +39 -0
- package/dist/bundle-health/legacy-page.d.ts.map +1 -0
- package/dist/bundle-health/legacy-page.js +50 -0
- package/dist/bundle-health/legacy-page.js.map +1 -0
- package/dist/bundle-health/report.d.ts +202 -0
- package/dist/bundle-health/report.d.ts.map +1 -0
- package/dist/bundle-health/report.js +438 -0
- package/dist/bundle-health/report.js.map +1 -0
- package/dist/code-point-order.d.ts +8 -0
- package/dist/code-point-order.d.ts.map +1 -0
- package/dist/code-point-order.js +20 -0
- package/dist/code-point-order.js.map +1 -0
- package/dist/connectors.d.ts +223 -0
- package/dist/connectors.d.ts.map +1 -0
- package/dist/connectors.js +1143 -0
- package/dist/connectors.js.map +1 -0
- package/dist/document-field-actions.d.ts +18 -0
- package/dist/document-field-actions.d.ts.map +1 -1
- package/dist/document-field-actions.js +91 -29
- package/dist/document-field-actions.js.map +1 -1
- package/dist/document-mutation.d.ts +7 -9
- package/dist/document-mutation.d.ts.map +1 -1
- package/dist/document-mutation.js +16 -16
- package/dist/document-mutation.js.map +1 -1
- package/dist/file-journaled-backend.d.ts +31 -5
- package/dist/file-journaled-backend.d.ts.map +1 -1
- package/dist/file-journaled-backend.js +131 -118
- package/dist/file-journaled-backend.js.map +1 -1
- package/dist/hosted-client/blob-delivery.d.ts +6 -0
- package/dist/hosted-client/blob-delivery.d.ts.map +1 -1
- package/dist/hosted-client/blob-delivery.js +23 -1
- package/dist/hosted-client/blob-delivery.js.map +1 -1
- package/dist/hosted-client/blob-write.d.ts +386 -0
- package/dist/hosted-client/blob-write.d.ts.map +1 -0
- package/dist/hosted-client/blob-write.js +770 -0
- package/dist/hosted-client/blob-write.js.map +1 -0
- package/dist/hosted-client/client.d.ts +64 -0
- package/dist/hosted-client/client.d.ts.map +1 -1
- package/dist/hosted-client/client.js +31 -0
- package/dist/hosted-client/client.js.map +1 -1
- package/dist/hosted-client/directory.d.ts +46 -0
- package/dist/hosted-client/directory.d.ts.map +1 -0
- package/dist/hosted-client/directory.js +74 -0
- package/dist/hosted-client/directory.js.map +1 -0
- package/dist/hosted-client/errors.d.ts +10 -0
- package/dist/hosted-client/errors.d.ts.map +1 -1
- package/dist/hosted-client/errors.js +13 -0
- package/dist/hosted-client/errors.js.map +1 -1
- package/dist/hosted-client/inbox.d.ts +320 -0
- package/dist/hosted-client/inbox.d.ts.map +1 -0
- package/dist/hosted-client/inbox.js +379 -0
- package/dist/hosted-client/inbox.js.map +1 -0
- package/dist/hosted-client/index.d.ts +11 -4
- package/dist/hosted-client/index.d.ts.map +1 -1
- package/dist/hosted-client/index.js +11 -4
- package/dist/hosted-client/index.js.map +1 -1
- package/dist/hosted-client/journal.d.ts +103 -0
- package/dist/hosted-client/journal.d.ts.map +1 -0
- package/dist/hosted-client/journal.js +464 -0
- package/dist/hosted-client/journal.js.map +1 -0
- package/dist/hosted-client/node.d.ts +5 -1
- package/dist/hosted-client/node.d.ts.map +1 -1
- package/dist/hosted-client/node.js +6 -1
- package/dist/hosted-client/node.js.map +1 -1
- package/dist/hosted-client/write.d.ts +28 -2
- package/dist/hosted-client/write.d.ts.map +1 -1
- package/dist/hosted-client/write.js +33 -11
- package/dist/hosted-client/write.js.map +1 -1
- package/dist/hosted-transport/answer-rows.d.ts +3 -2
- package/dist/hosted-transport/answer-rows.d.ts.map +1 -1
- package/dist/hosted-transport/answer-rows.js +1 -1
- package/dist/hosted-transport/answer-rows.js.map +1 -1
- package/dist/hosted-transport/carrier.d.ts +10 -0
- package/dist/hosted-transport/carrier.d.ts.map +1 -1
- package/dist/hosted-transport/carrier.js +22 -2
- package/dist/hosted-transport/carrier.js.map +1 -1
- package/dist/hosted-transport/whole-document-transport.d.ts +1 -1
- package/dist/hosted-transport/whole-document-transport.d.ts.map +1 -1
- package/dist/hosted-transport/whole-document-transport.js +20 -12
- package/dist/hosted-transport/whole-document-transport.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/kind-authoring.d.ts +170 -0
- package/dist/kind-authoring.d.ts.map +1 -0
- package/dist/kind-authoring.js +451 -0
- package/dist/kind-authoring.js.map +1 -0
- package/dist/kind-conformance-error.d.ts +15 -0
- package/dist/kind-conformance-error.d.ts.map +1 -0
- package/dist/kind-conformance-error.js +20 -0
- package/dist/kind-conformance-error.js.map +1 -0
- package/dist/kinds-load.js +4 -4
- package/dist/kinds-load.js.map +1 -1
- package/dist/kinds.d.ts +2 -0
- package/dist/kinds.d.ts.map +1 -1
- package/dist/kinds.js +10 -2
- package/dist/kinds.js.map +1 -1
- package/dist/paths.d.ts +16 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +25 -0
- package/dist/paths.js.map +1 -1
- package/dist/query-filter.d.ts +9 -0
- package/dist/query-filter.d.ts.map +1 -1
- package/dist/query-filter.js +19 -3
- package/dist/query-filter.js.map +1 -1
- package/dist/recipe-lifecycle/stamp.d.ts +11 -0
- package/dist/recipe-lifecycle/stamp.d.ts.map +1 -1
- package/dist/recipe-lifecycle/stamp.js +6 -0
- package/dist/recipe-lifecycle/stamp.js.map +1 -1
- package/dist/record-overlay/apply.d.ts +43 -0
- package/dist/record-overlay/apply.d.ts.map +1 -0
- package/dist/record-overlay/apply.js +527 -0
- package/dist/record-overlay/apply.js.map +1 -0
- package/dist/record-overlay/canonical.d.ts +22 -0
- package/dist/record-overlay/canonical.d.ts.map +1 -0
- package/dist/record-overlay/canonical.js +47 -0
- package/dist/record-overlay/canonical.js.map +1 -0
- package/dist/record-overlay/digest.d.ts +4 -0
- package/dist/record-overlay/digest.d.ts.map +1 -0
- package/dist/record-overlay/digest.js +11 -0
- package/dist/record-overlay/digest.js.map +1 -0
- package/dist/record-overlay/errors.d.ts +44 -0
- package/dist/record-overlay/errors.d.ts.map +1 -0
- package/dist/record-overlay/errors.js +55 -0
- package/dist/record-overlay/errors.js.map +1 -0
- package/dist/record-overlay/findings.d.ts +5 -0
- package/dist/record-overlay/findings.d.ts.map +1 -0
- package/dist/record-overlay/findings.js +83 -0
- package/dist/record-overlay/findings.js.map +1 -0
- package/dist/record-overlay/index.d.ts +23 -0
- package/dist/record-overlay/index.d.ts.map +1 -0
- package/dist/record-overlay/index.js +21 -0
- package/dist/record-overlay/index.js.map +1 -0
- package/dist/record-overlay/plan.d.ts +12 -0
- package/dist/record-overlay/plan.d.ts.map +1 -0
- package/dist/record-overlay/plan.js +285 -0
- package/dist/record-overlay/plan.js.map +1 -0
- package/dist/record-overlay/policy.d.ts +69 -0
- package/dist/record-overlay/policy.d.ts.map +1 -0
- package/dist/record-overlay/policy.js +228 -0
- package/dist/record-overlay/policy.js.map +1 -0
- package/dist/record-overlay/redact.d.ts +3 -0
- package/dist/record-overlay/redact.d.ts.map +1 -0
- package/dist/record-overlay/redact.js +27 -0
- package/dist/record-overlay/redact.js.map +1 -0
- package/dist/record-overlay/signal-kind.d.ts +30 -0
- package/dist/record-overlay/signal-kind.d.ts.map +1 -0
- package/dist/record-overlay/signal-kind.js +38 -0
- package/dist/record-overlay/signal-kind.js.map +1 -0
- package/dist/record-overlay/signal-text.d.ts +45 -0
- package/dist/record-overlay/signal-text.d.ts.map +1 -0
- package/dist/record-overlay/signal-text.js +116 -0
- package/dist/record-overlay/signal-text.js.map +1 -0
- package/dist/record-overlay/source-index.d.ts +19 -0
- package/dist/record-overlay/source-index.d.ts.map +1 -0
- package/dist/record-overlay/source-index.js +102 -0
- package/dist/record-overlay/source-index.js.map +1 -0
- package/dist/record-overlay/types.d.ts +291 -0
- package/dist/record-overlay/types.d.ts.map +1 -0
- package/dist/record-overlay/types.js +2 -0
- package/dist/record-overlay/types.js.map +1 -0
- package/dist/remote-operations.js +5 -2
- package/dist/remote-operations.js.map +1 -1
- package/dist/search/contract.d.ts +151 -0
- package/dist/search/contract.d.ts.map +1 -0
- package/dist/search/contract.js +78 -0
- package/dist/search/contract.js.map +1 -0
- package/dist/search/errors.d.ts +30 -0
- package/dist/search/errors.d.ts.map +1 -0
- package/dist/search/errors.js +50 -0
- package/dist/search/errors.js.map +1 -0
- package/dist/search/identity.d.ts +12 -0
- package/dist/search/identity.d.ts.map +1 -0
- package/dist/search/identity.js +90 -0
- package/dist/search/identity.js.map +1 -0
- package/dist/search/index.d.ts +15 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +14 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/memory-source.d.ts +25 -0
- package/dist/search/memory-source.d.ts.map +1 -0
- package/dist/search/memory-source.js +230 -0
- package/dist/search/memory-source.js.map +1 -0
- package/dist/search/rank.d.ts +34 -0
- package/dist/search/rank.d.ts.map +1 -0
- package/dist/search/rank.js +129 -0
- package/dist/search/rank.js.map +1 -0
- package/dist/search/row-text.d.ts +38 -0
- package/dist/search/row-text.d.ts.map +1 -0
- package/dist/search/row-text.js +88 -0
- package/dist/search/row-text.js.map +1 -0
- package/dist/search/search.d.ts +28 -0
- package/dist/search/search.d.ts.map +1 -0
- package/dist/search/search.js +240 -0
- package/dist/search/search.js.map +1 -0
- package/dist/search/source.d.ts +67 -0
- package/dist/search/source.d.ts.map +1 -0
- package/dist/search/source.js +2 -0
- package/dist/search/source.js.map +1 -0
- package/dist/search/text.d.ts +47 -0
- package/dist/search/text.d.ts.map +1 -0
- package/dist/search/text.js +148 -0
- package/dist/search/text.js.map +1 -0
- package/dist/source-engine/guard.d.ts +82 -0
- package/dist/source-engine/guard.d.ts.map +1 -0
- package/dist/source-engine/guard.js +396 -0
- package/dist/source-engine/guard.js.map +1 -0
- package/dist/source-engine/index.d.ts +24 -0
- package/dist/source-engine/index.d.ts.map +1 -0
- package/dist/source-engine/index.js +20 -0
- package/dist/source-engine/index.js.map +1 -0
- package/dist/source-engine/machine-state.d.ts +74 -0
- package/dist/source-engine/machine-state.d.ts.map +1 -0
- package/dist/source-engine/machine-state.js +48 -0
- package/dist/source-engine/machine-state.js.map +1 -0
- package/dist/source-engine/observe.d.ts +18 -0
- package/dist/source-engine/observe.d.ts.map +1 -0
- package/dist/source-engine/observe.js +598 -0
- package/dist/source-engine/observe.js.map +1 -0
- package/dist/source-engine/robots.d.ts +53 -0
- package/dist/source-engine/robots.d.ts.map +1 -0
- package/dist/source-engine/robots.js +158 -0
- package/dist/source-engine/robots.js.map +1 -0
- package/dist/source-engine/types.d.ts +285 -0
- package/dist/source-engine/types.d.ts.map +1 -0
- package/dist/source-engine/types.js +2 -0
- package/dist/source-engine/types.js.map +1 -0
- package/dist/source-harness/conformance.d.ts +16 -0
- package/dist/source-harness/conformance.d.ts.map +1 -0
- package/dist/source-harness/conformance.js +265 -0
- package/dist/source-harness/conformance.js.map +1 -0
- package/dist/source-harness/fixture.d.ts +95 -0
- package/dist/source-harness/fixture.d.ts.map +1 -0
- package/dist/source-harness/fixture.js +261 -0
- package/dist/source-harness/fixture.js.map +1 -0
- package/dist/source-harness/index.d.ts +22 -0
- package/dist/source-harness/index.d.ts.map +1 -0
- package/dist/source-harness/index.js +19 -0
- package/dist/source-harness/index.js.map +1 -0
- package/dist/source-harness/peer.d.ts +74 -0
- package/dist/source-harness/peer.d.ts.map +1 -0
- package/dist/source-harness/peer.js +106 -0
- package/dist/source-harness/peer.js.map +1 -0
- package/dist/storage.d.ts +2 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +1 -1
- package/dist/storage.js.map +1 -1
- package/dist/uncertain-write.d.ts +39 -6
- package/dist/uncertain-write.d.ts.map +1 -1
- package/dist/uncertain-write.js +25 -3
- package/dist/uncertain-write.js.map +1 -1
- package/package.json +33 -1
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The API is experimental (`0.2.0-pre`) and may change before a stable release. The on-disk
|
|
3
|
+
* format is a supported compatibility promise, because the hosted client's durable journal
|
|
4
|
+
* (`openHostedClientJournal`) keeps app state in it: a format bump ships a reader for the older
|
|
5
|
+
* layout, or a migration.
|
|
3
6
|
*
|
|
4
7
|
* A Node {@link JournaledBackend}: the private store behind a CLI working copy, proven by the
|
|
5
8
|
* same contract kit as the IndexedDB adapter. It is an append-only, checksummed transaction log
|
|
@@ -35,24 +38,30 @@
|
|
|
35
38
|
* entry of every file the store created. On macOS `fsync` does not force the drive's own cache
|
|
36
39
|
* (Node exposes no `F_FULLFSYNC`), which is the same promise every Node program gets there.
|
|
37
40
|
*/
|
|
41
|
+
var _a;
|
|
38
42
|
import { promises as fs } from "node:fs";
|
|
39
43
|
import path from "node:path";
|
|
40
44
|
import { assertBlobKey } from "./blob-contract.js";
|
|
41
|
-
import { BLOB_JOURNAL_BOUNDS,
|
|
45
|
+
import { BLOB_JOURNAL_BOUNDS, captureBlobAdmission, captureBlobForget, captureBlobIntentRecord, captureBlobMetaWrite, captureBlobRetirement, captureBlobUpdate, decideBlobAdmission, decideBlobForget, decideBlobMetaWrite, decideBlobRetirement, decideBlobUpdate, readBlobJournal, } from "./blob-journal.js";
|
|
42
46
|
import { resolveContentType } from "./content-type.js";
|
|
43
47
|
import { OPEN_LOG, STORE_RAW, STORE_BYTE_LIMIT } from "./file-journaled-backend-internal.js";
|
|
44
48
|
import { captureFilesystemHostPolicy } from "./filesystem-host.js";
|
|
45
49
|
import { acquireFilesystemMutationLock } from "./filesystem-lock.js";
|
|
46
50
|
import { MalformedDocumentError, parseMarkdown, stringifyDoc } from "./frontmatter.js";
|
|
47
|
-
import { assertDeletionIntent, assertJournalGuard, assertJournalIntentChanges, assertJournalMetaChanges, assertJournalResolutionOptions, assertJournalSnapshot, assertMetaWrite,
|
|
51
|
+
import { assertDeletionIntent, assertJournalGuard, assertJournalIntentChanges, assertJournalMetaChanges, assertJournalResolutionOptions, assertJournalSnapshot, assertMetaWrite, captureIntentUpdate, captureJournalDeleteOptions, captureJournalGuardOption, captureJournalValue, captureJournalWriteOptions, captureMetaWrite, captureRetireAcknowledged, deletionIntentRecord, IntentHoldConflict, IntentStateConflict, JournalGuardConflict, JournalSnapshotConflict, } from "./journaled-backend.js";
|
|
48
52
|
import { mutationActorFromFrontmatter } from "./mutation-attribution.js";
|
|
49
53
|
import { assertSafeBlobKey, assertSafeConceptId, assertSafeReservedDir, assertSafeReservedFilename, compareStorageKeys, pathFromConceptId } from "./paths.js";
|
|
50
54
|
import { parseLeadingFrontmatter } from "./portable-frontmatter.js";
|
|
51
55
|
import { sha256HexOfBytes } from "./sha256.js";
|
|
52
56
|
import { blobVersion, defaultActor, VersionConflict, versionOfBytes } from "./versioning.js";
|
|
53
57
|
// ── on-disk format ─────────────────────────────────────────────────────────────────────────
|
|
54
|
-
/**
|
|
55
|
-
|
|
58
|
+
/**
|
|
59
|
+
* The newest layout this adapter writes and reads; it reads every earlier one too. A store stays
|
|
60
|
+
* at format 1 until it holds a blob intent (format 2), and at 2 until a blob intent carries
|
|
61
|
+
* not-dispatched evidence (format 3); the store keeps the highest format it has written. Bumping
|
|
62
|
+
* this requires a reader for the older layouts; open refuses any newer format.
|
|
63
|
+
*/
|
|
64
|
+
export const FILE_JOURNAL_FORMAT_VERSION = 3;
|
|
56
65
|
export const FILE_JOURNAL_LOG = "store.log";
|
|
57
66
|
export const FILE_JOURNAL_SNAPSHOT = "store.snapshot";
|
|
58
67
|
const SNAPSHOT_TEMP = "store.snapshot.tmp";
|
|
@@ -128,7 +137,7 @@ export class FileJournalFormatError extends FileJournalCorruptError {
|
|
|
128
137
|
name = "FileJournalFormatError";
|
|
129
138
|
format;
|
|
130
139
|
constructor(file, format, offset) {
|
|
131
|
-
super(file, `written in store format ${format}; this adapter reads formats 1
|
|
140
|
+
super(file, `written in store format ${format}; this adapter reads formats 1 to ${FILE_JOURNAL_FORMAT_VERSION}`, offset);
|
|
132
141
|
this.format = format;
|
|
133
142
|
}
|
|
134
143
|
}
|
|
@@ -355,7 +364,7 @@ function scanLog(bytes, file) {
|
|
|
355
364
|
throw new FileJournalCorruptError(file, "a damaged record is followed by a whole one", at);
|
|
356
365
|
return { records, validBytes: at, totalBytes: bytes.byteLength };
|
|
357
366
|
}
|
|
358
|
-
if (found.format
|
|
367
|
+
if (!readableFormat(found.format))
|
|
359
368
|
throw new FileJournalFormatError(file, found.format, at);
|
|
360
369
|
let record;
|
|
361
370
|
try {
|
|
@@ -366,7 +375,7 @@ function scanLog(bytes, file) {
|
|
|
366
375
|
}
|
|
367
376
|
if (!record || !Number.isSafeInteger(record.txn) || !Array.isArray(record.changes))
|
|
368
377
|
throw new FileJournalCorruptError(file, "record has no transaction number or changes", at);
|
|
369
|
-
if ((found.format
|
|
378
|
+
if ((found.format >= 2 ? record.format !== found.format : record.format !== undefined && record.format !== 1))
|
|
370
379
|
throw new FileJournalCorruptError(file, "record format does not match its frame", at);
|
|
371
380
|
validateIntentChanges(record.changes, found.format, file);
|
|
372
381
|
record.format = found.format;
|
|
@@ -381,7 +390,7 @@ function decodeSnapshot(bytes, file) {
|
|
|
381
390
|
const found = frameAt(bytes, 0, SNAPSHOT_MAGIC);
|
|
382
391
|
if (!found || found.end !== bytes.byteLength)
|
|
383
392
|
throw new FileJournalCorruptError(file, "snapshot checksum or length does not match");
|
|
384
|
-
if (found.format
|
|
393
|
+
if (!readableFormat(found.format))
|
|
385
394
|
throw new FileJournalFormatError(file, found.format);
|
|
386
395
|
let decoded;
|
|
387
396
|
try {
|
|
@@ -397,14 +406,19 @@ function decodeSnapshot(bytes, file) {
|
|
|
397
406
|
validateIntentChanges(decoded.intents.map(([key, value]) => ({ family: "intent", key, value })), found.format, file);
|
|
398
407
|
return decoded;
|
|
399
408
|
}
|
|
409
|
+
const readableFormat = (format) => Number.isSafeInteger(format) && format >= 1 && format <= FILE_JOURNAL_FORMAT_VERSION;
|
|
410
|
+
/** The layout a blob intent needs: not-dispatched evidence first appeared in format 3. */
|
|
411
|
+
const blobIntentFormat = (row) => ("notDispatched" in row ? 3 : 2);
|
|
400
412
|
function validateIntentChanges(changes, format, file) {
|
|
401
413
|
for (const change of changes) {
|
|
402
414
|
if (change.family !== "intent" || change.value === null)
|
|
403
415
|
continue;
|
|
404
416
|
const row = change.value;
|
|
405
417
|
if ("recordFamily" in row) {
|
|
406
|
-
if (row.recordFamily !== "blob" || format
|
|
418
|
+
if (row.recordFamily !== "blob" || format < 2)
|
|
407
419
|
throw new FileJournalCorruptError(file, "blob intent is not allowed in this format");
|
|
420
|
+
if (blobIntentFormat(row) > format)
|
|
421
|
+
throw new FileJournalCorruptError(file, "blob not-dispatched evidence is not allowed in this format");
|
|
408
422
|
try {
|
|
409
423
|
captureBlobIntentRecord(row);
|
|
410
424
|
assertReceiptBound(row);
|
|
@@ -440,7 +454,7 @@ function settlementReserve(state, txn) {
|
|
|
440
454
|
// replay. Unknown/nonterminal evidence can never release this allocation.
|
|
441
455
|
if (row.receipt !== undefined && ["acknowledged", "refused", "conflict"].includes(row.state))
|
|
442
456
|
continue;
|
|
443
|
-
const mutableBytes = encodePayload({ state: row.state, attempts: row.attempts, updatedAt: row.updatedAt, transaction: txn, ...(row.notAppliedThrough === undefined ? {} : { notAppliedThrough: row.notAppliedThrough }), ...(row.deletionHold === undefined ? {} : { deletionHold: row.deletionHold }) }).byteLength;
|
|
457
|
+
const mutableBytes = encodePayload({ state: row.state, attempts: row.attempts, updatedAt: row.updatedAt, transaction: txn, ...(row.notAppliedThrough === undefined ? {} : { notAppliedThrough: row.notAppliedThrough }), ...(row.deletionHold === undefined ? {} : { deletionHold: row.deletionHold }), ...(row.notDispatched === undefined ? {} : { notDispatched: row.notDispatched }) }).byteLength;
|
|
444
458
|
const remaining = BLOB_JOURNAL_BOUNDS.terminalReceiptBytes + BLOB_JOURNAL_BOUNDS.settlementStructureBytes + BLOB_JOURNAL_BOUNDS.baselineBytes - mutableBytes;
|
|
445
459
|
if (!Number.isSafeInteger(remaining) || remaining < 0 || !Number.isSafeInteger(reserved + remaining))
|
|
446
460
|
throw new RangeError("blob settlement reserve is not representable");
|
|
@@ -579,6 +593,9 @@ export class FileJournaledBackend {
|
|
|
579
593
|
#maximumBytes = MAXIMUM_RECORD_BYTES;
|
|
580
594
|
#logBytes;
|
|
581
595
|
#queue = Promise.resolve();
|
|
596
|
+
/** Blob claims this handle made, by request id, at their attempt count. The exclusive lock means
|
|
597
|
+
* any `in_flight` row found at open was claimed by a process that has since died. */
|
|
598
|
+
#claims = new Map();
|
|
582
599
|
#unavailable = null;
|
|
583
600
|
#closing = null;
|
|
584
601
|
#readOnly = false;
|
|
@@ -597,11 +614,11 @@ export class FileJournaledBackend {
|
|
|
597
614
|
* file. Compacts when the log is larger than `compactAfterBytes`.
|
|
598
615
|
*/
|
|
599
616
|
static open(options) {
|
|
600
|
-
return
|
|
617
|
+
return _a.#open(options, (file) => fs.open(file, "r+"));
|
|
601
618
|
}
|
|
602
619
|
/** @internal The test seam: open with a substitute log handle. Reachable only through {@link OPEN_LOG}. */
|
|
603
620
|
static [OPEN_LOG](options, openLog) {
|
|
604
|
-
return
|
|
621
|
+
return _a.#open(options, openLog);
|
|
605
622
|
}
|
|
606
623
|
static async #open(options, openLog) {
|
|
607
624
|
if (typeof options.directory !== "string" || options.directory.trim() === "")
|
|
@@ -664,7 +681,7 @@ export class FileJournaledBackend {
|
|
|
664
681
|
txn = record.txn;
|
|
665
682
|
}
|
|
666
683
|
if (options.readOnly) {
|
|
667
|
-
const backend = new
|
|
684
|
+
const backend = new _a(directory, state, READ_ONLY_LOG, release, txn, scanned.validBytes, policy);
|
|
668
685
|
backend.#maximumBytes = maximumBytes;
|
|
669
686
|
backend.#readOnly = true;
|
|
670
687
|
return backend;
|
|
@@ -679,7 +696,7 @@ export class FileJournaledBackend {
|
|
|
679
696
|
await log.truncate(scanned.validBytes);
|
|
680
697
|
await log.sync();
|
|
681
698
|
}
|
|
682
|
-
const backend = new
|
|
699
|
+
const backend = new _a(directory, state, log, release, txn, scanned.validBytes, policy);
|
|
683
700
|
backend.#maximumBytes = maximumBytes;
|
|
684
701
|
if (scanned.validBytes > compactAfter)
|
|
685
702
|
await backend.compact();
|
|
@@ -769,18 +786,20 @@ export class FileJournaledBackend {
|
|
|
769
786
|
*/
|
|
770
787
|
#mutate(decide) {
|
|
771
788
|
return this.#serial(async () => {
|
|
772
|
-
const { changes, result, format } = decide(this.#state);
|
|
773
|
-
if (changes.length === 0)
|
|
789
|
+
const { changes, result, format, afterCommit } = decide(this.#state);
|
|
790
|
+
if (changes.length === 0) {
|
|
791
|
+
afterCommit?.();
|
|
774
792
|
return result;
|
|
793
|
+
}
|
|
775
794
|
const nextFormat = Math.max(this.#state.format, format ?? 1);
|
|
776
|
-
const record = { ...(nextFormat
|
|
795
|
+
const record = { ...(nextFormat >= 2 ? { format: nextFormat } : {}), txn: this.#txn + 1, changes };
|
|
777
796
|
const payload = encodePayload(record);
|
|
778
797
|
assertByteBound(payload, this.#maximumBytes, "transaction");
|
|
779
798
|
const projected = { ...this.#state, format: nextFormat, documents: new Map(this.#state.documents), reserved: new Map(this.#state.reserved), blobs: new Map(this.#state.blobs), intents: new Map(this.#state.intents), meta: new Map(this.#state.meta) };
|
|
780
799
|
applyChanges(projected, changes);
|
|
781
800
|
const snapshotBytes = encodePayload(snapshotPayload(projected, record.txn));
|
|
782
801
|
assertByteBound(snapshotBytes, this.#maximumBytes, "projected snapshot");
|
|
783
|
-
if (nextFormat
|
|
802
|
+
if (nextFormat >= 2) {
|
|
784
803
|
const reserved = settlementReserve(projected, record.txn);
|
|
785
804
|
if (snapshotBytes.byteLength + reserved > this.#maximumBytes)
|
|
786
805
|
throw new RangeError(`a projected snapshot of ${snapshotBytes.byteLength} bytes plus ${reserved} bytes of blob settlement reserve exceeds the store's ${this.#maximumBytes} byte bound`);
|
|
@@ -805,6 +824,7 @@ export class FileJournaledBackend {
|
|
|
805
824
|
this.#txn = record.txn;
|
|
806
825
|
this.#state.format = nextFormat;
|
|
807
826
|
applyChanges(this.#state, changes);
|
|
827
|
+
afterCommit?.();
|
|
808
828
|
return result;
|
|
809
829
|
});
|
|
810
830
|
}
|
|
@@ -1006,7 +1026,7 @@ export class FileJournaledBackend {
|
|
|
1006
1026
|
if (resolveIntents)
|
|
1007
1027
|
assertJournalSnapshot(id, resolveIntents.expected, documentIntents(state), intent?.requestId);
|
|
1008
1028
|
if (requireSettled) {
|
|
1009
|
-
const holder =
|
|
1029
|
+
const holder = _a.#holder(state, id);
|
|
1010
1030
|
if (holder)
|
|
1011
1031
|
throw new IntentHoldConflict(id, holder.requestId, holder.state);
|
|
1012
1032
|
}
|
|
@@ -1029,7 +1049,7 @@ export class FileJournaledBackend {
|
|
|
1029
1049
|
record = { ...intent, local: version, content: raw, sequence, attempts: 0, state: "pending", updatedAt: now };
|
|
1030
1050
|
changes.push({ family: "sequence", value: sequence }, { family: "intent", key: record.requestId, value: structuredClone(record) });
|
|
1031
1051
|
}
|
|
1032
|
-
changes.push(...
|
|
1052
|
+
changes.push(..._a.#metaChanges(meta, removeMeta));
|
|
1033
1053
|
return { changes, result: { version, raw, intent: record } };
|
|
1034
1054
|
});
|
|
1035
1055
|
}
|
|
@@ -1051,11 +1071,11 @@ export class FileJournaledBackend {
|
|
|
1051
1071
|
if (captured.intent && state.intents.has(captured.intent.requestId))
|
|
1052
1072
|
throw new JournalSnapshotConflict(id);
|
|
1053
1073
|
if (requireSettled) {
|
|
1054
|
-
const holder =
|
|
1074
|
+
const holder = _a.#holder(state, id);
|
|
1055
1075
|
if (holder) {
|
|
1056
1076
|
if (!captured.onHeld)
|
|
1057
1077
|
throw new IntentHoldConflict(id, holder.requestId, holder.state);
|
|
1058
|
-
return { changes:
|
|
1078
|
+
return { changes: _a.#metaChanges(captured.onHeld.meta), result: { outcome: "held", requestId: holder.requestId, state: holder.state } };
|
|
1059
1079
|
}
|
|
1060
1080
|
}
|
|
1061
1081
|
const current = state.documents.get(id)?.version ?? null;
|
|
@@ -1080,7 +1100,7 @@ export class FileJournaledBackend {
|
|
|
1080
1100
|
record = deletionIntentRecord(captured.intent, sequence, now);
|
|
1081
1101
|
changes.push({ family: "sequence", value: sequence }, { family: "intent", key: record.requestId, value: structuredClone(record) });
|
|
1082
1102
|
}
|
|
1083
|
-
changes.push(...
|
|
1103
|
+
changes.push(..._a.#metaChanges(captured.meta, captured.removeMeta));
|
|
1084
1104
|
return { changes, result: { outcome: current !== null ? "deleted" : "absent", ...(record ? { intent: record } : {}) } };
|
|
1085
1105
|
});
|
|
1086
1106
|
}
|
|
@@ -1164,7 +1184,7 @@ export class FileJournaledBackend {
|
|
|
1164
1184
|
const changes = [];
|
|
1165
1185
|
if (replacement)
|
|
1166
1186
|
changes.push({ family: "document", key: replacement.id, value: replacement.record });
|
|
1167
|
-
changes.push({ family: "intent", key: requestId, value: structuredClone(next) }, ...
|
|
1187
|
+
changes.push({ family: "intent", key: requestId, value: structuredClone(next) }, ..._a.#metaChanges(meta));
|
|
1168
1188
|
return { changes, result: next };
|
|
1169
1189
|
});
|
|
1170
1190
|
}
|
|
@@ -1174,64 +1194,63 @@ export class FileJournaledBackend {
|
|
|
1174
1194
|
await this.#mutate((state) => {
|
|
1175
1195
|
this.#checkGuard(state, guard);
|
|
1176
1196
|
const changes = requestIds.map((key) => ({ family: "intent", key, value: null }));
|
|
1177
|
-
return { changes: [...changes, ...
|
|
1197
|
+
return { changes: [...changes, ..._a.#metaChanges([], removeMeta ?? [])], result: undefined };
|
|
1178
1198
|
});
|
|
1179
1199
|
}
|
|
1180
1200
|
// ── optional ordinary-file journal (no filesystem/body mirror) ─────────────────────────
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1201
|
+
/** The committed index as the pure blob transition owner reads it. */
|
|
1202
|
+
#blobView(state) {
|
|
1203
|
+
return {
|
|
1204
|
+
blobIntentsFor: key => [...state.intents.values()].filter((row) => !isDocumentIntent(row) && row.target === key),
|
|
1205
|
+
blobIntent: id => { const row = state.intents.get(id); return row && !isDocumentIntent(row) ? row : undefined; },
|
|
1206
|
+
intentState: id => state.intents.get(id)?.state ?? null,
|
|
1207
|
+
meta: key => state.meta.has(key) ? { present: true, value: state.meta.get(key) } : { present: false },
|
|
1208
|
+
sequence: state.sequence,
|
|
1209
|
+
claimedInThisSession: (id, attempts) => this.#claims.get(id) === attempts,
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
/** A decided blob verb as one mutation: its changes, the format they need, and the session claims. */
|
|
1213
|
+
#blobDecision(decision) {
|
|
1214
|
+
const formats = decision.intents.flatMap(({ value }) => (value === null ? [] : [blobIntentFormat(value)]));
|
|
1215
|
+
const format = formats.length === 0 ? undefined : Math.max(...formats);
|
|
1216
|
+
return {
|
|
1217
|
+
changes: _a.#blobChanges(decision),
|
|
1218
|
+
result: decision.result,
|
|
1219
|
+
...(format === undefined ? {} : { format }),
|
|
1220
|
+
afterCommit: () => {
|
|
1221
|
+
if (decision.session) {
|
|
1222
|
+
if (decision.session.claimed === null)
|
|
1223
|
+
this.#claims.delete(decision.session.requestId);
|
|
1224
|
+
else
|
|
1225
|
+
this.#claims.set(decision.session.requestId, decision.session.claimed);
|
|
1226
|
+
}
|
|
1227
|
+
for (const { requestId, value } of decision.intents)
|
|
1228
|
+
if (value === null)
|
|
1229
|
+
this.#claims.delete(requestId);
|
|
1230
|
+
},
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
/** A decided blob verb as one log record's changes. */
|
|
1234
|
+
static #blobChanges(decision) {
|
|
1235
|
+
return [
|
|
1236
|
+
...(decision.sequence === undefined ? [] : [{ family: "sequence", value: decision.sequence }]),
|
|
1237
|
+
...decision.intents.map(({ requestId, value }) => ({ family: "intent", key: requestId, value: value === null ? null : structuredClone(value) })),
|
|
1238
|
+
..._a.#metaChanges(decision.meta, decision.removeMeta),
|
|
1239
|
+
];
|
|
1187
1240
|
}
|
|
1188
1241
|
async readBlobWithJournal(key, options = {}) {
|
|
1189
1242
|
assertBlobKey(key);
|
|
1190
1243
|
this.#assertOpen();
|
|
1191
|
-
|
|
1192
|
-
const intents = [...state.intents.values()].filter((row) => !isDocumentIntent(row) && row.target === key).sort(bySequence).map(row => structuredClone(row));
|
|
1193
|
-
const meta = new Map();
|
|
1194
|
-
const premises = [];
|
|
1195
|
-
for (const name of new Set(options.meta ?? [])) {
|
|
1196
|
-
if (typeof name !== "string")
|
|
1197
|
-
throw new JournalGuardConflict(key);
|
|
1198
|
-
if (state.meta.has(name)) {
|
|
1199
|
-
const value = structuredClone(state.meta.get(name));
|
|
1200
|
-
meta.set(name, value);
|
|
1201
|
-
premises.push({ key: name, expected: { present: true, value: structuredClone(value) } });
|
|
1202
|
-
}
|
|
1203
|
-
else
|
|
1204
|
-
premises.push({ key: name, expected: { present: false } });
|
|
1205
|
-
}
|
|
1206
|
-
return { key, intents, meta, guard: { key, intents: structuredClone(intents), meta: premises } };
|
|
1244
|
+
return readBlobJournal(this.#blobView(this.#state), key, options);
|
|
1207
1245
|
}
|
|
1208
1246
|
async writeBlobMeta(key, changes, options) {
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
if (captured.guard.key !== key)
|
|
1212
|
-
throw new JournalGuardConflict(key);
|
|
1213
|
-
await this.#mutate(state => {
|
|
1214
|
-
this.#blobGuard(state, captured.guard);
|
|
1215
|
-
return { changes: FileJournaledBackend.#metaChanges(captured.meta ?? [], captured.removeMeta ?? []), result: undefined };
|
|
1216
|
-
});
|
|
1247
|
+
const captured = captureBlobMetaWrite(key, changes, options);
|
|
1248
|
+
await this.#mutate(state => this.#blobDecision(decideBlobMetaWrite(this.#blobView(state), captured)));
|
|
1217
1249
|
}
|
|
1218
1250
|
async admitPreparedBlob(prepared, options) {
|
|
1219
|
-
const captured =
|
|
1220
|
-
options = captureBlobMutationOptions(options);
|
|
1221
|
-
const { guard, meta = [], removeMeta = [] } = options;
|
|
1222
|
-
if (guard.key !== captured.intent.key)
|
|
1223
|
-
throw new JournalGuardConflict(captured.intent.key);
|
|
1251
|
+
const captured = captureBlobAdmission(prepared, options);
|
|
1224
1252
|
const now = new Date().toISOString();
|
|
1225
|
-
return this.#mutate(state =>
|
|
1226
|
-
this.#blobGuard(state, guard);
|
|
1227
|
-
if (state.intents.has(captured.identity.requestId))
|
|
1228
|
-
throw new JournalSnapshotConflict(guard.key);
|
|
1229
|
-
const sequence = state.sequence + 1;
|
|
1230
|
-
if (!Number.isSafeInteger(sequence))
|
|
1231
|
-
throw new RangeError("journal sequence exhausted");
|
|
1232
|
-
const record = captureBlobIntentRecord({ recordFamily: "blob", requestId: captured.identity.requestId, target: captured.intent.key, prepared, sequence, attempts: 0, state: "pending", updatedAt: now });
|
|
1233
|
-
return { format: 2, changes: [{ family: "sequence", value: sequence }, { family: "intent", key: record.requestId, value: structuredClone(record) }, ...FileJournaledBackend.#metaChanges(meta, removeMeta)], result: record };
|
|
1234
|
-
});
|
|
1253
|
+
return this.#mutate(state => this.#blobDecision(decideBlobAdmission(this.#blobView(state), captured, now)));
|
|
1235
1254
|
}
|
|
1236
1255
|
async listBlobIntents(state) {
|
|
1237
1256
|
this.#assertOpen();
|
|
@@ -1244,56 +1263,17 @@ export class FileJournaledBackend {
|
|
|
1244
1263
|
return row && !isDocumentIntent(row) ? structuredClone(row) : undefined;
|
|
1245
1264
|
}
|
|
1246
1265
|
async updateBlobIntent(requestId, expectedState, patch, options) {
|
|
1247
|
-
|
|
1248
|
-
options = captureBlobMutationOptions(options);
|
|
1249
|
-
if (!patch || typeof patch !== "object" || Array.isArray(patch) || Object.keys(patch).some(key => !["state", "attempts", "receipt", "notAppliedThrough", "deletionHold"].includes(key)))
|
|
1250
|
-
throw new JournalGuardConflict(options.guard.key);
|
|
1251
|
-
const { guard, meta = [], removeMeta = [] } = options;
|
|
1266
|
+
const captured = captureBlobUpdate(requestId, expectedState, patch, options);
|
|
1252
1267
|
const now = new Date().toISOString();
|
|
1253
|
-
return this.#mutate(state =>
|
|
1254
|
-
this.#blobGuard(state, guard);
|
|
1255
|
-
const current = state.intents.get(requestId);
|
|
1256
|
-
if (!current || isDocumentIntent(current) || current.state !== expectedState)
|
|
1257
|
-
throw new IntentStateConflict(requestId, expectedState, current?.state ?? null);
|
|
1258
|
-
if (current.target !== guard.key)
|
|
1259
|
-
throw new JournalGuardConflict(guard.key);
|
|
1260
|
-
const next = captureBlobIntentRecord({ ...current, ...patch, updatedAt: now });
|
|
1261
|
-
assertReceiptBound(next);
|
|
1262
|
-
const beforePrefix = current.notAppliedThrough ?? 0, afterPrefix = next.notAppliedThrough ?? 0;
|
|
1263
|
-
const sameHold = equalJournalValue(current.deletionHold, next.deletionHold);
|
|
1264
|
-
if (current.receipt && (next.attempts !== current.attempts || next.state !== current.state || !equalJournalValue(current.receipt, next.receipt) || afterPrefix !== beforePrefix || !sameHold))
|
|
1265
|
-
throw new JournalGuardConflict(guard.key);
|
|
1266
|
-
if (next.attempts !== current.attempts) {
|
|
1267
|
-
// The durable claim consumes exactly one eligibility before any wire operation.
|
|
1268
|
-
if (current.receipt || !["pending", "unknown"].includes(current.state) || next.state !== "in_flight" || next.attempts !== current.attempts + 1 || beforePrefix !== current.attempts || afterPrefix !== beforePrefix || !sameHold)
|
|
1269
|
-
throw new JournalGuardConflict(guard.key);
|
|
1270
|
-
}
|
|
1271
|
-
else if (afterPrefix !== beforePrefix) {
|
|
1272
|
-
// Typed evidence belongs to this exact claimed count and must extend the complete prefix.
|
|
1273
|
-
if (current.receipt || !["in_flight", "unknown"].includes(current.state) || next.state !== "pending" || patch.attempts !== current.attempts || beforePrefix !== current.attempts - 1 || afterPrefix !== current.attempts || patch.deletionHold === undefined || next.receipt !== undefined)
|
|
1274
|
-
throw new JournalGuardConflict(guard.key);
|
|
1275
|
-
}
|
|
1276
|
-
else if (!sameHold || patch.state === "in_flight")
|
|
1277
|
-
throw new JournalGuardConflict(guard.key);
|
|
1278
|
-
return { changes: [{ family: "intent", key: requestId, value: structuredClone(next) }, ...FileJournaledBackend.#metaChanges(meta, removeMeta)], result: next };
|
|
1279
|
-
});
|
|
1268
|
+
return this.#mutate(state => this.#blobDecision(decideBlobUpdate(this.#blobView(state), captured, now, assertReceiptBound)));
|
|
1280
1269
|
}
|
|
1281
1270
|
async retireBlobIntents(key, requestIds, options) {
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
await this.#mutate(state => {
|
|
1289
|
-
this.#blobGuard(state, guard);
|
|
1290
|
-
for (const id of ids) {
|
|
1291
|
-
const row = state.intents.get(id);
|
|
1292
|
-
if (!row || isDocumentIntent(row) || row.target !== key || !row.receipt || !["acknowledged", "refused", "conflict"].includes(row.state))
|
|
1293
|
-
throw new JournalGuardConflict(key);
|
|
1294
|
-
}
|
|
1295
|
-
return { changes: [...ids.map((id) => ({ family: "intent", key: id, value: null })), ...FileJournaledBackend.#metaChanges(meta, removeMeta)], result: undefined };
|
|
1296
|
-
});
|
|
1271
|
+
const captured = captureBlobRetirement(key, requestIds, options);
|
|
1272
|
+
await this.#mutate(state => this.#blobDecision(decideBlobRetirement(this.#blobView(state), captured)));
|
|
1273
|
+
}
|
|
1274
|
+
async forgetBlobIntent(requestId, options) {
|
|
1275
|
+
const captured = captureBlobForget(requestId, options);
|
|
1276
|
+
await this.#mutate(state => this.#blobDecision(decideBlobForget(this.#blobView(state), captured)));
|
|
1297
1277
|
}
|
|
1298
1278
|
async readMeta(key) {
|
|
1299
1279
|
this.#assertOpen();
|
|
@@ -1317,5 +1297,38 @@ export class FileJournaledBackend {
|
|
|
1317
1297
|
return { changes: [{ family: "meta", key, present: true, value: captured }], result: undefined };
|
|
1318
1298
|
});
|
|
1319
1299
|
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Every meta row whose key starts with `prefix` (all rows when it is empty), in code-unit key
|
|
1302
|
+
* order, read in one synchronous pass over the last committed transaction. Values are copies.
|
|
1303
|
+
*/
|
|
1304
|
+
async listMeta(prefix = "") {
|
|
1305
|
+
if (typeof prefix !== "string")
|
|
1306
|
+
throw new TypeError("a meta prefix is a string");
|
|
1307
|
+
this.#assertOpen();
|
|
1308
|
+
const meta = this.#state.meta;
|
|
1309
|
+
const keys = [...meta.keys()].filter((key) => key.startsWith(prefix)).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
1310
|
+
return keys.map((key) => ({ key, value: structuredClone(meta.get(key)) }));
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Remove one meta row, compare-and-swap: `expected` is the row's presence and value as the
|
|
1314
|
+
* caller last read it, compared in the same transaction as the removal. A mismatch is
|
|
1315
|
+
* `JournalGuardConflict` and writes nothing. It resolves true when it removed the row and false
|
|
1316
|
+
* when the row was absent as expected, which writes nothing either.
|
|
1317
|
+
*/
|
|
1318
|
+
async removeMeta(key, options) {
|
|
1319
|
+
if (typeof key !== "string")
|
|
1320
|
+
throw new TypeError("a meta key is a string");
|
|
1321
|
+
if (!options || typeof options !== "object" || options.expected === undefined)
|
|
1322
|
+
throw new JournalGuardConflict(key);
|
|
1323
|
+
const { expected } = captureMetaWrite({ expected: options.expected });
|
|
1324
|
+
return this.#mutate((state) => {
|
|
1325
|
+
const actual = state.meta.has(key) ? { present: true, value: state.meta.get(key) } : { present: false };
|
|
1326
|
+
assertMetaWrite(key, { expected }, actual, []);
|
|
1327
|
+
if (!actual.present)
|
|
1328
|
+
return { changes: [], result: false };
|
|
1329
|
+
return { changes: [{ family: "meta", key, present: false }], result: true };
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1320
1332
|
}
|
|
1333
|
+
_a = FileJournaledBackend;
|
|
1321
1334
|
//# sourceMappingURL=file-journaled-backend.js.map
|