@tomorrowos/sdk 1.1.6 → 1.1.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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/screen-references.d.ts +37 -0
- package/dist/screen-references.d.ts.map +1 -0
- package/dist/screen-references.js +228 -0
- package/dist/tomorrowos.d.ts +1 -0
- package/dist/tomorrowos.d.ts.map +1 -1
- package/dist/tomorrowos.js +17 -5
- package/package.json +1 -1
- package/templates/cms-starter/package-lock.json +3 -3
- package/templates/cms-starter/package.json +2 -2
- package/templates/cms-starter-v0/package-lock.json +3 -3
- package/templates/cms-starter-v0/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export { getSdkPackageVersion } from "./sdk-version.js";
|
|
|
6
6
|
export type { DeviceOnOffTimer, DevicePlaylistAssignment, DeviceRegistryRecord, PairedDeviceRecord, PendingCodeEntry, PendingCodeRecord, DeviceRegistryEntry, PlaylistItemRecord, PlaylistItemSchedule, PlaylistItemTagRule, PlaylistSchedule, PublishedPlaylistSnapshot, StoredPlaylist, TomorrowOSDataSnapshot, TomorrowOSMigratableStore, TomorrowOSStore } from "./store/types.js";
|
|
7
7
|
export { PlaylistCatalog } from "./playlist-catalog.js";
|
|
8
8
|
export { filterPolicyForScreen, itemMatchesScreen, itemScheduleActive, parseItemSchedule, parseTagRule, ScreenTagStore } from "./screen-tags.js";
|
|
9
|
+
export { parseScreenReferences, policyWithScreenReferences, ScreenReferenceStore } from "./screen-references.js";
|
|
10
|
+
export type { ScreenReferenceHost, ScreenReferences } from "./screen-references.js";
|
|
9
11
|
export type { ItemSchedule, ScreenTagHost, ScreenTagHttpDeps, TagRule, TagRuleOp } from "./screen-tags.js";
|
|
10
12
|
export type { BuiltDevicePolicy, SavePlaylistInput } from "./playlist-catalog.js";
|
|
11
13
|
export { DEFAULT_PAIRING_CODE_SECRET, generateDeterministicPairingCode, generateRandomPairingCode, isValidPairingCodeFormat, normalizePairingCode, PAIRING_CODE_ALPHABET, PAIRING_CODE_LENGTH, resolvePairingCodeSecret } from "./pairing-code.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,YAAY,EACV,gBAAgB,EAChB,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,gBAAgB,EAChB,yBAAyB,EACzB,cAAc,EACd,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACf,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,OAAO,EACP,SAAS,EACV,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EACL,2BAA2B,EAC3B,gCAAgC,EAChC,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,wBAAwB,EACzB,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"}
|
|
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,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,YAAY,EACV,gBAAgB,EAChB,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,gBAAgB,EAChB,yBAAyB,EACzB,cAAc,EACd,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACpF,YAAY,EACV,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,OAAO,EACP,SAAS,EACV,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EACL,2BAA2B,EAC3B,gCAAgC,EAChC,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,wBAAwB,EACzB,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
|
@@ -3,6 +3,7 @@ export { buildServerStatus } from "./server-status.js";
|
|
|
3
3
|
export { getSdkPackageVersion } from "./sdk-version.js";
|
|
4
4
|
export { PlaylistCatalog } from "./playlist-catalog.js";
|
|
5
5
|
export { filterPolicyForScreen, itemMatchesScreen, itemScheduleActive, parseItemSchedule, parseTagRule, ScreenTagStore } from "./screen-tags.js";
|
|
6
|
+
export { parseScreenReferences, policyWithScreenReferences, ScreenReferenceStore } from "./screen-references.js";
|
|
6
7
|
export { DEFAULT_PAIRING_CODE_SECRET, generateDeterministicPairingCode, generateRandomPairingCode, isValidPairingCodeFormat, normalizePairingCode, PAIRING_CODE_ALPHABET, PAIRING_CODE_LENGTH, resolvePairingCodeSecret } from "./pairing-code.js";
|
|
7
8
|
export { MemoryStore } from "./store/memory-store.js";
|
|
8
9
|
export { PostgresStore } from "./store/postgres-store.js";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-screen key/value references.
|
|
3
|
+
*
|
|
4
|
+
* Stored in screen-references.json beside the database. Each setPolicy for a
|
|
5
|
+
* screen includes that screen's references. The shared playlist is unchanged.
|
|
6
|
+
* The default CMS panel does not edit these; a product UI calls the HTTP routes.
|
|
7
|
+
*/
|
|
8
|
+
import type { IncomingMessage, ServerResponse } from "http";
|
|
9
|
+
export type ScreenReferences = Record<string, string>;
|
|
10
|
+
export interface ScreenReferenceHost {
|
|
11
|
+
pushLatestPolicyToDevice(deviceId: string): Promise<{
|
|
12
|
+
pushed: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
export declare function parseScreenReferences(raw: unknown): ScreenReferences;
|
|
16
|
+
/** Copy a policy and add this screen's references. Does not mutate the input. */
|
|
17
|
+
export declare function policyWithScreenReferences(policy: unknown, references: ScreenReferences): unknown;
|
|
18
|
+
export declare class ScreenReferenceStore {
|
|
19
|
+
private readonly filePath;
|
|
20
|
+
constructor(filePath: string);
|
|
21
|
+
get(deviceId: string): Promise<ScreenReferences>;
|
|
22
|
+
all(): Promise<Record<string, ScreenReferences>>;
|
|
23
|
+
replace(deviceId: string, references: ScreenReferences): Promise<ScreenReferences>;
|
|
24
|
+
removeKey(deviceId: string, key: string): Promise<ScreenReferences>;
|
|
25
|
+
deleteDevice(deviceId: string): Promise<void>;
|
|
26
|
+
private read;
|
|
27
|
+
private write;
|
|
28
|
+
}
|
|
29
|
+
export interface ScreenReferenceHttpDeps {
|
|
30
|
+
host: ScreenReferenceHost;
|
|
31
|
+
store: {
|
|
32
|
+
getPairedDevice(deviceId: string): Promise<unknown>;
|
|
33
|
+
};
|
|
34
|
+
references: ScreenReferenceStore;
|
|
35
|
+
}
|
|
36
|
+
export declare function handleScreenReferenceHttp(req: IncomingMessage, res: ServerResponse, deps: ScreenReferenceHttpDeps): Promise<boolean>;
|
|
37
|
+
//# sourceMappingURL=screen-references.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screen-references.d.ts","sourceRoot":"","sources":["../src/screen-references.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAO5D,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAMtD,MAAM,WAAW,mBAAmB;IAClC,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC1E;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,CA0BpE;AAED,iFAAiF;AACjF,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,OAAO,EACf,UAAU,EAAE,gBAAgB,GAC3B,OAAO,CAMT;AAED,qBAAa,oBAAoB;IACnB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM;IAEvC,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAKhD,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAShD,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAQlF,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAUnE,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAOrC,IAAI;YAuBJ,KAAK;CAMpB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE;QAAE,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC;IAC/D,UAAU,EAAE,oBAAoB,CAAC;CAClC;AAyDD,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,uBAAuB,GAC5B,OAAO,CAAC,OAAO,CAAC,CA4DlB"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-screen key/value references.
|
|
3
|
+
*
|
|
4
|
+
* Stored in screen-references.json beside the database. Each setPolicy for a
|
|
5
|
+
* screen includes that screen's references. The shared playlist is unchanged.
|
|
6
|
+
* The default CMS panel does not edit these; a product UI calls the HTTP routes.
|
|
7
|
+
*/
|
|
8
|
+
import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
9
|
+
import { dirname } from "path";
|
|
10
|
+
const KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
|
|
11
|
+
const MAX_KEYS = 32;
|
|
12
|
+
const MAX_VALUE_LENGTH = 500;
|
|
13
|
+
export function parseScreenReferences(raw) {
|
|
14
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
15
|
+
throw new Error("references must be an object of string values");
|
|
16
|
+
}
|
|
17
|
+
const entries = Object.entries(raw);
|
|
18
|
+
if (entries.length > MAX_KEYS) {
|
|
19
|
+
throw new Error(`At most ${MAX_KEYS} references`);
|
|
20
|
+
}
|
|
21
|
+
const out = {};
|
|
22
|
+
for (const [key, value] of entries) {
|
|
23
|
+
const name = key.trim();
|
|
24
|
+
if (!KEY_PATTERN.test(name)) {
|
|
25
|
+
throw new Error(`Invalid reference key “${key}”. Use a letter, then letters, numbers, or underscores.`);
|
|
26
|
+
}
|
|
27
|
+
if (typeof value !== "string") {
|
|
28
|
+
throw new Error(`Reference “${name}” must be a string`);
|
|
29
|
+
}
|
|
30
|
+
const text = value.trim();
|
|
31
|
+
if (!text || text.length > MAX_VALUE_LENGTH) {
|
|
32
|
+
throw new Error(`Reference “${name}” must be 1–${MAX_VALUE_LENGTH} characters`);
|
|
33
|
+
}
|
|
34
|
+
out[name] = text;
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
/** Copy a policy and add this screen's references. Does not mutate the input. */
|
|
39
|
+
export function policyWithScreenReferences(policy, references) {
|
|
40
|
+
if (!policy || typeof policy !== "object" || Array.isArray(policy))
|
|
41
|
+
return policy;
|
|
42
|
+
return {
|
|
43
|
+
...policy,
|
|
44
|
+
references: { ...references }
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export class ScreenReferenceStore {
|
|
48
|
+
filePath;
|
|
49
|
+
constructor(filePath) {
|
|
50
|
+
this.filePath = filePath;
|
|
51
|
+
}
|
|
52
|
+
async get(deviceId) {
|
|
53
|
+
const file = await this.read();
|
|
54
|
+
return { ...(file.devices[deviceId] ?? {}) };
|
|
55
|
+
}
|
|
56
|
+
async all() {
|
|
57
|
+
const file = await this.read();
|
|
58
|
+
const out = {};
|
|
59
|
+
for (const [deviceId, references] of Object.entries(file.devices)) {
|
|
60
|
+
out[deviceId] = { ...references };
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
async replace(deviceId, references) {
|
|
65
|
+
const file = await this.read();
|
|
66
|
+
if (Object.keys(references).length === 0)
|
|
67
|
+
delete file.devices[deviceId];
|
|
68
|
+
else
|
|
69
|
+
file.devices[deviceId] = references;
|
|
70
|
+
await this.write(file);
|
|
71
|
+
return { ...references };
|
|
72
|
+
}
|
|
73
|
+
async removeKey(deviceId, key) {
|
|
74
|
+
const file = await this.read();
|
|
75
|
+
const current = { ...(file.devices[deviceId] ?? {}) };
|
|
76
|
+
delete current[key];
|
|
77
|
+
if (Object.keys(current).length === 0)
|
|
78
|
+
delete file.devices[deviceId];
|
|
79
|
+
else
|
|
80
|
+
file.devices[deviceId] = current;
|
|
81
|
+
await this.write(file);
|
|
82
|
+
return current;
|
|
83
|
+
}
|
|
84
|
+
async deleteDevice(deviceId) {
|
|
85
|
+
const file = await this.read();
|
|
86
|
+
if (!file.devices[deviceId])
|
|
87
|
+
return;
|
|
88
|
+
delete file.devices[deviceId];
|
|
89
|
+
await this.write(file);
|
|
90
|
+
}
|
|
91
|
+
async read() {
|
|
92
|
+
try {
|
|
93
|
+
const raw = JSON.parse(await readFile(this.filePath, "utf8"));
|
|
94
|
+
const devices = {};
|
|
95
|
+
if (raw?.devices && typeof raw.devices === "object") {
|
|
96
|
+
for (const [deviceId, references] of Object.entries(raw.devices)) {
|
|
97
|
+
try {
|
|
98
|
+
devices[deviceId] = parseScreenReferences(references);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
devices[deviceId] = {};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return { devices };
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
const code = err.code;
|
|
109
|
+
if (code === "ENOENT")
|
|
110
|
+
return { devices: {} };
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async write(file) {
|
|
115
|
+
await mkdir(dirname(this.filePath), { recursive: true });
|
|
116
|
+
const tmp = `${this.filePath}.${process.pid}.tmp`;
|
|
117
|
+
await writeFile(tmp, JSON.stringify(file, null, 2), "utf8");
|
|
118
|
+
await rename(tmp, this.filePath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function sendJson(res, status, body) {
|
|
122
|
+
res.writeHead(status, {
|
|
123
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
124
|
+
"Cache-Control": "no-store"
|
|
125
|
+
});
|
|
126
|
+
res.end(JSON.stringify(body));
|
|
127
|
+
}
|
|
128
|
+
function pathnameOf(req) {
|
|
129
|
+
const url = new URL(req.url || "/", "http://localhost");
|
|
130
|
+
let pathname = url.pathname || "/";
|
|
131
|
+
if (pathname.length > 1 && pathname.endsWith("/"))
|
|
132
|
+
pathname = pathname.slice(0, -1);
|
|
133
|
+
return pathname;
|
|
134
|
+
}
|
|
135
|
+
async function readJson(req) {
|
|
136
|
+
const chunks = [];
|
|
137
|
+
let size = 0;
|
|
138
|
+
for await (const chunk of req) {
|
|
139
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
140
|
+
size += buf.length;
|
|
141
|
+
if (size > 1_000_000)
|
|
142
|
+
throw new Error("Body too large");
|
|
143
|
+
chunks.push(buf);
|
|
144
|
+
}
|
|
145
|
+
if (chunks.length === 0)
|
|
146
|
+
return {};
|
|
147
|
+
const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
148
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
149
|
+
throw new Error("JSON object required");
|
|
150
|
+
}
|
|
151
|
+
return parsed;
|
|
152
|
+
}
|
|
153
|
+
async function requirePaired(deps, deviceId, res) {
|
|
154
|
+
const paired = await deps.store.getPairedDevice(deviceId);
|
|
155
|
+
if (paired)
|
|
156
|
+
return true;
|
|
157
|
+
sendJson(res, 404, { status: "failed", error: "Device not found" });
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
async function pushReferences(deps, deviceId) {
|
|
161
|
+
try {
|
|
162
|
+
const result = await deps.host.pushLatestPolicyToDevice(deviceId);
|
|
163
|
+
return result.pushed;
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
export async function handleScreenReferenceHttp(req, res, deps) {
|
|
170
|
+
const method = String(req.method || "GET").toUpperCase();
|
|
171
|
+
const pathname = pathnameOf(req);
|
|
172
|
+
if (method === "GET" && pathname === "/screen-references") {
|
|
173
|
+
sendJson(res, 200, {
|
|
174
|
+
status: "success",
|
|
175
|
+
referencesByDevice: await deps.references.all()
|
|
176
|
+
});
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
const collection = /^\/device\/([^/]+)\/references$/.exec(pathname);
|
|
180
|
+
if (collection && method === "GET") {
|
|
181
|
+
const deviceId = decodeURIComponent(collection[1]);
|
|
182
|
+
if (!(await requirePaired(deps, deviceId, res)))
|
|
183
|
+
return true;
|
|
184
|
+
sendJson(res, 200, {
|
|
185
|
+
status: "success",
|
|
186
|
+
deviceId,
|
|
187
|
+
references: await deps.references.get(deviceId)
|
|
188
|
+
});
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
if (collection && method === "PUT") {
|
|
192
|
+
const deviceId = decodeURIComponent(collection[1]);
|
|
193
|
+
if (!(await requirePaired(deps, deviceId, res)))
|
|
194
|
+
return true;
|
|
195
|
+
let references;
|
|
196
|
+
try {
|
|
197
|
+
const body = await readJson(req);
|
|
198
|
+
references = parseScreenReferences(body.references);
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
sendJson(res, 400, {
|
|
202
|
+
status: "failed",
|
|
203
|
+
error: err instanceof Error ? err.message : "Invalid references"
|
|
204
|
+
});
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
const saved = await deps.references.replace(deviceId, references);
|
|
208
|
+
const pushed = await pushReferences(deps, deviceId);
|
|
209
|
+
sendJson(res, 200, { status: "success", deviceId, references: saved, pushed });
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
const oneKey = /^\/device\/([^/]+)\/references\/([^/]+)$/.exec(pathname);
|
|
213
|
+
if (oneKey && method === "DELETE") {
|
|
214
|
+
const deviceId = decodeURIComponent(oneKey[1]);
|
|
215
|
+
const key = decodeURIComponent(oneKey[2]);
|
|
216
|
+
if (!KEY_PATTERN.test(key)) {
|
|
217
|
+
sendJson(res, 400, { status: "failed", error: "Invalid reference key" });
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
if (!(await requirePaired(deps, deviceId, res)))
|
|
221
|
+
return true;
|
|
222
|
+
const references = await deps.references.removeKey(deviceId, key);
|
|
223
|
+
const pushed = await pushReferences(deps, deviceId);
|
|
224
|
+
sendJson(res, 200, { status: "success", deviceId, references, pushed });
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
return false;
|
|
228
|
+
}
|
package/dist/tomorrowos.d.ts
CHANGED
|
@@ -116,6 +116,7 @@ export declare class TomorrowOS extends EventEmitter {
|
|
|
116
116
|
/** Port passed to listen(); used for suggested LAN CMS URL. */
|
|
117
117
|
private listenPort;
|
|
118
118
|
private readonly screenTags;
|
|
119
|
+
private readonly screenReferences;
|
|
119
120
|
private scheduleRefreshStarted;
|
|
120
121
|
constructor(options: TomorrowOSOptions);
|
|
121
122
|
/** Push all device assignments using latest playlist definitions (e.g. after reboot). */
|
package/dist/tomorrowos.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;AAaxB,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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"}
|
package/dist/tomorrowos.js
CHANGED
|
@@ -7,6 +7,7 @@ import { fileURLToPath } from "url";
|
|
|
7
7
|
import { WebSocket, WebSocketServer } from "ws";
|
|
8
8
|
import { generateDeterministicPairingCode, generateRandomPairingCode, isValidPairingCodeFormat, normalizePairingCode, resolvePairingCodeSecret } from "./pairing-code.js";
|
|
9
9
|
import { PlaylistCatalog } from "./playlist-catalog.js";
|
|
10
|
+
import { handleScreenReferenceHttp, policyWithScreenReferences, ScreenReferenceStore } from "./screen-references.js";
|
|
10
11
|
import { filterPolicyForScreen, handleScreenTagHttp, ScreenTagStore, startItemScheduleRefresher } from "./screen-tags.js";
|
|
11
12
|
import { MemoryStore } from "./store/memory-store.js";
|
|
12
13
|
import { resolveBrandLogoPath, syncProjectAssetsToStaticRoot } from "./brand-assets.js";
|
|
@@ -237,12 +238,12 @@ function sendJson(res, status, body) {
|
|
|
237
238
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
238
239
|
res.end(JSON.stringify(body));
|
|
239
240
|
}
|
|
240
|
-
function
|
|
241
|
+
function screenDataFilePath(store, fileName) {
|
|
241
242
|
const databasePath = store.databasePath;
|
|
242
243
|
if (typeof databasePath === "string" && databasePath && databasePath !== ":memory:") {
|
|
243
|
-
return path.join(path.dirname(databasePath),
|
|
244
|
+
return path.join(path.dirname(databasePath), fileName);
|
|
244
245
|
}
|
|
245
|
-
return path.join(process.cwd(), "data",
|
|
246
|
+
return path.join(process.cwd(), "data", fileName);
|
|
246
247
|
}
|
|
247
248
|
function parseDevicePath(pathname) {
|
|
248
249
|
const base = /^\/device\/([^/]+)\/(.+)$/.exec(pathname);
|
|
@@ -271,13 +272,15 @@ export class TomorrowOS extends EventEmitter {
|
|
|
271
272
|
/** Port passed to listen(); used for suggested LAN CMS URL. */
|
|
272
273
|
listenPort = null;
|
|
273
274
|
screenTags;
|
|
275
|
+
screenReferences;
|
|
274
276
|
scheduleRefreshStarted = false;
|
|
275
277
|
constructor(options) {
|
|
276
278
|
super();
|
|
277
279
|
this.brand = options.brand;
|
|
278
280
|
this.store = options.store ?? new MemoryStore();
|
|
279
281
|
this.playlists = new PlaylistCatalog(this.store);
|
|
280
|
-
this.screenTags = new ScreenTagStore(
|
|
282
|
+
this.screenTags = new ScreenTagStore(screenDataFilePath(this.store, "screen-tags.json"));
|
|
283
|
+
this.screenReferences = new ScreenReferenceStore(screenDataFilePath(this.store, "screen-references.json"));
|
|
281
284
|
}
|
|
282
285
|
/** Push all device assignments using latest playlist definitions (e.g. after reboot). */
|
|
283
286
|
async pushLatestPolicyToDevice(deviceId) {
|
|
@@ -321,9 +324,10 @@ export class TomorrowOS extends EventEmitter {
|
|
|
321
324
|
return params;
|
|
322
325
|
}
|
|
323
326
|
const tags = await this.screenTags.get(deviceId);
|
|
327
|
+
const references = await this.screenReferences.get(deviceId);
|
|
324
328
|
return {
|
|
325
329
|
...params,
|
|
326
|
-
policy: filterPolicyForScreen(params.policy, tags)
|
|
330
|
+
policy: policyWithScreenReferences(filterPolicyForScreen(params.policy, tags), references)
|
|
327
331
|
};
|
|
328
332
|
}
|
|
329
333
|
/** Verify a 6-character alphanumeric pairing code (POST /pairing/verify). */
|
|
@@ -414,6 +418,7 @@ export class TomorrowOS extends EventEmitter {
|
|
|
414
418
|
}
|
|
415
419
|
await this.store.deletePairedDevice(id);
|
|
416
420
|
await this.screenTags.deleteDevice(id);
|
|
421
|
+
await this.screenReferences.deleteDevice(id);
|
|
417
422
|
this.pendingDeviceMeta.delete(id);
|
|
418
423
|
if (screenshotAssetId) {
|
|
419
424
|
await this.deleteUploadedAssetIfUnreferenced(screenshotAssetId);
|
|
@@ -1724,6 +1729,13 @@ export class TomorrowOS extends EventEmitter {
|
|
|
1724
1729
|
});
|
|
1725
1730
|
if (screenTagsHandled)
|
|
1726
1731
|
return;
|
|
1732
|
+
const screenReferencesHandled = await handleScreenReferenceHttp(req, res, {
|
|
1733
|
+
host: this,
|
|
1734
|
+
store: this.store,
|
|
1735
|
+
references: this.screenReferences
|
|
1736
|
+
});
|
|
1737
|
+
if (screenReferencesHandled)
|
|
1738
|
+
return;
|
|
1727
1739
|
/** Same-origin brand for TomorrowOS players (e.g. GET /brand.json + applyBrand). */
|
|
1728
1740
|
if (method === "GET" && pathname === "/brand.json") {
|
|
1729
1741
|
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-cms",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.7",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "my-cms",
|
|
9
|
-
"version": "1.1.
|
|
9
|
+
"version": "1.1.7",
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@tomorrowos/sdk": "^1.1.
|
|
11
|
+
"@tomorrowos/sdk": "^1.1.7",
|
|
12
12
|
"dotenv": "^17.2.3",
|
|
13
13
|
"tsx": "^4.19.0"
|
|
14
14
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-cms",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.7",
|
|
4
4
|
"description": "CMS server on @tomorrowos/sdk. Add your UI (React, static files, etc.) alongside this server.",
|
|
5
5
|
"private": true,
|
|
6
6
|
"type": "module",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"start": "tsx server.ts"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@tomorrowos/sdk": "^1.1.
|
|
15
|
+
"@tomorrowos/sdk": "^1.1.7",
|
|
16
16
|
"dotenv": "^17.2.3",
|
|
17
17
|
"tsx": "^4.19.0"
|
|
18
18
|
},
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-cms",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.7",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "my-cms",
|
|
9
|
-
"version": "1.1.
|
|
9
|
+
"version": "1.1.7",
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@tomorrowos/sdk": "^1.1.
|
|
11
|
+
"@tomorrowos/sdk": "^1.1.7",
|
|
12
12
|
"dotenv": "^17.2.3",
|
|
13
13
|
"tsx": "^4.19.0"
|
|
14
14
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-cms",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.7",
|
|
4
4
|
"description": "TomorrowOS CMS for Vercel / v0 Publish (Fluid Function + WebSockets + optional Next Preview).",
|
|
5
5
|
"private": true,
|
|
6
6
|
"type": "module",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"build": "node -e \"process.exit(0)\""
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@tomorrowos/sdk": "^1.1.
|
|
19
|
+
"@tomorrowos/sdk": "^1.1.7",
|
|
20
20
|
"dotenv": "^17.2.3",
|
|
21
21
|
"tsx": "^4.19.0"
|
|
22
22
|
},
|