@fortemi/core 2026.9.6 → 2026.9.7
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 +1 -1
- package/dist/index.d.ts +101 -25
- package/dist/index.js +879 -258
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
- package/schemas/lifecycle-purge/contract.receipt.json +15 -0
- package/schemas/lifecycle-purge/v1.conformance.json +73 -0
package/dist/index.js
CHANGED
|
@@ -6975,6 +6975,61 @@ var migration0033 = {
|
|
|
6975
6975
|
`
|
|
6976
6976
|
};
|
|
6977
6977
|
|
|
6978
|
+
// src/migrations/0034_lifecycle_purge_authority.ts
|
|
6979
|
+
init_geometry_buffer();
|
|
6980
|
+
var migration0034 = {
|
|
6981
|
+
version: 34,
|
|
6982
|
+
name: "0034_lifecycle_purge_authority",
|
|
6983
|
+
sql: `
|
|
6984
|
+
ALTER TABLE deletion_receipt RENAME TO deletion_receipt_legacy_0023;
|
|
6985
|
+
|
|
6986
|
+
CREATE TABLE lifecycle_purge_preview (
|
|
6987
|
+
id TEXT PRIMARY KEY,
|
|
6988
|
+
selector_fingerprint TEXT NOT NULL,
|
|
6989
|
+
selector JSONB NOT NULL,
|
|
6990
|
+
selected_note_ids JSONB NOT NULL,
|
|
6991
|
+
counts JSONB NOT NULL,
|
|
6992
|
+
expires_at TIMESTAMPTZ NOT NULL,
|
|
6993
|
+
consumed_by TEXT
|
|
6994
|
+
);
|
|
6995
|
+
|
|
6996
|
+
CREATE TABLE lifecycle_purge_operation (
|
|
6997
|
+
id TEXT PRIMARY KEY,
|
|
6998
|
+
preview_id TEXT NOT NULL UNIQUE REFERENCES lifecycle_purge_preview(id),
|
|
6999
|
+
selector_fingerprint TEXT NOT NULL,
|
|
7000
|
+
state TEXT NOT NULL CHECK (state IN ('cleanup_pending', 'completed')),
|
|
7001
|
+
counts JSONB NOT NULL,
|
|
7002
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
7003
|
+
completed_at TIMESTAMPTZ
|
|
7004
|
+
);
|
|
7005
|
+
|
|
7006
|
+
CREATE TABLE lifecycle_purge_erasure_target (
|
|
7007
|
+
operation_id TEXT NOT NULL REFERENCES lifecycle_purge_operation(id),
|
|
7008
|
+
note_id TEXT NOT NULL,
|
|
7009
|
+
PRIMARY KEY (operation_id, note_id)
|
|
7010
|
+
);
|
|
7011
|
+
CREATE INDEX idx_lifecycle_purge_erasure_note
|
|
7012
|
+
ON lifecycle_purge_erasure_target(note_id);
|
|
7013
|
+
|
|
7014
|
+
CREATE TABLE lifecycle_purge_blob_cleanup (
|
|
7015
|
+
operation_id TEXT NOT NULL REFERENCES lifecycle_purge_operation(id),
|
|
7016
|
+
blob_id TEXT NOT NULL,
|
|
7017
|
+
content_hash TEXT NOT NULL,
|
|
7018
|
+
completed_at TIMESTAMPTZ,
|
|
7019
|
+
PRIMARY KEY (operation_id, blob_id)
|
|
7020
|
+
);
|
|
7021
|
+
|
|
7022
|
+
CREATE TABLE deletion_receipt (
|
|
7023
|
+
operation_id TEXT PRIMARY KEY REFERENCES lifecycle_purge_operation(id),
|
|
7024
|
+
contract_version TEXT NOT NULL,
|
|
7025
|
+
outcome TEXT NOT NULL CHECK (outcome = 'completed'),
|
|
7026
|
+
counts JSONB NOT NULL,
|
|
7027
|
+
completed_at TIMESTAMPTZ NOT NULL,
|
|
7028
|
+
policy JSONB NOT NULL
|
|
7029
|
+
);
|
|
7030
|
+
`
|
|
7031
|
+
};
|
|
7032
|
+
|
|
6978
7033
|
// src/migrations/index.ts
|
|
6979
7034
|
var allMigrations = [
|
|
6980
7035
|
migration0001,
|
|
@@ -7009,7 +7064,8 @@ var allMigrations = [
|
|
|
7009
7064
|
migration0030,
|
|
7010
7065
|
migration0031,
|
|
7011
7066
|
migration0032,
|
|
7012
|
-
migration0033
|
|
7067
|
+
migration0033,
|
|
7068
|
+
migration0034
|
|
7013
7069
|
];
|
|
7014
7070
|
|
|
7015
7071
|
// src/data-archive.ts
|
|
@@ -7538,6 +7594,13 @@ var NotesRepository = class {
|
|
|
7538
7594
|
*/
|
|
7539
7595
|
async restore(id) {
|
|
7540
7596
|
await this.db.transaction(async (tx) => {
|
|
7597
|
+
const retained = await tx.query(
|
|
7598
|
+
"SELECT 1 FROM lifecycle_purge_erasure_target WHERE note_id = $1 LIMIT 1",
|
|
7599
|
+
[id]
|
|
7600
|
+
);
|
|
7601
|
+
if (retained.rows.length > 0) {
|
|
7602
|
+
throw new Error("Terminally purged notes cannot be restored");
|
|
7603
|
+
}
|
|
7541
7604
|
await tx.query(
|
|
7542
7605
|
`UPDATE note SET deleted_at = NULL, updated_at = now() WHERE id = $1`,
|
|
7543
7606
|
[id]
|
|
@@ -24223,6 +24286,46 @@ function countOutcomes(outcomes) {
|
|
|
24223
24286
|
|
|
24224
24287
|
// src/repositories/lifecycle-purge-repository.ts
|
|
24225
24288
|
init_geometry_buffer();
|
|
24289
|
+
|
|
24290
|
+
// src/purge-selector.ts
|
|
24291
|
+
init_geometry_buffer();
|
|
24292
|
+
var LIFECYCLE_PURGE_MAX_NOTE_IDS = 500;
|
|
24293
|
+
function assertPurgeOpaqueId(value, label) {
|
|
24294
|
+
if (typeof value !== "string" || value.trim() !== value || value.length === 0 || value.length > 200) {
|
|
24295
|
+
throw new Error(`${label} must be a nonempty opaque identifier`);
|
|
24296
|
+
}
|
|
24297
|
+
}
|
|
24298
|
+
function validatePurgeSelector(selector) {
|
|
24299
|
+
const nonempty = (value) => typeof value === "string" && value.trim() === value && value.length > 0;
|
|
24300
|
+
if (!selector.note_ids && !selector.source || selector.note_ids !== void 0 && (!Array.isArray(selector.note_ids) || selector.note_ids.length === 0 || selector.note_ids.length > LIFECYCLE_PURGE_MAX_NOTE_IDS || !selector.note_ids.every(nonempty) || new Set(selector.note_ids).size !== selector.note_ids.length) || selector.source !== void 0 && (!nonempty(selector.source.namespace) || selector.source.namespace.length > 200 || selector.source.external_id !== void 0 && !nonempty(selector.source.external_id)) || selector.source?.external_id !== void 0 && selector.source.external_id.length > 1e3 || selector.tenant_id !== void 0 && !nonempty(selector.tenant_id) || selector.archive_id !== void 0 && selector.archive_id !== null && !nonempty(selector.archive_id)) {
|
|
24301
|
+
throw new Error("Purge selector must target nonempty note_ids or source identity");
|
|
24302
|
+
}
|
|
24303
|
+
}
|
|
24304
|
+
function purgeSelectorHash(selector) {
|
|
24305
|
+
validatePurgeSelector(selector);
|
|
24306
|
+
return computeHash(new TextEncoder().encode(JSON.stringify({
|
|
24307
|
+
selector_version: 2,
|
|
24308
|
+
source_scope_required: selector.tenant_id !== void 0 || selector.archive_id !== void 0 || selector.source !== void 0,
|
|
24309
|
+
tenant_id: selector.tenant_id ?? "default",
|
|
24310
|
+
archive_id: selector.archive_id ?? null,
|
|
24311
|
+
note_ids: [...selector.note_ids ?? []].sort(),
|
|
24312
|
+
source: selector.source ? {
|
|
24313
|
+
namespace: selector.source.namespace,
|
|
24314
|
+
external_id_hash: selector.source.external_id ? computeHash(new TextEncoder().encode(selector.source.external_id)) : null
|
|
24315
|
+
} : null
|
|
24316
|
+
})));
|
|
24317
|
+
}
|
|
24318
|
+
|
|
24319
|
+
// src/repositories/lifecycle-purge-repository.ts
|
|
24320
|
+
var LIFECYCLE_PURGE_CONTRACT_VERSION = "1.0.0";
|
|
24321
|
+
var LIFECYCLE_PURGE_PREVIEW_TTL_SECONDS = 900;
|
|
24322
|
+
var RECEIPT_POLICY = {
|
|
24323
|
+
authority: "Fortemi/fortemi#1092",
|
|
24324
|
+
mode: "terminal_purge",
|
|
24325
|
+
receipt_contains_content: false,
|
|
24326
|
+
backup_disposition: "beyond_use_then_reerase",
|
|
24327
|
+
restore_reerasure: true
|
|
24328
|
+
};
|
|
24226
24329
|
function zeroCounts() {
|
|
24227
24330
|
return {
|
|
24228
24331
|
notes: 0,
|
|
@@ -24237,18 +24340,14 @@ function zeroCounts() {
|
|
|
24237
24340
|
source_identities: 0
|
|
24238
24341
|
};
|
|
24239
24342
|
}
|
|
24240
|
-
function
|
|
24241
|
-
return
|
|
24242
|
-
|
|
24243
|
-
|
|
24244
|
-
|
|
24245
|
-
source: selector.source ? {
|
|
24246
|
-
namespace: selector.source.namespace,
|
|
24247
|
-
external_id_hash: selector.source.external_id ? computeHash(new TextEncoder().encode(selector.source.external_id)) : null
|
|
24248
|
-
} : null
|
|
24249
|
-
})));
|
|
24343
|
+
function asCounts(value) {
|
|
24344
|
+
return { ...zeroCounts(), ...value };
|
|
24345
|
+
}
|
|
24346
|
+
function iso(value) {
|
|
24347
|
+
return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
|
|
24250
24348
|
}
|
|
24251
24349
|
function buildSelectorWhere(selector, startIdx) {
|
|
24350
|
+
validatePurgeSelector(selector);
|
|
24252
24351
|
const clauses = [];
|
|
24253
24352
|
const params = [];
|
|
24254
24353
|
let idx = startIdx;
|
|
@@ -24269,7 +24368,6 @@ function buildSelectorWhere(selector, startIdx) {
|
|
|
24269
24368
|
if (selector.source) params.push(selector.source.namespace);
|
|
24270
24369
|
if (selector.source?.external_id) params.push(selector.source.external_id);
|
|
24271
24370
|
}
|
|
24272
|
-
if (clauses.length === 0) throw new Error("Purge selector must target note_ids or source identity");
|
|
24273
24371
|
return { sql: clauses.join(" AND "), params };
|
|
24274
24372
|
}
|
|
24275
24373
|
async function selectedNoteIds(db, selector) {
|
|
@@ -24280,129 +24378,375 @@ async function selectedNoteIds(db, selector) {
|
|
|
24280
24378
|
);
|
|
24281
24379
|
return result.rows.map((row) => row.id);
|
|
24282
24380
|
}
|
|
24381
|
+
async function countSelected(db, noteIds) {
|
|
24382
|
+
const counts = zeroCounts();
|
|
24383
|
+
if (noteIds.length === 0) return counts;
|
|
24384
|
+
const queries = [
|
|
24385
|
+
"SELECT COUNT(*) AS count FROM note WHERE id = ANY($1)",
|
|
24386
|
+
`SELECT
|
|
24387
|
+
(SELECT COUNT(*) FROM note_revision WHERE note_id = ANY($1))
|
|
24388
|
+
+ (SELECT COUNT(*) FROM note_original_history WHERE note_id = ANY($1)) AS count`,
|
|
24389
|
+
`SELECT
|
|
24390
|
+
(SELECT COUNT(*) FROM link WHERE source_note_id = ANY($1) OR target_note_id = ANY($1))
|
|
24391
|
+
+ (SELECT COUNT(*) FROM link_url_target WHERE source_note_id = ANY($1)) AS count`,
|
|
24392
|
+
`SELECT
|
|
24393
|
+
(SELECT COUNT(*) FROM note_tag WHERE note_id = ANY($1))
|
|
24394
|
+
+ (SELECT COUNT(*) FROM note_skos_tag WHERE note_id = ANY($1)) AS count`,
|
|
24395
|
+
`SELECT
|
|
24396
|
+
(SELECT COUNT(*) FROM embedding WHERE note_id = ANY($1))
|
|
24397
|
+
+ (SELECT COUNT(*) FROM embedding_set_member WHERE note_id = ANY($1)) AS count`,
|
|
24398
|
+
"SELECT COUNT(*) AS count FROM attachment WHERE note_id = ANY($1)",
|
|
24399
|
+
`SELECT COUNT(*) AS count FROM attachment_blob ab
|
|
24400
|
+
WHERE EXISTS (SELECT 1 FROM attachment a
|
|
24401
|
+
WHERE (a.blob_id = ab.id OR a.preview_blob_id = ab.id) AND a.note_id = ANY($1))
|
|
24402
|
+
AND NOT EXISTS (SELECT 1 FROM attachment a
|
|
24403
|
+
WHERE (a.blob_id = ab.id OR a.preview_blob_id = ab.id) AND NOT (a.note_id = ANY($1)))`,
|
|
24404
|
+
"SELECT COUNT(*) AS count FROM graph_edge_artifact WHERE from_note_id = ANY($1) OR to_note_id = ANY($1)",
|
|
24405
|
+
`SELECT
|
|
24406
|
+
(SELECT COUNT(*) FROM provenance_edge
|
|
24407
|
+
WHERE note_id = ANY($1)
|
|
24408
|
+
OR revision_id IN (SELECT id FROM note_revision WHERE note_id = ANY($1)))
|
|
24409
|
+
+ (SELECT COUNT(*) FROM provenance_derivation
|
|
24410
|
+
WHERE source_note_id = ANY($1)
|
|
24411
|
+
OR revision_id IN (SELECT id FROM note_revision WHERE note_id = ANY($1)))
|
|
24412
|
+
+ (SELECT COUNT(*) FROM provenance_record
|
|
24413
|
+
WHERE note_id = ANY($1)
|
|
24414
|
+
OR attachment_id IN (SELECT id FROM attachment WHERE note_id = ANY($1))) AS count`,
|
|
24415
|
+
"SELECT COUNT(*) AS count FROM source_identity WHERE note_id = ANY($1)"
|
|
24416
|
+
];
|
|
24417
|
+
const values = [];
|
|
24418
|
+
for (const sql of queries) {
|
|
24419
|
+
const result = await db.query(sql, [noteIds]);
|
|
24420
|
+
values.push(Number(result.rows[0]?.count ?? 0));
|
|
24421
|
+
}
|
|
24422
|
+
[
|
|
24423
|
+
counts.notes,
|
|
24424
|
+
counts.revisions,
|
|
24425
|
+
counts.links,
|
|
24426
|
+
counts.tags,
|
|
24427
|
+
counts.embeddings,
|
|
24428
|
+
counts.attachments,
|
|
24429
|
+
counts.blobs,
|
|
24430
|
+
counts.graph_edges,
|
|
24431
|
+
counts.provenance_edges,
|
|
24432
|
+
counts.source_identities
|
|
24433
|
+
] = values;
|
|
24434
|
+
return counts;
|
|
24435
|
+
}
|
|
24436
|
+
async function deleteSelected(tx, noteIds, deleteSourceJournals = false) {
|
|
24437
|
+
if (noteIds.length === 0) return [];
|
|
24438
|
+
const params = [noteIds];
|
|
24439
|
+
const blobs = await tx.query(
|
|
24440
|
+
`SELECT ab.id, ab.content_hash FROM attachment_blob ab WHERE EXISTS (
|
|
24441
|
+
SELECT 1 FROM attachment a
|
|
24442
|
+
WHERE (a.blob_id = ab.id OR a.preview_blob_id = ab.id) AND a.note_id = ANY($1))
|
|
24443
|
+
AND NOT EXISTS (SELECT 1 FROM attachment a
|
|
24444
|
+
WHERE (a.blob_id = ab.id OR a.preview_blob_id = ab.id) AND NOT (a.note_id = ANY($1)))`,
|
|
24445
|
+
params
|
|
24446
|
+
);
|
|
24447
|
+
const sources = await tx.query(
|
|
24448
|
+
"SELECT DISTINCT tenant_id, archive_id, namespace FROM source_identity WHERE note_id = ANY($1)",
|
|
24449
|
+
params
|
|
24450
|
+
);
|
|
24451
|
+
await tx.query("DELETE FROM community_assignment WHERE note_id = ANY($1)", params);
|
|
24452
|
+
await tx.query("DELETE FROM graph_edge_artifact WHERE from_note_id = ANY($1) OR to_note_id = ANY($1)", params);
|
|
24453
|
+
await tx.query("DELETE FROM embedding_set_member WHERE note_id = ANY($1)", params);
|
|
24454
|
+
await tx.query("DELETE FROM embedding WHERE note_id = ANY($1)", params);
|
|
24455
|
+
await tx.query("DELETE FROM attachment_embedding WHERE attachment_id IN (SELECT id FROM attachment WHERE note_id = ANY($1))", params);
|
|
24456
|
+
await tx.query("DELETE FROM attachment WHERE note_id = ANY($1)", params);
|
|
24457
|
+
await tx.query(
|
|
24458
|
+
`DELETE FROM attachment_blob ab WHERE ab.id = ANY($1)
|
|
24459
|
+
AND NOT EXISTS (SELECT 1 FROM attachment a WHERE a.blob_id = ab.id OR a.preview_blob_id = ab.id)`,
|
|
24460
|
+
[blobs.rows.map((row) => row.id)]
|
|
24461
|
+
);
|
|
24462
|
+
await tx.query("DELETE FROM source_identity WHERE note_id = ANY($1)", params);
|
|
24463
|
+
if (deleteSourceJournals) {
|
|
24464
|
+
for (const source of sources.rows) {
|
|
24465
|
+
const scope = [source.tenant_id, source.archive_id, source.namespace];
|
|
24466
|
+
const stillOwned = await tx.query(
|
|
24467
|
+
`SELECT 1 FROM source_identity
|
|
24468
|
+
WHERE tenant_id = $1 AND archive_id IS NOT DISTINCT FROM $2 AND namespace = $3 LIMIT 1`,
|
|
24469
|
+
scope
|
|
24470
|
+
);
|
|
24471
|
+
if (stillOwned.rows.length === 0) {
|
|
24472
|
+
await tx.query(
|
|
24473
|
+
"DELETE FROM source_import_batch WHERE tenant_id = $1 AND archive_id IS NOT DISTINCT FROM $2 AND namespace = $3",
|
|
24474
|
+
scope
|
|
24475
|
+
);
|
|
24476
|
+
await tx.query(
|
|
24477
|
+
"DELETE FROM source_import_run WHERE tenant_id = $1 AND archive_id IS NOT DISTINCT FROM $2 AND namespace = $3",
|
|
24478
|
+
scope
|
|
24479
|
+
);
|
|
24480
|
+
}
|
|
24481
|
+
}
|
|
24482
|
+
}
|
|
24483
|
+
await tx.query(
|
|
24484
|
+
`DELETE FROM provenance_edge
|
|
24485
|
+
WHERE note_id = ANY($1)
|
|
24486
|
+
OR revision_id IN (SELECT id FROM note_revision WHERE note_id = ANY($1))`,
|
|
24487
|
+
params
|
|
24488
|
+
);
|
|
24489
|
+
await tx.query("DELETE FROM job_queue WHERE note_id = ANY($1)", params);
|
|
24490
|
+
await tx.query("DELETE FROM collection_note WHERE note_id = ANY($1)", params);
|
|
24491
|
+
await tx.query("DELETE FROM note_skos_tag WHERE note_id = ANY($1)", params);
|
|
24492
|
+
await tx.query("DELETE FROM note_tag WHERE note_id = ANY($1)", params);
|
|
24493
|
+
await tx.query(
|
|
24494
|
+
`DELETE FROM shard_field_presence
|
|
24495
|
+
WHERE (component = 'notes' AND record_id = ANY($1))
|
|
24496
|
+
OR (component = 'links' AND record_id IN (
|
|
24497
|
+
SELECT id FROM link WHERE source_note_id = ANY($1) OR target_note_id = ANY($1)
|
|
24498
|
+
UNION
|
|
24499
|
+
SELECT id FROM link_url_target WHERE source_note_id = ANY($1)
|
|
24500
|
+
))`,
|
|
24501
|
+
params
|
|
24502
|
+
);
|
|
24503
|
+
await tx.query("DELETE FROM link_url_target WHERE source_note_id = ANY($1)", params);
|
|
24504
|
+
await tx.query("DELETE FROM link WHERE source_note_id = ANY($1) OR target_note_id = ANY($1)", params);
|
|
24505
|
+
await tx.query("DELETE FROM note_revision WHERE note_id = ANY($1)", params);
|
|
24506
|
+
await tx.query("DELETE FROM note_revised_current WHERE note_id = ANY($1)", params);
|
|
24507
|
+
await tx.query("DELETE FROM note_original WHERE note_id = ANY($1)", params);
|
|
24508
|
+
await tx.query("DELETE FROM note WHERE id = ANY($1)", params);
|
|
24509
|
+
return blobs.rows;
|
|
24510
|
+
}
|
|
24511
|
+
async function statusFrom(db, operationId) {
|
|
24512
|
+
const result = await db.query(
|
|
24513
|
+
`SELECT o.id, o.state, o.counts, r.counts AS receipt_counts,
|
|
24514
|
+
r.completed_at AS receipt_completed_at, r.policy AS receipt_policy
|
|
24515
|
+
,(SELECT COUNT(*) FROM lifecycle_purge_blob_cleanup b
|
|
24516
|
+
WHERE b.operation_id = o.id AND b.completed_at IS NULL) AS blob_cleanup_pending
|
|
24517
|
+
FROM lifecycle_purge_operation o
|
|
24518
|
+
LEFT JOIN deletion_receipt r ON r.operation_id = o.id
|
|
24519
|
+
WHERE o.id = $1`,
|
|
24520
|
+
[operationId]
|
|
24521
|
+
);
|
|
24522
|
+
const row = result.rows[0];
|
|
24523
|
+
if (!row) return null;
|
|
24524
|
+
const receipt = row.receipt_completed_at === null ? void 0 : {
|
|
24525
|
+
contract_version: LIFECYCLE_PURGE_CONTRACT_VERSION,
|
|
24526
|
+
operation_id: row.id,
|
|
24527
|
+
outcome: "completed",
|
|
24528
|
+
counts: asCounts(row.receipt_counts),
|
|
24529
|
+
completed_at: iso(row.receipt_completed_at),
|
|
24530
|
+
policy: row.receipt_policy ?? RECEIPT_POLICY
|
|
24531
|
+
};
|
|
24532
|
+
return {
|
|
24533
|
+
contract_version: LIFECYCLE_PURGE_CONTRACT_VERSION,
|
|
24534
|
+
operation_id: row.id,
|
|
24535
|
+
outcome: row.state,
|
|
24536
|
+
counts: asCounts(row.counts),
|
|
24537
|
+
blob_cleanup_pending: Number(row.blob_cleanup_pending),
|
|
24538
|
+
search_cleanup_pending: false,
|
|
24539
|
+
...receipt ? { receipt } : {}
|
|
24540
|
+
};
|
|
24541
|
+
}
|
|
24542
|
+
async function finalize(tx, operationId) {
|
|
24543
|
+
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
24544
|
+
await tx.query(
|
|
24545
|
+
`UPDATE lifecycle_purge_operation SET state = 'completed', completed_at = $2 WHERE id = $1`,
|
|
24546
|
+
[operationId, completedAt]
|
|
24547
|
+
);
|
|
24548
|
+
await tx.query(
|
|
24549
|
+
`INSERT INTO deletion_receipt (operation_id, contract_version, outcome, counts, completed_at, policy)
|
|
24550
|
+
SELECT id, $2, 'completed', counts, $3, $4::jsonb FROM lifecycle_purge_operation WHERE id = $1
|
|
24551
|
+
ON CONFLICT (operation_id) DO UPDATE SET outcome = 'completed', completed_at = $3`,
|
|
24552
|
+
[operationId, LIFECYCLE_PURGE_CONTRACT_VERSION, completedAt, JSON.stringify(RECEIPT_POLICY)]
|
|
24553
|
+
);
|
|
24554
|
+
}
|
|
24555
|
+
async function reeraseLifecyclePurgeTargets(tx, candidateNoteIds, trackBlobCleanup = false) {
|
|
24556
|
+
const capability = await tx.query(
|
|
24557
|
+
"SELECT to_regclass('lifecycle_purge_erasure_target')::text AS erasure_target"
|
|
24558
|
+
);
|
|
24559
|
+
if (capability.rows[0]?.erasure_target === null) {
|
|
24560
|
+
return { note_ids: [], operation_ids: [] };
|
|
24561
|
+
}
|
|
24562
|
+
const result = candidateNoteIds ? await tx.query(
|
|
24563
|
+
`SELECT DISTINCT t.note_id, t.operation_id
|
|
24564
|
+
FROM lifecycle_purge_erasure_target t JOIN note n ON n.id = t.note_id
|
|
24565
|
+
WHERE t.note_id = ANY($1) ORDER BY t.note_id`,
|
|
24566
|
+
[candidateNoteIds]
|
|
24567
|
+
) : await tx.query(
|
|
24568
|
+
`SELECT DISTINCT t.note_id, t.operation_id
|
|
24569
|
+
FROM lifecycle_purge_erasure_target t JOIN note n ON n.id = t.note_id
|
|
24570
|
+
ORDER BY t.note_id`
|
|
24571
|
+
);
|
|
24572
|
+
if (result.rows.length === 0) return { note_ids: [], operation_ids: [] };
|
|
24573
|
+
const noteIds = [...new Set(result.rows.map((row) => row.note_id))];
|
|
24574
|
+
const operationIds = [...new Set(result.rows.map((row) => row.operation_id))];
|
|
24575
|
+
await tx.query("UPDATE lifecycle_purge_operation SET state = 'cleanup_pending', completed_at = NULL WHERE id = ANY($1)", [operationIds]);
|
|
24576
|
+
await tx.query("DELETE FROM deletion_receipt WHERE operation_id = ANY($1)", [operationIds]);
|
|
24577
|
+
const blobs = await deleteSelected(tx, noteIds);
|
|
24578
|
+
for (const operationId of operationIds) {
|
|
24579
|
+
for (const blob of trackBlobCleanup ? blobs : []) {
|
|
24580
|
+
await tx.query(
|
|
24581
|
+
`INSERT INTO lifecycle_purge_blob_cleanup
|
|
24582
|
+
(operation_id, blob_id, content_hash, completed_at)
|
|
24583
|
+
VALUES ($1, $2, $3, NULL)
|
|
24584
|
+
ON CONFLICT (operation_id, blob_id)
|
|
24585
|
+
DO UPDATE SET content_hash = $3, completed_at = NULL`,
|
|
24586
|
+
[operationId, blob.id, blob.content_hash]
|
|
24587
|
+
);
|
|
24588
|
+
}
|
|
24589
|
+
if (!trackBlobCleanup || blobs.length === 0) await finalize(tx, operationId);
|
|
24590
|
+
}
|
|
24591
|
+
return { note_ids: noteIds, operation_ids: operationIds };
|
|
24592
|
+
}
|
|
24283
24593
|
var LifecyclePurgeRepository = class {
|
|
24284
|
-
constructor(db, events) {
|
|
24594
|
+
constructor(db, events, blobStore) {
|
|
24285
24595
|
this.db = db;
|
|
24286
24596
|
this.events = events;
|
|
24597
|
+
this.blobStore = blobStore;
|
|
24287
24598
|
}
|
|
24288
24599
|
async preview(selector) {
|
|
24289
|
-
|
|
24290
|
-
return
|
|
24291
|
-
|
|
24292
|
-
async purge(selector, operationKey) {
|
|
24293
|
-
const existing = await this.db.query(
|
|
24294
|
-
`SELECT id, operation_key, tenant_id, archive_id, selector_hash, outcome, counts, completed_at, policy
|
|
24295
|
-
FROM deletion_receipt
|
|
24296
|
-
WHERE operation_key = $1`,
|
|
24297
|
-
[operationKey]
|
|
24298
|
-
);
|
|
24299
|
-
if (existing.rows[0]) return existing.rows[0];
|
|
24300
|
-
const hash = selectorHash(selector);
|
|
24301
|
-
let receipt;
|
|
24302
|
-
await this.db.transaction(async (tx) => {
|
|
24600
|
+
validatePurgeSelector(selector);
|
|
24601
|
+
return this.db.transaction(async (tx) => {
|
|
24602
|
+
await tx.query("DELETE FROM lifecycle_purge_preview WHERE consumed_by IS NULL AND expires_at <= now()");
|
|
24303
24603
|
const noteIds = await selectedNoteIds(tx, selector);
|
|
24304
|
-
const counts = await
|
|
24305
|
-
|
|
24306
|
-
|
|
24307
|
-
|
|
24308
|
-
|
|
24309
|
-
|
|
24310
|
-
|
|
24311
|
-
|
|
24312
|
-
|
|
24313
|
-
|
|
24314
|
-
|
|
24315
|
-
|
|
24316
|
-
|
|
24317
|
-
|
|
24318
|
-
|
|
24604
|
+
const counts = await countSelected(tx, noteIds);
|
|
24605
|
+
const previewId = generateId();
|
|
24606
|
+
const expiresAt = new Date(Date.now() + LIFECYCLE_PURGE_PREVIEW_TTL_SECONDS * 1e3).toISOString();
|
|
24607
|
+
await tx.query(
|
|
24608
|
+
`INSERT INTO lifecycle_purge_preview
|
|
24609
|
+
(id, selector_fingerprint, selector, selected_note_ids, counts, expires_at, consumed_by)
|
|
24610
|
+
VALUES ($1, $2, $3::jsonb, $4::jsonb, $5::jsonb, $6, NULL)`,
|
|
24611
|
+
[previewId, purgeSelectorHash(selector), JSON.stringify(selector), JSON.stringify(noteIds), JSON.stringify(counts), expiresAt]
|
|
24612
|
+
);
|
|
24613
|
+
return { contract_version: LIFECYCLE_PURGE_CONTRACT_VERSION, preview_id: previewId, counts, expires_at: expiresAt };
|
|
24614
|
+
});
|
|
24615
|
+
}
|
|
24616
|
+
async begin(request) {
|
|
24617
|
+
assertPurgeOpaqueId(request.operation_id, "operation_id");
|
|
24618
|
+
assertPurgeOpaqueId(request.preview_id, "preview_id");
|
|
24619
|
+
let created = false;
|
|
24620
|
+
const status = await this.db.transaction(async (tx) => {
|
|
24621
|
+
const existing = await tx.query("SELECT preview_id FROM lifecycle_purge_operation WHERE id = $1", [request.operation_id]);
|
|
24622
|
+
if (existing.rows[0]) {
|
|
24623
|
+
if (existing.rows[0].preview_id !== request.preview_id) throw new Error("Purge operation ID was already used for another preview");
|
|
24624
|
+
return await statusFrom(tx, request.operation_id);
|
|
24625
|
+
}
|
|
24626
|
+
const previewResult = await tx.query(
|
|
24627
|
+
`SELECT selector_fingerprint, selector, selected_note_ids, counts, expires_at, consumed_by
|
|
24628
|
+
FROM lifecycle_purge_preview WHERE id = $1`,
|
|
24629
|
+
[request.preview_id]
|
|
24630
|
+
);
|
|
24631
|
+
const preview = previewResult.rows[0];
|
|
24632
|
+
if (!preview) throw new Error("Lifecycle purge preview was not found");
|
|
24633
|
+
if (preview.consumed_by !== null) throw new Error("Lifecycle purge preview was already consumed");
|
|
24634
|
+
if (new Date(preview.expires_at).getTime() <= Date.now()) throw new Error("Lifecycle purge preview expired; create a new preview");
|
|
24635
|
+
validatePurgeSelector(preview.selector);
|
|
24636
|
+
if (purgeSelectorHash(preview.selector) !== preview.selector_fingerprint) throw new Error("Lifecycle purge preview integrity check failed");
|
|
24637
|
+
const currentIds = await selectedNoteIds(tx, preview.selector);
|
|
24638
|
+
const currentCounts = await countSelected(tx, currentIds);
|
|
24639
|
+
if (JSON.stringify(currentIds) !== JSON.stringify(preview.selected_note_ids) || JSON.stringify(currentCounts) !== JSON.stringify(asCounts(preview.counts))) {
|
|
24640
|
+
throw new Error("Lifecycle purge preview is stale; create a new preview");
|
|
24641
|
+
}
|
|
24642
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
24643
|
+
await tx.query(
|
|
24644
|
+
`INSERT INTO lifecycle_purge_operation
|
|
24645
|
+
(id, preview_id, selector_fingerprint, state, counts, created_at)
|
|
24646
|
+
VALUES ($1, $2, $3, 'cleanup_pending', $4::jsonb, $5)`,
|
|
24647
|
+
[request.operation_id, request.preview_id, preview.selector_fingerprint, JSON.stringify(currentCounts), now2]
|
|
24648
|
+
);
|
|
24649
|
+
for (const noteId of currentIds) {
|
|
24650
|
+
await tx.query(
|
|
24651
|
+
"INSERT INTO lifecycle_purge_erasure_target (operation_id, note_id) VALUES ($1, $2)",
|
|
24652
|
+
[request.operation_id, noteId]
|
|
24653
|
+
);
|
|
24654
|
+
}
|
|
24655
|
+
const blobs = await deleteSelected(
|
|
24656
|
+
tx,
|
|
24657
|
+
currentIds,
|
|
24658
|
+
preview.selector.source !== void 0 && preview.selector.source.external_id === void 0
|
|
24659
|
+
);
|
|
24660
|
+
if (this.blobStore) {
|
|
24661
|
+
for (const blob of blobs) {
|
|
24662
|
+
await tx.query(
|
|
24663
|
+
`INSERT INTO lifecycle_purge_blob_cleanup
|
|
24664
|
+
(operation_id, blob_id, content_hash, completed_at)
|
|
24665
|
+
VALUES ($1, $2, $3, NULL)`,
|
|
24666
|
+
[request.operation_id, blob.id, blob.content_hash]
|
|
24667
|
+
);
|
|
24319
24668
|
}
|
|
24320
|
-
}
|
|
24669
|
+
}
|
|
24321
24670
|
await tx.query(
|
|
24322
|
-
`
|
|
24323
|
-
|
|
24324
|
-
|
|
24325
|
-
[
|
|
24326
|
-
receipt.id,
|
|
24327
|
-
receipt.operation_key,
|
|
24328
|
-
receipt.tenant_id,
|
|
24329
|
-
receipt.archive_id,
|
|
24330
|
-
receipt.selector_hash,
|
|
24331
|
-
JSON.stringify(receipt.counts),
|
|
24332
|
-
receipt.completed_at,
|
|
24333
|
-
JSON.stringify(receipt.policy)
|
|
24334
|
-
]
|
|
24671
|
+
`UPDATE lifecycle_purge_preview
|
|
24672
|
+
SET consumed_by = $2, selector = '{}'::jsonb, selected_note_ids = '[]'::jsonb
|
|
24673
|
+
WHERE id = $1`,
|
|
24674
|
+
[request.preview_id, request.operation_id]
|
|
24335
24675
|
);
|
|
24676
|
+
if (!this.blobStore || blobs.length === 0) await finalize(tx, request.operation_id);
|
|
24677
|
+
created = true;
|
|
24678
|
+
return await statusFrom(tx, request.operation_id);
|
|
24336
24679
|
});
|
|
24337
|
-
|
|
24338
|
-
|
|
24339
|
-
|
|
24340
|
-
|
|
24341
|
-
|
|
24342
|
-
|
|
24343
|
-
|
|
24344
|
-
|
|
24345
|
-
|
|
24346
|
-
|
|
24347
|
-
|
|
24348
|
-
|
|
24349
|
-
|
|
24350
|
-
|
|
24351
|
-
|
|
24352
|
-
|
|
24353
|
-
|
|
24354
|
-
|
|
24355
|
-
|
|
24356
|
-
|
|
24357
|
-
|
|
24358
|
-
|
|
24359
|
-
|
|
24360
|
-
|
|
24361
|
-
|
|
24362
|
-
|
|
24363
|
-
|
|
24364
|
-
|
|
24365
|
-
|
|
24366
|
-
|
|
24367
|
-
|
|
24368
|
-
|
|
24369
|
-
|
|
24370
|
-
|
|
24371
|
-
|
|
24372
|
-
|
|
24373
|
-
|
|
24374
|
-
|
|
24375
|
-
|
|
24376
|
-
|
|
24377
|
-
|
|
24378
|
-
|
|
24379
|
-
|
|
24380
|
-
|
|
24680
|
+
if (created && status.outcome === "completed") {
|
|
24681
|
+
this.events?.emit("purge.completed", {
|
|
24682
|
+
receiptId: status.operation_id,
|
|
24683
|
+
counts: status.counts
|
|
24684
|
+
});
|
|
24685
|
+
}
|
|
24686
|
+
return status;
|
|
24687
|
+
}
|
|
24688
|
+
async status(operationId) {
|
|
24689
|
+
assertPurgeOpaqueId(operationId, "operation_id");
|
|
24690
|
+
return statusFrom(this.db, operationId);
|
|
24691
|
+
}
|
|
24692
|
+
async resume(operationId) {
|
|
24693
|
+
assertPurgeOpaqueId(operationId, "operation_id");
|
|
24694
|
+
const initial = await this.status(operationId);
|
|
24695
|
+
if (!initial) throw new Error("Lifecycle purge operation was not found");
|
|
24696
|
+
if (initial.outcome === "completed") return initial;
|
|
24697
|
+
if (!this.blobStore) throw new Error("Lifecycle purge byte cleanup requires the configured BlobStore");
|
|
24698
|
+
const pending = await this.db.query(
|
|
24699
|
+
`SELECT blob_id, content_hash FROM lifecycle_purge_blob_cleanup
|
|
24700
|
+
WHERE operation_id = $1 AND completed_at IS NULL ORDER BY blob_id`,
|
|
24701
|
+
[operationId]
|
|
24702
|
+
);
|
|
24703
|
+
for (const blob of pending.rows) {
|
|
24704
|
+
if (!this.blobStore.delete) throw new Error("Lifecycle purge requires BlobStore.delete()");
|
|
24705
|
+
await this.blobStore.delete(blob.content_hash);
|
|
24706
|
+
await this.db.query(
|
|
24707
|
+
`UPDATE lifecycle_purge_blob_cleanup SET completed_at = $3
|
|
24708
|
+
WHERE operation_id = $1 AND blob_id = $2`,
|
|
24709
|
+
[operationId, blob.blob_id, (/* @__PURE__ */ new Date()).toISOString()]
|
|
24710
|
+
);
|
|
24711
|
+
}
|
|
24712
|
+
await this.db.transaction(async (tx) => {
|
|
24713
|
+
const remaining = await tx.query(
|
|
24714
|
+
"SELECT 1 FROM lifecycle_purge_blob_cleanup WHERE operation_id = $1 AND completed_at IS NULL LIMIT 1",
|
|
24715
|
+
[operationId]
|
|
24716
|
+
);
|
|
24717
|
+
if (remaining.rows.length === 0) await finalize(tx, operationId);
|
|
24718
|
+
});
|
|
24719
|
+
const completed = await this.status(operationId);
|
|
24720
|
+
if (completed.outcome === "completed") {
|
|
24721
|
+
this.events?.emit("purge.completed", {
|
|
24722
|
+
receiptId: completed.operation_id,
|
|
24723
|
+
counts: completed.counts
|
|
24724
|
+
});
|
|
24725
|
+
}
|
|
24726
|
+
return completed;
|
|
24381
24727
|
}
|
|
24382
|
-
|
|
24383
|
-
|
|
24384
|
-
|
|
24385
|
-
|
|
24386
|
-
|
|
24387
|
-
|
|
24388
|
-
|
|
24389
|
-
|
|
24390
|
-
|
|
24391
|
-
|
|
24392
|
-
|
|
24393
|
-
|
|
24394
|
-
|
|
24395
|
-
|
|
24396
|
-
|
|
24397
|
-
|
|
24398
|
-
|
|
24399
|
-
|
|
24400
|
-
|
|
24401
|
-
|
|
24402
|
-
|
|
24403
|
-
|
|
24404
|
-
await tx.query("DELETE FROM shard_field_presence WHERE component = $2 AND record_id = ANY($1)", [noteIds, "notes"]);
|
|
24405
|
-
await tx.query("DELETE FROM note WHERE id = ANY($1)", params);
|
|
24728
|
+
/** Compatibility convenience: create and consume a fresh preview. */
|
|
24729
|
+
async purge(selector, operationId) {
|
|
24730
|
+
validatePurgeSelector(selector);
|
|
24731
|
+
assertPurgeOpaqueId(operationId, "operation_id");
|
|
24732
|
+
const existing = await this.db.query(
|
|
24733
|
+
"SELECT selector_fingerprint FROM lifecycle_purge_operation WHERE id = $1",
|
|
24734
|
+
[operationId]
|
|
24735
|
+
);
|
|
24736
|
+
if (existing.rows[0]) {
|
|
24737
|
+
if (existing.rows[0].selector_fingerprint !== purgeSelectorHash(selector)) {
|
|
24738
|
+
throw new Error("Purge operation key conflicts with selector");
|
|
24739
|
+
}
|
|
24740
|
+
let replay = await this.status(operationId);
|
|
24741
|
+
if (replay.outcome === "cleanup_pending") replay = await this.resume(operationId);
|
|
24742
|
+
if (!replay.receipt) throw new Error("Lifecycle purge did not produce a terminal receipt");
|
|
24743
|
+
return replay.receipt;
|
|
24744
|
+
}
|
|
24745
|
+
const preview = await this.preview(selector);
|
|
24746
|
+
let status = await this.begin({ operation_id: operationId, preview_id: preview.preview_id });
|
|
24747
|
+
if (status.outcome === "cleanup_pending") status = await this.resume(operationId);
|
|
24748
|
+
if (!status.receipt) throw new Error("Lifecycle purge did not produce a terminal receipt");
|
|
24749
|
+
return status.receipt;
|
|
24406
24750
|
}
|
|
24407
24751
|
};
|
|
24408
24752
|
|
|
@@ -24898,7 +25242,7 @@ function parseObject(value) {
|
|
|
24898
25242
|
if (typeof value === "string") return JSON.parse(value);
|
|
24899
25243
|
return value;
|
|
24900
25244
|
}
|
|
24901
|
-
function
|
|
25245
|
+
function iso2(value) {
|
|
24902
25246
|
if (!value) return void 0;
|
|
24903
25247
|
return value instanceof Date ? value.toISOString() : value;
|
|
24904
25248
|
}
|
|
@@ -24990,7 +25334,7 @@ var CommunitiesRepository = class {
|
|
|
24990
25334
|
graphSourceId: row.graph_source_id,
|
|
24991
25335
|
searchQuery: parameters?.filters?.query,
|
|
24992
25336
|
filters: parameters?.filters ?? void 0,
|
|
24993
|
-
createdAt:
|
|
25337
|
+
createdAt: iso2(row.created_at),
|
|
24994
25338
|
freshness: freshness?.status ?? "unknown"
|
|
24995
25339
|
};
|
|
24996
25340
|
});
|
|
@@ -39140,7 +39484,7 @@ function jsonObject3(value) {
|
|
|
39140
39484
|
if (typeof value === "string") return JSON.parse(value);
|
|
39141
39485
|
return value;
|
|
39142
39486
|
}
|
|
39143
|
-
function
|
|
39487
|
+
function iso3(value) {
|
|
39144
39488
|
return value instanceof Date ? value.toISOString() : value;
|
|
39145
39489
|
}
|
|
39146
39490
|
function coreV1OptionErrors(options) {
|
|
@@ -39473,8 +39817,8 @@ async function exportShardBytes(db, options, mode) {
|
|
|
39473
39817
|
...row.extraction_status !== null ? { extraction_status: row.extraction_status } : {},
|
|
39474
39818
|
...row.extraction_reason !== null ? { reason: row.extraction_reason } : {},
|
|
39475
39819
|
...!coreV1 ? {
|
|
39476
|
-
created_at:
|
|
39477
|
-
deleted_at: row.deleted_at ?
|
|
39820
|
+
created_at: iso3(row.created_at),
|
|
39821
|
+
deleted_at: row.deleted_at ? iso3(row.deleted_at) : null
|
|
39478
39822
|
} : {},
|
|
39479
39823
|
attachment: {
|
|
39480
39824
|
// `path` is the display filename per the binary-attachment projection
|
|
@@ -39755,7 +40099,7 @@ async function exportShardBytes(db, options, mode) {
|
|
|
39755
40099
|
parameters: jsonObject3(row.parameters_json),
|
|
39756
40100
|
input_hash: row.input_hash,
|
|
39757
40101
|
freshness: jsonObject3(row.freshness_json) ?? { status: "unknown" },
|
|
39758
|
-
created_at:
|
|
40102
|
+
created_at: iso3(row.created_at)
|
|
39759
40103
|
}));
|
|
39760
40104
|
files.set("graph_sources.json", encoder5.encode(JSON.stringify(shardGraphSources)));
|
|
39761
40105
|
components4.push("graph_sources");
|
|
@@ -39807,7 +40151,7 @@ async function exportShardBytes(db, options, mode) {
|
|
|
39807
40151
|
representative_note_ids: community.representative_note_ids ?? [],
|
|
39808
40152
|
metadata: jsonObject3(community.metadata_json)
|
|
39809
40153
|
})),
|
|
39810
|
-
created_at:
|
|
40154
|
+
created_at: iso3(row.created_at)
|
|
39811
40155
|
}));
|
|
39812
40156
|
files.set("communities.json", encoder5.encode(JSON.stringify(shardCommunitySets)));
|
|
39813
40157
|
components4.push("communities");
|
|
@@ -39840,7 +40184,7 @@ async function exportShardBytes(db, options, mode) {
|
|
|
39840
40184
|
if (!tagsByName.has(name)) {
|
|
39841
40185
|
tagsByName.set(name, {
|
|
39842
40186
|
name,
|
|
39843
|
-
created_at:
|
|
40187
|
+
created_at: iso3(template.created_at)
|
|
39844
40188
|
});
|
|
39845
40189
|
}
|
|
39846
40190
|
}
|
|
@@ -40281,6 +40625,7 @@ async function importNativeFullV1(db, data, options = {}) {
|
|
|
40281
40625
|
}
|
|
40282
40626
|
const sidecars = collectSidecarBlobs(files);
|
|
40283
40627
|
let promotion;
|
|
40628
|
+
const reerasedOperationIds = [];
|
|
40284
40629
|
try {
|
|
40285
40630
|
let appliedCounts = {};
|
|
40286
40631
|
await db.transaction(async (tx) => {
|
|
@@ -40312,8 +40657,29 @@ async function importNativeFullV1(db, data, options = {}) {
|
|
|
40312
40657
|
});
|
|
40313
40658
|
appliedCounts = stateCounts(selected);
|
|
40314
40659
|
await writeNativeLineage(tx, selected, manifest, components3.every((component) => state[component].length === 0));
|
|
40660
|
+
const reerased = await reeraseLifecyclePurgeTargets(
|
|
40661
|
+
tx,
|
|
40662
|
+
selected.notes.map((note) => note.id),
|
|
40663
|
+
Boolean(options.blobStore)
|
|
40664
|
+
);
|
|
40665
|
+
reerasedOperationIds.push(...reerased.operation_ids);
|
|
40666
|
+
if (reerased.note_ids.length > 0) {
|
|
40667
|
+
warnings.push(
|
|
40668
|
+
`${reerased.note_ids.length} retained lifecycle purge target(s) were re-erased before indexing.`
|
|
40669
|
+
);
|
|
40670
|
+
}
|
|
40315
40671
|
await options.onProgress?.({ phase: "index", done: 0, total: 1 });
|
|
40316
40672
|
});
|
|
40673
|
+
if (options.blobStore) {
|
|
40674
|
+
const lifecycle = new LifecyclePurgeRepository(db, void 0, options.blobStore);
|
|
40675
|
+
for (const operationId of reerasedOperationIds) {
|
|
40676
|
+
try {
|
|
40677
|
+
await lifecycle.resume(operationId);
|
|
40678
|
+
} catch {
|
|
40679
|
+
warnings.push("A restored purge target was re-erased, but byte cleanup remains pending.");
|
|
40680
|
+
}
|
|
40681
|
+
}
|
|
40682
|
+
}
|
|
40317
40683
|
try {
|
|
40318
40684
|
await options.onProgress?.({ phase: "index", done: 1, total: 1 });
|
|
40319
40685
|
} catch {
|
|
@@ -40453,6 +40819,7 @@ async function importShard(db, data, options) {
|
|
|
40453
40819
|
});
|
|
40454
40820
|
const inputData = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
|
|
40455
40821
|
let files;
|
|
40822
|
+
const reerasedOperationIds = [];
|
|
40456
40823
|
try {
|
|
40457
40824
|
await report?.({ phase: "unpack", done: 0, total: 1 });
|
|
40458
40825
|
files = unpackTarGz(inputData);
|
|
@@ -41611,6 +41978,17 @@ async function importShard(db, data, options) {
|
|
|
41611
41978
|
report?.({ phase: "embedding_set_members", done: index + 1, total: parsedEmbMembers.length });
|
|
41612
41979
|
await maybeYield2(index + 1, batchSize);
|
|
41613
41980
|
}
|
|
41981
|
+
const reerased = await reeraseLifecyclePurgeTargets(
|
|
41982
|
+
tx,
|
|
41983
|
+
parsedNotes.map((note) => note.id),
|
|
41984
|
+
Boolean(options?.blobStore)
|
|
41985
|
+
);
|
|
41986
|
+
reerasedOperationIds.push(...reerased.operation_ids);
|
|
41987
|
+
if (reerased.note_ids.length > 0) {
|
|
41988
|
+
warnings.push(
|
|
41989
|
+
`${reerased.note_ids.length} retained lifecycle purge target(s) were re-erased before indexing.`
|
|
41990
|
+
);
|
|
41991
|
+
}
|
|
41614
41992
|
});
|
|
41615
41993
|
report?.({ phase: "index", done: 1, total: 1 });
|
|
41616
41994
|
} catch (err) {
|
|
@@ -41633,6 +42011,16 @@ async function importShard(db, data, options) {
|
|
|
41633
42011
|
capability_report: capabilityReport
|
|
41634
42012
|
};
|
|
41635
42013
|
}
|
|
42014
|
+
if (options?.blobStore) {
|
|
42015
|
+
const lifecycle = new LifecyclePurgeRepository(db, void 0, options.blobStore);
|
|
42016
|
+
for (const operationId of reerasedOperationIds) {
|
|
42017
|
+
try {
|
|
42018
|
+
await lifecycle.resume(operationId);
|
|
42019
|
+
} catch {
|
|
42020
|
+
warnings.push("A restored purge target was re-erased, but byte cleanup remains pending.");
|
|
42021
|
+
}
|
|
42022
|
+
}
|
|
42023
|
+
}
|
|
41636
42024
|
return {
|
|
41637
42025
|
success: true,
|
|
41638
42026
|
counts,
|
|
@@ -46306,7 +46694,11 @@ var RECORD_COLLECTIONS = [
|
|
|
46306
46694
|
"source_identity",
|
|
46307
46695
|
"source_import_run",
|
|
46308
46696
|
"source_import_batch",
|
|
46309
|
-
"deletion_receipt"
|
|
46697
|
+
"deletion_receipt",
|
|
46698
|
+
"lifecycle_purge_preview",
|
|
46699
|
+
"lifecycle_purge_operation",
|
|
46700
|
+
"lifecycle_purge_erasure_target",
|
|
46701
|
+
"lifecycle_purge_blob_cleanup"
|
|
46310
46702
|
];
|
|
46311
46703
|
var RECORD_STORE_CAPABILITIES = {
|
|
46312
46704
|
crud: true,
|
|
@@ -46315,6 +46707,7 @@ var RECORD_STORE_CAPABILITIES = {
|
|
|
46315
46707
|
boundedTextScan: true,
|
|
46316
46708
|
sourceAddressedUpsert: true,
|
|
46317
46709
|
deletionReceipts: true,
|
|
46710
|
+
purgeJournalCompaction: true,
|
|
46318
46711
|
typedMetadataPredicates: false,
|
|
46319
46712
|
evidenceLocators: false,
|
|
46320
46713
|
fullTextSearch: false,
|
|
@@ -46365,6 +46758,12 @@ var MemoryRecordStore = class {
|
|
|
46365
46758
|
return (await this.applyBatch([{ op: "delete", collection, id }]))[0];
|
|
46366
46759
|
}
|
|
46367
46760
|
async applyBatch(mutations) {
|
|
46761
|
+
return this.commitBatch(mutations, []);
|
|
46762
|
+
}
|
|
46763
|
+
async applyPurgeBatch(mutations, scrubJournal) {
|
|
46764
|
+
return this.commitBatch(mutations, scrubJournal);
|
|
46765
|
+
}
|
|
46766
|
+
async commitBatch(mutations, scrubJournal) {
|
|
46368
46767
|
if (mutations.length === 0) return [];
|
|
46369
46768
|
const stagedCollections = /* @__PURE__ */ new Map();
|
|
46370
46769
|
for (const [collection, records] of this.collections) {
|
|
@@ -46373,7 +46772,8 @@ var MemoryRecordStore = class {
|
|
|
46373
46772
|
new Map([...records].map(([id, record]) => [id, structuredClone(record)]))
|
|
46374
46773
|
);
|
|
46375
46774
|
}
|
|
46376
|
-
const
|
|
46775
|
+
const scrub = new Set(scrubJournal.map((key3) => `${key3.collection}\0${key3.id}`));
|
|
46776
|
+
const stagedJournal = structuredClone(this.journal).filter((entry) => !scrub.has(`${entry.collection}\0${entry.id}`));
|
|
46377
46777
|
let stagedSeq = this.seq;
|
|
46378
46778
|
const entries = [];
|
|
46379
46779
|
const table = (collection) => {
|
|
@@ -46430,10 +46830,10 @@ var MemoryRecordStore = class {
|
|
|
46430
46830
|
|
|
46431
46831
|
// src/records/idb-record-store.ts
|
|
46432
46832
|
init_geometry_buffer();
|
|
46433
|
-
var DB_VERSION =
|
|
46833
|
+
var DB_VERSION = 4;
|
|
46434
46834
|
var JOURNAL_STORE = "journal";
|
|
46435
46835
|
var META_STORE = "meta";
|
|
46436
|
-
var RECORD_SCHEMA_VERSION =
|
|
46836
|
+
var RECORD_SCHEMA_VERSION = 4;
|
|
46437
46837
|
function requestToPromise(request) {
|
|
46438
46838
|
return new Promise((resolve, reject) => {
|
|
46439
46839
|
request.onsuccess = () => resolve(request.result);
|
|
@@ -46517,6 +46917,12 @@ var IdbRecordStore = class _IdbRecordStore {
|
|
|
46517
46917
|
return (await this.applyBatch([{ op: "delete", collection, id }]))[0];
|
|
46518
46918
|
}
|
|
46519
46919
|
async applyBatch(mutations) {
|
|
46920
|
+
return this.commitBatch(mutations, []);
|
|
46921
|
+
}
|
|
46922
|
+
async applyPurgeBatch(mutations, scrubJournal) {
|
|
46923
|
+
return this.commitBatch(mutations, scrubJournal);
|
|
46924
|
+
}
|
|
46925
|
+
async commitBatch(mutations, scrubJournal) {
|
|
46520
46926
|
if (mutations.length === 0) return [];
|
|
46521
46927
|
const normalizedMutations = mutations.map(normalizeRecordMutation);
|
|
46522
46928
|
const collections = [...new Set(normalizedMutations.map((mutation) => mutation.collection))];
|
|
@@ -46524,6 +46930,23 @@ var IdbRecordStore = class _IdbRecordStore {
|
|
|
46524
46930
|
const completed = transactionComplete(tx);
|
|
46525
46931
|
const pendingEntries = [];
|
|
46526
46932
|
try {
|
|
46933
|
+
if (scrubJournal.length > 0) {
|
|
46934
|
+
const scrub = new Set(scrubJournal.map((key3) => `${key3.collection}\0${key3.id}`));
|
|
46935
|
+
await new Promise((resolve, reject) => {
|
|
46936
|
+
const request = tx.objectStore(JOURNAL_STORE).openCursor();
|
|
46937
|
+
request.onerror = () => reject(request.error);
|
|
46938
|
+
request.onsuccess = () => {
|
|
46939
|
+
const cursor = request.result;
|
|
46940
|
+
if (!cursor) {
|
|
46941
|
+
resolve();
|
|
46942
|
+
return;
|
|
46943
|
+
}
|
|
46944
|
+
const entry = cursor.value;
|
|
46945
|
+
if (scrub.has(`${entry.collection}\0${entry.id}`)) cursor.delete();
|
|
46946
|
+
cursor.continue();
|
|
46947
|
+
};
|
|
46948
|
+
});
|
|
46949
|
+
}
|
|
46527
46950
|
for (const mutation of normalizedMutations) {
|
|
46528
46951
|
const records = tx.objectStore(mutation.collection);
|
|
46529
46952
|
const pending = mutation.op === "put" ? {
|
|
@@ -46689,6 +47112,8 @@ var CanonicalNotesRepository = class {
|
|
|
46689
47112
|
await this.store.put("note", { ...note, deleted_at: nowIso(), updated_at: nowIso() });
|
|
46690
47113
|
}
|
|
46691
47114
|
async restore(noteId) {
|
|
47115
|
+
const retained = (await this.store.list("lifecycle_purge_erasure_target")).some((target) => target.note_id === noteId);
|
|
47116
|
+
if (retained) throw new Error("Terminally purged notes cannot be restored");
|
|
46692
47117
|
const note = await this.store.get("note", noteId);
|
|
46693
47118
|
if (!note) throw new Error(`Note not found: ${noteId}`);
|
|
46694
47119
|
await this.store.put("note", { ...note, deleted_at: null, updated_at: nowIso() });
|
|
@@ -46922,11 +47347,12 @@ async function projectAttachments(db, store) {
|
|
|
46922
47347
|
for (const att of attachments) {
|
|
46923
47348
|
await db.query(
|
|
46924
47349
|
`INSERT INTO attachment (
|
|
46925
|
-
id, note_id, blob_id, document_type_id, mime_type, extracted_text,
|
|
47350
|
+
id, note_id, blob_id, preview_blob_id, document_type_id, mime_type, extracted_text,
|
|
46926
47351
|
filename, display_name, position, status, created_at, deleted_at
|
|
46927
47352
|
)
|
|
46928
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
|
47353
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
|
46929
47354
|
ON CONFLICT (id) DO UPDATE SET
|
|
47355
|
+
preview_blob_id = EXCLUDED.preview_blob_id,
|
|
46930
47356
|
mime_type = EXCLUDED.mime_type,
|
|
46931
47357
|
extracted_text = EXCLUDED.extracted_text,
|
|
46932
47358
|
filename = EXCLUDED.filename,
|
|
@@ -46938,6 +47364,7 @@ async function projectAttachments(db, store) {
|
|
|
46938
47364
|
att.id,
|
|
46939
47365
|
att.note_id,
|
|
46940
47366
|
att.blob_id,
|
|
47367
|
+
att.preview_blob_id ?? null,
|
|
46941
47368
|
att.document_type_id,
|
|
46942
47369
|
att.mime_type,
|
|
46943
47370
|
att.extracted_text,
|
|
@@ -46955,7 +47382,7 @@ async function projectAttachments(db, store) {
|
|
|
46955
47382
|
`UPDATE attachment_blob ab
|
|
46956
47383
|
SET reference_count = (
|
|
46957
47384
|
SELECT COUNT(*) FROM attachment a
|
|
46958
|
-
WHERE a.blob_id = ab.id AND a.deleted_at IS NULL
|
|
47385
|
+
WHERE (a.blob_id = ab.id OR a.preview_blob_id = ab.id) AND a.deleted_at IS NULL
|
|
46959
47386
|
)`
|
|
46960
47387
|
);
|
|
46961
47388
|
return { blobs: blobs.length, attachments: attachments.length };
|
|
@@ -47310,6 +47737,306 @@ function createRecordBackend(store, options = {}) {
|
|
|
47310
47737
|
|
|
47311
47738
|
// src/records/record-shard.ts
|
|
47312
47739
|
init_geometry_buffer();
|
|
47740
|
+
|
|
47741
|
+
// src/records/lifecycle-purge.ts
|
|
47742
|
+
init_geometry_buffer();
|
|
47743
|
+
var RECEIPT_POLICY2 = {
|
|
47744
|
+
authority: "Fortemi/fortemi#1092",
|
|
47745
|
+
mode: "terminal_purge",
|
|
47746
|
+
receipt_contains_content: false,
|
|
47747
|
+
backup_disposition: "beyond_use_then_reerase",
|
|
47748
|
+
restore_reerasure: true
|
|
47749
|
+
};
|
|
47750
|
+
function zeroCounts2() {
|
|
47751
|
+
return {
|
|
47752
|
+
notes: 0,
|
|
47753
|
+
revisions: 0,
|
|
47754
|
+
links: 0,
|
|
47755
|
+
tags: 0,
|
|
47756
|
+
embeddings: 0,
|
|
47757
|
+
attachments: 0,
|
|
47758
|
+
blobs: 0,
|
|
47759
|
+
graph_edges: 0,
|
|
47760
|
+
provenance_edges: 0,
|
|
47761
|
+
source_identities: 0
|
|
47762
|
+
};
|
|
47763
|
+
}
|
|
47764
|
+
function asCounts2(value) {
|
|
47765
|
+
return { ...zeroCounts2(), ...value };
|
|
47766
|
+
}
|
|
47767
|
+
async function selectedNoteIds2(store, selector) {
|
|
47768
|
+
validatePurgeSelector(selector);
|
|
47769
|
+
const notes = await store.list("note");
|
|
47770
|
+
const requested = selector.note_ids ? new Set(selector.note_ids) : null;
|
|
47771
|
+
if (selector.tenant_id === void 0 && selector.archive_id === void 0 && !selector.source) {
|
|
47772
|
+
return notes.filter((note) => requested.has(note.id)).map((note) => note.id).sort();
|
|
47773
|
+
}
|
|
47774
|
+
const identities = await store.list("source_identity");
|
|
47775
|
+
const scoped2 = new Set(identities.filter((identity) => identity.tenant_id === (selector.tenant_id ?? "default") && identity.archive_id === (selector.archive_id ?? null) && (!selector.source || identity.namespace === selector.source.namespace) && (selector.source?.external_id === void 0 || identity.external_id === selector.source.external_id)).map((identity) => identity.note_id));
|
|
47776
|
+
return notes.filter((note) => scoped2.has(note.id) && (!requested || requested.has(note.id))).map((note) => note.id).sort();
|
|
47777
|
+
}
|
|
47778
|
+
function attachmentBlobIds(attachment) {
|
|
47779
|
+
return [attachment.blob_id, attachment.preview_blob_id].filter((id) => Boolean(id));
|
|
47780
|
+
}
|
|
47781
|
+
async function removableBlobs(store, noteSet) {
|
|
47782
|
+
const attachments = await store.list("attachment");
|
|
47783
|
+
const candidates = new Set(attachments.filter((record) => noteSet.has(record.note_id)).flatMap(attachmentBlobIds));
|
|
47784
|
+
for (const record of attachments) {
|
|
47785
|
+
if (!noteSet.has(record.note_id)) {
|
|
47786
|
+
for (const id of attachmentBlobIds(record)) candidates.delete(id);
|
|
47787
|
+
}
|
|
47788
|
+
}
|
|
47789
|
+
return (await store.list("attachment_blob")).filter((record) => candidates.has(record.id));
|
|
47790
|
+
}
|
|
47791
|
+
async function count(store, noteIds) {
|
|
47792
|
+
const counts = zeroCounts2();
|
|
47793
|
+
if (noteIds.length === 0) return counts;
|
|
47794
|
+
const noteSet = new Set(noteIds);
|
|
47795
|
+
counts.notes = (await store.list("note")).filter((record) => noteSet.has(record.id)).length;
|
|
47796
|
+
counts.revisions = (await store.list("note_revision")).filter((record) => noteSet.has(record.note_id)).length;
|
|
47797
|
+
counts.links = (await store.list("link")).filter((record) => noteSet.has(record.source_note_id) || noteSet.has(record.target_note_id)).length;
|
|
47798
|
+
counts.tags = (await store.list("note_tag")).filter((record) => noteSet.has(record.note_id)).length;
|
|
47799
|
+
counts.attachments = (await store.list("attachment")).filter((record) => noteSet.has(record.note_id)).length;
|
|
47800
|
+
counts.blobs = (await removableBlobs(store, noteSet)).length;
|
|
47801
|
+
counts.source_identities = (await store.list("source_identity")).filter((record) => noteSet.has(record.note_id)).length;
|
|
47802
|
+
return counts;
|
|
47803
|
+
}
|
|
47804
|
+
function receiptFromRecord(record) {
|
|
47805
|
+
return {
|
|
47806
|
+
contract_version: LIFECYCLE_PURGE_CONTRACT_VERSION,
|
|
47807
|
+
operation_id: record.operation_id,
|
|
47808
|
+
outcome: "completed",
|
|
47809
|
+
counts: asCounts2(record.counts),
|
|
47810
|
+
completed_at: record.completed_at,
|
|
47811
|
+
policy: record.policy
|
|
47812
|
+
};
|
|
47813
|
+
}
|
|
47814
|
+
async function recordStorePurgeStatus(store, operationId) {
|
|
47815
|
+
assertPurgeOpaqueId(operationId, "operation_id");
|
|
47816
|
+
const operation = await store.get("lifecycle_purge_operation", operationId);
|
|
47817
|
+
if (!operation) return null;
|
|
47818
|
+
const receipt = await store.get("deletion_receipt", operationId);
|
|
47819
|
+
const pending = (await store.list("lifecycle_purge_blob_cleanup")).filter((record) => record.operation_id === operationId && record.completed_at === null).length;
|
|
47820
|
+
return {
|
|
47821
|
+
contract_version: LIFECYCLE_PURGE_CONTRACT_VERSION,
|
|
47822
|
+
operation_id: operationId,
|
|
47823
|
+
outcome: operation.state,
|
|
47824
|
+
counts: asCounts2(operation.counts),
|
|
47825
|
+
blob_cleanup_pending: pending,
|
|
47826
|
+
search_cleanup_pending: false,
|
|
47827
|
+
...receipt ? { receipt: receiptFromRecord(receipt) } : {}
|
|
47828
|
+
};
|
|
47829
|
+
}
|
|
47830
|
+
async function previewRecordStorePurge(store, selector) {
|
|
47831
|
+
if (!store.applyPurgeBatch) throw new Error("RecordStore purge requires atomic journal-compacting applyPurgeBatch() support");
|
|
47832
|
+
validatePurgeSelector(selector);
|
|
47833
|
+
const now2 = Date.now();
|
|
47834
|
+
const mutations = (await store.list("lifecycle_purge_preview")).filter((record) => record.consumed_by === null && Date.parse(record.expires_at) <= now2).map((record) => ({ op: "delete", collection: "lifecycle_purge_preview", id: record.id }));
|
|
47835
|
+
const noteIds = await selectedNoteIds2(store, selector);
|
|
47836
|
+
const counts = await count(store, noteIds);
|
|
47837
|
+
const previewId = generateId();
|
|
47838
|
+
const expiresAt = new Date(now2 + LIFECYCLE_PURGE_PREVIEW_TTL_SECONDS * 1e3).toISOString();
|
|
47839
|
+
mutations.push({
|
|
47840
|
+
op: "put",
|
|
47841
|
+
collection: "lifecycle_purge_preview",
|
|
47842
|
+
record: {
|
|
47843
|
+
id: previewId,
|
|
47844
|
+
selector_fingerprint: purgeSelectorHash(selector),
|
|
47845
|
+
selector: structuredClone(selector),
|
|
47846
|
+
selected_note_ids: noteIds,
|
|
47847
|
+
counts,
|
|
47848
|
+
expires_at: expiresAt,
|
|
47849
|
+
consumed_by: null
|
|
47850
|
+
}
|
|
47851
|
+
});
|
|
47852
|
+
await store.applyPurgeBatch(
|
|
47853
|
+
mutations,
|
|
47854
|
+
mutations.filter((mutation) => mutation.op === "delete").map((mutation) => ({ collection: mutation.collection, id: mutation.id }))
|
|
47855
|
+
);
|
|
47856
|
+
return {
|
|
47857
|
+
contract_version: LIFECYCLE_PURGE_CONTRACT_VERSION,
|
|
47858
|
+
preview_id: previewId,
|
|
47859
|
+
counts,
|
|
47860
|
+
expires_at: expiresAt
|
|
47861
|
+
};
|
|
47862
|
+
}
|
|
47863
|
+
async function graphDeletionMutations(store, noteIds, deleteSourceJournals) {
|
|
47864
|
+
const noteSet = new Set(noteIds);
|
|
47865
|
+
const mutations = [];
|
|
47866
|
+
for (const collection of [
|
|
47867
|
+
"note_revised_current",
|
|
47868
|
+
"note_original",
|
|
47869
|
+
"note_revision",
|
|
47870
|
+
"note_tag",
|
|
47871
|
+
"collection_note",
|
|
47872
|
+
"attachment",
|
|
47873
|
+
"source_identity"
|
|
47874
|
+
]) {
|
|
47875
|
+
for (const record of await store.list(collection)) {
|
|
47876
|
+
const noteId = collection === "note_revised_current" ? record.id : "note_id" in record && typeof record.note_id === "string" ? record.note_id : null;
|
|
47877
|
+
if (noteId && noteSet.has(noteId)) mutations.push({ op: "delete", collection, id: record.id });
|
|
47878
|
+
}
|
|
47879
|
+
}
|
|
47880
|
+
for (const record of await store.list("link")) {
|
|
47881
|
+
if (noteSet.has(record.source_note_id) || noteSet.has(record.target_note_id)) {
|
|
47882
|
+
mutations.push({ op: "delete", collection: "link", id: record.id });
|
|
47883
|
+
}
|
|
47884
|
+
}
|
|
47885
|
+
for (const note of await store.list("note")) {
|
|
47886
|
+
if (noteSet.has(note.id)) mutations.push({ op: "delete", collection: "note", id: note.id });
|
|
47887
|
+
}
|
|
47888
|
+
const blobs = await removableBlobs(store, noteSet);
|
|
47889
|
+
for (const blob of blobs) mutations.push({ op: "delete", collection: "attachment_blob", id: blob.id });
|
|
47890
|
+
if (deleteSourceJournals) {
|
|
47891
|
+
const selectedIdentities = (await store.list("source_identity")).filter((identity) => noteSet.has(identity.note_id));
|
|
47892
|
+
const survivors = (await store.list("source_identity")).filter((identity) => !noteSet.has(identity.note_id));
|
|
47893
|
+
const scopeKey = (record) => JSON.stringify([record.tenant_id, record.archive_id, record.namespace]);
|
|
47894
|
+
const emptiedScopes = new Set(selectedIdentities.map(scopeKey).filter((scope) => !survivors.some((item) => scopeKey(item) === scope)));
|
|
47895
|
+
for (const run of await store.list("source_import_run")) {
|
|
47896
|
+
if (emptiedScopes.has(scopeKey(run))) mutations.push({ op: "delete", collection: "source_import_run", id: run.id });
|
|
47897
|
+
}
|
|
47898
|
+
for (const batch of await store.list("source_import_batch")) {
|
|
47899
|
+
if (emptiedScopes.has(scopeKey(batch))) mutations.push({ op: "delete", collection: "source_import_batch", id: batch.id });
|
|
47900
|
+
}
|
|
47901
|
+
}
|
|
47902
|
+
return { mutations, blobs };
|
|
47903
|
+
}
|
|
47904
|
+
function terminalRecords(operation, completedAt) {
|
|
47905
|
+
const completed = { ...operation, state: "completed", completed_at: completedAt };
|
|
47906
|
+
return {
|
|
47907
|
+
operation: completed,
|
|
47908
|
+
receipt: {
|
|
47909
|
+
id: operation.id,
|
|
47910
|
+
contract_version: LIFECYCLE_PURGE_CONTRACT_VERSION,
|
|
47911
|
+
operation_id: operation.id,
|
|
47912
|
+
outcome: "completed",
|
|
47913
|
+
counts: operation.counts,
|
|
47914
|
+
completed_at: completedAt,
|
|
47915
|
+
policy: RECEIPT_POLICY2
|
|
47916
|
+
}
|
|
47917
|
+
};
|
|
47918
|
+
}
|
|
47919
|
+
async function beginRecordStorePurge(store, request, blobStore) {
|
|
47920
|
+
if (!store.applyPurgeBatch) throw new Error("RecordStore purge requires atomic journal-compacting applyPurgeBatch() support");
|
|
47921
|
+
assertPurgeOpaqueId(request.operation_id, "operation_id");
|
|
47922
|
+
assertPurgeOpaqueId(request.preview_id, "preview_id");
|
|
47923
|
+
const prior = await store.get("lifecycle_purge_operation", request.operation_id);
|
|
47924
|
+
if (prior) {
|
|
47925
|
+
if (prior.preview_id !== request.preview_id) throw new Error("Purge operation ID was already used for another preview");
|
|
47926
|
+
return await recordStorePurgeStatus(store, request.operation_id);
|
|
47927
|
+
}
|
|
47928
|
+
const preview = await store.get("lifecycle_purge_preview", request.preview_id);
|
|
47929
|
+
if (!preview) throw new Error("Lifecycle purge preview was not found");
|
|
47930
|
+
if (preview.consumed_by !== null) throw new Error("Lifecycle purge preview was already consumed");
|
|
47931
|
+
if (Date.parse(preview.expires_at) <= Date.now()) throw new Error("Lifecycle purge preview expired; create a new preview");
|
|
47932
|
+
const selector = preview.selector;
|
|
47933
|
+
validatePurgeSelector(selector);
|
|
47934
|
+
if (purgeSelectorHash(selector) !== preview.selector_fingerprint) throw new Error("Lifecycle purge preview integrity check failed");
|
|
47935
|
+
const noteIds = await selectedNoteIds2(store, selector);
|
|
47936
|
+
const counts = await count(store, noteIds);
|
|
47937
|
+
if (JSON.stringify(noteIds) !== JSON.stringify(preview.selected_note_ids) || JSON.stringify(counts) !== JSON.stringify(asCounts2(preview.counts))) {
|
|
47938
|
+
throw new Error("Lifecycle purge preview is stale; create a new preview");
|
|
47939
|
+
}
|
|
47940
|
+
const deletion = await graphDeletionMutations(
|
|
47941
|
+
store,
|
|
47942
|
+
noteIds,
|
|
47943
|
+
selector.source !== void 0 && selector.source.external_id === void 0
|
|
47944
|
+
);
|
|
47945
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
47946
|
+
let operation = {
|
|
47947
|
+
id: request.operation_id,
|
|
47948
|
+
preview_id: request.preview_id,
|
|
47949
|
+
selector_fingerprint: preview.selector_fingerprint,
|
|
47950
|
+
state: blobStore && deletion.blobs.length > 0 ? "cleanup_pending" : "completed",
|
|
47951
|
+
counts,
|
|
47952
|
+
created_at: now2,
|
|
47953
|
+
completed_at: null
|
|
47954
|
+
};
|
|
47955
|
+
const mutations = [...deletion.mutations];
|
|
47956
|
+
for (const noteId of noteIds) {
|
|
47957
|
+
mutations.push({
|
|
47958
|
+
op: "put",
|
|
47959
|
+
collection: "lifecycle_purge_erasure_target",
|
|
47960
|
+
record: { id: `${request.operation_id}\0${noteId}`, operation_id: request.operation_id, note_id: noteId }
|
|
47961
|
+
});
|
|
47962
|
+
}
|
|
47963
|
+
if (blobStore) {
|
|
47964
|
+
for (const blob of deletion.blobs) {
|
|
47965
|
+
mutations.push({
|
|
47966
|
+
op: "put",
|
|
47967
|
+
collection: "lifecycle_purge_blob_cleanup",
|
|
47968
|
+
record: {
|
|
47969
|
+
id: `${request.operation_id}\0${blob.id}`,
|
|
47970
|
+
operation_id: request.operation_id,
|
|
47971
|
+
blob_id: blob.id,
|
|
47972
|
+
content_hash: blob.content_hash,
|
|
47973
|
+
completed_at: null
|
|
47974
|
+
}
|
|
47975
|
+
});
|
|
47976
|
+
}
|
|
47977
|
+
}
|
|
47978
|
+
if (operation.state === "completed") {
|
|
47979
|
+
const terminal = terminalRecords(operation, now2);
|
|
47980
|
+
operation = terminal.operation;
|
|
47981
|
+
mutations.push({ op: "put", collection: "deletion_receipt", record: terminal.receipt });
|
|
47982
|
+
}
|
|
47983
|
+
mutations.push({ op: "put", collection: "lifecycle_purge_operation", record: operation });
|
|
47984
|
+
mutations.push({
|
|
47985
|
+
op: "put",
|
|
47986
|
+
collection: "lifecycle_purge_preview",
|
|
47987
|
+
record: { ...preview, selector: {}, selected_note_ids: [], consumed_by: request.operation_id }
|
|
47988
|
+
});
|
|
47989
|
+
const scrubJournal = deletion.mutations.filter((mutation) => mutation.op === "delete").map((mutation) => ({ collection: mutation.collection, id: mutation.id }));
|
|
47990
|
+
scrubJournal.push({ collection: "lifecycle_purge_preview", id: request.preview_id });
|
|
47991
|
+
await store.applyPurgeBatch(mutations, scrubJournal);
|
|
47992
|
+
return await recordStorePurgeStatus(store, request.operation_id);
|
|
47993
|
+
}
|
|
47994
|
+
async function resumeRecordStorePurge(store, operationId, blobStore) {
|
|
47995
|
+
if (!store.applyBatch) throw new Error("RecordStore purge requires atomic applyBatch() support");
|
|
47996
|
+
assertPurgeOpaqueId(operationId, "operation_id");
|
|
47997
|
+
const status = await recordStorePurgeStatus(store, operationId);
|
|
47998
|
+
if (!status) throw new Error("Lifecycle purge operation was not found");
|
|
47999
|
+
if (status.outcome === "completed") return status;
|
|
48000
|
+
if (!blobStore?.delete) throw new Error("Lifecycle purge byte cleanup requires BlobStore.delete()");
|
|
48001
|
+
const pending = (await store.list("lifecycle_purge_blob_cleanup")).filter((record) => record.operation_id === operationId && record.completed_at === null);
|
|
48002
|
+
for (const blob of pending) {
|
|
48003
|
+
await blobStore.delete(blob.content_hash);
|
|
48004
|
+
await store.put("lifecycle_purge_blob_cleanup", { ...blob, completed_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
48005
|
+
}
|
|
48006
|
+
const operation = await store.get("lifecycle_purge_operation", operationId);
|
|
48007
|
+
if (!operation) throw new Error("Lifecycle purge operation was not found");
|
|
48008
|
+
const terminal = terminalRecords(operation, (/* @__PURE__ */ new Date()).toISOString());
|
|
48009
|
+
await store.applyBatch([
|
|
48010
|
+
{ op: "put", collection: "lifecycle_purge_operation", record: terminal.operation },
|
|
48011
|
+
{ op: "put", collection: "deletion_receipt", record: terminal.receipt }
|
|
48012
|
+
]);
|
|
48013
|
+
return await recordStorePurgeStatus(store, operationId);
|
|
48014
|
+
}
|
|
48015
|
+
async function purgeRecordStoreGraph(store, selector, operationId, blobStore) {
|
|
48016
|
+
validatePurgeSelector(selector);
|
|
48017
|
+
assertPurgeOpaqueId(operationId, "operation_id");
|
|
48018
|
+
const existing = await store.get("lifecycle_purge_operation", operationId);
|
|
48019
|
+
if (existing) {
|
|
48020
|
+
if (existing.selector_fingerprint !== purgeSelectorHash(selector)) {
|
|
48021
|
+
throw new Error("Purge operation key conflicts with selector");
|
|
48022
|
+
}
|
|
48023
|
+
let replay = await recordStorePurgeStatus(store, operationId);
|
|
48024
|
+
if (replay.outcome === "cleanup_pending") replay = await resumeRecordStorePurge(store, operationId, blobStore);
|
|
48025
|
+
if (!replay.receipt) throw new Error("Lifecycle purge did not produce a terminal receipt");
|
|
48026
|
+
return replay.receipt;
|
|
48027
|
+
}
|
|
48028
|
+
const preview = await previewRecordStorePurge(store, selector);
|
|
48029
|
+
let status = await beginRecordStorePurge(store, { operation_id: operationId, preview_id: preview.preview_id }, blobStore);
|
|
48030
|
+
if (status.outcome === "cleanup_pending") status = await resumeRecordStorePurge(store, operationId, blobStore);
|
|
48031
|
+
if (!status.receipt) throw new Error("Lifecycle purge did not produce a terminal receipt");
|
|
48032
|
+
return status.receipt;
|
|
48033
|
+
}
|
|
48034
|
+
async function recordStoreErasureTargetIds(store, candidateNoteIds) {
|
|
48035
|
+
const candidates = candidateNoteIds ? new Set(candidateNoteIds) : null;
|
|
48036
|
+
return new Set((await store.list("lifecycle_purge_erasure_target")).filter((record) => !candidates || candidates.has(record.note_id)).map((record) => record.note_id));
|
|
48037
|
+
}
|
|
48038
|
+
|
|
48039
|
+
// src/records/record-shard.ts
|
|
47313
48040
|
var encoder8 = new TextEncoder();
|
|
47314
48041
|
var decoder9 = new TextDecoder();
|
|
47315
48042
|
function emptyCounts4() {
|
|
@@ -47983,6 +48710,17 @@ async function importShardToRecords(store, data, options) {
|
|
|
47983
48710
|
capabilityReport
|
|
47984
48711
|
);
|
|
47985
48712
|
}
|
|
48713
|
+
const retainedTargets = await recordStoreErasureTargetIds(
|
|
48714
|
+
store,
|
|
48715
|
+
parsedNotes.map((note) => note.id)
|
|
48716
|
+
);
|
|
48717
|
+
if (retainedTargets.size > 0) {
|
|
48718
|
+
parsedNotes = parsedNotes.filter((note) => !retainedTargets.has(note.id));
|
|
48719
|
+
parsedLinks = parsedLinks.filter((link2) => !retainedTargets.has(link2.from_note_id) && (link2.to_note_id === null || !retainedTargets.has(link2.to_note_id)));
|
|
48720
|
+
warnings.push(
|
|
48721
|
+
`${retainedTargets.size} retained lifecycle purge target(s) were re-erased before record import.`
|
|
48722
|
+
);
|
|
48723
|
+
}
|
|
47986
48724
|
for (const component of manifest.components ?? []) {
|
|
47987
48725
|
if (UNSUPPORTED_COMPONENTS.includes(component)) {
|
|
47988
48726
|
const count2 = manifest.counts?.[component];
|
|
@@ -48671,125 +49409,8 @@ function validate3(items, maxItems, initialReason) {
|
|
|
48671
49409
|
}));
|
|
48672
49410
|
}
|
|
48673
49411
|
|
|
48674
|
-
// src/records/lifecycle-purge.ts
|
|
48675
|
-
init_geometry_buffer();
|
|
48676
|
-
function hashSelector(selector) {
|
|
48677
|
-
return computeHash(new TextEncoder().encode(JSON.stringify({
|
|
48678
|
-
tenant_id: selector.tenant_id ?? "default",
|
|
48679
|
-
archive_id: selector.archive_id ?? null,
|
|
48680
|
-
note_ids: [...selector.note_ids ?? []].sort(),
|
|
48681
|
-
source: selector.source ? {
|
|
48682
|
-
namespace: selector.source.namespace,
|
|
48683
|
-
external_id_hash: selector.source.external_id ? computeHash(new TextEncoder().encode(selector.source.external_id)) : null
|
|
48684
|
-
} : null
|
|
48685
|
-
})));
|
|
48686
|
-
}
|
|
48687
|
-
function zeroCounts2() {
|
|
48688
|
-
return {
|
|
48689
|
-
notes: 0,
|
|
48690
|
-
revisions: 0,
|
|
48691
|
-
links: 0,
|
|
48692
|
-
tags: 0,
|
|
48693
|
-
embeddings: 0,
|
|
48694
|
-
attachments: 0,
|
|
48695
|
-
blobs: 0,
|
|
48696
|
-
graph_edges: 0,
|
|
48697
|
-
provenance_edges: 0,
|
|
48698
|
-
source_identities: 0
|
|
48699
|
-
};
|
|
48700
|
-
}
|
|
48701
|
-
async function selectedNoteIds2(store, selector) {
|
|
48702
|
-
const notes = await store.list("note");
|
|
48703
|
-
if (selector.note_ids?.length) return notes.filter((note) => selector.note_ids.includes(note.id)).map((note) => note.id);
|
|
48704
|
-
if (!selector.source) throw new Error("Purge selector must target note_ids or source identity");
|
|
48705
|
-
const identities = await store.list("source_identity");
|
|
48706
|
-
return identities.filter((identity) => identity.tenant_id === (selector.tenant_id ?? "default") && identity.archive_id === (selector.archive_id ?? null) && identity.namespace === selector.source.namespace && (selector.source.external_id === void 0 || identity.external_id === selector.source.external_id)).map((identity) => identity.note_id);
|
|
48707
|
-
}
|
|
48708
|
-
async function count(store, noteIds) {
|
|
48709
|
-
const counts = zeroCounts2();
|
|
48710
|
-
if (noteIds.length === 0) return counts;
|
|
48711
|
-
const noteSet = new Set(noteIds);
|
|
48712
|
-
counts.notes = (await store.list("note")).filter((record) => noteSet.has(record.id)).length;
|
|
48713
|
-
counts.revisions = 0;
|
|
48714
|
-
counts.links = (await store.list("link")).filter((record) => noteSet.has(record.source_note_id) || noteSet.has(record.target_note_id)).length;
|
|
48715
|
-
counts.tags = (await store.list("note_tag")).filter((record) => noteSet.has(record.note_id)).length;
|
|
48716
|
-
counts.attachments = (await store.list("attachment")).filter((record) => noteSet.has(record.note_id)).length;
|
|
48717
|
-
const purgedBlobIds = new Set((await store.list("attachment")).filter((record) => noteSet.has(record.note_id)).map((record) => record.blob_id));
|
|
48718
|
-
counts.blobs = (await store.list("attachment_blob")).filter((record) => purgedBlobIds.has(record.id)).length;
|
|
48719
|
-
counts.source_identities = (await store.list("source_identity")).filter((record) => noteSet.has(record.note_id)).length;
|
|
48720
|
-
return counts;
|
|
48721
|
-
}
|
|
48722
|
-
async function previewRecordStorePurge(store, selector) {
|
|
48723
|
-
return { selector_hash: hashSelector(selector), counts: await count(store, await selectedNoteIds2(store, selector)) };
|
|
48724
|
-
}
|
|
48725
|
-
async function purgeRecordStoreGraph(store, selector, operationKey) {
|
|
48726
|
-
if (!store.applyBatch) throw new Error("RecordStore purge requires atomic applyBatch() support");
|
|
48727
|
-
const prior = (await store.list("deletion_receipt")).find((receipt2) => receipt2.operation_key === operationKey);
|
|
48728
|
-
if (prior) return {
|
|
48729
|
-
id: prior.id,
|
|
48730
|
-
operation_key: prior.operation_key,
|
|
48731
|
-
tenant_id: prior.tenant_id,
|
|
48732
|
-
archive_id: prior.archive_id,
|
|
48733
|
-
selector_hash: prior.selector_hash,
|
|
48734
|
-
outcome: "completed",
|
|
48735
|
-
counts: prior.counts,
|
|
48736
|
-
completed_at: prior.completed_at,
|
|
48737
|
-
policy: prior.policy
|
|
48738
|
-
};
|
|
48739
|
-
const noteIds = await selectedNoteIds2(store, selector);
|
|
48740
|
-
const noteSet = new Set(noteIds);
|
|
48741
|
-
const counts = await count(store, noteIds);
|
|
48742
|
-
const mutations = [];
|
|
48743
|
-
for (const collection of ["note_revised_current", "note_original", "note_tag", "collection_note", "attachment", "source_identity"]) {
|
|
48744
|
-
for (const record of await store.list(collection)) {
|
|
48745
|
-
const noteId = collection === "note_revised_current" ? record.id : "note_id" in record && typeof record.note_id === "string" ? record.note_id : null;
|
|
48746
|
-
if (noteId && noteSet.has(noteId)) {
|
|
48747
|
-
mutations.push({ op: "delete", collection, id: record.id });
|
|
48748
|
-
}
|
|
48749
|
-
}
|
|
48750
|
-
}
|
|
48751
|
-
for (const record of await store.list("link")) {
|
|
48752
|
-
if (noteSet.has(record.source_note_id) || noteSet.has(record.target_note_id)) mutations.push({ op: "delete", collection: "link", id: record.id });
|
|
48753
|
-
}
|
|
48754
|
-
for (const record of await store.list("note")) {
|
|
48755
|
-
if (noteSet.has(record.id)) mutations.push({ op: "delete", collection: "note", id: record.id });
|
|
48756
|
-
}
|
|
48757
|
-
const liveBlobIds = new Set((await store.list("attachment")).filter((record) => !noteSet.has(record.note_id)).map((record) => record.blob_id));
|
|
48758
|
-
for (const record of await store.list("attachment_blob")) {
|
|
48759
|
-
if (!liveBlobIds.has(record.id)) mutations.push({ op: "delete", collection: "attachment_blob", id: record.id });
|
|
48760
|
-
}
|
|
48761
|
-
const receipt = {
|
|
48762
|
-
id: generateId(),
|
|
48763
|
-
operation_key: operationKey,
|
|
48764
|
-
tenant_id: selector.tenant_id ?? "default",
|
|
48765
|
-
archive_id: selector.archive_id ?? null,
|
|
48766
|
-
selector_hash: hashSelector(selector),
|
|
48767
|
-
outcome: "completed",
|
|
48768
|
-
counts,
|
|
48769
|
-
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
48770
|
-
policy: {
|
|
48771
|
-
authority: "fortemi#1092",
|
|
48772
|
-
mode: "terminal-purge",
|
|
48773
|
-
receipt_contains_content: false
|
|
48774
|
-
}
|
|
48775
|
-
};
|
|
48776
|
-
mutations.push({ op: "put", collection: "deletion_receipt", record: receipt });
|
|
48777
|
-
await store.applyBatch(mutations);
|
|
48778
|
-
return {
|
|
48779
|
-
id: receipt.id,
|
|
48780
|
-
operation_key: receipt.operation_key,
|
|
48781
|
-
tenant_id: receipt.tenant_id,
|
|
48782
|
-
archive_id: receipt.archive_id,
|
|
48783
|
-
selector_hash: receipt.selector_hash,
|
|
48784
|
-
outcome: "completed",
|
|
48785
|
-
counts,
|
|
48786
|
-
completed_at: receipt.completed_at,
|
|
48787
|
-
policy: receipt.policy
|
|
48788
|
-
};
|
|
48789
|
-
}
|
|
48790
|
-
|
|
48791
49412
|
// src/index.ts
|
|
48792
|
-
var VERSION = "2026.9.
|
|
49413
|
+
var VERSION = "2026.9.7";
|
|
48793
49414
|
/*! Bundled license information:
|
|
48794
49415
|
|
|
48795
49416
|
ieee754/index.js:
|
|
@@ -48804,6 +49425,6 @@ buffer/index.js:
|
|
|
48804
49425
|
*)
|
|
48805
49426
|
*/
|
|
48806
49427
|
|
|
48807
|
-
export { AIWG_SCAN_REQUIRED_FIELDS, AllowlistTrustStore, ArchiveManager, AttachmentsRepository, BridgeInferenceProvider, CORE_V1_COMPONENTS, CURRENT_MIGRATION_HEAD, CURRENT_SHARD_VERSION, CanonicalAttachmentsRepository, CanonicalNotesRepository, CapabilityManager, CaptureKnowledgeInputSchema, CollectionsRepository, CommunitiesRepository, DATASET_EXECUTION_CAPABILITY_IDS, DATASET_EXECUTION_CONTRACT, DATASET_EXECUTION_SCHEMA_VERSION, DATASET_INGEST_CONTRACT, DATASET_INGEST_SCHEMA_VERSION, DATASET_LINEAGE_CONTRACT, DATASET_LINEAGE_SCHEMA_VERSION, DATASET_MATERIALIZATION_CONTRACT, DATASET_MATERIALIZATION_KINDS, DATASET_MATERIALIZATION_SCHEMA_VERSION, DB_SNAPSHOT_SCHEMA_VERSION, DEFAULT_LARGE_DOCUMENT_CHARS, DEFAULT_LARGE_DOCUMENT_CHUNKS, DatasetIngestError, DatasetIngestExecutor, DatasetLineageLedger, DatasetMaterializationError, DbSnapshotVersionError, EMBED_REQUEST_KIND, EMBED_RESPONSE_KIND, EmbeddingSetsRepository, FORTEMI_BROWSER_LOCAL_DATASET_EXECUTION_DESCRIPTOR, FORTEMI_COMPATIBILITY_PATH, FORTEMI_COMPATIBILITY_STATES, FORTEMI_PORTABLE_SHARD_DATASET_EXECUTION_DESCRIPTOR, FORTEMI_REQUIRED_COMPATIBILITY_CAPABILITIES, FORTEMI_SERVER_COMPATIBILITY_REVISION, FORTEMI_STATIC_CACHE_DATASET_EXECUTION_DESCRIPTOR, FallbackRouter, FortemiToolManifest, GetNoteInputSchema, GraphRepository, IdbRecordStore, JOB_CAPABILITIES, JOB_PRIORITIES, JobQueueWorker, LINEAGE_ENTITY_KINDS, LINEAGE_RELATIONSHIP_KINDS, LOCAL_ENDPOINTS, LifecyclePurgeRepository, LineageValidationError, LinksRepository, ListNotesInputSchema, ManageArchiveInputSchema, ManageAttachmentsInputSchema, ManageCapabilitiesInputSchema, ManageCollectionsInputSchema, ManageLinksInputSchema, ManageNoteInputSchema, ManageTagsInputSchema, MemoryBlobStore, MemoryDatasetIngestStore, MemoryRecordStore, MigrationRunner, NotesRepository, OpenAICompatibleProvider, PGliteStorageBackend, PGliteStorageBackendFactory, PGliteWorkerClient, PGliteWorkerStorageBackend, PGliteWorkerStorageBackendFactory, ProvenanceRepository, ProviderRegistry, RECORD_COLLECTIONS, RECORD_SCHEMA_VERSION, RECORD_STORE_CAPABILITIES, REGISTERED_METADATA_PATHS, RemoteBackendError, SHARD_FORMAT, SIGNATURE_ENTRY, SIGNING_ENVELOPE_VERSION, SOURCE_UPSERT_CONTRACT_VERSION, SOURCE_UPSERT_MAX_ITEMS, SUPPORTED_PGLITE_VERSION, SearchInputSchema, SearchRepository, SkosRepository, SourceUpsertRepository, TagsRepository, TemplatesRepository, TransactionProxy, TypedEventBus, VERSION, aiRevisionHandler, aiwgFortemiIndexFromKnowledgeShard, aiwgFortemiIndexToCommunityGraph, aiwgFortemiIndexToKnowledgeShard, aiwgFortemiIndexToKnowledgeShardWithReport, allMigrations, appendPluginScript, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, assertShardComponentRecord, bindSearchEvidence, buildAiwgChunkedIndex, buildAiwgStaticEmbeddingSet, buildMetadataPredicateConditions, buildNoteConditions, buildPluginCsp, captureKnowledge, chunkText, classifyError, classifyModel, clearPrefetchedShard, collectionFromShard, collectionToShard, compareDatasetIncrementalParity, computeBlobHash, computeHash, computeLineageDigest, computeSri, conceptTaggingHandler, configureInferenceRuntime, cosineSimilarity, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, createBlobStore, createBridgeInferenceProviders, createCosineSemanticProvider, createCspReportHandler, createFortemi, createLazyBlobStore, createLegacyProvider, createLocalProviderProfile, createPGliteBackend, createPGliteInstance, createRecordBackend, createRecordStore, createRemoteBackend, createRoutes, createSearchEvidenceSet, createShardBackend, createShardCapabilityReport, createWorkerEmbedFunction, datasetDestinationScopeKey, defaultStorageBackendFactory, defineInferenceProvider, defineInferenceRuntime, defineLegacyInferenceProvider, defineOpenAICompatibleProvider, deriveDatasetIngestIdempotencyKey, detectCommunities, detectGpuCapabilities, detectInferenceCapabilities, digestDatasetMaterializationValue, discoverLocalProviders, dropAttachmentProjection, dropNoteProjection, dumpDbSnapshot, embeddingConfigToShard, embeddingFromShard, embeddingGenerationHandler, embeddingSetFromShard, embeddingSetMemberToShard, embeddingSetToShard, embeddingToShard, enqueueFullWorkflow, enqueueJob, enqueueNoteCreationJobs, estimateModelFit, estimateVramMB, estimateVramTier, executeDatasetMaterialization, executeDatasetRetrieval, exportFullV1Snapshot, exportShard, exportShardFromRecords, exportShardFromRecordsWithReport, exportShardWithReport, fetchAndValidateFortemiCompatibility, fetchPluginScript, filterAiwgRecordsByPrivacy, findAiwgStaticDuplicatePairs, formatFortemiCompatibilitySummary, fortemiCompatibilityUrl, fortemiManifest, fromPrefetched, generateId, getAiwgFortemiFacets, getAiwgFortemiIndexExportSchema, getConfiguredInferenceProviderId, getEmbedFunction, getEmbeddingTaskSelectionOptions, getFortemiBridge, getFortemiSecretStore, getJobQueueStatus, getKnowledgeShardContractReceipt, getKnowledgeShardProfileRegistry, getKnowledgeShardSchema, getLlmFunction, getNote, getPrefetchedSha256, getProviderRouteRequirementIssue, handleEmbedRequests, hasFortemiSecureSecrets, importFullV1Snapshot, importShard, importShardToRecords, inferInferenceTaskCapability, inferLocalEmbeddingDimensions, isPluginScriptAllowed, isShardPrefetched, isShardSigningSupported, linkFromShard, linkToShard, linkingHandler, listNotes, manageArchive, manageAttachments, manageCapabilities, manageCollections, manageLinks, manageNote, manageTags, matchRoute, mergeInferenceRuntimeConfigs, mergeSearchEvidenceSets, migrateLegacyBlobStore, negotiateDatasetExecutionCapabilities, negotiateDatasetExecutionCapabilitiesFromWire, negotiateDatasetMaterializationProfile, noteFromShard, noteSkosTagToShard, noteToShard, openShard, packTarGz, parseCspReport, parseSearchEvidenceLocator, parseSearchEvidenceSet, prefetchShard, previewRecordStorePurge, profileSupportError, projectAttachments, projectNotes, projectRecords, provenanceEdgeToShard, providerSatisfiesRouteRequirements, purgeRecordStoreGraph, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, registerLlmCapability, registerSemanticCapability, registerSemanticCapabilityWorker, registerServiceWorker, resolveSearchEvidence, restoreDbSnapshot, searchTool, selectBackend, selectEmbeddingTask, selectLlmModel, setEmbedFunction, setEmbeddingTaskSelectionOptions, setLlmFunction, sha256Hex, sidecarBlobDigests, signShard, skosConceptToShard, skosRelationToShard, skosSchemeToShard, suggestTags, tagsToShard, templateToShard, titleGenerationHandler, unpackTarGz, unregisterLlmCapability, unregisterSemanticCapability, upsertRecordStoreRequest, upsertRecordStoreSources, urlLinkToShard, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgFortemiIndexExportSchema, validateAiwgFortemiProjectedRecordSchema, validateAiwgStaticEmbeddingSet, validateChecksums, validateCoreV1ShardArchive, validateDatasetBenchmarkEvidence, validateDatasetExecutionDescriptor, validateDatasetExecutionRequest, validateFortemiCompatibilityResponse, validateFullV1ShardArchive, validateProviderRoute, validateRecordV1ShardArchive, validateShardArchive, validateShardComponentRecord, validateShardManifest, verifyDbSnapshotMeta, verifyShardSignature, verifySri };
|
|
49428
|
+
export { AIWG_SCAN_REQUIRED_FIELDS, AllowlistTrustStore, ArchiveManager, AttachmentsRepository, BridgeInferenceProvider, CORE_V1_COMPONENTS, CURRENT_MIGRATION_HEAD, CURRENT_SHARD_VERSION, CanonicalAttachmentsRepository, CanonicalNotesRepository, CapabilityManager, CaptureKnowledgeInputSchema, CollectionsRepository, CommunitiesRepository, DATASET_EXECUTION_CAPABILITY_IDS, DATASET_EXECUTION_CONTRACT, DATASET_EXECUTION_SCHEMA_VERSION, DATASET_INGEST_CONTRACT, DATASET_INGEST_SCHEMA_VERSION, DATASET_LINEAGE_CONTRACT, DATASET_LINEAGE_SCHEMA_VERSION, DATASET_MATERIALIZATION_CONTRACT, DATASET_MATERIALIZATION_KINDS, DATASET_MATERIALIZATION_SCHEMA_VERSION, DB_SNAPSHOT_SCHEMA_VERSION, DEFAULT_LARGE_DOCUMENT_CHARS, DEFAULT_LARGE_DOCUMENT_CHUNKS, DatasetIngestError, DatasetIngestExecutor, DatasetLineageLedger, DatasetMaterializationError, DbSnapshotVersionError, EMBED_REQUEST_KIND, EMBED_RESPONSE_KIND, EmbeddingSetsRepository, FORTEMI_BROWSER_LOCAL_DATASET_EXECUTION_DESCRIPTOR, FORTEMI_COMPATIBILITY_PATH, FORTEMI_COMPATIBILITY_STATES, FORTEMI_PORTABLE_SHARD_DATASET_EXECUTION_DESCRIPTOR, FORTEMI_REQUIRED_COMPATIBILITY_CAPABILITIES, FORTEMI_SERVER_COMPATIBILITY_REVISION, FORTEMI_STATIC_CACHE_DATASET_EXECUTION_DESCRIPTOR, FallbackRouter, FortemiToolManifest, GetNoteInputSchema, GraphRepository, IdbRecordStore, JOB_CAPABILITIES, JOB_PRIORITIES, JobQueueWorker, LIFECYCLE_PURGE_CONTRACT_VERSION, LIFECYCLE_PURGE_MAX_NOTE_IDS, LIFECYCLE_PURGE_PREVIEW_TTL_SECONDS, LINEAGE_ENTITY_KINDS, LINEAGE_RELATIONSHIP_KINDS, LOCAL_ENDPOINTS, LifecyclePurgeRepository, LineageValidationError, LinksRepository, ListNotesInputSchema, ManageArchiveInputSchema, ManageAttachmentsInputSchema, ManageCapabilitiesInputSchema, ManageCollectionsInputSchema, ManageLinksInputSchema, ManageNoteInputSchema, ManageTagsInputSchema, MemoryBlobStore, MemoryDatasetIngestStore, MemoryRecordStore, MigrationRunner, NotesRepository, OpenAICompatibleProvider, PGliteStorageBackend, PGliteStorageBackendFactory, PGliteWorkerClient, PGliteWorkerStorageBackend, PGliteWorkerStorageBackendFactory, ProvenanceRepository, ProviderRegistry, RECORD_COLLECTIONS, RECORD_SCHEMA_VERSION, RECORD_STORE_CAPABILITIES, REGISTERED_METADATA_PATHS, RemoteBackendError, SHARD_FORMAT, SIGNATURE_ENTRY, SIGNING_ENVELOPE_VERSION, SOURCE_UPSERT_CONTRACT_VERSION, SOURCE_UPSERT_MAX_ITEMS, SUPPORTED_PGLITE_VERSION, SearchInputSchema, SearchRepository, SkosRepository, SourceUpsertRepository, TagsRepository, TemplatesRepository, TransactionProxy, TypedEventBus, VERSION, aiRevisionHandler, aiwgFortemiIndexFromKnowledgeShard, aiwgFortemiIndexToCommunityGraph, aiwgFortemiIndexToKnowledgeShard, aiwgFortemiIndexToKnowledgeShardWithReport, allMigrations, appendPluginScript, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, assertShardComponentRecord, beginRecordStorePurge, bindSearchEvidence, buildAiwgChunkedIndex, buildAiwgStaticEmbeddingSet, buildMetadataPredicateConditions, buildNoteConditions, buildPluginCsp, captureKnowledge, chunkText, classifyError, classifyModel, clearPrefetchedShard, collectionFromShard, collectionToShard, compareDatasetIncrementalParity, computeBlobHash, computeHash, computeLineageDigest, computeSri, conceptTaggingHandler, configureInferenceRuntime, cosineSimilarity, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, createBlobStore, createBridgeInferenceProviders, createCosineSemanticProvider, createCspReportHandler, createFortemi, createLazyBlobStore, createLegacyProvider, createLocalProviderProfile, createPGliteBackend, createPGliteInstance, createRecordBackend, createRecordStore, createRemoteBackend, createRoutes, createSearchEvidenceSet, createShardBackend, createShardCapabilityReport, createWorkerEmbedFunction, datasetDestinationScopeKey, defaultStorageBackendFactory, defineInferenceProvider, defineInferenceRuntime, defineLegacyInferenceProvider, defineOpenAICompatibleProvider, deriveDatasetIngestIdempotencyKey, detectCommunities, detectGpuCapabilities, detectInferenceCapabilities, digestDatasetMaterializationValue, discoverLocalProviders, dropAttachmentProjection, dropNoteProjection, dumpDbSnapshot, embeddingConfigToShard, embeddingFromShard, embeddingGenerationHandler, embeddingSetFromShard, embeddingSetMemberToShard, embeddingSetToShard, embeddingToShard, enqueueFullWorkflow, enqueueJob, enqueueNoteCreationJobs, estimateModelFit, estimateVramMB, estimateVramTier, executeDatasetMaterialization, executeDatasetRetrieval, exportFullV1Snapshot, exportShard, exportShardFromRecords, exportShardFromRecordsWithReport, exportShardWithReport, fetchAndValidateFortemiCompatibility, fetchPluginScript, filterAiwgRecordsByPrivacy, findAiwgStaticDuplicatePairs, formatFortemiCompatibilitySummary, fortemiCompatibilityUrl, fortemiManifest, fromPrefetched, generateId, getAiwgFortemiFacets, getAiwgFortemiIndexExportSchema, getConfiguredInferenceProviderId, getEmbedFunction, getEmbeddingTaskSelectionOptions, getFortemiBridge, getFortemiSecretStore, getJobQueueStatus, getKnowledgeShardContractReceipt, getKnowledgeShardProfileRegistry, getKnowledgeShardSchema, getLlmFunction, getNote, getPrefetchedSha256, getProviderRouteRequirementIssue, handleEmbedRequests, hasFortemiSecureSecrets, importFullV1Snapshot, importShard, importShardToRecords, inferInferenceTaskCapability, inferLocalEmbeddingDimensions, isPluginScriptAllowed, isShardPrefetched, isShardSigningSupported, linkFromShard, linkToShard, linkingHandler, listNotes, manageArchive, manageAttachments, manageCapabilities, manageCollections, manageLinks, manageNote, manageTags, matchRoute, mergeInferenceRuntimeConfigs, mergeSearchEvidenceSets, migrateLegacyBlobStore, negotiateDatasetExecutionCapabilities, negotiateDatasetExecutionCapabilitiesFromWire, negotiateDatasetMaterializationProfile, noteFromShard, noteSkosTagToShard, noteToShard, openShard, packTarGz, parseCspReport, parseSearchEvidenceLocator, parseSearchEvidenceSet, prefetchShard, previewRecordStorePurge, profileSupportError, projectAttachments, projectNotes, projectRecords, provenanceEdgeToShard, providerSatisfiesRouteRequirements, purgeRecordStoreGraph, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, recordStoreErasureTargetIds, recordStorePurgeStatus, registerLlmCapability, registerSemanticCapability, registerSemanticCapabilityWorker, registerServiceWorker, resolveSearchEvidence, restoreDbSnapshot, resumeRecordStorePurge, searchTool, selectBackend, selectEmbeddingTask, selectLlmModel, setEmbedFunction, setEmbeddingTaskSelectionOptions, setLlmFunction, sha256Hex, sidecarBlobDigests, signShard, skosConceptToShard, skosRelationToShard, skosSchemeToShard, suggestTags, tagsToShard, templateToShard, titleGenerationHandler, unpackTarGz, unregisterLlmCapability, unregisterSemanticCapability, upsertRecordStoreRequest, upsertRecordStoreSources, urlLinkToShard, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgFortemiIndexExportSchema, validateAiwgFortemiProjectedRecordSchema, validateAiwgStaticEmbeddingSet, validateChecksums, validateCoreV1ShardArchive, validateDatasetBenchmarkEvidence, validateDatasetExecutionDescriptor, validateDatasetExecutionRequest, validateFortemiCompatibilityResponse, validateFullV1ShardArchive, validateProviderRoute, validateRecordV1ShardArchive, validateShardArchive, validateShardComponentRecord, validateShardManifest, verifyDbSnapshotMeta, verifyShardSignature, verifySri };
|
|
48808
49429
|
//# sourceMappingURL=index.js.map
|
|
48809
49430
|
//# sourceMappingURL=index.js.map
|