@tomorrowos/sdk 0.2.4 → 0.3.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/dist/index.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  export { TomorrowOS } from "./tomorrowos.js";
2
2
  export type { DeviceListItem, ListenOptions, TomorrowOSBrand, TomorrowOSOptions } from "./tomorrowos.js";
3
- export type { PairedDeviceRecord, PendingCodeRecord, TomorrowOSStore } from "./store/types.js";
3
+ export type { DevicePlaylistAssignment, DeviceRegistryRecord, PairedDeviceRecord, PendingCodeRecord, PlaylistItemRecord, PlaylistSchedule, PublishedPlaylistSnapshot, StoredPlaylist, TomorrowOSStore } from "./store/types.js";
4
+ export { PlaylistCatalog } from "./playlist-catalog.js";
5
+ export type { BuiltDevicePolicy, SavePlaylistInput } from "./playlist-catalog.js";
6
+ export { generateRandomPairingCode, isValidPairingCodeFormat, normalizePairingCode, PAIRING_CODE_ALPHABET, PAIRING_CODE_LENGTH } from "./pairing-code.js";
4
7
  export { MemoryStore } from "./store/memory-store.js";
5
8
  //# 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,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAC1B,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,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"}
package/dist/index.js CHANGED
@@ -1,2 +1,4 @@
1
1
  export { TomorrowOS } from "./tomorrowos.js";
2
+ export { PlaylistCatalog } from "./playlist-catalog.js";
3
+ export { generateRandomPairingCode, isValidPairingCodeFormat, normalizePairingCode, PAIRING_CODE_ALPHABET, PAIRING_CODE_LENGTH } from "./pairing-code.js";
2
4
  export { MemoryStore } from "./store/memory-store.js";
@@ -0,0 +1,8 @@
1
+ /** 0-9 and A-Z — 36 characters per digit position. */
2
+ export declare const PAIRING_CODE_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
3
+ export declare const PAIRING_CODE_LENGTH = 6;
4
+ export declare function normalizePairingCode(raw: string): string;
5
+ export declare function isValidPairingCodeFormat(code: string): boolean;
6
+ /** Random 6-character code; each position is digit or uppercase letter. */
7
+ export declare function generateRandomPairingCode(): string;
8
+ //# sourceMappingURL=pairing-code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairing-code.d.ts","sourceRoot":"","sources":["../src/pairing-code.ts"],"names":[],"mappings":"AAEA,sDAAsD;AACtD,eAAO,MAAM,qBAAqB,yCAAyC,CAAC;AAE5E,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKxD;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAK9D;AAED,2EAA2E;AAC3E,wBAAgB,yBAAyB,IAAI,MAAM,CAOlD"}
@@ -0,0 +1,23 @@
1
+ import { randomBytes } from "crypto";
2
+ /** 0-9 and A-Z — 36 characters per digit position. */
3
+ export const PAIRING_CODE_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
4
+ export const PAIRING_CODE_LENGTH = 6;
5
+ export function normalizePairingCode(raw) {
6
+ return String(raw || "")
7
+ .trim()
8
+ .toUpperCase()
9
+ .replace(/[^0-9A-Z]/g, "");
10
+ }
11
+ export function isValidPairingCodeFormat(code) {
12
+ return (code.length === PAIRING_CODE_LENGTH &&
13
+ [...code].every((ch) => PAIRING_CODE_ALPHABET.includes(ch)));
14
+ }
15
+ /** Random 6-character code; each position is digit or uppercase letter. */
16
+ export function generateRandomPairingCode() {
17
+ const bytes = randomBytes(PAIRING_CODE_LENGTH);
18
+ let out = "";
19
+ for (let i = 0; i < PAIRING_CODE_LENGTH; i += 1) {
20
+ out += PAIRING_CODE_ALPHABET[bytes[i] % PAIRING_CODE_ALPHABET.length];
21
+ }
22
+ return out;
23
+ }
@@ -0,0 +1,35 @@
1
+ import type { DevicePlaylistAssignment, PlaylistItemRecord, PlaylistSchedule, PublishedPlaylistSnapshot, StoredPlaylist, TomorrowOSStore } from "./store/types.js";
2
+ export interface SavePlaylistInput {
3
+ id?: string;
4
+ name: string;
5
+ schedule?: PlaylistSchedule;
6
+ items: PlaylistItemRecord[];
7
+ }
8
+ export interface BuiltDevicePolicy {
9
+ policy: {
10
+ playlists: PublishedPlaylistSnapshot[];
11
+ fallback: {
12
+ type: "brand";
13
+ };
14
+ revision?: number;
15
+ syncMode?: "latest" | "snapshot";
16
+ };
17
+ }
18
+ export declare class PlaylistCatalog {
19
+ private readonly store;
20
+ constructor(store: TomorrowOSStore);
21
+ listPlaylists(): Promise<StoredPlaylist[]>;
22
+ listPlaylistsIncludingRetired(): Promise<StoredPlaylist[]>;
23
+ getPlaylist(id: string): Promise<StoredPlaylist | undefined>;
24
+ savePlaylist(input: SavePlaylistInput): Promise<StoredPlaylist>;
25
+ retirePlaylist(id: string): Promise<StoredPlaylist>;
26
+ getDeviceAssignments(deviceId: string): Promise<DevicePlaylistAssignment[]>;
27
+ publishPlaylistsToDevice(deviceId: string, playlistIds: string[]): Promise<BuiltDevicePolicy>;
28
+ removePlaylistFromDevice(deviceId: string, playlistId: string): Promise<BuiltDevicePolicy>;
29
+ buildPolicyForDevice(deviceId: string, options?: {
30
+ useLatest?: boolean;
31
+ }): Promise<BuiltDevicePolicy>;
32
+ private buildPolicyFromAssignments;
33
+ canPublishPlaylistToNewDevice(playlistId: string): Promise<boolean>;
34
+ }
35
+ //# sourceMappingURL=playlist-catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playlist-catalog.d.ts","sourceRoot":"","sources":["../src/playlist-catalog.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,wBAAwB,EACxB,kBAAkB,EAClB,gBAAgB,EAChB,yBAAyB,EACzB,cAAc,EACd,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,kBAAkB,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE;QACN,SAAS,EAAE,yBAAyB,EAAE,CAAC;QACvC,QAAQ,EAAE;YAAE,IAAI,EAAE,OAAO,CAAA;SAAE,CAAC;QAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;KAClC,CAAC;CACH;AAYD,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,eAAe;IAE7C,aAAa,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAK1C,6BAA6B,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAI1D,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAI5D,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC;IAgC/D,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAcnD,oBAAoB,CACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAIhC,wBAAwB,CAC5B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAAE,GACpB,OAAO,CAAC,iBAAiB,CAAC;IA8BvB,wBAAwB,CAC5B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,iBAAiB,CAAC;IAOvB,oBAAoB,CACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAO,GACpC,OAAO,CAAC,iBAAiB,CAAC;YAKf,0BAA0B;IA+BxC,6BAA6B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAGpE"}
@@ -0,0 +1,133 @@
1
+ import { randomUUID } from "crypto";
2
+ function cloneSnapshot(playlist) {
3
+ return {
4
+ id: playlist.id,
5
+ name: playlist.name,
6
+ version: playlist.version,
7
+ schedule: playlist.schedule ? { ...playlist.schedule } : undefined,
8
+ items: playlist.items.map((item) => ({ ...item }))
9
+ };
10
+ }
11
+ export class PlaylistCatalog {
12
+ store;
13
+ constructor(store) {
14
+ this.store = store;
15
+ }
16
+ async listPlaylists() {
17
+ const all = await this.store.listPlaylists();
18
+ return all.filter((p) => !p.retired);
19
+ }
20
+ async listPlaylistsIncludingRetired() {
21
+ return this.store.listPlaylists();
22
+ }
23
+ async getPlaylist(id) {
24
+ return this.store.getPlaylist(id);
25
+ }
26
+ async savePlaylist(input) {
27
+ const name = String(input.name || "").trim();
28
+ if (!name) {
29
+ throw Object.assign(new Error("Playlist name is required"), {
30
+ code: "PLAYLIST_INVALID"
31
+ });
32
+ }
33
+ const id = input.id?.trim() || randomUUID();
34
+ const taken = await this.store.isPlaylistNameTaken(name, id);
35
+ if (taken) {
36
+ throw Object.assign(new Error("Playlist name already exists"), {
37
+ code: "PLAYLIST_NAME_CONFLICT"
38
+ });
39
+ }
40
+ const existing = await this.store.getPlaylist(id);
41
+ const now = new Date().toISOString();
42
+ const record = {
43
+ id,
44
+ name,
45
+ schedule: input.schedule,
46
+ items: Array.isArray(input.items) ? input.items : [],
47
+ version: (existing?.version ?? 0) + 1,
48
+ updatedAt: now,
49
+ retired: false
50
+ };
51
+ await this.store.setPlaylist(record);
52
+ return record;
53
+ }
54
+ async retirePlaylist(id) {
55
+ const existing = await this.store.getPlaylist(id);
56
+ if (!existing) {
57
+ throw Object.assign(new Error("Playlist not found"), { code: "PLAYLIST_NOT_FOUND" });
58
+ }
59
+ const record = {
60
+ ...existing,
61
+ retired: true,
62
+ retiredAt: new Date().toISOString()
63
+ };
64
+ await this.store.setPlaylist(record);
65
+ return record;
66
+ }
67
+ async getDeviceAssignments(deviceId) {
68
+ return this.store.getDeviceAssignments(deviceId);
69
+ }
70
+ async publishPlaylistsToDevice(deviceId, playlistIds) {
71
+ const ids = [...new Set(playlistIds.map((x) => String(x).trim()).filter(Boolean))];
72
+ if (ids.length === 0) {
73
+ throw Object.assign(new Error("Select at least one playlist"), {
74
+ code: "PLAYLIST_INVALID"
75
+ });
76
+ }
77
+ const assignments = [];
78
+ for (const playlistId of ids) {
79
+ const playlist = await this.store.getPlaylist(playlistId);
80
+ if (!playlist || playlist.retired) {
81
+ throw Object.assign(new Error(`Playlist not available: ${playlistId}`), {
82
+ code: "PLAYLIST_NOT_FOUND"
83
+ });
84
+ }
85
+ assignments.push({
86
+ playlistId,
87
+ publishedVersion: playlist.version,
88
+ publishedAt: new Date().toISOString(),
89
+ snapshot: cloneSnapshot(playlist)
90
+ });
91
+ }
92
+ await this.store.setDeviceAssignments(deviceId, assignments);
93
+ return this.buildPolicyFromAssignments(assignments, { useLatest: false });
94
+ }
95
+ async removePlaylistFromDevice(deviceId, playlistId) {
96
+ const assignments = await this.store.getDeviceAssignments(deviceId);
97
+ const next = assignments.filter((a) => a.playlistId !== playlistId);
98
+ await this.store.setDeviceAssignments(deviceId, next);
99
+ return this.buildPolicyFromAssignments(next, { useLatest: false });
100
+ }
101
+ async buildPolicyForDevice(deviceId, options = {}) {
102
+ const assignments = await this.store.getDeviceAssignments(deviceId);
103
+ return this.buildPolicyFromAssignments(assignments, options);
104
+ }
105
+ async buildPolicyFromAssignments(assignments, options) {
106
+ const useLatest = options.useLatest === true;
107
+ const playlists = [];
108
+ for (const assignment of assignments) {
109
+ if (useLatest) {
110
+ const current = await this.store.getPlaylist(assignment.playlistId);
111
+ if (current && !current.retired) {
112
+ playlists.push(cloneSnapshot(current));
113
+ continue;
114
+ }
115
+ }
116
+ playlists.push({
117
+ ...assignment.snapshot,
118
+ items: assignment.snapshot.items.map((item) => ({ ...item }))
119
+ });
120
+ }
121
+ return {
122
+ policy: {
123
+ playlists,
124
+ fallback: { type: "brand" },
125
+ revision: Date.now(),
126
+ syncMode: useLatest ? "latest" : "snapshot"
127
+ }
128
+ };
129
+ }
130
+ canPublishPlaylistToNewDevice(playlistId) {
131
+ return this.store.getPlaylist(playlistId).then((p) => !!p && !p?.retired);
132
+ }
133
+ }
@@ -1,17 +1,33 @@
1
- import type { PairedDeviceEntry, PairedDeviceRecord, PendingCodeRecord, TomorrowOSStore } from "./types.js";
1
+ import type { DevicePlaylistAssignment, DeviceRegistryRecord, PairedDeviceEntry, PairedDeviceRecord, PendingCodeRecord, StoredPlaylist, TomorrowOSStore } from "./types.js";
2
2
  /**
3
3
  * Default store for development / single-node demos.
4
4
  * Data is lost on process restart — not for multi-instance production.
5
5
  */
6
6
  export declare class MemoryStore implements TomorrowOSStore {
7
7
  private readonly pendingCodes;
8
+ private readonly deviceRegistry;
9
+ private readonly codeToDeviceId;
8
10
  private readonly pairedDevices;
11
+ private readonly playlists;
12
+ private readonly deviceAssignments;
9
13
  setPendingCode(code: string, record: PendingCodeRecord): Promise<void>;
10
14
  getPendingCode(code: string): Promise<PendingCodeRecord | undefined>;
11
15
  deletePendingCode(code: string): Promise<void>;
16
+ getDeviceRegistry(deviceId: string): Promise<DeviceRegistryRecord | undefined>;
17
+ setDeviceRegistry(deviceId: string, record: DeviceRegistryRecord): Promise<void>;
18
+ getDeviceRegistryByCode(code: string): Promise<{
19
+ deviceId: string;
20
+ record: DeviceRegistryRecord;
21
+ } | undefined>;
12
22
  setPairedDevice(deviceId: string, record: PairedDeviceRecord): Promise<void>;
13
23
  getPairedDevice(deviceId: string): Promise<PairedDeviceRecord | undefined>;
14
24
  deletePairedDevice(deviceId: string): Promise<void>;
15
25
  listPairedDevices(): Promise<PairedDeviceEntry[]>;
26
+ listPlaylists(): Promise<StoredPlaylist[]>;
27
+ getPlaylist(id: string): Promise<StoredPlaylist | undefined>;
28
+ setPlaylist(record: StoredPlaylist): Promise<void>;
29
+ isPlaylistNameTaken(name: string, excludeId?: string): Promise<boolean>;
30
+ getDeviceAssignments(deviceId: string): Promise<DevicePlaylistAssignment[]>;
31
+ setDeviceAssignments(deviceId: string, assignments: DevicePlaylistAssignment[]): Promise<void>;
16
32
  }
17
33
  //# sourceMappingURL=memory-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../src/store/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,qBAAa,WAAY,YAAW,eAAe;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAwC;IACrE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyC;IAEjE,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAIpE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC;IAIV,eAAe,CACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAIpC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;CAMxD"}
1
+ {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../src/store/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,qBAAa,WAAY,YAAW,eAAe;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAwC;IACrE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA2C;IAC1E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6B;IAC5D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyC;IACvE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqC;IAC/D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAiD;IAE7E,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAIpE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,iBAAiB,CACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;IAItC,iBAAiB,CACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;IASV,uBAAuB,CAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,oBAAoB,CAAA;KAAE,GAAG,SAAS,CAAC;IAQpE,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC;IAIV,eAAe,CACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAIpC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAOjD,aAAa,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAI1C,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAI5D,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUvE,oBAAoB,CACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAIhC,oBAAoB,CACxB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,wBAAwB,EAAE,GACtC,OAAO,CAAC,IAAI,CAAC;CASjB"}
@@ -4,7 +4,11 @@
4
4
  */
5
5
  export class MemoryStore {
6
6
  pendingCodes = new Map();
7
+ deviceRegistry = new Map();
8
+ codeToDeviceId = new Map();
7
9
  pairedDevices = new Map();
10
+ playlists = new Map();
11
+ deviceAssignments = new Map();
8
12
  async setPendingCode(code, record) {
9
13
  this.pendingCodes.set(code, record);
10
14
  }
@@ -14,6 +18,26 @@ export class MemoryStore {
14
18
  async deletePendingCode(code) {
15
19
  this.pendingCodes.delete(code);
16
20
  }
21
+ async getDeviceRegistry(deviceId) {
22
+ return this.deviceRegistry.get(deviceId);
23
+ }
24
+ async setDeviceRegistry(deviceId, record) {
25
+ const existing = this.deviceRegistry.get(deviceId);
26
+ if (existing?.permanentPairingCode) {
27
+ this.codeToDeviceId.delete(existing.permanentPairingCode);
28
+ }
29
+ this.deviceRegistry.set(deviceId, record);
30
+ this.codeToDeviceId.set(record.permanentPairingCode, deviceId);
31
+ }
32
+ async getDeviceRegistryByCode(code) {
33
+ const deviceId = this.codeToDeviceId.get(code);
34
+ if (!deviceId)
35
+ return undefined;
36
+ const record = this.deviceRegistry.get(deviceId);
37
+ if (!record)
38
+ return undefined;
39
+ return { deviceId, record };
40
+ }
17
41
  async setPairedDevice(deviceId, record) {
18
42
  this.pairedDevices.set(deviceId, record);
19
43
  }
@@ -29,4 +53,37 @@ export class MemoryStore {
29
53
  record
30
54
  }));
31
55
  }
56
+ async listPlaylists() {
57
+ return [...this.playlists.values()];
58
+ }
59
+ async getPlaylist(id) {
60
+ return this.playlists.get(id);
61
+ }
62
+ async setPlaylist(record) {
63
+ this.playlists.set(record.id, record);
64
+ }
65
+ async isPlaylistNameTaken(name, excludeId) {
66
+ const target = name.trim().toLowerCase();
67
+ for (const playlist of this.playlists.values()) {
68
+ if (playlist.retired)
69
+ continue;
70
+ if (excludeId && playlist.id === excludeId)
71
+ continue;
72
+ if (playlist.name.trim().toLowerCase() === target)
73
+ return true;
74
+ }
75
+ return false;
76
+ }
77
+ async getDeviceAssignments(deviceId) {
78
+ return [...(this.deviceAssignments.get(deviceId) ?? [])];
79
+ }
80
+ async setDeviceAssignments(deviceId, assignments) {
81
+ this.deviceAssignments.set(deviceId, assignments.map((a) => ({
82
+ ...a,
83
+ snapshot: {
84
+ ...a.snapshot,
85
+ items: a.snapshot.items.map((item) => ({ ...item }))
86
+ }
87
+ })));
88
+ }
32
89
  }
@@ -6,6 +6,14 @@ export interface PendingCodeRecord {
6
6
  deviceId: string;
7
7
  createdAt: number;
8
8
  }
9
+ /** Permanent pairing code bound to a device (serial) on first hello — never rotated. */
10
+ export interface DeviceRegistryRecord {
11
+ permanentPairingCode: string;
12
+ codeCreatedAt: number;
13
+ serialNumber?: string;
14
+ firstSeenAt?: number;
15
+ lastHelloAt?: number;
16
+ }
9
17
  export interface PairedDeviceRecord {
10
18
  pairingToken: string;
11
19
  pairedAt: string;
@@ -21,6 +29,41 @@ export interface PairedDeviceEntry {
21
29
  deviceId: string;
22
30
  record: PairedDeviceRecord;
23
31
  }
32
+ export interface PlaylistItemRecord {
33
+ url: string;
34
+ type?: string;
35
+ durationMs?: number;
36
+ }
37
+ export interface PlaylistSchedule {
38
+ startDate?: string;
39
+ endDate?: string;
40
+ daysOfWeek?: number[];
41
+ start?: string;
42
+ end?: string;
43
+ }
44
+ export interface StoredPlaylist {
45
+ id: string;
46
+ name: string;
47
+ schedule?: PlaylistSchedule;
48
+ items: PlaylistItemRecord[];
49
+ version: number;
50
+ updatedAt: string;
51
+ retired?: boolean;
52
+ retiredAt?: string;
53
+ }
54
+ export interface PublishedPlaylistSnapshot {
55
+ id: string;
56
+ name: string;
57
+ version: number;
58
+ schedule?: PlaylistSchedule;
59
+ items: PlaylistItemRecord[];
60
+ }
61
+ export interface DevicePlaylistAssignment {
62
+ playlistId: string;
63
+ publishedVersion: number;
64
+ publishedAt: string;
65
+ snapshot: PublishedPlaylistSnapshot;
66
+ }
24
67
  /**
25
68
  * Implement with Postgres, Redis, etc. Defaults to MemoryStore (Map).
26
69
  * All methods async so DB backends do not need sync adapters.
@@ -29,9 +72,21 @@ export interface TomorrowOSStore {
29
72
  setPendingCode(code: string, record: PendingCodeRecord): Promise<void>;
30
73
  getPendingCode(code: string): Promise<PendingCodeRecord | undefined>;
31
74
  deletePendingCode(code: string): Promise<void>;
75
+ getDeviceRegistry(deviceId: string): Promise<DeviceRegistryRecord | undefined>;
76
+ setDeviceRegistry(deviceId: string, record: DeviceRegistryRecord): Promise<void>;
77
+ getDeviceRegistryByCode(code: string): Promise<{
78
+ deviceId: string;
79
+ record: DeviceRegistryRecord;
80
+ } | undefined>;
32
81
  setPairedDevice(deviceId: string, record: PairedDeviceRecord): Promise<void>;
33
82
  getPairedDevice(deviceId: string): Promise<PairedDeviceRecord | undefined>;
34
83
  deletePairedDevice(deviceId: string): Promise<void>;
35
84
  listPairedDevices(): Promise<PairedDeviceEntry[]>;
85
+ listPlaylists(): Promise<StoredPlaylist[]>;
86
+ getPlaylist(id: string): Promise<StoredPlaylist | undefined>;
87
+ setPlaylist(record: StoredPlaylist): Promise<void>;
88
+ isPlaylistNameTaken(name: string, excludeId?: string): Promise<boolean>;
89
+ getDeviceAssignments(deviceId: string): Promise<DevicePlaylistAssignment[]>;
90
+ setDeviceAssignments(deviceId: string, assignments: DevicePlaylistAssignment[]): Promise<void>;
36
91
  }
37
92
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAC;IACrE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAC;IAC3E,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;CACnD"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { EventEmitter } from "events";
2
2
  import http from "http";
3
+ import { PlaylistCatalog, type BuiltDevicePolicy } from "./playlist-catalog.js";
3
4
  import type { TomorrowOSStore } from "./store/types.js";
4
5
  export interface TomorrowOSBrand {
5
6
  name?: string;
@@ -29,10 +30,13 @@ export interface ListenOptions {
29
30
  }
30
31
  export interface DeviceListItem {
31
32
  deviceId: string;
33
+ /** Permanent 6-character alphanumeric pairing code (same after unpair). */
34
+ pairingCode: string | null;
32
35
  connected: boolean;
33
36
  deviceName: string | null;
34
37
  platform: string | null;
35
38
  system: string | null;
39
+ serialNumber: string | null;
36
40
  pairedAt: string;
37
41
  lastBootAt: string | null;
38
42
  lastOnlineAt: string | null;
@@ -42,10 +46,17 @@ export interface DeviceListItem {
42
46
  screenOnlineLabel: string;
43
47
  /** Same as lastBootAt while connected; used for uptime display. */
44
48
  screenOnlineSince: string | null;
49
+ publishedPlaylists: Array<{
50
+ playlistId: string;
51
+ name: string;
52
+ version: number;
53
+ publishedAt: string;
54
+ }>;
45
55
  }
46
56
  export declare class TomorrowOS extends EventEmitter {
47
57
  readonly brand: TomorrowOSBrand;
48
58
  private readonly store;
59
+ readonly playlists: PlaylistCatalog;
49
60
  private readonly devices;
50
61
  private readonly pendingDeviceMeta;
51
62
  private httpServer;
@@ -53,7 +64,13 @@ export declare class TomorrowOS extends EventEmitter {
53
64
  private staticRoot;
54
65
  private staticIndexFile;
55
66
  constructor(options: TomorrowOSOptions);
56
- /** Verify a 6-digit pairing code (same as POST /pairing/verify). */
67
+ /** Push all device assignments using latest playlist definitions (e.g. after reboot). */
68
+ pushLatestPolicyToDevice(deviceId: string): Promise<{
69
+ pushed: boolean;
70
+ policy?: BuiltDevicePolicy["policy"];
71
+ }>;
72
+ private sendDeviceCommand;
73
+ /** Verify a 6-character alphanumeric pairing code (POST /pairing/verify). */
57
74
  pairingVerify(code: string): Promise<{
58
75
  deviceId: string;
59
76
  }>;
@@ -75,6 +92,7 @@ export declare class TomorrowOS extends EventEmitter {
75
92
  listDevices(): Promise<DeviceListItem[]>;
76
93
  private isDeviceConnected;
77
94
  private captureHelloMeta;
95
+ private getOrCreatePermanentPairingCode;
78
96
  private mergePairedRecord;
79
97
  private touchPairedOnline;
80
98
  private touchPairedOffline;
@@ -1 +1 @@
1
- {"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,IAAI,MAAM,MAAM,CAAC;AAUxB,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EAIV,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAG1B,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,eAAe,CAAC;IACvB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAuBD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,kBAAkB,EAAE,KAAK,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;CACJ;AAsFD,qBAAa,UAAW,SAAQ,YAAY;IAC1C,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAsC;IACxE,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,GAAG,CAAgC;IAC3C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,eAAe,CAAgB;gBAE3B,OAAO,EAAE,iBAAiB;IAOtC,yFAAyF;IACnF,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QACxD,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;KACtC,CAAC;YAkBY,iBAAiB;IAY/B,6EAA6E;IACvE,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAyDhE,6EAA6E;IACvE,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IA4BvF,OAAO;uBACU,MAAM;sBAvFgC,MAAM;;2BAwFxC,MAAM;sBA9BgC,MAAM;sBAAY,OAAO;;MA+BlF;IAEF,kFAAkF;IAC5E,WAAW,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAkD9C,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,gBAAgB;YAaV,+BAA+B;YAiC/B,iBAAiB;YASjB,iBAAiB;YA6BjB,kBAAkB;IAUhC,uFAAuF;IACvF,OAAO,CAAC,kBAAkB;YAmBZ,gBAAgB;YAMhB,uBAAuB;IAoCrC,MAAM,CAAC,QAAQ,EAAE,MAAM;oBAGD,CAAC,oBACT,MAAM,WACN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;;IAU5E,OAAO,CAAC,mBAAmB;IA6D3B,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC,MAAM;YAkD7B,iBAAiB;YAqCjB,cAAc;YAmCd,UAAU;YAoLV,gBAAgB;IA2E9B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,gBAAgB;CAuGzB"}