videobook-engine 5.3.2 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +6 -0
  2. package/dist/catalog-metadata.d.ts +1 -1
  3. package/dist/catalog-metadata.js +1 -1
  4. package/dist/domain.js +15 -5
  5. package/dist/domain.js.map +1 -1
  6. package/dist/engine-types.d.ts +140 -1
  7. package/dist/engine-types.d.ts.map +1 -1
  8. package/dist/engine-types.js.map +1 -1
  9. package/dist/engine.d.ts +21 -0
  10. package/dist/engine.d.ts.map +1 -1
  11. package/dist/engine.js +3 -0
  12. package/dist/engine.js.map +1 -1
  13. package/dist/fork.d.ts +3 -4
  14. package/dist/fork.d.ts.map +1 -1
  15. package/dist/fork.js +4 -4
  16. package/dist/fork.js.map +1 -1
  17. package/dist/index.d.ts +5 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +4 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/merge-policy.d.ts +3 -4
  22. package/dist/merge-policy.d.ts.map +1 -1
  23. package/dist/merge-policy.js +5 -4
  24. package/dist/merge-policy.js.map +1 -1
  25. package/dist/migrate-tags-v24.d.ts +12 -0
  26. package/dist/migrate-tags-v24.d.ts.map +1 -0
  27. package/dist/migrate-tags-v24.js +40 -0
  28. package/dist/migrate-tags-v24.js.map +1 -0
  29. package/dist/mvp-contracts.d.ts +2 -2
  30. package/dist/mvp-contracts.js +1 -1
  31. package/dist/schema.d.ts +3 -2
  32. package/dist/schema.d.ts.map +1 -1
  33. package/dist/schema.js +59 -0
  34. package/dist/schema.js.map +1 -1
  35. package/dist/store.d.ts +6 -0
  36. package/dist/store.d.ts.map +1 -1
  37. package/dist/store.js +150 -54
  38. package/dist/store.js.map +1 -1
  39. package/dist/tag-merge.d.ts +4 -0
  40. package/dist/tag-merge.d.ts.map +1 -0
  41. package/dist/tag-merge.js +85 -0
  42. package/dist/tag-merge.js.map +1 -0
  43. package/dist/tag-queries.d.ts +30 -0
  44. package/dist/tag-queries.d.ts.map +1 -0
  45. package/dist/tag-queries.js +313 -0
  46. package/dist/tag-queries.js.map +1 -0
  47. package/dist/tag-rows.d.ts +36 -0
  48. package/dist/tag-rows.d.ts.map +1 -0
  49. package/dist/tag-rows.js +48 -0
  50. package/dist/tag-rows.js.map +1 -0
  51. package/dist/tag-transfer.d.ts +26 -0
  52. package/dist/tag-transfer.d.ts.map +1 -0
  53. package/dist/tag-transfer.js +341 -0
  54. package/dist/tag-transfer.js.map +1 -0
  55. package/dist/tag-validation.d.ts +15 -0
  56. package/dist/tag-validation.d.ts.map +1 -0
  57. package/dist/tag-validation.js +36 -0
  58. package/dist/tag-validation.js.map +1 -0
  59. package/dist/tag-values.d.ts +68 -0
  60. package/dist/tag-values.d.ts.map +1 -0
  61. package/dist/tag-values.js +107 -0
  62. package/dist/tag-values.js.map +1 -0
  63. package/dist/tags.d.ts +44 -0
  64. package/dist/tags.d.ts.map +1 -0
  65. package/dist/tags.js +488 -0
  66. package/dist/tags.js.map +1 -0
  67. package/docs/doltlite-staging.md +35 -15
  68. package/docs/edit-performance.md +2 -4
  69. package/docs/engine-layout.md +92 -4
  70. package/docs/mvp-prd.md +2 -1
  71. package/docs/release-evidence.md +74 -35
  72. package/docs/temporal-search-performance.md +4 -6
  73. package/fixtures/v5/contract-fixtures.json +3 -3
  74. package/package.json +7 -3
@@ -0,0 +1,341 @@
1
+ /**
2
+ * Tag history reads and snapshot transfer.
3
+ *
4
+ * Two jobs the consumer cannot do for itself without touching catalogs
5
+ * directly: reading an artifact's tag state as some revision recorded it,
6
+ * and moving tag state between artifacts — including between separate
7
+ * engines — as validated data rather than raw rows.
8
+ *
9
+ * Transfer never trusts the source's keys or references. Labels are
10
+ * re-normalized into destination-local identities, an entity reference
11
+ * survives only if that entity exists in the destination, and automatic
12
+ * evidence crosses only when the destination artifact actually carries the
13
+ * bytes that were analyzed. A modified or generated derivative therefore
14
+ * cannot inherit automatic tags describing content it does not have.
15
+ */
16
+ import { ok } from "./engine-types.js";
17
+ import { resultOf, syncResultOf } from "./context.js";
18
+ import { EngineFault } from "./store.js";
19
+ import { normalizeAutomaticTags } from "./tag-validation.js";
20
+ import { automaticTagsMatch, compareTags, snapshotMatches, storedSnapshot, } from "./tag-rows.js";
21
+ import { MANUAL_TAGS_PER_ARTIFACT_MAX, TAG_DISMISSALS_PER_ARTIFACT_MAX, normalizeTagList, tagIdentity, } from "./tag-values.js";
22
+ /** Schema version that introduced the tag tables. */
23
+ const TAG_SCHEMA_VERSION = 25;
24
+ export const TAG_SNAPSHOT_VERSION = 1;
25
+ export function createTagTransferApi(context, tagState) {
26
+ return {
27
+ readAtRevision: (artifactId, revision) => syncResultOf(() => readAtRevision(context, artifactId, revision)),
28
+ export: (artifactId) => syncResultOf(() => exportTags(context, tagState, artifactId)),
29
+ import: (artifactId, snapshot) => importTags(context, tagState, artifactId, snapshot),
30
+ };
31
+ }
32
+ // -------------------------------------------------------------- history
33
+ /**
34
+ * Tag state as `revision` recorded it. A revision written before the tag
35
+ * schema existed has no tag state at all, which is an empty snapshot
36
+ * rather than an error.
37
+ */
38
+ function readAtRevision(context, artifactId, revision) {
39
+ const db = context.store.db;
40
+ const artifact = db
41
+ .prepare("SELECT artifact_id FROM dolt_at_artifacts(?) WHERE artifact_id=? LIMIT 1")
42
+ .get(revision, artifactId);
43
+ if (!artifact) {
44
+ throw new EngineFault({
45
+ code: "NOT_FOUND",
46
+ message: `Artifact not found: ${artifactId} at ${revision}`,
47
+ });
48
+ }
49
+ const schema = db
50
+ .prepare("SELECT version FROM dolt_at_engine_schema(?) WHERE singleton=1")
51
+ .get(revision);
52
+ if ((schema?.version ?? 0) < TAG_SCHEMA_VERSION) {
53
+ return emptyState(artifactId);
54
+ }
55
+ const tags = db
56
+ .prepare(`SELECT origin, facet, tag_key, label, entity_id, created_at
57
+ FROM dolt_at_artifact_tags(?)
58
+ WHERE artifact_id=?
59
+ ORDER BY facet, tag_key`)
60
+ .all(revision, artifactId);
61
+ const dismissals = db
62
+ .prepare(`SELECT facet, tag_key, label, dismissed_at
63
+ FROM dolt_at_artifact_tag_dismissals(?)
64
+ WHERE artifact_id=?
65
+ ORDER BY facet, tag_key`)
66
+ .all(revision, artifactId);
67
+ const snapshot = db
68
+ .prepare(`SELECT artifact_id, source_hash, generator, model, extractor_version,
69
+ tag_count, generation, analyzed_at
70
+ FROM dolt_at_artifact_tag_snapshots(?)
71
+ WHERE artifact_id=?`)
72
+ .get(revision, artifactId);
73
+ const hashes = new Set(db
74
+ .prepare("SELECT object_hash FROM dolt_at_artifact_files(?) WHERE artifact_id=?")
75
+ .all(revision, artifactId).map((row) => row.object_hash));
76
+ const stale = snapshot !== undefined && !hashes.has(snapshot.source_hash);
77
+ const dismissed = new Set(dismissals.map((row) => tagIdentity(row.facet, row.tag_key)));
78
+ const manual = tags
79
+ .filter((row) => row.origin === "manual")
80
+ .map((row) => historicalTag(row));
81
+ const automatic = tags
82
+ .filter((row) => row.origin === "automatic")
83
+ .map((row) => ({
84
+ ...historicalTag(row),
85
+ dismissed: dismissed.has(tagIdentity(row.facet, row.tag_key)),
86
+ stale,
87
+ }));
88
+ const claimed = new Set(manual.map((tag) => tagIdentity(tag.facet, tag.key)));
89
+ const effective = [...manual];
90
+ for (const tag of automatic) {
91
+ if (tag.dismissed || tag.stale)
92
+ continue;
93
+ const identity = tagIdentity(tag.facet, tag.key);
94
+ if (claimed.has(identity))
95
+ continue;
96
+ claimed.add(identity);
97
+ effective.push({
98
+ facet: tag.facet,
99
+ key: tag.key,
100
+ label: tag.label,
101
+ origin: tag.origin,
102
+ ...(tag.entityId ? { entityId: tag.entityId } : {}),
103
+ createdAt: tag.createdAt,
104
+ });
105
+ }
106
+ effective.sort(compareTags);
107
+ return {
108
+ artifactId,
109
+ effective,
110
+ manual,
111
+ automatic,
112
+ dismissed: dismissals.map((row) => ({
113
+ facet: row.facet,
114
+ key: row.tag_key,
115
+ label: row.label,
116
+ dismissedAt: row.dismissed_at,
117
+ })),
118
+ ...(snapshot
119
+ ? {
120
+ snapshot: {
121
+ artifactId,
122
+ sourceHash: snapshot.source_hash,
123
+ generator: snapshot.generator,
124
+ ...(snapshot.model === null ? {} : { model: snapshot.model }),
125
+ extractorVersion: snapshot.extractor_version,
126
+ tagCount: snapshot.tag_count,
127
+ generation: snapshot.generation,
128
+ analyzedAt: snapshot.analyzed_at,
129
+ stale,
130
+ },
131
+ }
132
+ : {}),
133
+ };
134
+ }
135
+ function historicalTag(row) {
136
+ return {
137
+ facet: row.facet,
138
+ key: row.tag_key,
139
+ label: row.label,
140
+ origin: row.origin,
141
+ ...(row.entity_id ? { entityId: row.entity_id } : {}),
142
+ createdAt: row.created_at,
143
+ };
144
+ }
145
+ function emptyState(artifactId) {
146
+ return {
147
+ artifactId,
148
+ effective: [],
149
+ manual: [],
150
+ automatic: [],
151
+ dismissed: [],
152
+ };
153
+ }
154
+ // ------------------------------------------------------------- transfer
155
+ /**
156
+ * A portable copy of an artifact's tag state. Automatic evidence is
157
+ * included only while it is still valid for the source, so a stale
158
+ * snapshot is never handed on.
159
+ */
160
+ function exportTags(context, tagState, artifactReference) {
161
+ const artifact = context.artifactRow(artifactReference);
162
+ const state = tagState(artifact.artifact_id);
163
+ const automatic = state.snapshot;
164
+ return {
165
+ version: TAG_SNAPSHOT_VERSION,
166
+ artifactId: artifact.artifact_id,
167
+ manual: state.manual.map((tag) => ({
168
+ facet: tag.facet,
169
+ label: tag.label,
170
+ ...(tag.entityId ? { entityId: tag.entityId } : {}),
171
+ })),
172
+ dismissed: state.dismissed.map((tag) => ({
173
+ facet: tag.facet,
174
+ label: tag.label,
175
+ })),
176
+ ...(automatic && !automatic.stale
177
+ ? {
178
+ automatic: {
179
+ sourceHash: automatic.sourceHash,
180
+ generator: automatic.generator,
181
+ ...(automatic.model ? { model: automatic.model } : {}),
182
+ extractorVersion: automatic.extractorVersion,
183
+ analyzedAt: automatic.analyzedAt,
184
+ tags: state.automatic.map((tag) => ({
185
+ facet: tag.facet,
186
+ label: tag.label,
187
+ ...(tag.entityId ? { entityId: tag.entityId } : {}),
188
+ })),
189
+ },
190
+ }
191
+ : {}),
192
+ };
193
+ }
194
+ async function importTags(context, tagState, artifactReference, snapshot) {
195
+ return resultOf(() => context.store.semanticOperation((commit) => {
196
+ const artifact = context.artifactRow(artifactReference);
197
+ const artifactId = artifact.artifact_id;
198
+ if (snapshot?.version !== TAG_SNAPSHOT_VERSION) {
199
+ throw new Error(`Tag snapshot version must be ${TAG_SNAPSHOT_VERSION}; received ${snapshot?.version ?? "none"}`);
200
+ }
201
+ const manual = localTags(context, normalizeTagList(snapshot.manual ?? []));
202
+ const dismissed = normalizeTagList(snapshot.dismissed ?? []);
203
+ const candidate = importableAutomatic(context, artifactId, snapshot);
204
+ // Importing the same evidence a second time is not a new analysis.
205
+ const current = storedSnapshot(context.store.db, artifactId);
206
+ const automatic = candidate &&
207
+ snapshotMatches(current, candidate) &&
208
+ automaticTagsMatch(context.store.db, artifactId, candidate.tags)
209
+ ? undefined
210
+ : candidate;
211
+ const existingManual = identitySet(context, artifactId, "SELECT facet, tag_key FROM artifact_tags WHERE artifact_id=? AND origin='manual'");
212
+ const existingDismissed = identitySet(context, artifactId, "SELECT facet, tag_key FROM artifact_tag_dismissals WHERE artifact_id=?");
213
+ const newManual = manual.filter((tag) => !existingManual.has(tagIdentity(tag.facet, tag.key)));
214
+ const newDismissed = dismissed.filter((tag) => !existingDismissed.has(tagIdentity(tag.facet, tag.key)));
215
+ assertCapacity(existingManual.size + newManual.length, MANUAL_TAGS_PER_ARTIFACT_MAX, `manual tags on ${artifactId}`);
216
+ assertCapacity(existingDismissed.size + newDismissed.length, TAG_DISMISSALS_PER_ARTIFACT_MAX, `dismissals on ${artifactId}`);
217
+ const result = {
218
+ importedManual: newManual.length,
219
+ importedDismissals: newDismissed.length,
220
+ importedAutomatic: automatic ? automatic.tags.length : 0,
221
+ skippedAutomatic: snapshot.automatic !== undefined && candidate === undefined,
222
+ };
223
+ if (newManual.length === 0 &&
224
+ newDismissed.length === 0 &&
225
+ automatic === undefined) {
226
+ return ok({ ...result, state: tagState(artifactId) });
227
+ }
228
+ const generation = current?.generation ?? 0;
229
+ const mutation = commit({
230
+ operation: "import_artifact_tags",
231
+ tables: [
232
+ "artifact_tags",
233
+ "artifact_tag_dismissals",
234
+ "artifact_tag_snapshots",
235
+ ],
236
+ artifactId,
237
+ details: {
238
+ artifactId,
239
+ sourceArtifactId: snapshot.artifactId,
240
+ manual: newManual.length,
241
+ dismissed: newDismissed.length,
242
+ automatic: automatic?.tags.length ?? 0,
243
+ },
244
+ writeSet: [`artifact-tags-import:${artifactId}`],
245
+ }, (_operationId, now) => {
246
+ const insertTag = context.store.db.prepare(`INSERT INTO artifact_tags(
247
+ artifact_id, origin, facet, tag_key, label, entity_id, created_at
248
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
249
+ ON CONFLICT(artifact_id, origin, facet, tag_key) DO UPDATE SET
250
+ label=excluded.label,
251
+ entity_id=excluded.entity_id`);
252
+ for (const tag of newManual) {
253
+ insertTag.run(artifactId, "manual", tag.facet, tag.key, tag.label, tag.entityId ?? null, now);
254
+ }
255
+ const insertDismissal = context.store.db.prepare(`INSERT INTO artifact_tag_dismissals(
256
+ artifact_id, facet, tag_key, label, dismissed_at
257
+ ) VALUES (?, ?, ?, ?, ?)
258
+ ON CONFLICT(artifact_id, facet, tag_key) DO NOTHING`);
259
+ for (const tag of newDismissed) {
260
+ insertDismissal.run(artifactId, tag.facet, tag.key, tag.label, now);
261
+ }
262
+ if (!automatic)
263
+ return;
264
+ context.store.db
265
+ .prepare("DELETE FROM artifact_tags WHERE artifact_id=? AND origin='automatic'")
266
+ .run(artifactId);
267
+ for (const tag of automatic.tags) {
268
+ insertTag.run(artifactId, "automatic", tag.facet, tag.key, tag.label, tag.entityId ?? null, now);
269
+ }
270
+ context.store.db
271
+ .prepare(`INSERT INTO artifact_tag_snapshots(
272
+ artifact_id, source_hash, generator, model, extractor_version,
273
+ tag_count, generation, analyzed_at
274
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
275
+ ON CONFLICT(artifact_id) DO UPDATE SET
276
+ source_hash=excluded.source_hash,
277
+ generator=excluded.generator,
278
+ model=excluded.model,
279
+ extractor_version=excluded.extractor_version,
280
+ tag_count=excluded.tag_count,
281
+ generation=excluded.generation,
282
+ analyzed_at=excluded.analyzed_at`)
283
+ .run(artifactId, automatic.sourceHash, automatic.generator, automatic.model ?? null, automatic.extractorVersion, automatic.tags.length, generation + 1, automatic.analyzedAt);
284
+ });
285
+ return ok({ ...result, state: tagState(artifactId) }, mutation.revision);
286
+ }));
287
+ }
288
+ /**
289
+ * Automatic evidence transfers only to an artifact that carries the exact
290
+ * bytes it describes, which is what makes a hash-identical duplicate a
291
+ * legitimate destination and a derivative an illegitimate one.
292
+ */
293
+ function importableAutomatic(context, artifactId, snapshot) {
294
+ const automatic = snapshot.automatic;
295
+ if (!automatic)
296
+ return undefined;
297
+ const normalized = normalizeAutomaticTags(automatic);
298
+ if (!Number.isSafeInteger(automatic.analyzedAt) || automatic.analyzedAt < 0) {
299
+ throw new Error("Automatic tag analyzedAt must be a nonnegative integer timestamp");
300
+ }
301
+ const present = context.store.db
302
+ .prepare("SELECT 1 AS present FROM artifact_files WHERE artifact_id=? AND object_hash=?")
303
+ .get(artifactId, normalized.sourceHash);
304
+ if (!present)
305
+ return undefined;
306
+ return {
307
+ ...normalized,
308
+ analyzedAt: automatic.analyzedAt,
309
+ tags: localTags(context, normalized.tags),
310
+ };
311
+ }
312
+ /**
313
+ * Re-points imported tags at destination-local identities: the label is
314
+ * re-normalized here, and a source entity reference survives only when the
315
+ * destination knows that entity.
316
+ */
317
+ function localTags(context, tags) {
318
+ const resolve = context.store.db.prepare("SELECT 1 AS present FROM entities WHERE entity_id=?");
319
+ return tags.map((tag) => {
320
+ if (!tag.entityId || resolve.get(tag.entityId))
321
+ return tag;
322
+ const { entityId: _dropped, ...local } = tag;
323
+ return local;
324
+ });
325
+ }
326
+ function identitySet(context, artifactId, sql) {
327
+ const rows = context.store.db
328
+ .prepare(sql)
329
+ .all(artifactId);
330
+ return new Set(rows.map((row) => tagIdentity(row.facet, row.tag_key)));
331
+ }
332
+ function assertCapacity(total, limit, what) {
333
+ if (total <= limit)
334
+ return;
335
+ throw new EngineFault({
336
+ code: "RESOURCE_EXHAUSTED",
337
+ message: `Importing would leave ${total} ${what}; the limit is ${limit}`,
338
+ details: { requested: total, limit },
339
+ });
340
+ }
341
+ //# sourceMappingURL=tag-transfer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-transfer.js","sourceRoot":"","sources":["../src/tag-transfer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { type TagInput } from "./tag-values.js";
2
+ export declare function normalizeAutomaticTags(input: {
3
+ sourceHash: string;
4
+ generator: string;
5
+ model?: string;
6
+ extractorVersion: string;
7
+ tags: readonly TagInput[];
8
+ }): {
9
+ extractorVersion: string;
10
+ tags: import("./tag-values.js").NormalizedTag[];
11
+ model?: string | undefined;
12
+ sourceHash: string;
13
+ generator: string;
14
+ };
15
+ //# sourceMappingURL=tag-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-validation.d.ts","sourceRoot":"","sources":["../src/tag-validation.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,QAAQ,EACd,MAAM,iBAAiB,CAAC;AAEzB,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAC;CAC3B;;;;;;EAwBA"}
@@ -0,0 +1,36 @@
1
+ /** Shared validation for new analysis and portable automatic evidence. */
2
+ import { EngineFault } from "./store.js";
3
+ import { AUTOMATIC_TAGS_PER_SNAPSHOT_MAX, normalizeTagList, } from "./tag-values.js";
4
+ export function normalizeAutomaticTags(input) {
5
+ const sourceHash = requiredText(input.sourceHash, "sourceHash").toLowerCase();
6
+ if (!/^[0-9a-f]{64}$/u.test(sourceHash)) {
7
+ throw new Error("Automatic tag sourceHash must be a hex sha256 content hash");
8
+ }
9
+ const generator = requiredText(input.generator, "generator");
10
+ const extractorVersion = requiredText(input.extractorVersion, "extractorVersion");
11
+ if (input.model !== undefined && typeof input.model !== "string") {
12
+ throw new Error("Automatic tag model must be a string");
13
+ }
14
+ const model = input.model?.trim();
15
+ if (!Array.isArray(input.tags))
16
+ throw new Error("Automatic tags must be an array");
17
+ const tags = normalizeTagList(input.tags);
18
+ if (tags.some((tag) => tag.facet === "custom")) {
19
+ throw new Error("Automatic tag facets must be people, places or editing; custom tags are manual");
20
+ }
21
+ if (tags.length > AUTOMATIC_TAGS_PER_SNAPSHOT_MAX) {
22
+ throw new EngineFault({
23
+ code: "RESOURCE_EXHAUSTED",
24
+ message: `An automatic snapshot carries at most ${AUTOMATIC_TAGS_PER_SNAPSHOT_MAX} tags; received ${tags.length}`,
25
+ details: { requested: tags.length, limit: AUTOMATIC_TAGS_PER_SNAPSHOT_MAX },
26
+ });
27
+ }
28
+ return { sourceHash, generator, ...(model ? { model } : {}), extractorVersion, tags };
29
+ }
30
+ function requiredText(value, field) {
31
+ if (typeof value !== "string" || !value.trim()) {
32
+ throw new Error(`Automatic tag ${field} is required`);
33
+ }
34
+ return value.trim();
35
+ }
36
+ //# sourceMappingURL=tag-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-validation.js","sourceRoot":"","sources":["../src/tag-validation.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EACL,+BAA+B,EAC/B,gBAAgB,GAEjB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,UAAU,sBAAsB,CAAC,KAMtC;IACC,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9E,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAC7D,MAAM,gBAAgB,GAAG,YAAY,CAAC,KAAK,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;IAClF,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;IAClC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACnF,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,gFAAgF,CAAC,CAAC;IACpG,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,GAAG,+BAA+B,EAAE,CAAC;QAClD,MAAM,IAAI,WAAW,CAAC;YACpB,IAAI,EAAE,oBAAoB;YAC1B,OAAO,EAAE,yCAAyC,+BAA+B,mBAAmB,IAAI,CAAC,MAAM,EAAE;YACjH,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,+BAA+B,EAAE;SAC5E,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;AACxF,CAAC;AAED,SAAS,YAAY,CAAC,KAAa,EAAE,KAAa;IAChD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,iBAAiB,KAAK,cAAc,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Asset-tag values: normalization, identity and the documented limits.
3
+ *
4
+ * Deliberately dependency-free and side-effect-free so the same rules can
5
+ * run in a browser client before a write reaches the engine. Semantic tag
6
+ * identity is the pair (facet, canonical key) — never the display label
7
+ * alone — so the same word carried by two facets stays distinct.
8
+ */
9
+ export declare const TAG_FACETS: readonly ["people", "places", "editing", "custom"];
10
+ export type TagFacet = (typeof TAG_FACETS)[number];
11
+ export type TagOrigin = "manual" | "automatic";
12
+ /** Display labels are capped in Unicode code points, not UTF-16 units. */
13
+ export declare const TAG_LABEL_MAX_CODE_POINTS = 64;
14
+ /** Manual assignments a single artifact may carry. */
15
+ export declare const MANUAL_TAGS_PER_ARTIFACT_MAX = 100;
16
+ /** Automatic assignments one analysis snapshot may carry. */
17
+ export declare const AUTOMATIC_TAGS_PER_SNAPSHOT_MAX = 12;
18
+ /**
19
+ * Durable dismissals a single artifact may carry. Dismissals record user
20
+ * intent, so the ceiling refuses the write instead of silently dropping
21
+ * the oldest suppression.
22
+ */
23
+ export declare const TAG_DISMISSALS_PER_ARTIFACT_MAX = 500;
24
+ export interface TagInput {
25
+ facet: TagFacet;
26
+ label: string;
27
+ /** An already-confirmed entity; never minted from a model guess. */
28
+ entityId?: string;
29
+ }
30
+ export interface TagIdentityInput {
31
+ facet: TagFacet;
32
+ label: string;
33
+ }
34
+ export interface NormalizedTag {
35
+ facet: TagFacet;
36
+ key: string;
37
+ label: string;
38
+ entityId?: string;
39
+ }
40
+ export declare function isTagFacet(value: string): value is TagFacet;
41
+ export declare function assertTagFacet(value: string): TagFacet;
42
+ /**
43
+ * NFKC, whitespace-collapsed, trimmed display label. Control characters
44
+ * that survive whitespace collapsing are rejected rather than stripped, so
45
+ * a caller never silently stores something other than what it sent.
46
+ */
47
+ export declare function normalizeTagLabel(value: string): string;
48
+ /** Stable, locale-independent canonical key for a normalized label. */
49
+ export declare function tagCanonicalKey(label: string): string;
50
+ /**
51
+ * Prefix form of a label for autocomplete: the same NFKC, whitespace, trim
52
+ * and case rules as a key, so what a user types matches what was stored.
53
+ * An empty prefix is a legitimate "no filter" rather than an error.
54
+ */
55
+ export declare function normalizeTagPrefix(value: string): string;
56
+ export declare function tagIdentity(facet: TagFacet, key: string): string;
57
+ export declare function normalizeTagIdentity(input: TagIdentityInput): {
58
+ facet: TagFacet;
59
+ key: string;
60
+ label: string;
61
+ };
62
+ export declare function normalizeTag(input: TagInput): NormalizedTag;
63
+ /**
64
+ * Normalizes a list and drops later duplicates of an identity already
65
+ * seen, so a caller's repeated word never becomes a repeated chip.
66
+ */
67
+ export declare function normalizeTagList(inputs: readonly TagInput[]): NormalizedTag[];
68
+ //# sourceMappingURL=tag-values.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-values.d.ts","sourceRoot":"","sources":["../src/tag-values.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,eAAO,MAAM,UAAU,oDAAqD,CAAC;AAE7E,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE/C,0EAA0E;AAC1E,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAE5C,sDAAsD;AACtD,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD,6DAA6D;AAC7D,eAAO,MAAM,+BAA+B,KAAK,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,QAAQ,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,QAAQ,CAE3D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAOtD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAgBvD;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWxD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,gBAAgB,GAAG;IAC7D,KAAK,EAAE,QAAQ,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAIA;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,GAAG,aAAa,CAS3D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,GAAG,aAAa,EAAE,CAW7E"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Asset-tag values: normalization, identity and the documented limits.
3
+ *
4
+ * Deliberately dependency-free and side-effect-free so the same rules can
5
+ * run in a browser client before a write reaches the engine. Semantic tag
6
+ * identity is the pair (facet, canonical key) — never the display label
7
+ * alone — so the same word carried by two facets stays distinct.
8
+ */
9
+ export const TAG_FACETS = ["people", "places", "editing", "custom"];
10
+ /** Display labels are capped in Unicode code points, not UTF-16 units. */
11
+ export const TAG_LABEL_MAX_CODE_POINTS = 64;
12
+ /** Manual assignments a single artifact may carry. */
13
+ export const MANUAL_TAGS_PER_ARTIFACT_MAX = 100;
14
+ /** Automatic assignments one analysis snapshot may carry. */
15
+ export const AUTOMATIC_TAGS_PER_SNAPSHOT_MAX = 12;
16
+ /**
17
+ * Durable dismissals a single artifact may carry. Dismissals record user
18
+ * intent, so the ceiling refuses the write instead of silently dropping
19
+ * the oldest suppression.
20
+ */
21
+ export const TAG_DISMISSALS_PER_ARTIFACT_MAX = 500;
22
+ const CONTROL_CHARACTER = /\p{Cc}/u;
23
+ export function isTagFacet(value) {
24
+ return TAG_FACETS.includes(value);
25
+ }
26
+ export function assertTagFacet(value) {
27
+ if (!isTagFacet(value)) {
28
+ throw new Error(`Tag facet must be one of ${TAG_FACETS.join(", ")}; received ${value}`);
29
+ }
30
+ return value;
31
+ }
32
+ /**
33
+ * NFKC, whitespace-collapsed, trimmed display label. Control characters
34
+ * that survive whitespace collapsing are rejected rather than stripped, so
35
+ * a caller never silently stores something other than what it sent.
36
+ */
37
+ export function normalizeTagLabel(value) {
38
+ if (typeof value !== "string") {
39
+ throw new Error("Tag label is required");
40
+ }
41
+ const label = value.normalize("NFKC").replace(/\s+/gu, " ").trim();
42
+ if (!label)
43
+ throw new Error("Tag label is required");
44
+ if (CONTROL_CHARACTER.test(label)) {
45
+ throw new Error("Tag label must not contain control characters");
46
+ }
47
+ const codePoints = [...label].length;
48
+ if (codePoints > TAG_LABEL_MAX_CODE_POINTS) {
49
+ throw new Error(`Tag label must be at most ${TAG_LABEL_MAX_CODE_POINTS} characters; received ${codePoints}`);
50
+ }
51
+ return label;
52
+ }
53
+ /** Stable, locale-independent canonical key for a normalized label. */
54
+ export function tagCanonicalKey(label) {
55
+ return normalizeTagLabel(label).toLowerCase();
56
+ }
57
+ /**
58
+ * Prefix form of a label for autocomplete: the same NFKC, whitespace, trim
59
+ * and case rules as a key, so what a user types matches what was stored.
60
+ * An empty prefix is a legitimate "no filter" rather than an error.
61
+ */
62
+ export function normalizeTagPrefix(value) {
63
+ const prefix = value.normalize("NFKC").replace(/\s+/gu, " ").trim();
64
+ if (CONTROL_CHARACTER.test(prefix)) {
65
+ throw new Error("Tag prefix must not contain control characters");
66
+ }
67
+ if ([...prefix].length > TAG_LABEL_MAX_CODE_POINTS) {
68
+ throw new Error(`Tag prefix must be at most ${TAG_LABEL_MAX_CODE_POINTS} characters`);
69
+ }
70
+ return prefix.toLowerCase();
71
+ }
72
+ export function tagIdentity(facet, key) {
73
+ return `${facet}:${key}`;
74
+ }
75
+ export function normalizeTagIdentity(input) {
76
+ const facet = assertTagFacet(input.facet);
77
+ const label = normalizeTagLabel(input.label);
78
+ return { facet, key: label.toLowerCase(), label };
79
+ }
80
+ export function normalizeTag(input) {
81
+ const { facet, key, label } = normalizeTagIdentity(input);
82
+ const entityId = input.entityId?.trim();
83
+ return {
84
+ facet,
85
+ key,
86
+ label,
87
+ ...(entityId ? { entityId } : {}),
88
+ };
89
+ }
90
+ /**
91
+ * Normalizes a list and drops later duplicates of an identity already
92
+ * seen, so a caller's repeated word never becomes a repeated chip.
93
+ */
94
+ export function normalizeTagList(inputs) {
95
+ const seen = new Set();
96
+ const tags = [];
97
+ for (const input of inputs) {
98
+ const tag = normalizeTag(input);
99
+ const identity = tagIdentity(tag.facet, tag.key);
100
+ if (seen.has(identity))
101
+ continue;
102
+ seen.add(identity);
103
+ tags.push(tag);
104
+ }
105
+ return tags;
106
+ }
107
+ //# sourceMappingURL=tag-values.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-values.js","sourceRoot":"","sources":["../src/tag-values.ts"],"names":[],"mappings":"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"}
package/dist/tags.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Asset tags: durable automatic and user-managed tagging.
3
+ *
4
+ * The engine owns state, normalization, transactions and provenance; the
5
+ * consumer owns inference, scheduling and vocabulary. Three rules shape
6
+ * everything here:
7
+ *
8
+ * 1. Semantic identity is (facet, canonical key). Two facets carrying the
9
+ * same word stay distinct assignments.
10
+ * 2. Manual intent outranks analysis. An automatic snapshot replaces only
11
+ * the automatic rows; it can never add, drop or relabel a manual row,
12
+ * and it can never clear a dismissal.
13
+ * 3. Automatic output is fenced to the content it described. A snapshot
14
+ * records the analyzed source hash and a per-artifact generation, so a
15
+ * late result cannot overwrite a newer one and tags for replaced
16
+ * content drop out of the effective set instead of lying.
17
+ */
18
+ import type { ArtifactTagState, AutomaticTagSnapshot, EngineError, ReplaceAutomaticTagsArgs, Result } from "./engine-types.js";
19
+ import { EngineContext } from "./context.js";
20
+ import type { TagIdentityInput, TagInput } from "./tag-values.js";
21
+ /** Artifacts a single batched read may cover. */
22
+ export declare const TAG_READ_BATCH_MAX = 500;
23
+ export declare function createTagsApi(context: EngineContext): {
24
+ read: (artifactId: string) => Result<ArtifactTagState, EngineError>;
25
+ readMany: (artifactIds: readonly string[]) => Result<ArtifactTagState[], EngineError>;
26
+ add: (artifactId: string, tag: TagInput) => Promise<Result<ArtifactTagState, EngineError>>;
27
+ addMany: (artifactId: string, tags: readonly TagInput[]) => Promise<Result<ArtifactTagState, EngineError>>;
28
+ remove: (artifactId: string, tag: TagIdentityInput) => Promise<Result<ArtifactTagState, EngineError>>;
29
+ restore: (artifactId: string, tag: TagIdentityInput) => Promise<Result<ArtifactTagState, EngineError>>;
30
+ automatic: {
31
+ snapshot: (artifactId: string) => Result<AutomaticTagSnapshot | undefined, EngineError>;
32
+ replace: (input: ReplaceAutomaticTagsArgs) => Promise<Result<ArtifactTagState, EngineError>>;
33
+ };
34
+ readAtRevision: (artifactId: string, revision: string) => Result<ArtifactTagState, EngineError>;
35
+ export: (artifactId: string) => Result<import("./engine-types.js").AssetTagSnapshotExport, EngineError>;
36
+ import: (artifactId: string, snapshot: import("./engine-types.js").AssetTagSnapshotExport) => Promise<Result<import("./engine-types.js").TagImportResult, EngineError>>;
37
+ query: (filter?: import("./engine-types.js").TagFilter, options?: import("./engine-types.js").TagQueryOptions) => Result<import("./engine-types.js").TagQueryPage, EngineError>;
38
+ candidates: (filter?: import("./engine-types.js").TagFilter) => Result<string[], EngineError>;
39
+ facets: (filter?: import("./engine-types.js").TagFilter, options?: {
40
+ limit?: number;
41
+ }) => Result<import("./engine-types.js").TagFacetCount[], EngineError>;
42
+ suggest: (query?: import("./engine-types.js").TagSuggestQuery) => Result<import("./engine-types.js").TagFacetCount[], EngineError>;
43
+ };
44
+ //# sourceMappingURL=tags.d.ts.map