@tomorrowos/sdk 0.4.7 → 0.5.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 +30 -0
- package/dist/cli.js +122 -2
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/store/factory.d.ts +20 -0
- package/dist/store/factory.d.ts.map +1 -0
- package/dist/store/factory.js +44 -0
- package/dist/store/migration.d.ts +12 -0
- package/dist/store/migration.d.ts.map +1 -0
- package/dist/store/migration.js +47 -0
- package/dist/store/postgres-store.d.ts +43 -0
- package/dist/store/postgres-store.d.ts.map +1 -0
- package/dist/store/postgres-store.js +435 -0
- package/dist/store/sqlite-store.d.ts +41 -0
- package/dist/store/sqlite-store.d.ts.map +1 -0
- package/dist/store/sqlite-store.js +367 -0
- package/dist/store/types.d.ts +22 -0
- package/dist/store/types.d.ts.map +1 -1
- package/package.json +5 -2
- package/templates/cms-starter/.env.example +10 -0
- package/templates/cms-starter/package.json +3 -2
- package/templates/cms-starter/server.ts +8 -4
- package/templates/cms-starter/template/databaseInterface/README.md +14 -0
- package/templates/cms-starter/template/databaseInterface/sqlite-interface.ts +10 -0
- package/templates/cms-starter/template/databaseInterface/supabase-interface.ts +17 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
import { Pool } from "pg";
|
|
2
|
+
function optionalString(value) {
|
|
3
|
+
return value ?? undefined;
|
|
4
|
+
}
|
|
5
|
+
function optionalNumber(value) {
|
|
6
|
+
if (value == null)
|
|
7
|
+
return undefined;
|
|
8
|
+
const parsed = Number(value);
|
|
9
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
10
|
+
}
|
|
11
|
+
function parseJson(raw, fallback) {
|
|
12
|
+
if (!raw)
|
|
13
|
+
return fallback;
|
|
14
|
+
return JSON.parse(raw);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Durable store backed by Postgres. Supabase works through its Postgres
|
|
18
|
+
* connection string, usually provided as DATABASE_URL.
|
|
19
|
+
*/
|
|
20
|
+
export class PostgresStore {
|
|
21
|
+
pool;
|
|
22
|
+
ready;
|
|
23
|
+
constructor(options) {
|
|
24
|
+
const connectionString = typeof options === "string" ? options : options.connectionString;
|
|
25
|
+
const ssl = typeof options === "string" ? undefined : options.ssl;
|
|
26
|
+
if (!connectionString.trim()) {
|
|
27
|
+
throw new Error("PostgresStore requires a connection string.");
|
|
28
|
+
}
|
|
29
|
+
this.pool = new Pool({ connectionString, ssl });
|
|
30
|
+
}
|
|
31
|
+
async init() {
|
|
32
|
+
await this.pool.query(`
|
|
33
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
34
|
+
id INTEGER PRIMARY KEY,
|
|
35
|
+
name TEXT NOT NULL UNIQUE,
|
|
36
|
+
applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
CREATE TABLE IF NOT EXISTS pending_codes (
|
|
40
|
+
code TEXT PRIMARY KEY,
|
|
41
|
+
device_id TEXT NOT NULL,
|
|
42
|
+
created_at BIGINT NOT NULL
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
CREATE TABLE IF NOT EXISTS device_registry (
|
|
46
|
+
device_id TEXT PRIMARY KEY,
|
|
47
|
+
permanent_pairing_code TEXT NOT NULL UNIQUE,
|
|
48
|
+
code_created_at BIGINT NOT NULL,
|
|
49
|
+
serial_number TEXT,
|
|
50
|
+
first_seen_at BIGINT,
|
|
51
|
+
last_hello_at BIGINT
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
CREATE TABLE IF NOT EXISTS paired_devices (
|
|
55
|
+
device_id TEXT PRIMARY KEY,
|
|
56
|
+
pairing_token TEXT NOT NULL,
|
|
57
|
+
paired_at TEXT NOT NULL,
|
|
58
|
+
device_name TEXT,
|
|
59
|
+
platform TEXT,
|
|
60
|
+
system TEXT,
|
|
61
|
+
last_boot_at TEXT,
|
|
62
|
+
last_online_at TEXT,
|
|
63
|
+
last_offline_at TEXT,
|
|
64
|
+
last_policy_push_at TEXT
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
CREATE TABLE IF NOT EXISTS playlists (
|
|
68
|
+
id TEXT PRIMARY KEY,
|
|
69
|
+
name TEXT NOT NULL,
|
|
70
|
+
schedule_json TEXT,
|
|
71
|
+
items_json TEXT NOT NULL DEFAULT '[]',
|
|
72
|
+
version INTEGER NOT NULL,
|
|
73
|
+
updated_at TEXT NOT NULL,
|
|
74
|
+
retired BOOLEAN NOT NULL DEFAULT false,
|
|
75
|
+
retired_at TEXT
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
CREATE UNIQUE INDEX IF NOT EXISTS playlists_active_name_unique
|
|
79
|
+
ON playlists (lower(trim(name)))
|
|
80
|
+
WHERE retired = false;
|
|
81
|
+
|
|
82
|
+
CREATE TABLE IF NOT EXISTS device_assignments (
|
|
83
|
+
device_id TEXT NOT NULL,
|
|
84
|
+
playlist_id TEXT NOT NULL,
|
|
85
|
+
sort_order INTEGER NOT NULL,
|
|
86
|
+
published_version INTEGER NOT NULL,
|
|
87
|
+
published_at TEXT NOT NULL,
|
|
88
|
+
snapshot_json TEXT NOT NULL,
|
|
89
|
+
PRIMARY KEY (device_id, playlist_id)
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
CREATE INDEX IF NOT EXISTS device_assignments_device_order_idx
|
|
93
|
+
ON device_assignments (device_id, sort_order);
|
|
94
|
+
|
|
95
|
+
INSERT INTO schema_migrations (id, name)
|
|
96
|
+
VALUES (1, 'initial_tomorrowos_store')
|
|
97
|
+
ON CONFLICT (id) DO NOTHING;
|
|
98
|
+
`);
|
|
99
|
+
}
|
|
100
|
+
async close() {
|
|
101
|
+
await this.pool.end();
|
|
102
|
+
}
|
|
103
|
+
async ensureReady() {
|
|
104
|
+
this.ready ??= this.init();
|
|
105
|
+
await this.ready;
|
|
106
|
+
}
|
|
107
|
+
async setPendingCode(code, record) {
|
|
108
|
+
await this.ensureReady();
|
|
109
|
+
await this.pool.query(`
|
|
110
|
+
INSERT INTO pending_codes (code, device_id, created_at)
|
|
111
|
+
VALUES ($1, $2, $3)
|
|
112
|
+
ON CONFLICT (code) DO UPDATE SET
|
|
113
|
+
device_id = EXCLUDED.device_id,
|
|
114
|
+
created_at = EXCLUDED.created_at
|
|
115
|
+
`, [code, record.deviceId, record.createdAt]);
|
|
116
|
+
}
|
|
117
|
+
async getPendingCode(code) {
|
|
118
|
+
await this.ensureReady();
|
|
119
|
+
const result = await this.pool.query(`
|
|
120
|
+
SELECT device_id, created_at
|
|
121
|
+
FROM pending_codes
|
|
122
|
+
WHERE code = $1
|
|
123
|
+
`, [code]);
|
|
124
|
+
const row = result.rows[0];
|
|
125
|
+
if (!row)
|
|
126
|
+
return undefined;
|
|
127
|
+
return { deviceId: row.device_id, createdAt: Number(row.created_at) };
|
|
128
|
+
}
|
|
129
|
+
async deletePendingCode(code) {
|
|
130
|
+
await this.ensureReady();
|
|
131
|
+
await this.pool.query("DELETE FROM pending_codes WHERE code = $1", [code]);
|
|
132
|
+
}
|
|
133
|
+
async listPendingCodes() {
|
|
134
|
+
await this.ensureReady();
|
|
135
|
+
const result = await this.pool.query(`
|
|
136
|
+
SELECT code, device_id, created_at
|
|
137
|
+
FROM pending_codes
|
|
138
|
+
ORDER BY created_at ASC
|
|
139
|
+
`);
|
|
140
|
+
return result.rows.map((row) => ({
|
|
141
|
+
code: row.code,
|
|
142
|
+
record: { deviceId: row.device_id, createdAt: Number(row.created_at) }
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
async getDeviceRegistry(deviceId) {
|
|
146
|
+
await this.ensureReady();
|
|
147
|
+
const result = await this.pool.query(`
|
|
148
|
+
SELECT *
|
|
149
|
+
FROM device_registry
|
|
150
|
+
WHERE device_id = $1
|
|
151
|
+
`, [deviceId]);
|
|
152
|
+
const row = result.rows[0];
|
|
153
|
+
return row ? this.mapDeviceRegistryRow(row) : undefined;
|
|
154
|
+
}
|
|
155
|
+
async setDeviceRegistry(deviceId, record) {
|
|
156
|
+
await this.ensureReady();
|
|
157
|
+
await this.pool.query(`
|
|
158
|
+
INSERT INTO device_registry (
|
|
159
|
+
device_id,
|
|
160
|
+
permanent_pairing_code,
|
|
161
|
+
code_created_at,
|
|
162
|
+
serial_number,
|
|
163
|
+
first_seen_at,
|
|
164
|
+
last_hello_at
|
|
165
|
+
)
|
|
166
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
167
|
+
ON CONFLICT (device_id) DO UPDATE SET
|
|
168
|
+
permanent_pairing_code = EXCLUDED.permanent_pairing_code,
|
|
169
|
+
code_created_at = EXCLUDED.code_created_at,
|
|
170
|
+
serial_number = EXCLUDED.serial_number,
|
|
171
|
+
first_seen_at = EXCLUDED.first_seen_at,
|
|
172
|
+
last_hello_at = EXCLUDED.last_hello_at
|
|
173
|
+
`, [
|
|
174
|
+
deviceId,
|
|
175
|
+
record.permanentPairingCode,
|
|
176
|
+
record.codeCreatedAt,
|
|
177
|
+
record.serialNumber ?? null,
|
|
178
|
+
record.firstSeenAt ?? null,
|
|
179
|
+
record.lastHelloAt ?? null
|
|
180
|
+
]);
|
|
181
|
+
}
|
|
182
|
+
async getDeviceRegistryByCode(code) {
|
|
183
|
+
await this.ensureReady();
|
|
184
|
+
const result = await this.pool.query(`
|
|
185
|
+
SELECT *
|
|
186
|
+
FROM device_registry
|
|
187
|
+
WHERE permanent_pairing_code = $1
|
|
188
|
+
`, [code]);
|
|
189
|
+
const row = result.rows[0];
|
|
190
|
+
if (!row)
|
|
191
|
+
return undefined;
|
|
192
|
+
return {
|
|
193
|
+
deviceId: row.device_id,
|
|
194
|
+
record: this.mapDeviceRegistryRow(row)
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
async listDeviceRegistry() {
|
|
198
|
+
await this.ensureReady();
|
|
199
|
+
const result = await this.pool.query(`
|
|
200
|
+
SELECT *
|
|
201
|
+
FROM device_registry
|
|
202
|
+
ORDER BY code_created_at ASC
|
|
203
|
+
`);
|
|
204
|
+
return result.rows.map((row) => ({
|
|
205
|
+
deviceId: row.device_id,
|
|
206
|
+
record: this.mapDeviceRegistryRow(row)
|
|
207
|
+
}));
|
|
208
|
+
}
|
|
209
|
+
async setPairedDevice(deviceId, record) {
|
|
210
|
+
await this.ensureReady();
|
|
211
|
+
await this.pool.query(`
|
|
212
|
+
INSERT INTO paired_devices (
|
|
213
|
+
device_id,
|
|
214
|
+
pairing_token,
|
|
215
|
+
paired_at,
|
|
216
|
+
device_name,
|
|
217
|
+
platform,
|
|
218
|
+
system,
|
|
219
|
+
last_boot_at,
|
|
220
|
+
last_online_at,
|
|
221
|
+
last_offline_at,
|
|
222
|
+
last_policy_push_at
|
|
223
|
+
)
|
|
224
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
225
|
+
ON CONFLICT (device_id) DO UPDATE SET
|
|
226
|
+
pairing_token = EXCLUDED.pairing_token,
|
|
227
|
+
paired_at = EXCLUDED.paired_at,
|
|
228
|
+
device_name = EXCLUDED.device_name,
|
|
229
|
+
platform = EXCLUDED.platform,
|
|
230
|
+
system = EXCLUDED.system,
|
|
231
|
+
last_boot_at = EXCLUDED.last_boot_at,
|
|
232
|
+
last_online_at = EXCLUDED.last_online_at,
|
|
233
|
+
last_offline_at = EXCLUDED.last_offline_at,
|
|
234
|
+
last_policy_push_at = EXCLUDED.last_policy_push_at
|
|
235
|
+
`, [
|
|
236
|
+
deviceId,
|
|
237
|
+
record.pairingToken,
|
|
238
|
+
record.pairedAt,
|
|
239
|
+
record.deviceName ?? null,
|
|
240
|
+
record.platform ?? null,
|
|
241
|
+
record.system ?? null,
|
|
242
|
+
record.lastBootAt ?? null,
|
|
243
|
+
record.lastOnlineAt ?? null,
|
|
244
|
+
record.lastOfflineAt ?? null,
|
|
245
|
+
record.lastPolicyPushAt ?? null
|
|
246
|
+
]);
|
|
247
|
+
}
|
|
248
|
+
async getPairedDevice(deviceId) {
|
|
249
|
+
await this.ensureReady();
|
|
250
|
+
const result = await this.pool.query(`
|
|
251
|
+
SELECT *
|
|
252
|
+
FROM paired_devices
|
|
253
|
+
WHERE device_id = $1
|
|
254
|
+
`, [deviceId]);
|
|
255
|
+
const row = result.rows[0];
|
|
256
|
+
return row ? this.mapPairedDeviceRow(row) : undefined;
|
|
257
|
+
}
|
|
258
|
+
async deletePairedDevice(deviceId) {
|
|
259
|
+
await this.ensureReady();
|
|
260
|
+
await this.pool.query("DELETE FROM paired_devices WHERE device_id = $1", [
|
|
261
|
+
deviceId
|
|
262
|
+
]);
|
|
263
|
+
}
|
|
264
|
+
async listPairedDevices() {
|
|
265
|
+
await this.ensureReady();
|
|
266
|
+
const result = await this.pool.query(`
|
|
267
|
+
SELECT *
|
|
268
|
+
FROM paired_devices
|
|
269
|
+
ORDER BY paired_at ASC
|
|
270
|
+
`);
|
|
271
|
+
return result.rows.map((row) => ({
|
|
272
|
+
deviceId: row.device_id,
|
|
273
|
+
record: this.mapPairedDeviceRow(row)
|
|
274
|
+
}));
|
|
275
|
+
}
|
|
276
|
+
async listPlaylists() {
|
|
277
|
+
await this.ensureReady();
|
|
278
|
+
const result = await this.pool.query(`
|
|
279
|
+
SELECT *
|
|
280
|
+
FROM playlists
|
|
281
|
+
ORDER BY updated_at DESC
|
|
282
|
+
`);
|
|
283
|
+
return result.rows.map((row) => this.mapPlaylistRow(row));
|
|
284
|
+
}
|
|
285
|
+
async getPlaylist(id) {
|
|
286
|
+
await this.ensureReady();
|
|
287
|
+
const result = await this.pool.query(`
|
|
288
|
+
SELECT *
|
|
289
|
+
FROM playlists
|
|
290
|
+
WHERE id = $1
|
|
291
|
+
`, [id]);
|
|
292
|
+
const row = result.rows[0];
|
|
293
|
+
return row ? this.mapPlaylistRow(row) : undefined;
|
|
294
|
+
}
|
|
295
|
+
async setPlaylist(record) {
|
|
296
|
+
await this.ensureReady();
|
|
297
|
+
await this.pool.query(`
|
|
298
|
+
INSERT INTO playlists (
|
|
299
|
+
id,
|
|
300
|
+
name,
|
|
301
|
+
schedule_json,
|
|
302
|
+
items_json,
|
|
303
|
+
version,
|
|
304
|
+
updated_at,
|
|
305
|
+
retired,
|
|
306
|
+
retired_at
|
|
307
|
+
)
|
|
308
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
309
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
310
|
+
name = EXCLUDED.name,
|
|
311
|
+
schedule_json = EXCLUDED.schedule_json,
|
|
312
|
+
items_json = EXCLUDED.items_json,
|
|
313
|
+
version = EXCLUDED.version,
|
|
314
|
+
updated_at = EXCLUDED.updated_at,
|
|
315
|
+
retired = EXCLUDED.retired,
|
|
316
|
+
retired_at = EXCLUDED.retired_at
|
|
317
|
+
`, [
|
|
318
|
+
record.id,
|
|
319
|
+
record.name,
|
|
320
|
+
record.schedule ? JSON.stringify(record.schedule) : null,
|
|
321
|
+
JSON.stringify(record.items ?? []),
|
|
322
|
+
record.version,
|
|
323
|
+
record.updatedAt,
|
|
324
|
+
record.retired === true,
|
|
325
|
+
record.retiredAt ?? null
|
|
326
|
+
]);
|
|
327
|
+
}
|
|
328
|
+
async isPlaylistNameTaken(name, excludeId) {
|
|
329
|
+
await this.ensureReady();
|
|
330
|
+
const target = name.trim().toLowerCase();
|
|
331
|
+
if (!target)
|
|
332
|
+
return false;
|
|
333
|
+
const result = await this.pool.query(`
|
|
334
|
+
SELECT id
|
|
335
|
+
FROM playlists
|
|
336
|
+
WHERE retired = false
|
|
337
|
+
AND lower(trim(name)) = $1
|
|
338
|
+
AND ($2::text IS NULL OR id != $2)
|
|
339
|
+
LIMIT 1
|
|
340
|
+
`, [target, excludeId ?? null]);
|
|
341
|
+
return result.rows.length > 0;
|
|
342
|
+
}
|
|
343
|
+
async getDeviceAssignments(deviceId) {
|
|
344
|
+
await this.ensureReady();
|
|
345
|
+
const result = await this.pool.query(`
|
|
346
|
+
SELECT playlist_id, published_version, published_at, snapshot_json
|
|
347
|
+
FROM device_assignments
|
|
348
|
+
WHERE device_id = $1
|
|
349
|
+
ORDER BY sort_order ASC
|
|
350
|
+
`, [deviceId]);
|
|
351
|
+
return result.rows.map((row) => ({
|
|
352
|
+
playlistId: row.playlist_id,
|
|
353
|
+
publishedVersion: row.published_version,
|
|
354
|
+
publishedAt: row.published_at,
|
|
355
|
+
snapshot: parseJson(row.snapshot_json, {
|
|
356
|
+
id: row.playlist_id,
|
|
357
|
+
name: row.playlist_id,
|
|
358
|
+
version: row.published_version,
|
|
359
|
+
items: []
|
|
360
|
+
})
|
|
361
|
+
}));
|
|
362
|
+
}
|
|
363
|
+
async setDeviceAssignments(deviceId, assignments) {
|
|
364
|
+
await this.ensureReady();
|
|
365
|
+
const client = await this.pool.connect();
|
|
366
|
+
try {
|
|
367
|
+
await client.query("BEGIN");
|
|
368
|
+
await client.query("DELETE FROM device_assignments WHERE device_id = $1", [
|
|
369
|
+
deviceId
|
|
370
|
+
]);
|
|
371
|
+
for (const [index, assignment] of assignments.entries()) {
|
|
372
|
+
await client.query(`
|
|
373
|
+
INSERT INTO device_assignments (
|
|
374
|
+
device_id,
|
|
375
|
+
playlist_id,
|
|
376
|
+
sort_order,
|
|
377
|
+
published_version,
|
|
378
|
+
published_at,
|
|
379
|
+
snapshot_json
|
|
380
|
+
)
|
|
381
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
382
|
+
`, [
|
|
383
|
+
deviceId,
|
|
384
|
+
assignment.playlistId,
|
|
385
|
+
index,
|
|
386
|
+
assignment.publishedVersion,
|
|
387
|
+
assignment.publishedAt,
|
|
388
|
+
JSON.stringify(assignment.snapshot)
|
|
389
|
+
]);
|
|
390
|
+
}
|
|
391
|
+
await client.query("COMMIT");
|
|
392
|
+
}
|
|
393
|
+
catch (err) {
|
|
394
|
+
await client.query("ROLLBACK");
|
|
395
|
+
throw err;
|
|
396
|
+
}
|
|
397
|
+
finally {
|
|
398
|
+
client.release();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
mapDeviceRegistryRow(row) {
|
|
402
|
+
return {
|
|
403
|
+
permanentPairingCode: row.permanent_pairing_code,
|
|
404
|
+
codeCreatedAt: Number(row.code_created_at),
|
|
405
|
+
serialNumber: optionalString(row.serial_number),
|
|
406
|
+
firstSeenAt: optionalNumber(row.first_seen_at),
|
|
407
|
+
lastHelloAt: optionalNumber(row.last_hello_at)
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
mapPairedDeviceRow(row) {
|
|
411
|
+
return {
|
|
412
|
+
pairingToken: row.pairing_token,
|
|
413
|
+
pairedAt: row.paired_at,
|
|
414
|
+
deviceName: optionalString(row.device_name),
|
|
415
|
+
platform: optionalString(row.platform),
|
|
416
|
+
system: optionalString(row.system),
|
|
417
|
+
lastBootAt: optionalString(row.last_boot_at),
|
|
418
|
+
lastOnlineAt: optionalString(row.last_online_at),
|
|
419
|
+
lastOfflineAt: optionalString(row.last_offline_at),
|
|
420
|
+
lastPolicyPushAt: optionalString(row.last_policy_push_at)
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
mapPlaylistRow(row) {
|
|
424
|
+
return {
|
|
425
|
+
id: row.id,
|
|
426
|
+
name: row.name,
|
|
427
|
+
schedule: parseJson(row.schedule_json, undefined),
|
|
428
|
+
items: parseJson(row.items_json, []),
|
|
429
|
+
version: row.version,
|
|
430
|
+
updatedAt: row.updated_at,
|
|
431
|
+
retired: row.retired,
|
|
432
|
+
retiredAt: optionalString(row.retired_at)
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { DevicePlaylistAssignment, DeviceRegistryEntry, DeviceRegistryRecord, PairedDeviceEntry, PairedDeviceRecord, PendingCodeEntry, PendingCodeRecord, StoredPlaylist, TomorrowOSMigratableStore } from "./types.js";
|
|
2
|
+
interface SQLiteStoreOptions {
|
|
3
|
+
databasePath?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Durable single-node store backed by a local SQLite database.
|
|
7
|
+
* For multi-instance production, inject a Postgres/Supabase-backed TomorrowOSStore instead.
|
|
8
|
+
*/
|
|
9
|
+
export declare class SQLiteStore implements TomorrowOSMigratableStore {
|
|
10
|
+
readonly databasePath: string;
|
|
11
|
+
private readonly db;
|
|
12
|
+
constructor(options?: SQLiteStoreOptions | string);
|
|
13
|
+
init(): void;
|
|
14
|
+
close(): void;
|
|
15
|
+
setPendingCode(code: string, record: PendingCodeRecord): Promise<void>;
|
|
16
|
+
getPendingCode(code: string): Promise<PendingCodeRecord | undefined>;
|
|
17
|
+
deletePendingCode(code: string): Promise<void>;
|
|
18
|
+
listPendingCodes(): Promise<PendingCodeEntry[]>;
|
|
19
|
+
getDeviceRegistry(deviceId: string): Promise<DeviceRegistryRecord | undefined>;
|
|
20
|
+
setDeviceRegistry(deviceId: string, record: DeviceRegistryRecord): Promise<void>;
|
|
21
|
+
getDeviceRegistryByCode(code: string): Promise<{
|
|
22
|
+
deviceId: string;
|
|
23
|
+
record: DeviceRegistryRecord;
|
|
24
|
+
} | undefined>;
|
|
25
|
+
listDeviceRegistry(): Promise<DeviceRegistryEntry[]>;
|
|
26
|
+
setPairedDevice(deviceId: string, record: PairedDeviceRecord): Promise<void>;
|
|
27
|
+
getPairedDevice(deviceId: string): Promise<PairedDeviceRecord | undefined>;
|
|
28
|
+
deletePairedDevice(deviceId: string): Promise<void>;
|
|
29
|
+
listPairedDevices(): Promise<PairedDeviceEntry[]>;
|
|
30
|
+
listPlaylists(): Promise<StoredPlaylist[]>;
|
|
31
|
+
getPlaylist(id: string): Promise<StoredPlaylist | undefined>;
|
|
32
|
+
setPlaylist(record: StoredPlaylist): Promise<void>;
|
|
33
|
+
isPlaylistNameTaken(name: string, excludeId?: string): Promise<boolean>;
|
|
34
|
+
getDeviceAssignments(deviceId: string): Promise<DevicePlaylistAssignment[]>;
|
|
35
|
+
setDeviceAssignments(deviceId: string, assignments: DevicePlaylistAssignment[]): Promise<void>;
|
|
36
|
+
private mapDeviceRegistryRow;
|
|
37
|
+
private mapPairedDeviceRow;
|
|
38
|
+
private mapPlaylistRow;
|
|
39
|
+
}
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=sqlite-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/store/sqlite-store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EAGjB,cAAc,EACd,yBAAyB,EAC1B,MAAM,YAAY,CAAC;AAEpB,UAAU,kBAAkB;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA+DD;;;GAGG;AACH,qBAAa,WAAY,YAAW,yBAAyB;IAC3D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAoB;gBAE3B,OAAO,GAAE,kBAAkB,GAAG,MAAW;IAerD,IAAI,IAAI,IAAI;IAwEZ,KAAK,IAAI,IAAI;IAIP,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAUpE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,gBAAgB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAY/C,iBAAiB,CACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;IAStC,iBAAiB,CACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;IA2BV,uBAAuB,CAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,oBAAoB,CAAA;KAAE,GAAG,SAAS,CAAC;IAapE,kBAAkB,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAYpD,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC;IAuCV,eAAe,CACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IASpC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAYjD,aAAa,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAS1C,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAS5D,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAiClD,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAcvE,oBAAoB,CACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAoBhC,oBAAoB,CACxB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,wBAAwB,EAAE,GACtC,OAAO,CAAC,IAAI,CAAC;IA8BhB,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,cAAc;CAYvB"}
|