@applicaster/zapp-react-dom-app 16.0.0-alpha.5652127751 → 16.0.0-alpha.6466900632

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,104 @@
1
+ import { getContextResolverBridge } from "../";
2
+
3
+ function createStorageMock(initial = {}) {
4
+ const data = { ...initial };
5
+
6
+ return {
7
+ getItem: (key) => (key in data ? data[key] : null),
8
+ setItem: (key, value) => {
9
+ data[key] = value;
10
+ },
11
+ };
12
+ }
13
+
14
+ describe("ContextResolverBridge polyfill", () => {
15
+ let bridge;
16
+
17
+ beforeEach(() => {
18
+ global.window = {
19
+ sessionStorage: createStorageMock(),
20
+ localStorage: createStorageMock(),
21
+ };
22
+
23
+ bridge = getContextResolverBridge();
24
+ });
25
+
26
+ it("resolves a key from session storage using the web namespace separator", () => {
27
+ // the namespace itself contains dots, so the key must be split on the last dot
28
+ window.sessionStorage.setItem("applicaster.v2_::_appVersion", "1.0.0");
29
+
30
+ const result = bridge.resolveContextKeys({
31
+ "applicaster.v2.appVersion": false,
32
+ });
33
+
34
+ expect(result).toEqual({ "applicaster.v2.appVersion": "1.0.0" });
35
+ });
36
+
37
+ it("falls back to local storage when the key is missing in session storage", () => {
38
+ window.localStorage.setItem("applicaster.v2_::_appVersion", "1.0.0");
39
+
40
+ const result = bridge.resolveContextKeys({
41
+ "applicaster.v2.appVersion": false,
42
+ });
43
+
44
+ expect(result).toEqual({ "applicaster.v2.appVersion": "1.0.0" });
45
+ });
46
+
47
+ it("prefers the session storage value when the key exists in both", () => {
48
+ window.sessionStorage.setItem("applicaster.v2_::_appVersion", "session");
49
+ window.localStorage.setItem("applicaster.v2_::_appVersion", "local");
50
+
51
+ const result = bridge.resolveContextKeys({
52
+ "applicaster.v2.appVersion": false,
53
+ });
54
+
55
+ expect(result).toEqual({ "applicaster.v2.appVersion": "session" });
56
+ });
57
+
58
+ it("returns null for keys that are missing from both storages", () => {
59
+ const result = bridge.resolveContextKeys({
60
+ "applicaster.v2.missing": false,
61
+ });
62
+
63
+ expect(result).toEqual({ "applicaster.v2.missing": null });
64
+ });
65
+
66
+ it("JSON-parses stored values just like the async storage path", () => {
67
+ window.sessionStorage.setItem(
68
+ "applicaster.v2_::_profile",
69
+ JSON.stringify({ name: "Ada" })
70
+ );
71
+
72
+ window.localStorage.setItem("applicaster.v2_::_count", "42");
73
+
74
+ const result = bridge.resolveContextKeys({
75
+ "applicaster.v2.profile": false,
76
+ "applicaster.v2.count": true,
77
+ });
78
+
79
+ expect(result).toEqual({
80
+ "applicaster.v2.profile": { name: "Ada" },
81
+ "applicaster.v2.count": 42,
82
+ });
83
+ });
84
+
85
+ it("resolves multiple keys synchronously in a single call", () => {
86
+ window.sessionStorage.setItem("applicaster.v2_::_a", "valueA");
87
+ window.localStorage.setItem("applicaster.v2_::_b", "valueB");
88
+
89
+ const result = bridge.resolveContextKeys({
90
+ "applicaster.v2.a": false,
91
+ "applicaster.v2.b": false,
92
+ "applicaster.v2.c": false,
93
+ });
94
+
95
+ // returned object is not a promise - reads happen synchronously
96
+ expect(result).not.toBeInstanceOf(Promise);
97
+
98
+ expect(result).toEqual({
99
+ "applicaster.v2.a": "valueA",
100
+ "applicaster.v2.b": "valueB",
101
+ "applicaster.v2.c": null,
102
+ });
103
+ });
104
+ });
@@ -200,3 +200,74 @@ export function getStorageModule(type) {
200
200
  removeItem,
201
201
  };
202
202
  }
203
+
204
+ const STORAGE_READ_ORDER = ["sessionStorage", "localStorage"];
205
+
206
+ /**
207
+ * Splits a normalized context key ("<namespace>.<key>") back into its
208
+ * namespace and key parts. Mirrors getNamespaceAndKey in the context keys
209
+ * manager: the key is everything after the last dot, since the namespace
210
+ * itself may contain dots (e.g. "applicaster.v2").
211
+ * @param {String} namespacedKey
212
+ * @returns {{ namespace: String, key: String }}
213
+ */
214
+ function splitNamespacedKey(namespacedKey) {
215
+ const lastDotIndex = namespacedKey.lastIndexOf(".");
216
+
217
+ if (lastDotIndex === -1) {
218
+ return { namespace: DEFAULT_NAMESPACE, key: namespacedKey };
219
+ }
220
+
221
+ return {
222
+ namespace: namespacedKey.slice(0, lastDotIndex),
223
+ key: namespacedKey.slice(lastDotIndex + 1),
224
+ };
225
+ }
226
+
227
+ /**
228
+ * Synchronously reads a single context key from session storage, then local
229
+ * storage (matching the native ContextResolverBridge resolution order), and
230
+ * returns the parsed value, or null when the key is absent from both.
231
+ * @param {String} namespacedKey
232
+ * @returns {Any|null}
233
+ */
234
+ function resolveContextKey(namespacedKey) {
235
+ const { namespace, key } = splitNamespacedKey(namespacedKey);
236
+ const storageKey = applyNamespaceToKeyName(key, namespace);
237
+
238
+ for (const type of STORAGE_READ_ORDER) {
239
+ const storage = window[type];
240
+ const value = storage ? storage.getItem(storageKey) : null;
241
+
242
+ if (value != null) {
243
+ return parseJsonIfNeeded(value);
244
+ }
245
+ }
246
+
247
+ return null;
248
+ }
249
+
250
+ /**
251
+ * Web polyfill for the native NativeModules.ContextResolverBridge module.
252
+ * Unlike the per-key async storage path, it resolves every requested key in a
253
+ * single synchronous pass over window.sessionStorage / window.localStorage.
254
+ * @returns {Object} bridge
255
+ * @returns {Function} bridge.resolveContextKeys: resolves a batch of keys
256
+ */
257
+ export function getContextResolverBridge() {
258
+ return {
259
+ /**
260
+ * @param {Object} keys map of normalized key -> required flag
261
+ * @returns {Object} map of normalized key -> resolved value (or null)
262
+ */
263
+ resolveContextKeys(keys) {
264
+ const result = {};
265
+
266
+ for (const namespacedKey of Object.keys(keys || {})) {
267
+ result[namespacedKey] = resolveContextKey(namespacedKey);
268
+ }
269
+
270
+ return result;
271
+ },
272
+ };
273
+ }
@@ -1,4 +1,4 @@
1
- import { getStorageModule } from "./Storage";
1
+ import { getStorageModule, getContextResolverBridge } from "./Storage";
2
2
  import { DeviceEventEmitter } from "./DeviceEventEmitter";
3
3
  import { isSamsungPlatform, isLgPlatform } from "../App/Loader/utils/platform";
4
4
 
@@ -23,6 +23,7 @@ const PLATFORM_KEYS = {
23
23
  export function registerNativeModulesPolyfills(NativeModules) {
24
24
  NativeModules.LocalStorage = getStorageModule("localStorage");
25
25
  NativeModules.SessionStorage = getStorageModule("sessionStorage");
26
+ NativeModules.ContextResolverBridge = getContextResolverBridge();
26
27
  NativeModules.AnalyticsBridge = require("./AnalyticsBridge").AnalyticsBridge;
27
28
  NativeModules.AppLoaderBridge = require("./AppLoaderBridge").AppLoaderBridge;
28
29
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-dom-app",
3
- "version": "16.0.0-alpha.5652127751",
3
+ "version": "16.0.0-alpha.6466900632",
4
4
  "description": "Zapp App Component for Applicaster's Quick Brick React Native App",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -22,11 +22,11 @@
22
22
  },
23
23
  "homepage": "https://github.com/applicaster/zapp-react-dom-app#readme",
24
24
  "dependencies": {
25
- "@applicaster/zapp-react-dom-ui-components": "16.0.0-alpha.5652127751",
26
- "@applicaster/zapp-react-native-bridge": "16.0.0-alpha.5652127751",
27
- "@applicaster/zapp-react-native-redux": "16.0.0-alpha.5652127751",
28
- "@applicaster/zapp-react-native-ui-components": "16.0.0-alpha.5652127751",
29
- "@applicaster/zapp-react-native-utils": "16.0.0-alpha.5652127751",
25
+ "@applicaster/zapp-react-dom-ui-components": "16.0.0-alpha.6466900632",
26
+ "@applicaster/zapp-react-native-bridge": "16.0.0-alpha.6466900632",
27
+ "@applicaster/zapp-react-native-redux": "16.0.0-alpha.6466900632",
28
+ "@applicaster/zapp-react-native-ui-components": "16.0.0-alpha.6466900632",
29
+ "@applicaster/zapp-react-native-utils": "16.0.0-alpha.6466900632",
30
30
  "abortcontroller-polyfill": "^1.7.5",
31
31
  "typeface-montserrat": "^0.0.54",
32
32
  "video.js": "7.14.3",