@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
|
+
}
|
package/Polyfills/index.js
CHANGED
|
@@ -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.
|
|
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.
|
|
26
|
-
"@applicaster/zapp-react-native-bridge": "16.0.0-alpha.
|
|
27
|
-
"@applicaster/zapp-react-native-redux": "16.0.0-alpha.
|
|
28
|
-
"@applicaster/zapp-react-native-ui-components": "16.0.0-alpha.
|
|
29
|
-
"@applicaster/zapp-react-native-utils": "16.0.0-alpha.
|
|
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",
|