@glivion/square-screen-js-sdk 0.1.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.
Files changed (44) hide show
  1. package/.github/workflows/build-js-sdk.yml +70 -0
  2. package/README.md +463 -0
  3. package/eslint.config.js +3 -0
  4. package/examples/react-app/README.md +73 -0
  5. package/examples/react-app/eslint.config.js +22 -0
  6. package/examples/react-app/index.html +13 -0
  7. package/examples/react-app/package-lock.json +2239 -0
  8. package/examples/react-app/package.json +31 -0
  9. package/examples/react-app/public/favicon.svg +1 -0
  10. package/examples/react-app/public/icons.svg +24 -0
  11. package/examples/react-app/src/App.css +184 -0
  12. package/examples/react-app/src/App.tsx +157 -0
  13. package/examples/react-app/src/EmergencyTicker.tsx +25 -0
  14. package/examples/react-app/src/HeadlessExample.tsx +66 -0
  15. package/examples/react-app/src/RendererExample.tsx +70 -0
  16. package/examples/react-app/src/assets/hero.png +0 -0
  17. package/examples/react-app/src/assets/react.svg +1 -0
  18. package/examples/react-app/src/assets/vite.svg +1 -0
  19. package/examples/react-app/src/index.css +183 -0
  20. package/examples/react-app/src/main.tsx +10 -0
  21. package/examples/react-app/src/mockNetworkDataSource.ts +116 -0
  22. package/examples/react-app/src/usePlayer.ts +71 -0
  23. package/examples/react-app/tsconfig.app.json +25 -0
  24. package/examples/react-app/tsconfig.json +7 -0
  25. package/examples/react-app/tsconfig.node.json +24 -0
  26. package/examples/react-app/vite.config.ts +7 -0
  27. package/examples/react-app/yarn.lock +1089 -0
  28. package/package.json +49 -0
  29. package/src/__tests__/cache/SquareScreenCache.test.ts +375 -0
  30. package/src/__tests__/network/NetworkClient.test.ts +217 -0
  31. package/src/__tests__/network/mappers.test.ts +163 -0
  32. package/src/__tests__/player/SquareScreenPlayer.test.ts +840 -0
  33. package/src/cache/SquareScreenCache.ts +154 -0
  34. package/src/constants.ts +9 -0
  35. package/src/core/types.ts +251 -0
  36. package/src/env.d.ts +4 -0
  37. package/src/index.ts +34 -0
  38. package/src/network/NetworkClient.ts +234 -0
  39. package/src/network/apiTypes.ts +89 -0
  40. package/src/network/mappers.ts +106 -0
  41. package/src/player/SquareScreenPlayer.ts +414 -0
  42. package/src/renderer/SquareScreenRenderer.ts +282 -0
  43. package/tsconfig.json +12 -0
  44. package/tsdown.config.ts +23 -0
@@ -0,0 +1,163 @@
1
+ import { describe, expect, it, vi, afterEach } from "vitest";
2
+ import {
3
+ mapEmergencyResponse,
4
+ mapHeartbeatAck,
5
+ mapHeartbeatPayload,
6
+ mapPlaybackStrategy,
7
+ mapPlaylistItem,
8
+ mapPlaylistResponse,
9
+ } from "../../network/mappers";
10
+ import type {
11
+ ApiEmergencyResponse,
12
+ ApiPlaylistResponse,
13
+ ApiPlaylistItem,
14
+ } from "../../network/apiTypes";
15
+
16
+ afterEach(() => {
17
+ vi.restoreAllMocks();
18
+ });
19
+
20
+ describe("mappers", () => {
21
+ it("maps playlist item shape from API to core model", () => {
22
+ const raw: ApiPlaylistItem = {
23
+ uuid: "item-1",
24
+ name: "Hero",
25
+ type: "image",
26
+ url: "https://cdn.example.com/hero.jpg",
27
+ duration_seconds: 12,
28
+ };
29
+
30
+ expect(mapPlaylistItem(raw)).toEqual({
31
+ uuid: "item-1",
32
+ name: "Hero",
33
+ type: "image",
34
+ url: "https://cdn.example.com/hero.jpg",
35
+ duration: 12,
36
+ });
37
+ });
38
+
39
+ it("returns undefined strategy when API sends empty array", () => {
40
+ expect(mapPlaybackStrategy([])).toBeUndefined();
41
+ });
42
+
43
+ it("maps strategy and defaults preloadCount to 0", () => {
44
+ expect(
45
+ mapPlaybackStrategy({
46
+ loop: true,
47
+ shuffle: false,
48
+ }),
49
+ ).toEqual({
50
+ loop: true,
51
+ shuffle: false,
52
+ preloadCount: 0,
53
+ });
54
+ });
55
+
56
+ it("maps a full playlist response and stamps cachedAt", () => {
57
+ vi.spyOn(Date, "now").mockReturnValue(1_717_171_717_171);
58
+ const raw: ApiPlaylistResponse = {
59
+ playlist: { uuid: "playlist-1", name: "Morning" },
60
+ items: [
61
+ {
62
+ uuid: "item-1",
63
+ name: "Hero",
64
+ type: "image",
65
+ url: "https://cdn.example.com/hero.jpg",
66
+ duration_seconds: 12,
67
+ },
68
+ ],
69
+ strategy: {
70
+ loop: true,
71
+ shuffle: true,
72
+ preload_count: 3,
73
+ },
74
+ schedule: {
75
+ uuid: "schedule-1",
76
+ name: "weekday",
77
+ priority: 5,
78
+ },
79
+ };
80
+
81
+ expect(mapPlaylistResponse(raw)).toEqual({
82
+ uuid: "playlist-1",
83
+ items: [
84
+ {
85
+ uuid: "item-1",
86
+ name: "Hero",
87
+ type: "image",
88
+ url: "https://cdn.example.com/hero.jpg",
89
+ duration: 12,
90
+ },
91
+ ],
92
+ strategy: {
93
+ loop: true,
94
+ shuffle: true,
95
+ preloadCount: 3,
96
+ },
97
+ schedule: {
98
+ uuid: "schedule-1",
99
+ name: "weekday",
100
+ priority: 5,
101
+ },
102
+ playlistMeta: { uuid: "playlist-1", name: "Morning" },
103
+ cachedAt: 1_717_171_717_171,
104
+ });
105
+ });
106
+
107
+ it("maps emergency response to null when inactive", () => {
108
+ const raw: ApiEmergencyResponse = { emergency: null };
109
+ expect(mapEmergencyResponse(raw)).toBeNull();
110
+ });
111
+
112
+ it("maps emergency response to core alert when active", () => {
113
+ const raw: ApiEmergencyResponse = {
114
+ emergency: {
115
+ id: 1,
116
+ uuid: "em-1",
117
+ company_id: 42,
118
+ title: "Emergency",
119
+ message: "Shelter in place",
120
+ background_color: "#aa0000",
121
+ text_color: "#ffffff",
122
+ target_scope: "all",
123
+ is_active: true,
124
+ started_at: "2026-01-01T00:00:00Z",
125
+ },
126
+ };
127
+
128
+ expect(mapEmergencyResponse(raw)).toEqual({
129
+ id: 1,
130
+ uuid: "em-1",
131
+ companyId: 42,
132
+ title: "Emergency",
133
+ message: "Shelter in place",
134
+ backgroundColor: "#aa0000",
135
+ textColor: "#ffffff",
136
+ targetScope: "all",
137
+ isActive: true,
138
+ startedAt: "2026-01-01T00:00:00Z",
139
+ });
140
+ });
141
+
142
+ it("maps heartbeat ack and payload fields", () => {
143
+ expect(mapHeartbeatAck({ received: true })).toEqual({ received: true });
144
+
145
+ expect(
146
+ mapHeartbeatPayload({
147
+ cpuUsage: 10,
148
+ memoryUsage: 20,
149
+ diskUsage: 30,
150
+ temperature: 40,
151
+ osVersion: "macOS",
152
+ playerVersion: "1.2.3",
153
+ }),
154
+ ).toEqual({
155
+ cpu_usage: 10,
156
+ memory_usage: 20,
157
+ disk_usage: 30,
158
+ temperature: 40,
159
+ os_version: "macOS",
160
+ app_version: "1.2.3",
161
+ });
162
+ });
163
+ });