@stll/folio-core 0.47.3 → 0.47.4

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.
@@ -164,16 +164,49 @@ const FOLIO_RESOLVED_STORY_SERIALIZATION_MISMATCHES = Object.freeze({
164
164
  blockProjection: "block-projection"
165
165
  });
166
166
  var FolioResolvedStorySerializationError = class extends TaggedError("FolioResolvedStorySerializationError") {};
167
+ /** Blocks whose id folio minted, because the package names no id for them. */
168
+ const mintedBlockOrdinals = (snapshot) => {
169
+ const ordinals = /* @__PURE__ */ new Set();
170
+ for (const [ordinal, block] of snapshot.blocks.entries()) if (block.idStability === "positional") ordinals.add(ordinal);
171
+ return ordinals;
172
+ };
167
173
  /**
168
- * `idStability` records how an id entered the current snapshot. A synthesized
169
- * paraId is positional until save writes it into the package, then becomes an
170
- * authored stable id when reopened. Compare the persisted block projection
171
- * without changing that live snapshot identity contract.
174
+ * What a block looks like once the story has been through the package.
175
+ *
176
+ * `idStability` is the snapshot's own fact rather than the package's, so it
177
+ * goes. `minted` names the blocks whose id folio invented for a paragraph the
178
+ * package gives no `w14:paraId`, and each of those stands down to its position.
179
+ * A minted id is derived from the paragraph's text and ordinal, so resolving
180
+ * its text can make an id-less paragraph derive a different id when reopened.
181
+ * The two save paths also disagree about whether it reaches the file at all:
182
+ * the selective patch keeps the author's id-less convention (see
183
+ * `withoutMintedIds`) while a full repack writes the model's ids. Which path
184
+ * ran is not what this check is about, so neither the id nor the choice between
185
+ * them may decide it. An authored id is compared exactly as it is, and a block
186
+ * that moved still fails.
172
187
  */
173
- const resolvedStoryBlockProjection = (block) => {
188
+ const resolvedStoryBlockProjection = (block, ordinal, minted) => {
174
189
  const persisted = { ...block };
175
190
  delete persisted.idStability;
176
- return persisted;
191
+ return minted.has(ordinal) ? {
192
+ ...persisted,
193
+ id: `minted-${String(ordinal)}`
194
+ } : persisted;
195
+ };
196
+ /**
197
+ * The two halves of the expectation, from one projection.
198
+ *
199
+ * The reading text carries each block's id, so it has to be built from the
200
+ * same projected blocks the block comparison uses; formatting the raw snapshot
201
+ * instead would put a minted id back into the text and fail the check the
202
+ * block projection just passed.
203
+ */
204
+ const resolvedStoryProjection = (snapshot, minted) => {
205
+ const blocks = snapshot.blocks.map((block, ordinal) => resolvedStoryBlockProjection(block, ordinal, minted));
206
+ return {
207
+ text: blocks.filter(isFolioAIContentBlock).map(formatBlockForLLM).join("\n"),
208
+ blocks
209
+ };
177
210
  };
178
211
  const numberingLevelsOf = (numbering) => {
179
212
  if (!numbering) return [];
@@ -735,10 +768,11 @@ var FolioDocxReviewer = class FolioDocxReviewer {
735
768
  const resolvedState = resolveReviewedState(sourceState, view);
736
769
  this.setEditableStoryState(story, resolvedState);
737
770
  const snapshot = createStateSnapshot(resolvedState);
771
+ const minted = mintedBlockOrdinals(snapshot);
738
772
  this.resolvedStoryExpectations.set(editableStoryKey(story), {
739
773
  story,
740
- text: formatStorySnapshotForLLM(snapshot, false),
741
- blocks: snapshot.blocks.map(resolvedStoryBlockProjection)
774
+ minted,
775
+ ...resolvedStoryProjection(snapshot, minted)
742
776
  });
743
777
  return snapshot;
744
778
  }
@@ -1356,13 +1390,14 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1356
1390
  async assertResolvedStoriesSerialized(buffer, expectations) {
1357
1391
  if (expectations.length === 0) return;
1358
1392
  const reopened = await FolioDocxReviewer.fromBuffer(buffer);
1359
- for (const { story, text, blocks } of expectations) {
1393
+ for (const { story, text, blocks, minted } of expectations) {
1360
1394
  const serialized = reopened.readReviewedStory({
1361
1395
  story,
1362
1396
  view: "current-markup"
1363
1397
  });
1364
- const serializedText = serialized ? formatStorySnapshotForLLM(serialized.snapshot, false) : null;
1365
- const serializedBlocks = serialized ? serialized.snapshot.blocks.map(resolvedStoryBlockProjection) : null;
1398
+ const persisted = serialized ? resolvedStoryProjection(serialized.snapshot, minted) : null;
1399
+ const serializedText = persisted?.text ?? null;
1400
+ const serializedBlocks = persisted?.blocks ?? null;
1366
1401
  const mismatches = [];
1367
1402
  if (!serialized) mismatches.push(FOLIO_RESOLVED_STORY_SERIALIZATION_MISMATCHES.storyMissing);
1368
1403
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/folio-core",
3
- "version": "0.47.3",
3
+ "version": "0.47.4",
4
4
  "description": "Headless, framework-neutral core of folio: the OOXML (.docx) parser, document model, ProseMirror integration, and page-layout engine. No React.",
5
5
  "keywords": [
6
6
  "document-model",