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.
- package/README.md +6 -0
- package/dist/catalog-metadata.d.ts +1 -1
- package/dist/catalog-metadata.js +1 -1
- package/dist/domain.js +15 -5
- package/dist/domain.js.map +1 -1
- package/dist/engine-types.d.ts +140 -1
- package/dist/engine-types.d.ts.map +1 -1
- package/dist/engine-types.js.map +1 -1
- package/dist/engine.d.ts +21 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +3 -0
- package/dist/engine.js.map +1 -1
- package/dist/fork.d.ts +3 -4
- package/dist/fork.d.ts.map +1 -1
- package/dist/fork.js +4 -4
- package/dist/fork.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/merge-policy.d.ts +3 -4
- package/dist/merge-policy.d.ts.map +1 -1
- package/dist/merge-policy.js +5 -4
- package/dist/merge-policy.js.map +1 -1
- package/dist/migrate-tags-v24.d.ts +12 -0
- package/dist/migrate-tags-v24.d.ts.map +1 -0
- package/dist/migrate-tags-v24.js +40 -0
- package/dist/migrate-tags-v24.js.map +1 -0
- package/dist/mvp-contracts.d.ts +2 -2
- package/dist/mvp-contracts.js +1 -1
- package/dist/schema.d.ts +3 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +59 -0
- package/dist/schema.js.map +1 -1
- package/dist/store.d.ts +6 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +150 -54
- package/dist/store.js.map +1 -1
- package/dist/tag-merge.d.ts +4 -0
- package/dist/tag-merge.d.ts.map +1 -0
- package/dist/tag-merge.js +85 -0
- package/dist/tag-merge.js.map +1 -0
- package/dist/tag-queries.d.ts +30 -0
- package/dist/tag-queries.d.ts.map +1 -0
- package/dist/tag-queries.js +313 -0
- package/dist/tag-queries.js.map +1 -0
- package/dist/tag-rows.d.ts +36 -0
- package/dist/tag-rows.d.ts.map +1 -0
- package/dist/tag-rows.js +48 -0
- package/dist/tag-rows.js.map +1 -0
- package/dist/tag-transfer.d.ts +26 -0
- package/dist/tag-transfer.d.ts.map +1 -0
- package/dist/tag-transfer.js +341 -0
- package/dist/tag-transfer.js.map +1 -0
- package/dist/tag-validation.d.ts +15 -0
- package/dist/tag-validation.d.ts.map +1 -0
- package/dist/tag-validation.js +36 -0
- package/dist/tag-validation.js.map +1 -0
- package/dist/tag-values.d.ts +68 -0
- package/dist/tag-values.d.ts.map +1 -0
- package/dist/tag-values.js +107 -0
- package/dist/tag-values.js.map +1 -0
- package/dist/tags.d.ts +44 -0
- package/dist/tags.d.ts.map +1 -0
- package/dist/tags.js +488 -0
- package/dist/tags.js.map +1 -0
- package/docs/doltlite-staging.md +35 -15
- package/docs/edit-performance.md +2 -4
- package/docs/engine-layout.md +92 -4
- package/docs/mvp-prd.md +2 -1
- package/docs/release-evidence.md +74 -35
- package/docs/temporal-search-performance.md +4 -6
- package/fixtures/v5/contract-fixtures.json +3 -3
- 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"}
|
package/dist/tag-rows.js
ADDED
|
@@ -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"}
|