@superdoc/sdk 2.18.0-next.1 → 2.18.0-next.3

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.
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ var documentEvidence = require('../runtime/document-evidence.cjs');
4
+
3
5
  function asString(v, fallback = '') {
4
6
  return typeof v === 'string' ? v : fallback;
5
7
  }
@@ -278,14 +280,34 @@ async function buildDocumentSnapshot(doc, options = {}) {
278
280
  };
279
281
  }
280
282
  // Blocks
281
- const includeBlocksInSnapshot = includesDomain(requestedDomains, 'blocks');
282
- const needBlocksRead = includeBlocksInSnapshot || includesDomain(requestedDomains, 'tables');
283
+ const includeBlocksInSnapshot = !options.countSummary && includesDomain(requestedDomains, 'blocks');
284
+ const needBlocksRead = options.countSummary || includeBlocksInSnapshot || includesDomain(requestedDomains, 'tables');
283
285
  const blocksFn = maybeMethod(doc, ['blocks', 'list']);
284
286
  const blocksRaw = needBlocksRead && blocksFn
285
- ? await safeCall(() => blocksFn({ offset: blockOffset, limit: blockLimit, includeText: includeBlocksInSnapshot }), null, recordError('blocks.list'))
287
+ ? await safeCall(() => blocksFn({
288
+ offset: options.countSummary ? Number.MAX_SAFE_INTEGER : blockOffset,
289
+ limit: options.countSummary ? 1 : blockLimit,
290
+ includeText: includeBlocksInSnapshot,
291
+ }), null, recordError('blocks.list'))
286
292
  : null;
287
293
  const blocksRec = asRecord(blocksRaw);
288
294
  const totalBlocks = explicitCount(blocksRec ?? {}, 'total') ?? countsFromInfo.blocks;
295
+ if (options.countSummary && blocksFn) {
296
+ if (blocksRec?.revision !== revision)
297
+ throw new MutationSnapshotError('REVISION_CONFLICT', 'Count summary revision changed.');
298
+ for (const [key, nodeType] of [
299
+ ['paragraphs', 'paragraph'],
300
+ ['headings', 'heading'],
301
+ ['tables', 'table'],
302
+ ]) {
303
+ if (countsFromInfo[key] !== 0)
304
+ continue;
305
+ const page = asRecord(await blocksFn({ nodeTypes: [nodeType], offset: Number.MAX_SAFE_INTEGER, limit: 1, includeText: false }));
306
+ if (page?.revision !== revision || typeof page.total !== 'number')
307
+ throw new MutationSnapshotError('REVISION_CONFLICT', 'Required count summary is unavailable.');
308
+ countsFromInfo[key] = page.total;
309
+ }
310
+ }
289
311
  const rawBlocks = Array.isArray(blocksRec?.blocks) ? blocksRec.blocks : [];
290
312
  const fullBlockText = new Map();
291
313
  const normalizedBlocks = rawBlocks.map((b, index) => {
@@ -743,6 +765,18 @@ class MutationSnapshotError extends Error {
743
765
  this.name = 'MutationSnapshotError';
744
766
  }
745
767
  }
768
+ async function buildExecutionSummary(doc) {
769
+ const summary = await buildDocumentSnapshot(doc, { countSummary: true });
770
+ if (summary.revision === 'unknown' ||
771
+ summary.diagnostics.some((item) => ['info', 'blocks.list'].includes(item.section)))
772
+ throw new MutationSnapshotError('SNAPSHOT_INCOMPLETE', 'Required summary evidence is unavailable.');
773
+ const readRevision = documentEvidence.getDocumentRevisionReader(doc);
774
+ const revision = readRevision
775
+ ? await readRevision()
776
+ : (await doc.blocks.list({ offset: Number.MAX_SAFE_INTEGER, limit: 1 })).revision;
777
+ assertMutationSnapshotRevision(summary.revision, revision ?? 'unknown');
778
+ return summary;
779
+ }
746
780
  const MUTATION_BLOCK_PAGE_SIZE = 1000;
747
781
  const MUTATION_BLOCK_PAGE_CONCURRENCY = 4;
748
782
  const MUTATION_NON_TABLE_DOMAINS = [
@@ -1009,6 +1043,7 @@ function resolveSnapshotSelector(snapshot, selector) {
1009
1043
  exports.AmbiguousSelectorError = AmbiguousSelectorError;
1010
1044
  exports.MutationSnapshotError = MutationSnapshotError;
1011
1045
  exports.buildDocumentSnapshot = buildDocumentSnapshot;
1046
+ exports.buildExecutionSummary = buildExecutionSummary;
1012
1047
  exports.buildMutationSnapshot = buildMutationSnapshot;
1013
1048
  exports.matchRunsForBlock = matchRunsForBlock;
1014
1049
  exports.normalizeSnapshotBlock = normalizeSnapshotBlock;
@@ -234,6 +234,7 @@ export type DocumentSnapshot = {
234
234
  export declare function matchRunsForBlock(queryFn: (args: Record<string, unknown>) => Promise<unknown>, text: string, nodeId: string): Promise<SnapshotRun[]>;
235
235
  type SnapshotOptions = {
236
236
  countsOnly?: boolean;
237
+ countSummary?: boolean;
237
238
  includeDomains?: readonly SnapshotDomain[];
238
239
  blockNodeTypes?: readonly string[];
239
240
  blockTextLimit?: number;
@@ -319,6 +320,7 @@ export declare class MutationSnapshotError extends Error {
319
320
  readonly code: 'SNAPSHOT_INCOMPLETE' | 'REVISION_CONFLICT';
320
321
  constructor(code: 'SNAPSHOT_INCOMPLETE' | 'REVISION_CONFLICT', message: string);
321
322
  }
323
+ export declare function buildExecutionSummary(doc: BoundDocApi): Promise<DocumentSnapshot>;
322
324
  /**
323
325
  * Build the complete block index used to authorize mutations. Inspection
324
326
  * snapshots stay windowed; mutation selectors must not treat that presentation
@@ -1,3 +1,4 @@
1
+ import { getDocumentRevisionReader } from '../runtime/document-evidence.js';
1
2
  function asString(v, fallback = '') {
2
3
  return typeof v === 'string' ? v : fallback;
3
4
  }
@@ -276,14 +277,34 @@ export async function buildDocumentSnapshot(doc, options = {}) {
276
277
  };
277
278
  }
278
279
  // Blocks
279
- const includeBlocksInSnapshot = includesDomain(requestedDomains, 'blocks');
280
- const needBlocksRead = includeBlocksInSnapshot || includesDomain(requestedDomains, 'tables');
280
+ const includeBlocksInSnapshot = !options.countSummary && includesDomain(requestedDomains, 'blocks');
281
+ const needBlocksRead = options.countSummary || includeBlocksInSnapshot || includesDomain(requestedDomains, 'tables');
281
282
  const blocksFn = maybeMethod(doc, ['blocks', 'list']);
282
283
  const blocksRaw = needBlocksRead && blocksFn
283
- ? await safeCall(() => blocksFn({ offset: blockOffset, limit: blockLimit, includeText: includeBlocksInSnapshot }), null, recordError('blocks.list'))
284
+ ? await safeCall(() => blocksFn({
285
+ offset: options.countSummary ? Number.MAX_SAFE_INTEGER : blockOffset,
286
+ limit: options.countSummary ? 1 : blockLimit,
287
+ includeText: includeBlocksInSnapshot,
288
+ }), null, recordError('blocks.list'))
284
289
  : null;
285
290
  const blocksRec = asRecord(blocksRaw);
286
291
  const totalBlocks = explicitCount(blocksRec ?? {}, 'total') ?? countsFromInfo.blocks;
292
+ if (options.countSummary && blocksFn) {
293
+ if (blocksRec?.revision !== revision)
294
+ throw new MutationSnapshotError('REVISION_CONFLICT', 'Count summary revision changed.');
295
+ for (const [key, nodeType] of [
296
+ ['paragraphs', 'paragraph'],
297
+ ['headings', 'heading'],
298
+ ['tables', 'table'],
299
+ ]) {
300
+ if (countsFromInfo[key] !== 0)
301
+ continue;
302
+ const page = asRecord(await blocksFn({ nodeTypes: [nodeType], offset: Number.MAX_SAFE_INTEGER, limit: 1, includeText: false }));
303
+ if (page?.revision !== revision || typeof page.total !== 'number')
304
+ throw new MutationSnapshotError('REVISION_CONFLICT', 'Required count summary is unavailable.');
305
+ countsFromInfo[key] = page.total;
306
+ }
307
+ }
287
308
  const rawBlocks = Array.isArray(blocksRec?.blocks) ? blocksRec.blocks : [];
288
309
  const fullBlockText = new Map();
289
310
  const normalizedBlocks = rawBlocks.map((b, index) => {
@@ -741,6 +762,18 @@ export class MutationSnapshotError extends Error {
741
762
  this.name = 'MutationSnapshotError';
742
763
  }
743
764
  }
765
+ export async function buildExecutionSummary(doc) {
766
+ const summary = await buildDocumentSnapshot(doc, { countSummary: true });
767
+ if (summary.revision === 'unknown' ||
768
+ summary.diagnostics.some((item) => ['info', 'blocks.list'].includes(item.section)))
769
+ throw new MutationSnapshotError('SNAPSHOT_INCOMPLETE', 'Required summary evidence is unavailable.');
770
+ const readRevision = getDocumentRevisionReader(doc);
771
+ const revision = readRevision
772
+ ? await readRevision()
773
+ : (await doc.blocks.list({ offset: Number.MAX_SAFE_INTEGER, limit: 1 })).revision;
774
+ assertMutationSnapshotRevision(summary.revision, revision ?? 'unknown');
775
+ return summary;
776
+ }
744
777
  const MUTATION_BLOCK_PAGE_SIZE = 1000;
745
778
  const MUTATION_BLOCK_PAGE_CONCURRENCY = 4;
746
779
  const MUTATION_NON_TABLE_DOMAINS = [
@@ -184,6 +184,7 @@ class ExecutionContext {
184
184
  fallbackReason;
185
185
  currentRevision;
186
186
  legacyRevisionOnly = false;
187
+ preserveSummaryCounts = false;
187
188
  constructor(source, requirements = {}) {
188
189
  this.source = source;
189
190
  this.requirements = requirements;
@@ -268,9 +269,11 @@ class ExecutionContext {
268
269
  ? 'verification requires complete evidence'
269
270
  : undefined);
270
271
  const complete = evidence === 'full' || this.fallbackReason ? await docSnapshot.buildMutationSnapshot(this.source) : undefined;
272
+ this.preserveSummaryCounts = !complete && documentEvidence.requiresSummaryCounts(this.source);
273
+ const summary = this.preserveSummaryCounts ? await docSnapshot.buildExecutionSummary(this.source) : undefined;
271
274
  if (complete && (complete.revision === 'unknown' || complete.diagnostics.some((item) => item.section === 'info')))
272
275
  throw new errors.SuperDocCliError('Required complete evidence is unavailable.', { code: 'EVIDENCE_UNAVAILABLE' });
273
- this.pre = new DocumentFacts(this.source, complete?.revision ?? compactRevision ?? (await readPublicRevision(this.source)), complete);
276
+ this.pre = new DocumentFacts(this.source, complete?.revision ?? summary?.revision ?? compactRevision ?? (await readPublicRevision(this.source)), complete, summary?.counts);
274
277
  this.currentRevision = this.pre.revision;
275
278
  await this.pre.prepare(checks);
276
279
  return this.pre;
@@ -278,7 +281,11 @@ class ExecutionContext {
278
281
  async finishRevision() {
279
282
  if (this.requirements.evidence === 'full')
280
283
  return this.finish();
281
- const summary = this.pre?.complete ? await docSnapshot.buildDocumentSnapshot(this.source, { countsOnly: true }) : undefined;
284
+ const summary = this.preserveSummaryCounts
285
+ ? await docSnapshot.buildExecutionSummary(this.source)
286
+ : this.pre?.complete
287
+ ? await docSnapshot.buildDocumentSnapshot(this.source, { countsOnly: true })
288
+ : undefined;
282
289
  if (summary && (summary.revision === 'unknown' || summary.diagnostics.some((item) => item.section === 'info')))
283
290
  throw new errors.SuperDocCliError('Required summary is unavailable.', { code: 'EVIDENCE_UNAVAILABLE' });
284
291
  const revision = summary?.revision ?? (await readPublicRevision(this.source));
@@ -289,11 +296,12 @@ class ExecutionContext {
289
296
  }
290
297
  async finish() {
291
298
  const complete = this.pre?.complete ? await docSnapshot.buildMutationSnapshot(this.source) : undefined;
299
+ const summary = this.preserveSummaryCounts ? await docSnapshot.buildExecutionSummary(this.source) : undefined;
292
300
  if (complete && (complete.revision === 'unknown' || complete.diagnostics.some((item) => item.section === 'info')))
293
301
  throw new errors.SuperDocCliError('Required complete evidence is unavailable.', { code: 'EVIDENCE_UNAVAILABLE' });
294
- const revision = complete?.revision ?? (await readPublicRevision(this.source));
302
+ const revision = complete?.revision ?? summary?.revision ?? (await readPublicRevision(this.source));
295
303
  this.assertExecutionRevision(revision);
296
- this.post = new DocumentFacts(this.source, revision, complete);
304
+ this.post = new DocumentFacts(this.source, revision, complete, summary?.counts);
297
305
  this.currentRevision = this.post.revision;
298
306
  return this.post;
299
307
  }
@@ -86,6 +86,7 @@ export declare class ExecutionContext {
86
86
  fallbackReason?: string;
87
87
  private currentRevision?;
88
88
  private legacyRevisionOnly;
89
+ private preserveSummaryCounts;
89
90
  constructor(source: BoundDocApi, requirements?: EvidenceRequirements);
90
91
  start(): Promise<DocumentFacts>;
91
92
  finishRevision(): Promise<DocumentFacts>;
@@ -1,6 +1,6 @@
1
- import { getDocumentRevisionReader, supportsDocumentFacts, forwardDocumentEvidence, getMutationRevision, } from '../runtime/document-evidence.js';
1
+ import { getDocumentRevisionReader, supportsDocumentFacts, requiresSummaryCounts, forwardDocumentEvidence, getMutationRevision, } from '../runtime/document-evidence.js';
2
2
  import { SuperDocCliError } from '../runtime/errors.js';
3
- import { buildDocumentSnapshot, buildMutationSnapshot, resolveSnapshotSelector, } from './doc-snapshot.js';
3
+ import { buildDocumentSnapshot, buildMutationSnapshot, buildExecutionSummary, resolveSnapshotSelector, } from './doc-snapshot.js';
4
4
  import { getOperationCatalogEntry } from './operation-catalog.js';
5
5
  export function supportsLocalSelector(selector) {
6
6
  return (selector.kind === 'nodeId' ||
@@ -181,6 +181,7 @@ export class ExecutionContext {
181
181
  fallbackReason;
182
182
  currentRevision;
183
183
  legacyRevisionOnly = false;
184
+ preserveSummaryCounts = false;
184
185
  constructor(source, requirements = {}) {
185
186
  this.source = source;
186
187
  this.requirements = requirements;
@@ -265,9 +266,11 @@ export class ExecutionContext {
265
266
  ? 'verification requires complete evidence'
266
267
  : undefined);
267
268
  const complete = evidence === 'full' || this.fallbackReason ? await buildMutationSnapshot(this.source) : undefined;
269
+ this.preserveSummaryCounts = !complete && requiresSummaryCounts(this.source);
270
+ const summary = this.preserveSummaryCounts ? await buildExecutionSummary(this.source) : undefined;
268
271
  if (complete && (complete.revision === 'unknown' || complete.diagnostics.some((item) => item.section === 'info')))
269
272
  throw new SuperDocCliError('Required complete evidence is unavailable.', { code: 'EVIDENCE_UNAVAILABLE' });
270
- this.pre = new DocumentFacts(this.source, complete?.revision ?? compactRevision ?? (await readPublicRevision(this.source)), complete);
273
+ this.pre = new DocumentFacts(this.source, complete?.revision ?? summary?.revision ?? compactRevision ?? (await readPublicRevision(this.source)), complete, summary?.counts);
271
274
  this.currentRevision = this.pre.revision;
272
275
  await this.pre.prepare(checks);
273
276
  return this.pre;
@@ -275,7 +278,11 @@ export class ExecutionContext {
275
278
  async finishRevision() {
276
279
  if (this.requirements.evidence === 'full')
277
280
  return this.finish();
278
- const summary = this.pre?.complete ? await buildDocumentSnapshot(this.source, { countsOnly: true }) : undefined;
281
+ const summary = this.preserveSummaryCounts
282
+ ? await buildExecutionSummary(this.source)
283
+ : this.pre?.complete
284
+ ? await buildDocumentSnapshot(this.source, { countsOnly: true })
285
+ : undefined;
279
286
  if (summary && (summary.revision === 'unknown' || summary.diagnostics.some((item) => item.section === 'info')))
280
287
  throw new SuperDocCliError('Required summary is unavailable.', { code: 'EVIDENCE_UNAVAILABLE' });
281
288
  const revision = summary?.revision ?? (await readPublicRevision(this.source));
@@ -286,11 +293,12 @@ export class ExecutionContext {
286
293
  }
287
294
  async finish() {
288
295
  const complete = this.pre?.complete ? await buildMutationSnapshot(this.source) : undefined;
296
+ const summary = this.preserveSummaryCounts ? await buildExecutionSummary(this.source) : undefined;
289
297
  if (complete && (complete.revision === 'unknown' || complete.diagnostics.some((item) => item.section === 'info')))
290
298
  throw new SuperDocCliError('Required complete evidence is unavailable.', { code: 'EVIDENCE_UNAVAILABLE' });
291
- const revision = complete?.revision ?? (await readPublicRevision(this.source));
299
+ const revision = complete?.revision ?? summary?.revision ?? (await readPublicRevision(this.source));
292
300
  this.assertExecutionRevision(revision);
293
- this.post = new DocumentFacts(this.source, revision, complete);
301
+ this.post = new DocumentFacts(this.source, revision, complete, summary?.counts);
294
302
  this.currentRevision = this.post.revision;
295
303
  return this.post;
296
304
  }