@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 CHANGED
@@ -38,6 +38,36 @@ npm install
38
38
  npm run dev
39
39
  ```
40
40
 
41
+ `tomorrowos init` creates `data/tomorrowos.db` with the TomorrowOS SQLite
42
+ schema. The starter server uses that SQLite database by default, so pairings,
43
+ playlists, and device assignments survive normal server restarts.
44
+
45
+ To switch to Supabase/Postgres, edit `.env`:
46
+
47
+ ```bash
48
+ TOMORROWOS_STORE=supabase
49
+ DATABASE_URL=postgresql://...
50
+ DATABASE_SSL=true
51
+ ```
52
+
53
+ For throwaway demos/tests, set `TOMORROWOS_STORE=memory`. The generated starter
54
+ also includes `template/databaseInterface/` with SQLite and Supabase examples
55
+ for customers who want to build their own `TomorrowOSStore` implementation.
56
+
57
+ Migrate database records between supported stores:
58
+
59
+ ```bash
60
+ npx tomorrowos migrate \
61
+ --from sqlite \
62
+ --from-sqlite ./data/tomorrowos.db \
63
+ --to supabase \
64
+ --to-database-url "$DATABASE_URL"
65
+ ```
66
+
67
+ The migrate command supports `sqlite`, `postgres`, and `supabase` in either
68
+ direction. It migrates TomorrowOS database records only; copy `public/uploads`
69
+ or object-storage media separately.
70
+
41
71
  Build a player (placeholder in current SDK — see `PLAYER_INSTALL.md` for platform tooling):
42
72
 
43
73
  ```bash
package/dist/cli.js CHANGED
@@ -2,6 +2,9 @@
2
2
  import fs from "fs";
3
3
  import path from "path";
4
4
  import { fileURLToPath } from "url";
5
+ import { migrateTomorrowOSData } from "./store/migration.js";
6
+ import { PostgresStore } from "./store/postgres-store.js";
7
+ import { SQLiteStore } from "./store/sqlite-store.js";
5
8
  function packageRoot() {
6
9
  const here = path.dirname(fileURLToPath(import.meta.url));
7
10
  return path.resolve(here, "..");
@@ -32,6 +35,12 @@ function removeStarterArtifacts(destDir) {
32
35
  if (fs.existsSync(modulesPath))
33
36
  fs.rmSync(modulesPath, { recursive: true, force: true });
34
37
  }
38
+ function initializeStarterSQLite(destDir) {
39
+ const dbPath = path.join(path.resolve(destDir), "data", "tomorrowos.db");
40
+ const store = new SQLiteStore(dbPath);
41
+ store.close();
42
+ return dbPath;
43
+ }
35
44
  /** Align generated cms-starter package.json with the installed SDK version. */
36
45
  function patchStarterPackageJson(destDir) {
37
46
  const pkgPath = path.join(path.resolve(destDir), "package.json");
@@ -68,9 +77,11 @@ function copyStarter(destDir, force) {
68
77
  });
69
78
  patchStarterPackageJson(resolved);
70
79
  removeStarterArtifacts(resolved);
80
+ const dbPath = initializeStarterSQLite(resolved);
71
81
  const sdkVer = getSdkVersion();
72
82
  console.log(`[tomorrowos] Created CMS project at ${resolved}`);
73
83
  console.log(`[tomorrowos] @tomorrowos/sdk dependency: ^${sdkVer}`);
84
+ console.log(`[tomorrowos] Initialized SQLite database: ${dbPath}`);
74
85
  console.log("Next: cd there, run npm install, then npm start");
75
86
  }
76
87
  function cmdBuild(argv) {
@@ -80,11 +91,93 @@ function cmdBuild(argv) {
80
91
  console.log("Use your TomorrowOS player repository (assemble Web app + config), then run the platform SDK (e.g. Tizen CLI tizen package) from the build output directory.");
81
92
  process.exitCode = 0;
82
93
  }
94
+ function optionValue(argv, name) {
95
+ const withEquals = `${name}=`;
96
+ const found = argv.find((arg) => arg.startsWith(withEquals));
97
+ if (found)
98
+ return found.slice(withEquals.length);
99
+ const idx = argv.indexOf(name);
100
+ if (idx >= 0)
101
+ return argv[idx + 1];
102
+ return undefined;
103
+ }
104
+ function normalizeDriver(raw, side) {
105
+ const driver = String(raw || "").trim().toLowerCase();
106
+ if (driver === "sqlite" || driver === "postgres" || driver === "supabase") {
107
+ return driver;
108
+ }
109
+ throw new Error(`--${side} must be sqlite, postgres, or supabase.`);
110
+ }
111
+ function resolvePostgresSsl(raw, databaseUrl) {
112
+ const value = raw?.trim().toLowerCase();
113
+ if (value === "false" || value === "0" || value === "off")
114
+ return false;
115
+ if (value === "true" || value === "1" || value === "on") {
116
+ return { rejectUnauthorized: false };
117
+ }
118
+ if (/supabase\.(co|com)|sslmode=require/i.test(databaseUrl)) {
119
+ return { rejectUnauthorized: false };
120
+ }
121
+ return undefined;
122
+ }
123
+ function createMigrationStore(argv, side) {
124
+ const driver = normalizeDriver(optionValue(argv, `--${side}`), side);
125
+ if (driver === "sqlite") {
126
+ const dbPath = optionValue(argv, `--${side}-sqlite`) ??
127
+ optionValue(argv, `--${side}-sqlite-path`);
128
+ if (!dbPath) {
129
+ throw new Error(`--${side}=sqlite requires --${side}-sqlite ./data/tomorrowos.db`);
130
+ }
131
+ const store = new SQLiteStore(dbPath);
132
+ return {
133
+ label: `sqlite:${path.resolve(dbPath)}`,
134
+ store,
135
+ close: async () => store.close()
136
+ };
137
+ }
138
+ const databaseUrl = optionValue(argv, `--${side}-database-url`) ??
139
+ optionValue(argv, `--${side}-url`);
140
+ if (!databaseUrl) {
141
+ throw new Error(`--${side}=${driver} requires --${side}-database-url postgresql://...`);
142
+ }
143
+ const store = new PostgresStore({
144
+ connectionString: databaseUrl,
145
+ ssl: resolvePostgresSsl(optionValue(argv, `--${side}-database-ssl`), databaseUrl)
146
+ });
147
+ return {
148
+ label: `${driver}:DATABASE_URL`,
149
+ store,
150
+ close: async () => store.close()
151
+ };
152
+ }
153
+ async function cmdMigrate(argv) {
154
+ if (argv.includes("-h") || argv.includes("--help")) {
155
+ printMigrateHelp();
156
+ return;
157
+ }
158
+ const from = createMigrationStore(argv, "from");
159
+ const to = createMigrationStore(argv, "to");
160
+ try {
161
+ console.log(`[tomorrowos] Migrating data from ${from.label} to ${to.label}`);
162
+ const result = await migrateTomorrowOSData(from.store, to.store);
163
+ console.log("[tomorrowos] Migration complete:");
164
+ console.log(` pending codes: ${result.pendingCodes}`);
165
+ console.log(` device registry records: ${result.deviceRegistry}`);
166
+ console.log(` paired devices: ${result.pairedDevices}`);
167
+ console.log(` playlists: ${result.playlists}`);
168
+ console.log(` device assignments: ${result.deviceAssignments}`);
169
+ console.log("[tomorrowos] Note: media files in public/uploads or object storage are not copied by database migration.");
170
+ }
171
+ finally {
172
+ await Promise.allSettled([from.close(), to.close()]);
173
+ }
174
+ }
83
175
  function printHelp() {
84
176
  console.log(`tomorrowos — TomorrowOS SDK CLI
85
177
 
86
178
  Usage:
87
179
  tomorrowos init [directory] Copy cms-starter template (default: my-tomorrowos-cms)
180
+ tomorrowos migrate [options] Migrate TomorrowOS data between supported databases
88
181
  tomorrowos build --platform … Placeholder; player packaging lives in the player repo
89
182
 
90
183
  Options:
@@ -93,9 +186,29 @@ Options:
93
186
  Examples:
94
187
  npx @tomorrowos/sdk init
95
188
  npx @tomorrowos/sdk init ./my-cms
189
+ npx @tomorrowos/sdk migrate --from sqlite --from-sqlite ./data/tomorrowos.db --to supabase --to-database-url "$DATABASE_URL"
96
190
  `);
97
191
  }
98
- function main() {
192
+ function printMigrateHelp() {
193
+ console.log(`tomorrowos migrate — migrate TomorrowOS SDK data
194
+
195
+ Supported stores:
196
+ sqlite
197
+ postgres
198
+ supabase
199
+
200
+ Examples:
201
+ tomorrowos migrate --from sqlite --from-sqlite ./data/tomorrowos.db --to supabase --to-database-url "$DATABASE_URL"
202
+ tomorrowos migrate --from supabase --from-database-url "$DATABASE_URL" --to sqlite --to-sqlite ./data/tomorrowos.db
203
+ tomorrowos migrate --from postgres --from-database-url "$OLD_DATABASE_URL" --to postgres --to-database-url "$NEW_DATABASE_URL"
204
+
205
+ Notes:
206
+ - Migrates pairing/device/playlist/assignment database records.
207
+ - Does not copy media files from public/uploads or object storage.
208
+ - Target records with the same primary keys are overwritten/updated.
209
+ `);
210
+ }
211
+ async function main() {
99
212
  const argv = process.argv.slice(2);
100
213
  if (argv.length === 0 || argv[0] === "-h" || argv[0] === "--help") {
101
214
  printHelp();
@@ -114,8 +227,15 @@ function main() {
114
227
  cmdBuild(rest);
115
228
  return;
116
229
  }
230
+ if (cmd === "migrate") {
231
+ await cmdMigrate(rest);
232
+ return;
233
+ }
117
234
  console.error(`[tomorrowos] Unknown command: ${cmd}`);
118
235
  printHelp();
119
236
  process.exit(1);
120
237
  }
121
- main();
238
+ main().catch((err) => {
239
+ console.error("[tomorrowos]", err instanceof Error ? err.message : err);
240
+ process.exit(1);
241
+ });
package/dist/index.d.ts CHANGED
@@ -1,8 +1,15 @@
1
1
  export { TomorrowOS } from "./tomorrowos.js";
2
2
  export type { DeviceListItem, ListenOptions, TomorrowOSBrand, TomorrowOSOptions } from "./tomorrowos.js";
3
- export type { DevicePlaylistAssignment, DeviceRegistryRecord, PairedDeviceRecord, PendingCodeRecord, PlaylistItemRecord, PlaylistSchedule, PublishedPlaylistSnapshot, StoredPlaylist, TomorrowOSStore } from "./store/types.js";
3
+ export type { DevicePlaylistAssignment, DeviceRegistryRecord, PairedDeviceRecord, PendingCodeEntry, PendingCodeRecord, DeviceRegistryEntry, PlaylistItemRecord, PlaylistSchedule, PublishedPlaylistSnapshot, StoredPlaylist, TomorrowOSDataSnapshot, TomorrowOSMigratableStore, TomorrowOSStore } from "./store/types.js";
4
4
  export { PlaylistCatalog } from "./playlist-catalog.js";
5
5
  export type { BuiltDevicePolicy, SavePlaylistInput } from "./playlist-catalog.js";
6
6
  export { generateRandomPairingCode, isValidPairingCodeFormat, normalizePairingCode, PAIRING_CODE_ALPHABET, PAIRING_CODE_LENGTH } from "./pairing-code.js";
7
7
  export { MemoryStore } from "./store/memory-store.js";
8
+ export { PostgresStore } from "./store/postgres-store.js";
9
+ export type { PostgresStoreOptions } from "./store/postgres-store.js";
10
+ export { SQLiteStore } from "./store/sqlite-store.js";
11
+ export { createTomorrowOSStore, defaultSQLitePath } from "./store/factory.js";
12
+ export type { CreateTomorrowOSStoreOptions, TomorrowOSStoreDriver } from "./store/factory.js";
13
+ export { exportTomorrowOSData, importTomorrowOSData, migrateTomorrowOSData } from "./store/migration.js";
14
+ export type { MigrationResult } from "./store/migration.js";
8
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EACV,cAAc,EACd,aAAa,EACb,eAAe,EACf,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,yBAAyB,EACzB,cAAc,EACd,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EACV,cAAc,EACd,aAAa,EACb,eAAe,EACf,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,yBAAyB,EACzB,cAAc,EACd,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,YAAY,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,4BAA4B,EAC5B,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.js CHANGED
@@ -2,3 +2,7 @@ export { TomorrowOS } from "./tomorrowos.js";
2
2
  export { PlaylistCatalog } from "./playlist-catalog.js";
3
3
  export { generateRandomPairingCode, isValidPairingCodeFormat, normalizePairingCode, PAIRING_CODE_ALPHABET, PAIRING_CODE_LENGTH } from "./pairing-code.js";
4
4
  export { MemoryStore } from "./store/memory-store.js";
5
+ export { PostgresStore } from "./store/postgres-store.js";
6
+ export { SQLiteStore } from "./store/sqlite-store.js";
7
+ export { createTomorrowOSStore, defaultSQLitePath } from "./store/factory.js";
8
+ export { exportTomorrowOSData, importTomorrowOSData, migrateTomorrowOSData } from "./store/migration.js";
@@ -0,0 +1,20 @@
1
+ import type { PoolConfig } from "pg";
2
+ import type { TomorrowOSStore } from "./types.js";
3
+ export type TomorrowOSStoreDriver = "sqlite" | "postgres" | "supabase" | "memory";
4
+ export interface CreateTomorrowOSStoreOptions {
5
+ /**
6
+ * Defaults to TOMORROWOS_STORE, then postgres when DATABASE_URL is set,
7
+ * otherwise sqlite. Use memory only for tests or throwaway demos.
8
+ */
9
+ driver?: TomorrowOSStoreDriver;
10
+ /** Defaults to TOMORROWOS_DB_PATH, then ./data/tomorrowos.db. */
11
+ sqlitePath?: string;
12
+ /** Supabase/Postgres connection string. Defaults to DATABASE_URL. */
13
+ databaseUrl?: string;
14
+ /** Defaults from DATABASE_SSL / DATABASE_URL. */
15
+ postgresSsl?: PoolConfig["ssl"];
16
+ env?: NodeJS.ProcessEnv;
17
+ }
18
+ export declare function defaultSQLitePath(cwd?: string): string;
19
+ export declare function createTomorrowOSStore(options?: CreateTomorrowOSStoreOptions): TomorrowOSStore;
20
+ //# sourceMappingURL=factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/store/factory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAKrC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;AAElF,MAAM,WAAW,4BAA4B;IAC3C;;;OAGG;IACH,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAC/B,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,WAAW,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,SAAgB,GAAG,MAAM,CAE7D;AAED,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,4BAAiC,GACzC,eAAe,CAmCjB"}
@@ -0,0 +1,44 @@
1
+ import path from "path";
2
+ import { MemoryStore } from "./memory-store.js";
3
+ import { PostgresStore } from "./postgres-store.js";
4
+ import { SQLiteStore } from "./sqlite-store.js";
5
+ export function defaultSQLitePath(cwd = process.cwd()) {
6
+ return path.join(cwd, "data", "tomorrowos.db");
7
+ }
8
+ export function createTomorrowOSStore(options = {}) {
9
+ const env = options.env ?? process.env;
10
+ const databaseUrl = options.databaseUrl ?? env.DATABASE_URL;
11
+ const driver = (options.driver ??
12
+ env.TOMORROWOS_STORE ??
13
+ (databaseUrl ? "postgres" : "sqlite")).toLowerCase();
14
+ if (driver === "memory") {
15
+ return new MemoryStore();
16
+ }
17
+ if (driver === "postgres" || driver === "supabase") {
18
+ if (!databaseUrl) {
19
+ throw new Error(`TOMORROWOS_STORE=${driver} requires DATABASE_URL. Set DATABASE_URL in .env or unset TOMORROWOS_STORE to use SQLite.`);
20
+ }
21
+ return new PostgresStore({
22
+ connectionString: databaseUrl,
23
+ ssl: options.postgresSsl ?? resolvePostgresSsl(env, databaseUrl)
24
+ });
25
+ }
26
+ if (driver === "sqlite") {
27
+ return new SQLiteStore({
28
+ databasePath: options.sqlitePath ?? env.TOMORROWOS_DB_PATH ?? defaultSQLitePath()
29
+ });
30
+ }
31
+ throw new Error(`Unsupported TOMORROWOS_STORE "${driver}". Use "sqlite", "supabase", "postgres", "memory", or pass a custom TomorrowOSStore to new TomorrowOS({ store }).`);
32
+ }
33
+ function resolvePostgresSsl(env, databaseUrl) {
34
+ const raw = env.DATABASE_SSL?.trim().toLowerCase();
35
+ if (raw === "false" || raw === "0" || raw === "off")
36
+ return false;
37
+ if (raw === "true" || raw === "1" || raw === "on") {
38
+ return { rejectUnauthorized: false };
39
+ }
40
+ if (/supabase\.(co|com)|sslmode=require/i.test(databaseUrl)) {
41
+ return { rejectUnauthorized: false };
42
+ }
43
+ return undefined;
44
+ }
@@ -0,0 +1,12 @@
1
+ import type { TomorrowOSDataSnapshot, TomorrowOSMigratableStore } from "./types.js";
2
+ export interface MigrationResult {
3
+ pendingCodes: number;
4
+ deviceRegistry: number;
5
+ pairedDevices: number;
6
+ playlists: number;
7
+ deviceAssignments: number;
8
+ }
9
+ export declare function exportTomorrowOSData(store: TomorrowOSMigratableStore): Promise<TomorrowOSDataSnapshot>;
10
+ export declare function importTomorrowOSData(store: TomorrowOSMigratableStore, snapshot: TomorrowOSDataSnapshot): Promise<MigrationResult>;
11
+ export declare function migrateTomorrowOSData(from: TomorrowOSMigratableStore, to: TomorrowOSMigratableStore): Promise<MigrationResult>;
12
+ //# sourceMappingURL=migration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../../src/store/migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,yBAAyB,EAC1B,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,sBAAsB,CAAC,CA2BjC;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,yBAAyB,EAChC,QAAQ,EAAE,sBAAsB,GAC/B,OAAO,CAAC,eAAe,CAAC,CA+B1B;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,yBAAyB,EAC/B,EAAE,EAAE,yBAAyB,GAC5B,OAAO,CAAC,eAAe,CAAC,CAG1B"}
@@ -0,0 +1,47 @@
1
+ export async function exportTomorrowOSData(store) {
2
+ const [pendingCodes, deviceRegistry, pairedDevices, playlists] = await Promise.all([
3
+ store.listPendingCodes(),
4
+ store.listDeviceRegistry(),
5
+ store.listPairedDevices(),
6
+ store.listPlaylists()
7
+ ]);
8
+ const deviceAssignments = await Promise.all(pairedDevices.map(async ({ deviceId }) => ({
9
+ deviceId,
10
+ assignments: await store.getDeviceAssignments(deviceId)
11
+ })));
12
+ return {
13
+ pendingCodes,
14
+ deviceRegistry,
15
+ pairedDevices,
16
+ playlists,
17
+ deviceAssignments
18
+ };
19
+ }
20
+ export async function importTomorrowOSData(store, snapshot) {
21
+ for (const { deviceId, record } of snapshot.deviceRegistry) {
22
+ await store.setDeviceRegistry(deviceId, record);
23
+ }
24
+ for (const { deviceId, record } of snapshot.pairedDevices) {
25
+ await store.setPairedDevice(deviceId, record);
26
+ }
27
+ for (const playlist of snapshot.playlists) {
28
+ await store.setPlaylist(playlist);
29
+ }
30
+ for (const { deviceId, assignments } of snapshot.deviceAssignments) {
31
+ await store.setDeviceAssignments(deviceId, assignments);
32
+ }
33
+ for (const { code, record } of snapshot.pendingCodes) {
34
+ await store.setPendingCode(code, record);
35
+ }
36
+ return {
37
+ pendingCodes: snapshot.pendingCodes.length,
38
+ deviceRegistry: snapshot.deviceRegistry.length,
39
+ pairedDevices: snapshot.pairedDevices.length,
40
+ playlists: snapshot.playlists.length,
41
+ deviceAssignments: snapshot.deviceAssignments.reduce((total, entry) => total + entry.assignments.length, 0)
42
+ };
43
+ }
44
+ export async function migrateTomorrowOSData(from, to) {
45
+ const snapshot = await exportTomorrowOSData(from);
46
+ return importTomorrowOSData(to, snapshot);
47
+ }
@@ -0,0 +1,43 @@
1
+ import { type PoolConfig } from "pg";
2
+ import type { DevicePlaylistAssignment, DeviceRegistryEntry, DeviceRegistryRecord, PairedDeviceEntry, PairedDeviceRecord, PendingCodeEntry, PendingCodeRecord, StoredPlaylist, TomorrowOSMigratableStore } from "./types.js";
3
+ export interface PostgresStoreOptions {
4
+ connectionString: string;
5
+ ssl?: PoolConfig["ssl"];
6
+ }
7
+ /**
8
+ * Durable store backed by Postgres. Supabase works through its Postgres
9
+ * connection string, usually provided as DATABASE_URL.
10
+ */
11
+ export declare class PostgresStore implements TomorrowOSMigratableStore {
12
+ private readonly pool;
13
+ private ready?;
14
+ constructor(options: PostgresStoreOptions | string);
15
+ init(): Promise<void>;
16
+ close(): Promise<void>;
17
+ private ensureReady;
18
+ setPendingCode(code: string, record: PendingCodeRecord): Promise<void>;
19
+ getPendingCode(code: string): Promise<PendingCodeRecord | undefined>;
20
+ deletePendingCode(code: string): Promise<void>;
21
+ listPendingCodes(): Promise<PendingCodeEntry[]>;
22
+ getDeviceRegistry(deviceId: string): Promise<DeviceRegistryRecord | undefined>;
23
+ setDeviceRegistry(deviceId: string, record: DeviceRegistryRecord): Promise<void>;
24
+ getDeviceRegistryByCode(code: string): Promise<{
25
+ deviceId: string;
26
+ record: DeviceRegistryRecord;
27
+ } | undefined>;
28
+ listDeviceRegistry(): Promise<DeviceRegistryEntry[]>;
29
+ setPairedDevice(deviceId: string, record: PairedDeviceRecord): Promise<void>;
30
+ getPairedDevice(deviceId: string): Promise<PairedDeviceRecord | undefined>;
31
+ deletePairedDevice(deviceId: string): Promise<void>;
32
+ listPairedDevices(): Promise<PairedDeviceEntry[]>;
33
+ listPlaylists(): Promise<StoredPlaylist[]>;
34
+ getPlaylist(id: string): Promise<StoredPlaylist | undefined>;
35
+ setPlaylist(record: StoredPlaylist): Promise<void>;
36
+ isPlaylistNameTaken(name: string, excludeId?: string): Promise<boolean>;
37
+ getDeviceAssignments(deviceId: string): Promise<DevicePlaylistAssignment[]>;
38
+ setDeviceAssignments(deviceId: string, assignments: DevicePlaylistAssignment[]): Promise<void>;
39
+ private mapDeviceRegistryRow;
40
+ private mapPairedDeviceRow;
41
+ private mapPlaylistRow;
42
+ }
43
+ //# sourceMappingURL=postgres-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-store.d.ts","sourceRoot":"","sources":["../../src/store/postgres-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,IAAI,CAAC;AAE3C,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,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;CACzB;AAyDD;;;GAGG;AACH,qBAAa,aAAc,YAAW,yBAAyB;IAC7D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;IAC5B,OAAO,CAAC,KAAK,CAAC,CAAgB;gBAElB,OAAO,EAAE,oBAAoB,GAAG,MAAM;IAY5C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAsErB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAId,WAAW;IAKnB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAWtE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAepE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9C,gBAAgB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAiB/C,iBAAiB,CACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;IAWtC,iBAAiB,CACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;IA4BV,uBAAuB,CAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,oBAAoB,CAAA;KAAE,GAAG,SAAS,CAAC;IAepE,kBAAkB,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAapD,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC;IAwCV,eAAe,CACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAWpC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOnD,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAajD,aAAa,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAU1C,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAW5D,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAkClD,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAevE,oBAAoB,CACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAqBhC,oBAAoB,CACxB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,wBAAwB,EAAE,GACtC,OAAO,CAAC,IAAI,CAAC;IAuChB,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,cAAc;CAYvB"}