@omgbase/core 0.27.1 → 0.29.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/dist/src/core/read/document.d.ts +11 -10
- package/dist/src/core/read/document.js +12 -11
- package/dist/src/core/read/nodes.d.ts +10 -10
- package/dist/src/core/read/nodes.js +11 -11
- package/dist/src/core/read/reader.d.ts +37 -24
- package/dist/src/core/read/reader.js +44 -31
- package/dist/src/graph/history.d.ts +25 -22
- package/dist/src/graph/history.js +17 -17
- package/dist/src/graph/inbound-links.d.ts +7 -7
- package/dist/src/graph/inbound-links.js +7 -7
- package/dist/src/graph/link-health.d.ts +9 -9
- package/dist/src/graph/link-health.js +9 -9
- package/dist/src/graph/links.d.ts +3 -3
- package/dist/src/graph/links.js +4 -4
- package/dist/src/mcp/arguments.d.ts +10 -3
- package/dist/src/mcp/arguments.js +32 -9
- package/dist/src/mcp/errors.d.ts +1 -1
- package/dist/src/mcp/graph.d.ts +22 -22
- package/dist/src/mcp/graph.js +53 -46
- package/dist/src/mcp/reference.d.ts +29 -1
- package/dist/src/mcp/reference.js +350 -222
- package/dist/src/mcp/server.d.ts +9 -0
- package/dist/src/mcp/server.js +169 -107
- package/dist/src/mutate/apply.d.ts +6 -6
- package/dist/src/mutate/apply.js +1 -1
- package/dist/src/mutate/canonical.d.ts +1 -1
- package/dist/src/mutate/canonical.js +2 -2
- package/dist/src/mutate/commit.d.ts +3 -3
- package/dist/src/mutate/commit.js +4 -4
- package/dist/src/mutate/docs.d.ts +1 -1
- package/dist/src/mutate/docs.js +8 -8
- package/dist/src/mutate/effects.d.ts +17 -17
- package/dist/src/mutate/effects.js +113 -113
- package/dist/src/mutate/macros.d.ts +5 -5
- package/dist/src/mutate/macros.js +1 -1
- package/dist/src/mutate/materialize.js +2 -2
- package/dist/src/mutate/ops.js +2 -2
- package/dist/src/mutate/opset.d.ts +7 -7
- package/dist/src/mutate/plan-update.js +2 -2
- package/dist/src/mutate/plan.d.ts +8 -8
- package/dist/src/mutate/plan.js +25 -25
- package/dist/src/mutate/select.d.ts +4 -4
- package/dist/src/mutate/select.js +52 -52
- package/dist/src/mutate/sugar.d.ts +10 -10
- package/dist/src/mutate/sugar.js +21 -21
- package/dist/src/oqx-js/context.d.ts +8 -4
- package/dist/src/oqx-js/context.js +206 -189
- package/dist/src/oqx-js/names.d.ts +28 -17
- package/dist/src/oqx-js/names.js +115 -78
- package/dist/src/oqx-js/planner.d.ts +4 -8
- package/dist/src/oqx-js/planner.js +6 -10
- package/dist/src/oqx-js/root-row.d.ts +6 -5
- package/dist/src/oqx-js/root-row.js +15 -16
- package/dist/src/oqx-js/run.d.ts +18 -17
- package/dist/src/oqx-js/run.js +57 -54
- package/dist/src/oqx-js/sql/translate.d.ts +5 -5
- package/dist/src/oqx-js/sql/translate.js +162 -161
- package/dist/src/oqx-js/store-index.d.ts +1 -1
- package/dist/src/oqx-js/store-index.js +28 -30
- package/dist/src/search/resolve.d.ts +2 -2
- package/dist/src/search/resolve.js +1 -1
- package/dist/src/search/rrf.js +2 -2
- package/dist/src/search/text.d.ts +5 -5
- package/dist/src/search/text.js +2 -2
- package/dist/src/spec-versions.d.ts +6 -6
- package/dist/src/spec-versions.js +6 -6
- package/dist/src/surface-paths.d.ts +18 -17
- package/dist/src/surface-paths.js +23 -21
- package/dist/src/sync/admin.d.ts +2 -2
- package/dist/src/sync/admin.js +1 -1
- package/package.json +1 -1
|
@@ -75,11 +75,11 @@ function bareAnchor(at, into, here, f) {
|
|
|
75
75
|
const message = `${here}: \`at\` must be "start", "end", { before: <block id> } or { after: <block id> } — "${at} block X" is into: X's parent (a block, or the document when X is top-level), at: { ${at}: X }`;
|
|
76
76
|
if (f === undefined || into.kind !== "block")
|
|
77
77
|
return effectInvalid(message);
|
|
78
|
-
const row = f.parentOf(into.
|
|
78
|
+
const row = f.parentOf(into._id);
|
|
79
79
|
if (row === null)
|
|
80
80
|
return effectInvalid(message);
|
|
81
|
-
const parent = row.parent === null ? referencePath(row.path) : { kind: "block",
|
|
82
|
-
return effectInvalid(message, f.build({ ...f.written, into: parent, at: { [at]: into.
|
|
81
|
+
const parent = row.parent === null ? referencePath(row.path) : { kind: "block", _id: row.parent };
|
|
82
|
+
return effectInvalid(message, f.build({ ...f.written, into: parent, at: { [at]: into._id } }));
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
85
85
|
* §8 (3.4): a plural-target `expect` written as `{ content_hash: { <id>: <hash>, … } }`
|
|
@@ -135,8 +135,8 @@ export function isTargetId(s) {
|
|
|
135
135
|
}
|
|
136
136
|
/**
|
|
137
137
|
* A bare string where a target object is expected (3.2, §11.8 "Sugar"): a
|
|
138
|
-
* path-looking string → `{ kind: "document",
|
|
139
|
-
* that kind; `$new.n` → `{ kind: "symbol",
|
|
138
|
+
* path-looking string → `{ kind: "document", _path }`; a `b_`/`d_`/`n_` id →
|
|
139
|
+
* that kind; `$new.n` → `{ kind: "symbol", _id }` (its kind is the symbol
|
|
140
140
|
* table's at resolution); a `#` locator → `filter_invalid` with a `fix`;
|
|
141
141
|
* anything else is no target. A `-> ` leader never reaches here from the
|
|
142
142
|
* planner (`select.ts` desugars it first); as a library input it is refused
|
|
@@ -152,15 +152,15 @@ export function bareTarget(ref, here) {
|
|
|
152
152
|
throw new MutationError("filter_invalid", reason, { reason, hint: "arguments", fix: "resolve the locator to an id first: `blocks_resolve { locator }`, or a `query` whose hit is the block" });
|
|
153
153
|
}
|
|
154
154
|
if (isNewSymbol(ref))
|
|
155
|
-
return { kind: "symbol",
|
|
155
|
+
return { kind: "symbol", _id: ref };
|
|
156
156
|
if (isValidId(ref, "b"))
|
|
157
|
-
return { kind: "block",
|
|
157
|
+
return { kind: "block", _id: ref };
|
|
158
158
|
if (isValidId(ref, "d"))
|
|
159
|
-
return { kind: "document",
|
|
159
|
+
return { kind: "document", _id: ref };
|
|
160
160
|
if (NODE_ID_RE.test(ref))
|
|
161
|
-
return { kind: "node",
|
|
161
|
+
return { kind: "node", _id: ref };
|
|
162
162
|
if (looksLikePath(ref))
|
|
163
|
-
return { kind: "document",
|
|
163
|
+
return { kind: "document", _path: ref };
|
|
164
164
|
throw effectInvalid(`${here}: a target is { kind: …, … } or a bare document path, a \`b_\`/\`d_\`/\`n_\` id or \`$new.n\` (got ${JSON.stringify(ref)})`);
|
|
165
165
|
}
|
|
166
166
|
export function validateTarget(v, here, kinds) {
|
|
@@ -169,25 +169,25 @@ export function validateTarget(v, here, kinds) {
|
|
|
169
169
|
if (!isRecord(v) || typeof v.kind !== "string")
|
|
170
170
|
throw effectInvalid(`${here}: a target is { kind: ${kinds.map((k) => `"${k}"`).join(" | ")}, … }`);
|
|
171
171
|
// A bare `$new.n` defers its kind check to resolution (`resolveEffect`); the `kinds` are the slot's.
|
|
172
|
-
if (v.kind === "symbol" && typeof v.
|
|
173
|
-
return { kind: "symbol",
|
|
172
|
+
if (v.kind === "symbol" && typeof v._id === "string" && isNewSymbol(v._id) && Object.keys(v).length === 2)
|
|
173
|
+
return { kind: "symbol", _id: v._id };
|
|
174
174
|
if (!kinds.includes(v.kind))
|
|
175
175
|
throw effectInvalid(`${here}: \`kind\` must be one of ${kinds.join(", ")} (got "${v.kind}")`);
|
|
176
176
|
switch (v.kind) {
|
|
177
177
|
case "document": {
|
|
178
|
-
onlyKeys(v, ["kind", "
|
|
179
|
-
const id = optionalString(v, "
|
|
180
|
-
const path = optionalString(v, "
|
|
178
|
+
onlyKeys(v, ["kind", "_id", "_path"], here);
|
|
179
|
+
const id = optionalString(v, "_id", here);
|
|
180
|
+
const path = optionalString(v, "_path", here);
|
|
181
181
|
if (id === undefined && path === undefined)
|
|
182
|
-
throw effectInvalid(`${here}: a document target needs \`
|
|
183
|
-
return { kind: "document", ...(id !== undefined ? { id } : {}), ...(path !== undefined ? { path } : {}) };
|
|
182
|
+
throw effectInvalid(`${here}: a document target needs \`_id\` or \`_path\``);
|
|
183
|
+
return { kind: "document", ...(id !== undefined ? { _id: id } : {}), ...(path !== undefined ? { _path: path } : {}) };
|
|
184
184
|
}
|
|
185
185
|
case "block":
|
|
186
|
-
onlyKeys(v, ["kind", "
|
|
187
|
-
return { kind: "block",
|
|
186
|
+
onlyKeys(v, ["kind", "_id"], here);
|
|
187
|
+
return { kind: "block", _id: requireString(v, "_id", here) };
|
|
188
188
|
case "node":
|
|
189
|
-
onlyKeys(v, ["kind", "
|
|
190
|
-
return { kind: "node",
|
|
189
|
+
onlyKeys(v, ["kind", "_id"], here);
|
|
190
|
+
return { kind: "node", _id: requireString(v, "_id", here) };
|
|
191
191
|
case "section":
|
|
192
192
|
onlyKeys(v, ["kind", "heading_id"], here);
|
|
193
193
|
return { kind: "section", heading_id: requireString(v, "heading_id", here) };
|
|
@@ -365,26 +365,26 @@ export function validateEffects(v, fix) {
|
|
|
365
365
|
return v.map((e, i) => validateEffect(e, i, fix));
|
|
366
366
|
}
|
|
367
367
|
// §11.2: the `field` spellings per target kind.
|
|
368
|
-
export const FRONTMATTER_FIELD = /^
|
|
368
|
+
export const FRONTMATTER_FIELD = /^_frontmatter\.(.+)$/;
|
|
369
369
|
function checkField(kind, field, here, op, f) {
|
|
370
370
|
if (kind === "document") {
|
|
371
371
|
if (FRONTMATTER_FIELD.test(field) || (op === "set" && field === "content"))
|
|
372
372
|
return;
|
|
373
|
-
throw effectInvalid(`${here}: a document's \`field\` is \`
|
|
373
|
+
throw effectInvalid(`${here}: a document's \`field\` is \`_frontmatter.<key>\`${op === "set" ? " or `content`" : ""} (got "${field}")`);
|
|
374
374
|
}
|
|
375
375
|
if (kind === "block") {
|
|
376
|
-
if (field === "markdown" || field === "
|
|
376
|
+
if (field === "markdown" || field === "_attrs.checked")
|
|
377
377
|
return;
|
|
378
378
|
// (3.4, §8) `text` / `content` / `body` / `raw` on a block: the fix spells `markdown`.
|
|
379
379
|
const fix = f !== undefined && BLOCK_FIELD_ALIASES.has(field) ? f.build({ ...f.written, field: "markdown" }) : undefined;
|
|
380
|
-
throw effectInvalid(`${here}: a block's \`field\` is \`markdown\` or \`
|
|
380
|
+
throw effectInvalid(`${here}: a block's \`field\` is \`markdown\` or \`_attrs.checked\` (got "${field}")`, fix);
|
|
381
381
|
}
|
|
382
382
|
// node: the adapter's editable props (`node_not_editable` names them when unknown)
|
|
383
383
|
if (field === "" || field.includes("."))
|
|
384
384
|
throw effectInvalid(`${here}: a node's \`field\` is one of its editable properties (got "${field}")`);
|
|
385
385
|
}
|
|
386
386
|
export function conflictingEffects(effects, focus) {
|
|
387
|
-
const what = focus.field !== undefined ? `${focus.target.kind} ${focus.target.
|
|
387
|
+
const what = focus.field !== undefined ? `${focus.target.kind} ${focus.target._id ?? focus.target._path} ${focus.field}` : `${focus.target.kind} ${focus.target._id ?? focus.target._path}`;
|
|
388
388
|
return new MutationError("conflicting_effects", `effects ${effects[0]} and ${effects[1]} contradict each other on ${what}`, { effects, focus, retriable: false });
|
|
389
389
|
}
|
|
390
390
|
/** The focus bookkeeping over an ordered effect list (§11.3). */
|
|
@@ -404,22 +404,22 @@ export class ConflictTracker {
|
|
|
404
404
|
for (const d of info.docs) {
|
|
405
405
|
const by = this.retiredDocs.get(d);
|
|
406
406
|
if (by !== undefined)
|
|
407
|
-
throw conflictingEffects([by, i], { kind: "identity", target: { kind: "document",
|
|
407
|
+
throw conflictingEffects([by, i], { kind: "identity", target: { kind: "document", _id: d } });
|
|
408
408
|
}
|
|
409
409
|
for (const b of info.blocks) {
|
|
410
410
|
const by = this.retiredBlocks.get(b);
|
|
411
411
|
if (by !== undefined)
|
|
412
|
-
throw conflictingEffects([by, i], { kind: "identity", target: { kind: "block",
|
|
412
|
+
throw conflictingEffects([by, i], { kind: "identity", target: { kind: "block", _id: b } });
|
|
413
413
|
}
|
|
414
414
|
for (const p of info.paths) {
|
|
415
415
|
const by = this.retiredPaths.get(p);
|
|
416
416
|
if (by !== undefined)
|
|
417
|
-
throw conflictingEffects([by, i], { kind: "identity", target: { kind: "path",
|
|
417
|
+
throw conflictingEffects([by, i], { kind: "identity", target: { kind: "path", _path: p } });
|
|
418
418
|
}
|
|
419
419
|
if (info.wholeDoc !== undefined) {
|
|
420
420
|
const by = this.touchedDocs.get(info.wholeDoc);
|
|
421
421
|
if (by !== undefined)
|
|
422
|
-
throw conflictingEffects([by, i], { kind: "identity", target: { kind: "document",
|
|
422
|
+
throw conflictingEffects([by, i], { kind: "identity", target: { kind: "document", _id: info.wholeDoc } });
|
|
423
423
|
}
|
|
424
424
|
if (info.scalar) {
|
|
425
425
|
const prev = this.scalar.get(info.scalar.key);
|
|
@@ -470,30 +470,30 @@ export class SymbolTable {
|
|
|
470
470
|
function docMissing(ref) {
|
|
471
471
|
return new MutationError("doc_missing", `doc ${ref} not found`, { doc: ref });
|
|
472
472
|
}
|
|
473
|
-
/** Resolve a document target: an existing document's `{
|
|
473
|
+
/** Resolve a document target: an existing document's `{ _id, _path }`, or a `$new.n` document this plan creates. */
|
|
474
474
|
export function resolveDocumentTarget(ctx, t) {
|
|
475
|
-
if (t.
|
|
476
|
-
const created = ctx.creates.find((c) => c.symbol === t.
|
|
475
|
+
if (t._id !== undefined && isNewSymbol(t._id)) {
|
|
476
|
+
const created = ctx.creates.find((c) => c.symbol === t._id);
|
|
477
477
|
if (!created)
|
|
478
|
-
throw symbolMissing(ctx, t.
|
|
479
|
-
if (t.
|
|
480
|
-
throw effectInvalid(
|
|
481
|
-
return { kind: "document",
|
|
478
|
+
throw symbolMissing(ctx, t._id, "document");
|
|
479
|
+
if (t._path !== undefined && canonicalDocPath(t._path) !== created.path)
|
|
480
|
+
throw effectInvalid(`\`_id\` ${JSON.stringify(t._id)} and \`_path\` ${JSON.stringify(t._path)} name different documents`);
|
|
481
|
+
return { kind: "document", _id: t._id, _path: created.path };
|
|
482
482
|
}
|
|
483
483
|
return findDocumentTarget(ctx.store, ctx.repoId, t);
|
|
484
484
|
}
|
|
485
|
-
/** An existing document's `{
|
|
485
|
+
/** An existing document's `{ _id, _path }` for a literal document target (`doc_missing`; `filter_invalid` when `_id` and `_path` disagree). */
|
|
486
486
|
export function findDocumentTarget(store, repoId, t) {
|
|
487
|
-
const byId = t.
|
|
488
|
-
if (t.
|
|
489
|
-
throw docMissing(t.
|
|
490
|
-
const byPath = t.
|
|
491
|
-
if (t.
|
|
492
|
-
throw docMissing(t.
|
|
487
|
+
const byId = t._id !== undefined ? findDoc(store, { docId: t._id }) : null;
|
|
488
|
+
if (t._id !== undefined && (!byId || byId.repoId !== repoId))
|
|
489
|
+
throw docMissing(t._id);
|
|
490
|
+
const byPath = t._path !== undefined ? findDoc(store, { repoId, path: t._path }) : null;
|
|
491
|
+
if (t._path !== undefined && !byPath)
|
|
492
|
+
throw docMissing(t._path);
|
|
493
493
|
if (byId && byPath && byId.docId !== byPath.docId)
|
|
494
|
-
throw effectInvalid(
|
|
494
|
+
throw effectInvalid(`\`_id\` ${JSON.stringify(t._id)} and \`_path\` ${JSON.stringify(t._path)} name different documents`);
|
|
495
495
|
const info = (byId ?? byPath);
|
|
496
|
-
return { kind: "document",
|
|
496
|
+
return { kind: "document", _id: info.docId, _path: info.path };
|
|
497
497
|
}
|
|
498
498
|
/** A bare `$new.n` target (3.2): the kind the symbol table recorded, checked against the slot's admitted kinds. */
|
|
499
499
|
function resolveSymbolTarget(ctx, symbol, kinds, here) {
|
|
@@ -502,7 +502,7 @@ function resolveSymbolTarget(ctx, symbol, kinds, here) {
|
|
|
502
502
|
throw new MutationError("target_missing", `${symbol} is not created by an earlier effect`, { symbol });
|
|
503
503
|
if (!kinds.includes(kind))
|
|
504
504
|
throw effectInvalid(`${here}: \`kind\` must be one of ${kinds.join(", ")} (got "${kind}")`);
|
|
505
|
-
return { kind,
|
|
505
|
+
return { kind, _id: symbol };
|
|
506
506
|
}
|
|
507
507
|
function symbolMissing(ctx, symbol, wanted) {
|
|
508
508
|
const kind = ctx.symbols.kindOf(symbol);
|
|
@@ -614,9 +614,9 @@ function placeWords(ctx, into, at, verb) {
|
|
|
614
614
|
case "section":
|
|
615
615
|
return `${edge} of ${sectionName(ctx, into.heading_id)} in ${path}`;
|
|
616
616
|
case "block":
|
|
617
|
-
return `${edge} of ${blockName(ctx, into.
|
|
617
|
+
return `${edge} of ${blockName(ctx, into._id)} in ${path}`;
|
|
618
618
|
case "node":
|
|
619
|
-
return `${edge} of node ${into.
|
|
619
|
+
return `${edge} of node ${into._id} in ${path}`;
|
|
620
620
|
}
|
|
621
621
|
}
|
|
622
622
|
/** The document of a block list's first block (a kernel `remove`/`merge` names blocks of one document). */
|
|
@@ -652,7 +652,7 @@ function descendantsOf(ctx, id) {
|
|
|
652
652
|
function targetDoc(ctx, t) {
|
|
653
653
|
switch (t.kind) {
|
|
654
654
|
case "document":
|
|
655
|
-
return t.
|
|
655
|
+
return t._id;
|
|
656
656
|
case "section": {
|
|
657
657
|
const inTree = locateLoaded(ctx, t.heading_id);
|
|
658
658
|
if (inTree)
|
|
@@ -663,18 +663,18 @@ function targetDoc(ctx, t) {
|
|
|
663
663
|
return row.doc_id;
|
|
664
664
|
}
|
|
665
665
|
case "block": {
|
|
666
|
-
const inTree = locateLoaded(ctx, t.
|
|
666
|
+
const inTree = locateLoaded(ctx, t._id);
|
|
667
667
|
if (inTree)
|
|
668
668
|
return inTree.docId;
|
|
669
|
-
if (isNewSymbol(t.
|
|
670
|
-
throw symbolMissing(ctx, t.
|
|
671
|
-
const row = ctx.store.db.prepare("SELECT doc_id FROM blocks WHERE block_id = ? AND deleted_commit IS NULL").get(t.
|
|
669
|
+
if (isNewSymbol(t._id))
|
|
670
|
+
throw symbolMissing(ctx, t._id, "block");
|
|
671
|
+
const row = ctx.store.db.prepare("SELECT doc_id FROM blocks WHERE block_id = ? AND deleted_commit IS NULL").get(t._id);
|
|
672
672
|
if (!row)
|
|
673
|
-
throw new MutationError("parent_missing", `parent ${t.
|
|
673
|
+
throw new MutationError("parent_missing", `parent ${t._id} not found`);
|
|
674
674
|
return row.doc_id;
|
|
675
675
|
}
|
|
676
676
|
case "node":
|
|
677
|
-
return nodeBlock(ctx, t.
|
|
677
|
+
return nodeBlock(ctx, t._id).docId;
|
|
678
678
|
}
|
|
679
679
|
}
|
|
680
680
|
function nodeBlock(ctx, nodeId) {
|
|
@@ -693,7 +693,7 @@ function placement(ctx, into, at) {
|
|
|
693
693
|
case "section":
|
|
694
694
|
return { parent: { heading: kernelBlockId(ctx, into.heading_id), scope: "section" }, at: kernel };
|
|
695
695
|
case "block":
|
|
696
|
-
return { parent: kernelBlockId(ctx, into.
|
|
696
|
+
return { parent: kernelBlockId(ctx, into._id), at: kernel };
|
|
697
697
|
case "node":
|
|
698
698
|
throw effectInvalid("a node is not a placement target");
|
|
699
699
|
}
|
|
@@ -711,7 +711,7 @@ function checkRevision(ctx, docId, expect) {
|
|
|
711
711
|
throw new MutationError("stale_plan", `document ${docId} is at revision ${base.currentRev ?? "null"}, not ${expect.revision}; re-plan`, {
|
|
712
712
|
doc: docId,
|
|
713
713
|
expected_revision: expect.revision,
|
|
714
|
-
current: { revision: base.currentRev,
|
|
714
|
+
current: { revision: base.currentRev, _hash: base.fileHash },
|
|
715
715
|
retriable: true,
|
|
716
716
|
});
|
|
717
717
|
}
|
|
@@ -745,7 +745,7 @@ function kernelExpect(e, contentHash) {
|
|
|
745
745
|
function recordParentPin(ctx, doc, into, expect) {
|
|
746
746
|
if (expect?.parent_children_hash === undefined)
|
|
747
747
|
return;
|
|
748
|
-
const parent = into.kind === "block" ? into.
|
|
748
|
+
const parent = into.kind === "block" ? into._id : null;
|
|
749
749
|
ctx.parents.push({ doc, parent, parent_children_hash: expect.parent_children_hash });
|
|
750
750
|
}
|
|
751
751
|
// ---- lowering (§11.3, the table) -----------------------------------------------------
|
|
@@ -763,18 +763,18 @@ function describe(e) {
|
|
|
763
763
|
}
|
|
764
764
|
}
|
|
765
765
|
function targetName(t) {
|
|
766
|
-
return t.kind === "section" ? `section ${t.heading_id}` : `${t.kind} ${t.
|
|
766
|
+
return t.kind === "section" ? `section ${t.heading_id}` : `${t.kind} ${t._id}`;
|
|
767
767
|
}
|
|
768
768
|
/** The target kinds each slot admits (§11.1); `validateTarget` checks a literal, `resolveEffect` a bare `$new.n`. */
|
|
769
769
|
const PLACEMENT_KINDS = ["document", "section", "block"];
|
|
770
770
|
const DOCUMENT_KIND = ["document"];
|
|
771
771
|
const BLOCK_KIND = ["block"];
|
|
772
772
|
const SET_KINDS = ["document", "block", "node"];
|
|
773
|
-
/** Resolve one validated effect's targets (§11.4): documents to `{
|
|
773
|
+
/** Resolve one validated effect's targets (§11.4): documents to `{ _id, _path }`, defaults made explicit; `i` names the effect in messages. */
|
|
774
774
|
export function resolveEffect(ctx, e, i = 0) {
|
|
775
775
|
const here = `effects[${i}]`;
|
|
776
776
|
const target = (t, kinds, member) => {
|
|
777
|
-
const literal = t.kind === "symbol" ? resolveSymbolTarget(ctx, t.
|
|
777
|
+
const literal = t.kind === "symbol" ? resolveSymbolTarget(ctx, t._id, kinds, `${here}.${member}`) : t;
|
|
778
778
|
return literal.kind === "document" ? resolveDocumentTarget(ctx, literal) : literal;
|
|
779
779
|
};
|
|
780
780
|
const targets = (list) => list.map((t, k) => target(t, BLOCK_KIND, `targets[${k}]`));
|
|
@@ -839,10 +839,10 @@ function requireMarkdownForFrontmatter(ctx, docId, field) {
|
|
|
839
839
|
throw frontmatterNotMarkdown(docPathOf(ctx, docId), d.format, field, isNewSymbol(docId) ? null : docId);
|
|
840
840
|
}
|
|
841
841
|
function frontmatterNotMarkdown(path, format, field, docId) {
|
|
842
|
-
return new MutationError("type_mismatch", `frontmatter effects apply to markdown documents; ${referencePath(path)} is a ${format} document — its top-level mapping is edited with \`set content\` (the whole file) until frontmatter effects learn yaml and json`, { ...(docId !== null ? { doc: docId } : {}),
|
|
842
|
+
return new MutationError("type_mismatch", `frontmatter effects apply to markdown documents; ${referencePath(path)} is a ${format} document — its top-level mapping is edited with \`set content\` (the whole file) until frontmatter effects learn yaml and json`, { ...(docId !== null ? { doc: docId } : {}), _path: referencePath(path), _format: format, field });
|
|
843
843
|
}
|
|
844
844
|
/**
|
|
845
|
-
* spec/mutate §11.3 `set`/`unset
|
|
845
|
+
* spec/mutate §11.3 `set`/`unset _frontmatter.<key>` (3.6, "Frontmatter
|
|
846
846
|
* entries"): the frontmatter is edited BY ENTRY. The document's head is cut
|
|
847
847
|
* into its parts (`splitFrontmatterRaw`, the §11.3 split rule): a `set`
|
|
848
848
|
* replaces the key's part — the lead lines (a comment documenting the key)
|
|
@@ -978,7 +978,7 @@ function focusOf(ctx, e) {
|
|
|
978
978
|
return { docs: [], blocks: [], paths: [e.path] };
|
|
979
979
|
case "insert": {
|
|
980
980
|
const doc = targetDoc(ctx, e.into);
|
|
981
|
-
const blocks = [...(e.into.kind === "block" ? [e.into.
|
|
981
|
+
const blocks = [...(e.into.kind === "block" ? [e.into._id] : e.into.kind === "section" ? [e.into.heading_id] : []), ...anchorIds(e.at)];
|
|
982
982
|
return { docs: [doc], blocks, paths: [] };
|
|
983
983
|
}
|
|
984
984
|
case "set":
|
|
@@ -986,62 +986,62 @@ function focusOf(ctx, e) {
|
|
|
986
986
|
if (e.target.kind === "document") {
|
|
987
987
|
const field = e.field === "content" ? "content" : e.field;
|
|
988
988
|
return {
|
|
989
|
-
scalar: { key: `document:${e.target.
|
|
990
|
-
docs: [e.target.
|
|
991
|
-
...(field === "content" ? { wholeDoc: e.target.
|
|
989
|
+
scalar: { key: `document:${e.target._id}:${field}`, target: { kind: "document", _id: e.target._id }, field, op: e.op, value: e.op === "set" ? e.value : undefined },
|
|
990
|
+
docs: [e.target._id], blocks: [], paths: [],
|
|
991
|
+
...(field === "content" ? { wholeDoc: e.target._id, retiresDocs: [e.target._id], retiresBlocks: allBlocksOf(ctx, e.target._id) } : {}),
|
|
992
992
|
};
|
|
993
993
|
}
|
|
994
994
|
if (e.target.kind === "block") {
|
|
995
|
-
const { docId } = blockInfo(ctx, e.target.
|
|
995
|
+
const { docId } = blockInfo(ctx, e.target._id);
|
|
996
996
|
return {
|
|
997
|
-
scalar: { key: `block:${e.target.
|
|
998
|
-
docs: [docId], blocks: [e.target.
|
|
999
|
-
...(e.field === "markdown" ? { retiresBlocks: descendantsOf(ctx, e.target.
|
|
997
|
+
scalar: { key: `block:${e.target._id}:${e.field}`, target: { kind: "block", _id: e.target._id }, field: e.field, op: e.op, value: e.op === "set" ? e.value : undefined },
|
|
998
|
+
docs: [docId], blocks: [e.target._id], paths: [],
|
|
999
|
+
...(e.field === "markdown" ? { retiresBlocks: descendantsOf(ctx, e.target._id) } : {}),
|
|
1000
1000
|
};
|
|
1001
1001
|
}
|
|
1002
1002
|
const node = nodeTarget(e.target);
|
|
1003
|
-
const { docId, blockId } = nodeBlock(ctx, node.
|
|
1003
|
+
const { docId, blockId } = nodeBlock(ctx, node._id);
|
|
1004
1004
|
return {
|
|
1005
|
-
scalar: { key: `node:${node.
|
|
1005
|
+
scalar: { key: `node:${node._id}:${e.field}`, target: { kind: "node", _id: node._id }, field: e.field, op: e.op, value: e.op === "set" ? e.value : undefined },
|
|
1006
1006
|
docs: [docId], blocks: [blockId], paths: [],
|
|
1007
1007
|
};
|
|
1008
1008
|
}
|
|
1009
1009
|
case "move": {
|
|
1010
1010
|
if ("targets" in e) {
|
|
1011
|
-
const ids = e.targets.map((t) => blockTarget(t).
|
|
1011
|
+
const ids = e.targets.map((t) => blockTarget(t)._id);
|
|
1012
1012
|
const { docs } = blocksOf(ids);
|
|
1013
1013
|
const dst = targetDoc(ctx, e.into);
|
|
1014
|
-
const blocks = [...ids, ...(e.into.kind === "block" ? [e.into.
|
|
1014
|
+
const blocks = [...ids, ...(e.into.kind === "block" ? [e.into._id] : e.into.kind === "section" ? [e.into.heading_id] : []), ...anchorIds(e.at)];
|
|
1015
1015
|
return { docs: [...new Set([...docs, dst])], blocks, paths: [] };
|
|
1016
1016
|
}
|
|
1017
1017
|
const t = docTarget(e.target);
|
|
1018
1018
|
return {
|
|
1019
|
-
scalar: { key: `document:${t.
|
|
1020
|
-
docs: [t.
|
|
1019
|
+
scalar: { key: `document:${t._id}:path`, target: { kind: "document", _id: t._id }, field: "_path", op: "set", value: e.to },
|
|
1020
|
+
docs: [t._id], blocks: [], paths: [e.to], retiresPaths: [t._path],
|
|
1021
1021
|
};
|
|
1022
1022
|
}
|
|
1023
1023
|
case "remove": {
|
|
1024
1024
|
if ("targets" in e) {
|
|
1025
|
-
const ids = e.targets.map((t) => blockTarget(t).
|
|
1025
|
+
const ids = e.targets.map((t) => blockTarget(t)._id);
|
|
1026
1026
|
const { docs } = blocksOf(ids);
|
|
1027
1027
|
return { docs, blocks: ids, paths: [], retiresBlocks: ids.flatMap((id) => [id, ...descendantsOf(ctx, id)]) };
|
|
1028
1028
|
}
|
|
1029
1029
|
const t = docTarget(e.target);
|
|
1030
|
-
return { docs: [t.
|
|
1030
|
+
return { docs: [t._id], blocks: [], paths: [], retiresDocs: [t._id], retiresBlocks: allBlocksOf(ctx, t._id) };
|
|
1031
1031
|
}
|
|
1032
1032
|
case "split": {
|
|
1033
|
-
const id = blockTarget(e.target).
|
|
1033
|
+
const id = blockTarget(e.target)._id;
|
|
1034
1034
|
return { ...blocksOf([id]), paths: [] };
|
|
1035
1035
|
}
|
|
1036
1036
|
case "merge": {
|
|
1037
|
-
const ids = e.targets.map((t) => blockTarget(t).
|
|
1037
|
+
const ids = e.targets.map((t) => blockTarget(t)._id);
|
|
1038
1038
|
return { ...blocksOf(ids), paths: [], retiresBlocks: ids.slice(1).flatMap((id) => [id, ...descendantsOf(ctx, id)]) };
|
|
1039
1039
|
}
|
|
1040
1040
|
case "retarget": {
|
|
1041
1041
|
const plan = linksRepair(ctx.store, ctx.repoId, e.repairs, e.path_glob !== undefined ? { pathGlob: e.path_glob } : {});
|
|
1042
1042
|
const ids = plan.ops.map((o) => o.block);
|
|
1043
1043
|
return {
|
|
1044
|
-
scalars: ids.map((id) => ({ key: `block:${id}:markdown`, target: { kind: "block", id }, field: "markdown" })),
|
|
1044
|
+
scalars: ids.map((id) => ({ key: `block:${id}:markdown`, target: { kind: "block", _id: id }, field: "markdown" })),
|
|
1045
1045
|
...blocksOf(ids), paths: [],
|
|
1046
1046
|
};
|
|
1047
1047
|
}
|
|
@@ -1098,7 +1098,7 @@ function applyLowering(ctx, i, e) {
|
|
|
1098
1098
|
case "set":
|
|
1099
1099
|
case "unset": {
|
|
1100
1100
|
if (e.target.kind === "document") {
|
|
1101
|
-
const docId = e.target.
|
|
1101
|
+
const docId = e.target._id;
|
|
1102
1102
|
checkRevision(ctx, docId, e.expect);
|
|
1103
1103
|
if (e.field === "content") {
|
|
1104
1104
|
if (e.op !== "set" || typeof e.value !== "string")
|
|
@@ -1109,7 +1109,7 @@ function applyLowering(ctx, i, e) {
|
|
|
1109
1109
|
ctx.summary.push(`set content of ${docPathOf(ctx, docId)}`);
|
|
1110
1110
|
return;
|
|
1111
1111
|
}
|
|
1112
|
-
// `
|
|
1112
|
+
// `_frontmatter.<key>` — on a created document too (3.0): its tree is loaded, the patch is a head like any other.
|
|
1113
1113
|
const key = FRONTMATTER_FIELD.exec(e.field)[1];
|
|
1114
1114
|
requireMarkdownForFrontmatter(ctx, docId, e.field);
|
|
1115
1115
|
patchFrontmatterKey(ctx, docId, key, e.op === "set" ? { value: e.value } : null);
|
|
@@ -1119,7 +1119,7 @@ function applyLowering(ctx, i, e) {
|
|
|
1119
1119
|
if (e.op !== "set")
|
|
1120
1120
|
throw effectInvalid("`unset` takes a document target");
|
|
1121
1121
|
if (e.target.kind === "block") {
|
|
1122
|
-
const id = e.target.
|
|
1122
|
+
const id = e.target._id;
|
|
1123
1123
|
const { docId } = blockInfo(ctx, id);
|
|
1124
1124
|
checkRevision(ctx, docId, e.expect);
|
|
1125
1125
|
const hash = pin(ctx, id, e.expect?.content_hash);
|
|
@@ -1132,7 +1132,7 @@ function applyLowering(ctx, i, e) {
|
|
|
1132
1132
|
}
|
|
1133
1133
|
else {
|
|
1134
1134
|
if (typeof e.value !== "boolean")
|
|
1135
|
-
throw effectInvalid("`
|
|
1135
|
+
throw effectInvalid("`_attrs.checked` takes a boolean value");
|
|
1136
1136
|
pushOp(ctx, { op: "update", block: kernelBlockId(ctx, id), attrs: { checked: e.value }, expect: kernelExpect(e.expect, hash) });
|
|
1137
1137
|
ctx.summary.push(`${e.value ? "check" : "uncheck"} ${name} in ${docPathOf(ctx, docId)}`);
|
|
1138
1138
|
}
|
|
@@ -1140,20 +1140,20 @@ function applyLowering(ctx, i, e) {
|
|
|
1140
1140
|
}
|
|
1141
1141
|
// node: the adapter's editor → one update (macros.ts nodeSet)
|
|
1142
1142
|
const node = nodeTarget(e.target);
|
|
1143
|
-
const { docId, blockId } = nodeBlock(ctx, node.
|
|
1143
|
+
const { docId, blockId } = nodeBlock(ctx, node._id);
|
|
1144
1144
|
checkRevision(ctx, docId, e.expect);
|
|
1145
|
-
const ops = nodeSet(ctx.store, node.
|
|
1145
|
+
const ops = nodeSet(ctx.store, node._id, e.field, typeof e.value === "string" ? e.value : ctx.canonical(e.value));
|
|
1146
1146
|
for (const op of ops) {
|
|
1147
1147
|
const u = op;
|
|
1148
1148
|
const hash = pin(ctx, blockId, e.expect?.content_hash ?? u.expect?.content_hash);
|
|
1149
1149
|
pushOp(ctx, { ...u, expect: { ...(u.expect ?? {}), content_hash: hash } });
|
|
1150
1150
|
}
|
|
1151
|
-
ctx.summary.push(`set ${e.field} = ${ctx.canonical(e.value)} on node ${node.
|
|
1151
|
+
ctx.summary.push(`set ${e.field} = ${ctx.canonical(e.value)} on node ${node._id} in ${docPathOf(ctx, docId)}`);
|
|
1152
1152
|
return;
|
|
1153
1153
|
}
|
|
1154
1154
|
case "move": {
|
|
1155
1155
|
if ("targets" in e) {
|
|
1156
|
-
const ids = e.targets.map((t) => blockTarget(t).
|
|
1156
|
+
const ids = e.targets.map((t) => blockTarget(t)._id);
|
|
1157
1157
|
const dst = targetDoc(ctx, e.into);
|
|
1158
1158
|
checkRevision(ctx, dst, e.expect);
|
|
1159
1159
|
recordParentPin(ctx, dst, e.into, e.expect);
|
|
@@ -1168,11 +1168,11 @@ function applyLowering(ctx, i, e) {
|
|
|
1168
1168
|
return;
|
|
1169
1169
|
}
|
|
1170
1170
|
const t = docTarget(e.target);
|
|
1171
|
-
if (isNewSymbol(t.
|
|
1172
|
-
throw new MutationError("target_missing", `${t.
|
|
1173
|
-
const base = readDocBase(ctx.store.db, t.
|
|
1171
|
+
if (isNewSymbol(t._id))
|
|
1172
|
+
throw new MutationError("target_missing", `${t._id} is a document this plan creates; it cannot be moved in the same plan`, { symbol: t._id });
|
|
1173
|
+
const base = readDocBase(ctx.store.db, t._id);
|
|
1174
1174
|
if (!base)
|
|
1175
|
-
throw docMissing(t.
|
|
1175
|
+
throw docMissing(t._id);
|
|
1176
1176
|
if (findDoc(ctx.store, { repoId: ctx.repoId, path: e.to }))
|
|
1177
1177
|
throw new MutationError("path_taken", `a document already exists at ${e.to}`, { path: e.to });
|
|
1178
1178
|
if (ctx.absentPaths.includes(e.to))
|
|
@@ -1180,13 +1180,13 @@ function applyLowering(ctx, i, e) {
|
|
|
1180
1180
|
if (ctx.docStore.exists(e.to))
|
|
1181
1181
|
throw new MutationError("path_taken", `file already exists on disk at ${e.to}`, { path: e.to });
|
|
1182
1182
|
ctx.absentPaths.push(e.to);
|
|
1183
|
-
if (!ctx.docBases.has(t.
|
|
1184
|
-
ctx.docBases.set(t.
|
|
1185
|
-
const inbound = inboundLinksTo(ctx.store, ctx.repoId, t.
|
|
1183
|
+
if (!ctx.docBases.has(t._id))
|
|
1184
|
+
ctx.docBases.set(t._id, base);
|
|
1185
|
+
const inbound = inboundLinksTo(ctx.store, ctx.repoId, t._id, t._path);
|
|
1186
1186
|
let retargeted = null;
|
|
1187
1187
|
let links = "";
|
|
1188
1188
|
if (e.retarget_inbound && inbound.length > 0) {
|
|
1189
|
-
const plan = planInboundRetarget(ctx.store, t.
|
|
1189
|
+
const plan = planInboundRetarget(ctx.store, t._id, t._path, e.to, inbound);
|
|
1190
1190
|
for (const op of plan.ops) {
|
|
1191
1191
|
const u = op;
|
|
1192
1192
|
pin(ctx, u.block, u.expect?.content_hash);
|
|
@@ -1195,13 +1195,13 @@ function applyLowering(ctx, i, e) {
|
|
|
1195
1195
|
retargeted = { blocks: plan.blocks, docs: plan.docs };
|
|
1196
1196
|
links = ` (retarget ${plural(inbound.length - plan.dangling.length, "link")} in ${plural(plan.docs.length, "document")})`;
|
|
1197
1197
|
}
|
|
1198
|
-
ctx.moves.push({ id: t.
|
|
1199
|
-
ctx.summary.push(`move ${referencePath(t.
|
|
1198
|
+
ctx.moves.push({ id: t._id, from: t._path, to: e.to, currentRev: base.currentRev, inbound, retargeted });
|
|
1199
|
+
ctx.summary.push(`move ${referencePath(t._path)} → ${referencePath(e.to)}${links}`);
|
|
1200
1200
|
return;
|
|
1201
1201
|
}
|
|
1202
1202
|
case "remove": {
|
|
1203
1203
|
if ("targets" in e) {
|
|
1204
|
-
const ids = e.targets.map((t) => blockTarget(t).
|
|
1204
|
+
const ids = e.targets.map((t) => blockTarget(t)._id);
|
|
1205
1205
|
const expect = {};
|
|
1206
1206
|
for (const id of ids) {
|
|
1207
1207
|
const given = e.expect?.[id];
|
|
@@ -1214,19 +1214,19 @@ function applyLowering(ctx, i, e) {
|
|
|
1214
1214
|
return;
|
|
1215
1215
|
}
|
|
1216
1216
|
const t = docTarget(e.target);
|
|
1217
|
-
if (isNewSymbol(t.
|
|
1218
|
-
throw new MutationError("target_missing", `${t.
|
|
1219
|
-
const base = readDocBase(ctx.store.db, t.
|
|
1217
|
+
if (isNewSymbol(t._id))
|
|
1218
|
+
throw new MutationError("target_missing", `${t._id} is a document this plan creates; it cannot be removed in the same plan`, { symbol: t._id });
|
|
1219
|
+
const base = readDocBase(ctx.store.db, t._id);
|
|
1220
1220
|
if (!base)
|
|
1221
|
-
throw docMissing(t.
|
|
1222
|
-
if (!ctx.docBases.has(t.
|
|
1223
|
-
ctx.docBases.set(t.
|
|
1224
|
-
ctx.tombstones.push({ id: t.
|
|
1225
|
-
ctx.summary.push(`remove ${referencePath(t.
|
|
1221
|
+
throw docMissing(t._id);
|
|
1222
|
+
if (!ctx.docBases.has(t._id))
|
|
1223
|
+
ctx.docBases.set(t._id, base);
|
|
1224
|
+
ctx.tombstones.push({ id: t._id, path: t._path, currentRev: base.currentRev });
|
|
1225
|
+
ctx.summary.push(`remove ${referencePath(t._path)}`);
|
|
1226
1226
|
return;
|
|
1227
1227
|
}
|
|
1228
1228
|
case "split": {
|
|
1229
|
-
const id = blockTarget(e.target).
|
|
1229
|
+
const id = blockTarget(e.target)._id;
|
|
1230
1230
|
const { docId } = blockInfo(ctx, id);
|
|
1231
1231
|
checkRevision(ctx, docId, e.expect);
|
|
1232
1232
|
const hash = pin(ctx, id, e.expect?.content_hash);
|
|
@@ -1236,7 +1236,7 @@ function applyLowering(ctx, i, e) {
|
|
|
1236
1236
|
return;
|
|
1237
1237
|
}
|
|
1238
1238
|
case "merge": {
|
|
1239
|
-
const ids = e.targets.map((t) => blockTarget(t).
|
|
1239
|
+
const ids = e.targets.map((t) => blockTarget(t)._id);
|
|
1240
1240
|
const expect = {};
|
|
1241
1241
|
for (const id of ids) {
|
|
1242
1242
|
const given = e.expect?.[id];
|
|
@@ -1256,7 +1256,7 @@ function applyLowering(ctx, i, e) {
|
|
|
1256
1256
|
pin(ctx, u.block, u.expect?.content_hash);
|
|
1257
1257
|
pushOp(ctx, u);
|
|
1258
1258
|
}
|
|
1259
|
-
const docs = new Set(plan.hits.map((h) => h.
|
|
1259
|
+
const docs = new Set(plan.hits.map((h) => h._path)).size;
|
|
1260
1260
|
const pair = e.repairs.length === 1 ? `${e.repairs[0].from} → ${e.repairs[0].to}` : `over ${plural(e.repairs.length, "pair")}`;
|
|
1261
1261
|
ctx.summary.push(`retarget ${plural(plan.hits.length, "link")} ${pair} in ${plural(docs, "document")}`);
|
|
1262
1262
|
return;
|
|
@@ -28,13 +28,13 @@ export declare function listsInsertItem(anchor: string, at: "start" | "end" | {
|
|
|
28
28
|
} | {
|
|
29
29
|
after: string;
|
|
30
30
|
}, markdown: string): Op[];
|
|
31
|
-
/** One rewritten block of a retarget/repair (spec/surface §4, 3.0): the block's
|
|
32
|
-
* `
|
|
33
|
-
* and the raw before/after. */
|
|
31
|
+
/** One rewritten block of a retarget/repair (spec/surface §4, 3.0; 5.0 "underscore = engine"): the block's
|
|
32
|
+
* `_id`, its document's `_path` (the storage form here; the surface roots it),
|
|
33
|
+
* and the raw before/after (`oldRaw`/`newRaw` name roles). */
|
|
34
34
|
export interface RetargetHit {
|
|
35
|
-
|
|
35
|
+
_id: string;
|
|
36
36
|
/** repo-relative path of the block's document */
|
|
37
|
-
|
|
37
|
+
_path: string;
|
|
38
38
|
oldRaw: string;
|
|
39
39
|
newRaw: string;
|
|
40
40
|
}
|
|
@@ -230,7 +230,7 @@ export function linksRepair(store, repoId, repairs, opts = {}) {
|
|
|
230
230
|
c.tally.forEach((n, i) => { effective[i].hits += n; });
|
|
231
231
|
const hash = rawHashOfBlock(store, blockId);
|
|
232
232
|
ops.push({ op: "update", block: blockId, markdown: c.newRaw, ...(hash ? { expect: { content_hash: hash } } : {}) });
|
|
233
|
-
hits.push({
|
|
233
|
+
hits.push({ _id: blockId, _path: c.path, oldRaw: c.oldRaw, newRaw: c.newRaw });
|
|
234
234
|
}
|
|
235
235
|
return { ops, hits, pairs };
|
|
236
236
|
}
|
|
@@ -41,9 +41,9 @@ function renderedBytes(store, row) {
|
|
|
41
41
|
if (row.revId === null)
|
|
42
42
|
return null;
|
|
43
43
|
const at = readDocumentAtRevision(store, row.docId, row.revId);
|
|
44
|
-
if (!at || !sha256(at.
|
|
44
|
+
if (!at || !sha256(at._raw).equals(row.renderedHash))
|
|
45
45
|
return null;
|
|
46
|
-
return at.
|
|
46
|
+
return at._raw;
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
49
49
|
* Materialize one row through `docStore` and, when the file is as recorded,
|
package/dist/src/mutate/ops.js
CHANGED
|
@@ -109,7 +109,7 @@ function checkContentHash(block, expect, opIndex) {
|
|
|
109
109
|
throw new MutationError("stale_expectation", "expect.content_hash required", {
|
|
110
110
|
op_index: opIndex,
|
|
111
111
|
block: block.id,
|
|
112
|
-
current: {
|
|
112
|
+
current: { _hash: rawHashHex(block.raw), markdown: block.raw },
|
|
113
113
|
retriable: true,
|
|
114
114
|
});
|
|
115
115
|
}
|
|
@@ -119,7 +119,7 @@ function checkContentHash(block, expect, opIndex) {
|
|
|
119
119
|
op_index: opIndex,
|
|
120
120
|
block: block.id,
|
|
121
121
|
expected_content_hash: expect.content_hash,
|
|
122
|
-
current: {
|
|
122
|
+
current: { _hash: current, markdown: block.raw },
|
|
123
123
|
retriable: true,
|
|
124
124
|
});
|
|
125
125
|
}
|