@hasna/models 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +38 -15
- package/dist/index.js +109 -85
- package/dist/storage.js +109 -15
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -3101,6 +3101,33 @@ import { mkdirSync as mkdirSync3 } from "fs";
|
|
|
3101
3101
|
import { dirname as dirname4 } from "path";
|
|
3102
3102
|
import { Database } from "bun:sqlite";
|
|
3103
3103
|
var SCHEMA_VERSION = 1;
|
|
3104
|
+
function rowToInstall(row) {
|
|
3105
|
+
return {
|
|
3106
|
+
id: String(row.id),
|
|
3107
|
+
provider: String(row.provider),
|
|
3108
|
+
entityKind: String(row.entity_kind),
|
|
3109
|
+
repoId: String(row.repo_id),
|
|
3110
|
+
revision: String(row.revision),
|
|
3111
|
+
installPath: String(row.install_path),
|
|
3112
|
+
bytes: Number(row.bytes),
|
|
3113
|
+
files: JSON.parse(String(row.files_json)),
|
|
3114
|
+
status: String(row.status),
|
|
3115
|
+
createdAt: String(row.created_at),
|
|
3116
|
+
updatedAt: String(row.updated_at)
|
|
3117
|
+
};
|
|
3118
|
+
}
|
|
3119
|
+
function parseInstallRef(input) {
|
|
3120
|
+
try {
|
|
3121
|
+
const trimmed = input.trim();
|
|
3122
|
+
const ref = parseProviderRef(trimmed);
|
|
3123
|
+
return {
|
|
3124
|
+
ref,
|
|
3125
|
+
hasExplicitRevision: trimmed.includes("@")
|
|
3126
|
+
};
|
|
3127
|
+
} catch {
|
|
3128
|
+
return null;
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3104
3131
|
|
|
3105
3132
|
class ModelsStore {
|
|
3106
3133
|
db;
|
|
@@ -3264,25 +3291,21 @@ class ModelsStore {
|
|
|
3264
3291
|
}
|
|
3265
3292
|
listInstalls() {
|
|
3266
3293
|
const rows = this.db.query("SELECT * FROM installs ORDER BY updated_at DESC").all();
|
|
3267
|
-
return rows.map(
|
|
3268
|
-
id: String(row.id),
|
|
3269
|
-
provider: String(row.provider),
|
|
3270
|
-
entityKind: String(row.entity_kind),
|
|
3271
|
-
repoId: String(row.repo_id),
|
|
3272
|
-
revision: String(row.revision),
|
|
3273
|
-
installPath: String(row.install_path),
|
|
3274
|
-
bytes: Number(row.bytes),
|
|
3275
|
-
files: JSON.parse(String(row.files_json)),
|
|
3276
|
-
status: String(row.status),
|
|
3277
|
-
createdAt: String(row.created_at),
|
|
3278
|
-
updatedAt: String(row.updated_at)
|
|
3279
|
-
}));
|
|
3294
|
+
return rows.map(rowToInstall);
|
|
3280
3295
|
}
|
|
3281
3296
|
findInstall(repoIdOrId) {
|
|
3282
3297
|
const row = this.db.query("SELECT * FROM installs WHERE id = ? OR repo_id = ? ORDER BY updated_at DESC LIMIT 1").get(repoIdOrId, repoIdOrId);
|
|
3283
|
-
if (
|
|
3298
|
+
if (row)
|
|
3299
|
+
return rowToInstall(row);
|
|
3300
|
+
const parsed = parseInstallRef(repoIdOrId);
|
|
3301
|
+
if (!parsed)
|
|
3284
3302
|
return null;
|
|
3285
|
-
|
|
3303
|
+
const refRow = parsed.hasExplicitRevision ? this.db.query(`SELECT * FROM installs
|
|
3304
|
+
WHERE provider = ? AND entity_kind = ? AND repo_id = ? AND revision = ?
|
|
3305
|
+
ORDER BY updated_at DESC LIMIT 1`).get(parsed.ref.provider, parsed.ref.entityKind, parsed.ref.repoId, parsed.ref.revision) : this.db.query(`SELECT * FROM installs
|
|
3306
|
+
WHERE provider = ? AND entity_kind = ? AND repo_id = ?
|
|
3307
|
+
ORDER BY updated_at DESC LIMIT 1`).get(parsed.ref.provider, parsed.ref.entityKind, parsed.ref.repoId);
|
|
3308
|
+
return refRow ? rowToInstall(refRow) : null;
|
|
3286
3309
|
}
|
|
3287
3310
|
deleteInstall(id) {
|
|
3288
3311
|
const result = this.db.prepare("DELETE FROM installs WHERE id = ?").run(id);
|
package/dist/index.js
CHANGED
|
@@ -23,8 +23,106 @@ function getInstallRoot() {
|
|
|
23
23
|
return process.env["HASNA_MODELS_INSTALLS"] || join(getModelsHome(), "installs");
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
// src/ref.ts
|
|
27
|
+
var ENTITY_KINDS = new Set(["model", "dataset", "space"]);
|
|
28
|
+
var PROVIDER_ALIASES = new Map([
|
|
29
|
+
["hf", "huggingface"],
|
|
30
|
+
["huggingface", "huggingface"]
|
|
31
|
+
]);
|
|
32
|
+
function parseEntityKind(value) {
|
|
33
|
+
if (ENTITY_KINDS.has(value))
|
|
34
|
+
return value;
|
|
35
|
+
throw new Error(`Unsupported entity kind: ${value}. Expected one of: ${[...ENTITY_KINDS].join(", ")}`);
|
|
36
|
+
}
|
|
37
|
+
function parseProviderRef(input, defaultKind = "model") {
|
|
38
|
+
const trimmed = input.trim();
|
|
39
|
+
if (!trimmed)
|
|
40
|
+
throw new Error("model or dataset ref is required");
|
|
41
|
+
let provider = "huggingface";
|
|
42
|
+
let entityKind = parseEntityKind(defaultKind);
|
|
43
|
+
let rest = trimmed;
|
|
44
|
+
const prefixMatch = rest.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):(.*)$/);
|
|
45
|
+
if (prefixMatch && !prefixMatch[1].includes("/")) {
|
|
46
|
+
const alias = PROVIDER_ALIASES.get(prefixMatch[1]);
|
|
47
|
+
if (alias) {
|
|
48
|
+
provider = alias;
|
|
49
|
+
rest = prefixMatch[2];
|
|
50
|
+
} else if (["model", "dataset", "space"].includes(prefixMatch[1])) {
|
|
51
|
+
entityKind = parseEntityKind(prefixMatch[1]);
|
|
52
|
+
rest = prefixMatch[2];
|
|
53
|
+
} else {
|
|
54
|
+
throw new Error(`Unsupported provider: ${prefixMatch[1]}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (rest.startsWith("model:")) {
|
|
58
|
+
entityKind = parseEntityKind("model");
|
|
59
|
+
rest = rest.slice("model:".length);
|
|
60
|
+
} else if (rest.startsWith("dataset:")) {
|
|
61
|
+
entityKind = parseEntityKind("dataset");
|
|
62
|
+
rest = rest.slice("dataset:".length);
|
|
63
|
+
} else if (rest.startsWith("space:")) {
|
|
64
|
+
entityKind = parseEntityKind("space");
|
|
65
|
+
rest = rest.slice("space:".length);
|
|
66
|
+
}
|
|
67
|
+
if (rest.includes(":")) {
|
|
68
|
+
throw new Error(`Unsupported ref prefix in: ${input}`);
|
|
69
|
+
}
|
|
70
|
+
let revision = "main";
|
|
71
|
+
const atIndex = rest.lastIndexOf("@");
|
|
72
|
+
if (atIndex > 0) {
|
|
73
|
+
revision = rest.slice(atIndex + 1);
|
|
74
|
+
rest = rest.slice(0, atIndex);
|
|
75
|
+
if (!revision.trim())
|
|
76
|
+
throw new Error(`Revision cannot be empty in ref: ${input}`);
|
|
77
|
+
} else if (rest.endsWith("@")) {
|
|
78
|
+
throw new Error(`Revision cannot be empty in ref: ${input}`);
|
|
79
|
+
}
|
|
80
|
+
if (!rest.includes("/")) {
|
|
81
|
+
throw new Error(`Expected a namespaced repo id such as owner/name, got ${input}`);
|
|
82
|
+
}
|
|
83
|
+
return { provider, entityKind, repoId: rest, revision };
|
|
84
|
+
}
|
|
85
|
+
function formatProviderRef(ref) {
|
|
86
|
+
const prefix = ref.provider === "huggingface" ? "hf" : ref.provider;
|
|
87
|
+
const kind = ref.entityKind === "model" ? "" : `${ref.entityKind}:`;
|
|
88
|
+
return `${prefix}:${kind}${ref.repoId}@${ref.revision}`;
|
|
89
|
+
}
|
|
90
|
+
function encodeRepoId(repoId) {
|
|
91
|
+
return repoId.split("/").map(encodeURIComponent).join("/");
|
|
92
|
+
}
|
|
93
|
+
function safePathSegment(value) {
|
|
94
|
+
return value.replace(/[^a-zA-Z0-9._-]+/g, "__").replace(/^_+|_+$/g, "") || "unnamed";
|
|
95
|
+
}
|
|
96
|
+
|
|
26
97
|
// src/storage.ts
|
|
27
98
|
var SCHEMA_VERSION = 1;
|
|
99
|
+
function rowToInstall(row) {
|
|
100
|
+
return {
|
|
101
|
+
id: String(row.id),
|
|
102
|
+
provider: String(row.provider),
|
|
103
|
+
entityKind: String(row.entity_kind),
|
|
104
|
+
repoId: String(row.repo_id),
|
|
105
|
+
revision: String(row.revision),
|
|
106
|
+
installPath: String(row.install_path),
|
|
107
|
+
bytes: Number(row.bytes),
|
|
108
|
+
files: JSON.parse(String(row.files_json)),
|
|
109
|
+
status: String(row.status),
|
|
110
|
+
createdAt: String(row.created_at),
|
|
111
|
+
updatedAt: String(row.updated_at)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function parseInstallRef(input) {
|
|
115
|
+
try {
|
|
116
|
+
const trimmed = input.trim();
|
|
117
|
+
const ref = parseProviderRef(trimmed);
|
|
118
|
+
return {
|
|
119
|
+
ref,
|
|
120
|
+
hasExplicitRevision: trimmed.includes("@")
|
|
121
|
+
};
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
28
126
|
|
|
29
127
|
class ModelsStore {
|
|
30
128
|
db;
|
|
@@ -188,25 +286,21 @@ class ModelsStore {
|
|
|
188
286
|
}
|
|
189
287
|
listInstalls() {
|
|
190
288
|
const rows = this.db.query("SELECT * FROM installs ORDER BY updated_at DESC").all();
|
|
191
|
-
return rows.map(
|
|
192
|
-
id: String(row.id),
|
|
193
|
-
provider: String(row.provider),
|
|
194
|
-
entityKind: String(row.entity_kind),
|
|
195
|
-
repoId: String(row.repo_id),
|
|
196
|
-
revision: String(row.revision),
|
|
197
|
-
installPath: String(row.install_path),
|
|
198
|
-
bytes: Number(row.bytes),
|
|
199
|
-
files: JSON.parse(String(row.files_json)),
|
|
200
|
-
status: String(row.status),
|
|
201
|
-
createdAt: String(row.created_at),
|
|
202
|
-
updatedAt: String(row.updated_at)
|
|
203
|
-
}));
|
|
289
|
+
return rows.map(rowToInstall);
|
|
204
290
|
}
|
|
205
291
|
findInstall(repoIdOrId) {
|
|
206
292
|
const row = this.db.query("SELECT * FROM installs WHERE id = ? OR repo_id = ? ORDER BY updated_at DESC LIMIT 1").get(repoIdOrId, repoIdOrId);
|
|
207
|
-
if (
|
|
293
|
+
if (row)
|
|
294
|
+
return rowToInstall(row);
|
|
295
|
+
const parsed = parseInstallRef(repoIdOrId);
|
|
296
|
+
if (!parsed)
|
|
208
297
|
return null;
|
|
209
|
-
|
|
298
|
+
const refRow = parsed.hasExplicitRevision ? this.db.query(`SELECT * FROM installs
|
|
299
|
+
WHERE provider = ? AND entity_kind = ? AND repo_id = ? AND revision = ?
|
|
300
|
+
ORDER BY updated_at DESC LIMIT 1`).get(parsed.ref.provider, parsed.ref.entityKind, parsed.ref.repoId, parsed.ref.revision) : this.db.query(`SELECT * FROM installs
|
|
301
|
+
WHERE provider = ? AND entity_kind = ? AND repo_id = ?
|
|
302
|
+
ORDER BY updated_at DESC LIMIT 1`).get(parsed.ref.provider, parsed.ref.entityKind, parsed.ref.repoId);
|
|
303
|
+
return refRow ? rowToInstall(refRow) : null;
|
|
210
304
|
}
|
|
211
305
|
deleteInstall(id) {
|
|
212
306
|
const result = this.db.prepare("DELETE FROM installs WHERE id = ?").run(id);
|
|
@@ -249,76 +343,6 @@ class ModelsStore {
|
|
|
249
343
|
function createStore(path) {
|
|
250
344
|
return new ModelsStore(path);
|
|
251
345
|
}
|
|
252
|
-
// src/ref.ts
|
|
253
|
-
var ENTITY_KINDS = new Set(["model", "dataset", "space"]);
|
|
254
|
-
var PROVIDER_ALIASES = new Map([
|
|
255
|
-
["hf", "huggingface"],
|
|
256
|
-
["huggingface", "huggingface"]
|
|
257
|
-
]);
|
|
258
|
-
function parseEntityKind(value) {
|
|
259
|
-
if (ENTITY_KINDS.has(value))
|
|
260
|
-
return value;
|
|
261
|
-
throw new Error(`Unsupported entity kind: ${value}. Expected one of: ${[...ENTITY_KINDS].join(", ")}`);
|
|
262
|
-
}
|
|
263
|
-
function parseProviderRef(input, defaultKind = "model") {
|
|
264
|
-
const trimmed = input.trim();
|
|
265
|
-
if (!trimmed)
|
|
266
|
-
throw new Error("model or dataset ref is required");
|
|
267
|
-
let provider = "huggingface";
|
|
268
|
-
let entityKind = parseEntityKind(defaultKind);
|
|
269
|
-
let rest = trimmed;
|
|
270
|
-
const prefixMatch = rest.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):(.*)$/);
|
|
271
|
-
if (prefixMatch && !prefixMatch[1].includes("/")) {
|
|
272
|
-
const alias = PROVIDER_ALIASES.get(prefixMatch[1]);
|
|
273
|
-
if (alias) {
|
|
274
|
-
provider = alias;
|
|
275
|
-
rest = prefixMatch[2];
|
|
276
|
-
} else if (["model", "dataset", "space"].includes(prefixMatch[1])) {
|
|
277
|
-
entityKind = parseEntityKind(prefixMatch[1]);
|
|
278
|
-
rest = prefixMatch[2];
|
|
279
|
-
} else {
|
|
280
|
-
throw new Error(`Unsupported provider: ${prefixMatch[1]}`);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
if (rest.startsWith("model:")) {
|
|
284
|
-
entityKind = parseEntityKind("model");
|
|
285
|
-
rest = rest.slice("model:".length);
|
|
286
|
-
} else if (rest.startsWith("dataset:")) {
|
|
287
|
-
entityKind = parseEntityKind("dataset");
|
|
288
|
-
rest = rest.slice("dataset:".length);
|
|
289
|
-
} else if (rest.startsWith("space:")) {
|
|
290
|
-
entityKind = parseEntityKind("space");
|
|
291
|
-
rest = rest.slice("space:".length);
|
|
292
|
-
}
|
|
293
|
-
if (rest.includes(":")) {
|
|
294
|
-
throw new Error(`Unsupported ref prefix in: ${input}`);
|
|
295
|
-
}
|
|
296
|
-
let revision = "main";
|
|
297
|
-
const atIndex = rest.lastIndexOf("@");
|
|
298
|
-
if (atIndex > 0) {
|
|
299
|
-
revision = rest.slice(atIndex + 1);
|
|
300
|
-
rest = rest.slice(0, atIndex);
|
|
301
|
-
if (!revision.trim())
|
|
302
|
-
throw new Error(`Revision cannot be empty in ref: ${input}`);
|
|
303
|
-
} else if (rest.endsWith("@")) {
|
|
304
|
-
throw new Error(`Revision cannot be empty in ref: ${input}`);
|
|
305
|
-
}
|
|
306
|
-
if (!rest.includes("/")) {
|
|
307
|
-
throw new Error(`Expected a namespaced repo id such as owner/name, got ${input}`);
|
|
308
|
-
}
|
|
309
|
-
return { provider, entityKind, repoId: rest, revision };
|
|
310
|
-
}
|
|
311
|
-
function formatProviderRef(ref) {
|
|
312
|
-
const prefix = ref.provider === "huggingface" ? "hf" : ref.provider;
|
|
313
|
-
const kind = ref.entityKind === "model" ? "" : `${ref.entityKind}:`;
|
|
314
|
-
return `${prefix}:${kind}${ref.repoId}@${ref.revision}`;
|
|
315
|
-
}
|
|
316
|
-
function encodeRepoId(repoId) {
|
|
317
|
-
return repoId.split("/").map(encodeURIComponent).join("/");
|
|
318
|
-
}
|
|
319
|
-
function safePathSegment(value) {
|
|
320
|
-
return value.replace(/[^a-zA-Z0-9._-]+/g, "__").replace(/^_+|_+$/g, "") || "unnamed";
|
|
321
|
-
}
|
|
322
346
|
// src/auth.ts
|
|
323
347
|
import { mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
|
|
324
348
|
import { dirname as dirname2 } from "path";
|
package/dist/storage.js
CHANGED
|
@@ -23,8 +23,106 @@ function getInstallRoot() {
|
|
|
23
23
|
return process.env["HASNA_MODELS_INSTALLS"] || join(getModelsHome(), "installs");
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
// src/ref.ts
|
|
27
|
+
var ENTITY_KINDS = new Set(["model", "dataset", "space"]);
|
|
28
|
+
var PROVIDER_ALIASES = new Map([
|
|
29
|
+
["hf", "huggingface"],
|
|
30
|
+
["huggingface", "huggingface"]
|
|
31
|
+
]);
|
|
32
|
+
function parseEntityKind(value) {
|
|
33
|
+
if (ENTITY_KINDS.has(value))
|
|
34
|
+
return value;
|
|
35
|
+
throw new Error(`Unsupported entity kind: ${value}. Expected one of: ${[...ENTITY_KINDS].join(", ")}`);
|
|
36
|
+
}
|
|
37
|
+
function parseProviderRef(input, defaultKind = "model") {
|
|
38
|
+
const trimmed = input.trim();
|
|
39
|
+
if (!trimmed)
|
|
40
|
+
throw new Error("model or dataset ref is required");
|
|
41
|
+
let provider = "huggingface";
|
|
42
|
+
let entityKind = parseEntityKind(defaultKind);
|
|
43
|
+
let rest = trimmed;
|
|
44
|
+
const prefixMatch = rest.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):(.*)$/);
|
|
45
|
+
if (prefixMatch && !prefixMatch[1].includes("/")) {
|
|
46
|
+
const alias = PROVIDER_ALIASES.get(prefixMatch[1]);
|
|
47
|
+
if (alias) {
|
|
48
|
+
provider = alias;
|
|
49
|
+
rest = prefixMatch[2];
|
|
50
|
+
} else if (["model", "dataset", "space"].includes(prefixMatch[1])) {
|
|
51
|
+
entityKind = parseEntityKind(prefixMatch[1]);
|
|
52
|
+
rest = prefixMatch[2];
|
|
53
|
+
} else {
|
|
54
|
+
throw new Error(`Unsupported provider: ${prefixMatch[1]}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (rest.startsWith("model:")) {
|
|
58
|
+
entityKind = parseEntityKind("model");
|
|
59
|
+
rest = rest.slice("model:".length);
|
|
60
|
+
} else if (rest.startsWith("dataset:")) {
|
|
61
|
+
entityKind = parseEntityKind("dataset");
|
|
62
|
+
rest = rest.slice("dataset:".length);
|
|
63
|
+
} else if (rest.startsWith("space:")) {
|
|
64
|
+
entityKind = parseEntityKind("space");
|
|
65
|
+
rest = rest.slice("space:".length);
|
|
66
|
+
}
|
|
67
|
+
if (rest.includes(":")) {
|
|
68
|
+
throw new Error(`Unsupported ref prefix in: ${input}`);
|
|
69
|
+
}
|
|
70
|
+
let revision = "main";
|
|
71
|
+
const atIndex = rest.lastIndexOf("@");
|
|
72
|
+
if (atIndex > 0) {
|
|
73
|
+
revision = rest.slice(atIndex + 1);
|
|
74
|
+
rest = rest.slice(0, atIndex);
|
|
75
|
+
if (!revision.trim())
|
|
76
|
+
throw new Error(`Revision cannot be empty in ref: ${input}`);
|
|
77
|
+
} else if (rest.endsWith("@")) {
|
|
78
|
+
throw new Error(`Revision cannot be empty in ref: ${input}`);
|
|
79
|
+
}
|
|
80
|
+
if (!rest.includes("/")) {
|
|
81
|
+
throw new Error(`Expected a namespaced repo id such as owner/name, got ${input}`);
|
|
82
|
+
}
|
|
83
|
+
return { provider, entityKind, repoId: rest, revision };
|
|
84
|
+
}
|
|
85
|
+
function formatProviderRef(ref) {
|
|
86
|
+
const prefix = ref.provider === "huggingface" ? "hf" : ref.provider;
|
|
87
|
+
const kind = ref.entityKind === "model" ? "" : `${ref.entityKind}:`;
|
|
88
|
+
return `${prefix}:${kind}${ref.repoId}@${ref.revision}`;
|
|
89
|
+
}
|
|
90
|
+
function encodeRepoId(repoId) {
|
|
91
|
+
return repoId.split("/").map(encodeURIComponent).join("/");
|
|
92
|
+
}
|
|
93
|
+
function safePathSegment(value) {
|
|
94
|
+
return value.replace(/[^a-zA-Z0-9._-]+/g, "__").replace(/^_+|_+$/g, "") || "unnamed";
|
|
95
|
+
}
|
|
96
|
+
|
|
26
97
|
// src/storage.ts
|
|
27
98
|
var SCHEMA_VERSION = 1;
|
|
99
|
+
function rowToInstall(row) {
|
|
100
|
+
return {
|
|
101
|
+
id: String(row.id),
|
|
102
|
+
provider: String(row.provider),
|
|
103
|
+
entityKind: String(row.entity_kind),
|
|
104
|
+
repoId: String(row.repo_id),
|
|
105
|
+
revision: String(row.revision),
|
|
106
|
+
installPath: String(row.install_path),
|
|
107
|
+
bytes: Number(row.bytes),
|
|
108
|
+
files: JSON.parse(String(row.files_json)),
|
|
109
|
+
status: String(row.status),
|
|
110
|
+
createdAt: String(row.created_at),
|
|
111
|
+
updatedAt: String(row.updated_at)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function parseInstallRef(input) {
|
|
115
|
+
try {
|
|
116
|
+
const trimmed = input.trim();
|
|
117
|
+
const ref = parseProviderRef(trimmed);
|
|
118
|
+
return {
|
|
119
|
+
ref,
|
|
120
|
+
hasExplicitRevision: trimmed.includes("@")
|
|
121
|
+
};
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
28
126
|
|
|
29
127
|
class ModelsStore {
|
|
30
128
|
db;
|
|
@@ -188,25 +286,21 @@ class ModelsStore {
|
|
|
188
286
|
}
|
|
189
287
|
listInstalls() {
|
|
190
288
|
const rows = this.db.query("SELECT * FROM installs ORDER BY updated_at DESC").all();
|
|
191
|
-
return rows.map(
|
|
192
|
-
id: String(row.id),
|
|
193
|
-
provider: String(row.provider),
|
|
194
|
-
entityKind: String(row.entity_kind),
|
|
195
|
-
repoId: String(row.repo_id),
|
|
196
|
-
revision: String(row.revision),
|
|
197
|
-
installPath: String(row.install_path),
|
|
198
|
-
bytes: Number(row.bytes),
|
|
199
|
-
files: JSON.parse(String(row.files_json)),
|
|
200
|
-
status: String(row.status),
|
|
201
|
-
createdAt: String(row.created_at),
|
|
202
|
-
updatedAt: String(row.updated_at)
|
|
203
|
-
}));
|
|
289
|
+
return rows.map(rowToInstall);
|
|
204
290
|
}
|
|
205
291
|
findInstall(repoIdOrId) {
|
|
206
292
|
const row = this.db.query("SELECT * FROM installs WHERE id = ? OR repo_id = ? ORDER BY updated_at DESC LIMIT 1").get(repoIdOrId, repoIdOrId);
|
|
207
|
-
if (
|
|
293
|
+
if (row)
|
|
294
|
+
return rowToInstall(row);
|
|
295
|
+
const parsed = parseInstallRef(repoIdOrId);
|
|
296
|
+
if (!parsed)
|
|
208
297
|
return null;
|
|
209
|
-
|
|
298
|
+
const refRow = parsed.hasExplicitRevision ? this.db.query(`SELECT * FROM installs
|
|
299
|
+
WHERE provider = ? AND entity_kind = ? AND repo_id = ? AND revision = ?
|
|
300
|
+
ORDER BY updated_at DESC LIMIT 1`).get(parsed.ref.provider, parsed.ref.entityKind, parsed.ref.repoId, parsed.ref.revision) : this.db.query(`SELECT * FROM installs
|
|
301
|
+
WHERE provider = ? AND entity_kind = ? AND repo_id = ?
|
|
302
|
+
ORDER BY updated_at DESC LIMIT 1`).get(parsed.ref.provider, parsed.ref.entityKind, parsed.ref.repoId);
|
|
303
|
+
return refRow ? rowToInstall(refRow) : null;
|
|
210
304
|
}
|
|
211
305
|
deleteInstall(id) {
|
|
212
306
|
const result = this.db.prepare("DELETE FROM installs WHERE id = ?").run(id);
|