wiki-formant 0.8.0 → 0.9.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/README.md CHANGED
@@ -242,6 +242,62 @@ and a crawler obeys only its most-specific matching group, so an agent with no
242
242
  group of its own falls through to `*`. Three wikis were measuring five crawlers
243
243
  they had never addressed.
244
244
 
245
+ ## Revisions
246
+
247
+ What changed between two versions of a page, and therefore which semver bump to
248
+ ask for. Both wikis had written it and `extractBlocks` was byte-identical
249
+ between the copies; they differed only in what each had learned since.
250
+
251
+ ```ts
252
+ const diff = computeRevisionDiff({
253
+ currentVersion: page.version,
254
+ oldContent, newContent, oldTitle, newTitle,
255
+ containers: b =>
256
+ b.type === 'infobox' ? [{ path: 'blocks', blocks: b.blocks }]
257
+ : b.type === 'columns' ? b.columns.map((c, i) => ({ path: `columns.${i}.blocks`, blocks: c.blocks }))
258
+ : null,
259
+ });
260
+ ```
261
+
262
+ - **Blocks match by id**, so a block that moved is one `moved` and not a removal
263
+ plus an addition.
264
+ - **Container keys are never diffed as attributes.** They are walked as their own
265
+ entries, and comparing them here would report an infobox as edited every time
266
+ anything inside it changed — turning a prose edit into a structural bump.
267
+ - **The path segment is a parameter**, because `root.1.columns.0.blocks.2` is
268
+ what a reviewing UI anchors on and only the consumer knows how its containers
269
+ are addressed.
270
+
271
+ One copy also built two Maps keyed by a recursive `JSON.stringify` of every
272
+ block, on every save, and never read either one. Matching is by id and always
273
+ was. That half is not here.
274
+
275
+ ## Feeds
276
+
277
+ Three repos, four feeds, 47 of 64 significant lines identical — including,
278
+ verbatim in two of them, the comment explaining why the apostrophe escapes
279
+ numerically. `renderFeed` is the union.
280
+
281
+ ```ts
282
+ return new Response(renderFeed(channel, items), { headers: FEED_HEADERS });
283
+ ```
284
+
285
+ **`lastBuildDate` comes from the newest item, not the clock.** One copy stamped
286
+ `new Date()` on every request, telling every poller the feed had changed when it
287
+ had not — the recrawl the conditional-GET helpers exist to prevent. An empty feed
288
+ carries no build date rather than a fictional one.
289
+
290
+ ## Licence declarations
291
+
292
+ S10 wants a licence on every surface, and each repo satisfied that by writing the
293
+ same block again. What is genuinely per-project is the *scope* — which half of a
294
+ site the grant covers and what it excludes — so that is the parameter.
295
+
296
+ ```ts
297
+ const license = ccBy40({ siteName: 'AcuiQ', siteUrl: SITE_URL });
298
+ const block = licenseBlock({ license, scope: 'The protocol compilation and prose', excludes });
299
+ ```
300
+
245
301
  ## Rendered-article passes
246
302
 
247
303
  `wiki-formant/dom` holds what runs against an article element after it is in the document. Not React, so not in `react.tsx`.
@@ -308,11 +364,15 @@ they name a gap in the corpus in the reader's own words.
308
364
  | `htmlToMarkdown`, `inlineToMarkdown`, `tableToMarkdown`, `frontmatter`, `markdownDocument`, `decodeEntities` | `wiki-formant/markdown` |
309
365
  | `corpusEtag`, `notModified`, `textHeaders`, `markdownHeaders`, `cleanSnippet`, `pageLine` | `wiki-formant/http` |
310
366
  | `parsePagination`, `paginatedResponse`, `toOffset` | `wiki-formant/pagination` |
367
+ | `mapBlockTree`, `mapBlockTreeAsync`, `someBlock`, `renderBlockTree` | `wiki-formant/blocks` |
311
368
  | `parseVersion`, `formatVersion`, `incrementVersion`, `bump`, `compareVersions` | `wiki-formant/versioning` |
312
369
  | `plausibleEvent`, `mcpCallProps`, `searchQueryProps`, `plausibleDomain` | `wiki-formant/analytics` |
313
370
  | `comboboxAria`, `listId`, `optionId` | `wiki-formant/combobox` |
371
+ | `computeRevisionDiff`, `diffBlocks`, `extractBlocks`, `classifyChanges`, `changeSummary` | `wiki-formant/revisions` |
372
+ | `renderFeed`, `renderItem`, `escXml`, `cdata`, `clampWords`, `absolutise`, `FEED_HEADERS` | `wiki-formant/feed` |
373
+ | `ccBy40`, `licenseBlock`, `licenseLines`, `licenseNote` | `wiki-formant/license` |
314
374
  | `AI_CRAWLERS`, `detectAiBot`, `aiCrawlerTokens`, `aiCrawlerRules` | `wiki-formant/crawlers` |
315
- | `useCollapsibleSidebar`, `SidebarProvider`, `useSidebar`, `TableOfContents`, `useTypeahead`, `useLinkPreview`, `useClickOutside` | `wiki-formant/react` |
375
+ | `useCollapsibleSidebar`, `SidebarProvider`, `useSidebar`, `TableOfContents`, `useTypeahead`, `useLinkPreview`, `useClickOutside`, `useTableSort`, `useCopy`, `ErrorBoundary` | `wiki-formant/react` |
316
376
  | `resolveSidebarOpen`, `sidebarBootScript`, `SIDEBAR_ATTRIBUTE` | `wiki-formant/sidebar` |
317
377
  | `addCopyButtons`, `activateTabGroups`, `tweetEmbedSrc`, `onTweetResize`, `hydrateTweetEmbeds`, `sizeTweetEmbeds`, `TWITTER_ORIGIN` | `wiki-formant/dom` |
318
378
  | `Iframe`, `YouTube`, `TwitterEmbed`, `createMapEmbed`, `createCodeBlock`, `createTabs` | `wiki-formant/tiptap` |
package/dist/blocks.d.ts CHANGED
@@ -42,6 +42,40 @@ export interface BlockTreeOptions<B> {
42
42
  * needs the sentences adjacent, not interleaved.
43
43
  */
44
44
  export declare function renderBlockTree<B>(blocks: readonly B[], opts: BlockTreeOptions<B>): string;
45
+ /**
46
+ * How a container exposes and rebuilds its nested groups.
47
+ *
48
+ * `renderBlockTree` only ever reads groups, so it takes `containers` alone.
49
+ * A transform has to put the mapped blocks back where it found them, which is
50
+ * the second half — and the half every hand-rolled copy of this walk wrote out
51
+ * per block type, at six call sites across two repos.
52
+ */
53
+ export interface BlockTreeShape<B> {
54
+ /** Nested groups in document order, or `null` for a leaf. */
55
+ containers: (block: B) => B[][] | null;
56
+ /** Rebuild a container from its mapped groups. */
57
+ rebuild: (block: B, groups: B[][]) => B;
58
+ }
59
+ /**
60
+ * Every block in the tree, transformed, with the tree's shape preserved.
61
+ *
62
+ * The three-branch walk this replaces — columns to `columns[].blocks`, infobox
63
+ * to `.blocks`, everything else atomic — was written by hand for heading
64
+ * injection, syntax highlighting, server-side data resolution, feed rendering
65
+ * and twice more besides. Each copy was correct and each had to be found again
66
+ * whenever a container type was added.
67
+ */
68
+ export declare function mapBlockTree<B>(blocks: readonly B[], map: (block: B) => B, shape: BlockTreeShape<B>): B[];
69
+ /** The async twin. Highlighting and data resolution both await per leaf. */
70
+ export declare function mapBlockTreeAsync<B>(blocks: readonly B[], map: (block: B) => Promise<B>, shape: BlockTreeShape<B>): Promise<B[]>;
71
+ /**
72
+ * Whether any leaf satisfies `predicate`, containers included.
73
+ *
74
+ * The copy this replaces asked "does this page hold a code block?" to decide
75
+ * whether to ship a highlighter, and answered by checking the top level only —
76
+ * so a code block inside an infobox shipped unhighlighted.
77
+ */
78
+ export declare function someBlock<B>(blocks: readonly B[], predicate: (block: B) => boolean, containers: (block: B) => B[][] | null): boolean;
45
79
  /**
46
80
  * Fenced blocks, one per tab, each under its label.
47
81
  *
@@ -1 +1 @@
1
- {"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../src/blocks.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;CAChC;AAID,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,uEAAuE;IACvE,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,CAAC;IAC7B;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC;IACxC;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,MAAM,CAe1F;AAID;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAOnE;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAE5E;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,KAAK,SAAe,GACnB,MAAM,CAMR;AAED,yDAAyD;AACzD,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,MAAM,CAGlE;AAED,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACpB,MAAM,CAWR;AAED,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAE/D"}
1
+ {"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../src/blocks.ts"],"names":[],"mappings":"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"}
package/dist/blocks.js CHANGED
@@ -34,6 +34,48 @@ export function renderBlockTree(blocks, opts) {
34
34
  .replace(/\n{3,}/g, '\n\n')
35
35
  .trim();
36
36
  }
37
+ /**
38
+ * Every block in the tree, transformed, with the tree's shape preserved.
39
+ *
40
+ * The three-branch walk this replaces — columns to `columns[].blocks`, infobox
41
+ * to `.blocks`, everything else atomic — was written by hand for heading
42
+ * injection, syntax highlighting, server-side data resolution, feed rendering
43
+ * and twice more besides. Each copy was correct and each had to be found again
44
+ * whenever a container type was added.
45
+ */
46
+ export function mapBlockTree(blocks, map, shape) {
47
+ return blocks.map(block => {
48
+ const groups = shape.containers(block);
49
+ if (!groups)
50
+ return map(block);
51
+ return shape.rebuild(block, groups.map(group => group.map(map)));
52
+ });
53
+ }
54
+ /** The async twin. Highlighting and data resolution both await per leaf. */
55
+ export async function mapBlockTreeAsync(blocks, map, shape) {
56
+ return Promise.all(blocks.map(async (block) => {
57
+ const groups = shape.containers(block);
58
+ if (!groups)
59
+ return map(block);
60
+ const mapped = await Promise.all(groups.map(group => Promise.all(group.map(map))));
61
+ return shape.rebuild(block, mapped);
62
+ }));
63
+ }
64
+ /**
65
+ * Whether any leaf satisfies `predicate`, containers included.
66
+ *
67
+ * The copy this replaces asked "does this page hold a code block?" to decide
68
+ * whether to ship a highlighter, and answered by checking the top level only —
69
+ * so a code block inside an infobox shipped unhighlighted.
70
+ */
71
+ export function someBlock(blocks, predicate, containers) {
72
+ return blocks.some(block => {
73
+ const groups = containers(block);
74
+ if (!groups)
75
+ return predicate(block);
76
+ return groups.some(group => someBlock(group, predicate, containers));
77
+ });
78
+ }
37
79
  // ---- the shared leaf renderers ----------------------------------------------
38
80
  /**
39
81
  * Fenced blocks, one per tab, each under its label.
@@ -1 +1 @@
1
- {"version":3,"file":"blocks.js","sourceRoot":"","sources":["../src/blocks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"blocks.js","sourceRoot":"","sources":["../src/blocks.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,16 @@ export interface ComboboxInput {
11
11
  count: number;
12
12
  /** Index of the highlighted row. */
13
13
  highlight: number;
14
+ /**
15
+ * Whether the surface is actually showing its list. Defaults to `count > 0`.
16
+ *
17
+ * The hook knows how many results it holds; only the surface knows whether it
18
+ * is rendering them. A header that hides its dropdown on blur while the hook
19
+ * still holds items would otherwise claim `aria-expanded` for a list that is
20
+ * not in the document, and point `aria-activedescendant` at a row that is not
21
+ * there — which is the dangling reference this module exists to prevent.
22
+ */
23
+ open?: boolean;
14
24
  }
15
25
  export interface ComboboxInputProps {
16
26
  role: 'combobox';
@@ -46,5 +56,5 @@ export declare function listId(baseId: string, listKey?: string): string;
46
56
  * than no attribute: the reader announces nothing and the field still claims to
47
57
  * have an active option.
48
58
  */
49
- export declare function comboboxAria({ baseId, listKey, count, highlight }: ComboboxInput): ComboboxAria;
59
+ export declare function comboboxAria({ baseId, listKey, count, highlight, open }: ComboboxInput): ComboboxAria;
50
60
  //# sourceMappingURL=combobox.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"combobox.d.ts","sourceRoot":"","sources":["../src/combobox.ts"],"names":[],"mappings":"AAsBA,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf;;;+DAG2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,uBAAuB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,CAAC;IACf,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,kBAAkB,CAAC;IAC/B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,mBAAmB,CAAC;CACrD;AAED;mFACmF;AACnF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3F;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,aAAa,GAAG,YAAY,CAmB/F"}
1
+ {"version":3,"file":"combobox.d.ts","sourceRoot":"","sources":["../src/combobox.ts"],"names":[],"mappings":"AAsBA,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf;;;+DAG2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,uBAAuB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,CAAC;IACf,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,kBAAkB,CAAC;IAC/B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,mBAAmB,CAAC;CACrD;AAED;mFACmF;AACnF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3F;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,aAAa,GAAG,YAAY,CAmBrG"}
package/dist/combobox.js CHANGED
@@ -36,14 +36,14 @@ export function listId(baseId, listKey) {
36
36
  * than no attribute: the reader announces nothing and the field still claims to
37
37
  * have an active option.
38
38
  */
39
- export function comboboxAria({ baseId, listKey, count, highlight }) {
39
+ export function comboboxAria({ baseId, listKey, count, highlight, open }) {
40
40
  const list = listId(baseId, listKey);
41
- const open = count > 0;
42
- const active = open && highlight >= 0 && highlight < count;
41
+ const expanded = (open ?? true) && count > 0;
42
+ const active = expanded && highlight >= 0 && highlight < count;
43
43
  return {
44
44
  inputProps: {
45
45
  role: 'combobox',
46
- 'aria-expanded': open,
46
+ 'aria-expanded': expanded,
47
47
  'aria-controls': list,
48
48
  'aria-autocomplete': 'list',
49
49
  'aria-activedescendant': active ? optionId(baseId, listKey, highlight) : undefined,
@@ -1 +1 @@
1
- {"version":3,"file":"combobox.js","sourceRoot":"","sources":["../src/combobox.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,0BAA0B;AAC1B,EAAE;AACF,gFAAgF;AAChF,wEAAwE;AACxE,uBAAuB;AACvB,EAAE;AACF,8EAA8E;AAC9E,mEAAmE;AACnE,6EAA6E;AAC7E,oBAAoB;AACpB,mCAAmC;AACnC,6EAA6E;AAC7E,aAAa;AACb,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,6EAA6E;AA0C7E;mFACmF;AACnF,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,OAA2B,EAAE,KAAa;IACjF,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,KAAK,EAAE,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,MAAc,EAAE,OAAgB;IACrD,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,OAAO,CAAC;AAC7D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAiB;IAC/E,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC;IACvB,MAAM,MAAM,GAAG,IAAI,IAAI,SAAS,IAAI,CAAC,IAAI,SAAS,GAAG,KAAK,CAAC;IAC3D,OAAO;QACL,UAAU,EAAE;YACV,IAAI,EAAE,UAAU;YAChB,eAAe,EAAE,IAAI;YACrB,eAAe,EAAE,IAAI;YACrB,mBAAmB,EAAE,MAAM;YAC3B,uBAAuB,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;SACnF;QACD,SAAS,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;QACxC,WAAW,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC;YAC/B,EAAE,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;YACpC,IAAI,EAAE,QAAQ;YACd,eAAe,EAAE,KAAK,KAAK,SAAS;SACrC,CAAC;KACH,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"combobox.js","sourceRoot":"","sources":["../src/combobox.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,0BAA0B;AAC1B,EAAE;AACF,gFAAgF;AAChF,wEAAwE;AACxE,uBAAuB;AACvB,EAAE;AACF,8EAA8E;AAC9E,mEAAmE;AACnE,6EAA6E;AAC7E,oBAAoB;AACpB,mCAAmC;AACnC,6EAA6E;AAC7E,aAAa;AACb,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,6EAA6E;AAoD7E;mFACmF;AACnF,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,OAA2B,EAAE,KAAa;IACjF,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,KAAK,EAAE,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,MAAc,EAAE,OAAgB;IACrD,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,OAAO,CAAC;AAC7D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAiB;IACrF,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,QAAQ,IAAI,SAAS,IAAI,CAAC,IAAI,SAAS,GAAG,KAAK,CAAC;IAC/D,OAAO;QACL,UAAU,EAAE;YACV,IAAI,EAAE,UAAU;YAChB,eAAe,EAAE,QAAQ;YACzB,eAAe,EAAE,IAAI;YACrB,mBAAmB,EAAE,MAAM;YAC3B,uBAAuB,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;SACnF;QACD,SAAS,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;QACxC,WAAW,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC;YAC/B,EAAE,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;YACpC,IAAI,EAAE,QAAQ;YACd,eAAe,EAAE,KAAK,KAAK,SAAS;SACrC,CAAC;KACH,CAAC;AACJ,CAAC"}
package/dist/feed.d.ts ADDED
@@ -0,0 +1,50 @@
1
+ export declare const escXml: (s: string) => string;
2
+ /**
3
+ * `content:encoded` carries raw HTML, so it travels inside CDATA. A literal
4
+ * `]]>` in the body would close the section early and corrupt every item after
5
+ * it — not just the one that contained it.
6
+ */
7
+ export declare const cdata: (html: string) => string;
8
+ /**
9
+ * Aggregators hard-truncate a description around 150 characters. Trimming on a
10
+ * word boundary first means the cut lands between words rather than inside one.
11
+ */
12
+ export declare function clampWords(s: string, max?: number): string;
13
+ /**
14
+ * Relative hrefs and srcs are meaningless once an item is syndicated, so every
15
+ * internal link is absolutised on the way out. Protocol-relative and anchor-only
16
+ * targets are left alone.
17
+ */
18
+ export declare function absolutise(html: string, baseUrl: string): string;
19
+ export interface FeedItem {
20
+ title: string;
21
+ url: string;
22
+ description: string;
23
+ date: Date;
24
+ /** Becomes an `<enclosure>`. */
25
+ image?: string;
26
+ /** Becomes `content:encoded`. Full-text RSS is what lets a feed be mirrored. */
27
+ html?: string;
28
+ categories?: string[];
29
+ }
30
+ export declare function renderItem(item: FeedItem): string;
31
+ export interface FeedChannel {
32
+ title: string;
33
+ link: string;
34
+ description: string;
35
+ /** The feed's own URL, for `atom:link rel="self"`. */
36
+ self: string;
37
+ language?: string;
38
+ /** Channel-level licence, so the grant travels with the feed. */
39
+ copyright?: string;
40
+ }
41
+ /**
42
+ * `lastBuildDate` comes from the newest item, not from the clock.
43
+ *
44
+ * A feed whose build date moves on every request tells every poller it changed
45
+ * when it did not, which is exactly the recrawl the conditional-GET helpers
46
+ * exist to prevent. An empty feed has no build date rather than a fictional one.
47
+ */
48
+ export declare function renderFeed(channel: FeedChannel, items: readonly FeedItem[]): string;
49
+ export declare const FEED_HEADERS: Record<string, string>;
50
+ //# sourceMappingURL=feed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feed.d.ts","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"AA0BA,eAAO,MAAM,MAAM,GAAI,GAAG,MAAM,KAAG,MAAyD,CAAC;AAE7F;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAI,MAAM,MAAM,KAAG,MAAyD,CAAC;AAE/F;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,SAAM,GAAG,MAAM,CAKvD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAKhE;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,IAAI,CAAC;IACX,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAajD;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,MAAM,CAoBnF;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAG/C,CAAC"}
package/dist/feed.js ADDED
@@ -0,0 +1,94 @@
1
+ // feed.ts — an RSS 2.0 channel, assembled.
2
+ //
3
+ // Three repos, four feeds, and the same 47 of 64 significant lines in each —
4
+ // including, verbatim in two of them, the four-line comment explaining why the
5
+ // apostrophe uses a numeric reference rather than `&apos;`. That comment
6
+ // travelling by copy-paste is what made this worth lifting.
7
+ //
8
+ // The union, because each copy had something the others lacked: one had
9
+ // `content:encoded`, `<enclosure>`, `<category>` and a word-boundary clamp; two
10
+ // had a `lastBuildDate` derived from the newest item, where the third stamped
11
+ // `new Date()` on every request — a validator that changes when nothing has,
12
+ // which is the same waste the ETag helpers exist to avoid.
13
+ //
14
+ // What stays with the caller is the channel's identity and the block walk that
15
+ // turns a page into HTML. Every project owns its own type set.
16
+ // Numeric reference for the apostrophe: `&apos;` is an XML entity that older
17
+ // readers parsing the feed as HTML do not carry in their entity table.
18
+ const XML_ESCAPES = {
19
+ '&': '&amp;',
20
+ '<': '&lt;',
21
+ '>': '&gt;',
22
+ '"': '&quot;',
23
+ "'": '&#39;',
24
+ };
25
+ export const escXml = (s) => s.replace(/[&<>"']/g, c => XML_ESCAPES[c] ?? c);
26
+ /**
27
+ * `content:encoded` carries raw HTML, so it travels inside CDATA. A literal
28
+ * `]]>` in the body would close the section early and corrupt every item after
29
+ * it — not just the one that contained it.
30
+ */
31
+ export const cdata = (html) => `<![CDATA[${html.replace(/]]>/g, ']]&gt;')}]]>`;
32
+ /**
33
+ * Aggregators hard-truncate a description around 150 characters. Trimming on a
34
+ * word boundary first means the cut lands between words rather than inside one.
35
+ */
36
+ export function clampWords(s, max = 150) {
37
+ if (s.length <= max)
38
+ return s;
39
+ const cut = s.slice(0, max);
40
+ const lastSpace = cut.lastIndexOf(' ');
41
+ return (lastSpace > 0 ? cut.slice(0, lastSpace) : cut).replace(/[.,;:–-]$/, '');
42
+ }
43
+ /**
44
+ * Relative hrefs and srcs are meaningless once an item is syndicated, so every
45
+ * internal link is absolutised on the way out. Protocol-relative and anchor-only
46
+ * targets are left alone.
47
+ */
48
+ export function absolutise(html, baseUrl) {
49
+ return html.replace(/\s(href|src)="\/(?!\/)([^"]*)"/gi, (_m, attr, path) => ` ${attr}="${baseUrl}/${path}"`);
50
+ }
51
+ export function renderItem(item) {
52
+ return [
53
+ ' <item>',
54
+ ` <title>${escXml(item.title)}</title>`,
55
+ ` <link>${escXml(item.url)}</link>`,
56
+ ` <guid isPermaLink="true">${escXml(item.url)}</guid>`,
57
+ ` <description>${escXml(item.description)}</description>`,
58
+ ...(item.categories ?? []).map(c => ` <category>${escXml(c)}</category>`),
59
+ ` <pubDate>${item.date.toUTCString()}</pubDate>`,
60
+ ...(item.image ? [` <enclosure url="${escXml(item.image)}" type="image/png" />`] : []),
61
+ ...(item.html ? [` <content:encoded>${cdata(item.html)}</content:encoded>`] : []),
62
+ ' </item>',
63
+ ].join('\n');
64
+ }
65
+ /**
66
+ * `lastBuildDate` comes from the newest item, not from the clock.
67
+ *
68
+ * A feed whose build date moves on every request tells every poller it changed
69
+ * when it did not, which is exactly the recrawl the conditional-GET helpers
70
+ * exist to prevent. An empty feed has no build date rather than a fictional one.
71
+ */
72
+ export function renderFeed(channel, items) {
73
+ const newest = items.reduce((max, item) => (!max || item.date > max ? item.date : max), null);
74
+ return [
75
+ '<?xml version="1.0" encoding="UTF-8"?>',
76
+ '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">',
77
+ ' <channel>',
78
+ ` <title>${escXml(channel.title)}</title>`,
79
+ ` <link>${escXml(channel.link)}</link>`,
80
+ ` <description>${escXml(channel.description)}</description>`,
81
+ ` <language>${escXml(channel.language ?? 'en')}</language>`,
82
+ ...(channel.copyright ? [` <copyright>${escXml(channel.copyright)}</copyright>`] : []),
83
+ ` <atom:link href="${escXml(channel.self)}" rel="self" type="application/rss+xml" />`,
84
+ ...(newest ? [` <lastBuildDate>${newest.toUTCString()}</lastBuildDate>`] : []),
85
+ ...items.map(renderItem),
86
+ ' </channel>',
87
+ '</rss>',
88
+ ].join('\n');
89
+ }
90
+ export const FEED_HEADERS = {
91
+ 'Content-Type': 'application/rss+xml; charset=utf-8',
92
+ 'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400',
93
+ };
94
+ //# sourceMappingURL=feed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feed.js","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -15,4 +15,7 @@ export * from './maps.js';
15
15
  export * from './freshness.js';
16
16
  export * from './combobox.js';
17
17
  export * from './crawlers.js';
18
+ export * from './revisions.js';
19
+ export * from './feed.js';
20
+ export * from './license.js';
18
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -20,4 +20,7 @@ export * from './maps.js';
20
20
  export * from './freshness.js';
21
21
  export * from './combobox.js';
22
22
  export * from './crawlers.js';
23
+ export * from './revisions.js';
24
+ export * from './feed.js';
25
+ export * from './license.js';
23
26
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,mBAAmB;AAEnB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,mBAAmB;AAEnB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC"}
@@ -0,0 +1,39 @@
1
+ export interface License {
2
+ /** SPDX identifier, e.g. `CC-BY-4.0`. */
3
+ spdx: string;
4
+ /** Human name, e.g. `Creative Commons Attribution 4.0 International`. */
5
+ name: string;
6
+ url: string;
7
+ /** The credit line a dataset should carry. */
8
+ attribution: string;
9
+ }
10
+ /** The grant every one of these wikis makes, parameterised by who is making it. */
11
+ export declare function ccBy40(opts: {
12
+ siteName: string;
13
+ siteUrl: string;
14
+ }): License;
15
+ export interface LicenseBlockOptions {
16
+ license: License;
17
+ /**
18
+ * What the grant covers, as a sentence fragment completing "… is licensed
19
+ * under". Defaults to naming the whole site, which is the rarer case: every
20
+ * consumer of this so far grants over some of what it serves and not all.
21
+ */
22
+ scope?: string;
23
+ /** Sentences appended after the identifiers — carve-outs, caveats, terms. */
24
+ excludes?: readonly string[];
25
+ /** `Licence` where the site's prose says so. Defaults to `License`. */
26
+ heading?: string;
27
+ }
28
+ /**
29
+ * The block, as lines, ready to join into any text export.
30
+ *
31
+ * Returned as an array rather than a string because every caller splices it
32
+ * into a document it is already building line by line, and a caller that wants
33
+ * the string can join it.
34
+ */
35
+ export declare function licenseLines({ license, scope, excludes, heading, }: LicenseBlockOptions): string[];
36
+ export declare function licenseBlock(opts: LicenseBlockOptions): string;
37
+ /** The one-line form, for a frontmatter field or a feed's `<copyright>`. */
38
+ export declare function licenseNote(license: License): string;
39
+ //# sourceMappingURL=license.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,OAAO;IACtB,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,mFAAmF;AACnF,wBAAgB,MAAM,CAAC,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAO3E;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,KAAsB,EACtB,QAAa,EACb,OAAiC,GAClC,EAAE,mBAAmB,GAAG,MAAM,EAAE,CAchC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAE9D;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAEpD"}
@@ -0,0 +1,50 @@
1
+ // license.ts — the licence declaration every agent surface has to carry.
2
+ //
3
+ // S10 requires a licence on every export — llms.txt, the agent card, the
4
+ // OpenAPI info block, the JSON-LD, the feed's `<copyright>` — and each repo
5
+ // satisfied that requirement by writing the same block again. The shape was
6
+ // identical in all three, and the sentence beginning "You may ingest, embed,
7
+ // and redistribute" was verbatim in all three.
8
+ //
9
+ // What is genuinely per-project is the scope: which half of a site the grant
10
+ // covers, and what it deliberately excludes. That is the `scope` and `excludes`
11
+ // below, and it is the only part a consumer should be writing by hand.
12
+ /** The grant every one of these wikis makes, parameterised by who is making it. */
13
+ export function ccBy40(opts) {
14
+ return {
15
+ spdx: 'CC-BY-4.0',
16
+ name: 'Creative Commons Attribution 4.0 International',
17
+ url: 'https://creativecommons.org/licenses/by/4.0/',
18
+ attribution: `Source: ${opts.siteName} (${opts.siteUrl}), CC BY 4.0`,
19
+ };
20
+ }
21
+ /**
22
+ * The block, as lines, ready to join into any text export.
23
+ *
24
+ * Returned as an array rather than a string because every caller splices it
25
+ * into a document it is already building line by line, and a caller that wants
26
+ * the string can join it.
27
+ */
28
+ export function licenseLines({ license, scope = 'This content', excludes = [], heading = 'License & Attribution', }) {
29
+ return [
30
+ `## ${heading}`,
31
+ '',
32
+ `${scope} is licensed under the ${license.name} (${license.spdx}): ${license.url}`,
33
+ '',
34
+ 'You may ingest, embed, and redistribute this content in RAG systems, fine-tuning',
35
+ 'datasets, or other derivative works, including commercially. Attribution at the',
36
+ 'dataset or system level is sufficient.',
37
+ '',
38
+ `- Recommended attribution: "${license.attribution}"`,
39
+ `- SPDX identifier: ${license.spdx}`,
40
+ ...(excludes.length ? ['', ...excludes] : []),
41
+ ];
42
+ }
43
+ export function licenseBlock(opts) {
44
+ return licenseLines(opts).join('\n');
45
+ }
46
+ /** The one-line form, for a frontmatter field or a feed's `<copyright>`. */
47
+ export function licenseNote(license) {
48
+ return `${license.name} (${license.spdx}): ${license.url}`;
49
+ }
50
+ //# sourceMappingURL=license.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"license.js","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,yEAAyE;AACzE,4EAA4E;AAC5E,4EAA4E;AAC5E,6EAA6E;AAC7E,+CAA+C;AAC/C,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,uEAAuE;AAYvE,mFAAmF;AACnF,MAAM,UAAU,MAAM,CAAC,IAA2C;IAChE,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,gDAAgD;QACtD,GAAG,EAAE,8CAA8C;QACnD,WAAW,EAAE,WAAW,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,OAAO,cAAc;KACrE,CAAC;AACJ,CAAC;AAgBD;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,EAC3B,OAAO,EACP,KAAK,GAAG,cAAc,EACtB,QAAQ,GAAG,EAAE,EACb,OAAO,GAAG,uBAAuB,GACb;IACpB,OAAO;QACL,MAAM,OAAO,EAAE;QACf,EAAE;QACF,GAAG,KAAK,0BAA0B,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC,GAAG,EAAE;QAClF,EAAE;QACF,kFAAkF;QAClF,iFAAiF;QACjF,wCAAwC;QACxC,EAAE;QACF,+BAA+B,OAAO,CAAC,WAAW,GAAG;QACrD,sBAAsB,OAAO,CAAC,IAAI,EAAE;QACpC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAyB;IACpD,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,OAAO,GAAG,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC;AAC7D,CAAC"}