From e313826d85eef70e8918a405c99d154d4a0a589b Mon Sep 17 00:00:00 2001 From: ScreenTinker Date: Thu, 6 Aug 2026 16:32:23 -0500 Subject: [PATCH] chore(release): v1.9.30 --- VERSION | 2 +- docs/openapi.yaml | 2 +- server/package-lock.json | 4 ++-- server/package.json | 2 +- server/test/player-sw-scope.test.js | 7 ++++++- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/VERSION b/VERSION index 5620bb8..d682215 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.9.29 +1.9.30 diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 097da3e..dcc68ca 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: ScreenTinker Public API - version: 1.9.29 + version: 1.9.30 description: | Public, token-scoped REST API for ScreenTinker digital signage. diff --git a/server/package-lock.json b/server/package-lock.json index 2c871d1..a6ffbd7 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "screentinker", - "version": "1.9.29", + "version": "1.9.30", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "screentinker", - "version": "1.9.29", + "version": "1.9.30", "dependencies": { "@azure/msal-node": "^5.2.1", "archiver": "^7.0.1", diff --git a/server/package.json b/server/package.json index 821d554..9abc9db 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "screentinker", - "version": "1.9.29", + "version": "1.9.30", "description": "ScreenTinker - Digital Signage Management Server", "main": "server.js", "scripts": { diff --git a/server/test/player-sw-scope.test.js b/server/test/player-sw-scope.test.js index b4c7c46..f0af2d8 100644 --- a/server/test/player-sw-scope.test.js +++ b/server/test/player-sw-scope.test.js @@ -66,7 +66,12 @@ test('the worker prunes to the set the player declares', () => { // a panel with a 1GB widget quota, a few replaced videos is the whole budget. const sw = fs.readFileSync(path.join(__dirname, '..', 'player', 'sw.js'), 'utf8'); assert.match(sw, /function pruneToPlaylist/); - assert.match(sw, /if \(data\.prune\)/, 'the prune must be driven by the player declaring a complete set'); + // The guard is `data.prune && data.urls.length > 0`, not a bare `data.prune`: an EMPTY list is + // never a prune instruction. `assignments: []` is what the server sends for a device between + // playlists and from the catch when a snapshot fails to parse, and honouring it as "keep nothing" + // wiped a panel's whole offline library (fixed in 1.9.30). + assert.match(sw, /if \(data\.prune && data\.urls\.length > 0\)/, + 'the prune must require a NON-EMPTY declared set'); const html = fs.readFileSync(path.join(__dirname, '..', 'player', 'index.html'), 'utf8'); assert.match(html, /prune:\s*true/);