@pagenary/publisher 2026.6.9 → 2026.6.11

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
@@ -89,7 +89,9 @@ inspect, build, or run the AIWG project from `~/dev/aiwg`.
89
89
  - **Fortemi-backed full-text search** — ranked results with snippets over a static
90
90
  chunked index emitted at build time; lazy chunk fetch (precache) and offset
91
91
  paging for infinite scroll, with a clean in-browser fallback. No server, no WASM.
92
- See `docs/ARCHITECTURE.md` and `.aiwg/architecture/adr/ADR-015-fortemi-core-search-adapter.md`.
92
+ The engine is *vendored* (`@fortemi/core`) — see `docs/VENDORING.md` for the
93
+ process, `docs/ARCHITECTURE.md`, and
94
+ `.aiwg/architecture/adr/ADR-015-fortemi-core-search-adapter.md`.
93
95
  - **Manifest-Driven Nav** — declarative navigation structure
94
96
  - **Keyboard Navigation** — arrow keys, Enter to select
95
97
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagenary/publisher",
3
- "version": "2026.6.9",
3
+ "version": "2026.6.11",
4
4
  "type": "module",
5
5
  "description": "Multi-tenant static publishing component for Pagenary platform.",
6
6
  "license": "AGPL-3.0-or-later",
@@ -67,6 +67,7 @@
67
67
  "terser": "^5.44.0"
68
68
  },
69
69
  "devDependencies": {
70
+ "@fortemi/core": "2026.6.6",
70
71
  "jest": "^29.7.0"
71
72
  }
72
73
  }
package/site/index.html CHANGED
@@ -21,7 +21,7 @@
21
21
  </script>
22
22
  <link rel="icon" type="image/png" href="./favicon.png" />
23
23
  <link rel="stylesheet" href="./styles.css" />
24
- <meta name="x-build" content="2026-06-16T20:26:27.780Z" />
24
+ <meta name="x-build" content="2026-06-17T21:38:42.780Z" />
25
25
  </head>
26
26
  <body>
27
27
  <a class="skip-link" href="#app">Skip to content</a>
@@ -27,7 +27,7 @@
27
27
  "headline": "API Reference",
28
28
  "description": "Module-level documentation for the publisher internals.",
29
29
  "url": "https://docs.pagenary.com/pages/api.html",
30
- "dateModified": "2026-06-16",
30
+ "dateModified": "2026-06-17",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/api.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Architecture",
28
28
  "description": "The static SPA pattern, build pipeline, and tenant content model.",
29
29
  "url": "https://docs.pagenary.com/pages/architecture.html",
30
- "dateModified": "2026-06-16",
30
+ "dateModified": "2026-06-17",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/architecture.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Deployment",
28
28
  "description": "Hosting the static output and multi-tenant domain routing.",
29
29
  "url": "https://docs.pagenary.com/pages/deployment.html",
30
- "dateModified": "2026-06-16",
30
+ "dateModified": "2026-06-17",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/deployment.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Developer Guide",
28
28
  "description": "Project layout, scripts, and the content authoring workflow.",
29
29
  "url": "https://docs.pagenary.com/pages/developer-guide.html",
30
- "dateModified": "2026-06-16",
30
+ "dateModified": "2026-06-17",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/developer-guide.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Extending",
28
28
  "description": "Add section templates, content types, and build behaviors.",
29
29
  "url": "https://docs.pagenary.com/pages/extending.html",
30
- "dateModified": "2026-06-16",
30
+ "dateModified": "2026-06-17",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/extending.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Quickstart",
28
28
  "description": "Install, build the default bundle, and serve it locally.",
29
29
  "url": "https://docs.pagenary.com/pages/quickstart.html",
30
- "dateModified": "2026-06-16",
30
+ "dateModified": "2026-06-17",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/quickstart.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "SEO Strategy",
28
28
  "description": "Metadata, hash-routing considerations, and discoverability.",
29
29
  "url": "https://docs.pagenary.com/pages/seo-strategy.html",
30
- "dateModified": "2026-06-16",
30
+ "dateModified": "2026-06-17",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/seo-strategy.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Tenant Configuration",
28
28
  "description": "Every config.json option: branding, theming, SEO, and export.",
29
29
  "url": "https://docs.pagenary.com/pages/tenant-config.html",
30
- "dateModified": "2026-06-16",
30
+ "dateModified": "2026-06-17",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/tenant-config.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Theming Recipes",
28
28
  "description": "Copy-paste recipes for colors, fonts, and nav positions — with live demos.",
29
29
  "url": "https://docs.pagenary.com/pages/theming-recipes.html",
30
- "dateModified": "2026-06-16",
30
+ "dateModified": "2026-06-17",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/theming-recipes.html"
@@ -27,7 +27,7 @@
27
27
  "headline": "Welcome",
28
28
  "description": "What Pagenary is and how this dogfooded portal is built.",
29
29
  "url": "https://docs.pagenary.com/pages/welcome.html",
30
- "dateModified": "2026-06-16",
30
+ "dateModified": "2026-06-17",
31
31
  "mainEntityOfPage": {
32
32
  "@type": "WebPage",
33
33
  "@id": "https://docs.pagenary.com/pages/welcome.html"
package/site/robots.txt CHANGED
@@ -1,5 +1,5 @@
1
1
  # Pagenary Docs
2
- # Generated: 2026-06-16T20:26:28.432Z
2
+ # Generated: 2026-06-17T21:38:43.419Z
3
3
 
4
4
  User-agent: *
5
5
  Allow: /
package/site/sitemap.xml CHANGED
@@ -2,67 +2,67 @@
2
2
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
3
3
  <url>
4
4
  <loc>https://docs.pagenary.com/</loc>
5
- <lastmod>2026-06-16</lastmod>
5
+ <lastmod>2026-06-17</lastmod>
6
6
  <changefreq>weekly</changefreq>
7
7
  <priority>1.0</priority>
8
8
  </url>
9
9
  <url>
10
10
  <loc>https://docs.pagenary.com/pages/welcome.html</loc>
11
- <lastmod>2026-06-16</lastmod>
11
+ <lastmod>2026-06-17</lastmod>
12
12
  <changefreq>monthly</changefreq>
13
13
  <priority>0.8</priority>
14
14
  </url>
15
15
  <url>
16
16
  <loc>https://docs.pagenary.com/pages/quickstart.html</loc>
17
- <lastmod>2026-06-16</lastmod>
17
+ <lastmod>2026-06-17</lastmod>
18
18
  <changefreq>monthly</changefreq>
19
19
  <priority>0.6</priority>
20
20
  </url>
21
21
  <url>
22
22
  <loc>https://docs.pagenary.com/pages/developer-guide.html</loc>
23
- <lastmod>2026-06-16</lastmod>
23
+ <lastmod>2026-06-17</lastmod>
24
24
  <changefreq>monthly</changefreq>
25
25
  <priority>0.6</priority>
26
26
  </url>
27
27
  <url>
28
28
  <loc>https://docs.pagenary.com/pages/tenant-config.html</loc>
29
- <lastmod>2026-06-16</lastmod>
29
+ <lastmod>2026-06-17</lastmod>
30
30
  <changefreq>monthly</changefreq>
31
31
  <priority>0.6</priority>
32
32
  </url>
33
33
  <url>
34
34
  <loc>https://docs.pagenary.com/pages/theming-recipes.html</loc>
35
- <lastmod>2026-06-16</lastmod>
35
+ <lastmod>2026-06-17</lastmod>
36
36
  <changefreq>monthly</changefreq>
37
37
  <priority>0.6</priority>
38
38
  </url>
39
39
  <url>
40
40
  <loc>https://docs.pagenary.com/pages/extending.html</loc>
41
- <lastmod>2026-06-16</lastmod>
41
+ <lastmod>2026-06-17</lastmod>
42
42
  <changefreq>monthly</changefreq>
43
43
  <priority>0.6</priority>
44
44
  </url>
45
45
  <url>
46
46
  <loc>https://docs.pagenary.com/pages/architecture.html</loc>
47
- <lastmod>2026-06-16</lastmod>
47
+ <lastmod>2026-06-17</lastmod>
48
48
  <changefreq>monthly</changefreq>
49
49
  <priority>0.6</priority>
50
50
  </url>
51
51
  <url>
52
52
  <loc>https://docs.pagenary.com/pages/api.html</loc>
53
- <lastmod>2026-06-16</lastmod>
53
+ <lastmod>2026-06-17</lastmod>
54
54
  <changefreq>monthly</changefreq>
55
55
  <priority>0.6</priority>
56
56
  </url>
57
57
  <url>
58
58
  <loc>https://docs.pagenary.com/pages/deployment.html</loc>
59
- <lastmod>2026-06-16</lastmod>
59
+ <lastmod>2026-06-17</lastmod>
60
60
  <changefreq>monthly</changefreq>
61
61
  <priority>0.6</priority>
62
62
  </url>
63
63
  <url>
64
64
  <loc>https://docs.pagenary.com/pages/seo-strategy.html</loc>
65
- <lastmod>2026-06-16</lastmod>
65
+ <lastmod>2026-06-17</lastmod>
66
66
  <changefreq>monthly</changefreq>
67
67
  <priority>0.6</priority>
68
68
  </url>
@@ -53,8 +53,10 @@ interface AiwgFortemiChunkPartRef {
53
53
  }
54
54
  declare const AIWG_SCAN_REQUIRED_FIELDS: Array<keyof AiwgFortemiRecord>;
55
55
  type AiwgFortemiProjectedRecord = Pick<AiwgFortemiRecord, 'schema_version' | 'id' | 'type' | 'title' | 'text' | 'facets' | 'tags' | 'concepts' | 'privacy'> & Partial<AiwgFortemiRecord>;
56
+ type AiwgDetailIdEncoding = 'uri' | 'base64url';
56
57
  interface AiwgFortemiChunkDetailRef {
57
58
  href: string;
59
+ encoding?: AiwgDetailIdEncoding;
58
60
  }
59
61
  interface AiwgFortemiChunkManifest {
60
62
  schema_version: 'aiwg.fortemi.index.chunk-manifest.v1';
@@ -125,6 +127,7 @@ interface AiwgChunkedIndexLoadOptions {
125
127
  maxCachedParts?: number;
126
128
  detailLoader?: AiwgChunkedIndexDetailLoader;
127
129
  maxCachedDetails?: number;
130
+ maxCachedMatches?: number;
128
131
  }
129
132
  type AiwgChunkedIndexProgressPhase = 'part' | 'query';
130
133
  interface AiwgChunkedIndexProgress {
@@ -200,12 +203,15 @@ declare function assertAiwgFortemiChunkManifest(value: unknown): AiwgFortemiChun
200
203
  declare function validateAiwgFortemiChunkPart(value: unknown, partRef?: AiwgFortemiChunkPartRef, manifest?: AiwgFortemiChunkManifest): AiwgChunkedIndexValidationResult;
201
204
  declare function assertAiwgFortemiChunkPart(value: unknown, partRef?: AiwgFortemiChunkPartRef, manifest?: AiwgFortemiChunkManifest): AiwgFortemiChunkPart;
202
205
  declare function createAiwgFetchChunkLoader(baseUrl?: string | URL): AiwgChunkedIndexLoader;
206
+ declare function encodeAiwgDetailId(id: string, encoding?: AiwgDetailIdEncoding): string;
207
+ declare function aiwgDetailHrefForId(detail: AiwgFortemiChunkDetailRef, id: string): string;
203
208
  declare function createAiwgFetchDetailLoader(baseUrl?: string | URL): AiwgChunkedIndexDetailLoader;
204
209
  declare function getAiwgFortemiFacets(items: AiwgFortemiRecord[]): Record<string, Record<string, number>>;
205
210
  interface AiwgChunkedIndexBuildOptions {
206
211
  partSize?: number;
207
212
  projection?: Array<keyof AiwgFortemiRecord>;
208
213
  detailHref?: string;
214
+ idEncoding?: AiwgDetailIdEncoding;
209
215
  generatedAt?: string;
210
216
  }
211
217
  interface AiwgChunkedIndexBuildResult {
@@ -216,12 +222,13 @@ interface AiwgChunkedIndexBuildResult {
216
222
  }>;
217
223
  details: Array<{
218
224
  id: string;
225
+ href: string;
219
226
  record: AiwgFortemiRecord;
220
227
  }>;
221
228
  }
222
229
  declare function buildAiwgChunkedIndex(index: AiwgFortemiIndexExport, options?: AiwgChunkedIndexBuildOptions): AiwgChunkedIndexBuildResult;
223
230
  declare function queryAiwgFortemiIndex(index: AiwgFortemiIndexExport, query?: string, options?: AiwgIndexQueryOptions): AiwgIndexQueryResult;
224
- declare function createAiwgReviewDecisionExport(source: AiwgFortemiIndexExport, decisions: AiwgReviewDecision[], generatedAt?: string): AiwgReviewDecisionExport;
231
+ declare function createAiwgReviewDecisionExport(source: Pick<AiwgFortemiIndexExport, 'schema_version'>, decisions: AiwgReviewDecision[], generatedAt?: string): AiwgReviewDecisionExport;
225
232
  declare function createAiwgIndexController(initialIndex?: AiwgFortemiIndexExport): AiwgIndexController;
226
233
  declare function aiwgFortemiIndexToCommunityGraph(index: AiwgFortemiIndexExport, options?: AiwgIndexGraphOptions): {
227
234
  nodes: {
@@ -239,4 +246,4 @@ declare function aiwgFortemiIndexToCommunityGraph(index: AiwgFortemiIndexExport,
239
246
  }[];
240
247
  };
241
248
 
242
- export { AIWG_SCAN_REQUIRED_FIELDS, type AiwgChunkedIndexBuildOptions, type AiwgChunkedIndexBuildResult, type AiwgChunkedIndexDetailLoader, type AiwgChunkedIndexLoadOptions, type AiwgChunkedIndexLoader, type AiwgChunkedIndexProgress, type AiwgChunkedIndexProgressPhase, type AiwgChunkedIndexQueryOptions, type AiwgChunkedIndexQueryResult, type AiwgChunkedIndexValidationResult, type AiwgFortemiChunkDetailRef, type AiwgFortemiChunkManifest, type AiwgFortemiChunkPart, type AiwgFortemiChunkPartRef, type AiwgFortemiIndexExport, type AiwgFortemiProjectedRecord, type AiwgFortemiProvenance, type AiwgFortemiRecord, type AiwgFortemiRecordSource, type AiwgFortemiRecordType, type AiwgFortemiRelationship, type AiwgIndexController, type AiwgIndexControllerListener, type AiwgIndexControllerSnapshot, type AiwgIndexGraphOptions, type AiwgIndexQueryMatch, type AiwgIndexQueryOptions, type AiwgIndexQueryRankedItem, type AiwgIndexQueryResult, type AiwgIndexQueryWeights, type AiwgIndexValidationResult, type AiwgPrivacyClassification, type AiwgProvenanceConfidence, type AiwgReviewAction, type AiwgReviewDecision, type AiwgReviewDecisionExport, type AiwgReviewInput, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, buildAiwgChunkedIndex, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, getAiwgFortemiFacets, queryAiwgFortemiIndex, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport };
249
+ export { AIWG_SCAN_REQUIRED_FIELDS, type AiwgChunkedIndexBuildOptions, type AiwgChunkedIndexBuildResult, type AiwgChunkedIndexDetailLoader, type AiwgChunkedIndexLoadOptions, type AiwgChunkedIndexLoader, type AiwgChunkedIndexProgress, type AiwgChunkedIndexProgressPhase, type AiwgChunkedIndexQueryOptions, type AiwgChunkedIndexQueryResult, type AiwgChunkedIndexValidationResult, type AiwgDetailIdEncoding, type AiwgFortemiChunkDetailRef, type AiwgFortemiChunkManifest, type AiwgFortemiChunkPart, type AiwgFortemiChunkPartRef, type AiwgFortemiIndexExport, type AiwgFortemiProjectedRecord, type AiwgFortemiProvenance, type AiwgFortemiRecord, type AiwgFortemiRecordSource, type AiwgFortemiRecordType, type AiwgFortemiRelationship, type AiwgIndexController, type AiwgIndexControllerListener, type AiwgIndexControllerSnapshot, type AiwgIndexGraphOptions, type AiwgIndexQueryMatch, type AiwgIndexQueryOptions, type AiwgIndexQueryRankedItem, type AiwgIndexQueryResult, type AiwgIndexQueryWeights, type AiwgIndexValidationResult, type AiwgPrivacyClassification, type AiwgProvenanceConfidence, type AiwgReviewAction, type AiwgReviewDecision, type AiwgReviewDecisionExport, type AiwgReviewInput, aiwgDetailHrefForId, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, buildAiwgChunkedIndex, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, encodeAiwgDetailId, getAiwgFortemiFacets, queryAiwgFortemiIndex, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport };
@@ -1 +1 @@
1
- var e=["schema_version","id","type","title","text","facets","tags","concepts","privacy"],t=["schema_version","id","type","source","title","text","facets","tags","concepts","relationships","provenance","privacy","updated_at"],r=new Set(["crm.contact","crm.organization","crm.event","crm.interaction","aiwg.artifact","docs.page"]),i={title:4,tag:3,concept:2,text:1};function s(e){return"string"==typeof e&&e.length>0}function n(e,t,r){e[t]??={},e[t][r]=(e[t][r]??0)+1}function a(e){return Number.isInteger(e)&&"number"==typeof e&&e>=0}function o(e){return Number.isInteger(e)&&"number"==typeof e&&e>0}function c(e){const i=[],n={},a=e;"aiwg.fortemi.index.export.v1"!==a?.schema_version&&i.push("schema_version must be aiwg.fortemi.index.export.v1"),s(a?.generated_at)||i.push("generated_at is required"),s(a?.source?.repo)||i.push("source.repo is required"),s(a?.source?.privacy)||i.push("source.privacy is required"),Array.isArray(a?.items)||i.push("items must be an array");const o=new Set;let c="";for(const[e,u]of(a.items??[]).entries()){for(const r of t)r in u||i.push("items["+e+"]."+r+" is required");"aiwg.fortemi.index.record.v1"!==u.schema_version&&i.push("items["+e+"].schema_version must be aiwg.fortemi.index.record.v1"),s(u.id)||i.push("items["+e+"].id is required"),s(u.id)&&o.has(u.id)&&i.push("duplicate id: "+u.id),s(u.id)&&o.add(u.id),c&&s(u.id)&&c.localeCompare(u.id)>0&&i.push("items must be sorted by id: "+c+" before "+u.id),s(u.id)&&(c=u.id),r.has(u.type)?n[u.type]=(n[u.type]??0)+1:i.push("items["+e+"].type is invalid"),s(u.source?.path)||i.push("items["+e+"].source.path is required"),s(u.source?.repo_relative_path)||i.push("items["+e+"].source.repo_relative_path is required"),s(u.source?.locator)||i.push("items["+e+"].source.locator is required"),Array.isArray(u.tags)||i.push("items["+e+"].tags must be an array"),Array.isArray(u.concepts)||i.push("items["+e+"].concepts must be an array"),Array.isArray(u.relationships)||i.push("items["+e+"].relationships must be an array"),Array.isArray(u.provenance)&&0!==u.provenance.length||i.push("items["+e+"].provenance must be a non-empty array"),u.privacy&&"boolean"==typeof u.privacy.pii&&s(u.privacy.classification)||i.push("items["+e+"].privacy requires classification and pii")}return{valid:0===i.length,errors:i,counts:n}}function u(e){const t=c(e);if(!t.valid)throw new Error("Invalid AIWG Fortemi index export:\n"+t.errors.join("\n"));return e}function f(t){const r=[],i=t;if("aiwg.fortemi.index.chunk-manifest.v1"!==i?.schema_version&&r.push("schema_version must be aiwg.fortemi.index.chunk-manifest.v1"),s(i?.generated_at)||r.push("generated_at is required"),s(i?.source?.repo)||r.push("source.repo is required"),s(i?.source?.privacy)||r.push("source.privacy is required"),a(i?.total)||r.push("total must be a non-negative integer"),o(i?.part_size)||r.push("part_size must be a positive integer"),void 0===i.facets||function(e){return!(!e||"object"!=typeof e||Array.isArray(e))&&Object.values(e).every(e=>!!e&&"object"==typeof e&&!Array.isArray(e)&&Object.values(e).every(e=>a(e)))}(i.facets)||r.push("facets must be a nested string-to-number count object"),void 0!==i.projection)if(Array.isArray(i.projection)&&i.projection.every(e=>"string"==typeof e)){const t=new Set(i.projection);for(const i of e)t.has(i)||r.push("projection must include scan-required field "+i)}else r.push("projection must be an array of field names");void 0!==i.detail&&(s(i.detail.href)?i.detail.href.includes("{id}")||r.push("detail.href must contain the {id} placeholder"):r.push("detail.href is required")),Array.isArray(i?.parts)||r.push("parts must be an array");let n=0;const c=Array.isArray(i?.parts)?i.parts:[];for(const[e,t]of c.entries())s(t.href)||r.push("parts["+e+"].href is required"),a(t.offset)||r.push("parts["+e+"].offset must be a non-negative integer"),a(t.count)||r.push("parts["+e+"].count must be a non-negative integer"),a(t.offset)&&t.offset!==n&&r.push("parts["+e+"].offset must be "+n),a(t.count)&&(n+=t.count);return a(i?.total)&&n!==i.total&&r.push("parts counts must add up to total"),{valid:0===r.length,errors:r}}function d(e){const t=f(e);if(!t.valid)throw new Error("Invalid AIWG Fortemi chunk manifest:\n"+t.errors.join("\n"));return e}function p(e,t,i){const n=[],o=e;if("aiwg.fortemi.index.chunk.v1"!==o?.schema_version&&n.push("schema_version must be aiwg.fortemi.index.chunk.v1"),"aiwg.fortemi.index.chunk-manifest.v1"!==o?.manifest_schema_version&&n.push("manifest_schema_version must be aiwg.fortemi.index.chunk-manifest.v1"),a(o?.offset)||n.push("offset must be a non-negative integer"),Array.isArray(o?.items)||n.push("items must be an array"),t&&a(o?.offset)&&o.offset!==t.offset&&n.push("offset must match manifest part offset "+t.offset),t&&Array.isArray(o?.items)&&o.items.length!==t.count&&n.push("items length must match manifest part count "+t.count),Array.isArray(o?.items))if(i?.projection)n.push(...function(e){const t=[],i=new Set;let n="";for(const[a,o]of e.entries())"aiwg.fortemi.index.record.v1"!==o.schema_version&&t.push("items["+a+"].schema_version must be aiwg.fortemi.index.record.v1"),s(o.id)||t.push("items["+a+"].id is required"),s(o.id)&&i.has(o.id)&&t.push("duplicate id: "+o.id),s(o.id)&&i.add(o.id),n&&s(o.id)&&n.localeCompare(o.id)>0&&t.push("items must be sorted by id: "+n+" before "+o.id),s(o.id)&&(n=o.id),o.type&&r.has(o.type)||t.push("items["+a+"].type is invalid"),s(o.title)||t.push("items["+a+"].title is required"),"string"!=typeof o.text&&t.push("items["+a+"].text is required"),o.facets&&"object"==typeof o.facets&&!Array.isArray(o.facets)||t.push("items["+a+"].facets must be an object"),Array.isArray(o.tags)||t.push("items["+a+"].tags must be an array"),Array.isArray(o.concepts)||t.push("items["+a+"].concepts must be an array"),o.privacy&&s(o.privacy.classification)||t.push("items["+a+"].privacy.classification is required");return t}(o.items).map(e=>"items."+e));else{const e=c({schema_version:"aiwg.fortemi.index.export.v1",generated_at:i?.generated_at??"1970-01-01T00:00:00.000Z",source:i?.source??{repo:"chunk",privacy:"public"},items:o.items});n.push(...e.errors.map(e=>"items."+e))}return{valid:0===n.length,errors:n}}function h(e,t,r){const i=p(e,t,r);if(!i.valid)throw new Error("Invalid AIWG Fortemi chunk part:\n"+i.errors.join("\n"));return e}function m(e){return async t=>{const r=e?new URL(t.href,e).toString():t.href,i=await fetch(r);if(!i.ok)throw new Error("Failed to fetch AIWG index chunk "+r+": "+i.status);return i.json()}}function l(e){return async(t,r)=>{if(!r.detail?.href)throw new Error("Manifest has no detail.href for record resolution");const i=r.detail.href.replace("{id}",encodeURIComponent(t)),s=e?new URL(i,e).toString():i,n=await fetch(s);if(!n.ok)throw new Error("Failed to fetch AIWG index detail "+s+": "+n.status);return n.json()}}function g(e){const t={};for(const r of e){n(t,"type",r.type),n(t,"privacy",r.privacy.classification);for(const e of r.tags)n(t,"tag",e);for(const e of r.concepts)n(t,"concept",e);for(const[e,i]of Object.entries(r.facets))for(const r of i)n(t,e,r)}return t}function y(e,t={}){const r=o(t.partSize)?t.partSize:500,i=t.projection,s=e.items,n=e=>String(e).padStart(4,"0"),a=e=>{if(!i)return e;const t={};for(const r of i)t[r]=e[r];return t},c=[],u=[];for(let e=0,t=0;e<s.length;e+=r,t+=1){const i=s.slice(e,e+r),o="part-"+n(t)+".json";c.push({href:o,part:{schema_version:"aiwg.fortemi.index.chunk.v1",manifest_schema_version:"aiwg.fortemi.index.chunk-manifest.v1",offset:e,items:i.map(a)}}),u.push({href:o,offset:e,count:i.length})}return{manifest:{schema_version:"aiwg.fortemi.index.chunk-manifest.v1",generated_at:t.generatedAt??e.generated_at,source:e.source,total:s.length,part_size:r,facets:g(s),parts:u,...i?{projection:i,detail:{href:t.detailHref??"detail/{id}.json"}}:{}},parts:c,details:i?s.map(e=>({id:e.id,record:e})):[]}}function v(e,t){if(!t||0===t.length)return!0;const r=new Set(e);return t.every(e=>r.has(e))}function w(e,t){if(!t)return[];const r=[];e.title.toLowerCase().includes(t)&&r.push({field:"title",value:e.title}),e.text.toLowerCase().includes(t)&&r.push({field:"text",value:e.text});for(const i of e.tags)i.toLowerCase().includes(t)&&r.push({field:"tag",value:i});for(const i of e.concepts)i.toLowerCase().includes(t)&&r.push({field:"concept",value:i});return r}function x(e,t){return e.reduce((e,r)=>e+t[r.field],0)}function _(e,t,r,i){const s=t.find(e=>"text"===e.field),n=t.find(e=>"title"===e.field),a=s??n??t[0];return function(e,t,r){const i=Math.max(20,r);if(!e)return"";if(!t)return e.length>i?`${e.slice(0,i).trimEnd()}...`:e;const s=e.toLowerCase().indexOf(t);if(s<0)return e.length>i?`${e.slice(0,i).trimEnd()}...`:e;const n=Math.max(0,Math.floor((i-t.length)/2)),a=Math.max(0,s-n),o=Math.min(e.length,a+i),c=a>0?"...":"",u=o<e.length?"...":"";return`${c}${e.slice(a,o).trim()}${u}`}(a?.value??e.text,r,i)}function b(e,t,r,s=0){const n={...i,...r.weights};return e.map((e,r)=>({item:e,ordinal:s+r,matches:w(e,t)})).filter(({item:e,matches:i})=>!(t&&0===i.length||r.types&&!r.types.includes(e.type)||r.privacy&&!r.privacy.includes(e.privacy.classification)||!v(e.tags,r.tags)||!v(e.concepts,r.concepts)||!function(e,t){return!t||Object.entries(t).every(([t,r])=>v(e.facets[t]??[],r))}(e,r.facets)||r.relationshipTargetId&&!(e.relationships??[]).some(e=>e.target_id===r.relationshipTargetId))).map(({item:e,ordinal:t,matches:r})=>({item:e,ordinal:t,rank:x(r,n),matches:r}))}function C(e,t,r){const i=function(e,t){return[...e].sort((e,r)=>t&&r.rank-e.rank||e.ordinal-r.ordinal)}(e,r.rank),s=r.offset??0,n=r.limit??i.length,a=i.slice(s,s+n),o={items:a.map(e=>e.item),total:i.length,facets:g(i.map(e=>e.item))};if(r.rank||r.snippets||r.includeMatches){const e=r.snippetLength??160;o.rankedItems=a.map(i=>({item:i.item,rank:i.rank,...r.snippets?{snippet:_(i.item,i.matches,t,e)}:{},...r.includeMatches?{matches:i.matches}:{}}))}return o}function A(e,t="",r={}){const i=t.trim().toLowerCase();return C(b(e.items,i,r),i,r)}function k(e){return o(e)?e:32}async function j(e,t){const r=function(e){return`${e.offset}:${e.href}`}(t),i=e.partCache.get(r);if(i)return e.partCache.delete(r),e.partCache.set(r,i),{part:i,fetched:!1};const s=h(await e.loader(t,e.manifest),t,e.manifest);for(e.partCache.set(r,s);e.partCache.size>e.maxCachedParts;){const t=e.partCache.keys().next().value;if(void 0===t)break;e.partCache.delete(t)}return{part:s,fetched:!0}}function S(e,t,r=(new Date).toISOString()){return{schema_version:"aiwg.fortemi.review-decisions.v1",generated_at:r,source_export_schema_version:e.schema_version,decisions:[...t].sort((e,t)=>e.item_id.localeCompare(t.item_id))}}function q(e){let t=e??null,r=null,i=null,s=null,n=[];const a=new Set,c=()=>({index:t,chunked:r?{manifest:r.manifest,cachedParts:r.partCache.size,maxCachedParts:r.maxCachedParts}:null,data:i,error:s,reviewDecisions:[...n]}),f=()=>{const e=c();for(const t of a)t(e)},p=()=>{if(!t)throw new Error("No AIWG index export loaded");return t};return{loadIndex(e){try{const a=u(e);return t=a,r=null,i=null,n=[],s=null,f(),a}catch(e){throw s=e instanceof Error?e:new Error(String(e)),f(),s}},loadChunkedIndex(e,a,c={}){try{const p=d(e);return t=null,r={manifest:p,loader:a,maxCachedParts:(u=c.maxCachedParts,o(u)?u:3),partCache:new Map,detailLoader:c.detailLoader,maxCachedDetails:k(c.maxCachedDetails),detailCache:new Map},i=null,n=[],s=null,f(),p}catch(e){throw s=e instanceof Error?e:new Error(String(e)),f(),s}var u},getIndex:()=>t,getChunkedManifest:()=>r?.manifest??null,getSnapshot:()=>c(),query(e="",t){const r=A(p(),e,t);return i=r,s=null,f(),r},async queryChunked(e="",t){if(!r)throw new Error("No AIWG chunked index manifest loaded");try{const n=await async function(e,t="",r={}){const i=t.trim().toLowerCase();let s=0,n=0;if(function(e,t){return!(""!==e.trim()||t.rank||t.snippets||t.includeMatches||t.types||t.facets||t.tags||t.concepts||t.privacy||t.relationshipTargetId)}(t,r)){const t=r.offset??0,i=r.limit??e.manifest.total,a=function(e,t,r){const i=t+r;return e.parts.filter(e=>e.count>0&&e.offset<i&&e.offset+e.count>t)}(e.manifest,t,i),o=[];for(const c of a){const u=await j(e,c);u.fetched&&(n+=1),s+=1,r.onProgress?.({phase:"part",done:s,total:a.length,href:c.href});const f=Math.max(0,t-c.offset),d=Math.min(u.part.items.length,t+i-c.offset);o.push(...u.part.items.slice(f,d))}return{items:o,total:e.manifest.total,facets:e.manifest.facets??{},manifestTotal:e.manifest.total,scannedParts:s,fetchedParts:n,complete:!0}}const a=[];for(const t of e.manifest.parts){const o=await j(e,t);o.fetched&&(n+=1),s+=1,r.onProgress?.({phase:"part",done:s,total:e.manifest.parts.length,href:t.href}),a.push(...b(o.part.items,i,r,t.offset)),r.onProgress?.({phase:"query",done:s,total:e.manifest.parts.length,href:t.href})}return{...C(a,i,r),manifestTotal:e.manifest.total,scannedParts:s,fetchedParts:n,complete:!0}}(r,e,t);return i=n,s=null,f(),n}catch(e){throw s=e instanceof Error?e:new Error(String(e)),f(),s}},async getRecord(e){if(r)try{return await async function(e,t){const r=e.detailCache.get(t);if(r)return e.detailCache.delete(t),e.detailCache.set(t,r),r;if(!e.manifest.projection)for(const r of e.partCache.values()){const e=r.items.find(e=>e.id===t);if(e)return e}if(!e.detailLoader)throw new Error("No detailLoader configured to resolve record "+t);const i=await e.detailLoader(t,e.manifest),s=u({schema_version:"aiwg.fortemi.index.export.v1",generated_at:e.manifest.generated_at,source:e.manifest.source,items:[i]}).items[0];if(s.id!==t)throw new Error("Detail record id mismatch: expected "+t+", got "+s.id);for(e.detailCache.set(t,s);e.detailCache.size>e.maxCachedDetails;){const t=e.detailCache.keys().next().value;if(void 0===t)break;e.detailCache.delete(t)}return s}(r,e)}catch(e){throw s=e instanceof Error?e:new Error(String(e)),f(),s}const t=p().items.find(t=>t.id===e);if(!t)throw new Error("Record not found: "+e);return t},clearChunkCache(){r?.partCache.clear(),r?.detailCache.clear(),s=null,f()},toCommunityGraph:e=>E(p(),e),setReviewDecision(e){const t={...e,updated_at:(new Date).toISOString()};return n=[...n.filter(e=>e.item_id!==t.item_id),t].sort((e,t)=>e.item_id.localeCompare(t.item_id)),s=null,f(),t},clearReviewDecision(e){n=n.filter(t=>t.item_id!==e),s=null,f()},createReviewDecisionExport:e=>S(p(),n,e),subscribe:e=>(a.add(e),()=>{a.delete(e)})}}function E(e,t={}){const r=new Set(e.items.map(e=>e.id)),i=t.relationshipWeights??{},s=new Map;for(const n of e.items)for(const e of n.relationships){if(!r.has(e.target_id)&&!t.includeDanglingRelationships)continue;const a=e.type,o=i[a]??1,c=`${n.id}\0${e.target_id}\0${a}`,u=s.get(c);u?u.weight+=o:s.set(c,{source:n.id,target:e.target_id,kind:a,weight:o})}const n=new Map;for(const r of e.items){const e=I(r,t);for(const t of e){const e=n.get(t)??[];e.push(r.id),n.set(t,e)}}return{nodes:e.items.map(e=>({id:e.id})),edges:Array.from(s.values()).sort((e,t)=>e.source.localeCompare(t.source)||e.target.localeCompare(t.target)||e.kind.localeCompare(t.kind)),communities:Array.from(n.entries()).map(([e,t])=>({id:e,nodes:[...new Set(t)].sort()})).sort((e,t)=>e.id.localeCompare(t.id))}}function I(e,t){if(t.communityFacet){const r=e.facets[t.communityFacet]??[];if(r.length>0)return r.map(e=>`${t.communityFacet}:${e}`)}if(t.communityTagPrefix){const r=t.communityTagPrefix,i=e.tags.filter(e=>e.startsWith(r));if(i.length>0)return i}return e.concepts.length>0?e.concepts.map(e=>`concept:${e}`):[`type:${e.type}`]}export{e as AIWG_SCAN_REQUIRED_FIELDS,E as aiwgFortemiIndexToCommunityGraph,d as assertAiwgFortemiChunkManifest,h as assertAiwgFortemiChunkPart,u as assertAiwgFortemiIndexExport,y as buildAiwgChunkedIndex,m as createAiwgFetchChunkLoader,l as createAiwgFetchDetailLoader,q as createAiwgIndexController,S as createAiwgReviewDecisionExport,g as getAiwgFortemiFacets,A as queryAiwgFortemiIndex,f as validateAiwgFortemiChunkManifest,p as validateAiwgFortemiChunkPart,c as validateAiwgFortemiIndexExport};
1
+ var e=["schema_version","id","type","title","text","facets","tags","concepts","privacy"],t=["schema_version","id","type","source","title","text","facets","tags","concepts","relationships","provenance","privacy","updated_at"],r=new Set(["crm.contact","crm.organization","crm.event","crm.interaction","aiwg.artifact","docs.page"]),i={title:4,tag:3,concept:2,text:1};function n(e){return"string"==typeof e&&e.length>0}function a(e,t,r){e[t]??={},e[t][r]=(e[t][r]??0)+1}function s(e){return Number.isInteger(e)&&"number"==typeof e&&e>=0}function o(e){return Number.isInteger(e)&&"number"==typeof e&&e>0}function c(e){const i=[],a={},s=e;"aiwg.fortemi.index.export.v1"!==s?.schema_version&&i.push("schema_version must be aiwg.fortemi.index.export.v1"),n(s?.generated_at)||i.push("generated_at is required"),n(s?.source?.repo)||i.push("source.repo is required"),n(s?.source?.privacy)||i.push("source.privacy is required"),Array.isArray(s?.items)||i.push("items must be an array");const o=new Set;let c="";for(const[e,u]of(s.items??[]).entries()){for(const r of t)r in u||i.push("items["+e+"]."+r+" is required");"aiwg.fortemi.index.record.v1"!==u.schema_version&&i.push("items["+e+"].schema_version must be aiwg.fortemi.index.record.v1"),n(u.id)||i.push("items["+e+"].id is required"),n(u.id)&&o.has(u.id)&&i.push("duplicate id: "+u.id),n(u.id)&&o.add(u.id),c&&n(u.id)&&c.localeCompare(u.id)>0&&i.push("items must be sorted by id: "+c+" before "+u.id),n(u.id)&&(c=u.id),r.has(u.type)?a[u.type]=(a[u.type]??0)+1:i.push("items["+e+"].type is invalid"),n(u.source?.path)||i.push("items["+e+"].source.path is required"),n(u.source?.repo_relative_path)||i.push("items["+e+"].source.repo_relative_path is required"),n(u.source?.locator)||i.push("items["+e+"].source.locator is required"),Array.isArray(u.tags)||i.push("items["+e+"].tags must be an array"),Array.isArray(u.concepts)||i.push("items["+e+"].concepts must be an array"),Array.isArray(u.relationships)||i.push("items["+e+"].relationships must be an array"),Array.isArray(u.provenance)&&0!==u.provenance.length||i.push("items["+e+"].provenance must be a non-empty array"),u.privacy&&"boolean"==typeof u.privacy.pii&&n(u.privacy.classification)||i.push("items["+e+"].privacy requires classification and pii")}return{valid:0===i.length,errors:i,counts:a}}function u(e){const t=c(e);if(!t.valid)throw new Error("Invalid AIWG Fortemi index export:\n"+t.errors.join("\n"));return e}function f(t){const r=[],i=t;if("aiwg.fortemi.index.chunk-manifest.v1"!==i?.schema_version&&r.push("schema_version must be aiwg.fortemi.index.chunk-manifest.v1"),n(i?.generated_at)||r.push("generated_at is required"),n(i?.source?.repo)||r.push("source.repo is required"),n(i?.source?.privacy)||r.push("source.privacy is required"),s(i?.total)||r.push("total must be a non-negative integer"),o(i?.part_size)||r.push("part_size must be a positive integer"),void 0===i.facets||function(e){return!(!e||"object"!=typeof e||Array.isArray(e))&&Object.values(e).every(e=>!!e&&"object"==typeof e&&!Array.isArray(e)&&Object.values(e).every(e=>s(e)))}(i.facets)||r.push("facets must be a nested string-to-number count object"),void 0!==i.projection)if(Array.isArray(i.projection)&&i.projection.every(e=>"string"==typeof e)){const t=new Set(i.projection);for(const i of e)t.has(i)||r.push("projection must include scan-required field "+i)}else r.push("projection must be an array of field names");void 0!==i.detail&&(n(i.detail.href)?i.detail.href.includes("{id}")||r.push("detail.href must contain the {id} placeholder"):r.push("detail.href is required"),void 0!==i.detail.encoding&&"uri"!==i.detail.encoding&&"base64url"!==i.detail.encoding&&r.push("detail.encoding must be 'uri' or 'base64url'")),Array.isArray(i?.parts)||r.push("parts must be an array");let a=0;const c=Array.isArray(i?.parts)?i.parts:[];for(const[e,t]of c.entries())n(t.href)||r.push("parts["+e+"].href is required"),s(t.offset)||r.push("parts["+e+"].offset must be a non-negative integer"),s(t.count)||r.push("parts["+e+"].count must be a non-negative integer"),s(t.offset)&&t.offset!==a&&r.push("parts["+e+"].offset must be "+a),s(t.count)&&(a+=t.count);return s(i?.total)&&a!==i.total&&r.push("parts counts must add up to total"),{valid:0===r.length,errors:r}}function d(e){const t=f(e);if(!t.valid)throw new Error("Invalid AIWG Fortemi chunk manifest:\n"+t.errors.join("\n"));return e}function h(e,t,i){const a=[],o=e;if("aiwg.fortemi.index.chunk.v1"!==o?.schema_version&&a.push("schema_version must be aiwg.fortemi.index.chunk.v1"),"aiwg.fortemi.index.chunk-manifest.v1"!==o?.manifest_schema_version&&a.push("manifest_schema_version must be aiwg.fortemi.index.chunk-manifest.v1"),s(o?.offset)||a.push("offset must be a non-negative integer"),Array.isArray(o?.items)||a.push("items must be an array"),t&&s(o?.offset)&&o.offset!==t.offset&&a.push("offset must match manifest part offset "+t.offset),t&&Array.isArray(o?.items)&&o.items.length!==t.count&&a.push("items length must match manifest part count "+t.count),Array.isArray(o?.items))if(i?.projection)a.push(...function(e){const t=[],i=new Set;let a="";for(const[s,o]of e.entries())"aiwg.fortemi.index.record.v1"!==o.schema_version&&t.push("items["+s+"].schema_version must be aiwg.fortemi.index.record.v1"),n(o.id)||t.push("items["+s+"].id is required"),n(o.id)&&i.has(o.id)&&t.push("duplicate id: "+o.id),n(o.id)&&i.add(o.id),a&&n(o.id)&&a.localeCompare(o.id)>0&&t.push("items must be sorted by id: "+a+" before "+o.id),n(o.id)&&(a=o.id),o.type&&r.has(o.type)||t.push("items["+s+"].type is invalid"),n(o.title)||t.push("items["+s+"].title is required"),"string"!=typeof o.text&&t.push("items["+s+"].text is required"),o.facets&&"object"==typeof o.facets&&!Array.isArray(o.facets)||t.push("items["+s+"].facets must be an object"),Array.isArray(o.tags)||t.push("items["+s+"].tags must be an array"),Array.isArray(o.concepts)||t.push("items["+s+"].concepts must be an array"),o.privacy&&n(o.privacy.classification)||t.push("items["+s+"].privacy.classification is required");return t}(o.items).map(e=>"items."+e));else{const e=c({schema_version:"aiwg.fortemi.index.export.v1",generated_at:i?.generated_at??"1970-01-01T00:00:00.000Z",source:i?.source??{repo:"chunk",privacy:"public"},items:o.items});a.push(...e.errors.map(e=>"items."+e))}return{valid:0===a.length,errors:a}}function l(e,t,r){const i=h(e,t,r);if(!i.valid)throw new Error("Invalid AIWG Fortemi chunk part:\n"+i.errors.join("\n"));return e}function p(e){return async t=>{const r=e?new URL(t.href,e).toString():t.href,i=await fetch(r);if(!i.ok)throw new Error("Failed to fetch AIWG index chunk "+r+": "+i.status);return i.json()}}function m(e,t="base64url"){if("uri"===t)return encodeURIComponent(e);const r=(new TextEncoder).encode(e);let i="";for(const e of r)i+=String.fromCharCode(e);return btoa(i).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function g(e,t){return e.href.replace("{id}",m(t,e.encoding??"uri"))}function y(e){return async(t,r)=>{if(!r.detail?.href)throw new Error("Manifest has no detail.href for record resolution");const i=g(r.detail,t),n=e?new URL(i,e).toString():i,a=await fetch(n);if(!a.ok)throw new Error("Failed to fetch AIWG index detail "+n+": "+a.status);return a.json()}}function v(e){const t={};for(const r of e){a(t,"type",r.type),a(t,"privacy",r.privacy.classification);for(const e of r.tags)a(t,"tag",e);for(const e of r.concepts)a(t,"concept",e);for(const[e,i]of Object.entries(r.facets))for(const r of i)a(t,e,r)}return t}function w(e,t={}){const r=o(t.partSize)?t.partSize:500,i=t.projection,n=t.idEncoding??"base64url",a=t.detailHref??"detail/{id}.json",s=e.items,c=e=>String(e).padStart(4,"0"),u=e=>{if(!i)return e;const t={};for(const r of i)t[r]=e[r];return t},f=[],d=[];for(let e=0,t=0;e<s.length;e+=r,t+=1){const i=s.slice(e,e+r),n="part-"+c(t)+".json";f.push({href:n,part:{schema_version:"aiwg.fortemi.index.chunk.v1",manifest_schema_version:"aiwg.fortemi.index.chunk-manifest.v1",offset:e,items:i.map(u)}}),d.push({href:n,offset:e,count:i.length})}return{manifest:{schema_version:"aiwg.fortemi.index.chunk-manifest.v1",generated_at:t.generatedAt??e.generated_at,source:e.source,total:s.length,part_size:r,facets:v(s),parts:d,...i?{projection:i,detail:{href:a,encoding:n}}:{}},parts:f,details:i?s.map(e=>({id:e.id,href:g({href:a,encoding:n},e.id),record:e})):[]}}function x(e,t){if(!t||0===t.length)return!0;const r=new Set(e);return t.every(e=>r.has(e))}function C(e,t){if(!t)return[];const r=[];e.title.toLowerCase().includes(t)&&r.push({field:"title",value:e.title}),e.text.toLowerCase().includes(t)&&r.push({field:"text",value:e.text});for(const i of e.tags)i.toLowerCase().includes(t)&&r.push({field:"tag",value:i});for(const i of e.concepts)i.toLowerCase().includes(t)&&r.push({field:"concept",value:i});return r}function _(e,t){return e.reduce((e,r)=>e+t[r.field],0)}function b(e,t,r,i){const n=t.find(e=>"text"===e.field),a=t.find(e=>"title"===e.field),s=n??a??t[0];return function(e,t,r){const i=Math.max(20,r);if(!e)return"";if(!t)return e.length>i?`${e.slice(0,i).trimEnd()}...`:e;const n=e.toLowerCase().indexOf(t);if(n<0)return e.length>i?`${e.slice(0,i).trimEnd()}...`:e;const a=Math.max(0,Math.floor((i-t.length)/2)),s=Math.max(0,n-a),o=Math.min(e.length,s+i),c=s>0?"...":"",u=o<e.length?"...":"";return`${c}${e.slice(s,o).trim()}${u}`}(s?.value??e.text,r,i)}function A(e,t,r,n=0){const a={...i,...r.weights};return e.map((e,r)=>({item:e,ordinal:n+r,matches:C(e,t)})).filter(({item:e,matches:i})=>!(t&&0===i.length||r.types&&!r.types.includes(e.type)||r.privacy&&!r.privacy.includes(e.privacy.classification)||!x(e.tags,r.tags)||!x(e.concepts,r.concepts)||!function(e,t){return!t||Object.entries(t).every(([t,r])=>x(e.facets[t]??[],r))}(e,r.facets)||r.relationshipTargetId&&!(e.relationships??[]).some(e=>e.target_id===r.relationshipTargetId))).map(({item:e,ordinal:t,matches:r})=>({item:e,ordinal:t,rank:_(r,a),matches:r}))}function k(e,t,r){const i=function(e,t){return[...e].sort((e,r)=>t&&r.rank-e.rank||e.ordinal-r.ordinal)}(e,r.rank),n=r.offset??0,a=r.limit??i.length,s=i.slice(n,n+a),o={items:s.map(e=>e.item),total:i.length,facets:v(i.map(e=>e.item))};if(r.rank||r.snippets||r.includeMatches){const e=r.snippetLength??160;o.rankedItems=s.map(i=>({item:i.item,rank:i.rank,...r.snippets?{snippet:b(i.item,i.matches,t,e)}:{},...r.includeMatches?{matches:i.matches}:{}}))}return o}function j(e,t="",r={}){const i=t.trim().toLowerCase();return k(A(e.items,i,r),i,r)}function E(e){return o(e)?e:32}function S(e){return o(e)?e:5e3}async function I(e,t){const r=function(e){return`${e.offset}:${e.href}`}(t),i=e.partCache.get(r);if(i)return e.partCache.delete(r),e.partCache.set(r,i),{part:i,fetched:!1};const n=l(await e.loader(t,e.manifest),t,e.manifest);for(e.partCache.set(r,n);e.partCache.size>e.maxCachedParts;){const t=e.partCache.keys().next().value;if(void 0===t)break;e.partCache.delete(t)}return{part:n,fetched:!0}}function q(e,t,r=(new Date).toISOString()){return{schema_version:"aiwg.fortemi.review-decisions.v1",generated_at:r,source_export_schema_version:e.schema_version,decisions:[...t].sort((e,t)=>e.item_id.localeCompare(t.item_id))}}function M(e){let t=e??null,r=null,n=null,a=null,s=[];const c=new Set,f=()=>({index:t,chunked:r?{manifest:r.manifest,cachedParts:r.partCache.size,maxCachedParts:r.maxCachedParts}:null,data:n,error:a,reviewDecisions:[...s]}),h=()=>{const e=f();for(const t of c)t(e)},l=()=>{if(!t)throw new Error("No AIWG index export loaded");return t};return{loadIndex(e){try{const i=u(e);return t=i,r=null,n=null,s=[],a=null,h(),i}catch(e){throw a=e instanceof Error?e:new Error(String(e)),h(),a}},loadChunkedIndex(e,i,c={}){try{const f=d(e);return t=null,r={manifest:f,loader:i,maxCachedParts:(u=c.maxCachedParts,o(u)?u:3),partCache:new Map,detailLoader:c.detailLoader,maxCachedDetails:E(c.maxCachedDetails),detailCache:new Map,maxCachedMatches:S(c.maxCachedMatches),matchCache:new Map},n=null,s=[],a=null,h(),f}catch(e){throw a=e instanceof Error?e:new Error(String(e)),h(),a}var u},getIndex:()=>t,getChunkedManifest:()=>r?.manifest??null,getSnapshot:()=>f(),query(e="",t){const r=j(l(),e,t);return n=r,a=null,h(),r},async queryChunked(e="",t){if(!r)throw new Error("No AIWG chunked index manifest loaded");try{const s=await async function(e,t="",r={}){const n=t.trim().toLowerCase();let a=0,s=0;if(function(e,t){return!(""!==e.trim()||t.rank||t.snippets||t.includeMatches||t.types||t.facets||t.tags||t.concepts||t.privacy||t.relationshipTargetId)}(t,r)){const t=r.offset??0,i=r.limit??e.manifest.total,n=function(e,t,r){const i=t+r;return e.parts.filter(e=>e.count>0&&e.offset<i&&e.offset+e.count>t)}(e.manifest,t,i),o=[];for(const c of n){const u=await I(e,c);u.fetched&&(s+=1),a+=1,r.onProgress?.({phase:"part",done:a,total:n.length,href:c.href});const f=Math.max(0,t-c.offset),d=Math.min(u.part.items.length,t+i-c.offset);o.push(...u.part.items.slice(f,d))}return{items:o,total:e.manifest.total,facets:e.manifest.facets??{},manifestTotal:e.manifest.total,scannedParts:a,fetchedParts:s,complete:!0}}const o=function(e,t){return JSON.stringify({q:e,types:t.types??null,facets:t.facets??null,tags:t.tags??null,concepts:t.concepts??null,privacy:t.privacy??null,rel:t.relationshipTargetId??null,weights:{...i,...t.weights}})}(n,r),c=e.matchCache.get(o);if(c)return e.matchCache.delete(o),e.matchCache.set(o,c),{...k(c,n,r),manifestTotal:e.manifest.total,scannedParts:0,fetchedParts:0,complete:!0};const u=[];for(const t of e.manifest.parts){const i=await I(e,t);i.fetched&&(s+=1),a+=1,r.onProgress?.({phase:"part",done:a,total:e.manifest.parts.length,href:t.href}),u.push(...A(i.part.items,n,r,t.offset)),r.onProgress?.({phase:"query",done:a,total:e.manifest.parts.length,href:t.href})}return function(e,t,r){e.matchCache.delete(t),e.matchCache.set(t,r);let i=0;for(const t of e.matchCache.values())i+=t.length;for(;i>e.maxCachedMatches&&e.matchCache.size>1;){const r=e.matchCache.keys().next().value;if(void 0===r||r===t)break;i-=e.matchCache.get(r)?.length??0,e.matchCache.delete(r)}}(e,o,u),{...k(u,n,r),manifestTotal:e.manifest.total,scannedParts:a,fetchedParts:s,complete:!0}}(r,e,t);return n=s,a=null,h(),s}catch(e){throw a=e instanceof Error?e:new Error(String(e)),h(),a}},async getRecord(e){if(r)try{return await async function(e,t){const r=e.detailCache.get(t);if(r)return e.detailCache.delete(t),e.detailCache.set(t,r),r;if(!e.manifest.projection)for(const r of e.partCache.values()){const e=r.items.find(e=>e.id===t);if(e)return e}if(!e.detailLoader)throw new Error("No detailLoader configured to resolve record "+t);const i=await e.detailLoader(t,e.manifest),n=u({schema_version:"aiwg.fortemi.index.export.v1",generated_at:e.manifest.generated_at,source:e.manifest.source,items:[i]}).items[0];if(n.id!==t)throw new Error("Detail record id mismatch: expected "+t+", got "+n.id);for(e.detailCache.set(t,n);e.detailCache.size>e.maxCachedDetails;){const t=e.detailCache.keys().next().value;if(void 0===t)break;e.detailCache.delete(t)}return n}(r,e)}catch(e){throw a=e instanceof Error?e:new Error(String(e)),h(),a}const t=l().items.find(t=>t.id===e);if(!t)throw new Error("Record not found: "+e);return t},clearChunkCache(){r?.partCache.clear(),r?.detailCache.clear(),r?.matchCache.clear(),a=null,h()},toCommunityGraph:e=>P(l(),e),setReviewDecision(e){const t={...e,updated_at:(new Date).toISOString()};return s=[...s.filter(e=>e.item_id!==t.item_id),t].sort((e,t)=>e.item_id.localeCompare(t.item_id)),a=null,h(),t},clearReviewDecision(e){s=s.filter(t=>t.item_id!==e),a=null,h()},createReviewDecisionExport(e){const i=t??(r?{schema_version:"aiwg.fortemi.index.export.v1"}:null);if(!i)throw new Error("No AIWG index export or chunked manifest loaded");return q(i,s,e)},subscribe:e=>(c.add(e),()=>{c.delete(e)})}}function P(e,t={}){const r=new Set(e.items.map(e=>e.id)),i=t.relationshipWeights??{},n=new Map;for(const a of e.items)for(const e of a.relationships){if(!r.has(e.target_id)&&!t.includeDanglingRelationships)continue;const s=e.type,o=i[s]??1,c=`${a.id}\0${e.target_id}\0${s}`,u=n.get(c);u?u.weight+=o:n.set(c,{source:a.id,target:e.target_id,kind:s,weight:o})}const a=new Map;for(const r of e.items){const e=L(r,t);for(const t of e){const e=a.get(t)??[];e.push(r.id),a.set(t,e)}}return{nodes:e.items.map(e=>({id:e.id})),edges:Array.from(n.values()).sort((e,t)=>e.source.localeCompare(t.source)||e.target.localeCompare(t.target)||e.kind.localeCompare(t.kind)),communities:Array.from(a.entries()).map(([e,t])=>({id:e,nodes:[...new Set(t)].sort()})).sort((e,t)=>e.id.localeCompare(t.id))}}function L(e,t){if(t.communityFacet){const r=e.facets[t.communityFacet]??[];if(r.length>0)return r.map(e=>`${t.communityFacet}:${e}`)}if(t.communityTagPrefix){const r=t.communityTagPrefix,i=e.tags.filter(e=>e.startsWith(r));if(i.length>0)return i}return e.concepts.length>0?e.concepts.map(e=>`concept:${e}`):[`type:${e.type}`]}export{e as AIWG_SCAN_REQUIRED_FIELDS,g as aiwgDetailHrefForId,P as aiwgFortemiIndexToCommunityGraph,d as assertAiwgFortemiChunkManifest,l as assertAiwgFortemiChunkPart,u as assertAiwgFortemiIndexExport,w as buildAiwgChunkedIndex,p as createAiwgFetchChunkLoader,y as createAiwgFetchDetailLoader,M as createAiwgIndexController,q as createAiwgReviewDecisionExport,m as encodeAiwgDetailId,v as getAiwgFortemiFacets,j as queryAiwgFortemiIndex,f as validateAiwgFortemiChunkManifest,h as validateAiwgFortemiChunkPart,c as validateAiwgFortemiIndexExport};
@@ -53,8 +53,10 @@ interface AiwgFortemiChunkPartRef {
53
53
  }
54
54
  declare const AIWG_SCAN_REQUIRED_FIELDS: Array<keyof AiwgFortemiRecord>;
55
55
  type AiwgFortemiProjectedRecord = Pick<AiwgFortemiRecord, 'schema_version' | 'id' | 'type' | 'title' | 'text' | 'facets' | 'tags' | 'concepts' | 'privacy'> & Partial<AiwgFortemiRecord>;
56
+ type AiwgDetailIdEncoding = 'uri' | 'base64url';
56
57
  interface AiwgFortemiChunkDetailRef {
57
58
  href: string;
59
+ encoding?: AiwgDetailIdEncoding;
58
60
  }
59
61
  interface AiwgFortemiChunkManifest {
60
62
  schema_version: 'aiwg.fortemi.index.chunk-manifest.v1';
@@ -125,6 +127,7 @@ interface AiwgChunkedIndexLoadOptions {
125
127
  maxCachedParts?: number;
126
128
  detailLoader?: AiwgChunkedIndexDetailLoader;
127
129
  maxCachedDetails?: number;
130
+ maxCachedMatches?: number;
128
131
  }
129
132
  type AiwgChunkedIndexProgressPhase = 'part' | 'query';
130
133
  interface AiwgChunkedIndexProgress {
@@ -200,12 +203,15 @@ declare function assertAiwgFortemiChunkManifest(value: unknown): AiwgFortemiChun
200
203
  declare function validateAiwgFortemiChunkPart(value: unknown, partRef?: AiwgFortemiChunkPartRef, manifest?: AiwgFortemiChunkManifest): AiwgChunkedIndexValidationResult;
201
204
  declare function assertAiwgFortemiChunkPart(value: unknown, partRef?: AiwgFortemiChunkPartRef, manifest?: AiwgFortemiChunkManifest): AiwgFortemiChunkPart;
202
205
  declare function createAiwgFetchChunkLoader(baseUrl?: string | URL): AiwgChunkedIndexLoader;
206
+ declare function encodeAiwgDetailId(id: string, encoding?: AiwgDetailIdEncoding): string;
207
+ declare function aiwgDetailHrefForId(detail: AiwgFortemiChunkDetailRef, id: string): string;
203
208
  declare function createAiwgFetchDetailLoader(baseUrl?: string | URL): AiwgChunkedIndexDetailLoader;
204
209
  declare function getAiwgFortemiFacets(items: AiwgFortemiRecord[]): Record<string, Record<string, number>>;
205
210
  interface AiwgChunkedIndexBuildOptions {
206
211
  partSize?: number;
207
212
  projection?: Array<keyof AiwgFortemiRecord>;
208
213
  detailHref?: string;
214
+ idEncoding?: AiwgDetailIdEncoding;
209
215
  generatedAt?: string;
210
216
  }
211
217
  interface AiwgChunkedIndexBuildResult {
@@ -216,12 +222,13 @@ interface AiwgChunkedIndexBuildResult {
216
222
  }>;
217
223
  details: Array<{
218
224
  id: string;
225
+ href: string;
219
226
  record: AiwgFortemiRecord;
220
227
  }>;
221
228
  }
222
229
  declare function buildAiwgChunkedIndex(index: AiwgFortemiIndexExport, options?: AiwgChunkedIndexBuildOptions): AiwgChunkedIndexBuildResult;
223
230
  declare function queryAiwgFortemiIndex(index: AiwgFortemiIndexExport, query?: string, options?: AiwgIndexQueryOptions): AiwgIndexQueryResult;
224
- declare function createAiwgReviewDecisionExport(source: AiwgFortemiIndexExport, decisions: AiwgReviewDecision[], generatedAt?: string): AiwgReviewDecisionExport;
231
+ declare function createAiwgReviewDecisionExport(source: Pick<AiwgFortemiIndexExport, 'schema_version'>, decisions: AiwgReviewDecision[], generatedAt?: string): AiwgReviewDecisionExport;
225
232
  declare function createAiwgIndexController(initialIndex?: AiwgFortemiIndexExport): AiwgIndexController;
226
233
  declare function aiwgFortemiIndexToCommunityGraph(index: AiwgFortemiIndexExport, options?: AiwgIndexGraphOptions): {
227
234
  nodes: {
@@ -239,4 +246,4 @@ declare function aiwgFortemiIndexToCommunityGraph(index: AiwgFortemiIndexExport,
239
246
  }[];
240
247
  };
241
248
 
242
- export { AIWG_SCAN_REQUIRED_FIELDS, type AiwgChunkedIndexBuildOptions, type AiwgChunkedIndexBuildResult, type AiwgChunkedIndexDetailLoader, type AiwgChunkedIndexLoadOptions, type AiwgChunkedIndexLoader, type AiwgChunkedIndexProgress, type AiwgChunkedIndexProgressPhase, type AiwgChunkedIndexQueryOptions, type AiwgChunkedIndexQueryResult, type AiwgChunkedIndexValidationResult, type AiwgFortemiChunkDetailRef, type AiwgFortemiChunkManifest, type AiwgFortemiChunkPart, type AiwgFortemiChunkPartRef, type AiwgFortemiIndexExport, type AiwgFortemiProjectedRecord, type AiwgFortemiProvenance, type AiwgFortemiRecord, type AiwgFortemiRecordSource, type AiwgFortemiRecordType, type AiwgFortemiRelationship, type AiwgIndexController, type AiwgIndexControllerListener, type AiwgIndexControllerSnapshot, type AiwgIndexGraphOptions, type AiwgIndexQueryMatch, type AiwgIndexQueryOptions, type AiwgIndexQueryRankedItem, type AiwgIndexQueryResult, type AiwgIndexQueryWeights, type AiwgIndexValidationResult, type AiwgPrivacyClassification, type AiwgProvenanceConfidence, type AiwgReviewAction, type AiwgReviewDecision, type AiwgReviewDecisionExport, type AiwgReviewInput, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, buildAiwgChunkedIndex, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, getAiwgFortemiFacets, queryAiwgFortemiIndex, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport };
249
+ export { AIWG_SCAN_REQUIRED_FIELDS, type AiwgChunkedIndexBuildOptions, type AiwgChunkedIndexBuildResult, type AiwgChunkedIndexDetailLoader, type AiwgChunkedIndexLoadOptions, type AiwgChunkedIndexLoader, type AiwgChunkedIndexProgress, type AiwgChunkedIndexProgressPhase, type AiwgChunkedIndexQueryOptions, type AiwgChunkedIndexQueryResult, type AiwgChunkedIndexValidationResult, type AiwgDetailIdEncoding, type AiwgFortemiChunkDetailRef, type AiwgFortemiChunkManifest, type AiwgFortemiChunkPart, type AiwgFortemiChunkPartRef, type AiwgFortemiIndexExport, type AiwgFortemiProjectedRecord, type AiwgFortemiProvenance, type AiwgFortemiRecord, type AiwgFortemiRecordSource, type AiwgFortemiRecordType, type AiwgFortemiRelationship, type AiwgIndexController, type AiwgIndexControllerListener, type AiwgIndexControllerSnapshot, type AiwgIndexGraphOptions, type AiwgIndexQueryMatch, type AiwgIndexQueryOptions, type AiwgIndexQueryRankedItem, type AiwgIndexQueryResult, type AiwgIndexQueryWeights, type AiwgIndexValidationResult, type AiwgPrivacyClassification, type AiwgProvenanceConfidence, type AiwgReviewAction, type AiwgReviewDecision, type AiwgReviewDecisionExport, type AiwgReviewInput, aiwgDetailHrefForId, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, buildAiwgChunkedIndex, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, encodeAiwgDetailId, getAiwgFortemiFacets, queryAiwgFortemiIndex, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport };
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * VENDORED — @fortemi/core/aiwg-index
3
3
  *
4
- * Source : @fortemi/core@2026.6.3 → dist/aiwg-index.js
5
- * SHA-256: f2e6793fd7e52e20441459e18633a9546c7cfc6281cbc63ab28f0767422946bf (upstream dist file)
4
+ * Source : @fortemi/core@2026.6.6 → dist/aiwg-index.js
5
+ * SHA-256: 70cb729f18d7fb901606737b5ede8dcb2eac7704176aae60d7d6fc10f9508c42 (upstream dist file)
6
6
  * License: AGPL-3.0-only (compatible with this package's AGPL-3.0-or-later)
7
7
  * Why : Pagenary's publisher build is a no-bundler copy-src→dist pipeline that
8
8
  * loads ES modules by relative path; bare specifiers (`@fortemi/core`)
@@ -11,7 +11,8 @@
11
11
  * browser (runtime search). See .aiwg/architecture/adr/ADR-015-*.md.
12
12
  * Update : Re-vendor by copying the dist file from a newer @fortemi/core release
13
13
  * and refreshing the SHA-256 above. Do not hand-edit below this banner.
14
- * 6.3 adds buildAiwgChunkedIndex / createAiwgFetchDetailLoader (additive).
14
+ * 6.5 adds encodeAiwgDetailId / aiwgDetailHrefForId + a transparent
15
+ * query match-cache (additive); 6.4 and 6.6 are doc-only.
15
16
  */
16
17
  // src/aiwg-index.ts
17
18
  var AIWG_SCAN_REQUIRED_FIELDS = [
@@ -149,6 +150,9 @@ function validateAiwgFortemiChunkManifest(value) {
149
150
  if (data.detail !== void 0) {
150
151
  if (!hasString(data.detail.href)) errors.push("detail.href is required");
151
152
  else if (!data.detail.href.includes("{id}")) errors.push("detail.href must contain the {id} placeholder");
153
+ if (data.detail.encoding !== void 0 && data.detail.encoding !== "uri" && data.detail.encoding !== "base64url") {
154
+ errors.push("detail.encoding must be 'uri' or 'base64url'");
155
+ }
152
156
  }
153
157
  if (!Array.isArray(data?.parts)) errors.push("parts must be an array");
154
158
  let expectedOffset = 0;
@@ -250,10 +254,20 @@ function createAiwgFetchChunkLoader(baseUrl) {
250
254
  return response.json();
251
255
  };
252
256
  }
257
+ function encodeAiwgDetailId(id, encoding = "base64url") {
258
+ if (encoding === "uri") return encodeURIComponent(id);
259
+ const bytes = new TextEncoder().encode(id);
260
+ let binary = "";
261
+ for (const byte of bytes) binary += String.fromCharCode(byte);
262
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
263
+ }
264
+ function aiwgDetailHrefForId(detail, id) {
265
+ return detail.href.replace("{id}", encodeAiwgDetailId(id, detail.encoding ?? "uri"));
266
+ }
253
267
  function createAiwgFetchDetailLoader(baseUrl) {
254
268
  return async (id, manifest) => {
255
269
  if (!manifest.detail?.href) throw new Error("Manifest has no detail.href for record resolution");
256
- const relative = manifest.detail.href.replace("{id}", encodeURIComponent(id));
270
+ const relative = aiwgDetailHrefForId(manifest.detail, id);
257
271
  const href = baseUrl ? new URL(relative, baseUrl).toString() : relative;
258
272
  const response = await fetch(href);
259
273
  if (!response.ok) throw new Error("Failed to fetch AIWG index detail " + href + ": " + response.status);
@@ -276,6 +290,8 @@ function getAiwgFortemiFacets(items) {
276
290
  function buildAiwgChunkedIndex(index, options = {}) {
277
291
  const partSize = hasPositiveInteger(options.partSize) ? options.partSize : 500;
278
292
  const projection = options.projection;
293
+ const idEncoding = options.idEncoding ?? "base64url";
294
+ const detailHref = options.detailHref ?? "detail/{id}.json";
279
295
  const items = index.items;
280
296
  const pad = (value) => String(value).padStart(4, "0");
281
297
  const project = (record) => {
@@ -308,12 +324,16 @@ function buildAiwgChunkedIndex(index, options = {}) {
308
324
  part_size: partSize,
309
325
  facets: getAiwgFortemiFacets(items),
310
326
  parts: partRefs,
311
- ...projection ? { projection, detail: { href: options.detailHref ?? "detail/{id}.json" } } : {}
327
+ ...projection ? { projection, detail: { href: detailHref, encoding: idEncoding } } : {}
312
328
  };
313
329
  return {
314
330
  manifest,
315
331
  parts,
316
- details: projection ? items.map((record) => ({ id: record.id, record })) : []
332
+ details: projection ? items.map((record) => ({
333
+ id: record.id,
334
+ href: aiwgDetailHrefForId({ href: detailHref, encoding: idEncoding }, record.id),
335
+ record
336
+ })) : []
317
337
  };
318
338
  }
319
339
  function includesAll(actual, expected) {
@@ -423,6 +443,34 @@ function clampMaxCachedDetails(value) {
423
443
  if (!hasPositiveInteger(value)) return 32;
424
444
  return value;
425
445
  }
446
+ function clampMaxCachedMatches(value) {
447
+ if (!hasPositiveInteger(value)) return 5e3;
448
+ return value;
449
+ }
450
+ function matchSetCacheKey(q, options) {
451
+ return JSON.stringify({
452
+ q,
453
+ types: options.types ?? null,
454
+ facets: options.facets ?? null,
455
+ tags: options.tags ?? null,
456
+ concepts: options.concepts ?? null,
457
+ privacy: options.privacy ?? null,
458
+ rel: options.relationshipTargetId ?? null,
459
+ weights: { ...DEFAULT_QUERY_WEIGHTS, ...options.weights }
460
+ });
461
+ }
462
+ function cacheMatchEntries(runtime, key, entries) {
463
+ runtime.matchCache.delete(key);
464
+ runtime.matchCache.set(key, entries);
465
+ let total = 0;
466
+ for (const set of runtime.matchCache.values()) total += set.length;
467
+ while (total > runtime.maxCachedMatches && runtime.matchCache.size > 1) {
468
+ const oldest = runtime.matchCache.keys().next().value;
469
+ if (oldest === void 0 || oldest === key) break;
470
+ total -= runtime.matchCache.get(oldest)?.length ?? 0;
471
+ runtime.matchCache.delete(oldest);
472
+ }
473
+ }
426
474
  function isDirectChunkBrowse(query, options) {
427
475
  return query.trim() === "" && !options.rank && !options.snippets && !options.includeMatches && !options.types && !options.facets && !options.tags && !options.concepts && !options.privacy && !options.relationshipTargetId;
428
476
  }
@@ -509,6 +557,19 @@ async function queryChunkedAiwgFortemiIndex(runtime, query = "", options = {}) {
509
557
  complete: true
510
558
  };
511
559
  }
560
+ const matchKey = matchSetCacheKey(q, options);
561
+ const cached = runtime.matchCache.get(matchKey);
562
+ if (cached) {
563
+ runtime.matchCache.delete(matchKey);
564
+ runtime.matchCache.set(matchKey, cached);
565
+ return {
566
+ ...createQueryResultFromRankedEntries(cached, q, options),
567
+ manifestTotal: runtime.manifest.total,
568
+ scannedParts: 0,
569
+ fetchedParts: 0,
570
+ complete: true
571
+ };
572
+ }
512
573
  const entries = [];
513
574
  for (const partRef of runtime.manifest.parts) {
514
575
  const loaded = await loadChunkPart(runtime, partRef);
@@ -518,6 +579,7 @@ async function queryChunkedAiwgFortemiIndex(runtime, query = "", options = {}) {
518
579
  entries.push(...createRankedEntries(loaded.part.items, q, options, partRef.offset));
519
580
  options.onProgress?.({ phase: "query", done: scannedParts, total: runtime.manifest.parts.length, href: partRef.href });
520
581
  }
582
+ cacheMatchEntries(runtime, matchKey, entries);
521
583
  return {
522
584
  ...createQueryResultFromRankedEntries(entries, q, options),
523
585
  manifestTotal: runtime.manifest.total,
@@ -588,7 +650,9 @@ function createAiwgIndexController(initialIndex) {
588
650
  partCache: /* @__PURE__ */ new Map(),
589
651
  detailLoader: options.detailLoader,
590
652
  maxCachedDetails: clampMaxCachedDetails(options.maxCachedDetails),
591
- detailCache: /* @__PURE__ */ new Map()
653
+ detailCache: /* @__PURE__ */ new Map(),
654
+ maxCachedMatches: clampMaxCachedMatches(options.maxCachedMatches),
655
+ matchCache: /* @__PURE__ */ new Map()
592
656
  };
593
657
  data = null;
594
658
  reviewDecisions = [];
@@ -648,6 +712,7 @@ function createAiwgIndexController(initialIndex) {
648
712
  clearChunkCache() {
649
713
  chunked?.partCache.clear();
650
714
  chunked?.detailCache.clear();
715
+ chunked?.matchCache.clear();
651
716
  error = null;
652
717
  notify();
653
718
  },
@@ -673,7 +738,9 @@ function createAiwgIndexController(initialIndex) {
673
738
  notify();
674
739
  },
675
740
  createReviewDecisionExport(generatedAt) {
676
- return createAiwgReviewDecisionExport(requireIndex(), reviewDecisions, generatedAt);
741
+ const source = index ?? (chunked ? { schema_version: "aiwg.fortemi.index.export.v1" } : null);
742
+ if (!source) throw new Error("No AIWG index export or chunked manifest loaded");
743
+ return createAiwgReviewDecisionExport(source, reviewDecisions, generatedAt);
677
744
  },
678
745
  subscribe(listener) {
679
746
  listeners.add(listener);
@@ -727,6 +794,6 @@ function communityIdsFor(item, options) {
727
794
  return [`type:${item.type}`];
728
795
  }
729
796
 
730
- export { AIWG_SCAN_REQUIRED_FIELDS, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, buildAiwgChunkedIndex, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, getAiwgFortemiFacets, queryAiwgFortemiIndex, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport };
797
+ export { AIWG_SCAN_REQUIRED_FIELDS, aiwgDetailHrefForId, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, buildAiwgChunkedIndex, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, encodeAiwgDetailId, getAiwgFortemiFacets, queryAiwgFortemiIndex, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport };
731
798
  //# sourceMappingURL=aiwg-index.js.map
732
799
  //# sourceMappingURL=aiwg-index.js.map