@fortemi/core 2026.7.2 → 2026.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aiwg-index.d.ts +21 -7
- package/dist/aiwg-index.js +127 -10
- package/dist/aiwg-index.js.map +1 -1
- package/dist/index.d.ts +50 -32
- package/dist/index.js +163 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/aiwg-index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type AiwgFortemiKnownRecordType = 'crm.contact' | 'crm.organization' | 'crm.event' | 'crm.interaction' | 'aiwg.artifact' | '
|
|
1
|
+
type AiwgFortemiKnownRecordType = 'crm.contact' | 'crm.organization' | 'crm.event' | 'crm.interaction' | 'aiwg.artifact' | 'aiwg.kb.page';
|
|
2
2
|
type AiwgFortemiRecordType = AiwgFortemiKnownRecordType | `aiwg.${string}` | `research.${string}` | `docs.${string}` | string;
|
|
3
3
|
type AiwgFortemiRecordSchemaVersion = 'aiwg.fortemi.index.record.v1' | 'aiwg.fortemi.index.record.v2';
|
|
4
4
|
type AiwgFortemiIndexExportSchemaVersion = 'aiwg.fortemi.index.export.v1' | 'aiwg.fortemi.index.export.v2';
|
|
@@ -141,10 +141,7 @@ interface AiwgFortemiIndexExport {
|
|
|
141
141
|
source: {
|
|
142
142
|
repo: string;
|
|
143
143
|
privacy: AiwgPrivacyClassification;
|
|
144
|
-
|
|
145
|
-
generated?: boolean;
|
|
146
|
-
checksum?: string;
|
|
147
|
-
updated_at?: string;
|
|
144
|
+
graph?: Record<string, unknown>;
|
|
148
145
|
};
|
|
149
146
|
items: AiwgFortemiRecord[];
|
|
150
147
|
compatibility?: Record<string, unknown>;
|
|
@@ -165,6 +162,7 @@ interface AiwgFortemiChunkManifest {
|
|
|
165
162
|
schema_version: 'aiwg.fortemi.index.chunk-manifest.v1';
|
|
166
163
|
generated_at: string;
|
|
167
164
|
source: AiwgFortemiIndexExport['source'];
|
|
165
|
+
source_export_schema_version?: AiwgFortemiIndexExportSchemaVersion;
|
|
168
166
|
total: number;
|
|
169
167
|
part_size: number;
|
|
170
168
|
facets?: Record<string, Record<string, number>>;
|
|
@@ -338,6 +336,14 @@ interface AiwgHeadlessEmbeddingBackend {
|
|
|
338
336
|
dimensions: number;
|
|
339
337
|
embed(input: string, record: AiwgFortemiRecord): number[] | Promise<number[]>;
|
|
340
338
|
}
|
|
339
|
+
interface AiwgPrivacyFilterOptions {
|
|
340
|
+
/** Include records classified `private` (default false). */
|
|
341
|
+
includePrivate?: boolean;
|
|
342
|
+
/** Include records flagged `pii` (default false). */
|
|
343
|
+
includePii?: boolean;
|
|
344
|
+
}
|
|
345
|
+
/** Drop `private`/`pii` records unless explicitly opted in (SEC6, default-safe). */
|
|
346
|
+
declare function filterAiwgRecordsByPrivacy(records: AiwgFortemiRecord[], options?: AiwgPrivacyFilterOptions): AiwgFortemiRecord[];
|
|
341
347
|
interface BuildAiwgStaticEmbeddingSetOptions {
|
|
342
348
|
id: string;
|
|
343
349
|
backend: AiwgHeadlessEmbeddingBackend;
|
|
@@ -346,6 +352,8 @@ interface BuildAiwgStaticEmbeddingSetOptions {
|
|
|
346
352
|
granularity?: AiwgStaticEmbeddingSet['granularity'];
|
|
347
353
|
metric?: AiwgStaticEmbeddingSet['metric'];
|
|
348
354
|
textForRecord?: (record: AiwgFortemiRecord) => string;
|
|
355
|
+
/** Privacy filtering (SEC6). Default-safe: excludes `private`/`pii` records. */
|
|
356
|
+
privacy?: AiwgPrivacyFilterOptions;
|
|
349
357
|
}
|
|
350
358
|
interface AiwgStaticSemanticQueryOptions {
|
|
351
359
|
limit?: number;
|
|
@@ -411,6 +419,7 @@ declare function validateAiwgFortemiChunkManifest(value: unknown): AiwgChunkedIn
|
|
|
411
419
|
declare function assertAiwgFortemiChunkManifest(value: unknown): AiwgFortemiChunkManifest;
|
|
412
420
|
declare function validateAiwgFortemiChunkPart(value: unknown, partRef?: AiwgFortemiChunkPartRef, manifest?: AiwgFortemiChunkManifest): AiwgChunkedIndexValidationResult;
|
|
413
421
|
declare function assertAiwgFortemiChunkPart(value: unknown, partRef?: AiwgFortemiChunkPartRef, manifest?: AiwgFortemiChunkManifest): AiwgFortemiChunkPart;
|
|
422
|
+
declare function resolveAiwgFetchUrl(href: string, baseUrl?: string | URL): string;
|
|
414
423
|
declare function createAiwgFetchChunkLoader(baseUrl?: string | URL): AiwgChunkedIndexLoader;
|
|
415
424
|
declare function encodeAiwgDetailId(id: string, encoding?: AiwgDetailIdEncoding): string;
|
|
416
425
|
declare function aiwgDetailHrefForId(detail: AiwgFortemiChunkDetailRef, id: string): string;
|
|
@@ -423,6 +432,8 @@ interface AiwgChunkedIndexBuildOptions {
|
|
|
423
432
|
detailHref?: string;
|
|
424
433
|
idEncoding?: AiwgDetailIdEncoding;
|
|
425
434
|
generatedAt?: string;
|
|
435
|
+
/** Privacy filtering (SEC6). Default-safe: excludes `private`/`pii` records. */
|
|
436
|
+
privacy?: AiwgPrivacyFilterOptions;
|
|
426
437
|
}
|
|
427
438
|
interface AiwgChunkedIndexBuildResult {
|
|
428
439
|
manifest: AiwgFortemiChunkManifest;
|
|
@@ -442,7 +453,10 @@ declare function validateAiwgStaticEmbeddingSet(value: unknown): AiwgChunkedInde
|
|
|
442
453
|
declare function assertAiwgStaticEmbeddingSet(value: unknown): AiwgStaticEmbeddingSet;
|
|
443
454
|
declare function queryAiwgSemanticIndex(index: AiwgFortemiIndexExport, embeddingSet: AiwgStaticEmbeddingSet, queryEmbedding: number[], options?: AiwgStaticSemanticQueryOptions): AiwgStaticSemanticResult[];
|
|
444
455
|
declare function queryAiwgHybridIndex(index: AiwgFortemiIndexExport, embeddingSet: AiwgStaticEmbeddingSet, query: string, queryEmbedding: number[], options?: AiwgStaticHybridQueryOptions): AiwgStaticSemanticResult[];
|
|
445
|
-
declare
|
|
456
|
+
declare const DEFAULT_AIWG_DUPLICATE_SCAN_MAX_EMBEDDINGS = 5000;
|
|
457
|
+
declare function findAiwgStaticDuplicatePairs(index: AiwgFortemiIndexExport, embeddingSet: AiwgStaticEmbeddingSet, threshold?: number, options?: {
|
|
458
|
+
maxEmbeddings?: number;
|
|
459
|
+
}): AiwgStaticDuplicatePair[];
|
|
446
460
|
declare function createAiwgReviewDecisionExport(source: Pick<AiwgFortemiIndexExport, 'schema_version'>, decisions: AiwgReviewDecision[], generatedAt?: string): AiwgReviewDecisionExport;
|
|
447
461
|
declare function createAiwgIndexController(initialIndex?: AiwgFortemiIndexExport): AiwgIndexController;
|
|
448
462
|
declare function aiwgFortemiIndexToCommunityGraph(index: AiwgFortemiIndexExport, options?: AiwgIndexGraphOptions): {
|
|
@@ -461,4 +475,4 @@ declare function aiwgFortemiIndexToCommunityGraph(index: AiwgFortemiIndexExport,
|
|
|
461
475
|
}[];
|
|
462
476
|
};
|
|
463
477
|
|
|
464
|
-
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 AiwgFortemiAttachmentReference, type AiwgFortemiBinarySource, type AiwgFortemiChunk, type AiwgFortemiChunkDetailRef, type AiwgFortemiChunkManifest, type AiwgFortemiChunkPart, type AiwgFortemiChunkPartRef, type AiwgFortemiIndexExport, type AiwgFortemiIndexExportSchemaVersion, type AiwgFortemiKnownRecordType, type AiwgFortemiProjectedRecord, type AiwgFortemiProvenance, type AiwgFortemiProvenanceEvent, type AiwgFortemiRecord, type AiwgFortemiRecordEmbedding, type AiwgFortemiRecordSchemaVersion, type AiwgFortemiRecordSource, type AiwgFortemiRecordType, type AiwgFortemiRelationship, type AiwgFortemiRelationshipDirection, type AiwgFortemiSearchProjection, type AiwgFortemiSkosConcept, type AiwgFortemiSkosRelation, type AiwgFortemiSkosRelationType, type AiwgHeadlessEmbeddingBackend, 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 AiwgRelationshipDirection, type AiwgRelationshipEdgeSummary, type AiwgRelationshipNodeSummary, type AiwgRelationshipQueryOptions, type AiwgRelationshipSetOperation, type AiwgRelationshipSetOptions, type AiwgRelationshipSetResult, type AiwgRelationshipTraversalOptions, type AiwgRelationshipTraversalResult, type AiwgReviewAction, type AiwgReviewDecision, type AiwgReviewDecisionExport, type AiwgReviewInput, type AiwgStaticDuplicatePair, type AiwgStaticEmbeddingRecord, type AiwgStaticEmbeddingSet, type AiwgStaticHybridQueryOptions, type AiwgStaticSemanticQueryOptions, type AiwgStaticSemanticResult, type BuildAiwgStaticEmbeddingSetOptions, aiwgDetailHrefForId, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, buildAiwgChunkedIndex, buildAiwgStaticEmbeddingSet, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, encodeAiwgDetailId, findAiwgStaticDuplicatePairs, getAiwgFortemiFacets, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgStaticEmbeddingSet };
|
|
478
|
+
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 AiwgFortemiAttachmentReference, type AiwgFortemiBinarySource, type AiwgFortemiChunk, type AiwgFortemiChunkDetailRef, type AiwgFortemiChunkManifest, type AiwgFortemiChunkPart, type AiwgFortemiChunkPartRef, type AiwgFortemiIndexExport, type AiwgFortemiIndexExportSchemaVersion, type AiwgFortemiKnownRecordType, type AiwgFortemiProjectedRecord, type AiwgFortemiProvenance, type AiwgFortemiProvenanceEvent, type AiwgFortemiRecord, type AiwgFortemiRecordEmbedding, type AiwgFortemiRecordSchemaVersion, type AiwgFortemiRecordSource, type AiwgFortemiRecordType, type AiwgFortemiRelationship, type AiwgFortemiRelationshipDirection, type AiwgFortemiSearchProjection, type AiwgFortemiSkosConcept, type AiwgFortemiSkosRelation, type AiwgFortemiSkosRelationType, type AiwgHeadlessEmbeddingBackend, type AiwgIndexController, type AiwgIndexControllerListener, type AiwgIndexControllerSnapshot, type AiwgIndexGraphOptions, type AiwgIndexQueryMatch, type AiwgIndexQueryOptions, type AiwgIndexQueryRankedItem, type AiwgIndexQueryResult, type AiwgIndexQueryWeights, type AiwgIndexValidationResult, type AiwgPrivacyClassification, type AiwgPrivacyFilterOptions, type AiwgProvenanceConfidence, type AiwgRelationshipDirection, type AiwgRelationshipEdgeSummary, type AiwgRelationshipNodeSummary, type AiwgRelationshipQueryOptions, type AiwgRelationshipSetOperation, type AiwgRelationshipSetOptions, type AiwgRelationshipSetResult, type AiwgRelationshipTraversalOptions, type AiwgRelationshipTraversalResult, type AiwgReviewAction, type AiwgReviewDecision, type AiwgReviewDecisionExport, type AiwgReviewInput, type AiwgStaticDuplicatePair, type AiwgStaticEmbeddingRecord, type AiwgStaticEmbeddingSet, type AiwgStaticHybridQueryOptions, type AiwgStaticSemanticQueryOptions, type AiwgStaticSemanticResult, type BuildAiwgStaticEmbeddingSetOptions, DEFAULT_AIWG_DUPLICATE_SCAN_MAX_EMBEDDINGS, aiwgDetailHrefForId, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, buildAiwgChunkedIndex, buildAiwgStaticEmbeddingSet, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, encodeAiwgDetailId, filterAiwgRecordsByPrivacy, findAiwgStaticDuplicatePairs, getAiwgFortemiFacets, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, resolveAiwgFetchUrl, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgStaticEmbeddingSet };
|
package/dist/aiwg-index.js
CHANGED
|
@@ -19,6 +19,16 @@ var AIWG_SCAN_REQUIRED_FIELDS = [
|
|
|
19
19
|
"concepts",
|
|
20
20
|
"privacy"
|
|
21
21
|
];
|
|
22
|
+
function isPrivacyExcluded(record, options) {
|
|
23
|
+
const privacy = record.privacy;
|
|
24
|
+
if (!privacy) return false;
|
|
25
|
+
if (privacy.classification === "private" && !options?.includePrivate) return true;
|
|
26
|
+
if (privacy.pii && !options?.includePii) return true;
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
function filterAiwgRecordsByPrivacy(records, options) {
|
|
30
|
+
return records.filter((record) => !isPrivacyExcluded(record, options));
|
|
31
|
+
}
|
|
22
32
|
var REQUIRED_RECORD_FIELDS = [
|
|
23
33
|
"schema_version",
|
|
24
34
|
"id",
|
|
@@ -45,8 +55,12 @@ function hasString(value) {
|
|
|
45
55
|
return typeof value === "string" && value.length > 0;
|
|
46
56
|
}
|
|
47
57
|
function pushFacet(counts, name, value) {
|
|
48
|
-
counts[name]
|
|
49
|
-
|
|
58
|
+
let bucket = counts[name];
|
|
59
|
+
if (bucket === void 0) {
|
|
60
|
+
bucket = /* @__PURE__ */ Object.create(null);
|
|
61
|
+
counts[name] = bucket;
|
|
62
|
+
}
|
|
63
|
+
bucket[value] = (bucket[value] ?? 0) + 1;
|
|
50
64
|
}
|
|
51
65
|
function hasNonNegativeInteger(value) {
|
|
52
66
|
return Number.isInteger(value) && typeof value === "number" && value >= 0;
|
|
@@ -168,6 +182,57 @@ function validateOptionalRichMetadata(item, index, errors) {
|
|
|
168
182
|
errors.push("items[" + index + "].compatibility must be an object");
|
|
169
183
|
}
|
|
170
184
|
}
|
|
185
|
+
function isPrivacyClassification(value) {
|
|
186
|
+
return value === "private" || value === "sanitized" || value === "public";
|
|
187
|
+
}
|
|
188
|
+
function isProvenanceConfidence(value) {
|
|
189
|
+
return value === "source" || value === "candidate" || value === "reviewed" || value === "rejected";
|
|
190
|
+
}
|
|
191
|
+
function validateProvenanceItems(item, index, errors) {
|
|
192
|
+
if (!Array.isArray(item.provenance)) return;
|
|
193
|
+
for (const [provIndex, prov] of item.provenance.entries()) {
|
|
194
|
+
const at = "items[" + index + "].provenance[" + provIndex + "]";
|
|
195
|
+
if (!isPlainRecord(prov)) {
|
|
196
|
+
errors.push(at + " must be an object");
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (!hasString(prov.field)) errors.push(at + ".field is required");
|
|
200
|
+
if (!hasString(prov.source)) errors.push(at + ".source is required");
|
|
201
|
+
if (!hasString(prov.path)) errors.push(at + ".path is required");
|
|
202
|
+
if (!isProvenanceConfidence(prov.confidence)) errors.push(at + ".confidence must be one of source, candidate, reviewed, rejected");
|
|
203
|
+
if (!isPrivacyClassification(prov.privacy)) errors.push(at + ".privacy must be one of private, sanitized, public");
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
var V2_ONLY_RECORD_FIELDS = ["search", "chunks", "embeddings", "skos_concepts", "skos_relations", "compatibility"];
|
|
207
|
+
var V2_ONLY_SOURCE_FIELDS = ["origin", "generated", "checksum", "updated_at"];
|
|
208
|
+
var V2_ONLY_RELATIONSHIP_FIELDS = ["target_path", "direction", "metadata"];
|
|
209
|
+
function forbidV2FieldsOnV1Record(item, index, errors) {
|
|
210
|
+
if (item.schema_version !== "aiwg.fortemi.index.record.v1") return;
|
|
211
|
+
const at = "items[" + index + "]";
|
|
212
|
+
const bag = item;
|
|
213
|
+
const v2msg = " is a v2-only field and must be absent on a record.v1 record";
|
|
214
|
+
for (const field of V2_ONLY_RECORD_FIELDS) {
|
|
215
|
+
if (bag[field] !== void 0) errors.push(at + "." + field + v2msg);
|
|
216
|
+
}
|
|
217
|
+
if (isPlainRecord(item.source)) {
|
|
218
|
+
const src = item.source;
|
|
219
|
+
for (const field of V2_ONLY_SOURCE_FIELDS) {
|
|
220
|
+
if (src[field] !== void 0) errors.push(at + ".source." + field + v2msg);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (isPlainRecord(item.privacy) && item.privacy.locality !== void 0) {
|
|
224
|
+
errors.push(at + ".privacy.locality" + v2msg);
|
|
225
|
+
}
|
|
226
|
+
if (Array.isArray(item.relationships)) {
|
|
227
|
+
for (const [relIndex, rel] of item.relationships.entries()) {
|
|
228
|
+
if (!isPlainRecord(rel)) continue;
|
|
229
|
+
const relBag = rel;
|
|
230
|
+
for (const field of V2_ONLY_RELATIONSHIP_FIELDS) {
|
|
231
|
+
if (relBag[field] !== void 0) errors.push(at + ".relationships[" + relIndex + "]." + field + v2msg);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
171
236
|
function validateAiwgFortemiIndexExport(value) {
|
|
172
237
|
const errors = [];
|
|
173
238
|
const counts = {};
|
|
@@ -178,10 +243,21 @@ function validateAiwgFortemiIndexExport(value) {
|
|
|
178
243
|
if (!hasString(data?.generated_at)) errors.push("generated_at is required");
|
|
179
244
|
if (!hasString(data?.source?.repo)) errors.push("source.repo is required");
|
|
180
245
|
if (!hasString(data?.source?.privacy)) errors.push("source.privacy is required");
|
|
246
|
+
else if (!isPrivacyClassification(data?.source?.privacy)) {
|
|
247
|
+
errors.push("source.privacy must be one of private, sanitized, public");
|
|
248
|
+
}
|
|
181
249
|
if (!Array.isArray(data?.items)) errors.push("items must be an array");
|
|
182
250
|
if (data.compatibility !== void 0 && !isPlainRecord(data.compatibility)) {
|
|
183
251
|
errors.push("compatibility must be an object");
|
|
184
252
|
}
|
|
253
|
+
if (data?.schema_version === "aiwg.fortemi.index.export.v1") {
|
|
254
|
+
if (isPlainRecord(data.source) && data.source.graph !== void 0) {
|
|
255
|
+
errors.push("source.graph is a v2-only field and must be absent on an export.v1 export");
|
|
256
|
+
}
|
|
257
|
+
if (data.compatibility !== void 0) {
|
|
258
|
+
errors.push("compatibility is a v2-only field and must be absent on an export.v1 export");
|
|
259
|
+
}
|
|
260
|
+
}
|
|
185
261
|
const ids = /* @__PURE__ */ new Set();
|
|
186
262
|
let previousId = "";
|
|
187
263
|
for (const [index, item] of (data.items ?? []).entries()) {
|
|
@@ -216,8 +292,12 @@ function validateAiwgFortemiIndexExport(value) {
|
|
|
216
292
|
errors.push("items[" + index + "].provenance must be a non-empty array");
|
|
217
293
|
}
|
|
218
294
|
validateOptionalRichMetadata(item, index, errors);
|
|
295
|
+
validateProvenanceItems(item, index, errors);
|
|
296
|
+
forbidV2FieldsOnV1Record(item, index, errors);
|
|
219
297
|
if (!item.privacy || typeof item.privacy.pii !== "boolean" || !hasString(item.privacy.classification)) {
|
|
220
298
|
errors.push("items[" + index + "].privacy requires classification and pii");
|
|
299
|
+
} else if (!isPrivacyClassification(item.privacy.classification)) {
|
|
300
|
+
errors.push("items[" + index + "].privacy.classification must be one of private, sanitized, public");
|
|
221
301
|
}
|
|
222
302
|
}
|
|
223
303
|
return { valid: errors.length === 0, errors, counts };
|
|
@@ -238,6 +318,9 @@ function validateAiwgFortemiChunkManifest(value) {
|
|
|
238
318
|
if (!hasString(data?.generated_at)) errors.push("generated_at is required");
|
|
239
319
|
if (!hasString(data?.source?.repo)) errors.push("source.repo is required");
|
|
240
320
|
if (!hasString(data?.source?.privacy)) errors.push("source.privacy is required");
|
|
321
|
+
if (data?.source_export_schema_version !== void 0 && !isSupportedIndexSchemaVersion(data.source_export_schema_version)) {
|
|
322
|
+
errors.push("source_export_schema_version must be aiwg.fortemi.index.export.v1 or aiwg.fortemi.index.export.v2 when present");
|
|
323
|
+
}
|
|
241
324
|
if (!hasNonNegativeInteger(data?.total)) errors.push("total must be a non-negative integer");
|
|
242
325
|
if (!hasPositiveInteger(data?.part_size)) errors.push("part_size must be a positive integer");
|
|
243
326
|
if (data.facets !== void 0 && !isFacetCounts(data.facets)) {
|
|
@@ -356,9 +439,35 @@ function assertAiwgFortemiChunkPart(value, partRef, manifest) {
|
|
|
356
439
|
}
|
|
357
440
|
return value;
|
|
358
441
|
}
|
|
442
|
+
var ALLOWED_AIWG_FETCH_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:", "blob:", "data:"]);
|
|
443
|
+
function tryParseUrl(value) {
|
|
444
|
+
try {
|
|
445
|
+
return new URL(value);
|
|
446
|
+
} catch {
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function resolveAiwgFetchUrl(href, baseUrl) {
|
|
451
|
+
if (baseUrl === void 0) {
|
|
452
|
+
const absolute = tryParseUrl(href);
|
|
453
|
+
if (absolute && !ALLOWED_AIWG_FETCH_SCHEMES.has(absolute.protocol)) {
|
|
454
|
+
throw new Error("Refusing AIWG index fetch with disallowed scheme: " + absolute.protocol);
|
|
455
|
+
}
|
|
456
|
+
return href;
|
|
457
|
+
}
|
|
458
|
+
const base = new URL(baseUrl);
|
|
459
|
+
const resolved = new URL(href, base);
|
|
460
|
+
if (!ALLOWED_AIWG_FETCH_SCHEMES.has(resolved.protocol)) {
|
|
461
|
+
throw new Error("Refusing AIWG index fetch with disallowed scheme: " + resolved.protocol);
|
|
462
|
+
}
|
|
463
|
+
if (resolved.origin !== base.origin) {
|
|
464
|
+
throw new Error("Refusing cross-origin AIWG index fetch: " + resolved.origin + " != " + base.origin);
|
|
465
|
+
}
|
|
466
|
+
return resolved.toString();
|
|
467
|
+
}
|
|
359
468
|
function createAiwgFetchChunkLoader(baseUrl) {
|
|
360
469
|
return async (part) => {
|
|
361
|
-
const href =
|
|
470
|
+
const href = resolveAiwgFetchUrl(part.href, baseUrl);
|
|
362
471
|
const response = await fetch(href);
|
|
363
472
|
if (!response.ok) throw new Error("Failed to fetch AIWG index chunk " + href + ": " + response.status);
|
|
364
473
|
return response.json();
|
|
@@ -378,14 +487,14 @@ function createAiwgFetchDetailLoader(baseUrl) {
|
|
|
378
487
|
return async (id, manifest) => {
|
|
379
488
|
if (!manifest.detail?.href) throw new Error("Manifest has no detail.href for record resolution");
|
|
380
489
|
const relative = aiwgDetailHrefForId(manifest.detail, id);
|
|
381
|
-
const href =
|
|
490
|
+
const href = resolveAiwgFetchUrl(relative, baseUrl);
|
|
382
491
|
const response = await fetch(href);
|
|
383
492
|
if (!response.ok) throw new Error("Failed to fetch AIWG index detail " + href + ": " + response.status);
|
|
384
493
|
return response.json();
|
|
385
494
|
};
|
|
386
495
|
}
|
|
387
496
|
function getAiwgFortemiFacets(items) {
|
|
388
|
-
const result =
|
|
497
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
389
498
|
for (const item of items) {
|
|
390
499
|
pushFacet(result, "type", item.type);
|
|
391
500
|
pushFacet(result, "privacy", item.privacy.classification);
|
|
@@ -421,7 +530,7 @@ function generatedAtString(value) {
|
|
|
421
530
|
async function buildAiwgStaticEmbeddingSet(index, options) {
|
|
422
531
|
assertAiwgFortemiIndexExport(index);
|
|
423
532
|
const granularity = options.granularity ?? "body";
|
|
424
|
-
const records = options.records ?? index.items;
|
|
533
|
+
const records = filterAiwgRecordsByPrivacy(options.records ?? index.items, options.privacy);
|
|
425
534
|
const embeddings = [];
|
|
426
535
|
for (const record of records) {
|
|
427
536
|
const input = options.textForRecord?.(record) ?? defaultEmbeddingInput(record, granularity);
|
|
@@ -482,7 +591,7 @@ function buildAiwgChunkedIndex(index, options = {}) {
|
|
|
482
591
|
const projection = options.projection;
|
|
483
592
|
const idEncoding = options.idEncoding ?? "base64url";
|
|
484
593
|
const detailHref = options.detailHref ?? "detail/{id}.json";
|
|
485
|
-
const items = index.items;
|
|
594
|
+
const items = filterAiwgRecordsByPrivacy(index.items, options.privacy);
|
|
486
595
|
const pad = (value) => String(value).padStart(4, "0");
|
|
487
596
|
const project = (record) => {
|
|
488
597
|
if (!projection) return record;
|
|
@@ -510,6 +619,7 @@ function buildAiwgChunkedIndex(index, options = {}) {
|
|
|
510
619
|
schema_version: "aiwg.fortemi.index.chunk-manifest.v1",
|
|
511
620
|
generated_at: options.generatedAt ?? index.generated_at,
|
|
512
621
|
source: index.source,
|
|
622
|
+
source_export_schema_version: index.schema_version,
|
|
513
623
|
total: items.length,
|
|
514
624
|
part_size: partSize,
|
|
515
625
|
facets: getAiwgFortemiFacets(items),
|
|
@@ -820,8 +930,15 @@ function queryAiwgHybridIndex(index, embeddingSet, query, queryEmbedding, option
|
|
|
820
930
|
};
|
|
821
931
|
}).filter((result) => result !== null && result.score >= (options.minScore ?? -1)).sort((left, right) => right.score - left.score || left.item.id.localeCompare(right.item.id)).slice(offset, offset + limit);
|
|
822
932
|
}
|
|
823
|
-
|
|
933
|
+
var DEFAULT_AIWG_DUPLICATE_SCAN_MAX_EMBEDDINGS = 5e3;
|
|
934
|
+
function findAiwgStaticDuplicatePairs(index, embeddingSet, threshold = 0.9, options) {
|
|
824
935
|
assertAiwgStaticEmbeddingSet(embeddingSet);
|
|
936
|
+
const maxEmbeddings = options?.maxEmbeddings ?? DEFAULT_AIWG_DUPLICATE_SCAN_MAX_EMBEDDINGS;
|
|
937
|
+
if (embeddingSet.embeddings.length > maxEmbeddings) {
|
|
938
|
+
throw new Error(
|
|
939
|
+
"Embedding set too large for duplicate scan: " + embeddingSet.embeddings.length + " > " + maxEmbeddings + " (raise options.maxEmbeddings to override for trusted input)"
|
|
940
|
+
);
|
|
941
|
+
}
|
|
825
942
|
const byId = new Map(index.items.map((item) => [item.id, item]));
|
|
826
943
|
const pairs = [];
|
|
827
944
|
for (let leftIndex = 0; leftIndex < embeddingSet.embeddings.length; leftIndex += 1) {
|
|
@@ -1286,7 +1403,7 @@ function createAiwgIndexController(initialIndex) {
|
|
|
1286
1403
|
notify();
|
|
1287
1404
|
},
|
|
1288
1405
|
createReviewDecisionExport(generatedAt) {
|
|
1289
|
-
const source = index ?? (chunked ? { schema_version: "aiwg.fortemi.index.export.v1" } : null);
|
|
1406
|
+
const source = index ?? (chunked ? { schema_version: chunked.manifest.source_export_schema_version ?? "aiwg.fortemi.index.export.v1" } : null);
|
|
1290
1407
|
if (!source) throw new Error("No AIWG index export or chunked manifest loaded");
|
|
1291
1408
|
return createAiwgReviewDecisionExport(source, reviewDecisions, generatedAt);
|
|
1292
1409
|
},
|
|
@@ -1342,6 +1459,6 @@ function communityIdsFor(item, options) {
|
|
|
1342
1459
|
return [`type:${item.type}`];
|
|
1343
1460
|
}
|
|
1344
1461
|
|
|
1345
|
-
export { AIWG_SCAN_REQUIRED_FIELDS, aiwgDetailHrefForId, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, buildAiwgChunkedIndex, buildAiwgStaticEmbeddingSet, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, encodeAiwgDetailId, findAiwgStaticDuplicatePairs, getAiwgFortemiFacets, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgStaticEmbeddingSet };
|
|
1462
|
+
export { AIWG_SCAN_REQUIRED_FIELDS, DEFAULT_AIWG_DUPLICATE_SCAN_MAX_EMBEDDINGS, aiwgDetailHrefForId, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, buildAiwgChunkedIndex, buildAiwgStaticEmbeddingSet, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, encodeAiwgDetailId, filterAiwgRecordsByPrivacy, findAiwgStaticDuplicatePairs, getAiwgFortemiFacets, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, resolveAiwgFetchUrl, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgStaticEmbeddingSet };
|
|
1346
1463
|
//# sourceMappingURL=aiwg-index.js.map
|
|
1347
1464
|
//# sourceMappingURL=aiwg-index.js.map
|