@appstrata/react 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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-static.d.ts","sourceRoot":"","sources":["../src/use-static.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,uFAAuF;IACvF,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,kDAAkD;IAClD,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,+CAA+C;IAC/C,cAAc,EAAE,CAAC,sBAAsB,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,0BAA0B;IAC1B,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,IAAI,eAAe,CAmB3C"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * useStatic - React hook for the AppStrata Static API.
3
+ *
4
+ * Convenience hook that returns capability-checked methods.
5
+ * All methods are safe no-ops if the "static" capability is unsupported.
6
+ */
7
+ import { useMemo } from "react";
8
+ import { usePlayer, useCapability } from "./hooks.js";
9
+ // ═══════════════════════════════════════════════════════════════════════════
10
+ // HOOK
11
+ // ═══════════════════════════════════════════════════════════════════════════
12
+ /**
13
+ * Access the Static API with safe no-op methods when unsupported.
14
+ *
15
+ * Eliminates the need for `player.static?.markStatic()` optional chaining.
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * const { markStatic, markSemiStatic, supported } = useStatic();
20
+ *
21
+ * useEffect(() => {
22
+ * if (isStaticContent) markStatic();
23
+ * else markSemiStatic(3600); // refresh hourly
24
+ * }, [isStaticContent]);
25
+ * ```
26
+ */
27
+ export function useStatic() {
28
+ const player = usePlayer();
29
+ const supported = useCapability("static");
30
+ return useMemo(() => ({
31
+ supported,
32
+ markStatic: () => {
33
+ if (supported)
34
+ player.static?.markStatic();
35
+ },
36
+ markSemiStatic: (refreshIntervalSeconds) => {
37
+ if (supported)
38
+ player.static?.markSemiStatic(refreshIntervalSeconds);
39
+ },
40
+ disableStatic: () => {
41
+ if (supported)
42
+ player.static?.disableStatic();
43
+ },
44
+ }), [supported, player]);
45
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * useStorage - React hook for the AppStrata Storage API.
3
+ *
4
+ * Two calling modes:
5
+ * 1. Single-key mode: useStorage(key, defaultValue?, options?)
6
+ * - Reactive: auto-loads value, manages loading state
7
+ * 2. Full API mode: useStorage(options?)
8
+ * - Imperative: returns capability-checked methods, no auto-loading
9
+ */
10
+ /**
11
+ * Fallback storage adapter for when the "storage" capability is unsupported.
12
+ * Implement these methods to route reads/writes through an alternative
13
+ * storage mechanism (e.g., localStorage).
14
+ */
15
+ export interface StorageFallbackAdapter {
16
+ get: (key: string) => unknown;
17
+ set: (key: string, value: unknown) => void;
18
+ remove: (key: string) => void;
19
+ /** Only needed for full API mode */
20
+ list?: () => string[];
21
+ /** Only needed for full API mode */
22
+ clear?: () => void;
23
+ }
24
+ /**
25
+ * Options for useStorage (both modes).
26
+ */
27
+ export interface UseStorageOptions {
28
+ fallback?: StorageFallbackAdapter;
29
+ }
30
+ /**
31
+ * Return type for single-key mode.
32
+ */
33
+ export interface UseStorageKeyResult<T> {
34
+ /** Current value (defaultValue while loading or if unsupported without fallback) */
35
+ value: T | undefined;
36
+ /** Whether the initial load is in progress */
37
+ loading: boolean;
38
+ /** Whether the player supports the "storage" capability (null = not yet initialized) */
39
+ supported: boolean | null;
40
+ /** Set the value for this key */
41
+ set: (value: T) => Promise<void>;
42
+ /** Remove this key */
43
+ remove: () => Promise<void>;
44
+ }
45
+ /**
46
+ * Return type for full API mode.
47
+ */
48
+ export interface UseStorageFullResult {
49
+ /** Whether the player supports the "storage" capability (null = not yet initialized) */
50
+ supported: boolean | null;
51
+ /** Get a value by key */
52
+ get: <T = unknown>(key: string) => Promise<T | undefined>;
53
+ /** Set a value for any key */
54
+ set: (key: string, value: unknown) => Promise<void>;
55
+ /** Remove a key */
56
+ remove: (key: string) => Promise<void>;
57
+ /** List all keys */
58
+ list: () => Promise<string[]>;
59
+ /** Clear all storage */
60
+ clear: () => Promise<void>;
61
+ }
62
+ /** Single-key mode with default value and options */
63
+ export declare function useStorage<T = unknown>(key: string, defaultValue: T, options?: UseStorageOptions): UseStorageKeyResult<T>;
64
+ /** Single-key mode with options (no default value) */
65
+ export declare function useStorage<T = unknown>(key: string, options: UseStorageOptions): UseStorageKeyResult<T>;
66
+ /** Single-key mode with just key */
67
+ export declare function useStorage<T = unknown>(key: string): UseStorageKeyResult<T>;
68
+ /** Full API mode with options */
69
+ export declare function useStorage(options: UseStorageOptions): UseStorageFullResult;
70
+ /** Full API mode without options */
71
+ export declare function useStorage(): UseStorageFullResult;
72
+ //# sourceMappingURL=use-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-storage.d.ts","sourceRoot":"","sources":["../src/use-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,oCAAoC;IACpC,IAAI,CAAC,EAAE,MAAM,MAAM,EAAE,CAAC;IACtB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,sBAAsB,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,oFAAoF;IACpF,KAAK,EAAE,CAAC,GAAG,SAAS,CAAC;IACrB,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,wFAAwF;IACxF,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,iCAAiC;IACjC,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,sBAAsB;IACtB,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,wFAAwF;IACxF,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,yBAAyB;IACzB,GAAG,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC1D,8BAA8B;IAC9B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,mBAAmB;IACnB,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,oBAAoB;IACpB,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9B,wBAAwB;IACxB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAMD,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,CAAC,GAAG,OAAO,EACpC,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,CAAC,EACf,OAAO,CAAC,EAAE,iBAAiB,GAC1B,mBAAmB,CAAC,CAAC,CAAC,CAAC;AAE1B,sDAAsD;AACtD,wBAAgB,UAAU,CAAC,CAAC,GAAG,OAAO,EACpC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,iBAAiB,GACzB,mBAAmB,CAAC,CAAC,CAAC,CAAC;AAE1B,oCAAoC;AACpC,wBAAgB,UAAU,CAAC,CAAC,GAAG,OAAO,EACpC,GAAG,EAAE,MAAM,GACV,mBAAmB,CAAC,CAAC,CAAC,CAAC;AAE1B,iCAAiC;AACjC,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,oBAAoB,CAAC;AAE7E,oCAAoC;AACpC,wBAAgB,UAAU,IAAI,oBAAoB,CAAC"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * useStorage - React hook for the AppStrata Storage API.
3
+ *
4
+ * Two calling modes:
5
+ * 1. Single-key mode: useStorage(key, defaultValue?, options?)
6
+ * - Reactive: auto-loads value, manages loading state
7
+ * 2. Full API mode: useStorage(options?)
8
+ * - Imperative: returns capability-checked methods, no auto-loading
9
+ */
10
+ import { useState, useEffect, useCallback, useMemo, useRef } from "react";
11
+ import { usePlayer, useCapability } from "./hooks.js";
12
+ // ═══════════════════════════════════════════════════════════════════════════
13
+ // IMPLEMENTATION
14
+ // ═══════════════════════════════════════════════════════════════════════════
15
+ export function useStorage(keyOrOptions, defaultValueOrOptions, maybeOptions) {
16
+ // Parse arguments to determine mode
17
+ const isKeyMode = typeof keyOrOptions === "string";
18
+ if (isKeyMode) {
19
+ return useStorageKey(keyOrOptions, defaultValueOrOptions, maybeOptions);
20
+ }
21
+ else {
22
+ return useStorageFull(keyOrOptions);
23
+ }
24
+ }
25
+ // ═══════════════════════════════════════════════════════════════════════════
26
+ // SINGLE-KEY MODE
27
+ // ═══════════════════════════════════════════════════════════════════════════
28
+ function useStorageKey(key, defaultValueOrOptions, maybeOptions) {
29
+ // Parse second argument: could be defaultValue or options
30
+ let defaultValue;
31
+ let options;
32
+ if (defaultValueOrOptions !== undefined &&
33
+ typeof defaultValueOrOptions === "object" &&
34
+ defaultValueOrOptions !== null &&
35
+ "fallback" in defaultValueOrOptions) {
36
+ // Second arg is options (has fallback property)
37
+ options = defaultValueOrOptions;
38
+ defaultValue = undefined;
39
+ }
40
+ else {
41
+ defaultValue = defaultValueOrOptions;
42
+ options = maybeOptions;
43
+ }
44
+ const player = usePlayer();
45
+ const supported = useCapability("storage");
46
+ const fallback = options?.fallback;
47
+ const [value, setValue] = useState(defaultValue);
48
+ const [loading, setLoading] = useState(true);
49
+ // Keep key in a ref to avoid stale closures
50
+ const keyRef = useRef(key);
51
+ keyRef.current = key;
52
+ // Load initial value
53
+ useEffect(() => {
54
+ // Wait for player to initialize before resolving
55
+ if (supported === null)
56
+ return;
57
+ let cancelled = false;
58
+ async function load() {
59
+ if (supported && player.storage) {
60
+ const stored = await player.storage.get(key);
61
+ if (!cancelled) {
62
+ setValue(stored !== undefined ? stored : defaultValue);
63
+ setLoading(false);
64
+ }
65
+ }
66
+ else if (fallback) {
67
+ const stored = fallback.get(key);
68
+ if (!cancelled) {
69
+ setValue(stored !== undefined ? stored : defaultValue);
70
+ setLoading(false);
71
+ }
72
+ }
73
+ else {
74
+ // Genuinely unsupported, no fallback: just use defaultValue
75
+ if (!cancelled) {
76
+ setValue(defaultValue);
77
+ setLoading(false);
78
+ }
79
+ }
80
+ }
81
+ setLoading(true);
82
+ load();
83
+ return () => {
84
+ cancelled = true;
85
+ };
86
+ }, [key, supported, player, fallback, defaultValue]);
87
+ const set = useCallback(async (newValue) => {
88
+ if (supported && player.storage) {
89
+ await player.storage.set(keyRef.current, newValue);
90
+ setValue(newValue);
91
+ }
92
+ else if (fallback) {
93
+ fallback.set(keyRef.current, newValue);
94
+ setValue(newValue);
95
+ }
96
+ // Unsupported without fallback: no-op
97
+ }, [supported, player, fallback]);
98
+ const remove = useCallback(async () => {
99
+ if (supported && player.storage) {
100
+ await player.storage.remove(keyRef.current);
101
+ setValue(defaultValue);
102
+ }
103
+ else if (fallback) {
104
+ fallback.remove(keyRef.current);
105
+ setValue(defaultValue);
106
+ }
107
+ // Unsupported without fallback: no-op
108
+ }, [supported, player, fallback, defaultValue]);
109
+ return { value, loading, supported, set, remove };
110
+ }
111
+ // ═══════════════════════════════════════════════════════════════════════════
112
+ // FULL API MODE
113
+ // ═══════════════════════════════════════════════════════════════════════════
114
+ function useStorageFull(options) {
115
+ const player = usePlayer();
116
+ const supported = useCapability("storage");
117
+ const fallback = options?.fallback;
118
+ const get = useCallback(async (key) => {
119
+ if (supported && player.storage) {
120
+ return player.storage.get(key);
121
+ }
122
+ if (fallback) {
123
+ return fallback.get(key);
124
+ }
125
+ return undefined;
126
+ }, [supported, player, fallback]);
127
+ const set = useCallback(async (key, value) => {
128
+ if (supported && player.storage) {
129
+ await player.storage.set(key, value);
130
+ }
131
+ else if (fallback) {
132
+ fallback.set(key, value);
133
+ }
134
+ }, [supported, player, fallback]);
135
+ const remove = useCallback(async (key) => {
136
+ if (supported && player.storage) {
137
+ await player.storage.remove(key);
138
+ }
139
+ else if (fallback) {
140
+ fallback.remove(key);
141
+ }
142
+ }, [supported, player, fallback]);
143
+ const list = useCallback(async () => {
144
+ if (supported && player.storage) {
145
+ return player.storage.list();
146
+ }
147
+ if (fallback?.list) {
148
+ return fallback.list();
149
+ }
150
+ return [];
151
+ }, [supported, player, fallback]);
152
+ const clear = useCallback(async () => {
153
+ if (supported && player.storage) {
154
+ await player.storage.clear();
155
+ }
156
+ else if (fallback?.clear) {
157
+ fallback.clear();
158
+ }
159
+ }, [supported, player, fallback]);
160
+ return useMemo(() => ({ supported, get, set, remove, list, clear }), [supported, get, set, remove, list, clear]);
161
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@appstrata/react",
3
+ "version": "0.1.0",
4
+ "description": "React hooks and components for AppStrata Digital Signage SDK",
5
+ "private": false,
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "keywords": [
16
+ "digital-signage",
17
+ "appstrata",
18
+ "react",
19
+ "hooks",
20
+ "sdk"
21
+ ],
22
+ "author": "AppStrata Team",
23
+ "license": "MIT",
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "publishConfig": {
28
+ "access": "restricted"
29
+ },
30
+ "dependencies": {
31
+ "@appstrata/core": "0.1.0",
32
+ "@appstrata/utils": "0.1.0",
33
+ "@appstrata/web": "0.1.0"
34
+ },
35
+ "peerDependencies": {
36
+ "react": ">=18"
37
+ },
38
+ "devDependencies": {
39
+ "@jest/globals": "^29.7.0",
40
+ "@testing-library/jest-dom": "^6.9.1",
41
+ "@testing-library/react": "^16.3.2",
42
+ "@types/react": "^18.3.28",
43
+ "@types/react-dom": "^18.3.7",
44
+ "jest": "^29.7.0",
45
+ "jest-environment-jsdom": "^29.7.0",
46
+ "react": "^18.3.1",
47
+ "react-dom": "^18.3.1",
48
+ "ts-jest": "^29.4.6",
49
+ "typescript": "^5.9.3"
50
+ },
51
+ "scripts": {
52
+ "build": "tsc --build tsconfig.build.json",
53
+ "clean": "rm -rf dist *.tsbuildinfo",
54
+ "typecheck": "tsc --noEmit",
55
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
56
+ "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch"
57
+ }
58
+ }