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,30 @@
1
+ /**
2
+ * Tag queries: filtering, facet counts and autocomplete over effective
3
+ * tags.
4
+ *
5
+ * "Effective" means exactly what `engine.tags.read` reports: every manual
6
+ * assignment, plus automatic assignments that are neither dismissed nor
7
+ * stale. That rule lives once in SQL here and once in TypeScript in
8
+ * `src/tags.ts`; `tests/tag-queries.test.ts` asserts the two agree.
9
+ *
10
+ * Every query is one indexed statement against the catalog — never a
11
+ * per-artifact round trip and never a JSON scan — and every result set is
12
+ * bounded by a documented limit.
13
+ */
14
+ import type { EngineError, Result, TagFacetCount, TagFilter, TagQueryOptions, TagQueryPage, TagSuggestQuery } from "./engine-types.js";
15
+ import { EngineContext } from "./context.js";
16
+ export declare const TAG_QUERY_LIMIT_DEFAULT = 50;
17
+ export declare const TAG_QUERY_LIMIT_MAX = 500;
18
+ export declare const TAG_FACET_LIMIT_DEFAULT = 50;
19
+ export declare const TAG_FACET_LIMIT_MAX = 500;
20
+ /** Candidate IDs a single call may hand to a composing search. */
21
+ export declare const TAG_CANDIDATE_MAX = 5000;
22
+ export declare function createTagQueriesApi(context: EngineContext): {
23
+ query: (filter?: TagFilter, options?: TagQueryOptions) => Result<TagQueryPage, EngineError>;
24
+ candidates: (filter?: TagFilter) => Result<string[], EngineError>;
25
+ facets: (filter?: TagFilter, options?: {
26
+ limit?: number;
27
+ }) => Result<TagFacetCount[], EngineError>;
28
+ suggest: (query?: TagSuggestQuery) => Result<TagFacetCount[], EngineError>;
29
+ };
30
+ //# sourceMappingURL=tag-queries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-queries.d.ts","sourceRoot":"","sources":["../src/tag-queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAIV,WAAW,EACX,MAAM,EACN,aAAa,EACb,SAAS,EACT,eAAe,EACf,YAAY,EACZ,eAAe,EAEhB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAgB,MAAM,cAAc,CAAC;AAS3D,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,kEAAkE;AAClE,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAkEtC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa;qBAG5C,SAAS,YACR,eAAe,KACvB,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC;0BAEf,SAAS,KAAQ,MAAM,CAAC,MAAM,EAAE,EAAE,WAAW,CAAC;sBAGzD,SAAS,YACR;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAC1B,MAAM,CAAC,aAAa,EAAE,EAAE,WAAW,CAAC;sBAG9B,eAAe,KACrB,MAAM,CAAC,aAAa,EAAE,EAAE,WAAW,CAAC;EAG1C"}
@@ -0,0 +1,313 @@
1
+ /**
2
+ * Tag queries: filtering, facet counts and autocomplete over effective
3
+ * tags.
4
+ *
5
+ * "Effective" means exactly what `engine.tags.read` reports: every manual
6
+ * assignment, plus automatic assignments that are neither dismissed nor
7
+ * stale. That rule lives once in SQL here and once in TypeScript in
8
+ * `src/tags.ts`; `tests/tag-queries.test.ts` asserts the two agree.
9
+ *
10
+ * Every query is one indexed statement against the catalog — never a
11
+ * per-artifact round trip and never a JSON scan — and every result set is
12
+ * bounded by a documented limit.
13
+ */
14
+ import { syncResultOf } from "./context.js";
15
+ import { assertTagFacet, normalizeTagIdentity, normalizeTagPrefix, tagIdentity, } from "./tag-values.js";
16
+ export const TAG_QUERY_LIMIT_DEFAULT = 50;
17
+ export const TAG_QUERY_LIMIT_MAX = 500;
18
+ export const TAG_FACET_LIMIT_DEFAULT = 50;
19
+ export const TAG_FACET_LIMIT_MAX = 500;
20
+ /** Candidate IDs a single call may hand to a composing search. */
21
+ export const TAG_CANDIDATE_MAX = 5000;
22
+ /**
23
+ * The effective-tag projection. Manual rows always count; automatic rows
24
+ * count only while no dismissal suppresses their identity and the
25
+ * snapshot's analyzed hash is still one of the artifact's files.
26
+ */
27
+ const EFFECTIVE_TAGS_SQL = `
28
+ SELECT t.artifact_id, t.facet, t.tag_key, t.label, t.origin
29
+ FROM artifact_tags t
30
+ WHERE t.origin='manual'
31
+ UNION ALL
32
+ SELECT t.artifact_id, t.facet, t.tag_key, t.label, t.origin
33
+ FROM artifact_tags t
34
+ JOIN artifact_tag_snapshots s ON s.artifact_id = t.artifact_id
35
+ WHERE t.origin='automatic'
36
+ AND NOT EXISTS (
37
+ SELECT 1 FROM artifact_tag_dismissals d
38
+ WHERE d.artifact_id = t.artifact_id
39
+ AND d.facet = t.facet
40
+ AND d.tag_key = t.tag_key
41
+ )
42
+ AND EXISTS (
43
+ SELECT 1 FROM artifact_files f
44
+ WHERE f.artifact_id = s.artifact_id
45
+ AND f.object_hash = s.source_hash
46
+ )
47
+ `;
48
+ export function createTagQueriesApi(context) {
49
+ return {
50
+ query: (filter = {}, options = {}) => syncResultOf(() => queryTags(context, filter, options)),
51
+ candidates: (filter = {}) => syncResultOf(() => candidateIds(context, filter)),
52
+ facets: (filter = {}, options = {}) => syncResultOf(() => facetCounts(context, filter, options.limit)),
53
+ suggest: (query = {}) => syncResultOf(() => suggestTags(context, query)),
54
+ };
55
+ }
56
+ // --------------------------------------------------------------- filter
57
+ /**
58
+ * Turns a filter into the artifact-selection clause. `all` and `any` each
59
+ * become one grouped lookup through the tag identity index, so a filter
60
+ * costs one statement regardless of how many artifacts it spans.
61
+ */
62
+ function filterClause(filter) {
63
+ const parts = [];
64
+ const params = [];
65
+ const kinds = filter.kinds ?? [];
66
+ if (kinds.length > 0) {
67
+ parts.push(`a.kind IN (${kinds.map(() => "?").join(", ")})`);
68
+ params.push(...kinds);
69
+ }
70
+ const all = normalizeIdentities(filter.all);
71
+ if (all.length > 0) {
72
+ const pairs = identityPredicate(all);
73
+ parts.push(`a.artifact_id IN (
74
+ SELECT e.artifact_id FROM effective e
75
+ WHERE ${pairs.sql}
76
+ GROUP BY e.artifact_id
77
+ HAVING COUNT(DISTINCT e.facet || ':' || e.tag_key) = ?
78
+ )`);
79
+ params.push(...pairs.params, all.length);
80
+ }
81
+ const any = normalizeIdentities(filter.any);
82
+ if (any.length > 0) {
83
+ const pairs = identityPredicate(any);
84
+ parts.push(`a.artifact_id IN (
85
+ SELECT e.artifact_id FROM effective e WHERE ${pairs.sql}
86
+ )`);
87
+ params.push(...pairs.params);
88
+ }
89
+ return {
90
+ sql: parts.length > 0 ? parts.join("\n AND ") : "1=1",
91
+ params,
92
+ };
93
+ }
94
+ function normalizeIdentities(inputs) {
95
+ if (!inputs || inputs.length === 0)
96
+ return [];
97
+ const seen = new Set();
98
+ const identities = [];
99
+ for (const input of inputs) {
100
+ const { facet, key } = normalizeTagIdentity(input);
101
+ const identity = tagIdentity(facet, key);
102
+ if (seen.has(identity))
103
+ continue;
104
+ seen.add(identity);
105
+ identities.push({ facet, key });
106
+ }
107
+ return identities;
108
+ }
109
+ function identityPredicate(identities) {
110
+ return {
111
+ sql: identities.map(() => "(e.facet = ? AND e.tag_key = ?)").join(" OR "),
112
+ params: identities.flatMap((identity) => [identity.facet, identity.key]),
113
+ };
114
+ }
115
+ // ---------------------------------------------------------------- pages
116
+ function queryTags(context, filter, options) {
117
+ const limit = boundedLimit(options.limit, TAG_QUERY_LIMIT_DEFAULT, TAG_QUERY_LIMIT_MAX, "Tag query limit");
118
+ const where = filterClause(filter);
119
+ const cursor = decodeCursor(options.cursor);
120
+ const cursorClause = cursor
121
+ ? " AND (a.created_at > ? OR (a.created_at = ? AND a.artifact_id > ?))"
122
+ : "";
123
+ const cursorParams = cursor
124
+ ? [cursor.createdAt, cursor.createdAt, cursor.artifactId]
125
+ : [];
126
+ const rows = context.store.db
127
+ .prepare(`WITH effective AS (${EFFECTIVE_TAGS_SQL})
128
+ SELECT a.artifact_id, a.label, a.kind, a.created_at
129
+ FROM artifacts a
130
+ WHERE ${where.sql}${cursorClause}
131
+ ORDER BY a.created_at, a.artifact_id
132
+ LIMIT ?`)
133
+ .all(...where.params, ...cursorParams, limit + 1);
134
+ const page = rows.slice(0, limit);
135
+ const total = countMatches(context, where);
136
+ const last = page.at(-1);
137
+ const tags = effectiveTagsFor(context, page.map((row) => row.artifact_id));
138
+ return {
139
+ artifacts: page.map((row) => ({
140
+ artifact: artifactOf(context, row),
141
+ tags: tags.get(row.artifact_id) ?? [],
142
+ })),
143
+ total,
144
+ ...(rows.length > limit && last
145
+ ? { nextCursor: encodeCursor(last.created_at, last.artifact_id) }
146
+ : {}),
147
+ };
148
+ }
149
+ function candidateIds(context, filter) {
150
+ const where = filterClause(filter);
151
+ const rows = context.store.db
152
+ .prepare(`WITH effective AS (${EFFECTIVE_TAGS_SQL})
153
+ SELECT a.artifact_id
154
+ FROM artifacts a
155
+ WHERE ${where.sql}
156
+ ORDER BY a.created_at, a.artifact_id
157
+ LIMIT ?`)
158
+ .all(...where.params, TAG_CANDIDATE_MAX);
159
+ return rows.map((row) => row.artifact_id);
160
+ }
161
+ function countMatches(context, where) {
162
+ const row = context.store.db
163
+ .prepare(`WITH effective AS (${EFFECTIVE_TAGS_SQL})
164
+ SELECT COUNT(*) AS total FROM artifacts a WHERE ${where.sql}`)
165
+ .get(...where.params);
166
+ return row.total;
167
+ }
168
+ function artifactOf(context, row) {
169
+ return context.artifact({
170
+ artifact_id: row.artifact_id,
171
+ label: row.label,
172
+ kind: row.kind,
173
+ created_at: row.created_at,
174
+ });
175
+ }
176
+ /** Effective tags for one page of artifacts, in one statement. */
177
+ function effectiveTagsFor(context, artifactIds) {
178
+ const tags = new Map();
179
+ if (artifactIds.length === 0)
180
+ return tags;
181
+ const placeholders = artifactIds.map(() => "?").join(", ");
182
+ const rows = context.store.db
183
+ .prepare(`WITH effective AS (${EFFECTIVE_TAGS_SQL})
184
+ SELECT e.artifact_id, e.facet, e.tag_key, e.label, e.origin,
185
+ t.created_at, t.entity_id
186
+ FROM effective e
187
+ JOIN artifact_tags t
188
+ ON t.artifact_id = e.artifact_id
189
+ AND t.origin = e.origin
190
+ AND t.facet = e.facet
191
+ AND t.tag_key = e.tag_key
192
+ WHERE e.artifact_id IN (${placeholders})
193
+ ORDER BY e.artifact_id, e.facet, e.tag_key, e.origin DESC`)
194
+ .all(...artifactIds);
195
+ for (const row of rows) {
196
+ const existing = tags.get(row.artifact_id) ?? [];
197
+ // origin DESC puts 'manual' first, so a manual assignment claims the
198
+ // identity and the automatic duplicate is dropped, exactly as
199
+ // engine.tags.read reports it.
200
+ if (existing.some((tag) => tag.facet === row.facet && tag.key === row.tag_key)) {
201
+ continue;
202
+ }
203
+ existing.push({
204
+ facet: row.facet,
205
+ key: row.tag_key,
206
+ label: row.label,
207
+ origin: row.origin,
208
+ ...(row.entity_id ? { entityId: row.entity_id } : {}),
209
+ createdAt: row.created_at,
210
+ });
211
+ tags.set(row.artifact_id, existing);
212
+ }
213
+ return tags;
214
+ }
215
+ // --------------------------------------------------------------- facets
216
+ function facetCounts(context, filter, requestedLimit) {
217
+ const limit = boundedLimit(requestedLimit, TAG_FACET_LIMIT_DEFAULT, TAG_FACET_LIMIT_MAX, "Tag facet limit");
218
+ const where = filterClause(filter);
219
+ return countedFacets(context, where, limit, undefined);
220
+ }
221
+ function suggestTags(context, query) {
222
+ const limit = boundedLimit(query.limit, TAG_FACET_LIMIT_DEFAULT, TAG_FACET_LIMIT_MAX, "Tag suggestion limit");
223
+ const prefix = normalizeTagPrefix(query.prefix ?? "");
224
+ const parts = [];
225
+ const params = [];
226
+ if (query.facet) {
227
+ parts.push("e.facet = ?");
228
+ params.push(assertTagFacet(query.facet));
229
+ }
230
+ if (prefix) {
231
+ parts.push("e.tag_key LIKE ? ESCAPE '\\'");
232
+ params.push(`${escapeLike(prefix)}%`);
233
+ }
234
+ const where = filterClause({
235
+ ...(query.kinds ? { kinds: query.kinds } : {}),
236
+ });
237
+ return countedFacets(context, where, limit, {
238
+ sql: parts.length > 0 ? parts.join(" AND ") : "1=1",
239
+ params,
240
+ });
241
+ }
242
+ /**
243
+ * Distinct matching artifacts per identity — never assignment rows and
244
+ * never only the current page — with the manual display label preferred.
245
+ */
246
+ function countedFacets(context, where, limit, identityFilter) {
247
+ const extra = identityFilter ? ` AND (${identityFilter.sql})` : "";
248
+ const rows = context.store.db
249
+ .prepare(`WITH effective AS (${EFFECTIVE_TAGS_SQL})
250
+ SELECT e.facet, e.tag_key, COUNT(DISTINCT e.artifact_id) AS artifacts
251
+ FROM effective e
252
+ JOIN artifacts a ON a.artifact_id = e.artifact_id
253
+ WHERE ${where.sql}${extra}
254
+ GROUP BY e.facet, e.tag_key
255
+ ORDER BY artifacts DESC, e.facet, e.tag_key
256
+ LIMIT ?`)
257
+ .all(...where.params, ...(identityFilter?.params ?? []), limit);
258
+ if (rows.length === 0)
259
+ return [];
260
+ const labels = preferredLabels(context, rows);
261
+ return rows.map((row) => ({
262
+ facet: row.facet,
263
+ key: row.tag_key,
264
+ label: labels.get(tagIdentity(row.facet, row.tag_key)) ?? row.tag_key,
265
+ artifacts: row.artifacts,
266
+ }));
267
+ }
268
+ function preferredLabels(context, rows) {
269
+ const predicate = rows.map(() => "(facet = ? AND tag_key = ?)").join(" OR ");
270
+ const params = rows.flatMap((row) => [row.facet, row.tag_key]);
271
+ const labelRows = context.store.db
272
+ .prepare(`WITH effective AS (${EFFECTIVE_TAGS_SQL})
273
+ SELECT facet, tag_key, origin, label FROM effective
274
+ WHERE ${predicate}
275
+ ORDER BY facet, tag_key, origin DESC, label`)
276
+ .all(...params);
277
+ const labels = new Map();
278
+ for (const row of labelRows) {
279
+ // origin DESC lists 'manual' first, so the user's spelling wins.
280
+ const identity = tagIdentity(row.facet, row.tag_key);
281
+ if (!labels.has(identity))
282
+ labels.set(identity, row.label);
283
+ }
284
+ return labels;
285
+ }
286
+ // -------------------------------------------------------------- helpers
287
+ function boundedLimit(requested, fallback, max, label) {
288
+ if (requested === undefined)
289
+ return fallback;
290
+ if (!Number.isInteger(requested) || requested < 1 || requested > max) {
291
+ throw new Error(`${label} must be an integer between 1 and ${max}`);
292
+ }
293
+ return requested;
294
+ }
295
+ function escapeLike(value) {
296
+ return value.replace(/[\\%_]/gu, (character) => `\\${character}`);
297
+ }
298
+ function encodeCursor(createdAt, artifactId) {
299
+ return Buffer.from(`${createdAt}:${artifactId}`, "utf8").toString("base64url");
300
+ }
301
+ function decodeCursor(cursor) {
302
+ if (cursor === undefined)
303
+ return undefined;
304
+ const decoded = Buffer.from(cursor, "base64url").toString("utf8");
305
+ const separator = decoded.indexOf(":");
306
+ const createdAt = Number(decoded.slice(0, separator));
307
+ const artifactId = decoded.slice(separator + 1);
308
+ if (separator < 0 || !Number.isInteger(createdAt) || !artifactId) {
309
+ throw new Error("Tag query cursor is invalid");
310
+ }
311
+ return { createdAt, artifactId };
312
+ }
313
+ //# sourceMappingURL=tag-queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-queries.js","sourceRoot":"","sources":["../src/tag-queries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Row-level tag comparisons shared by the write and transfer paths, so
3
+ * "this write would change nothing" means the same thing in both.
4
+ */
5
+ import type { DatabaseSync } from "@dolthub/doltlite";
6
+ import { type NormalizedTag, type TagFacet } from "./tag-values.js";
7
+ /** Match SQLite's BINARY ordering, including astral Unicode code points. */
8
+ export declare function compareTags(left: {
9
+ facet: TagFacet;
10
+ key: string;
11
+ }, right: {
12
+ facet: TagFacet;
13
+ key: string;
14
+ }): number;
15
+ interface StoredSnapshotRow {
16
+ artifact_id: string;
17
+ source_hash: string;
18
+ generator: string;
19
+ model: string | null;
20
+ extractor_version: string;
21
+ tag_count: number;
22
+ generation: number;
23
+ analyzed_at: number;
24
+ }
25
+ export declare function storedSnapshot(db: DatabaseSync, artifactId: string): StoredSnapshotRow | undefined;
26
+ /** Whether the stored automatic set is exactly `tags`, labels included. */
27
+ export declare function automaticTagsMatch(db: DatabaseSync, artifactId: string, tags: readonly NormalizedTag[]): boolean;
28
+ /** Whether a stored snapshot already records exactly this analysis run. */
29
+ export declare function snapshotMatches(current: StoredSnapshotRow | undefined, analysis: {
30
+ sourceHash: string;
31
+ generator: string;
32
+ model?: string;
33
+ extractorVersion: string;
34
+ }): boolean;
35
+ export {};
36
+ //# sourceMappingURL=tag-rows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-rows.d.ts","sourceRoot":"","sources":["../src/tag-rows.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EAAe,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEjF,4EAA4E;AAC5E,wBAAgB,WAAW,CACzB,IAAI,EAAE;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACtC,KAAK,EAAE;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACtC,MAAM,CAGR;AASD,UAAU,iBAAiB;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,cAAc,CAC5B,EAAE,EAAE,YAAY,EAChB,UAAU,EAAE,MAAM,GACjB,iBAAiB,GAAG,SAAS,CAS/B;AAED,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,YAAY,EAChB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,SAAS,aAAa,EAAE,GAC7B,OAAO,CAqBT;AAED,2EAA2E;AAC3E,wBAAgB,eAAe,CAC7B,OAAO,EAAE,iBAAiB,GAAG,SAAS,EACtC,QAAQ,EAAE;IACR,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;CAC1B,GACA,OAAO,CAQT"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Row-level tag comparisons shared by the write and transfer paths, so
3
+ * "this write would change nothing" means the same thing in both.
4
+ */
5
+ import { tagIdentity } from "./tag-values.js";
6
+ /** Match SQLite's BINARY ordering, including astral Unicode code points. */
7
+ export function compareTags(left, right) {
8
+ if (left.facet !== right.facet)
9
+ return left.facet < right.facet ? -1 : 1;
10
+ return Buffer.compare(Buffer.from(left.key), Buffer.from(right.key));
11
+ }
12
+ export function storedSnapshot(db, artifactId) {
13
+ return db
14
+ .prepare(`SELECT artifact_id, source_hash, generator, model, extractor_version,
15
+ tag_count, generation, analyzed_at
16
+ FROM artifact_tag_snapshots
17
+ WHERE artifact_id=?`)
18
+ .get(artifactId);
19
+ }
20
+ /** Whether the stored automatic set is exactly `tags`, labels included. */
21
+ export function automaticTagsMatch(db, artifactId, tags) {
22
+ const rows = db
23
+ .prepare(`SELECT facet, tag_key, label, entity_id
24
+ FROM artifact_tags
25
+ WHERE artifact_id=? AND origin='automatic'
26
+ ORDER BY facet, tag_key`)
27
+ .all(artifactId);
28
+ if (rows.length !== tags.length)
29
+ return false;
30
+ const byIdentity = new Map(rows.map((row) => [tagIdentity(row.facet, row.tag_key), row]));
31
+ return tags.every((tag) => {
32
+ const row = byIdentity.get(tagIdentity(tag.facet, tag.key));
33
+ return (row !== undefined &&
34
+ row.facet === tag.facet &&
35
+ row.tag_key === tag.key &&
36
+ row.label === tag.label &&
37
+ (row.entity_id ?? undefined) === tag.entityId);
38
+ });
39
+ }
40
+ /** Whether a stored snapshot already records exactly this analysis run. */
41
+ export function snapshotMatches(current, analysis) {
42
+ return (current !== undefined &&
43
+ current.source_hash === analysis.sourceHash &&
44
+ current.generator === analysis.generator &&
45
+ (current.model ?? undefined) === (analysis.model || undefined) &&
46
+ current.extractor_version === analysis.extractorVersion);
47
+ }
48
+ //# sourceMappingURL=tag-rows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-rows.js","sourceRoot":"","sources":["../src/tag-rows.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,WAAW,EAAqC,MAAM,iBAAiB,CAAC;AAEjF,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CACzB,IAAsC,EACtC,KAAuC;IAEvC,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AACvE,CAAC;AAoBD,MAAM,UAAU,cAAc,CAC5B,EAAgB,EAChB,UAAkB;IAElB,OAAO,EAAE;SACN,OAAO,CACN;;;2BAGqB,CACtB;SACA,GAAG,CAAC,UAAU,CAA6C,CAAC;AACjE,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,kBAAkB,CAChC,EAAgB,EAChB,UAAkB,EAClB,IAA8B;IAE9B,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CACN;;;+BAGyB,CAC1B;SACA,GAAG,CAAC,UAAU,CAA8B,CAAC;IAChD,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC9C,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IAC1F,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACxB,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5D,OAAO,CACL,GAAG,KAAK,SAAS;YACjB,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK;YACvB,GAAG,CAAC,OAAO,KAAK,GAAG,CAAC,GAAG;YACvB,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK;YACvB,CAAC,GAAG,CAAC,SAAS,IAAI,SAAS,CAAC,KAAK,GAAG,CAAC,QAAQ,CAC9C,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,eAAe,CAC7B,OAAsC,EACtC,QAKC;IAED,OAAO,CACL,OAAO,KAAK,SAAS;QACrB,OAAO,CAAC,WAAW,KAAK,QAAQ,CAAC,UAAU;QAC3C,OAAO,CAAC,SAAS,KAAK,QAAQ,CAAC,SAAS;QACxC,CAAC,OAAO,CAAC,KAAK,IAAI,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,IAAI,SAAS,CAAC;QAC9D,OAAO,CAAC,iBAAiB,KAAK,QAAQ,CAAC,gBAAgB,CACxD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,26 @@
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 type { ArtifactTagState, AssetTagSnapshotExport, EngineError, Result, TagImportResult } from "./engine-types.js";
17
+ import { EngineContext } from "./context.js";
18
+ export declare const TAG_SNAPSHOT_VERSION = 1;
19
+ type ReadTagState = (artifactId: string) => ArtifactTagState;
20
+ export declare function createTagTransferApi(context: EngineContext, tagState: ReadTagState): {
21
+ readAtRevision: (artifactId: string, revision: string) => Result<ArtifactTagState, EngineError>;
22
+ export: (artifactId: string) => Result<AssetTagSnapshotExport, EngineError>;
23
+ import: (artifactId: string, snapshot: AssetTagSnapshotExport) => Promise<Result<TagImportResult, EngineError>>;
24
+ };
25
+ export {};
26
+ //# sourceMappingURL=tag-transfer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-transfer.d.ts","sourceRoot":"","sources":["../src/tag-transfer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAEhB,sBAAsB,EAGtB,WAAW,EACX,MAAM,EACN,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,aAAa,EAA0B,MAAM,cAAc,CAAC;AAoBrE,eAAO,MAAM,oBAAoB,IAAI,CAAC;AA6BtC,KAAK,YAAY,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,gBAAgB,CAAC;AAE7D,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,YAAY;iCAIN,MAAM,YACR,MAAM,KACf,MAAM,CAAC,gBAAgB,EAAE,WAAW,CAAC;yBAEnB,MAAM,KAAG,MAAM,CAAC,sBAAsB,EAAE,WAAW,CAAC;yBAG3D,MAAM,YACR,sBAAsB,KAC/B,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;EAGnD"}