@bendyline/docblocks 2.7.8 → 2.7.9

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.
@@ -14,8 +14,8 @@ This notice is included in the published npm tarball. Transitive packages instal
14
14
 
15
15
  | Package | Version | License | Source |
16
16
  | --- | --- | --- | --- |
17
- | @bendyline/squisq | 2.13.6 | MIT | https://github.com/bendyline/squisq |
18
- | @bendyline/squisq-graph | 2.13.0 | MIT | https://github.com/bendyline/squisq |
17
+ | @bendyline/squisq | 2.13.8 | MIT | https://github.com/bendyline/squisq |
18
+ | @bendyline/squisq-graph | 2.13.1 | MIT | https://github.com/bendyline/squisq |
19
19
  | @types/debug | 4.1.12 | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped |
20
20
  | @types/hast | 3.0.4 | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped |
21
21
  | @types/katex | 0.16.8 | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped |
@@ -133,7 +133,7 @@ This notice is included in the published npm tarball. Transitive packages instal
133
133
  Package-local license, copying, and notice files are reproduced below. Where a published package omits its repository license, the reviewed aggregate or README identified in `Source files` supplies the retained material.
134
134
 
135
135
  ==============================================================================
136
- Components: @bendyline/squisq-graph@2.13.0, @bendyline/squisq@2.13.6
136
+ Components: @bendyline/squisq-graph@2.13.1, @bendyline/squisq@2.13.8
137
137
  Source files: node_modules/@bendyline/squisq-graph/LICENSE, node_modules/@bendyline/squisq/LICENSE
138
138
  ==============================================================================
139
139
  MIT License
@@ -159,7 +159,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
159
159
  SOFTWARE.
160
160
 
161
161
  ==============================================================================
162
- Components: @bendyline/squisq-graph@2.13.0
162
+ Components: @bendyline/squisq-graph@2.13.1
163
163
  Source files: node_modules/@bendyline/squisq-graph/NOTICE.md
164
164
  ==============================================================================
165
165
  # Third-Party Notices for @bendyline/squisq-graph
@@ -177,7 +177,7 @@ This package has no third-party runtime dependencies. The layout engine and its
177
177
  worker entry are Squisq-authored (MIT).
178
178
 
179
179
  ==============================================================================
180
- Components: @bendyline/squisq-graph@2.13.0
180
+ Components: @bendyline/squisq-graph@2.13.1
181
181
  Source files: node_modules/@bendyline/squisq-graph/THIRD_PARTY_LICENSES.txt
182
182
  ==============================================================================
183
183
  THIRD-PARTY LICENSES FOR @bendyline/squisq-graph
@@ -189,7 +189,7 @@ repository license, the pinned upstream copy is vendored and identified below.
189
189
  COMPONENTS
190
190
 
191
191
  ==============================================================================
192
- Components: @bendyline/squisq@2.13.6
192
+ Components: @bendyline/squisq@2.13.8
193
193
  Source files: node_modules/@bendyline/squisq/NOTICE.md
194
194
  ==============================================================================
195
195
  # Third-Party Notices for @bendyline/squisq
@@ -220,7 +220,7 @@ third-party license texts for bundled code and data are shipped in
220
220
  THIRD_PARTY_LICENSES.txt.
221
221
 
222
222
  ==============================================================================
223
- Components: @bendyline/squisq@2.13.6
223
+ Components: @bendyline/squisq@2.13.8
224
224
  Source files: node_modules/@bendyline/squisq/THIRD_PARTY_LICENSES.txt
225
225
  ==============================================================================
226
226
  THIRD-PARTY LICENSES FOR @bendyline/squisq
@@ -1,5 +1,5 @@
1
1
  import { MarkdownDocument } from '@bendyline/squisq/markdown';
2
- import { C as CatalogSortOrder } from './settings-HrrCikVX.js';
2
+ import { C as CatalogSortOrder } from './settings-C-TS15JZ.js';
3
3
 
4
4
  /**
5
5
  * Synchronous outside-in path layout.
@@ -36,8 +36,11 @@ declare function chooseOutsideInMarkdownPath(layout: OutsideInLayout, paths: rea
36
36
  *
37
37
  * Everything here is deterministic: identical inputs produce identical bytes
38
38
  * on every surface (shell, VS Code extension host, CLI). There are no
39
- * timestamps, no filesystem mtimes, and no locale-sensitive ordering, so a
40
- * catalog committed to git only changes when the documents do.
39
+ * filesystem mtimes and no locale-sensitive ordering. The only clock reading
40
+ * is a document's first-published and last-modified dates, carried forward from
41
+ * the previous catalog JSON and stamped only when a document first appears or
42
+ * its content changes — so a catalog committed to git still only changes when
43
+ * the documents do.
41
44
  */
42
45
 
43
46
  declare const CATALOG_GENERATOR = "docblocks-workspace-catalog";
@@ -65,11 +68,17 @@ interface CatalogEntry {
65
68
  readonly description?: string;
66
69
  readonly author?: string;
67
70
  readonly date?: string;
71
+ /** When the document first appeared in the catalog JSON (ISO 8601, UTC). */
72
+ readonly firstPublishedDate?: string;
73
+ /** When the catalog JSON last picked up a change to the document's content. */
74
+ readonly lastModifiedDate?: string;
68
75
  readonly tags?: readonly string[];
69
76
  readonly theme?: string;
70
77
  readonly outline: readonly CatalogOutlineEntry[];
71
78
  readonly wordCount: number;
72
79
  readonly excerpt?: string;
80
+ /** SHA-256 hex of the source text with normalized line endings; absent when unread. */
81
+ readonly contentHash?: string;
73
82
  /** Present only when the catalog is configured with `content: "markdown"`. */
74
83
  readonly markdown?: string;
75
84
  }
@@ -106,6 +115,23 @@ declare function createWorkspaceCatalog(title: string, entries: readonly Catalog
106
115
  */
107
116
  declare function serializeCatalogJson(catalog: WorkspaceCatalog): string;
108
117
  declare function isGeneratedCatalogJson(text: string): boolean;
118
+ /** The dates a previously generated catalog recorded for one document. */
119
+ interface CatalogDateRecord {
120
+ readonly firstPublishedDate: string;
121
+ readonly lastModifiedDate: string;
122
+ readonly contentHash?: string;
123
+ }
124
+ /**
125
+ * Per-path date history from a previously generated catalog JSON. Anything
126
+ * malformed is skipped, so the worst case is a document dated as new.
127
+ */
128
+ declare function readCatalogDateHistory(text: string): Map<string, CatalogDateRecord>;
129
+ /**
130
+ * Date each entry. A document keeps the dates `history` holds for its path;
131
+ * it is stamped `now` when it first appears, and its `lastModifiedDate`
132
+ * moves to `now` when its content hash changes.
133
+ */
134
+ declare function stampCatalogDates(catalog: WorkspaceCatalog, history: ReadonlyMap<string, CatalogDateRecord>, now: Date): WorkspaceCatalog;
109
135
  /** Insert the generator marker directly after the doctype so it cannot affect quirks mode. */
110
136
  declare function stampCatalogHtml(html: string, inputsDigest: string): string;
111
137
  declare function readCatalogHtmlDigest(text: string): string | null;
@@ -119,4 +145,4 @@ declare function relativeCatalogLink(fromDirectory: string, toPath: string): str
119
145
  */
120
146
  declare function buildCatalogMarkdownDocument(catalog: WorkspaceCatalog, outputPath: string): MarkdownDocument;
121
147
 
122
- export { type CatalogEntry as C, OUTSIDE_IN_FORMAT_IDS as O, type WorkspaceCatalog as W, CATALOG_FORMAT_VERSION as a, CATALOG_GENERATOR as b, CATALOG_LIMITS as c, type CatalogDocumentFormat as d, type CatalogEntryHints as e, type CatalogOutlineEntry as f, type CatalogSource as g, type OutsideInFormatId as h, type OutsideInLayout as i, buildCatalogMarkdownDocument as j, catalogEntryForUnreadable as k, catalogEntryFromDocument as l, catalogEntryFromMarkdown as m, chooseOutsideInMarkdownPath as n, createWorkspaceCatalog as o, isGeneratedCatalogHtml as p, isGeneratedCatalogJson as q, readCatalogHtmlDigest as r, relativeCatalogLink as s, relocateOutsideInLayout as t, resolveOutsideInLayout as u, serializeCatalogJson as v, sortCatalogEntries as w, stampCatalogHtml as x };
148
+ export { stampCatalogHtml as A, type CatalogEntry as C, OUTSIDE_IN_FORMAT_IDS as O, type WorkspaceCatalog as W, CATALOG_FORMAT_VERSION as a, CATALOG_GENERATOR as b, CATALOG_LIMITS as c, type CatalogDateRecord as d, type CatalogDocumentFormat as e, type CatalogEntryHints as f, type CatalogOutlineEntry as g, type CatalogSource as h, type OutsideInFormatId as i, type OutsideInLayout as j, buildCatalogMarkdownDocument as k, catalogEntryForUnreadable as l, catalogEntryFromDocument as m, catalogEntryFromMarkdown as n, chooseOutsideInMarkdownPath as o, createWorkspaceCatalog as p, isGeneratedCatalogHtml as q, isGeneratedCatalogJson as r, readCatalogDateHistory as s, readCatalogHtmlDigest as t, relativeCatalogLink as u, relocateOutsideInLayout as v, resolveOutsideInLayout as w, serializeCatalogJson as x, sortCatalogEntries as y, stampCatalogDates as z };
@@ -168,11 +168,14 @@ function orderedEntry(entry) {
168
168
  "description",
169
169
  "author",
170
170
  "date",
171
+ "firstPublishedDate",
172
+ "lastModifiedDate",
171
173
  "tags",
172
174
  "theme",
173
175
  "outline",
174
176
  "wordCount",
175
177
  "excerpt",
178
+ "contentHash",
176
179
  "markdown"
177
180
  ];
178
181
  for (const key of keys) {
@@ -202,6 +205,50 @@ var CATALOG_JSON_MARKER = /^\uFEFF?\{\s*"generator"\s*:\s*"docblocks-workspace-c
202
205
  function isGeneratedCatalogJson(text) {
203
206
  return CATALOG_JSON_MARKER.test(text.slice(0, 256));
204
207
  }
208
+ var CATALOG_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2}))?$/;
209
+ var CONTENT_HASH_PATTERN = /^[0-9a-f]{64}$/;
210
+ function catalogDate(value) {
211
+ return typeof value === "string" && CATALOG_DATE_PATTERN.test(value) && Number.isFinite(Date.parse(value)) ? value : void 0;
212
+ }
213
+ function readCatalogDateHistory(text) {
214
+ const history = /* @__PURE__ */ new Map();
215
+ if (!isGeneratedCatalogJson(text)) return history;
216
+ let parsed;
217
+ try {
218
+ parsed = JSON.parse(text.replace(/^\uFEFF/, ""));
219
+ } catch {
220
+ return history;
221
+ }
222
+ const documents = parsed?.documents;
223
+ if (!Array.isArray(documents)) return history;
224
+ for (const document of documents) {
225
+ if (!document || typeof document !== "object") continue;
226
+ const record = document;
227
+ const first = catalogDate(record.firstPublishedDate);
228
+ if (typeof record.path !== "string" || !first) continue;
229
+ const hash = record.contentHash;
230
+ history.set(record.path, {
231
+ firstPublishedDate: first,
232
+ lastModifiedDate: catalogDate(record.lastModifiedDate) ?? first,
233
+ ...typeof hash === "string" && CONTENT_HASH_PATTERN.test(hash) ? { contentHash: hash } : {}
234
+ });
235
+ }
236
+ return history;
237
+ }
238
+ function stampCatalogDates(catalog, history, now) {
239
+ const timestamp = now.toISOString().replace(/\.\d{3}Z$/, "Z");
240
+ return {
241
+ ...catalog,
242
+ documents: catalog.documents.map((entry) => {
243
+ const previous = history.get(entry.path);
244
+ return {
245
+ ...entry,
246
+ firstPublishedDate: previous?.firstPublishedDate ?? timestamp,
247
+ lastModifiedDate: previous && previous.contentHash === entry.contentHash ? previous.lastModifiedDate : timestamp
248
+ };
249
+ })
250
+ };
251
+ }
205
252
  var CATALOG_HTML_MARKER = /<!-- docblocks-workspace-catalog v1 inputs=sha256:([0-9a-f]{64}) -->/;
206
253
  var CATALOG_HTML_MARKER_SCAN_CHARACTERS = 2048;
207
254
  function stampCatalogHtml(html, inputsDigest) {
@@ -369,6 +416,8 @@ export {
369
416
  createWorkspaceCatalog,
370
417
  serializeCatalogJson,
371
418
  isGeneratedCatalogJson,
419
+ readCatalogDateHistory,
420
+ stampCatalogDates,
372
421
  stampCatalogHtml,
373
422
  readCatalogHtmlDigest,
374
423
  isGeneratedCatalogHtml,
@@ -21,6 +21,7 @@ var WORKSPACE_SETTINGS_LIMITS = {
21
21
  var WORKSPACE_THEME_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
22
22
  var CATALOG_SORT_ORDERS = ["title", "path", "date"];
23
23
  var CATALOG_JSON_CONTENT_MODES = ["metadata", "markdown"];
24
+ var WORKSPACE_OUTPUT_TEMPLATE_FORMATS = ["html"];
24
25
  var EMPTY_WORKSPACE_SETTINGS = Object.freeze({
25
26
  version: WORKSPACE_SETTINGS_VERSION
26
27
  });
@@ -85,6 +86,32 @@ function boundedPath(value, label) {
85
86
  if (!path) fail(`${label} must not be the workspace root.`);
86
87
  return path;
87
88
  }
89
+ var OUTPUT_TEMPLATE_EXTENSIONS = {
90
+ html: [".html", ".htm"]
91
+ };
92
+ function validateOutputTemplatePath(value, format) {
93
+ const label = `documents.outputTemplates.${format}`;
94
+ const path = boundedPath(value, label);
95
+ const lower = path.toLowerCase();
96
+ if (!OUTPUT_TEMPLATE_EXTENSIONS[format].some((extension) => lower.endsWith(extension))) {
97
+ fail(`${label} must end with ${OUTPUT_TEMPLATE_EXTENSIONS[format].join(" or ")}.`);
98
+ }
99
+ return path;
100
+ }
101
+ function checkOutputTemplatePath(value, format) {
102
+ try {
103
+ return { ok: true, path: validateOutputTemplatePath(value, format) };
104
+ } catch (error) {
105
+ return { ok: false, message: shapeMessage(error) };
106
+ }
107
+ }
108
+ function parseOutputTemplates(value) {
109
+ if (value === void 0) return void 0;
110
+ const record = exactRecord(value, "documents.outputTemplates", WORKSPACE_OUTPUT_TEMPLATE_FORMATS);
111
+ return compact({
112
+ html: record.html === void 0 ? void 0 : validateOutputTemplatePath(record.html, "html")
113
+ });
114
+ }
88
115
  var OUTPUT_EXTENSIONS = {
89
116
  html: [".html", ".htm"],
90
117
  json: [".json"]
@@ -146,6 +173,7 @@ function compact(value) {
146
173
  const entries = Object.entries(value).filter(([, field]) => field !== void 0);
147
174
  return entries.length > 0 ? Object.fromEntries(entries) : void 0;
148
175
  }
176
+ var DOCUMENT_SETTINGS_KEYS = ["defaultTheme", "outputTemplates"];
149
177
  function parseWorkspaceSettings(value) {
150
178
  const root = isRecord(value) ? value : fail("Workspace settings must be a JSON object.");
151
179
  const version = root.version;
@@ -169,9 +197,10 @@ function parseWorkspaceSettings(value) {
169
197
  }
170
198
  let documents;
171
199
  if (root.documents !== void 0) {
172
- const record = exactRecord(root.documents, "documents", ["defaultTheme"]);
200
+ const record = exactRecord(root.documents, "documents", DOCUMENT_SETTINGS_KEYS);
173
201
  documents = compact({
174
- defaultTheme: optionalTheme(record.defaultTheme, "documents.defaultTheme")
202
+ defaultTheme: optionalTheme(record.defaultTheme, "documents.defaultTheme"),
203
+ outputTemplates: parseOutputTemplates(record.outputTemplates)
175
204
  });
176
205
  }
177
206
  let versionHistory;
@@ -278,7 +307,11 @@ function serializeWorkspaceSettings(settings) {
278
307
  if (normalized.$schema !== void 0) ordered.$schema = normalized.$schema;
279
308
  ordered.version = normalized.version;
280
309
  if (normalized.documents) {
281
- ordered.documents = pick(normalized.documents, ["defaultTheme"]);
310
+ const { outputTemplates } = normalized.documents;
311
+ ordered.documents = {
312
+ ...pick(normalized.documents, ["defaultTheme"]),
313
+ ...outputTemplates ? { outputTemplates: pick(outputTemplates, WORKSPACE_OUTPUT_TEMPLATE_FORMATS) } : {}
314
+ };
282
315
  }
283
316
  if (normalized.versionHistory) {
284
317
  ordered.versionHistory = pick(normalized.versionHistory, ["enabled", "keep"]);
@@ -340,7 +373,7 @@ function optionalSection(key, value) {
340
373
  }
341
374
  function diffWorkspaceSettings(before, after) {
342
375
  const base = before ?? EMPTY_WORKSPACE_SETTINGS;
343
- const documents = diffFields(base.documents, after.documents, ["defaultTheme"]);
376
+ const documents = diffFields(base.documents, after.documents, DOCUMENT_SETTINGS_KEYS);
344
377
  const versionHistory = diffFields(base.versionHistory, after.versionHistory, ["enabled", "keep"]);
345
378
  const top = diffFields(
346
379
  base.catalog,
@@ -378,12 +411,13 @@ function parseWorkspaceSettingsPatch(value) {
378
411
  const nullable = (field, parse) => field === null ? null : parse(field);
379
412
  const result = {};
380
413
  if (root.documents !== void 0) {
381
- const record = exactRecord(root.documents, "documents", ["defaultTheme"]);
414
+ const record = exactRecord(root.documents, "documents", DOCUMENT_SETTINGS_KEYS);
382
415
  result.documents = compact({
383
416
  defaultTheme: nullable(
384
417
  record.defaultTheme,
385
418
  (input) => optionalTheme(input, "documents.defaultTheme")
386
- )
419
+ ),
420
+ outputTemplates: nullable(record.outputTemplates, parseOutputTemplates)
387
421
  }) ?? {};
388
422
  }
389
423
  if (root.versionHistory !== void 0) {
@@ -451,11 +485,15 @@ function tryApplyWorkspaceSettingsPatch(base, patch) {
451
485
  }
452
486
  }
453
487
  function isDefaultWorkspaceSettings(settings) {
454
- return settings?.documents?.defaultTheme === void 0 && settings?.versionHistory?.enabled === void 0 && settings?.versionHistory?.keep === void 0 && settings?.catalog?.html?.enabled !== true && settings?.catalog?.json?.enabled !== true;
488
+ return settings?.documents?.defaultTheme === void 0 && settings?.documents?.outputTemplates === void 0 && settings?.versionHistory?.enabled === void 0 && settings?.versionHistory?.keep === void 0 && settings?.catalog?.html?.enabled !== true && settings?.catalog?.json?.enabled !== true;
455
489
  }
456
490
  function resolveWorkspaceDefaultThemeId(settings) {
457
491
  return settings?.documents?.defaultTheme;
458
492
  }
493
+ function resolveWorkspaceOutputTemplate(settings, format) {
494
+ const templates = settings?.documents?.outputTemplates;
495
+ return format === "html" ? templates?.html : void 0;
496
+ }
459
497
  function resolveFallbackThemeId(frontmatterThemeId, settings) {
460
498
  if (frontmatterThemeId) return void 0;
461
499
  return resolveWorkspaceDefaultThemeId(settings);
@@ -494,7 +532,10 @@ export {
494
532
  WORKSPACE_THEME_ID_PATTERN,
495
533
  CATALOG_SORT_ORDERS,
496
534
  CATALOG_JSON_CONTENT_MODES,
535
+ WORKSPACE_OUTPUT_TEMPLATE_FORMATS,
497
536
  EMPTY_WORKSPACE_SETTINGS,
537
+ validateOutputTemplatePath,
538
+ checkOutputTemplatePath,
498
539
  validateCatalogOutputPath,
499
540
  checkCatalogOutputPath,
500
541
  parseWorkspaceSettings,
@@ -508,6 +549,7 @@ export {
508
549
  tryApplyWorkspaceSettingsPatch,
509
550
  isDefaultWorkspaceSettings,
510
551
  resolveWorkspaceDefaultThemeId,
552
+ resolveWorkspaceOutputTemplate,
511
553
  resolveFallbackThemeId,
512
554
  resolveWorkspaceVersioningEnabled,
513
555
  resolveCatalogOutputs
@@ -32,9 +32,22 @@ declare const CATALOG_SORT_ORDERS: readonly ["title", "path", "date"];
32
32
  type CatalogSortOrder = (typeof CATALOG_SORT_ORDERS)[number];
33
33
  declare const CATALOG_JSON_CONTENT_MODES: readonly ["metadata", "markdown"];
34
34
  type CatalogJsonContent = (typeof CATALOG_JSON_CONTENT_MODES)[number];
35
+ /** Rendered formats a workspace can name a default page template for. */
36
+ declare const WORKSPACE_OUTPUT_TEMPLATE_FORMATS: readonly ["html"];
37
+ type WorkspaceOutputTemplateFormat = (typeof WORKSPACE_OUTPUT_TEMPLATE_FORMATS)[number];
38
+ /** Workspace-relative page template path, by rendered output format. */
39
+ type WorkspaceOutputTemplates = {
40
+ readonly [Format in WorkspaceOutputTemplateFormat]?: string;
41
+ };
35
42
  interface WorkspaceDocumentSettings {
36
43
  /** Theme for documents whose frontmatter names none. */
37
44
  readonly defaultTheme?: string;
45
+ /**
46
+ * Page template for rendered documents whose frontmatter names none
47
+ * (`squisq-output-template`). A document's own choice, including `none`,
48
+ * always wins.
49
+ */
50
+ readonly outputTemplates?: WorkspaceOutputTemplates;
38
51
  }
39
52
  interface WorkspaceVersionHistorySettings {
40
53
  /** Absent inherits the app-wide preference. */
@@ -76,6 +89,16 @@ type WorkspaceSettingsParseResult = {
76
89
  readonly status: 'invalid' | 'unsupported-version';
77
90
  readonly message: string;
78
91
  };
92
+ /** Validate a configured page template path for one rendered format. */
93
+ declare function validateOutputTemplatePath(value: unknown, format: WorkspaceOutputTemplateFormat): WorkspacePath;
94
+ /** Try-form for UI validation messages. */
95
+ declare function checkOutputTemplatePath(value: unknown, format: WorkspaceOutputTemplateFormat): {
96
+ ok: true;
97
+ path: WorkspacePath;
98
+ } | {
99
+ ok: false;
100
+ message: string;
101
+ };
79
102
  type CatalogOutputKind = 'html' | 'json';
80
103
  /**
81
104
  * Validate a configured catalog output path. Outputs may never land inside
@@ -135,7 +158,8 @@ declare function tryApplyWorkspaceSettingsPatch(base: WorkspaceSettings | null,
135
158
  };
136
159
  /**
137
160
  * True when settings change nothing about how DocBlocks treats the folder: no
138
- * default theme, no version-history choice, and no catalog output turned on.
161
+ * default theme or page template, no version-history choice, and no catalog
162
+ * output turned on.
139
163
  * Fields that only configure a disabled output (a catalog title, path, order,
140
164
  * or page theme) are inert. Such settings are never worth creating a
141
165
  * `.docblocks/` folder for.
@@ -143,6 +167,8 @@ declare function tryApplyWorkspaceSettingsPatch(base: WorkspaceSettings | null,
143
167
  declare function isDefaultWorkspaceSettings(settings: WorkspaceSettings | null | undefined): boolean;
144
168
  /** The workspace default theme, or undefined to use the built-in default. */
145
169
  declare function resolveWorkspaceDefaultThemeId(settings: WorkspaceSettings | null | undefined): string | undefined;
170
+ /** The workspace default page template for a rendered format, workspace-relative. */
171
+ declare function resolveWorkspaceOutputTemplate(settings: WorkspaceSettings | null | undefined, format: string): string | undefined;
146
172
  /**
147
173
  * The theme a renderer should be told about explicitly. Every Squisq
148
174
  * exporter lets an explicit theme beat the document's own frontmatter, so a
@@ -167,4 +193,4 @@ interface ResolvedCatalogOutputs {
167
193
  /** Effective catalog configuration, or null when no output is enabled. */
168
194
  declare function resolveCatalogOutputs(settings: WorkspaceSettings | null | undefined): ResolvedCatalogOutputs | null;
169
195
 
170
- export { resolveCatalogOutputs as A, resolveFallbackThemeId as B, type CatalogSortOrder as C, DEFAULT_CATALOG_HTML_PATH as D, EMPTY_WORKSPACE_SETTINGS as E, resolveWorkspaceDefaultThemeId as F, resolveWorkspaceVersioningEnabled as G, serializeWorkspaceSettings as H, tryApplyWorkspaceSettingsPatch as I, validateCatalogOutputPath as J, type ResolvedCatalogOutputs as R, UnsupportedWorkspaceSettingsVersionError as U, type WorkspaceSettings as W, type WorkspaceSettingsPatch as a, CATALOG_JSON_CONTENT_MODES as b, CATALOG_SORT_ORDERS as c, type CatalogJsonContent as d, type CatalogOutputKind as e, DEFAULT_CATALOG_JSON_PATH as f, DEFAULT_CATALOG_TITLE as g, WORKSPACE_SETTINGS_DIRECTORY as h, WORKSPACE_SETTINGS_LIMITS as i, WORKSPACE_SETTINGS_PATH as j, WORKSPACE_SETTINGS_VERSION as k, WORKSPACE_THEME_ID_PATTERN as l, type WorkspaceCatalogHtmlSettings as m, type WorkspaceCatalogJsonSettings as n, type WorkspaceCatalogSettings as o, type WorkspaceDocumentSettings as p, type WorkspaceSettingsParseResult as q, type WorkspaceVersionHistorySettings as r, applyWorkspaceSettingsPatch as s, checkCatalogOutputPath as t, diffWorkspaceSettings as u, isDefaultWorkspaceSettings as v, isEmptyWorkspaceSettingsPatch as w, parseWorkspaceSettings as x, parseWorkspaceSettingsPatch as y, parseWorkspaceSettingsText as z };
196
+ export { isEmptyWorkspaceSettingsPatch as A, parseWorkspaceSettings as B, type CatalogSortOrder as C, DEFAULT_CATALOG_HTML_PATH as D, EMPTY_WORKSPACE_SETTINGS as E, parseWorkspaceSettingsPatch as F, parseWorkspaceSettingsText as G, resolveCatalogOutputs as H, resolveFallbackThemeId as I, resolveWorkspaceDefaultThemeId as J, resolveWorkspaceOutputTemplate as K, resolveWorkspaceVersioningEnabled as L, serializeWorkspaceSettings as M, tryApplyWorkspaceSettingsPatch as N, validateCatalogOutputPath as O, validateOutputTemplatePath as P, type ResolvedCatalogOutputs as R, UnsupportedWorkspaceSettingsVersionError as U, type WorkspaceSettings as W, type WorkspaceSettingsPatch as a, CATALOG_JSON_CONTENT_MODES as b, CATALOG_SORT_ORDERS as c, type CatalogJsonContent as d, type CatalogOutputKind as e, DEFAULT_CATALOG_JSON_PATH as f, DEFAULT_CATALOG_TITLE as g, WORKSPACE_OUTPUT_TEMPLATE_FORMATS as h, WORKSPACE_SETTINGS_DIRECTORY as i, WORKSPACE_SETTINGS_LIMITS as j, WORKSPACE_SETTINGS_PATH as k, WORKSPACE_SETTINGS_VERSION as l, WORKSPACE_THEME_ID_PATTERN as m, type WorkspaceCatalogHtmlSettings as n, type WorkspaceCatalogJsonSettings as o, type WorkspaceCatalogSettings as p, type WorkspaceDocumentSettings as q, type WorkspaceOutputTemplateFormat as r, type WorkspaceOutputTemplates as s, type WorkspaceSettingsParseResult as t, type WorkspaceVersionHistorySettings as u, applyWorkspaceSettingsPatch as v, checkCatalogOutputPath as w, checkOutputTemplatePath as x, diffWorkspaceSettings as y, isDefaultWorkspaceSettings as z };
@@ -1,4 +1,4 @@
1
- import { W as WorkspaceSettings, a as WorkspaceSettingsPatch } from '../settings-HrrCikVX.js';
1
+ import { W as WorkspaceSettings, a as WorkspaceSettingsPatch } from '../settings-C-TS15JZ.js';
2
2
  import '../workspace-path-CWVrcuPL.js';
3
3
 
4
4
  /**
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  parseWorkspaceSettings,
3
3
  parseWorkspaceSettingsPatch
4
- } from "../chunk-BC42VRYW.js";
4
+ } from "../chunk-PQBUG5DY.js";
5
5
  import {
6
6
  HOST_WIRE_LIMITS,
7
7
  isBoundedString
@@ -1,8 +1,8 @@
1
1
  import { o as FileSystemProviderV2, n as FileSystemProviderCapabilities } from '../v2-DwxWjFC0.js';
2
2
  import { a as WorkspacePath } from '../workspace-path-CWVrcuPL.js';
3
- import { d as CatalogDocumentFormat, f as CatalogOutlineEntry } from '../catalog-BQK05tne.js';
3
+ import { e as CatalogDocumentFormat, g as CatalogOutlineEntry } from '../catalog-BmEXHaZf.js';
4
4
  import '@bendyline/squisq/markdown';
5
- import '../settings-HrrCikVX.js';
5
+ import '../settings-C-TS15JZ.js';
6
6
 
7
7
  /**
8
8
  * What the index reads from. Only read methods are reachable, so the index
@@ -3,7 +3,7 @@ import {
3
3
  chooseOutsideInMarkdownPath,
4
4
  normalizeLineEndings,
5
5
  resolveOutsideInLayout
6
- } from "../chunk-R2JX5N3F.js";
6
+ } from "../chunk-L7HADBCQ.js";
7
7
  import {
8
8
  isCompanionDirectoryName,
9
9
  isDiscoverableWorkspaceEntry,
@@ -1,7 +1,7 @@
1
- import { R as ResolvedCatalogOutputs, W as WorkspaceSettings, a as WorkspaceSettingsPatch } from '../settings-HrrCikVX.js';
2
- export { b as CATALOG_JSON_CONTENT_MODES, c as CATALOG_SORT_ORDERS, d as CatalogJsonContent, e as CatalogOutputKind, C as CatalogSortOrder, D as DEFAULT_CATALOG_HTML_PATH, f as DEFAULT_CATALOG_JSON_PATH, g as DEFAULT_CATALOG_TITLE, E as EMPTY_WORKSPACE_SETTINGS, U as UnsupportedWorkspaceSettingsVersionError, h as WORKSPACE_SETTINGS_DIRECTORY, i as WORKSPACE_SETTINGS_LIMITS, j as WORKSPACE_SETTINGS_PATH, k as WORKSPACE_SETTINGS_VERSION, l as WORKSPACE_THEME_ID_PATTERN, m as WorkspaceCatalogHtmlSettings, n as WorkspaceCatalogJsonSettings, o as WorkspaceCatalogSettings, p as WorkspaceDocumentSettings, q as WorkspaceSettingsParseResult, r as WorkspaceVersionHistorySettings, s as applyWorkspaceSettingsPatch, t as checkCatalogOutputPath, u as diffWorkspaceSettings, v as isDefaultWorkspaceSettings, w as isEmptyWorkspaceSettingsPatch, x as parseWorkspaceSettings, y as parseWorkspaceSettingsPatch, z as parseWorkspaceSettingsText, A as resolveCatalogOutputs, B as resolveFallbackThemeId, F as resolveWorkspaceDefaultThemeId, G as resolveWorkspaceVersioningEnabled, H as serializeWorkspaceSettings, I as tryApplyWorkspaceSettingsPatch, J as validateCatalogOutputPath } from '../settings-HrrCikVX.js';
3
- import { C as CatalogEntry, W as WorkspaceCatalog } from '../catalog-BQK05tne.js';
4
- export { a as CATALOG_FORMAT_VERSION, b as CATALOG_GENERATOR, c as CATALOG_LIMITS, d as CatalogDocumentFormat, e as CatalogEntryHints, f as CatalogOutlineEntry, g as CatalogSource, O as OUTSIDE_IN_FORMAT_IDS, h as OutsideInFormatId, i as OutsideInLayout, j as buildCatalogMarkdownDocument, k as catalogEntryForUnreadable, l as catalogEntryFromDocument, m as catalogEntryFromMarkdown, n as chooseOutsideInMarkdownPath, o as createWorkspaceCatalog, p as isGeneratedCatalogHtml, q as isGeneratedCatalogJson, r as readCatalogHtmlDigest, s as relativeCatalogLink, t as relocateOutsideInLayout, u as resolveOutsideInLayout, v as serializeCatalogJson, w as sortCatalogEntries, x as stampCatalogHtml } from '../catalog-BQK05tne.js';
1
+ import { R as ResolvedCatalogOutputs, W as WorkspaceSettings, a as WorkspaceSettingsPatch } from '../settings-C-TS15JZ.js';
2
+ export { b as CATALOG_JSON_CONTENT_MODES, c as CATALOG_SORT_ORDERS, d as CatalogJsonContent, e as CatalogOutputKind, C as CatalogSortOrder, D as DEFAULT_CATALOG_HTML_PATH, f as DEFAULT_CATALOG_JSON_PATH, g as DEFAULT_CATALOG_TITLE, E as EMPTY_WORKSPACE_SETTINGS, U as UnsupportedWorkspaceSettingsVersionError, h as WORKSPACE_OUTPUT_TEMPLATE_FORMATS, i as WORKSPACE_SETTINGS_DIRECTORY, j as WORKSPACE_SETTINGS_LIMITS, k as WORKSPACE_SETTINGS_PATH, l as WORKSPACE_SETTINGS_VERSION, m as WORKSPACE_THEME_ID_PATTERN, n as WorkspaceCatalogHtmlSettings, o as WorkspaceCatalogJsonSettings, p as WorkspaceCatalogSettings, q as WorkspaceDocumentSettings, r as WorkspaceOutputTemplateFormat, s as WorkspaceOutputTemplates, t as WorkspaceSettingsParseResult, u as WorkspaceVersionHistorySettings, v as applyWorkspaceSettingsPatch, w as checkCatalogOutputPath, x as checkOutputTemplatePath, y as diffWorkspaceSettings, z as isDefaultWorkspaceSettings, A as isEmptyWorkspaceSettingsPatch, B as parseWorkspaceSettings, F as parseWorkspaceSettingsPatch, G as parseWorkspaceSettingsText, H as resolveCatalogOutputs, I as resolveFallbackThemeId, J as resolveWorkspaceDefaultThemeId, K as resolveWorkspaceOutputTemplate, L as resolveWorkspaceVersioningEnabled, M as serializeWorkspaceSettings, N as tryApplyWorkspaceSettingsPatch, O as validateCatalogOutputPath, P as validateOutputTemplatePath } from '../settings-C-TS15JZ.js';
3
+ import { C as CatalogEntry, W as WorkspaceCatalog } from '../catalog-BmEXHaZf.js';
4
+ export { a as CATALOG_FORMAT_VERSION, b as CATALOG_GENERATOR, c as CATALOG_LIMITS, d as CatalogDateRecord, e as CatalogDocumentFormat, f as CatalogEntryHints, g as CatalogOutlineEntry, h as CatalogSource, O as OUTSIDE_IN_FORMAT_IDS, i as OutsideInFormatId, j as OutsideInLayout, k as buildCatalogMarkdownDocument, l as catalogEntryForUnreadable, m as catalogEntryFromDocument, n as catalogEntryFromMarkdown, o as chooseOutsideInMarkdownPath, p as createWorkspaceCatalog, q as isGeneratedCatalogHtml, r as isGeneratedCatalogJson, s as readCatalogDateHistory, t as readCatalogHtmlDigest, u as relativeCatalogLink, v as relocateOutsideInLayout, w as resolveOutsideInLayout, x as serializeCatalogJson, y as sortCatalogEntries, z as stampCatalogDates, A as stampCatalogHtml } from '../catalog-BmEXHaZf.js';
5
5
  import { MarkdownDocument } from '@bendyline/squisq/markdown';
6
6
  import { v as FileSystemVersion, o as FileSystemProviderV2 } from '../v2-DwxWjFC0.js';
7
7
  import { a as WorkspacePath } from '../workspace-path-CWVrcuPL.js';
@@ -37,6 +37,20 @@ declare const WORKSPACE_SETTINGS_JSON_SCHEMA: {
37
37
  readonly type: "string";
38
38
  readonly pattern: string;
39
39
  };
40
+ readonly outputTemplates: {
41
+ readonly type: "object";
42
+ readonly additionalProperties: false;
43
+ readonly description: "Page templates for rendered documents whose frontmatter names none (squisq-output-template). A document’s own choice, including \"none\", always wins.";
44
+ readonly properties: {
45
+ readonly html: {
46
+ readonly pattern: "\\.html?$";
47
+ readonly description: "Workspace-relative HTML page template for Web pages, such as \"_templates/post.html\". Its unnamed <slot> receives the page content.";
48
+ readonly type: "string";
49
+ readonly minLength: 1;
50
+ readonly maxLength: 1024;
51
+ };
52
+ };
53
+ };
40
54
  };
41
55
  };
42
56
  readonly versionHistory: {
@@ -154,6 +168,11 @@ interface WorkspaceOutputsRenderer {
154
168
  }): string | Promise<string>;
155
169
  /** Parse rendered HTML that has no Markdown companion yet. Optional. */
156
170
  importHtml?(html: string): MarkdownDocument | Promise<MarkdownDocument>;
171
+ /**
172
+ * True for an HTML page template — the chrome other pages are generated
173
+ * through, not a document of its own — which the catalog leaves out. Optional.
174
+ */
175
+ isPageTemplate?(html: string): boolean;
157
176
  }
158
177
  declare const WORKSPACE_OUTPUT_LIMITS: {
159
178
  readonly maxDocuments: 5000;
@@ -188,6 +207,8 @@ interface RefreshWorkspaceOutputsOptions {
188
207
  /** Compute everything, write nothing. */
189
208
  readonly dryRun?: boolean;
190
209
  readonly limits?: Partial<WorkspaceOutputLimits>;
210
+ /** Time stamped on new or changed documents in the catalog JSON. Defaults to now. */
211
+ readonly now?: Date;
191
212
  }
192
213
  type WorkspaceOutputStatus = 'written' | 'unchanged' | 'blocked' | 'would-write';
193
214
  interface WorkspaceOutputResult {
@@ -211,7 +232,7 @@ declare function isCatalogRelevantPath(path: string, settings: WorkspaceSettings
211
232
  /** True for the workspace settings directory or anything inside it. */
212
233
  declare function isWorkspaceSettingsPath(path: string): boolean;
213
234
  /** Walk the workspace and build its catalog without writing anything. */
214
- declare function collectWorkspaceCatalog(io: WorkspaceOutputsIO, resolved: ResolvedCatalogOutputs, renderer: Pick<WorkspaceOutputsRenderer, 'importHtml'>, options?: Omit<RefreshWorkspaceOutputsOptions, 'force' | 'dryRun'>): Promise<WorkspaceCatalog>;
235
+ declare function collectWorkspaceCatalog(io: WorkspaceOutputsIO, resolved: ResolvedCatalogOutputs, renderer: Pick<WorkspaceOutputsRenderer, 'importHtml' | 'isPageTemplate'>, options?: Omit<RefreshWorkspaceOutputsOptions, 'force' | 'dryRun' | 'now'>): Promise<WorkspaceCatalog>;
215
236
  /**
216
237
  * Regenerate every enabled catalog output for one workspace. Throws
217
238
  * {@link WorkspaceOutputsLimitError} when over budget, `FsError('aborted')`
@@ -11,6 +11,8 @@ import {
11
11
  createWorkspaceCatalog,
12
12
  isGeneratedCatalogHtml,
13
13
  isGeneratedCatalogJson,
14
+ normalizeLineEndings,
15
+ readCatalogDateHistory,
14
16
  readCatalogHtmlDigest,
15
17
  relativeCatalogLink,
16
18
  relocateOutsideInLayout,
@@ -18,8 +20,9 @@ import {
18
20
  serializeCatalogJson,
19
21
  sha256Hex,
20
22
  sortCatalogEntries,
23
+ stampCatalogDates,
21
24
  stampCatalogHtml
22
- } from "../chunk-R2JX5N3F.js";
25
+ } from "../chunk-L7HADBCQ.js";
23
26
  import {
24
27
  CATALOG_JSON_CONTENT_MODES,
25
28
  CATALOG_SORT_ORDERS,
@@ -28,6 +31,7 @@ import {
28
31
  DEFAULT_CATALOG_TITLE,
29
32
  EMPTY_WORKSPACE_SETTINGS,
30
33
  UnsupportedWorkspaceSettingsVersionError,
34
+ WORKSPACE_OUTPUT_TEMPLATE_FORMATS,
31
35
  WORKSPACE_SETTINGS_DIRECTORY,
32
36
  WORKSPACE_SETTINGS_LIMITS,
33
37
  WORKSPACE_SETTINGS_PATH,
@@ -35,6 +39,7 @@ import {
35
39
  WORKSPACE_THEME_ID_PATTERN,
36
40
  applyWorkspaceSettingsPatch,
37
41
  checkCatalogOutputPath,
42
+ checkOutputTemplatePath,
38
43
  diffWorkspaceSettings,
39
44
  isDefaultWorkspaceSettings,
40
45
  isEmptyWorkspaceSettingsPatch,
@@ -44,11 +49,13 @@ import {
44
49
  resolveCatalogOutputs,
45
50
  resolveFallbackThemeId,
46
51
  resolveWorkspaceDefaultThemeId,
52
+ resolveWorkspaceOutputTemplate,
47
53
  resolveWorkspaceVersioningEnabled,
48
54
  serializeWorkspaceSettings,
49
55
  tryApplyWorkspaceSettingsPatch,
50
- validateCatalogOutputPath
51
- } from "../chunk-BC42VRYW.js";
56
+ validateCatalogOutputPath,
57
+ validateOutputTemplatePath
58
+ } from "../chunk-PQBUG5DY.js";
52
59
  import {
53
60
  SQUISQ_RUNTIME_DIRECTORY_NAME,
54
61
  isDiscoverableWorkspaceEntry,
@@ -92,6 +99,18 @@ var WORKSPACE_SETTINGS_JSON_SCHEMA = {
92
99
  defaultTheme: {
93
100
  ...themeId,
94
101
  description: "Theme for documents whose frontmatter does not name one. A document\u2019s own theme always wins."
102
+ },
103
+ outputTemplates: {
104
+ type: "object",
105
+ additionalProperties: false,
106
+ description: 'Page templates for rendered documents whose frontmatter names none (squisq-output-template). A document\u2019s own choice, including "none", always wins.',
107
+ properties: {
108
+ html: {
109
+ ...relativePath,
110
+ pattern: "\\.html?$",
111
+ description: 'Workspace-relative HTML page template for Web pages, such as "_templates/post.html". Its unnamed <slot> receives the page content.'
112
+ }
113
+ }
95
114
  }
96
115
  }
97
116
  },
@@ -351,7 +370,7 @@ async function collectWorkspaceCatalog(io, resolved, renderer, options = {}) {
351
370
  const text = new TextDecoder("utf-8").decode(read.data);
352
371
  if (target.mode === "markdown") {
353
372
  entry = await catalogEntryFromMarkdown(target.source, text, { includeMarkdown });
354
- } else if (isGeneratedCatalogHtml(text)) {
373
+ } else if (isGeneratedCatalogHtml(text) || renderer.isPageTemplate?.(text)) {
355
374
  entry = null;
356
375
  } else if (renderer.importHtml) {
357
376
  const head = text.slice(0, 64 * 1024);
@@ -362,6 +381,7 @@ async function collectWorkspaceCatalog(io, resolved, renderer, options = {}) {
362
381
  } else {
363
382
  entry = catalogEntryForUnreadable(target.source);
364
383
  }
384
+ if (entry) entry = { ...entry, contentHash: await sha256Hex(normalizeLineEndings(text)) };
365
385
  }
366
386
  cache?.set(target.source.path, {
367
387
  readPath: target.readPath,
@@ -426,7 +446,9 @@ async function writeOutput(io, request, limits, options) {
426
446
  return blocked(`${path} is an edited document. Choose another catalog path.`);
427
447
  }
428
448
  }
429
- const bytes = new TextEncoder().encode(await request.produce());
449
+ const bytes = new TextEncoder().encode(
450
+ await request.produce(existing ? new TextDecoder("utf-8").decode(existing.data) : null)
451
+ );
430
452
  if (bytes.byteLength > limits.maxOutputBytes) {
431
453
  return blocked(
432
454
  `The catalog would exceed ${Math.round(limits.maxOutputBytes / 1024 / 1024)} MiB and was not written.`
@@ -451,13 +473,20 @@ async function refreshWorkspaceOutputs(io, settings, renderer, options = {}) {
451
473
  if (resolved.json) {
452
474
  throwIfAborted(options.signal);
453
475
  const content = resolved.json.content;
476
+ const now = options.now ?? /* @__PURE__ */ new Date();
454
477
  outputs.push(
455
478
  await writeOutput(
456
479
  io,
457
480
  {
458
481
  kind: "json",
459
482
  path: resolved.json.path,
460
- produce: async () => serializeCatalogJson(content === "markdown" ? catalog : metadataCatalog)
483
+ produce: async (previous) => serializeCatalogJson(
484
+ stampCatalogDates(
485
+ content === "markdown" ? catalog : metadataCatalog,
486
+ readCatalogDateHistory(previous ?? ""),
487
+ now
488
+ )
489
+ )
461
490
  },
462
491
  limits,
463
492
  options
@@ -771,6 +800,7 @@ export {
771
800
  WORKSPACE_OUTPUTS_DEBOUNCE_MS,
772
801
  WORKSPACE_OUTPUTS_MAX_WAIT_MS,
773
802
  WORKSPACE_OUTPUT_LIMITS,
803
+ WORKSPACE_OUTPUT_TEMPLATE_FORMATS,
774
804
  WORKSPACE_SETTINGS_DIRECTORY,
775
805
  WORKSPACE_SETTINGS_JSON_SCHEMA,
776
806
  WORKSPACE_SETTINGS_LIMITS,
@@ -784,6 +814,7 @@ export {
784
814
  catalogEntryFromDocument,
785
815
  catalogEntryFromMarkdown,
786
816
  checkCatalogOutputPath,
817
+ checkOutputTemplatePath,
787
818
  chooseOutsideInMarkdownPath,
788
819
  collectWorkspaceCatalog,
789
820
  createWorkspaceCatalog,
@@ -800,6 +831,7 @@ export {
800
831
  parseWorkspaceSettings,
801
832
  parseWorkspaceSettingsPatch,
802
833
  parseWorkspaceSettingsText,
834
+ readCatalogDateHistory,
803
835
  readCatalogHtmlDigest,
804
836
  readWorkspaceSettings,
805
837
  refreshWorkspaceOutputs,
@@ -809,12 +841,15 @@ export {
809
841
  resolveFallbackThemeId,
810
842
  resolveOutsideInLayout,
811
843
  resolveWorkspaceDefaultThemeId,
844
+ resolveWorkspaceOutputTemplate,
812
845
  resolveWorkspaceVersioningEnabled,
813
846
  saveWorkspaceSettingsPatch,
814
847
  serializeCatalogJson,
815
848
  serializeWorkspaceSettings,
816
849
  sortCatalogEntries,
850
+ stampCatalogDates,
817
851
  stampCatalogHtml,
818
852
  tryApplyWorkspaceSettingsPatch,
819
- validateCatalogOutputPath
853
+ validateCatalogOutputPath,
854
+ validateOutputTemplatePath
820
855
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/docblocks",
3
- "version": "2.7.8",
3
+ "version": "2.7.9",
4
4
  "description": "Core data structures and filesystem abstractions for DocBlocks",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",
@@ -126,7 +126,7 @@
126
126
  "typecheck": "tsc --noEmit"
127
127
  },
128
128
  "dependencies": {
129
- "@bendyline/squisq": "2.13.6",
129
+ "@bendyline/squisq": "2.13.8",
130
130
  "zod": "3.25.76"
131
131
  }
132
132
  }