@screenly/edge-apps 1.0.0 → 1.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.
@@ -1,3 +1,2 @@
1
1
  export * from './mock.js';
2
- export * from './setup.js';
3
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AA+BA,cAAc,WAAW,CAAA"}
@@ -1,2 +1,18 @@
1
+ import { JSDOM } from 'jsdom';
2
+ // Preserve the side-effect import contract of `@screenly/edge-apps/test`
3
+ // for downstream Bun tests that need DOM globals.
4
+ const g = globalThis;
5
+ if (typeof g.window === 'undefined' || typeof g.document === 'undefined') {
6
+ const dom = new JSDOM('<!DOCTYPE html><html><head></head><body></body></html>', {
7
+ url: 'http://localhost',
8
+ });
9
+ g.window = dom.window;
10
+ g.document = dom.window.document;
11
+ g.navigator = dom.window.navigator;
12
+ g.Node = dom.window.Node;
13
+ g.HTMLElement = dom.window.HTMLElement;
14
+ g.customElements = dom.window.customElements;
15
+ g.getComputedStyle = dom.window.getComputedStyle;
16
+ g.SVGSVGElement = dom.window.SVGSVGElement;
17
+ }
1
18
  export * from './mock.js';
2
- export * from './setup.js';
@@ -5,6 +5,7 @@ export * from './theme.js';
5
5
  export * from './locale.js';
6
6
  export * from './metadata.js';
7
7
  export * from './oauth.js';
8
+ export * from './sentry.js';
8
9
  export * from './settings.js';
9
10
  export * from './screen.js';
10
11
  export * from './weather.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,qBAAqB,CAAA;AACnC,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,qBAAqB,CAAA;AACnC,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,eAAe,CAAA"}
@@ -5,6 +5,7 @@ export * from './theme.js';
5
5
  export * from './locale.js';
6
6
  export * from './metadata.js';
7
7
  export * from './oauth.js';
8
+ export * from './sentry.js';
8
9
  export * from './settings.js';
9
10
  export * from './screen.js';
10
11
  export * from './weather.js';
@@ -0,0 +1,5 @@
1
+ import type { ErrorEvent } from '@sentry/browser';
2
+ export declare function scrubSensitiveData(event: ErrorEvent): ErrorEvent | null;
3
+ export declare function setupSentry(app: string, contexts?: Record<string, Record<string, unknown>>): void;
4
+ export declare function reportError(error: unknown, context?: Record<string, unknown>): void;
5
+ //# sourceMappingURL=sentry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentry.d.ts","sourceRoot":"","sources":["../../src/utils/sentry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAWjD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAwBvE;AAED,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAM,GACrD,IAAI,CAqBN;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACpC,IAAI,CAEN"}
@@ -0,0 +1,55 @@
1
+ import * as Sentry from '@sentry/browser';
2
+ import { getHostname } from './metadata.js';
3
+ import { getSetting, getSettings } from './settings.js';
4
+ const SENSITIVE_KEY_PATTERNS = ['token', 'secret', 'password', 'credential'];
5
+ function isSensitiveKey(key) {
6
+ const lowerKey = key.toLowerCase();
7
+ return SENSITIVE_KEY_PATTERNS.some((pattern) => lowerKey.includes(pattern));
8
+ }
9
+ export function scrubSensitiveData(event) {
10
+ const settings = getSettings();
11
+ const secrets = Object.keys(settings)
12
+ .filter(isSensitiveKey)
13
+ .map((key) => settings[key])
14
+ .filter((value) => typeof value === 'string' && value !== '');
15
+ if (secrets.length === 0) {
16
+ return event;
17
+ }
18
+ try {
19
+ let serialized = JSON.stringify(event);
20
+ for (const secret of secrets) {
21
+ serialized = serialized.split(secret).join('[REDACTED]');
22
+ }
23
+ return JSON.parse(serialized);
24
+ }
25
+ catch {
26
+ // If the event can't be serialized to scrub it, drop it rather than risk
27
+ // sending an unredacted secret.
28
+ return null;
29
+ }
30
+ }
31
+ export function setupSentry(app, contexts = {}) {
32
+ const dsn = getSetting('sentry_dsn');
33
+ if (!dsn) {
34
+ return;
35
+ }
36
+ try {
37
+ Sentry.init({
38
+ dsn,
39
+ tracesSampleRate: 0,
40
+ beforeSend: scrubSensitiveData,
41
+ });
42
+ Sentry.setTag('edge_app', app);
43
+ Sentry.setTag('hostname', getHostname());
44
+ Sentry.setUser({ id: getHostname() });
45
+ for (const [name, data] of Object.entries(contexts)) {
46
+ Sentry.setContext(name, data);
47
+ }
48
+ }
49
+ catch (error) {
50
+ console.error('Sentry init failed', error);
51
+ }
52
+ }
53
+ export function reportError(error, context = {}) {
54
+ Sentry.captureException(error, { extra: context });
55
+ }
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@screenly/edge-apps",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A TypeScript library for interfacing with Screenly Edge Apps API",
5
5
  "type": "module",
6
+ "sideEffects": ["**/*.css", "**/register.js", "**/buffer-shim.js"],
6
7
  "engines": {
7
8
  "node": ">=20.6.0"
8
9
  },
@@ -87,19 +88,21 @@
87
88
  "access": "public"
88
89
  },
89
90
  "dependencies": {
91
+ "@eslint/js": "^10.0.1",
90
92
  "@photostructure/tz-lookup": "^11.5.0",
93
+ "@sentry/browser": "^10.59.0",
91
94
  "@tailwindcss/vite": "^4.2.1",
92
95
  "country-locale-map": "^1.9.12",
93
96
  "dayjs": "^1.11.19",
94
- "@eslint/js": "^10.0.1",
95
97
  "eslint": "^10.0.3",
96
98
  "jiti": "^2.6.1",
99
+ "jsdom": "^28.1.0",
97
100
  "offline-geocode-city": "^1.0.2",
98
- "typescript-eslint": "^8.57.0",
99
101
  "panic-overlay": "^1.0.51",
100
102
  "sharp": "^0.34.5",
101
103
  "tailwindcss": "^4.2.1",
102
104
  "typescript": "^5.9.3",
105
+ "typescript-eslint": "^8.57.0",
103
106
  "vite": "^7.3.1",
104
107
  "yaml": "^2.8.2"
105
108
  },
@@ -117,7 +120,6 @@
117
120
  "@types/sharp": "^0.32.0",
118
121
  "@vitest/coverage-v8": "^3.2.4",
119
122
  "globals": "^13.24.0",
120
- "jsdom": "^28.1.0",
121
123
  "prettier": "^3.8.1",
122
124
  "vitest": "^3.2.4"
123
125
  }
package/tsconfig.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "forceConsistentCasingInFileNames": true,
15
15
  "resolveJsonModule": true,
16
16
  "allowSyntheticDefaultImports": true,
17
- "types": ["vite/client"]
17
+ "types": ["vite/client", "node"]
18
18
  },
19
19
  "include": ["src/**/*"],
20
20
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=setup.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/test/setup.ts"],"names":[],"mappings":""}
@@ -1,13 +0,0 @@
1
- import { JSDOM } from 'jsdom';
2
- // Setup jsdom for DOM operations in tests
3
- const dom = new JSDOM('<!DOCTYPE html><html><head></head><body></body></html>', {
4
- url: 'http://localhost',
5
- });
6
- global.document = dom.window.document;
7
- global.window = dom.window;
8
- global.navigator = dom.window.navigator;
9
- global.Node = dom.window.Node;
10
- global.HTMLElement = dom.window.HTMLElement;
11
- global.customElements = dom.window.customElements;
12
- global.getComputedStyle = dom.window.getComputedStyle;
13
- global.SVGSVGElement = dom.window.SVGSVGElement;