@fortemi/core 2026.5.4 → 2026.6.1
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 +4 -4
- package/dist/index.d.ts +249 -53
- package/dist/index.js +495 -130
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -150,6 +150,103 @@ async function createPGliteInstance(persistence, archiveName = "default") {
|
|
|
150
150
|
return db;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
// src/worker/worker-client.ts
|
|
154
|
+
var PGliteWorkerClient = class {
|
|
155
|
+
constructor(worker) {
|
|
156
|
+
this.worker = worker;
|
|
157
|
+
this.readyPromise = new Promise((resolve) => {
|
|
158
|
+
this.resolveReady = resolve;
|
|
159
|
+
});
|
|
160
|
+
this.worker.addEventListener("message", (e) => {
|
|
161
|
+
const msg = e.data;
|
|
162
|
+
if (msg.type === "READY") {
|
|
163
|
+
this.resolveReady();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (!("id" in msg)) return;
|
|
167
|
+
const pending = this.pending.get(msg.id);
|
|
168
|
+
if (!pending) return;
|
|
169
|
+
this.pending.delete(msg.id);
|
|
170
|
+
if (msg.type === "ERROR") {
|
|
171
|
+
pending.reject(new Error(msg.error));
|
|
172
|
+
} else {
|
|
173
|
+
pending.resolve(msg);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
pending = /* @__PURE__ */ new Map();
|
|
178
|
+
readyPromise;
|
|
179
|
+
resolveReady;
|
|
180
|
+
/** Resolves when the worker broadcasts READY after database initialisation. */
|
|
181
|
+
async waitReady() {
|
|
182
|
+
return this.readyPromise;
|
|
183
|
+
}
|
|
184
|
+
send(request) {
|
|
185
|
+
const id = generateId();
|
|
186
|
+
return new Promise((resolve, reject) => {
|
|
187
|
+
this.pending.set(id, {
|
|
188
|
+
resolve,
|
|
189
|
+
reject
|
|
190
|
+
});
|
|
191
|
+
this.worker.postMessage({ ...request, id });
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
async query(sql, params) {
|
|
195
|
+
const resp = await this.send({ type: "QUERY", sql, params });
|
|
196
|
+
return { rows: resp.rows, fields: resp.fields };
|
|
197
|
+
}
|
|
198
|
+
async exec(sql) {
|
|
199
|
+
await this.send({ type: "EXEC", sql });
|
|
200
|
+
}
|
|
201
|
+
async transaction(fn) {
|
|
202
|
+
const resp = await this.send({ type: "BEGIN" });
|
|
203
|
+
const txId = resp.txId;
|
|
204
|
+
const proxy = new TransactionProxy(this, txId);
|
|
205
|
+
try {
|
|
206
|
+
const result = await fn(proxy);
|
|
207
|
+
await this.send({ type: "COMMIT", txId });
|
|
208
|
+
return result;
|
|
209
|
+
} catch (err) {
|
|
210
|
+
await this.send({ type: "ROLLBACK", txId }).catch(() => {
|
|
211
|
+
});
|
|
212
|
+
throw err;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/** Forward TX_QUERY for TransactionProxy — not part of the public surface. */
|
|
216
|
+
async _txQuery(txId, sql, params) {
|
|
217
|
+
const resp = await this.send({
|
|
218
|
+
type: "TX_QUERY",
|
|
219
|
+
txId,
|
|
220
|
+
sql,
|
|
221
|
+
params
|
|
222
|
+
});
|
|
223
|
+
return { rows: resp.rows };
|
|
224
|
+
}
|
|
225
|
+
/** Forward TX_EXEC for TransactionProxy — not part of the public surface. */
|
|
226
|
+
async _txExec(txId, sql) {
|
|
227
|
+
await this.send({ type: "TX_EXEC", txId, sql });
|
|
228
|
+
}
|
|
229
|
+
async ping() {
|
|
230
|
+
await this.send({ type: "PING" });
|
|
231
|
+
}
|
|
232
|
+
async close() {
|
|
233
|
+
await this.send({ type: "CLOSE" });
|
|
234
|
+
this.worker.terminate();
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
var TransactionProxy = class {
|
|
238
|
+
constructor(client, txId) {
|
|
239
|
+
this.client = client;
|
|
240
|
+
this.txId = txId;
|
|
241
|
+
}
|
|
242
|
+
async query(sql, params) {
|
|
243
|
+
return this.client._txQuery(this.txId, sql, params);
|
|
244
|
+
}
|
|
245
|
+
async exec(sql) {
|
|
246
|
+
return this.client._txExec(this.txId, sql);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
153
250
|
// src/storage-backend.ts
|
|
154
251
|
var PGliteStorageBackend = class {
|
|
155
252
|
constructor(id, db) {
|
|
@@ -177,6 +274,41 @@ var PGliteStorageBackendFactory = class {
|
|
|
177
274
|
}
|
|
178
275
|
};
|
|
179
276
|
var defaultStorageBackendFactory = new PGliteStorageBackendFactory();
|
|
277
|
+
var PGliteWorkerStorageBackend = class {
|
|
278
|
+
constructor(id, client) {
|
|
279
|
+
this.id = id;
|
|
280
|
+
this.client = client;
|
|
281
|
+
}
|
|
282
|
+
mode = "readwrite";
|
|
283
|
+
query(sql, params) {
|
|
284
|
+
return this.client.query(sql, params);
|
|
285
|
+
}
|
|
286
|
+
exec(sql) {
|
|
287
|
+
return this.client.exec(sql);
|
|
288
|
+
}
|
|
289
|
+
transaction(fn) {
|
|
290
|
+
return this.client.transaction(fn);
|
|
291
|
+
}
|
|
292
|
+
close() {
|
|
293
|
+
return this.client.close();
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
var PGliteWorkerStorageBackendFactory = class {
|
|
297
|
+
constructor(options) {
|
|
298
|
+
this.options = options;
|
|
299
|
+
}
|
|
300
|
+
async open(input) {
|
|
301
|
+
const worker = this.options.createWorker();
|
|
302
|
+
const client = new PGliteWorkerClient(worker);
|
|
303
|
+
worker.postMessage({
|
|
304
|
+
type: "INIT",
|
|
305
|
+
persistence: input.persistence,
|
|
306
|
+
archiveName: input.archiveName
|
|
307
|
+
});
|
|
308
|
+
await client.waitReady();
|
|
309
|
+
return new PGliteWorkerStorageBackend(`pglite-worker:${input.persistence}:${input.archiveName}`, client);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
180
312
|
|
|
181
313
|
// src/capability-manager.ts
|
|
182
314
|
var VALID_TRANSITIONS = {
|
|
@@ -799,13 +931,13 @@ var allMigrations = [
|
|
|
799
931
|
|
|
800
932
|
// src/archive-manager.ts
|
|
801
933
|
var ArchiveManager = class {
|
|
802
|
-
constructor(persistenceOrFactory, events) {
|
|
934
|
+
constructor(persistenceOrFactory, events, persistenceOverride) {
|
|
803
935
|
this.events = events;
|
|
804
936
|
if (typeof persistenceOrFactory === "string") {
|
|
805
937
|
this.persistence = persistenceOrFactory;
|
|
806
938
|
this.backendFactory = defaultStorageBackendFactory;
|
|
807
939
|
} else {
|
|
808
|
-
this.persistence = "memory";
|
|
940
|
+
this.persistence = persistenceOverride ?? "memory";
|
|
809
941
|
this.backendFactory = persistenceOrFactory;
|
|
810
942
|
}
|
|
811
943
|
this.archives.set("default", {
|
|
@@ -1170,103 +1302,6 @@ function createBlobStore(archiveName) {
|
|
|
1170
1302
|
return new IdbBlobStore(archiveName);
|
|
1171
1303
|
}
|
|
1172
1304
|
|
|
1173
|
-
// src/worker/worker-client.ts
|
|
1174
|
-
var PGliteWorkerClient = class {
|
|
1175
|
-
constructor(worker) {
|
|
1176
|
-
this.worker = worker;
|
|
1177
|
-
this.readyPromise = new Promise((resolve) => {
|
|
1178
|
-
this.resolveReady = resolve;
|
|
1179
|
-
});
|
|
1180
|
-
this.worker.addEventListener("message", (e) => {
|
|
1181
|
-
const msg = e.data;
|
|
1182
|
-
if (msg.type === "READY") {
|
|
1183
|
-
this.resolveReady();
|
|
1184
|
-
return;
|
|
1185
|
-
}
|
|
1186
|
-
if (!("id" in msg)) return;
|
|
1187
|
-
const pending = this.pending.get(msg.id);
|
|
1188
|
-
if (!pending) return;
|
|
1189
|
-
this.pending.delete(msg.id);
|
|
1190
|
-
if (msg.type === "ERROR") {
|
|
1191
|
-
pending.reject(new Error(msg.error));
|
|
1192
|
-
} else {
|
|
1193
|
-
pending.resolve(msg);
|
|
1194
|
-
}
|
|
1195
|
-
});
|
|
1196
|
-
}
|
|
1197
|
-
pending = /* @__PURE__ */ new Map();
|
|
1198
|
-
readyPromise;
|
|
1199
|
-
resolveReady;
|
|
1200
|
-
/** Resolves when the worker broadcasts READY after database initialisation. */
|
|
1201
|
-
async waitReady() {
|
|
1202
|
-
return this.readyPromise;
|
|
1203
|
-
}
|
|
1204
|
-
send(request) {
|
|
1205
|
-
const id = generateId();
|
|
1206
|
-
return new Promise((resolve, reject) => {
|
|
1207
|
-
this.pending.set(id, {
|
|
1208
|
-
resolve,
|
|
1209
|
-
reject
|
|
1210
|
-
});
|
|
1211
|
-
this.worker.postMessage({ ...request, id });
|
|
1212
|
-
});
|
|
1213
|
-
}
|
|
1214
|
-
async query(sql, params) {
|
|
1215
|
-
const resp = await this.send({ type: "QUERY", sql, params });
|
|
1216
|
-
return { rows: resp.rows, fields: resp.fields };
|
|
1217
|
-
}
|
|
1218
|
-
async exec(sql) {
|
|
1219
|
-
await this.send({ type: "EXEC", sql });
|
|
1220
|
-
}
|
|
1221
|
-
async transaction(fn) {
|
|
1222
|
-
const resp = await this.send({ type: "BEGIN" });
|
|
1223
|
-
const txId = resp.txId;
|
|
1224
|
-
const proxy = new TransactionProxy(this, txId);
|
|
1225
|
-
try {
|
|
1226
|
-
const result = await fn(proxy);
|
|
1227
|
-
await this.send({ type: "COMMIT", txId });
|
|
1228
|
-
return result;
|
|
1229
|
-
} catch (err) {
|
|
1230
|
-
await this.send({ type: "ROLLBACK", txId }).catch(() => {
|
|
1231
|
-
});
|
|
1232
|
-
throw err;
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
/** Forward TX_QUERY for TransactionProxy — not part of the public surface. */
|
|
1236
|
-
async _txQuery(txId, sql, params) {
|
|
1237
|
-
const resp = await this.send({
|
|
1238
|
-
type: "TX_QUERY",
|
|
1239
|
-
txId,
|
|
1240
|
-
sql,
|
|
1241
|
-
params
|
|
1242
|
-
});
|
|
1243
|
-
return { rows: resp.rows };
|
|
1244
|
-
}
|
|
1245
|
-
/** Forward TX_EXEC for TransactionProxy — not part of the public surface. */
|
|
1246
|
-
async _txExec(txId, sql) {
|
|
1247
|
-
await this.send({ type: "TX_EXEC", txId, sql });
|
|
1248
|
-
}
|
|
1249
|
-
async ping() {
|
|
1250
|
-
await this.send({ type: "PING" });
|
|
1251
|
-
}
|
|
1252
|
-
async close() {
|
|
1253
|
-
await this.send({ type: "CLOSE" });
|
|
1254
|
-
this.worker.terminate();
|
|
1255
|
-
}
|
|
1256
|
-
};
|
|
1257
|
-
var TransactionProxy = class {
|
|
1258
|
-
constructor(client, txId) {
|
|
1259
|
-
this.client = client;
|
|
1260
|
-
this.txId = txId;
|
|
1261
|
-
}
|
|
1262
|
-
async query(sql, params) {
|
|
1263
|
-
return this.client._txQuery(this.txId, sql, params);
|
|
1264
|
-
}
|
|
1265
|
-
async exec(sql) {
|
|
1266
|
-
return this.client._txExec(this.txId, sql);
|
|
1267
|
-
}
|
|
1268
|
-
};
|
|
1269
|
-
|
|
1270
1305
|
// src/repositories/notes-repository.ts
|
|
1271
1306
|
var NotesRepository = class {
|
|
1272
1307
|
constructor(db, events) {
|
|
@@ -2953,12 +2988,12 @@ var JobQueueWorker = class {
|
|
|
2953
2988
|
if (job.required_capability) {
|
|
2954
2989
|
const capName = job.required_capability;
|
|
2955
2990
|
if (!this.capabilityManager?.isReady(capName)) {
|
|
2956
|
-
|
|
2991
|
+
await this.blockForCapability(job, capName);
|
|
2957
2992
|
continue;
|
|
2958
2993
|
}
|
|
2959
2994
|
}
|
|
2960
2995
|
await this.db.query(
|
|
2961
|
-
`UPDATE job_queue SET status = 'processing', updated_at = now() WHERE id = $1`,
|
|
2996
|
+
`UPDATE job_queue SET status = 'processing', error = NULL, updated_at = now() WHERE id = $1`,
|
|
2962
2997
|
[job.id]
|
|
2963
2998
|
);
|
|
2964
2999
|
try {
|
|
@@ -2966,7 +3001,7 @@ var JobQueueWorker = class {
|
|
|
2966
3001
|
const jobResult = await handler(job, this.db);
|
|
2967
3002
|
console.log(`[JobQueue] Completed ${job.job_type}:`, jobResult);
|
|
2968
3003
|
await this.db.query(
|
|
2969
|
-
`UPDATE job_queue SET status = 'completed', result = $1, updated_at = now() WHERE id = $2`,
|
|
3004
|
+
`UPDATE job_queue SET status = 'completed', error = NULL, result = $1, updated_at = now() WHERE id = $2`,
|
|
2970
3005
|
[JSON.stringify(jobResult ?? null), job.id]
|
|
2971
3006
|
);
|
|
2972
3007
|
this.events?.emit("job.completed", {
|
|
@@ -3000,6 +3035,30 @@ var JobQueueWorker = class {
|
|
|
3000
3035
|
}
|
|
3001
3036
|
return processed;
|
|
3002
3037
|
}
|
|
3038
|
+
async blockForCapability(job, capability) {
|
|
3039
|
+
const message = `requires capability '${capability}' \u2014 not ready`;
|
|
3040
|
+
console.log(`[JobQueue] Deferring ${job.job_type} \u2014 ${message}`);
|
|
3041
|
+
await this.db.query(
|
|
3042
|
+
`UPDATE job_queue
|
|
3043
|
+
SET status = 'pending', error = $1, updated_at = now()
|
|
3044
|
+
WHERE id = $2 AND error IS DISTINCT FROM $1`,
|
|
3045
|
+
[message, job.id]
|
|
3046
|
+
);
|
|
3047
|
+
this.events?.emit("job.blocked", {
|
|
3048
|
+
id: job.id,
|
|
3049
|
+
noteId: job.note_id,
|
|
3050
|
+
type: job.job_type,
|
|
3051
|
+
capability,
|
|
3052
|
+
message
|
|
3053
|
+
});
|
|
3054
|
+
this.events?.emit("capability.required", {
|
|
3055
|
+
name: capability,
|
|
3056
|
+
jobId: job.id,
|
|
3057
|
+
noteId: job.note_id,
|
|
3058
|
+
type: job.job_type,
|
|
3059
|
+
message
|
|
3060
|
+
});
|
|
3061
|
+
}
|
|
3003
3062
|
getBackoffDelay(retryCount) {
|
|
3004
3063
|
const delay = this.options.backoffBaseMs * Math.pow(2, retryCount);
|
|
3005
3064
|
return Math.min(delay, this.options.backoffMaxMs);
|
|
@@ -5147,6 +5206,33 @@ var FallbackRouter = class {
|
|
|
5147
5206
|
}
|
|
5148
5207
|
};
|
|
5149
5208
|
|
|
5209
|
+
// src/fortemi-bridge.ts
|
|
5210
|
+
function getFortemiBridge(host = globalThis) {
|
|
5211
|
+
return host?.fortemiBridge ?? null;
|
|
5212
|
+
}
|
|
5213
|
+
function getFortemiSecretStore(host = globalThis) {
|
|
5214
|
+
return host?.fortemiBridge?.secrets ?? host?.fortemiSecureStorage ?? null;
|
|
5215
|
+
}
|
|
5216
|
+
async function hasFortemiSecureSecrets(host = globalThis) {
|
|
5217
|
+
const bridge = getFortemiBridge(host);
|
|
5218
|
+
if (bridge) {
|
|
5219
|
+
try {
|
|
5220
|
+
const capabilities = await bridge.capabilities();
|
|
5221
|
+
if (!capabilities.secureSecrets) return false;
|
|
5222
|
+
return Boolean(await bridge.secrets.isAvailable());
|
|
5223
|
+
} catch {
|
|
5224
|
+
return false;
|
|
5225
|
+
}
|
|
5226
|
+
}
|
|
5227
|
+
const legacySecrets = host?.fortemiSecureStorage;
|
|
5228
|
+
if (!legacySecrets) return false;
|
|
5229
|
+
try {
|
|
5230
|
+
return Boolean(await legacySecrets.isAvailable());
|
|
5231
|
+
} catch {
|
|
5232
|
+
return false;
|
|
5233
|
+
}
|
|
5234
|
+
}
|
|
5235
|
+
|
|
5150
5236
|
// src/security/plugin-content.ts
|
|
5151
5237
|
var DEFAULT_DIRECTIVES = {
|
|
5152
5238
|
"default-src": ["'self'"],
|
|
@@ -5803,25 +5889,52 @@ async function exportShard(db, options) {
|
|
|
5803
5889
|
components.push("provenance_edges");
|
|
5804
5890
|
counts.provenance_edges = filteredProvenanceRows.length;
|
|
5805
5891
|
if (options?.includeEmbeddings) {
|
|
5806
|
-
const
|
|
5892
|
+
const embeddingSetIds = options.embeddingSetIds?.filter(Boolean) ?? [];
|
|
5893
|
+
const setScoped = embeddingSetIds.length > 0;
|
|
5894
|
+
const embSetRows = await db.query(
|
|
5895
|
+
`SELECT * FROM embedding_set
|
|
5896
|
+
${setScoped ? "WHERE id = ANY($1)" : ""}
|
|
5897
|
+
ORDER BY created_at`,
|
|
5898
|
+
setScoped ? [embeddingSetIds] : []
|
|
5899
|
+
);
|
|
5900
|
+
const exportedSetIds = new Set(embSetRows.rows.map((row) => row.id));
|
|
5807
5901
|
const shardEmbSets = embSetRows.rows.map(embeddingSetToShard);
|
|
5808
5902
|
files.set("embedding_sets.json", encoder.encode(JSON.stringify(shardEmbSets)));
|
|
5809
5903
|
components.push("embedding_sets");
|
|
5810
5904
|
counts.embedding_sets = shardEmbSets.length;
|
|
5811
|
-
const embMemberRows = await db.query(
|
|
5812
|
-
|
|
5905
|
+
const embMemberRows = await db.query(
|
|
5906
|
+
`SELECT * FROM embedding_set_member
|
|
5907
|
+
${setScoped ? "WHERE embedding_set_id = ANY($1)" : ""}`,
|
|
5908
|
+
setScoped ? [embeddingSetIds] : []
|
|
5909
|
+
);
|
|
5910
|
+
const scopedEmbMemberRows = embMemberRows.rows.filter(
|
|
5911
|
+
(member) => exportedSetIds.has(member.embedding_set_id) && exportedNoteIds.has(member.note_id)
|
|
5912
|
+
);
|
|
5913
|
+
const membersJsonl = scopedEmbMemberRows.map((m) => JSON.stringify(embeddingSetMemberToShard(m))).join("\n");
|
|
5813
5914
|
files.set("embedding_set_members.jsonl", encoder.encode(membersJsonl));
|
|
5814
5915
|
components.push("embedding_set_members");
|
|
5815
|
-
counts.embedding_set_members =
|
|
5816
|
-
const embRows = await db.query(
|
|
5817
|
-
|
|
5916
|
+
counts.embedding_set_members = scopedEmbMemberRows.length;
|
|
5917
|
+
const embRows = await db.query(
|
|
5918
|
+
`SELECT * FROM embedding
|
|
5919
|
+
${setScoped ? "WHERE embedding_set_id = ANY($1)" : ""}
|
|
5920
|
+
ORDER BY created_at`,
|
|
5921
|
+
setScoped ? [embeddingSetIds] : []
|
|
5922
|
+
);
|
|
5923
|
+
const memberEmbeddingIds = new Set(scopedEmbMemberRows.map((member) => member.embedding_id));
|
|
5924
|
+
const scopedEmbRows = embRows.rows.filter(
|
|
5925
|
+
(embedding) => exportedSetIds.has(embedding.embedding_set_id) && exportedNoteIds.has(embedding.note_id) && (memberEmbeddingIds.size === 0 || memberEmbeddingIds.has(embedding.id))
|
|
5926
|
+
);
|
|
5927
|
+
const embJsonl = scopedEmbRows.map((e) => JSON.stringify(embeddingToShard(e))).join("\n");
|
|
5818
5928
|
files.set("embeddings.jsonl", encoder.encode(embJsonl));
|
|
5819
5929
|
components.push("embeddings");
|
|
5820
|
-
counts.embeddings =
|
|
5930
|
+
counts.embeddings = scopedEmbRows.length;
|
|
5821
5931
|
}
|
|
5822
5932
|
const graphSourceRows = await db.query(`SELECT * FROM graph_source ORDER BY created_at, id`);
|
|
5823
|
-
|
|
5824
|
-
|
|
5933
|
+
const graphScoped = !!options?.embeddingSetIds?.length;
|
|
5934
|
+
const scopedGraphSourceRows = graphScoped ? graphSourceRows.rows.filter((row) => !row.embedding_set_id || options.embeddingSetIds?.includes(row.embedding_set_id)) : graphSourceRows.rows;
|
|
5935
|
+
const exportedGraphSourceIds = new Set(scopedGraphSourceRows.map((row) => row.id));
|
|
5936
|
+
if (scopedGraphSourceRows.length > 0) {
|
|
5937
|
+
const shardGraphSources = scopedGraphSourceRows.map((row) => ({
|
|
5825
5938
|
id: row.id,
|
|
5826
5939
|
name: row.name,
|
|
5827
5940
|
kind: row.kind,
|
|
@@ -5843,8 +5956,9 @@ async function exportShard(db, options) {
|
|
|
5843
5956
|
counts.graph_sources = shardGraphSources.length;
|
|
5844
5957
|
}
|
|
5845
5958
|
const graphEdgeRows = await db.query(`SELECT * FROM graph_edge_artifact ORDER BY graph_source_id, from_note_id, to_note_id, kind`);
|
|
5846
|
-
|
|
5847
|
-
|
|
5959
|
+
const scopedGraphEdgeRows = graphScoped ? graphEdgeRows.rows.filter((row) => exportedGraphSourceIds.has(row.graph_source_id)) : graphEdgeRows.rows;
|
|
5960
|
+
if (scopedGraphEdgeRows.length > 0) {
|
|
5961
|
+
const graphEdgesJsonl = scopedGraphEdgeRows.map((row) => JSON.stringify({
|
|
5848
5962
|
graph_source_id: row.graph_source_id,
|
|
5849
5963
|
from_note_id: row.from_note_id,
|
|
5850
5964
|
to_note_id: row.to_note_id,
|
|
@@ -5855,18 +5969,21 @@ async function exportShard(db, options) {
|
|
|
5855
5969
|
})).join("\n");
|
|
5856
5970
|
files.set("graph_edges.jsonl", encoder.encode(graphEdgesJsonl));
|
|
5857
5971
|
components.push("graph_edges");
|
|
5858
|
-
counts.graph_edges =
|
|
5972
|
+
counts.graph_edges = scopedGraphEdgeRows.length;
|
|
5859
5973
|
}
|
|
5860
5974
|
const communitySetRows = await db.query(`SELECT * FROM community_set ORDER BY created_at, id`);
|
|
5861
5975
|
const communityRows = await db.query(`SELECT * FROM community ORDER BY community_set_id, rank NULLS LAST, id`);
|
|
5862
|
-
|
|
5976
|
+
const scopedCommunitySetRows = graphScoped ? communitySetRows.rows.filter((row) => exportedGraphSourceIds.has(row.graph_source_id)) : communitySetRows.rows;
|
|
5977
|
+
const exportedCommunitySetIds = new Set(scopedCommunitySetRows.map((row) => row.id));
|
|
5978
|
+
const scopedCommunityRows = graphScoped ? communityRows.rows.filter((row) => exportedCommunitySetIds.has(row.community_set_id)) : communityRows.rows;
|
|
5979
|
+
if (scopedCommunitySetRows.length > 0) {
|
|
5863
5980
|
const communitiesBySet = /* @__PURE__ */ new Map();
|
|
5864
|
-
for (const row of
|
|
5981
|
+
for (const row of scopedCommunityRows) {
|
|
5865
5982
|
const rows = communitiesBySet.get(row.community_set_id) ?? [];
|
|
5866
5983
|
rows.push(row);
|
|
5867
5984
|
communitiesBySet.set(row.community_set_id, rows);
|
|
5868
5985
|
}
|
|
5869
|
-
const shardCommunitySets =
|
|
5986
|
+
const shardCommunitySets = scopedCommunitySetRows.map((row) => ({
|
|
5870
5987
|
id: row.id,
|
|
5871
5988
|
graph_source_id: row.graph_source_id,
|
|
5872
5989
|
name: row.name,
|
|
@@ -5889,11 +6006,12 @@ async function exportShard(db, options) {
|
|
|
5889
6006
|
files.set("communities.json", encoder.encode(JSON.stringify(shardCommunitySets)));
|
|
5890
6007
|
components.push("communities");
|
|
5891
6008
|
counts.community_sets = shardCommunitySets.length;
|
|
5892
|
-
counts.communities =
|
|
6009
|
+
counts.communities = scopedCommunityRows.length;
|
|
5893
6010
|
}
|
|
5894
6011
|
const assignmentRows = await db.query(`SELECT * FROM community_assignment ORDER BY community_set_id, community_id, note_id`);
|
|
5895
|
-
|
|
5896
|
-
|
|
6012
|
+
const scopedAssignmentRows = graphScoped ? assignmentRows.rows.filter((row) => exportedCommunitySetIds.has(row.community_set_id)) : assignmentRows.rows;
|
|
6013
|
+
if (scopedAssignmentRows.length > 0) {
|
|
6014
|
+
const assignmentsJsonl = scopedAssignmentRows.map((row) => JSON.stringify({
|
|
5897
6015
|
community_set_id: row.community_set_id,
|
|
5898
6016
|
community_id: row.community_id,
|
|
5899
6017
|
note_id: row.note_id,
|
|
@@ -5903,7 +6021,7 @@ async function exportShard(db, options) {
|
|
|
5903
6021
|
})).join("\n");
|
|
5904
6022
|
files.set("community_assignments.jsonl", encoder.encode(assignmentsJsonl));
|
|
5905
6023
|
components.push("community_assignments");
|
|
5906
|
-
counts.community_assignments =
|
|
6024
|
+
counts.community_assignments = scopedAssignmentRows.length;
|
|
5907
6025
|
}
|
|
5908
6026
|
const checksums = {};
|
|
5909
6027
|
for (const [filename, data] of files) {
|
|
@@ -5925,9 +6043,25 @@ async function exportShard(db, options) {
|
|
|
5925
6043
|
|
|
5926
6044
|
// src/shard/shard-import.ts
|
|
5927
6045
|
var decoder = new TextDecoder();
|
|
6046
|
+
var DEFAULT_BATCH_SIZE = 250;
|
|
6047
|
+
async function yieldToEventLoop() {
|
|
6048
|
+
const scheduler = globalThis.scheduler;
|
|
6049
|
+
if (scheduler?.yield) {
|
|
6050
|
+
await scheduler.yield();
|
|
6051
|
+
return;
|
|
6052
|
+
}
|
|
6053
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
6054
|
+
}
|
|
6055
|
+
async function maybeYield(done, batchSize) {
|
|
6056
|
+
if (batchSize > 0 && done > 0 && done % batchSize === 0) {
|
|
6057
|
+
await yieldToEventLoop();
|
|
6058
|
+
}
|
|
6059
|
+
}
|
|
5928
6060
|
async function importShard(db, data, options) {
|
|
5929
6061
|
const start = performance.now();
|
|
5930
6062
|
const strategy = options?.conflictStrategy ?? "skip";
|
|
6063
|
+
const batchSize = options?.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
6064
|
+
const report = options?.onProgress;
|
|
5931
6065
|
const warnings = [];
|
|
5932
6066
|
const errors = [];
|
|
5933
6067
|
const counts = {
|
|
@@ -5951,9 +6085,11 @@ async function importShard(db, data, options) {
|
|
|
5951
6085
|
};
|
|
5952
6086
|
const skipped = {};
|
|
5953
6087
|
const inputData = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
|
|
6088
|
+
report?.({ phase: "unpack", done: 0, total: 1 });
|
|
5954
6089
|
let files;
|
|
5955
6090
|
try {
|
|
5956
6091
|
files = unpackTarGz(inputData);
|
|
6092
|
+
report?.({ phase: "unpack", done: 1, total: 1 });
|
|
5957
6093
|
} catch (err) {
|
|
5958
6094
|
return {
|
|
5959
6095
|
success: false,
|
|
@@ -6000,6 +6136,7 @@ async function importShard(db, data, options) {
|
|
|
6000
6136
|
duration_ms: performance.now() - start
|
|
6001
6137
|
};
|
|
6002
6138
|
}
|
|
6139
|
+
report?.({ phase: "validate", done: 0, total: 1 });
|
|
6003
6140
|
const checksumResult = await validateChecksums(manifest.checksums, files);
|
|
6004
6141
|
if (!checksumResult.valid) {
|
|
6005
6142
|
return {
|
|
@@ -6011,6 +6148,7 @@ async function importShard(db, data, options) {
|
|
|
6011
6148
|
duration_ms: performance.now() - start
|
|
6012
6149
|
};
|
|
6013
6150
|
}
|
|
6151
|
+
report?.({ phase: "validate", done: 1, total: 1 });
|
|
6014
6152
|
const parsedNotes = parseJsonl(files.get("notes.jsonl"));
|
|
6015
6153
|
const parsedCollections = parseJsonArray(files.get("collections.json"));
|
|
6016
6154
|
parseJsonArray(files.get("tags.json"));
|
|
@@ -6061,7 +6199,8 @@ async function importShard(db, data, options) {
|
|
|
6061
6199
|
const conflictClause = strategy === "skip" ? "ON CONFLICT DO NOTHING" : "";
|
|
6062
6200
|
try {
|
|
6063
6201
|
await db.transaction(async (tx) => {
|
|
6064
|
-
|
|
6202
|
+
report?.({ phase: "collections", done: 0, total: parsedCollections.length });
|
|
6203
|
+
for (const [index, shardCol] of parsedCollections.entries()) {
|
|
6065
6204
|
const col = collectionFromShard(shardCol);
|
|
6066
6205
|
if (strategy === "replace") {
|
|
6067
6206
|
await tx.query(
|
|
@@ -6078,8 +6217,11 @@ async function importShard(db, data, options) {
|
|
|
6078
6217
|
);
|
|
6079
6218
|
}
|
|
6080
6219
|
counts.collections++;
|
|
6220
|
+
report?.({ phase: "collections", done: index + 1, total: parsedCollections.length });
|
|
6221
|
+
await maybeYield(index + 1, batchSize);
|
|
6081
6222
|
}
|
|
6082
|
-
|
|
6223
|
+
report?.({ phase: "notes", done: 0, total: parsedNotes.length });
|
|
6224
|
+
for (const [index, shardNote] of parsedNotes.entries()) {
|
|
6083
6225
|
const note = noteFromShard(shardNote);
|
|
6084
6226
|
const contentHash = computeHash(new TextEncoder().encode(note.original_content));
|
|
6085
6227
|
if (strategy === "replace") {
|
|
@@ -6156,7 +6298,12 @@ async function importShard(db, data, options) {
|
|
|
6156
6298
|
);
|
|
6157
6299
|
}
|
|
6158
6300
|
counts.notes++;
|
|
6301
|
+
report?.({ phase: "notes", done: index + 1, total: parsedNotes.length });
|
|
6302
|
+
await maybeYield(index + 1, batchSize);
|
|
6159
6303
|
}
|
|
6304
|
+
const totalSkos = parsedSkosSchemes.length + parsedSkosConcepts.length + parsedSkosRelations.length + parsedNoteSkosTags.length;
|
|
6305
|
+
let doneSkos = 0;
|
|
6306
|
+
report?.({ phase: "skos", done: doneSkos, total: totalSkos });
|
|
6160
6307
|
for (const scheme of parsedSkosSchemes) {
|
|
6161
6308
|
if (strategy === "replace") {
|
|
6162
6309
|
await tx.query(
|
|
@@ -6173,6 +6320,8 @@ async function importShard(db, data, options) {
|
|
|
6173
6320
|
);
|
|
6174
6321
|
}
|
|
6175
6322
|
counts.skos_schemes++;
|
|
6323
|
+
report?.({ phase: "skos", done: ++doneSkos, total: totalSkos });
|
|
6324
|
+
await maybeYield(doneSkos, batchSize);
|
|
6176
6325
|
}
|
|
6177
6326
|
for (const concept of parsedSkosConcepts) {
|
|
6178
6327
|
const altLabels = JSON.stringify(concept.alt_labels ?? []);
|
|
@@ -6191,8 +6340,11 @@ async function importShard(db, data, options) {
|
|
|
6191
6340
|
);
|
|
6192
6341
|
}
|
|
6193
6342
|
counts.skos_concepts++;
|
|
6343
|
+
report?.({ phase: "skos", done: ++doneSkos, total: totalSkos });
|
|
6344
|
+
await maybeYield(doneSkos, batchSize);
|
|
6194
6345
|
}
|
|
6195
|
-
|
|
6346
|
+
report?.({ phase: "links", done: 0, total: parsedLinks.length });
|
|
6347
|
+
for (const [index, shardLink] of parsedLinks.entries()) {
|
|
6196
6348
|
const link = linkFromShard(shardLink);
|
|
6197
6349
|
if (strategy === "replace") {
|
|
6198
6350
|
await tx.query(
|
|
@@ -6209,6 +6361,8 @@ async function importShard(db, data, options) {
|
|
|
6209
6361
|
);
|
|
6210
6362
|
}
|
|
6211
6363
|
counts.links++;
|
|
6364
|
+
report?.({ phase: "links", done: index + 1, total: parsedLinks.length });
|
|
6365
|
+
await maybeYield(index + 1, batchSize);
|
|
6212
6366
|
}
|
|
6213
6367
|
for (const relation of parsedSkosRelations) {
|
|
6214
6368
|
if (strategy === "replace") {
|
|
@@ -6226,6 +6380,8 @@ async function importShard(db, data, options) {
|
|
|
6226
6380
|
);
|
|
6227
6381
|
}
|
|
6228
6382
|
counts.skos_relations++;
|
|
6383
|
+
report?.({ phase: "skos", done: ++doneSkos, total: totalSkos });
|
|
6384
|
+
await maybeYield(doneSkos, batchSize);
|
|
6229
6385
|
}
|
|
6230
6386
|
for (const tag of parsedNoteSkosTags) {
|
|
6231
6387
|
await tx.query(
|
|
@@ -6235,8 +6391,11 @@ async function importShard(db, data, options) {
|
|
|
6235
6391
|
[tag.id, tag.note_id, tag.concept_id, tag.created_at]
|
|
6236
6392
|
);
|
|
6237
6393
|
counts.note_skos_tags++;
|
|
6394
|
+
report?.({ phase: "skos", done: ++doneSkos, total: totalSkos });
|
|
6395
|
+
await maybeYield(doneSkos, batchSize);
|
|
6238
6396
|
}
|
|
6239
|
-
|
|
6397
|
+
report?.({ phase: "provenance", done: 0, total: parsedProvenanceEdges.length });
|
|
6398
|
+
for (const [index, edge] of parsedProvenanceEdges.entries()) {
|
|
6240
6399
|
const attributes = edge.attributes === null ? null : JSON.stringify(edge.attributes);
|
|
6241
6400
|
if (strategy === "replace") {
|
|
6242
6401
|
await tx.query(
|
|
@@ -6253,8 +6412,11 @@ async function importShard(db, data, options) {
|
|
|
6253
6412
|
);
|
|
6254
6413
|
}
|
|
6255
6414
|
counts.provenance_edges++;
|
|
6415
|
+
report?.({ phase: "provenance", done: index + 1, total: parsedProvenanceEdges.length });
|
|
6416
|
+
await maybeYield(index + 1, batchSize);
|
|
6256
6417
|
}
|
|
6257
|
-
|
|
6418
|
+
report?.({ phase: "embedding_sets", done: 0, total: parsedEmbSets.length });
|
|
6419
|
+
for (const [index, shardSet] of parsedEmbSets.entries()) {
|
|
6258
6420
|
const set = embeddingSetFromShard(shardSet);
|
|
6259
6421
|
if (strategy === "replace") {
|
|
6260
6422
|
await tx.query(
|
|
@@ -6277,8 +6439,11 @@ async function importShard(db, data, options) {
|
|
|
6277
6439
|
);
|
|
6278
6440
|
}
|
|
6279
6441
|
counts.embedding_sets++;
|
|
6442
|
+
report?.({ phase: "embedding_sets", done: index + 1, total: parsedEmbSets.length });
|
|
6443
|
+
await maybeYield(index + 1, batchSize);
|
|
6280
6444
|
}
|
|
6281
|
-
|
|
6445
|
+
report?.({ phase: "embeddings", done: 0, total: parsedEmbeddings.length });
|
|
6446
|
+
for (const [index, shardEmb] of parsedEmbeddings.entries()) {
|
|
6282
6447
|
const emb = embeddingFromShard(shardEmb);
|
|
6283
6448
|
if (strategy === "replace") {
|
|
6284
6449
|
await tx.query(
|
|
@@ -6295,7 +6460,12 @@ async function importShard(db, data, options) {
|
|
|
6295
6460
|
);
|
|
6296
6461
|
}
|
|
6297
6462
|
counts.embeddings++;
|
|
6463
|
+
report?.({ phase: "embeddings", done: index + 1, total: parsedEmbeddings.length });
|
|
6464
|
+
await maybeYield(index + 1, batchSize);
|
|
6298
6465
|
}
|
|
6466
|
+
const totalGraph = parsedGraphSources.length + parsedGraphEdges.length;
|
|
6467
|
+
let doneGraph = 0;
|
|
6468
|
+
report?.({ phase: "graph", done: doneGraph, total: totalGraph });
|
|
6299
6469
|
for (const source of parsedGraphSources) {
|
|
6300
6470
|
const parameters = source.parameters == null ? null : JSON.stringify(source.parameters);
|
|
6301
6471
|
const freshness = JSON.stringify({ ...source.freshness ?? {}, status: "unknown" });
|
|
@@ -6308,6 +6478,8 @@ async function importShard(db, data, options) {
|
|
|
6308
6478
|
[source.id, source.name, source.kind, source.source_table ?? null, source.embedding_set_id ?? null, source.virtual_set_id ?? null, source.model ?? null, source.dimension ?? null, source.truncate_dimension ?? null, source.metric ?? null, source.algorithm ?? null, parameters, source.input_hash, freshness, source.created_at]
|
|
6309
6479
|
);
|
|
6310
6480
|
counts.graph_sources++;
|
|
6481
|
+
report?.({ phase: "graph", done: ++doneGraph, total: totalGraph });
|
|
6482
|
+
await maybeYield(doneGraph, batchSize);
|
|
6311
6483
|
}
|
|
6312
6484
|
for (const edge of parsedGraphEdges) {
|
|
6313
6485
|
const metadata = edge.metadata == null ? null : JSON.stringify(edge.metadata);
|
|
@@ -6318,7 +6490,12 @@ async function importShard(db, data, options) {
|
|
|
6318
6490
|
[edge.graph_source_id, edge.from_note_id, edge.to_note_id, edge.weight, edge.kind, edge.rank ?? null, metadata]
|
|
6319
6491
|
);
|
|
6320
6492
|
counts.graph_edges++;
|
|
6493
|
+
report?.({ phase: "graph", done: ++doneGraph, total: totalGraph });
|
|
6494
|
+
await maybeYield(doneGraph, batchSize);
|
|
6321
6495
|
}
|
|
6496
|
+
const totalCommunities = parsedCommunitySets.length + parsedCommunitySets.reduce((sum, set) => sum + (set.communities?.length ?? 0), 0) + parsedCommunityAssignments.length;
|
|
6497
|
+
let doneCommunities = 0;
|
|
6498
|
+
report?.({ phase: "communities", done: doneCommunities, total: totalCommunities });
|
|
6322
6499
|
for (const set of parsedCommunitySets) {
|
|
6323
6500
|
const parameters = set.parameters == null ? null : JSON.stringify(set.parameters);
|
|
6324
6501
|
const freshness = JSON.stringify({ ...set.freshness ?? {}, status: "unknown" });
|
|
@@ -6329,6 +6506,8 @@ async function importShard(db, data, options) {
|
|
|
6329
6506
|
[set.id, set.graph_source_id, set.name, set.source_type, set.algorithm ?? null, parameters, set.input_hash, freshness, set.created_at]
|
|
6330
6507
|
);
|
|
6331
6508
|
counts.community_sets++;
|
|
6509
|
+
report?.({ phase: "communities", done: ++doneCommunities, total: totalCommunities });
|
|
6510
|
+
await maybeYield(doneCommunities, batchSize);
|
|
6332
6511
|
for (const community of set.communities ?? []) {
|
|
6333
6512
|
const metadata = community.metadata == null ? null : JSON.stringify(community.metadata);
|
|
6334
6513
|
await tx.query(
|
|
@@ -6338,6 +6517,8 @@ async function importShard(db, data, options) {
|
|
|
6338
6517
|
[set.id, community.id, community.label ?? null, community.rank ?? null, community.size ?? null, community.confidence ?? null, community.representative_note_ids ?? [], metadata]
|
|
6339
6518
|
);
|
|
6340
6519
|
counts.communities++;
|
|
6520
|
+
report?.({ phase: "communities", done: ++doneCommunities, total: totalCommunities });
|
|
6521
|
+
await maybeYield(doneCommunities, batchSize);
|
|
6341
6522
|
}
|
|
6342
6523
|
}
|
|
6343
6524
|
for (const assignment of parsedCommunityAssignments) {
|
|
@@ -6349,16 +6530,22 @@ async function importShard(db, data, options) {
|
|
|
6349
6530
|
[assignment.community_set_id, assignment.community_id, assignment.note_id, assignment.confidence ?? null, assignment.source_type, metadata]
|
|
6350
6531
|
);
|
|
6351
6532
|
counts.community_assignments++;
|
|
6533
|
+
report?.({ phase: "communities", done: ++doneCommunities, total: totalCommunities });
|
|
6534
|
+
await maybeYield(doneCommunities, batchSize);
|
|
6352
6535
|
}
|
|
6353
|
-
|
|
6536
|
+
report?.({ phase: "embedding_set_members", done: 0, total: parsedEmbMembers.length });
|
|
6537
|
+
for (const [index, member] of parsedEmbMembers.entries()) {
|
|
6354
6538
|
await tx.query(
|
|
6355
6539
|
`INSERT INTO embedding_set_member (embedding_set_id, note_id, embedding_id)
|
|
6356
6540
|
VALUES ($1, $2, $3) ON CONFLICT DO NOTHING`,
|
|
6357
6541
|
[member.embedding_set_id, member.note_id, member.embedding_id]
|
|
6358
6542
|
);
|
|
6359
6543
|
counts.embedding_set_members++;
|
|
6544
|
+
report?.({ phase: "embedding_set_members", done: index + 1, total: parsedEmbMembers.length });
|
|
6545
|
+
await maybeYield(index + 1, batchSize);
|
|
6360
6546
|
}
|
|
6361
6547
|
});
|
|
6548
|
+
report?.({ phase: "index", done: 1, total: 1 });
|
|
6362
6549
|
} catch (err) {
|
|
6363
6550
|
return {
|
|
6364
6551
|
success: false,
|
|
@@ -6388,9 +6575,187 @@ function parseJsonArray(data) {
|
|
|
6388
6575
|
return JSON.parse(decoder.decode(data));
|
|
6389
6576
|
}
|
|
6390
6577
|
|
|
6578
|
+
// src/aiwg-index.ts
|
|
6579
|
+
var REQUIRED_RECORD_FIELDS = [
|
|
6580
|
+
"schema_version",
|
|
6581
|
+
"id",
|
|
6582
|
+
"type",
|
|
6583
|
+
"source",
|
|
6584
|
+
"title",
|
|
6585
|
+
"text",
|
|
6586
|
+
"facets",
|
|
6587
|
+
"tags",
|
|
6588
|
+
"concepts",
|
|
6589
|
+
"relationships",
|
|
6590
|
+
"provenance",
|
|
6591
|
+
"privacy",
|
|
6592
|
+
"updated_at"
|
|
6593
|
+
];
|
|
6594
|
+
var VALID_TYPES = /* @__PURE__ */ new Set([
|
|
6595
|
+
"crm.contact",
|
|
6596
|
+
"crm.organization",
|
|
6597
|
+
"crm.event",
|
|
6598
|
+
"crm.interaction",
|
|
6599
|
+
"aiwg.artifact"
|
|
6600
|
+
]);
|
|
6601
|
+
function hasString(value) {
|
|
6602
|
+
return typeof value === "string" && value.length > 0;
|
|
6603
|
+
}
|
|
6604
|
+
function pushFacet(counts, name, value) {
|
|
6605
|
+
counts[name] ??= {};
|
|
6606
|
+
counts[name][value] = (counts[name][value] ?? 0) + 1;
|
|
6607
|
+
}
|
|
6608
|
+
function validateAiwgFortemiIndexExport(value) {
|
|
6609
|
+
const errors = [];
|
|
6610
|
+
const counts = {};
|
|
6611
|
+
const data = value;
|
|
6612
|
+
if (data?.schema_version !== "aiwg.fortemi.index.export.v1") {
|
|
6613
|
+
errors.push("schema_version must be aiwg.fortemi.index.export.v1");
|
|
6614
|
+
}
|
|
6615
|
+
if (!hasString(data?.generated_at)) errors.push("generated_at is required");
|
|
6616
|
+
if (!hasString(data?.source?.repo)) errors.push("source.repo is required");
|
|
6617
|
+
if (!hasString(data?.source?.privacy)) errors.push("source.privacy is required");
|
|
6618
|
+
if (!Array.isArray(data?.items)) errors.push("items must be an array");
|
|
6619
|
+
const ids = /* @__PURE__ */ new Set();
|
|
6620
|
+
let previousId = "";
|
|
6621
|
+
for (const [index, item] of (data.items ?? []).entries()) {
|
|
6622
|
+
for (const field of REQUIRED_RECORD_FIELDS) {
|
|
6623
|
+
if (!(field in item)) errors.push("items[" + index + "]." + field + " is required");
|
|
6624
|
+
}
|
|
6625
|
+
if (item.schema_version !== "aiwg.fortemi.index.record.v1") {
|
|
6626
|
+
errors.push("items[" + index + "].schema_version must be aiwg.fortemi.index.record.v1");
|
|
6627
|
+
}
|
|
6628
|
+
if (!hasString(item.id)) errors.push("items[" + index + "].id is required");
|
|
6629
|
+
if (hasString(item.id) && ids.has(item.id)) errors.push("duplicate id: " + item.id);
|
|
6630
|
+
if (hasString(item.id)) ids.add(item.id);
|
|
6631
|
+
if (previousId && hasString(item.id) && previousId.localeCompare(item.id) > 0) {
|
|
6632
|
+
errors.push("items must be sorted by id: " + previousId + " before " + item.id);
|
|
6633
|
+
}
|
|
6634
|
+
if (hasString(item.id)) previousId = item.id;
|
|
6635
|
+
if (!VALID_TYPES.has(item.type)) errors.push("items[" + index + "].type is invalid");
|
|
6636
|
+
else counts[item.type] = (counts[item.type] ?? 0) + 1;
|
|
6637
|
+
if (!hasString(item.source?.path)) errors.push("items[" + index + "].source.path is required");
|
|
6638
|
+
if (!hasString(item.source?.repo_relative_path)) errors.push("items[" + index + "].source.repo_relative_path is required");
|
|
6639
|
+
if (!hasString(item.source?.locator)) errors.push("items[" + index + "].source.locator is required");
|
|
6640
|
+
if (!Array.isArray(item.tags)) errors.push("items[" + index + "].tags must be an array");
|
|
6641
|
+
if (!Array.isArray(item.concepts)) errors.push("items[" + index + "].concepts must be an array");
|
|
6642
|
+
if (!Array.isArray(item.relationships)) errors.push("items[" + index + "].relationships must be an array");
|
|
6643
|
+
if (!Array.isArray(item.provenance) || item.provenance.length === 0) {
|
|
6644
|
+
errors.push("items[" + index + "].provenance must be a non-empty array");
|
|
6645
|
+
}
|
|
6646
|
+
if (!item.privacy || typeof item.privacy.pii !== "boolean" || !hasString(item.privacy.classification)) {
|
|
6647
|
+
errors.push("items[" + index + "].privacy requires classification and pii");
|
|
6648
|
+
}
|
|
6649
|
+
}
|
|
6650
|
+
return { valid: errors.length === 0, errors, counts };
|
|
6651
|
+
}
|
|
6652
|
+
function assertAiwgFortemiIndexExport(value) {
|
|
6653
|
+
const result = validateAiwgFortemiIndexExport(value);
|
|
6654
|
+
if (!result.valid) {
|
|
6655
|
+
throw new Error("Invalid AIWG Fortemi index export:\n" + result.errors.join("\n"));
|
|
6656
|
+
}
|
|
6657
|
+
return value;
|
|
6658
|
+
}
|
|
6659
|
+
function getAiwgFortemiFacets(items) {
|
|
6660
|
+
const result = {};
|
|
6661
|
+
for (const item of items) {
|
|
6662
|
+
pushFacet(result, "type", item.type);
|
|
6663
|
+
pushFacet(result, "privacy", item.privacy.classification);
|
|
6664
|
+
for (const tag of item.tags) pushFacet(result, "tag", tag);
|
|
6665
|
+
for (const concept of item.concepts) pushFacet(result, "concept", concept);
|
|
6666
|
+
for (const [name, values] of Object.entries(item.facets)) {
|
|
6667
|
+
for (const value of values) pushFacet(result, name, value);
|
|
6668
|
+
}
|
|
6669
|
+
}
|
|
6670
|
+
return result;
|
|
6671
|
+
}
|
|
6672
|
+
function includesAll(actual, expected) {
|
|
6673
|
+
if (!expected || expected.length === 0) return true;
|
|
6674
|
+
const actualSet = new Set(actual);
|
|
6675
|
+
return expected.every((value) => actualSet.has(value));
|
|
6676
|
+
}
|
|
6677
|
+
function matchesFacetFilters(item, filters) {
|
|
6678
|
+
if (!filters) return true;
|
|
6679
|
+
return Object.entries(filters).every(([name, expected]) => includesAll(item.facets[name] ?? [], expected));
|
|
6680
|
+
}
|
|
6681
|
+
function queryAiwgFortemiIndex(index, query = "", options = {}) {
|
|
6682
|
+
const q = query.trim().toLowerCase();
|
|
6683
|
+
const filtered = index.items.filter((item) => {
|
|
6684
|
+
if (q) {
|
|
6685
|
+
const haystack = [item.title, item.text, ...item.tags, ...item.concepts].join("\n").toLowerCase();
|
|
6686
|
+
if (!haystack.includes(q)) return false;
|
|
6687
|
+
}
|
|
6688
|
+
if (options.types && !options.types.includes(item.type)) return false;
|
|
6689
|
+
if (options.privacy && !options.privacy.includes(item.privacy.classification)) return false;
|
|
6690
|
+
if (!includesAll(item.tags, options.tags)) return false;
|
|
6691
|
+
if (!includesAll(item.concepts, options.concepts)) return false;
|
|
6692
|
+
if (!matchesFacetFilters(item, options.facets)) return false;
|
|
6693
|
+
if (options.relationshipTargetId && !item.relationships.some((rel) => rel.target_id === options.relationshipTargetId)) return false;
|
|
6694
|
+
return true;
|
|
6695
|
+
});
|
|
6696
|
+
const offset = options.offset ?? 0;
|
|
6697
|
+
const limit = options.limit ?? filtered.length;
|
|
6698
|
+
return {
|
|
6699
|
+
items: filtered.slice(offset, offset + limit),
|
|
6700
|
+
total: filtered.length,
|
|
6701
|
+
facets: getAiwgFortemiFacets(filtered)
|
|
6702
|
+
};
|
|
6703
|
+
}
|
|
6704
|
+
function createAiwgReviewDecisionExport(source, decisions, generatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
6705
|
+
return {
|
|
6706
|
+
schema_version: "aiwg.fortemi.review-decisions.v1",
|
|
6707
|
+
generated_at: generatedAt,
|
|
6708
|
+
source_export_schema_version: source.schema_version,
|
|
6709
|
+
decisions: [...decisions].sort((left, right) => left.item_id.localeCompare(right.item_id))
|
|
6710
|
+
};
|
|
6711
|
+
}
|
|
6712
|
+
function aiwgFortemiIndexToCommunityGraph(index, options = {}) {
|
|
6713
|
+
const ids = new Set(index.items.map((item) => item.id));
|
|
6714
|
+
const relationshipWeights = options.relationshipWeights ?? {};
|
|
6715
|
+
const edgeCounts = /* @__PURE__ */ new Map();
|
|
6716
|
+
for (const item of index.items) {
|
|
6717
|
+
for (const relationship of item.relationships) {
|
|
6718
|
+
if (!ids.has(relationship.target_id) && !options.includeDanglingRelationships) continue;
|
|
6719
|
+
const kind = relationship.type;
|
|
6720
|
+
const baseWeight = relationshipWeights[kind] ?? 1;
|
|
6721
|
+
const key = `${item.id}\0${relationship.target_id}\0${kind}`;
|
|
6722
|
+
const existing = edgeCounts.get(key);
|
|
6723
|
+
if (existing) existing.weight += baseWeight;
|
|
6724
|
+
else edgeCounts.set(key, { source: item.id, target: relationship.target_id, kind, weight: baseWeight });
|
|
6725
|
+
}
|
|
6726
|
+
}
|
|
6727
|
+
const communities = /* @__PURE__ */ new Map();
|
|
6728
|
+
for (const item of index.items) {
|
|
6729
|
+
const communityIds = communityIdsFor(item, options);
|
|
6730
|
+
for (const communityId of communityIds) {
|
|
6731
|
+
const nodes = communities.get(communityId) ?? [];
|
|
6732
|
+
nodes.push(item.id);
|
|
6733
|
+
communities.set(communityId, nodes);
|
|
6734
|
+
}
|
|
6735
|
+
}
|
|
6736
|
+
return {
|
|
6737
|
+
nodes: index.items.map((item) => ({ id: item.id })),
|
|
6738
|
+
edges: Array.from(edgeCounts.values()).sort((left, right) => left.source.localeCompare(right.source) || left.target.localeCompare(right.target) || left.kind.localeCompare(right.kind)),
|
|
6739
|
+
communities: Array.from(communities.entries()).map(([id, nodes]) => ({ id, nodes: [...new Set(nodes)].sort() })).sort((left, right) => left.id.localeCompare(right.id))
|
|
6740
|
+
};
|
|
6741
|
+
}
|
|
6742
|
+
function communityIdsFor(item, options) {
|
|
6743
|
+
if (options.communityFacet) {
|
|
6744
|
+
const values = item.facets[options.communityFacet] ?? [];
|
|
6745
|
+
if (values.length > 0) return values.map((value) => `${options.communityFacet}:${value}`);
|
|
6746
|
+
}
|
|
6747
|
+
if (options.communityTagPrefix) {
|
|
6748
|
+
const prefix = options.communityTagPrefix;
|
|
6749
|
+
const tags = item.tags.filter((tag) => tag.startsWith(prefix));
|
|
6750
|
+
if (tags.length > 0) return tags;
|
|
6751
|
+
}
|
|
6752
|
+
if (item.concepts.length > 0) return item.concepts.map((concept) => `concept:${concept}`);
|
|
6753
|
+
return [`type:${item.type}`];
|
|
6754
|
+
}
|
|
6755
|
+
|
|
6391
6756
|
// src/index.ts
|
|
6392
|
-
var VERSION = "2026.
|
|
6757
|
+
var VERSION = "2026.6.1";
|
|
6393
6758
|
|
|
6394
|
-
export { ArchiveManager, AttachmentsRepository, CURRENT_SHARD_VERSION, CapabilityManager, CaptureKnowledgeInputSchema, CollectionsRepository, CommunitiesRepository, EmbeddingSetsRepository, FallbackRouter, FortemiToolManifest, GetNoteInputSchema, GraphRepository, JOB_CAPABILITIES, JOB_PRIORITIES, JobQueueWorker, LOCAL_ENDPOINTS, LinksRepository, ListNotesInputSchema, ManageArchiveInputSchema, ManageAttachmentsInputSchema, ManageCapabilitiesInputSchema, ManageCollectionsInputSchema, ManageLinksInputSchema, ManageNoteInputSchema, ManageTagsInputSchema, MemoryBlobStore, MigrationRunner, NotesRepository, OpenAICompatibleProvider, PGliteStorageBackend, PGliteStorageBackendFactory, PGliteWorkerClient, ProviderRegistry, SHARD_FORMAT, SearchInputSchema, SearchRepository, SkosRepository, TagsRepository, TransactionProxy, TypedEventBus, VERSION, aiRevisionHandler, allMigrations, appendPluginScript, buildNoteConditions, buildPluginCsp, captureKnowledge, chunkText, classifyError, classifyModel, collectionFromShard, collectionToShard, computeHash, computeSri, conceptTaggingHandler, cosineSimilarity, createBlobStore, createCspReportHandler, createFortemi, createLegacyProvider, createPGliteInstance, createRoutes, defaultStorageBackendFactory, detectCommunities, detectGpuCapabilities, detectInferenceCapabilities, discoverLocalProviders, embeddingFromShard, embeddingGenerationHandler, embeddingSetFromShard, embeddingSetMemberToShard, embeddingSetToShard, embeddingToShard, enqueueFullWorkflow, enqueueJob, enqueueNoteCreationJobs, estimateModelFit, estimateVramMB, estimateVramTier, exportShard, fetchPluginScript, fortemiManifest, generateId, getEmbedFunction, getJobQueueStatus, getLlmFunction, getNote, importShard, isPluginScriptAllowed, linkFromShard, linkToShard, linkingHandler, listNotes, manageArchive, manageAttachments, manageCapabilities, manageCollections, manageLinks, manageNote, manageTags, matchRoute, noteFromShard, noteSkosTagToShard, noteToShard, packTarGz, parseCspReport, provenanceEdgeToShard, registerLlmCapability, registerSemanticCapability, registerServiceWorker, searchTool, selectLlmModel, setEmbedFunction, setLlmFunction, sha256Hex, skosConceptToShard, skosRelationToShard, skosSchemeToShard, suggestTags, tagsFromShard, tagsToShard, titleGenerationHandler, unpackTarGz, unregisterLlmCapability, unregisterSemanticCapability, validateChecksums, verifySri };
|
|
6759
|
+
export { ArchiveManager, AttachmentsRepository, CURRENT_SHARD_VERSION, CapabilityManager, CaptureKnowledgeInputSchema, CollectionsRepository, CommunitiesRepository, EmbeddingSetsRepository, FallbackRouter, FortemiToolManifest, GetNoteInputSchema, GraphRepository, JOB_CAPABILITIES, JOB_PRIORITIES, JobQueueWorker, LOCAL_ENDPOINTS, LinksRepository, ListNotesInputSchema, ManageArchiveInputSchema, ManageAttachmentsInputSchema, ManageCapabilitiesInputSchema, ManageCollectionsInputSchema, ManageLinksInputSchema, ManageNoteInputSchema, ManageTagsInputSchema, MemoryBlobStore, MigrationRunner, NotesRepository, OpenAICompatibleProvider, PGliteStorageBackend, PGliteStorageBackendFactory, PGliteWorkerClient, PGliteWorkerStorageBackend, PGliteWorkerStorageBackendFactory, ProviderRegistry, SHARD_FORMAT, SearchInputSchema, SearchRepository, SkosRepository, TagsRepository, TransactionProxy, TypedEventBus, VERSION, aiRevisionHandler, aiwgFortemiIndexToCommunityGraph, allMigrations, appendPluginScript, assertAiwgFortemiIndexExport, buildNoteConditions, buildPluginCsp, captureKnowledge, chunkText, classifyError, classifyModel, collectionFromShard, collectionToShard, computeHash, computeSri, conceptTaggingHandler, cosineSimilarity, createAiwgReviewDecisionExport, createBlobStore, createCspReportHandler, createFortemi, createLegacyProvider, createPGliteInstance, createRoutes, defaultStorageBackendFactory, detectCommunities, detectGpuCapabilities, detectInferenceCapabilities, discoverLocalProviders, embeddingFromShard, embeddingGenerationHandler, embeddingSetFromShard, embeddingSetMemberToShard, embeddingSetToShard, embeddingToShard, enqueueFullWorkflow, enqueueJob, enqueueNoteCreationJobs, estimateModelFit, estimateVramMB, estimateVramTier, exportShard, fetchPluginScript, fortemiManifest, generateId, getAiwgFortemiFacets, getEmbedFunction, getFortemiBridge, getFortemiSecretStore, getJobQueueStatus, getLlmFunction, getNote, hasFortemiSecureSecrets, importShard, isPluginScriptAllowed, linkFromShard, linkToShard, linkingHandler, listNotes, manageArchive, manageAttachments, manageCapabilities, manageCollections, manageLinks, manageNote, manageTags, matchRoute, noteFromShard, noteSkosTagToShard, noteToShard, packTarGz, parseCspReport, provenanceEdgeToShard, queryAiwgFortemiIndex, registerLlmCapability, registerSemanticCapability, registerServiceWorker, searchTool, selectLlmModel, setEmbedFunction, setLlmFunction, sha256Hex, skosConceptToShard, skosRelationToShard, skosSchemeToShard, suggestTags, tagsFromShard, tagsToShard, titleGenerationHandler, unpackTarGz, unregisterLlmCapability, unregisterSemanticCapability, validateAiwgFortemiIndexExport, validateChecksums, verifySri };
|
|
6395
6760
|
//# sourceMappingURL=index.js.map
|
|
6396
6761
|
//# sourceMappingURL=index.js.map
|