@rockhall/electron-offline-content 0.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/CHANGELOG.md +384 -0
- package/LICENSE +21 -0
- package/README.md +794 -0
- package/dist/internal/asset-file-name.cjs +13 -0
- package/dist/internal/asset-file-name.cjs.map +1 -0
- package/dist/internal/asset-file-name.d.cts +6 -0
- package/dist/internal/asset-file-name.d.cts.map +1 -0
- package/dist/internal/asset-file-name.d.ts +6 -0
- package/dist/internal/asset-file-name.d.ts.map +1 -0
- package/dist/internal/asset-file-name.js +12 -0
- package/dist/internal/asset-file-name.js.map +1 -0
- package/dist/internal/asset-key.cjs +30 -0
- package/dist/internal/asset-key.cjs.map +1 -0
- package/dist/internal/asset-key.d.cts +19 -0
- package/dist/internal/asset-key.d.cts.map +1 -0
- package/dist/internal/asset-key.d.ts +19 -0
- package/dist/internal/asset-key.d.ts.map +1 -0
- package/dist/internal/asset-key.js +27 -0
- package/dist/internal/asset-key.js.map +1 -0
- package/dist/internal/log-format.cjs +98 -0
- package/dist/internal/log-format.cjs.map +1 -0
- package/dist/internal/log-format.d.cts +10 -0
- package/dist/internal/log-format.d.cts.map +1 -0
- package/dist/internal/log-format.d.ts +10 -0
- package/dist/internal/log-format.d.ts.map +1 -0
- package/dist/internal/log-format.js +97 -0
- package/dist/internal/log-format.js.map +1 -0
- package/dist/internal/media-kind.cjs +46 -0
- package/dist/internal/media-kind.cjs.map +1 -0
- package/dist/internal/media-kind.d.cts +20 -0
- package/dist/internal/media-kind.d.cts.map +1 -0
- package/dist/internal/media-kind.d.ts +20 -0
- package/dist/internal/media-kind.d.ts.map +1 -0
- package/dist/internal/media-kind.js +45 -0
- package/dist/internal/media-kind.js.map +1 -0
- package/dist/internal/url-warn.cjs +14 -0
- package/dist/internal/url-warn.cjs.map +1 -0
- package/dist/internal/url-warn.d.cts +10 -0
- package/dist/internal/url-warn.d.cts.map +1 -0
- package/dist/internal/url-warn.d.ts +10 -0
- package/dist/internal/url-warn.d.ts.map +1 -0
- package/dist/internal/url-warn.js +13 -0
- package/dist/internal/url-warn.js.map +1 -0
- package/dist/internal/validation.cjs +222 -0
- package/dist/internal/validation.cjs.map +1 -0
- package/dist/internal/validation.d.cts +78 -0
- package/dist/internal/validation.d.cts.map +1 -0
- package/dist/internal/validation.d.ts +78 -0
- package/dist/internal/validation.d.ts.map +1 -0
- package/dist/internal/validation.js +196 -0
- package/dist/internal/validation.js.map +1 -0
- package/dist/main/asset-download.cjs +265 -0
- package/dist/main/asset-download.cjs.map +1 -0
- package/dist/main/asset-download.d.cts +12 -0
- package/dist/main/asset-download.d.cts.map +1 -0
- package/dist/main/asset-download.d.ts +12 -0
- package/dist/main/asset-download.d.ts.map +1 -0
- package/dist/main/asset-download.js +263 -0
- package/dist/main/asset-download.js.map +1 -0
- package/dist/main/database.cjs +473 -0
- package/dist/main/database.cjs.map +1 -0
- package/dist/main/database.d.cts +81 -0
- package/dist/main/database.d.cts.map +1 -0
- package/dist/main/database.d.ts +81 -0
- package/dist/main/database.d.ts.map +1 -0
- package/dist/main/database.js +472 -0
- package/dist/main/database.js.map +1 -0
- package/dist/main/index.cjs +22 -0
- package/dist/main/index.d.cts +7 -0
- package/dist/main/index.d.ts +7 -0
- package/dist/main/index.js +7 -0
- package/dist/main/media-cache.cjs +862 -0
- package/dist/main/media-cache.cjs.map +1 -0
- package/dist/main/media-cache.d.cts +134 -0
- package/dist/main/media-cache.d.cts.map +1 -0
- package/dist/main/media-cache.d.ts +134 -0
- package/dist/main/media-cache.d.ts.map +1 -0
- package/dist/main/media-cache.js +854 -0
- package/dist/main/media-cache.js.map +1 -0
- package/dist/main/storage-root-lock.cjs +124 -0
- package/dist/main/storage-root-lock.cjs.map +1 -0
- package/dist/main/storage-root-lock.d.cts +11 -0
- package/dist/main/storage-root-lock.d.cts.map +1 -0
- package/dist/main/storage-root-lock.d.ts +11 -0
- package/dist/main/storage-root-lock.d.ts.map +1 -0
- package/dist/main/storage-root-lock.js +120 -0
- package/dist/main/storage-root-lock.js.map +1 -0
- package/dist/main/store.cjs +197 -0
- package/dist/main/store.cjs.map +1 -0
- package/dist/main/store.d.cts +83 -0
- package/dist/main/store.d.cts.map +1 -0
- package/dist/main/store.d.ts +83 -0
- package/dist/main/store.d.ts.map +1 -0
- package/dist/main/store.js +195 -0
- package/dist/main/store.js.map +1 -0
- package/dist/preload/index.cjs +36 -0
- package/dist/preload/index.cjs.map +1 -0
- package/dist/preload/index.d.cts +14 -0
- package/dist/preload/index.d.cts.map +1 -0
- package/dist/preload/index.d.ts +14 -0
- package/dist/preload/index.d.ts.map +1 -0
- package/dist/preload/index.js +34 -0
- package/dist/preload/index.js.map +1 -0
- package/dist/react/index.cjs +199 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +50 -0
- package/dist/react/index.d.cts.map +1 -0
- package/dist/react/index.d.ts +50 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +191 -0
- package/dist/react/index.js.map +1 -0
- package/dist/renderer/helpers.cjs +36 -0
- package/dist/renderer/helpers.cjs.map +1 -0
- package/dist/renderer/helpers.d.cts +11 -0
- package/dist/renderer/helpers.d.cts.map +1 -0
- package/dist/renderer/helpers.d.ts +11 -0
- package/dist/renderer/helpers.d.ts.map +1 -0
- package/dist/renderer/helpers.js +35 -0
- package/dist/renderer/helpers.js.map +1 -0
- package/dist/renderer/index.cjs +20 -0
- package/dist/renderer/index.cjs.map +1 -0
- package/dist/renderer/index.d.cts +14 -0
- package/dist/renderer/index.d.cts.map +1 -0
- package/dist/renderer/index.d.ts +14 -0
- package/dist/renderer/index.d.ts.map +1 -0
- package/dist/renderer/index.js +14 -0
- package/dist/renderer/index.js.map +1 -0
- package/dist/renderer/runtime.cjs +278 -0
- package/dist/renderer/runtime.cjs.map +1 -0
- package/dist/renderer/runtime.d.cts +35 -0
- package/dist/renderer/runtime.d.cts.map +1 -0
- package/dist/renderer/runtime.d.ts +35 -0
- package/dist/renderer/runtime.d.ts.map +1 -0
- package/dist/renderer/runtime.js +273 -0
- package/dist/renderer/runtime.js.map +1 -0
- package/dist/renderer/window-globals.d.cts +9 -0
- package/dist/renderer/window-globals.d.cts.map +1 -0
- package/dist/renderer/window-globals.d.ts +9 -0
- package/dist/renderer/window-globals.d.ts.map +1 -0
- package/dist/shared/errors.cjs +102 -0
- package/dist/shared/errors.cjs.map +1 -0
- package/dist/shared/errors.d.cts +45 -0
- package/dist/shared/errors.d.cts.map +1 -0
- package/dist/shared/errors.d.ts +45 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/errors.js +93 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/ipc.cjs +14 -0
- package/dist/shared/ipc.cjs.map +1 -0
- package/dist/shared/ipc.d.cts +12 -0
- package/dist/shared/ipc.d.cts.map +1 -0
- package/dist/shared/ipc.d.ts +12 -0
- package/dist/shared/ipc.d.ts.map +1 -0
- package/dist/shared/ipc.js +13 -0
- package/dist/shared/ipc.js.map +1 -0
- package/dist/shared/normalize.cjs +19 -0
- package/dist/shared/normalize.cjs.map +1 -0
- package/dist/shared/normalize.d.cts +11 -0
- package/dist/shared/normalize.d.cts.map +1 -0
- package/dist/shared/normalize.d.ts +11 -0
- package/dist/shared/normalize.d.ts.map +1 -0
- package/dist/shared/normalize.js +18 -0
- package/dist/shared/normalize.js.map +1 -0
- package/dist/shared/pagination.cjs +32 -0
- package/dist/shared/pagination.cjs.map +1 -0
- package/dist/shared/pagination.d.cts +14 -0
- package/dist/shared/pagination.d.cts.map +1 -0
- package/dist/shared/pagination.d.ts +14 -0
- package/dist/shared/pagination.d.ts.map +1 -0
- package/dist/shared/pagination.js +28 -0
- package/dist/shared/pagination.js.map +1 -0
- package/dist/shared/stem.cjs +16 -0
- package/dist/shared/stem.cjs.map +1 -0
- package/dist/shared/stem.d.cts +6 -0
- package/dist/shared/stem.d.cts.map +1 -0
- package/dist/shared/stem.d.ts +6 -0
- package/dist/shared/stem.d.ts.map +1 -0
- package/dist/shared/stem.js +14 -0
- package/dist/shared/stem.js.map +1 -0
- package/dist/shared/types.cjs +15 -0
- package/dist/shared/types.cjs.map +1 -0
- package/dist/shared/types.d.cts +234 -0
- package/dist/shared/types.d.cts.map +1 -0
- package/dist/shared/types.d.ts +234 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +14 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +120 -0
- package/skills/authenticated-downloads/SKILL.md +203 -0
- package/skills/cache-configuration/SKILL.md +357 -0
- package/skills/cache-configuration/references/options.md +356 -0
- package/skills/getting-started/SKILL.md +407 -0
- package/skills/production-checklist/SKILL.md +397 -0
- package/skills/react-rendering/SKILL.md +424 -0
- package/skills/react-rendering/references/hooks.md +443 -0
- package/skills/store-authoring/SKILL.md +369 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_internal_validation = require("../internal/validation.cjs");
|
|
3
|
+
const require_shared_pagination = require("../shared/pagination.cjs");
|
|
4
|
+
let node_fs = require("node:fs");
|
|
5
|
+
let node_module = require("node:module");
|
|
6
|
+
let node_path = require("node:path");
|
|
7
|
+
//#region src/main/database.ts
|
|
8
|
+
const { DatabaseSync } = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href)("node:sqlite");
|
|
9
|
+
var MediaCacheDatabase = class {
|
|
10
|
+
root;
|
|
11
|
+
options;
|
|
12
|
+
db;
|
|
13
|
+
closed = false;
|
|
14
|
+
constructor(root, options) {
|
|
15
|
+
this.root = root;
|
|
16
|
+
this.options = options;
|
|
17
|
+
const sqliteDir = (0, node_path.join)(root, "sqlite");
|
|
18
|
+
(0, node_fs.mkdirSync)(sqliteDir, { recursive: true });
|
|
19
|
+
this.db = new DatabaseSync((0, node_path.join)(sqliteDir, "media-cache.db"));
|
|
20
|
+
this.db.exec("PRAGMA journal_mode = WAL;");
|
|
21
|
+
this.db.exec("PRAGMA foreign_keys = ON;");
|
|
22
|
+
this.migrate();
|
|
23
|
+
}
|
|
24
|
+
close() {
|
|
25
|
+
if (this.closed) return;
|
|
26
|
+
this.closed = true;
|
|
27
|
+
this.db.close();
|
|
28
|
+
}
|
|
29
|
+
assertNotClosed() {
|
|
30
|
+
if (this.closed) throw new Error("MediaCacheDatabase is closed");
|
|
31
|
+
}
|
|
32
|
+
/** @internal Used only by prepareDevRuntimeState in dev passthrough startup. */
|
|
33
|
+
clearAllState() {
|
|
34
|
+
this.assertNotClosed();
|
|
35
|
+
this.db.exec("BEGIN");
|
|
36
|
+
try {
|
|
37
|
+
this.db.prepare(`DELETE FROM pending_deletions`).run();
|
|
38
|
+
this.db.prepare(`DELETE FROM asset_indexes`).run();
|
|
39
|
+
this.db.prepare(`DELETE FROM assets`).run();
|
|
40
|
+
this.db.prepare(`DELETE FROM index_definitions`).run();
|
|
41
|
+
this.db.prepare(`DELETE FROM generations`).run();
|
|
42
|
+
this.db.prepare(`DELETE FROM sync_runs`).run();
|
|
43
|
+
this.db.prepare(`DELETE FROM status_snapshot`).run();
|
|
44
|
+
this.db.exec("COMMIT");
|
|
45
|
+
} catch (error) {
|
|
46
|
+
this.db.exec("ROLLBACK");
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
loadStatus() {
|
|
51
|
+
this.assertNotClosed();
|
|
52
|
+
const row = this.db.prepare(`SELECT status_json
|
|
53
|
+
FROM status_snapshot
|
|
54
|
+
WHERE scope_type = 'global' AND scope_key = '*'`).get();
|
|
55
|
+
if (!row) return null;
|
|
56
|
+
return require_internal_validation.parseJsonWithSchema(require_internal_validation.parseWithSchema(require_internal_validation.statusSnapshotRowSchema, row, "status snapshot row").status_json, require_internal_validation.mediaCacheStatusSchema, "persisted media cache status");
|
|
57
|
+
}
|
|
58
|
+
saveStatus(status, now) {
|
|
59
|
+
this.assertNotClosed();
|
|
60
|
+
const statusJson = require_internal_validation.stringifyWithSchema(status, require_internal_validation.mediaCacheStatusSchema, "media cache status");
|
|
61
|
+
this.db.exec("BEGIN");
|
|
62
|
+
try {
|
|
63
|
+
this.db.prepare(`DELETE FROM status_snapshot
|
|
64
|
+
WHERE scope_type = 'global' AND scope_key = '*'`).run();
|
|
65
|
+
this.db.prepare(`INSERT INTO status_snapshot (scope_type, scope_key, status_json, updated_at_ms)
|
|
66
|
+
VALUES ('global', '*', ?, ?)`).run(statusJson, now);
|
|
67
|
+
this.db.exec("COMMIT");
|
|
68
|
+
} catch (error) {
|
|
69
|
+
this.db.exec("ROLLBACK");
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
createSyncRun(now) {
|
|
74
|
+
this.assertNotClosed();
|
|
75
|
+
const statsJson = require_internal_validation.stringifyWithSchema(emptyStats(), require_internal_validation.syncRunStatsSchema, "sync run stats");
|
|
76
|
+
const result = this.db.prepare(`INSERT INTO sync_runs (started_at_ms, status, stats_json)
|
|
77
|
+
VALUES (?, 'running', ?)`).run(now, statsJson);
|
|
78
|
+
return Number(result.lastInsertRowid);
|
|
79
|
+
}
|
|
80
|
+
completeSyncRun(id, status, now, stats, errorCode = null, errorMessage = null) {
|
|
81
|
+
this.assertNotClosed();
|
|
82
|
+
const statsJson = require_internal_validation.stringifyWithSchema(stats, require_internal_validation.syncRunStatsSchema, "sync run stats");
|
|
83
|
+
this.db.prepare(`UPDATE sync_runs
|
|
84
|
+
SET finished_at_ms = ?, status = ?, error_code = ?, error_message = ?, stats_json = ?
|
|
85
|
+
WHERE id = ?`).run(now, status, errorCode, errorMessage, statsJson, id);
|
|
86
|
+
return this.getSyncRun(id);
|
|
87
|
+
}
|
|
88
|
+
getSyncRun(id) {
|
|
89
|
+
this.assertNotClosed();
|
|
90
|
+
const row = this.db.prepare(`SELECT id, started_at_ms, finished_at_ms, status, error_code, error_message, stats_json
|
|
91
|
+
FROM sync_runs
|
|
92
|
+
WHERE id = ?`).get(id);
|
|
93
|
+
if (!row) return null;
|
|
94
|
+
const validatedRow = require_internal_validation.parseWithSchema(require_internal_validation.syncRunRowSchema, row, "sync run row");
|
|
95
|
+
return {
|
|
96
|
+
id: validatedRow.id,
|
|
97
|
+
status: validatedRow.status,
|
|
98
|
+
startedAt: validatedRow.started_at_ms,
|
|
99
|
+
finishedAt: validatedRow.finished_at_ms,
|
|
100
|
+
errorCode: validatedRow.error_code,
|
|
101
|
+
errorMessage: validatedRow.error_message,
|
|
102
|
+
stats: require_internal_validation.parseJsonWithSchema(validatedRow.stats_json, require_internal_validation.syncRunStatsSchema, `sync run ${validatedRow.id} stats`)
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
pruneSyncHistory(limit) {
|
|
106
|
+
this.assertNotClosed();
|
|
107
|
+
if (limit < 1) return;
|
|
108
|
+
this.db.exec("BEGIN");
|
|
109
|
+
try {
|
|
110
|
+
const rows = require_internal_validation.parseWithSchema(require_internal_validation.syncRunIdRowSchema.array(), this.db.prepare(`SELECT id
|
|
111
|
+
FROM sync_runs
|
|
112
|
+
ORDER BY started_at_ms DESC
|
|
113
|
+
LIMIT -1 OFFSET ?`).all(limit), "sync run history rows");
|
|
114
|
+
if (rows.length === 0) {
|
|
115
|
+
this.db.exec("COMMIT");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const ids = rows.map((row) => row.id);
|
|
119
|
+
const placeholders = ids.map(() => "?").join(", ");
|
|
120
|
+
this.db.prepare(`DELETE FROM sync_runs WHERE id IN (${placeholders})`).run(...ids);
|
|
121
|
+
this.db.exec("COMMIT");
|
|
122
|
+
} catch (error) {
|
|
123
|
+
this.db.exec("ROLLBACK");
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
getActiveGenerationId() {
|
|
128
|
+
this.assertNotClosed();
|
|
129
|
+
const row = this.db.prepare(`SELECT id AS generation_id
|
|
130
|
+
FROM generations
|
|
131
|
+
WHERE is_active = 1`).get();
|
|
132
|
+
return row ? require_internal_validation.parseWithSchema(require_internal_validation.activeGenerationRowSchema, row, "active generation row").generation_id : null;
|
|
133
|
+
}
|
|
134
|
+
listStagedGenerationIds() {
|
|
135
|
+
this.assertNotClosed();
|
|
136
|
+
return require_internal_validation.parseWithSchema(require_internal_validation.generationIdRowSchema.array(), this.db.prepare(`SELECT id
|
|
137
|
+
FROM generations
|
|
138
|
+
WHERE is_active = 0
|
|
139
|
+
ORDER BY id ASC`).all(), "staged generation rows").map((row) => row.id);
|
|
140
|
+
}
|
|
141
|
+
createStagedGeneration(manifest, now) {
|
|
142
|
+
this.assertNotClosed();
|
|
143
|
+
this.db.exec("BEGIN");
|
|
144
|
+
try {
|
|
145
|
+
const generationInsert = this.db.prepare(`INSERT INTO generations (snapshot_id, retrieved_at, expires_at, committed_at_ms, is_active)
|
|
146
|
+
VALUES (?, ?, ?, ?, 0)`).run(manifest.snapshotId ?? null, manifest.retrievedAt ?? null, manifest.expiresAt ?? null, now);
|
|
147
|
+
const generationId = Number(generationInsert.lastInsertRowid);
|
|
148
|
+
const indexDefStmt = this.db.prepare(`INSERT INTO index_definitions (generation_id, name, cardinality, required, builtin)
|
|
149
|
+
VALUES (?, ?, ?, ?, ?)`);
|
|
150
|
+
for (const def of manifest.indexDefinitions) indexDefStmt.run(generationId, def.name, def.cardinality, def.required ? 1 : 0, def.builtin ? 1 : 0);
|
|
151
|
+
const assetStmt = this.db.prepare(`INSERT INTO assets (
|
|
152
|
+
generation_id, asset_key, display_key, version, mime_type, media_kind, file_name, file_stem,
|
|
153
|
+
byte_length, url, metadata_json, indexes_json, order_index, relative_path
|
|
154
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`);
|
|
155
|
+
const indexStmt = this.db.prepare(`INSERT INTO asset_indexes (generation_id, asset_key, index_name, index_value)
|
|
156
|
+
VALUES (?, ?, ?, ?)`);
|
|
157
|
+
manifest.assets.forEach((asset, assetOrder) => {
|
|
158
|
+
assetStmt.run(generationId, asset.key, asset.displayKey, asset.version, asset.mimeType, asset.mediaKind, asset.fileName, asset.fileStem, asset.byteLength ?? null, asset.url, JSON.stringify(asset.metadata), JSON.stringify(asset.indexes), assetOrder);
|
|
159
|
+
for (const [indexName, indexValue] of Object.entries(asset.indexes)) if (Array.isArray(indexValue)) for (const v of new Set(indexValue)) indexStmt.run(generationId, asset.key, indexName, v);
|
|
160
|
+
else indexStmt.run(generationId, asset.key, indexName, indexValue);
|
|
161
|
+
});
|
|
162
|
+
this.db.exec("COMMIT");
|
|
163
|
+
return generationId;
|
|
164
|
+
} catch (error) {
|
|
165
|
+
this.db.exec("ROLLBACK");
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
deleteGeneration(generationId) {
|
|
170
|
+
this.assertNotClosed();
|
|
171
|
+
this.db.exec("BEGIN");
|
|
172
|
+
try {
|
|
173
|
+
this.db.prepare(`DELETE FROM asset_indexes WHERE generation_id = ?`).run(generationId);
|
|
174
|
+
this.db.prepare(`DELETE FROM assets WHERE generation_id = ?`).run(generationId);
|
|
175
|
+
this.db.prepare(`DELETE FROM index_definitions WHERE generation_id = ?`).run(generationId);
|
|
176
|
+
this.db.prepare(`DELETE FROM generations WHERE id = ?`).run(generationId);
|
|
177
|
+
this.db.exec("COMMIT");
|
|
178
|
+
} catch (error) {
|
|
179
|
+
this.db.exec("ROLLBACK");
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
setAssetRelativePath(generationId, assetKey, relativePath) {
|
|
184
|
+
this.assertNotClosed();
|
|
185
|
+
this.db.prepare(`UPDATE assets
|
|
186
|
+
SET relative_path = ?
|
|
187
|
+
WHERE generation_id = ? AND asset_key = ?`).run(relativePath, generationId, assetKey);
|
|
188
|
+
}
|
|
189
|
+
setAssetDownloadState(generationId, assetKey, relativePath, fallbackMimeType) {
|
|
190
|
+
this.assertNotClosed();
|
|
191
|
+
this.db.prepare(`UPDATE assets
|
|
192
|
+
SET
|
|
193
|
+
relative_path = ?,
|
|
194
|
+
mime_type = COALESCE(mime_type, ?)
|
|
195
|
+
WHERE generation_id = ? AND asset_key = ?`).run(relativePath, fallbackMimeType, generationId, assetKey);
|
|
196
|
+
}
|
|
197
|
+
getGenerationAssets(generationId) {
|
|
198
|
+
this.assertNotClosed();
|
|
199
|
+
return require_internal_validation.parseWithSchema(require_internal_validation.generationAssetRowSchema.array(), this.db.prepare(`SELECT
|
|
200
|
+
asset_key AS assetKey,
|
|
201
|
+
version,
|
|
202
|
+
relative_path AS relativePath,
|
|
203
|
+
mime_type AS mimeType,
|
|
204
|
+
url
|
|
205
|
+
FROM assets
|
|
206
|
+
WHERE generation_id = ?
|
|
207
|
+
ORDER BY order_index`).all(generationId), `generation ${generationId} asset rows`);
|
|
208
|
+
}
|
|
209
|
+
activateGeneration(generationId, now) {
|
|
210
|
+
this.assertNotClosed();
|
|
211
|
+
const previousActive = this.getActiveGenerationId();
|
|
212
|
+
this.db.exec("BEGIN");
|
|
213
|
+
try {
|
|
214
|
+
this.db.prepare(`UPDATE generations SET is_active = 0 WHERE is_active = 1`).run();
|
|
215
|
+
if (this.db.prepare(`UPDATE generations SET is_active = 1, committed_at_ms = ? WHERE id = ?`).run(now, generationId).changes !== 1) throw new Error(`Cannot activate missing generation ${generationId}`);
|
|
216
|
+
this.db.exec("COMMIT");
|
|
217
|
+
} catch (error) {
|
|
218
|
+
this.db.exec("ROLLBACK");
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
return previousActive;
|
|
222
|
+
}
|
|
223
|
+
clearPendingDeletionsForGeneration(generationId) {
|
|
224
|
+
this.assertNotClosed();
|
|
225
|
+
const activeRelativePaths = this.getGenerationAssets(generationId).flatMap((row) => row.relativePath ? [row.relativePath] : []);
|
|
226
|
+
this.deletePendingDeletionsByRelativePath(activeRelativePaths);
|
|
227
|
+
}
|
|
228
|
+
markPendingDeletion(assetKey, relativePath, generationId, deletionKey, deleteAfterMs) {
|
|
229
|
+
this.assertNotClosed();
|
|
230
|
+
this.db.prepare(`INSERT INTO pending_deletions (
|
|
231
|
+
deletion_key, logical_key, asset_key, relative_path, generation_id, delete_after_ms
|
|
232
|
+
) VALUES (?, ?, ?, ?, ?, ?)
|
|
233
|
+
ON CONFLICT(deletion_key)
|
|
234
|
+
DO UPDATE SET
|
|
235
|
+
logical_key = excluded.logical_key,
|
|
236
|
+
asset_key = excluded.asset_key,
|
|
237
|
+
relative_path = excluded.relative_path,
|
|
238
|
+
generation_id = excluded.generation_id,
|
|
239
|
+
delete_after_ms = excluded.delete_after_ms`).run(deletionKey, this.logicalKey(assetKey), assetKey, relativePath, generationId, deleteAfterMs);
|
|
240
|
+
}
|
|
241
|
+
getExpiredPendingDeletions(now) {
|
|
242
|
+
this.assertNotClosed();
|
|
243
|
+
return require_internal_validation.parseWithSchema(require_internal_validation.pendingDeletionSchema.array(), this.db.prepare(`SELECT deletion_key AS deletionKey, logical_key AS logicalKey, relative_path AS relativePath
|
|
244
|
+
FROM pending_deletions
|
|
245
|
+
WHERE delete_after_ms <= ?`).all(now), "expired pending deletions");
|
|
246
|
+
}
|
|
247
|
+
deletePendingDeletions(deletionKeys) {
|
|
248
|
+
this.assertNotClosed();
|
|
249
|
+
if (deletionKeys.length === 0) return;
|
|
250
|
+
const placeholders = deletionKeys.map(() => "?").join(", ");
|
|
251
|
+
this.db.prepare(`DELETE FROM pending_deletions WHERE deletion_key IN (${placeholders})`).run(...deletionKeys);
|
|
252
|
+
}
|
|
253
|
+
deletePendingDeletionsByRelativePath(relativePaths) {
|
|
254
|
+
this.assertNotClosed();
|
|
255
|
+
if (relativePaths.length === 0) return;
|
|
256
|
+
const placeholders = relativePaths.map(() => "?").join(", ");
|
|
257
|
+
this.db.prepare(`DELETE FROM pending_deletions WHERE relative_path IN (${placeholders})`).run(...relativePaths);
|
|
258
|
+
}
|
|
259
|
+
getProtocolAssetTarget(assetKey) {
|
|
260
|
+
this.assertNotClosed();
|
|
261
|
+
const activeGeneration = this.getActiveGenerationId();
|
|
262
|
+
if (!activeGeneration) return null;
|
|
263
|
+
const row = this.db.prepare(`SELECT relative_path
|
|
264
|
+
FROM assets
|
|
265
|
+
WHERE generation_id = ? AND asset_key = ?`).get(activeGeneration, assetKey);
|
|
266
|
+
if (!row) return null;
|
|
267
|
+
const validatedRow = require_internal_validation.parseWithSchema(require_internal_validation.protocolAssetTargetRowSchema, row, "protocol asset row");
|
|
268
|
+
return { absolutePath: validatedRow.relative_path ? (0, node_path.join)(this.root, ...validatedRow.relative_path.split(/[\\/]/)) : null };
|
|
269
|
+
}
|
|
270
|
+
getAsset(assetKey) {
|
|
271
|
+
this.assertNotClosed();
|
|
272
|
+
const activeGeneration = this.getActiveGenerationId();
|
|
273
|
+
if (!activeGeneration) return null;
|
|
274
|
+
const row = this.db.prepare(`SELECT
|
|
275
|
+
generation_id AS generationId,
|
|
276
|
+
asset_key AS assetKey,
|
|
277
|
+
display_key AS displayKey,
|
|
278
|
+
version,
|
|
279
|
+
mime_type AS mimeType,
|
|
280
|
+
media_kind AS mediaKind,
|
|
281
|
+
byte_length AS byteLength,
|
|
282
|
+
metadata_json AS metadata,
|
|
283
|
+
indexes_json AS indexesJson,
|
|
284
|
+
relative_path AS relativePath,
|
|
285
|
+
url,
|
|
286
|
+
file_stem AS fileStem,
|
|
287
|
+
order_index AS orderIndex
|
|
288
|
+
FROM assets
|
|
289
|
+
WHERE generation_id = ? AND asset_key = ?`).get(activeGeneration, assetKey);
|
|
290
|
+
if (!row) return null;
|
|
291
|
+
const validatedRow = require_internal_validation.parseWithSchema(require_internal_validation.activeAssetRowSchema, row, `asset "${assetKey}"`);
|
|
292
|
+
return this.buildResolvedAsset(validatedRow);
|
|
293
|
+
}
|
|
294
|
+
listByIndex(indexName, value, pagination) {
|
|
295
|
+
this.assertNotClosed();
|
|
296
|
+
require_shared_pagination.resolvePaginationWindow(pagination);
|
|
297
|
+
const activeGeneration = this.getActiveGenerationId();
|
|
298
|
+
if (!activeGeneration) return {
|
|
299
|
+
items: [],
|
|
300
|
+
nextCursor: null
|
|
301
|
+
};
|
|
302
|
+
return require_shared_pagination.paginateArray(require_internal_validation.parseWithSchema(require_internal_validation.activeAssetRowSchema.array(), this.db.prepare(`SELECT
|
|
303
|
+
a.generation_id AS generationId,
|
|
304
|
+
a.asset_key AS assetKey,
|
|
305
|
+
a.display_key AS displayKey,
|
|
306
|
+
a.version,
|
|
307
|
+
a.mime_type AS mimeType,
|
|
308
|
+
a.media_kind AS mediaKind,
|
|
309
|
+
a.byte_length AS byteLength,
|
|
310
|
+
a.metadata_json AS metadata,
|
|
311
|
+
a.indexes_json AS indexesJson,
|
|
312
|
+
a.relative_path AS relativePath,
|
|
313
|
+
a.url,
|
|
314
|
+
a.file_stem AS fileStem,
|
|
315
|
+
a.order_index AS orderIndex
|
|
316
|
+
FROM asset_indexes ai
|
|
317
|
+
INNER JOIN assets a
|
|
318
|
+
ON a.generation_id = ai.generation_id AND a.asset_key = ai.asset_key
|
|
319
|
+
WHERE ai.generation_id = ? AND ai.index_name = ? AND ai.index_value = ?
|
|
320
|
+
ORDER BY a.order_index`).all(activeGeneration, indexName, value), "index match rows").map((row) => this.buildResolvedAsset(row)), pagination);
|
|
321
|
+
}
|
|
322
|
+
findByFileStem(stem, pagination) {
|
|
323
|
+
this.assertNotClosed();
|
|
324
|
+
require_shared_pagination.resolvePaginationWindow(pagination);
|
|
325
|
+
const activeGeneration = this.getActiveGenerationId();
|
|
326
|
+
if (!activeGeneration) return {
|
|
327
|
+
items: [],
|
|
328
|
+
nextCursor: null
|
|
329
|
+
};
|
|
330
|
+
return require_shared_pagination.paginateArray(require_internal_validation.parseWithSchema(require_internal_validation.activeAssetRowSchema.array(), this.db.prepare(`SELECT
|
|
331
|
+
generation_id AS generationId,
|
|
332
|
+
asset_key AS assetKey,
|
|
333
|
+
display_key AS displayKey,
|
|
334
|
+
version,
|
|
335
|
+
mime_type AS mimeType,
|
|
336
|
+
media_kind AS mediaKind,
|
|
337
|
+
byte_length AS byteLength,
|
|
338
|
+
metadata_json AS metadata,
|
|
339
|
+
indexes_json AS indexesJson,
|
|
340
|
+
relative_path AS relativePath,
|
|
341
|
+
url,
|
|
342
|
+
file_stem AS fileStem,
|
|
343
|
+
order_index AS orderIndex
|
|
344
|
+
FROM assets
|
|
345
|
+
WHERE generation_id = ? AND file_stem = ?
|
|
346
|
+
ORDER BY order_index`).all(activeGeneration, stem), "file stem match rows").map((row) => ({ asset: this.buildResolvedAsset(row) })), pagination);
|
|
347
|
+
}
|
|
348
|
+
logicalKey(assetKey) {
|
|
349
|
+
return assetKey;
|
|
350
|
+
}
|
|
351
|
+
buildResolvedAsset(row) {
|
|
352
|
+
const metadata = require_internal_validation.parseJsonWithSchema(row.metadata, require_internal_validation.jsonObjectSchema, `metadata for asset "${row.assetKey}"`);
|
|
353
|
+
const indexes = require_internal_validation.parseJsonWithSchema(row.indexesJson, require_internal_validation.jsonObjectSchema, `indexes for asset "${row.assetKey}"`);
|
|
354
|
+
let url;
|
|
355
|
+
if (this.options.devPassthrough) {
|
|
356
|
+
url = row.url;
|
|
357
|
+
const origin = this.options.assetBaseUrlOrigin;
|
|
358
|
+
if (origin) try {
|
|
359
|
+
const base = new URL(origin);
|
|
360
|
+
const resolved = new URL(url);
|
|
361
|
+
resolved.protocol = base.protocol;
|
|
362
|
+
resolved.hostname = base.hostname;
|
|
363
|
+
resolved.port = base.port;
|
|
364
|
+
url = resolved.toString();
|
|
365
|
+
} catch (err) {
|
|
366
|
+
if (this.options.onWarn) this.options.onWarn(`asset source for "${row.assetKey}"`, err);
|
|
367
|
+
}
|
|
368
|
+
} else url = `media://asset/${encodeURIComponent(row.assetKey)}`;
|
|
369
|
+
return {
|
|
370
|
+
key: row.assetKey,
|
|
371
|
+
displayKey: row.displayKey,
|
|
372
|
+
version: row.version,
|
|
373
|
+
mimeType: row.mimeType,
|
|
374
|
+
kind: row.mediaKind,
|
|
375
|
+
byteLength: row.byteLength ?? void 0,
|
|
376
|
+
url,
|
|
377
|
+
metadata,
|
|
378
|
+
indexes
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
migrate() {
|
|
382
|
+
this.db.exec(`
|
|
383
|
+
CREATE TABLE IF NOT EXISTS generations (
|
|
384
|
+
id INTEGER PRIMARY KEY,
|
|
385
|
+
snapshot_id TEXT,
|
|
386
|
+
retrieved_at TEXT,
|
|
387
|
+
expires_at TEXT,
|
|
388
|
+
committed_at_ms INTEGER NOT NULL,
|
|
389
|
+
is_active INTEGER NOT NULL DEFAULT 0
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
CREATE TABLE IF NOT EXISTS assets (
|
|
393
|
+
generation_id INTEGER NOT NULL,
|
|
394
|
+
asset_key TEXT NOT NULL,
|
|
395
|
+
display_key TEXT NOT NULL,
|
|
396
|
+
version TEXT NOT NULL,
|
|
397
|
+
mime_type TEXT NOT NULL,
|
|
398
|
+
media_kind TEXT NOT NULL,
|
|
399
|
+
file_name TEXT NOT NULL,
|
|
400
|
+
file_stem TEXT NOT NULL,
|
|
401
|
+
byte_length INTEGER,
|
|
402
|
+
url TEXT NOT NULL,
|
|
403
|
+
metadata_json TEXT NOT NULL,
|
|
404
|
+
indexes_json TEXT NOT NULL,
|
|
405
|
+
order_index INTEGER NOT NULL,
|
|
406
|
+
relative_path TEXT,
|
|
407
|
+
PRIMARY KEY (generation_id, asset_key)
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
CREATE INDEX IF NOT EXISTS idx_assets_file_stem
|
|
411
|
+
ON assets (generation_id, file_stem, order_index);
|
|
412
|
+
|
|
413
|
+
CREATE TABLE IF NOT EXISTS asset_indexes (
|
|
414
|
+
generation_id INTEGER NOT NULL,
|
|
415
|
+
asset_key TEXT NOT NULL,
|
|
416
|
+
index_name TEXT NOT NULL,
|
|
417
|
+
index_value TEXT NOT NULL,
|
|
418
|
+
PRIMARY KEY (generation_id, asset_key, index_name, index_value)
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
CREATE INDEX IF NOT EXISTS idx_asset_indexes_lookup
|
|
422
|
+
ON asset_indexes (generation_id, index_name, index_value, asset_key);
|
|
423
|
+
|
|
424
|
+
CREATE TABLE IF NOT EXISTS index_definitions (
|
|
425
|
+
generation_id INTEGER NOT NULL,
|
|
426
|
+
name TEXT NOT NULL,
|
|
427
|
+
cardinality TEXT NOT NULL,
|
|
428
|
+
required INTEGER NOT NULL,
|
|
429
|
+
builtin INTEGER NOT NULL,
|
|
430
|
+
PRIMARY KEY (generation_id, name)
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
CREATE TABLE IF NOT EXISTS pending_deletions (
|
|
434
|
+
deletion_key TEXT PRIMARY KEY,
|
|
435
|
+
logical_key TEXT NOT NULL,
|
|
436
|
+
asset_key TEXT NOT NULL,
|
|
437
|
+
relative_path TEXT NOT NULL,
|
|
438
|
+
generation_id INTEGER NOT NULL,
|
|
439
|
+
delete_after_ms INTEGER NOT NULL
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
CREATE TABLE IF NOT EXISTS sync_runs (
|
|
443
|
+
id INTEGER PRIMARY KEY,
|
|
444
|
+
started_at_ms INTEGER NOT NULL,
|
|
445
|
+
finished_at_ms INTEGER,
|
|
446
|
+
status TEXT NOT NULL,
|
|
447
|
+
error_code TEXT,
|
|
448
|
+
error_message TEXT,
|
|
449
|
+
stats_json TEXT NOT NULL
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
CREATE TABLE IF NOT EXISTS status_snapshot (
|
|
453
|
+
scope_type TEXT NOT NULL,
|
|
454
|
+
scope_key TEXT NOT NULL,
|
|
455
|
+
status_json TEXT NOT NULL,
|
|
456
|
+
updated_at_ms INTEGER NOT NULL,
|
|
457
|
+
PRIMARY KEY (scope_type, scope_key)
|
|
458
|
+
);
|
|
459
|
+
`);
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
function emptyStats() {
|
|
463
|
+
return {
|
|
464
|
+
totalAssets: 0,
|
|
465
|
+
downloadedAssets: 0,
|
|
466
|
+
skippedAssets: 0,
|
|
467
|
+
bytesDownloaded: 0
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
//#endregion
|
|
471
|
+
exports.MediaCacheDatabase = MediaCacheDatabase;
|
|
472
|
+
|
|
473
|
+
//# sourceMappingURL=database.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.cjs","names":["parseJsonWithSchema","parseWithSchema","statusSnapshotRowSchema","mediaCacheStatusSchema","stringifyWithSchema","syncRunStatsSchema","syncRunRowSchema","syncRunIdRowSchema","activeGenerationRowSchema","generationIdRowSchema","generationAssetRowSchema","pendingDeletionSchema","protocolAssetTargetRowSchema","activeAssetRowSchema","paginateArray","jsonObjectSchema"],"sources":["../../src/main/database.ts"],"sourcesContent":["import { mkdirSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { join } from \"node:path\";\nimport { paginateArray, resolvePaginationWindow } from \"../shared/pagination.js\";\nimport type {\n FileStemMatch,\n FlatManifest,\n MediaCacheStatus,\n MediaKind,\n PaginationInput,\n PaginationResult,\n ResolvedMediaAsset,\n SyncRunStats,\n SyncRunSummary,\n} from \"../shared/types.js\";\nimport {\n activeAssetRowSchema,\n activeGenerationRowSchema,\n generationAssetRowSchema,\n generationIdRowSchema,\n jsonObjectSchema,\n mediaCacheStatusSchema,\n parseJsonWithSchema,\n parseWithSchema,\n pendingDeletionSchema,\n protocolAssetTargetRowSchema,\n statusSnapshotRowSchema,\n stringifyWithSchema,\n syncRunIdRowSchema,\n syncRunRowSchema,\n syncRunStatsSchema,\n} from \"../internal/validation.js\";\n\nconst require = createRequire(import.meta.url);\nconst { DatabaseSync } = require(\"node:sqlite\") as typeof import(\"node:sqlite\");\n\nexport interface ActiveAssetRow {\n generationId: number;\n assetKey: string;\n displayKey: string;\n version: string;\n mimeType: string;\n mediaKind: MediaKind;\n byteLength: number | null;\n metadata: string;\n indexesJson: string;\n relativePath: string | null;\n url: string;\n fileStem: string;\n orderIndex: number;\n}\n\nexport interface GenerationAssetRow {\n assetKey: string;\n version: string;\n relativePath: string | null;\n mimeType: string;\n url: string;\n}\n\nexport interface PendingDeletion {\n deletionKey: string;\n logicalKey: string;\n relativePath: string;\n}\n\nexport interface ProtocolAssetTarget {\n absolutePath: string | null;\n}\n\nexport class MediaCacheDatabase {\n private readonly db: import(\"node:sqlite\").DatabaseSync;\n private closed = false;\n\n constructor(\n private readonly root: string,\n private readonly options: {\n devPassthrough: boolean;\n assetBaseUrlOrigin: string | null;\n /**\n * Invoked only when dev passthrough origin override fails (fallback to stored URL).\n * Not called for invalid `url` (parse error) — those throw.\n */\n onWarn?: (contextLabel: string, err: unknown) => void;\n },\n ) {\n const sqliteDir = join(root, \"sqlite\");\n mkdirSync(sqliteDir, { recursive: true });\n this.db = new DatabaseSync(join(sqliteDir, \"media-cache.db\"));\n this.db.exec(\"PRAGMA journal_mode = WAL;\");\n this.db.exec(\"PRAGMA foreign_keys = ON;\");\n this.migrate();\n }\n\n close(): void {\n if (this.closed) {\n return;\n }\n this.closed = true;\n this.db.close();\n }\n\n private assertNotClosed(): void {\n if (this.closed) {\n throw new Error(\"MediaCacheDatabase is closed\");\n }\n }\n\n /** @internal Used only by prepareDevRuntimeState in dev passthrough startup. */\n clearAllState(): void {\n this.assertNotClosed();\n this.db.exec(\"BEGIN\");\n try {\n this.db.prepare(`DELETE FROM pending_deletions`).run();\n this.db.prepare(`DELETE FROM asset_indexes`).run();\n this.db.prepare(`DELETE FROM assets`).run();\n this.db.prepare(`DELETE FROM index_definitions`).run();\n this.db.prepare(`DELETE FROM generations`).run();\n this.db.prepare(`DELETE FROM sync_runs`).run();\n this.db.prepare(`DELETE FROM status_snapshot`).run();\n this.db.exec(\"COMMIT\");\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n\n loadStatus(): MediaCacheStatus | null {\n this.assertNotClosed();\n const row = this.db\n .prepare(\n `SELECT status_json\n FROM status_snapshot\n WHERE scope_type = 'global' AND scope_key = '*'`,\n )\n .get();\n if (!row) {\n return null;\n }\n\n const validatedRow = parseWithSchema(statusSnapshotRowSchema, row, \"status snapshot row\");\n return parseJsonWithSchema(\n validatedRow.status_json,\n mediaCacheStatusSchema,\n \"persisted media cache status\",\n );\n }\n\n saveStatus(status: MediaCacheStatus, now: number): void {\n this.assertNotClosed();\n const statusJson = stringifyWithSchema(status, mediaCacheStatusSchema, \"media cache status\");\n this.db.exec(\"BEGIN\");\n try {\n this.db\n .prepare(\n `DELETE FROM status_snapshot\n WHERE scope_type = 'global' AND scope_key = '*'`,\n )\n .run();\n this.db\n .prepare(\n `INSERT INTO status_snapshot (scope_type, scope_key, status_json, updated_at_ms)\n VALUES ('global', '*', ?, ?)`,\n )\n .run(statusJson, now);\n this.db.exec(\"COMMIT\");\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n\n createSyncRun(now: number): number {\n this.assertNotClosed();\n const statsJson = stringifyWithSchema(emptyStats(), syncRunStatsSchema, \"sync run stats\");\n const result = this.db\n .prepare(\n `INSERT INTO sync_runs (started_at_ms, status, stats_json)\n VALUES (?, 'running', ?)`,\n )\n .run(now, statsJson);\n return Number(result.lastInsertRowid);\n }\n\n completeSyncRun(\n id: number,\n status: \"success\" | \"error\",\n now: number,\n stats: SyncRunStats,\n errorCode: string | null = null,\n errorMessage: string | null = null,\n ): SyncRunSummary {\n this.assertNotClosed();\n const statsJson = stringifyWithSchema(stats, syncRunStatsSchema, \"sync run stats\");\n this.db\n .prepare(\n `UPDATE sync_runs\n SET finished_at_ms = ?, status = ?, error_code = ?, error_message = ?, stats_json = ?\n WHERE id = ?`,\n )\n .run(now, status, errorCode, errorMessage, statsJson, id);\n\n return this.getSyncRun(id)!;\n }\n\n getSyncRun(id: number): SyncRunSummary | null {\n this.assertNotClosed();\n const row = this.db\n .prepare(\n `SELECT id, started_at_ms, finished_at_ms, status, error_code, error_message, stats_json\n FROM sync_runs\n WHERE id = ?`,\n )\n .get(id);\n\n if (!row) {\n return null;\n }\n\n const validatedRow = parseWithSchema(syncRunRowSchema, row, \"sync run row\");\n return {\n id: validatedRow.id,\n status: validatedRow.status,\n startedAt: validatedRow.started_at_ms,\n finishedAt: validatedRow.finished_at_ms,\n errorCode: validatedRow.error_code,\n errorMessage: validatedRow.error_message,\n stats: parseJsonWithSchema(\n validatedRow.stats_json,\n syncRunStatsSchema,\n `sync run ${validatedRow.id} stats`,\n ),\n };\n }\n\n pruneSyncHistory(limit: number): void {\n this.assertNotClosed();\n if (limit < 1) {\n return;\n }\n this.db.exec(\"BEGIN\");\n try {\n const rows = parseWithSchema(\n syncRunIdRowSchema.array(),\n this.db\n .prepare(\n `SELECT id\n FROM sync_runs\n ORDER BY started_at_ms DESC\n LIMIT -1 OFFSET ?`,\n )\n .all(limit),\n \"sync run history rows\",\n );\n if (rows.length === 0) {\n this.db.exec(\"COMMIT\");\n return;\n }\n\n const ids = rows.map((row) => row.id);\n const placeholders = ids.map(() => \"?\").join(\", \");\n this.db.prepare(`DELETE FROM sync_runs WHERE id IN (${placeholders})`).run(...ids);\n this.db.exec(\"COMMIT\");\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n\n getActiveGenerationId(): number | null {\n this.assertNotClosed();\n const row = this.db\n .prepare(\n `SELECT id AS generation_id\n FROM generations\n WHERE is_active = 1`,\n )\n .get();\n return row\n ? parseWithSchema(activeGenerationRowSchema, row, \"active generation row\").generation_id\n : null;\n }\n\n listStagedGenerationIds(): number[] {\n this.assertNotClosed();\n const rows = parseWithSchema(\n generationIdRowSchema.array(),\n this.db\n .prepare(\n `SELECT id\n FROM generations\n WHERE is_active = 0\n ORDER BY id ASC`,\n )\n .all(),\n \"staged generation rows\",\n );\n return rows.map((row) => row.id);\n }\n\n createStagedGeneration(manifest: FlatManifest, now: number): number {\n this.assertNotClosed();\n this.db.exec(\"BEGIN\");\n try {\n const generationInsert = this.db\n .prepare(\n `INSERT INTO generations (snapshot_id, retrieved_at, expires_at, committed_at_ms, is_active)\n VALUES (?, ?, ?, ?, 0)`,\n )\n .run(\n manifest.snapshotId ?? null,\n manifest.retrievedAt ?? null,\n manifest.expiresAt ?? null,\n now,\n );\n\n const generationId = Number(generationInsert.lastInsertRowid);\n\n const indexDefStmt = this.db.prepare(\n `INSERT INTO index_definitions (generation_id, name, cardinality, required, builtin)\n VALUES (?, ?, ?, ?, ?)`,\n );\n for (const def of manifest.indexDefinitions) {\n indexDefStmt.run(\n generationId,\n def.name,\n def.cardinality,\n def.required ? 1 : 0,\n def.builtin ? 1 : 0,\n );\n }\n\n const assetStmt = this.db.prepare(\n `INSERT INTO assets (\n generation_id, asset_key, display_key, version, mime_type, media_kind, file_name, file_stem,\n byte_length, url, metadata_json, indexes_json, order_index, relative_path\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`,\n );\n const indexStmt = this.db.prepare(\n `INSERT INTO asset_indexes (generation_id, asset_key, index_name, index_value)\n VALUES (?, ?, ?, ?)`,\n );\n\n manifest.assets.forEach((asset, assetOrder) => {\n assetStmt.run(\n generationId,\n asset.key,\n asset.displayKey,\n asset.version,\n asset.mimeType,\n asset.mediaKind,\n asset.fileName,\n asset.fileStem,\n asset.byteLength ?? null,\n asset.url,\n JSON.stringify(asset.metadata),\n JSON.stringify(asset.indexes),\n assetOrder,\n );\n\n for (const [indexName, indexValue] of Object.entries(asset.indexes)) {\n if (Array.isArray(indexValue)) {\n for (const v of new Set(indexValue)) {\n indexStmt.run(generationId, asset.key, indexName, v);\n }\n } else {\n indexStmt.run(generationId, asset.key, indexName, indexValue);\n }\n }\n });\n\n this.db.exec(\"COMMIT\");\n return generationId;\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n\n deleteGeneration(generationId: number): void {\n this.assertNotClosed();\n this.db.exec(\"BEGIN\");\n try {\n this.db.prepare(`DELETE FROM asset_indexes WHERE generation_id = ?`).run(generationId);\n this.db.prepare(`DELETE FROM assets WHERE generation_id = ?`).run(generationId);\n this.db.prepare(`DELETE FROM index_definitions WHERE generation_id = ?`).run(generationId);\n this.db.prepare(`DELETE FROM generations WHERE id = ?`).run(generationId);\n this.db.exec(\"COMMIT\");\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n\n setAssetRelativePath(generationId: number, assetKey: string, relativePath: string): void {\n this.assertNotClosed();\n this.db\n .prepare(\n `UPDATE assets\n SET relative_path = ?\n WHERE generation_id = ? AND asset_key = ?`,\n )\n .run(relativePath, generationId, assetKey);\n }\n\n setAssetDownloadState(\n generationId: number,\n assetKey: string,\n relativePath: string,\n fallbackMimeType: string | null,\n ): void {\n this.assertNotClosed();\n this.db\n .prepare(\n `UPDATE assets\n SET\n relative_path = ?,\n mime_type = COALESCE(mime_type, ?)\n WHERE generation_id = ? AND asset_key = ?`,\n )\n .run(relativePath, fallbackMimeType, generationId, assetKey);\n }\n\n getGenerationAssets(generationId: number): GenerationAssetRow[] {\n this.assertNotClosed();\n return parseWithSchema(\n generationAssetRowSchema.array(),\n this.db\n .prepare(\n `SELECT\n asset_key AS assetKey,\n version,\n relative_path AS relativePath,\n mime_type AS mimeType,\n url\n FROM assets\n WHERE generation_id = ?\n ORDER BY order_index`,\n )\n .all(generationId),\n `generation ${generationId} asset rows`,\n );\n }\n\n activateGeneration(generationId: number, now: number): number | null {\n this.assertNotClosed();\n const previousActive = this.getActiveGenerationId();\n\n this.db.exec(\"BEGIN\");\n try {\n this.db.prepare(`UPDATE generations SET is_active = 0 WHERE is_active = 1`).run();\n const result = this.db\n .prepare(`UPDATE generations SET is_active = 1, committed_at_ms = ? WHERE id = ?`)\n .run(now, generationId);\n if (result.changes !== 1) {\n throw new Error(`Cannot activate missing generation ${generationId}`);\n }\n this.db.exec(\"COMMIT\");\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n\n return previousActive;\n }\n\n clearPendingDeletionsForGeneration(generationId: number): void {\n this.assertNotClosed();\n const activeRelativePaths = this.getGenerationAssets(generationId).flatMap((row) =>\n row.relativePath ? [row.relativePath] : [],\n );\n this.deletePendingDeletionsByRelativePath(activeRelativePaths);\n }\n\n markPendingDeletion(\n assetKey: string,\n relativePath: string,\n generationId: number,\n deletionKey: string,\n deleteAfterMs: number,\n ): void {\n this.assertNotClosed();\n this.db\n .prepare(\n `INSERT INTO pending_deletions (\n deletion_key, logical_key, asset_key, relative_path, generation_id, delete_after_ms\n ) VALUES (?, ?, ?, ?, ?, ?)\n ON CONFLICT(deletion_key)\n DO UPDATE SET\n logical_key = excluded.logical_key,\n asset_key = excluded.asset_key,\n relative_path = excluded.relative_path,\n generation_id = excluded.generation_id,\n delete_after_ms = excluded.delete_after_ms`,\n )\n .run(\n deletionKey,\n this.logicalKey(assetKey),\n assetKey,\n relativePath,\n generationId,\n deleteAfterMs,\n );\n }\n\n getExpiredPendingDeletions(now: number): PendingDeletion[] {\n this.assertNotClosed();\n return parseWithSchema(\n pendingDeletionSchema.array(),\n this.db\n .prepare(\n `SELECT deletion_key AS deletionKey, logical_key AS logicalKey, relative_path AS relativePath\n FROM pending_deletions\n WHERE delete_after_ms <= ?`,\n )\n .all(now),\n \"expired pending deletions\",\n );\n }\n\n deletePendingDeletions(deletionKeys: string[]): void {\n this.assertNotClosed();\n if (deletionKeys.length === 0) {\n return;\n }\n\n const placeholders = deletionKeys.map(() => \"?\").join(\", \");\n this.db\n .prepare(`DELETE FROM pending_deletions WHERE deletion_key IN (${placeholders})`)\n .run(...deletionKeys);\n }\n\n deletePendingDeletionsByRelativePath(relativePaths: string[]): void {\n this.assertNotClosed();\n if (relativePaths.length === 0) {\n return;\n }\n\n const placeholders = relativePaths.map(() => \"?\").join(\", \");\n this.db\n .prepare(`DELETE FROM pending_deletions WHERE relative_path IN (${placeholders})`)\n .run(...relativePaths);\n }\n\n getProtocolAssetTarget(assetKey: string): ProtocolAssetTarget | null {\n this.assertNotClosed();\n const activeGeneration = this.getActiveGenerationId();\n if (!activeGeneration) {\n return null;\n }\n\n const row = this.db\n .prepare(\n `SELECT relative_path\n FROM assets\n WHERE generation_id = ? AND asset_key = ?`,\n )\n .get(activeGeneration, assetKey);\n\n if (!row) {\n return null;\n }\n\n const validatedRow = parseWithSchema(protocolAssetTargetRowSchema, row, \"protocol asset row\");\n return {\n absolutePath: validatedRow.relative_path\n ? join(this.root, ...validatedRow.relative_path.split(/[\\\\/]/))\n : null,\n };\n }\n\n getAsset(assetKey: string): ResolvedMediaAsset | null {\n this.assertNotClosed();\n const activeGeneration = this.getActiveGenerationId();\n if (!activeGeneration) {\n return null;\n }\n\n const row = this.db\n .prepare(\n `SELECT\n generation_id AS generationId,\n asset_key AS assetKey,\n display_key AS displayKey,\n version,\n mime_type AS mimeType,\n media_kind AS mediaKind,\n byte_length AS byteLength,\n metadata_json AS metadata,\n indexes_json AS indexesJson,\n relative_path AS relativePath,\n url,\n file_stem AS fileStem,\n order_index AS orderIndex\n FROM assets\n WHERE generation_id = ? AND asset_key = ?`,\n )\n .get(activeGeneration, assetKey);\n\n if (!row) {\n return null;\n }\n\n const validatedRow = parseWithSchema(activeAssetRowSchema, row, `asset \"${assetKey}\"`);\n return this.buildResolvedAsset(validatedRow);\n }\n\n listByIndex(\n indexName: string,\n value: string,\n pagination?: PaginationInput,\n ): PaginationResult<ResolvedMediaAsset> {\n this.assertNotClosed();\n resolvePaginationWindow(pagination);\n\n const activeGeneration = this.getActiveGenerationId();\n if (!activeGeneration) {\n return { items: [], nextCursor: null };\n }\n\n const rows = parseWithSchema(\n activeAssetRowSchema.array(),\n this.db\n .prepare(\n `SELECT\n a.generation_id AS generationId,\n a.asset_key AS assetKey,\n a.display_key AS displayKey,\n a.version,\n a.mime_type AS mimeType,\n a.media_kind AS mediaKind,\n a.byte_length AS byteLength,\n a.metadata_json AS metadata,\n a.indexes_json AS indexesJson,\n a.relative_path AS relativePath,\n a.url,\n a.file_stem AS fileStem,\n a.order_index AS orderIndex\n FROM asset_indexes ai\n INNER JOIN assets a\n ON a.generation_id = ai.generation_id AND a.asset_key = ai.asset_key\n WHERE ai.generation_id = ? AND ai.index_name = ? AND ai.index_value = ?\n ORDER BY a.order_index`,\n )\n .all(activeGeneration, indexName, value),\n \"index match rows\",\n );\n\n const assets = rows.map((row) => this.buildResolvedAsset(row));\n return paginateArray(assets, pagination);\n }\n\n findByFileStem(stem: string, pagination?: PaginationInput): PaginationResult<FileStemMatch> {\n this.assertNotClosed();\n resolvePaginationWindow(pagination);\n\n const activeGeneration = this.getActiveGenerationId();\n if (!activeGeneration) {\n return { items: [], nextCursor: null };\n }\n\n const rows = parseWithSchema(\n activeAssetRowSchema.array(),\n this.db\n .prepare(\n `SELECT\n generation_id AS generationId,\n asset_key AS assetKey,\n display_key AS displayKey,\n version,\n mime_type AS mimeType,\n media_kind AS mediaKind,\n byte_length AS byteLength,\n metadata_json AS metadata,\n indexes_json AS indexesJson,\n relative_path AS relativePath,\n url,\n file_stem AS fileStem,\n order_index AS orderIndex\n FROM assets\n WHERE generation_id = ? AND file_stem = ?\n ORDER BY order_index`,\n )\n .all(activeGeneration, stem),\n \"file stem match rows\",\n );\n\n const matches: FileStemMatch[] = rows.map((row) => ({\n asset: this.buildResolvedAsset(row),\n }));\n return paginateArray(matches, pagination);\n }\n\n logicalKey(assetKey: string): string {\n return assetKey;\n }\n\n private buildResolvedAsset(row: ActiveAssetRow): ResolvedMediaAsset {\n const metadata = parseJsonWithSchema(\n row.metadata,\n jsonObjectSchema,\n `metadata for asset \"${row.assetKey}\"`,\n );\n const indexes = parseJsonWithSchema(\n row.indexesJson,\n jsonObjectSchema,\n `indexes for asset \"${row.assetKey}\"`,\n ) as Record<string, string | string[]>;\n\n let url: string;\n if (this.options.devPassthrough) {\n url = row.url;\n const origin = this.options.assetBaseUrlOrigin;\n if (origin) {\n try {\n const base = new URL(origin);\n const resolved = new URL(url);\n resolved.protocol = base.protocol;\n resolved.hostname = base.hostname;\n resolved.port = base.port;\n url = resolved.toString();\n } catch (err) {\n if (this.options.onWarn) {\n this.options.onWarn(`asset source for \"${row.assetKey}\"`, err);\n }\n }\n }\n } else {\n url = `media://asset/${encodeURIComponent(row.assetKey)}`;\n }\n\n return {\n key: row.assetKey,\n displayKey: row.displayKey,\n version: row.version,\n mimeType: row.mimeType,\n kind: row.mediaKind,\n byteLength: row.byteLength ?? undefined,\n url,\n metadata,\n indexes,\n };\n }\n\n private migrate(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS generations (\n id INTEGER PRIMARY KEY,\n snapshot_id TEXT,\n retrieved_at TEXT,\n expires_at TEXT,\n committed_at_ms INTEGER NOT NULL,\n is_active INTEGER NOT NULL DEFAULT 0\n );\n\n CREATE TABLE IF NOT EXISTS assets (\n generation_id INTEGER NOT NULL,\n asset_key TEXT NOT NULL,\n display_key TEXT NOT NULL,\n version TEXT NOT NULL,\n mime_type TEXT NOT NULL,\n media_kind TEXT NOT NULL,\n file_name TEXT NOT NULL,\n file_stem TEXT NOT NULL,\n byte_length INTEGER,\n url TEXT NOT NULL,\n metadata_json TEXT NOT NULL,\n indexes_json TEXT NOT NULL,\n order_index INTEGER NOT NULL,\n relative_path TEXT,\n PRIMARY KEY (generation_id, asset_key)\n );\n\n CREATE INDEX IF NOT EXISTS idx_assets_file_stem\n ON assets (generation_id, file_stem, order_index);\n\n CREATE TABLE IF NOT EXISTS asset_indexes (\n generation_id INTEGER NOT NULL,\n asset_key TEXT NOT NULL,\n index_name TEXT NOT NULL,\n index_value TEXT NOT NULL,\n PRIMARY KEY (generation_id, asset_key, index_name, index_value)\n );\n\n CREATE INDEX IF NOT EXISTS idx_asset_indexes_lookup\n ON asset_indexes (generation_id, index_name, index_value, asset_key);\n\n CREATE TABLE IF NOT EXISTS index_definitions (\n generation_id INTEGER NOT NULL,\n name TEXT NOT NULL,\n cardinality TEXT NOT NULL,\n required INTEGER NOT NULL,\n builtin INTEGER NOT NULL,\n PRIMARY KEY (generation_id, name)\n );\n\n CREATE TABLE IF NOT EXISTS pending_deletions (\n deletion_key TEXT PRIMARY KEY,\n logical_key TEXT NOT NULL,\n asset_key TEXT NOT NULL,\n relative_path TEXT NOT NULL,\n generation_id INTEGER NOT NULL,\n delete_after_ms INTEGER NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS sync_runs (\n id INTEGER PRIMARY KEY,\n started_at_ms INTEGER NOT NULL,\n finished_at_ms INTEGER,\n status TEXT NOT NULL,\n error_code TEXT,\n error_message TEXT,\n stats_json TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS status_snapshot (\n scope_type TEXT NOT NULL,\n scope_key TEXT NOT NULL,\n status_json TEXT NOT NULL,\n updated_at_ms INTEGER NOT NULL,\n PRIMARY KEY (scope_type, scope_key)\n );\n `);\n }\n}\n\nfunction emptyStats(): SyncRunStats {\n return {\n totalAssets: 0,\n downloadedAssets: 0,\n skippedAssets: 0,\n bytesDownloaded: 0,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { FileStemMatch, FlatManifest, MediaCacheStatus, MediaKind, PaginationInput, PaginationResult, ResolvedMediaAsset, SyncRunStats, SyncRunSummary } from "../shared/types.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/main/database.d.ts
|
|
4
|
+
interface ActiveAssetRow {
|
|
5
|
+
generationId: number;
|
|
6
|
+
assetKey: string;
|
|
7
|
+
displayKey: string;
|
|
8
|
+
version: string;
|
|
9
|
+
mimeType: string;
|
|
10
|
+
mediaKind: MediaKind;
|
|
11
|
+
byteLength: number | null;
|
|
12
|
+
metadata: string;
|
|
13
|
+
indexesJson: string;
|
|
14
|
+
relativePath: string | null;
|
|
15
|
+
url: string;
|
|
16
|
+
fileStem: string;
|
|
17
|
+
orderIndex: number;
|
|
18
|
+
}
|
|
19
|
+
interface GenerationAssetRow {
|
|
20
|
+
assetKey: string;
|
|
21
|
+
version: string;
|
|
22
|
+
relativePath: string | null;
|
|
23
|
+
mimeType: string;
|
|
24
|
+
url: string;
|
|
25
|
+
}
|
|
26
|
+
interface PendingDeletion {
|
|
27
|
+
deletionKey: string;
|
|
28
|
+
logicalKey: string;
|
|
29
|
+
relativePath: string;
|
|
30
|
+
}
|
|
31
|
+
interface ProtocolAssetTarget {
|
|
32
|
+
absolutePath: string | null;
|
|
33
|
+
}
|
|
34
|
+
declare class MediaCacheDatabase {
|
|
35
|
+
private readonly root;
|
|
36
|
+
private readonly options;
|
|
37
|
+
private readonly db;
|
|
38
|
+
private closed;
|
|
39
|
+
constructor(root: string, options: {
|
|
40
|
+
devPassthrough: boolean;
|
|
41
|
+
assetBaseUrlOrigin: string | null;
|
|
42
|
+
/**
|
|
43
|
+
* Invoked only when dev passthrough origin override fails (fallback to stored URL).
|
|
44
|
+
* Not called for invalid `url` (parse error) — those throw.
|
|
45
|
+
*/
|
|
46
|
+
onWarn?: (contextLabel: string, err: unknown) => void;
|
|
47
|
+
});
|
|
48
|
+
close(): void;
|
|
49
|
+
private assertNotClosed;
|
|
50
|
+
/** @internal Used only by prepareDevRuntimeState in dev passthrough startup. */
|
|
51
|
+
clearAllState(): void;
|
|
52
|
+
loadStatus(): MediaCacheStatus | null;
|
|
53
|
+
saveStatus(status: MediaCacheStatus, now: number): void;
|
|
54
|
+
createSyncRun(now: number): number;
|
|
55
|
+
completeSyncRun(id: number, status: "success" | "error", now: number, stats: SyncRunStats, errorCode?: string | null, errorMessage?: string | null): SyncRunSummary;
|
|
56
|
+
getSyncRun(id: number): SyncRunSummary | null;
|
|
57
|
+
pruneSyncHistory(limit: number): void;
|
|
58
|
+
getActiveGenerationId(): number | null;
|
|
59
|
+
listStagedGenerationIds(): number[];
|
|
60
|
+
createStagedGeneration(manifest: FlatManifest, now: number): number;
|
|
61
|
+
deleteGeneration(generationId: number): void;
|
|
62
|
+
setAssetRelativePath(generationId: number, assetKey: string, relativePath: string): void;
|
|
63
|
+
setAssetDownloadState(generationId: number, assetKey: string, relativePath: string, fallbackMimeType: string | null): void;
|
|
64
|
+
getGenerationAssets(generationId: number): GenerationAssetRow[];
|
|
65
|
+
activateGeneration(generationId: number, now: number): number | null;
|
|
66
|
+
clearPendingDeletionsForGeneration(generationId: number): void;
|
|
67
|
+
markPendingDeletion(assetKey: string, relativePath: string, generationId: number, deletionKey: string, deleteAfterMs: number): void;
|
|
68
|
+
getExpiredPendingDeletions(now: number): PendingDeletion[];
|
|
69
|
+
deletePendingDeletions(deletionKeys: string[]): void;
|
|
70
|
+
deletePendingDeletionsByRelativePath(relativePaths: string[]): void;
|
|
71
|
+
getProtocolAssetTarget(assetKey: string): ProtocolAssetTarget | null;
|
|
72
|
+
getAsset(assetKey: string): ResolvedMediaAsset | null;
|
|
73
|
+
listByIndex(indexName: string, value: string, pagination?: PaginationInput): PaginationResult<ResolvedMediaAsset>;
|
|
74
|
+
findByFileStem(stem: string, pagination?: PaginationInput): PaginationResult<FileStemMatch>;
|
|
75
|
+
logicalKey(assetKey: string): string;
|
|
76
|
+
private buildResolvedAsset;
|
|
77
|
+
private migrate;
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
export { ActiveAssetRow, GenerationAssetRow, MediaCacheDatabase, PendingDeletion, ProtocolAssetTarget };
|
|
81
|
+
//# sourceMappingURL=database.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.cts","names":[],"sources":["../../src/main/database.ts"],"mappings":";;;UAoCiB,cAAA;EACf,YAAA;EACA,QAAA;EACA,UAAA;EACA,OAAA;EACA,QAAA;EACA,SAAA,EAAW,SAAA;EACX,UAAA;EACA,QAAA;EACA,WAAA;EACA,YAAA;EACA,GAAA;EACA,QAAA;EACA,UAAA;AAAA;AAAA,UAGe,kBAAA;EACf,QAAA;EACA,OAAA;EACA,YAAA;EACA,QAAA;EACA,GAAA;AAAA;AAAA,UAGe,eAAA;EACf,WAAA;EACA,UAAA;EACA,YAAA;AAAA;AAAA,UAGe,mBAAA;EACf,YAAA;AAAA;AAAA,cAGW,kBAAA;EAAA,iBAKQ,IAAA;EAAA,iBACA,OAAA;EAAA,iBALF,EAAA;EAAA,QACT,MAAA;cAGW,IAAA,UACA,OAAA;IACf,cAAA;IACA,kBAAA;IAhBJ;;;;IAqBI,MAAA,IAAU,YAAA,UAAsB,GAAA;EAAA;EAWpC,KAAA,CAAA;EAAA,QAQQ,eAAA;EAnCI;EA0CZ,aAAA,CAAA;EAkBA,UAAA,CAAA,GAAc,gBAAA;EAqBd,UAAA,CAAW,MAAA,EAAQ,gBAAA,EAAkB,GAAA;EAwBrC,aAAA,CAAc,GAAA;EAYd,eAAA,CACE,EAAA,UACA,MAAA,uBACA,GAAA,UACA,KAAA,EAAO,YAAA,EACP,SAAA,kBACA,YAAA,mBACC,cAAA;EAcH,UAAA,CAAW,EAAA,WAAa,cAAA;EA8BxB,gBAAA,CAAiB,KAAA;EAkCjB,qBAAA,CAAA;EAcA,uBAAA,CAAA;EAiBA,sBAAA,CAAuB,QAAA,EAAU,YAAA,EAAc,GAAA;EA+E/C,gBAAA,CAAiB,YAAA;EAejB,oBAAA,CAAqB,YAAA,UAAsB,QAAA,UAAkB,YAAA;EAW7D,qBAAA,CACE,YAAA,UACA,QAAA,UACA,YAAA,UACA,gBAAA;EAcF,mBAAA,CAAoB,YAAA,WAAuB,kBAAA;EAqB3C,kBAAA,CAAmB,YAAA,UAAsB,GAAA;EAsBzC,kCAAA,CAAmC,YAAA;EAQnC,mBAAA,CACE,QAAA,UACA,YAAA,UACA,YAAA,UACA,WAAA,UACA,aAAA;EA0BF,0BAAA,CAA2B,GAAA,WAAc,eAAA;EAezC,sBAAA,CAAuB,YAAA;EAYvB,oCAAA,CAAqC,aAAA;EAYrC,sBAAA,CAAuB,QAAA,WAAmB,mBAAA;EA2B1C,QAAA,CAAS,QAAA,WAAmB,kBAAA;EAoC5B,WAAA,CACE,SAAA,UACA,KAAA,UACA,UAAA,GAAa,eAAA,GACZ,gBAAA,CAAiB,kBAAA;EAyCpB,cAAA,CAAe,IAAA,UAAc,UAAA,GAAa,eAAA,GAAkB,gBAAA,CAAiB,aAAA;EAyC7E,UAAA,CAAW,QAAA;EAAA,QAIH,kBAAA;EAAA,QA+CA,OAAA;AAAA"}
|