@chatlychat/react-native 0.0.0-stage → 0.1.1
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.
- package/README.md +208 -2
- package/dist/ChatlyProvider.d.ts +69 -0
- package/dist/ChatlyProvider.d.ts.map +1 -0
- package/dist/ChatlyProvider.js +146 -0
- package/dist/ChatlyProvider.js.map +1 -0
- package/dist/ChatlyWidget.d.ts +15 -0
- package/dist/ChatlyWidget.d.ts.map +1 -0
- package/dist/ChatlyWidget.js +122 -0
- package/dist/ChatlyWidget.js.map +1 -0
- package/dist/blocks/money.d.ts +56 -0
- package/dist/blocks/money.d.ts.map +1 -0
- package/dist/blocks/money.js +82 -0
- package/dist/blocks/money.js.map +1 -0
- package/dist/blocks/money.parity.spec.d.ts +2 -0
- package/dist/blocks/money.parity.spec.d.ts.map +1 -0
- package/dist/blocks/money.parity.spec.js +87 -0
- package/dist/blocks/money.parity.spec.js.map +1 -0
- package/dist/blocks/read-envelope.d.ts +15 -0
- package/dist/blocks/read-envelope.d.ts.map +1 -0
- package/dist/blocks/read-envelope.js +196 -0
- package/dist/blocks/read-envelope.js.map +1 -0
- package/dist/blocks/read-envelope.parity.spec.d.ts +2 -0
- package/dist/blocks/read-envelope.parity.spec.d.ts.map +1 -0
- package/dist/blocks/read-envelope.parity.spec.js +146 -0
- package/dist/blocks/read-envelope.parity.spec.js.map +1 -0
- package/dist/blocks/types.d.ts +171 -0
- package/dist/blocks/types.d.ts.map +1 -0
- package/dist/blocks/types.js +3 -0
- package/dist/blocks/types.js.map +1 -0
- package/dist/client/RealtimeClient.d.ts +29 -0
- package/dist/client/RealtimeClient.d.ts.map +1 -0
- package/dist/client/RealtimeClient.js +113 -0
- package/dist/client/RealtimeClient.js.map +1 -0
- package/dist/client/RealtimeClient.spec.d.ts +2 -0
- package/dist/client/RealtimeClient.spec.d.ts.map +1 -0
- package/dist/client/RealtimeClient.spec.js +123 -0
- package/dist/client/RealtimeClient.spec.js.map +1 -0
- package/dist/client/RestClient.d.ts +160 -0
- package/dist/client/RestClient.d.ts.map +1 -0
- package/dist/client/RestClient.js +395 -0
- package/dist/client/RestClient.js.map +1 -0
- package/dist/client/RestClient.spec.d.ts +2 -0
- package/dist/client/RestClient.spec.d.ts.map +1 -0
- package/dist/client/RestClient.spec.js +267 -0
- package/dist/client/RestClient.spec.js.map +1 -0
- package/dist/client/types.d.ts +148 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +14 -0
- package/dist/client/types.js.map +1 -0
- package/dist/components/Composer.d.ts +35 -0
- package/dist/components/Composer.d.ts.map +1 -0
- package/dist/components/Composer.js +180 -0
- package/dist/components/Composer.js.map +1 -0
- package/dist/components/HelpCenter.d.ts +13 -0
- package/dist/components/HelpCenter.d.ts.map +1 -0
- package/dist/components/HelpCenter.js +96 -0
- package/dist/components/HelpCenter.js.map +1 -0
- package/dist/components/MessageBlocks.d.ts +41 -0
- package/dist/components/MessageBlocks.d.ts.map +1 -0
- package/dist/components/MessageBlocks.js +167 -0
- package/dist/components/MessageBlocks.js.map +1 -0
- package/dist/components/MessageBubble.d.ts +14 -0
- package/dist/components/MessageBubble.d.ts.map +1 -0
- package/dist/components/MessageBubble.js +87 -0
- package/dist/components/MessageBubble.js.map +1 -0
- package/dist/components/MessageBubble.spec.d.ts +2 -0
- package/dist/components/MessageBubble.spec.d.ts.map +1 -0
- package/dist/components/MessageBubble.spec.js +207 -0
- package/dist/components/MessageBubble.spec.js.map +1 -0
- package/dist/components/MessageCanvas.d.ts +39 -0
- package/dist/components/MessageCanvas.d.ts.map +1 -0
- package/dist/components/MessageCanvas.js +144 -0
- package/dist/components/MessageCanvas.js.map +1 -0
- package/dist/components/MessageCanvas.spec.d.ts +2 -0
- package/dist/components/MessageCanvas.spec.d.ts.map +1 -0
- package/dist/components/MessageCanvas.spec.js +142 -0
- package/dist/components/MessageCanvas.spec.js.map +1 -0
- package/dist/components/MessagesList.d.ts +8 -0
- package/dist/components/MessagesList.d.ts.map +1 -0
- package/dist/components/MessagesList.js +58 -0
- package/dist/components/MessagesList.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/push/registerPushToken.d.ts +12 -0
- package/dist/push/registerPushToken.d.ts.map +1 -0
- package/dist/push/registerPushToken.js +32 -0
- package/dist/push/registerPushToken.js.map +1 -0
- package/dist/storage/TokenStore.d.ts +53 -0
- package/dist/storage/TokenStore.d.ts.map +1 -0
- package/dist/storage/TokenStore.js +89 -0
- package/dist/storage/TokenStore.js.map +1 -0
- package/dist/test/react-native-dom.d.ts +97 -0
- package/dist/test/react-native-dom.d.ts.map +1 -0
- package/dist/test/react-native-dom.js +155 -0
- package/dist/test/react-native-dom.js.map +1 -0
- package/dist/test/react-native-dom.spec.d.ts +2 -0
- package/dist/test/react-native-dom.spec.d.ts.map +1 -0
- package/dist/test/react-native-dom.spec.js +83 -0
- package/dist/test/react-native-dom.spec.js.map +1 -0
- package/dist/theme/ChatlyTheme.d.ts +37 -0
- package/dist/theme/ChatlyTheme.d.ts.map +1 -0
- package/dist/theme/ChatlyTheme.js +2 -0
- package/dist/theme/ChatlyTheme.js.map +1 -0
- package/dist/theme/defaultTheme.d.ts +7 -0
- package/dist/theme/defaultTheme.d.ts.map +1 -0
- package/dist/theme/defaultTheme.js +37 -0
- package/dist/theme/defaultTheme.js.map +1 -0
- package/dist/useChatly.d.ts +3 -0
- package/dist/useChatly.d.ts.map +1 -0
- package/dist/useChatly.js +10 -0
- package/dist/useChatly.js.map +1 -0
- package/package.json +49 -3
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Money on a card, restated for the published package.
|
|
3
|
+
*
|
|
4
|
+
* ── Why this is a copy and not an import ───────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* The canonical implementation is `packages/shared/src/widget-blocks/money.ts`
|
|
7
|
+
* and this file is deliberately identical in behaviour. `@livechat/shared` is
|
|
8
|
+
* `private: true` — it is the monorepo's internal package with a hundred
|
|
9
|
+
* export paths — so a published npm package cannot depend on it. The same
|
|
10
|
+
* reason `client/types.ts` restates the realtime types rather than importing
|
|
11
|
+
* them.
|
|
12
|
+
*
|
|
13
|
+
* A copy is a drift risk, so it is pinned rather than trusted:
|
|
14
|
+
* `money.parity.spec.ts` imports BOTH this and the shared original and
|
|
15
|
+
* asserts they agree over a currency table that includes the cases a
|
|
16
|
+
* hand-rolled version gets wrong. `check-block-field-parity.mjs` knows about
|
|
17
|
+
* the second `Intl.NumberFormat` by name, with that spec as the reason.
|
|
18
|
+
*
|
|
19
|
+
* ── Minor units, never a float ─────────────────────────────────────────
|
|
20
|
+
*
|
|
21
|
+
* `amount` is an integer count of the currency's smallest unit: 4820 is
|
|
22
|
+
* $48.20, 4820 is also ¥4,820, and 4820 is 4.820 KWD.
|
|
23
|
+
*
|
|
24
|
+
* The DIVISOR IS NOT 100. Three currencies have no minor unit (JPY, KRW, VND)
|
|
25
|
+
* and four have three digits (KWD, BHD, JOD, OMR), so `(amount / 100)`
|
|
26
|
+
* renders ¥4,820 as "¥48" and 4.820 KWD as "48.200 KWD" — wrong by two orders
|
|
27
|
+
* of magnitude in opposite directions, and perfect in USD, which is why it
|
|
28
|
+
* would ship. The digit count is read off the formatter, so a currency added
|
|
29
|
+
* to CLDR after this was written is right with no table here.
|
|
30
|
+
*
|
|
31
|
+
* React Native ships a full ICU on iOS and, since 0.73 with Hermes
|
|
32
|
+
* `intl` enabled, on Android too. The `catch` below is what covers an engine
|
|
33
|
+
* without it: a card with an unrecognised currency still shows a price rather
|
|
34
|
+
* than taking the thread's render down.
|
|
35
|
+
*/
|
|
36
|
+
const CURRENCY_RE = /^[A-Z]{3}$/;
|
|
37
|
+
const isMinor = (v) => typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
|
|
38
|
+
/** Read a price off a stored card, or null. Refuses what cannot be drawn. */
|
|
39
|
+
export function readMoney(raw) {
|
|
40
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const bag = raw;
|
|
44
|
+
const currency = bag['currency'];
|
|
45
|
+
if (!isMinor(bag['amount']) || typeof currency !== 'string' || !CURRENCY_RE.test(currency)) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const compareAt = bag['compareAt'];
|
|
49
|
+
return {
|
|
50
|
+
amount: bag['amount'],
|
|
51
|
+
currency,
|
|
52
|
+
...(isMinor(compareAt) ? { compareAt } : {}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function minorPerMajor(formatter) {
|
|
56
|
+
const digits = formatter.resolvedOptions().maximumFractionDigits;
|
|
57
|
+
return 10 ** (typeof digits === 'number' ? digits : 2);
|
|
58
|
+
}
|
|
59
|
+
/** The locale-free rendition, used as the fallback and for accessibility text. */
|
|
60
|
+
export function moneyToPlainText(money) {
|
|
61
|
+
return `${money.currency} ${(money.amount / 100).toFixed(2)}`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Format one amount in the DEVICE's locale.
|
|
65
|
+
*
|
|
66
|
+
* `locale` undefined means "whatever this device is set to", which is the
|
|
67
|
+
* right default: the app runs on the shopper's phone and a German shopper
|
|
68
|
+
* reads `48,20 $`.
|
|
69
|
+
*/
|
|
70
|
+
export function formatMoney(money, locale) {
|
|
71
|
+
try {
|
|
72
|
+
const formatter = new Intl.NumberFormat(locale, {
|
|
73
|
+
style: 'currency',
|
|
74
|
+
currency: money.currency,
|
|
75
|
+
});
|
|
76
|
+
return formatter.format(money.amount / minorPerMajor(formatter));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return moneyToPlainText(money);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=money.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.js","sourceRoot":"","sources":["../../src/blocks/money.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAWH,MAAM,WAAW,GAAG,YAAY,CAAC;AAEjC,MAAM,OAAO,GAAG,CAAC,CAAU,EAAe,EAAE,CAC1C,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAE7D,6EAA6E;AAC7E,MAAM,UAAU,SAAS,CAAC,GAAY;IACpC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAClE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,GAAG,GAAG,GAA8B,CAAC;IAC3C,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC;IACjC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3F,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC;IACnC,OAAO;QACL,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC;QACrB,QAAQ;QACR,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7C,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,SAA4B;IACjD,MAAM,MAAM,GAAG,SAAS,CAAC,eAAe,EAAE,CAAC,qBAAqB,CAAC;IACjE,OAAO,EAAE,IAAI,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,gBAAgB,CAAC,KAAY;IAC3C,OAAO,GAAG,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;AAChE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,KAAY,EAAE,MAAe;IACvD,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE;YAC9C,KAAK,EAAE,UAAU;YACjB,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB,CAAC,CAAC;QACH,OAAO,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.parity.spec.d.ts","sourceRoot":"","sources":["../../src/blocks/money.parity.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The RN copy of the money formatter against the canonical one.
|
|
3
|
+
*
|
|
4
|
+
* ── Why a parity spec and not an import ────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* `@livechat/shared` is `private: true`, so the published package cannot
|
|
7
|
+
* depend on it and `blocks/money.ts` is a deliberate copy. A copy with no
|
|
8
|
+
* test is a copy that drifts, and the drift is invisible: both versions look
|
|
9
|
+
* right in USD, and the cases they disagree on are the ones a hand-rolled
|
|
10
|
+
* divisor gets wrong.
|
|
11
|
+
*
|
|
12
|
+
* The shared package is a DEV dependency, so nothing here ships.
|
|
13
|
+
*
|
|
14
|
+
* This is the control that justifies the second `Intl.NumberFormat` in the
|
|
15
|
+
* repo — see `check-block-field-parity.mjs`, which otherwise fails the build
|
|
16
|
+
* on one.
|
|
17
|
+
*/
|
|
18
|
+
import { formatMoney as sharedFormat, moneyToPlainText as sharedPlain, readMoney as sharedRead, } from '@livechat/shared/widget-blocks';
|
|
19
|
+
import { describe, expect, it } from 'vitest';
|
|
20
|
+
import { formatMoney, moneyToPlainText, readMoney } from './money';
|
|
21
|
+
/**
|
|
22
|
+
* Every shape worth disagreeing about.
|
|
23
|
+
*
|
|
24
|
+
* The three zero-decimal currencies and the four three-decimal ones are the
|
|
25
|
+
* whole point: a `/ 100` divisor is wrong by 100× for JPY and by 10× for KWD,
|
|
26
|
+
* and right for USD.
|
|
27
|
+
*/
|
|
28
|
+
const CASES = [
|
|
29
|
+
{ amount: 4820, currency: 'USD' },
|
|
30
|
+
{ amount: 4820, currency: 'EUR' },
|
|
31
|
+
{ amount: 4820, currency: 'GBP' },
|
|
32
|
+
{ amount: 4820, currency: 'JPY' },
|
|
33
|
+
{ amount: 4820, currency: 'KRW' },
|
|
34
|
+
{ amount: 4820, currency: 'VND' },
|
|
35
|
+
{ amount: 4820, currency: 'KWD' },
|
|
36
|
+
{ amount: 4820, currency: 'BHD' },
|
|
37
|
+
{ amount: 4820, currency: 'JOD' },
|
|
38
|
+
{ amount: 4820, currency: 'OMR' },
|
|
39
|
+
{ amount: 0, currency: 'USD' },
|
|
40
|
+
{ amount: 1, currency: 'USD' },
|
|
41
|
+
{ amount: 100000000, currency: 'USD' },
|
|
42
|
+
{ amount: 4820, currency: 'ZZZ' },
|
|
43
|
+
];
|
|
44
|
+
const LOCALES = [undefined, 'en-US', 'de-DE', 'ja-JP', 'ar-EG'];
|
|
45
|
+
describe('RN money formatter ≡ shared money formatter', () => {
|
|
46
|
+
it('formats every currency identically, in every locale', () => {
|
|
47
|
+
for (const money of CASES) {
|
|
48
|
+
for (const locale of LOCALES) {
|
|
49
|
+
expect(formatMoney(money, locale)).toBe(sharedFormat(money, locale));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
it('agrees on the locale-free rendition', () => {
|
|
54
|
+
for (const money of CASES) {
|
|
55
|
+
expect(moneyToPlainText(money)).toBe(sharedPlain(money));
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
it('agrees on what is readable, and on what is refused', () => {
|
|
59
|
+
const raws = [
|
|
60
|
+
{ amount: 4820, currency: 'USD' },
|
|
61
|
+
{ amount: 4820, currency: 'USD', compareAt: 6000 },
|
|
62
|
+
// Refused: a float, a negative, a lower-case code, a string amount, a
|
|
63
|
+
// non-object. Each would reach a shopper as `NaN` or as a price off by
|
|
64
|
+
// orders of magnitude.
|
|
65
|
+
{ amount: 48.2, currency: 'USD' },
|
|
66
|
+
{ amount: -1, currency: 'USD' },
|
|
67
|
+
{ amount: 4820, currency: 'usd' },
|
|
68
|
+
{ amount: '4820', currency: 'USD' },
|
|
69
|
+
{ currency: 'USD' },
|
|
70
|
+
{ amount: 4820 },
|
|
71
|
+
null,
|
|
72
|
+
[],
|
|
73
|
+
'nope',
|
|
74
|
+
{ amount: 4820, currency: 'USD', compareAt: -3 },
|
|
75
|
+
];
|
|
76
|
+
for (const raw of raws) {
|
|
77
|
+
expect(readMoney(raw)).toEqual(sharedRead(raw));
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
it('is a real comparison — the shared module is actually loaded', () => {
|
|
81
|
+
// A parity spec whose import resolved to the local copy would pass
|
|
82
|
+
// trivially and forever.
|
|
83
|
+
expect(sharedFormat).not.toBe(formatMoney);
|
|
84
|
+
expect(sharedFormat({ amount: 4820, currency: 'JPY' }, 'en-US')).toBe('¥4,820');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
//# sourceMappingURL=money.parity.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.parity.spec.js","sourceRoot":"","sources":["../../src/blocks/money.parity.spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EACL,WAAW,IAAI,YAAY,EAC3B,gBAAgB,IAAI,WAAW,EAC/B,SAAS,IAAI,UAAU,GACxB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,KAAK,GAAG;IACZ,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;IACjC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;IACjC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;IACjC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;IACjC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;IACjC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;IACjC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;IACjC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;IACjC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;IACjC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;IACjC,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE;IAC9B,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE;IAC9B,EAAE,MAAM,EAAE,SAAW,EAAE,QAAQ,EAAE,KAAK,EAAE;IACxC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;CACzB,CAAC;AAEX,MAAM,OAAO,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAU,CAAC;AAEzE,QAAQ,CAAC,6CAA6C,EAAE,GAAG,EAAE;IAC3D,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;YAC1B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;YACvE,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;YAC1B,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,IAAI,GAAc;YACtB,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;YACjC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE;YAClD,sEAAsE;YACtE,uEAAuE;YACvE,uBAAuB;YACvB,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;YACjC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE;YAC/B,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;YACjC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;YACnC,EAAE,QAAQ,EAAE,KAAK,EAAE;YACnB,EAAE,MAAM,EAAE,IAAI,EAAE;YAChB,IAAI;YACJ,EAAE;YACF,MAAM;YACN,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE;SACjD,CAAC;QACF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;QAClD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,mEAAmE;QACnE,yBAAyB;QACzB,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC3C,MAAM,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { WidgetBlocksEnvelope, WidgetCanvas } from './types';
|
|
2
|
+
/** `https:` and nothing else. Mirrors `isSafeMediaSrc` in the shared guard. */
|
|
3
|
+
export declare function isSafeMediaSrc(src: string): boolean;
|
|
4
|
+
export declare function readBlocksEnvelope(raw: unknown): WidgetBlocksEnvelope | null;
|
|
5
|
+
/**
|
|
6
|
+
* Read a stored canvas.
|
|
7
|
+
*
|
|
8
|
+
* Looser than the block reader on one point and stricter on another. Looser:
|
|
9
|
+
* a canvas with no fields and no actions still renders, because a step can be
|
|
10
|
+
* a read-only confirmation. Stricter: `canvasId` is required, because it is
|
|
11
|
+
* what a submission is posted against — a canvas without one is a form with
|
|
12
|
+
* nowhere to go, which is worse than no form.
|
|
13
|
+
*/
|
|
14
|
+
export declare function readCanvas(raw: unknown): WidgetCanvas | null;
|
|
15
|
+
//# sourceMappingURL=read-envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-envelope.d.ts","sourceRoot":"","sources":["../../src/blocks/read-envelope.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAA6B,oBAAoB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAO7F,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAenD;AAqHD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,oBAAoB,GAAG,IAAI,CAM5E;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAoB5D"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a stored block envelope, refusing what cannot be drawn.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors `packages/shared/src/widget-blocks/read-envelope.ts` exactly, and
|
|
5
|
+
* `read-envelope.parity.spec.ts` asserts that over a table of accepted and
|
|
6
|
+
* refused shapes. See `blocks/money.ts` for why this package restates rather
|
|
7
|
+
* than imports.
|
|
8
|
+
*
|
|
9
|
+
* ── The contract, which is the same on every renderer ──────────────────
|
|
10
|
+
*
|
|
11
|
+
* Never throw, never render a hole. A row written before a field was
|
|
12
|
+
* validated — or by a future server this client does not know about — must
|
|
13
|
+
* produce the most of the message that CAN be drawn, not an exception and not
|
|
14
|
+
* a blank space. So:
|
|
15
|
+
*
|
|
16
|
+
* an unreadable BLOCK is dropped, the rest of the message renders;
|
|
17
|
+
* an unsafe card image drops the IMAGE, not the card;
|
|
18
|
+
* an unreadable price drops the PRICE, because `formatMoney` handed a
|
|
19
|
+
* string renders `NaN` at a shopper;
|
|
20
|
+
* an unknown `type` is expected rather than exceptional — an old app meets
|
|
21
|
+
* a newer server — and is dropped silently.
|
|
22
|
+
*
|
|
23
|
+
* ── The media guard ────────────────────────────────────────────────────
|
|
24
|
+
*
|
|
25
|
+
* `https:` only, no control characters, no untrimmed string, length capped.
|
|
26
|
+
* React Native's `Image` will happily fetch `http:` and `data:`, and a card
|
|
27
|
+
* is authored by whoever holds an API key — so the same refusal the widget
|
|
28
|
+
* applies in a browser applies here. There is no `javascript:` hazard in an
|
|
29
|
+
* `Image` source, but `http:` on a phone is a downgrade a shopper cannot see
|
|
30
|
+
* and `file:` is the device's own disk.
|
|
31
|
+
*/
|
|
32
|
+
import { readMoney } from './money';
|
|
33
|
+
import { BLOCK_PROTOCOL_VERSION } from './types';
|
|
34
|
+
const isBag = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
35
|
+
const str = (v) => typeof v === 'string' && v.length > 0;
|
|
36
|
+
/** `https:` and nothing else. Mirrors `isSafeMediaSrc` in the shared guard. */
|
|
37
|
+
export function isSafeMediaSrc(src) {
|
|
38
|
+
if (src.length === 0 || src.length > 2000) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
// eslint-disable-next-line no-control-regex -- matching control characters IS the check
|
|
42
|
+
if (/[\u0000-\u001F\u007F]/.test(src) || src !== src.trim()) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
let parsed;
|
|
46
|
+
try {
|
|
47
|
+
parsed = new URL(src);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return parsed.protocol === 'https:';
|
|
53
|
+
}
|
|
54
|
+
function readAction(raw) {
|
|
55
|
+
if (!isBag(raw) || !str(raw['label'])) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
switch (raw['kind']) {
|
|
59
|
+
case 'url':
|
|
60
|
+
// An `href` is opened with `Linking.openURL`, which will launch any
|
|
61
|
+
// scheme the OS has a handler for — so the same https-only rule.
|
|
62
|
+
return str(raw['href']) && isSafeMediaSrc(raw['href'])
|
|
63
|
+
? raw
|
|
64
|
+
: null;
|
|
65
|
+
case 'postback':
|
|
66
|
+
return str(raw['value']) ? raw : null;
|
|
67
|
+
case 'host_event':
|
|
68
|
+
return str(raw['event']) ? raw : null;
|
|
69
|
+
case 'step':
|
|
70
|
+
return str(raw['canvasId']) ? raw : null;
|
|
71
|
+
case 'copy':
|
|
72
|
+
return str(raw['text']) ? raw : null;
|
|
73
|
+
default:
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function readActions(raw) {
|
|
78
|
+
return Array.isArray(raw) ? raw.map(readAction).filter((a) => a !== null) : [];
|
|
79
|
+
}
|
|
80
|
+
function readPairs(raw) {
|
|
81
|
+
return Array.isArray(raw)
|
|
82
|
+
? raw.filter((f) => isBag(f) && str(f['label']) && str(f['value']))
|
|
83
|
+
: [];
|
|
84
|
+
}
|
|
85
|
+
function readCard(raw) {
|
|
86
|
+
if (!isBag(raw) || !str(raw['title'])) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const card = {
|
|
90
|
+
...raw,
|
|
91
|
+
actions: readActions(raw['actions']),
|
|
92
|
+
fields: readPairs(raw['fields']),
|
|
93
|
+
};
|
|
94
|
+
if (card['image'] !== undefined && !(str(card['image']) && isSafeMediaSrc(card['image']))) {
|
|
95
|
+
delete card['image'];
|
|
96
|
+
}
|
|
97
|
+
if (card['price'] !== undefined) {
|
|
98
|
+
const price = readMoney(card['price']);
|
|
99
|
+
if (price === null) {
|
|
100
|
+
delete card['price'];
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
card['price'] = price;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return card;
|
|
107
|
+
}
|
|
108
|
+
function readBlock(raw) {
|
|
109
|
+
if (!isBag(raw)) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
switch (raw['type']) {
|
|
113
|
+
case 'text':
|
|
114
|
+
return str(raw['markdown']) ? raw : null;
|
|
115
|
+
case 'media':
|
|
116
|
+
// Unlike a card's image, a media block IS its image: nothing is left to
|
|
117
|
+
// render once the src is refused, so the block goes.
|
|
118
|
+
return str(raw['url']) &&
|
|
119
|
+
isSafeMediaSrc(raw['url']) &&
|
|
120
|
+
str(raw['alt']) &&
|
|
121
|
+
(raw['kind'] === 'image' || raw['kind'] === 'video')
|
|
122
|
+
? raw
|
|
123
|
+
: null;
|
|
124
|
+
case 'fields': {
|
|
125
|
+
const items = readPairs(raw['items']);
|
|
126
|
+
return items.length > 0 ? { ...raw, items } : null;
|
|
127
|
+
}
|
|
128
|
+
case 'card': {
|
|
129
|
+
const card = readCard(raw);
|
|
130
|
+
return card === null ? null : card;
|
|
131
|
+
}
|
|
132
|
+
case 'card_grid': {
|
|
133
|
+
const cards = Array.isArray(raw['cards'])
|
|
134
|
+
? raw['cards'].map(readCard).filter((c) => c !== null)
|
|
135
|
+
: [];
|
|
136
|
+
if (cards.length === 0) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
const grid = { ...raw, cards };
|
|
140
|
+
if (grid['layout'] !== 'grid' && grid['layout'] !== 'carousel') {
|
|
141
|
+
delete grid['layout'];
|
|
142
|
+
}
|
|
143
|
+
return grid;
|
|
144
|
+
}
|
|
145
|
+
case 'actions': {
|
|
146
|
+
const actions = readActions(raw['actions']);
|
|
147
|
+
return actions.length > 0 ? { ...raw, actions } : null;
|
|
148
|
+
}
|
|
149
|
+
case 'quick_replies': {
|
|
150
|
+
const options = readPairs(raw['options']);
|
|
151
|
+
return options.length > 0 ? { ...raw, options } : null;
|
|
152
|
+
}
|
|
153
|
+
case 'status':
|
|
154
|
+
// `id` is required: a status block exists to be patched.
|
|
155
|
+
return str(raw['id']) && str(raw['text']) ? raw : null;
|
|
156
|
+
case 'divider':
|
|
157
|
+
return raw;
|
|
158
|
+
default:
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
export function readBlocksEnvelope(raw) {
|
|
163
|
+
if (!isBag(raw) || raw['v'] !== BLOCK_PROTOCOL_VERSION || !Array.isArray(raw['blocks'])) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
const blocks = raw['blocks'].map(readBlock).filter((b) => b !== null);
|
|
167
|
+
return blocks.length > 0 ? { v: BLOCK_PROTOCOL_VERSION, blocks } : null;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Read a stored canvas.
|
|
171
|
+
*
|
|
172
|
+
* Looser than the block reader on one point and stricter on another. Looser:
|
|
173
|
+
* a canvas with no fields and no actions still renders, because a step can be
|
|
174
|
+
* a read-only confirmation. Stricter: `canvasId` is required, because it is
|
|
175
|
+
* what a submission is posted against — a canvas without one is a form with
|
|
176
|
+
* nowhere to go, which is worse than no form.
|
|
177
|
+
*/
|
|
178
|
+
export function readCanvas(raw) {
|
|
179
|
+
if (!isBag(raw) || raw['v'] !== BLOCK_PROTOCOL_VERSION || !str(raw['canvasId'])) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
const fields = Array.isArray(raw['fields'])
|
|
183
|
+
? raw['fields'].filter((f) => isBag(f) && str(f['name']) && str(f['label']) && str(f['type']))
|
|
184
|
+
: [];
|
|
185
|
+
const actions = Array.isArray(raw['actions'])
|
|
186
|
+
? raw['actions'].filter((a) => isBag(a) &&
|
|
187
|
+
str(a['id']) &&
|
|
188
|
+
str(a['label']) &&
|
|
189
|
+
(a['kind'] === 'submit' || a['kind'] === 'cancel'))
|
|
190
|
+
: [];
|
|
191
|
+
const blocks = Array.isArray(raw['blocks'])
|
|
192
|
+
? raw['blocks'].map(readBlock).filter((b) => b !== null)
|
|
193
|
+
: [];
|
|
194
|
+
return { ...raw, fields, actions, blocks };
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=read-envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-envelope.js","sourceRoot":"","sources":["../../src/blocks/read-envelope.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-envelope.parity.spec.d.ts","sourceRoot":"","sources":["../../src/blocks/read-envelope.parity.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The RN envelope reader against the canonical one.
|
|
3
|
+
*
|
|
4
|
+
* ── What this is really guarding ───────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* A reader that is slightly more permissive than the server's is a renderer
|
|
7
|
+
* that draws something the rest of the product refuses — and a reader that is
|
|
8
|
+
* slightly stricter silently drops content the shopper was supposed to see.
|
|
9
|
+
* Neither produces an error anywhere. The two readers are separate files
|
|
10
|
+
* because `@livechat/shared` is private and cannot be a runtime dependency of
|
|
11
|
+
* a published package, so this is the only thing that keeps them the same.
|
|
12
|
+
*
|
|
13
|
+
* Every case below is a shape a real author can produce. The refusals matter
|
|
14
|
+
* more than the acceptances: they are the ones that would otherwise reach a
|
|
15
|
+
* phone as a blank space, a broken image, or `NaN` where a price goes.
|
|
16
|
+
*/
|
|
17
|
+
import { readBlocksEnvelope as sharedRead } from '@livechat/shared/widget-blocks';
|
|
18
|
+
import { describe, expect, it } from 'vitest';
|
|
19
|
+
import { readBlocksEnvelope } from './read-envelope';
|
|
20
|
+
const env = (...blocks) => ({ v: 1, blocks });
|
|
21
|
+
const CASES = [
|
|
22
|
+
{ why: 'a plain text block', raw: env({ type: 'text', markdown: 'hello' }) },
|
|
23
|
+
{ why: 'text with no markdown is dropped', raw: env({ type: 'text' }) },
|
|
24
|
+
{
|
|
25
|
+
why: 'an https image',
|
|
26
|
+
raw: env({ type: 'media', kind: 'image', url: 'https://cdn.test/a.png', alt: 'a' }),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
why: 'an http image is refused — the block IS its image',
|
|
30
|
+
raw: env({ type: 'media', kind: 'image', url: 'http://cdn.test/a.png', alt: 'a' }),
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
why: 'a media block with no alt is refused',
|
|
34
|
+
raw: env({ type: 'media', kind: 'image', url: 'https://cdn.test/a.png' }),
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
why: 'an unknown media kind is refused',
|
|
38
|
+
raw: env({ type: 'media', kind: 'audio', url: 'https://cdn.test/a.mp3', alt: 'a' }),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
why: 'fields with one readable pair',
|
|
42
|
+
raw: env({ type: 'fields', items: [{ label: 'a', value: 'b' }] }),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
why: 'fields with no readable pair is dropped',
|
|
46
|
+
raw: env({ type: 'fields', items: [{ label: 'a' }] }),
|
|
47
|
+
},
|
|
48
|
+
{ why: 'a card', raw: env({ type: 'card', title: 'Hat', subtitle: 'wool' }) },
|
|
49
|
+
{ why: 'a card with no title is dropped', raw: env({ type: 'card', subtitle: 'wool' }) },
|
|
50
|
+
{
|
|
51
|
+
why: 'an unsafe card image drops the IMAGE, not the card',
|
|
52
|
+
raw: env({ type: 'card', title: 'Hat', image: 'javascript:alert(1)' }),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
why: 'an unreadable price drops the PRICE, not the card',
|
|
56
|
+
raw: env({ type: 'card', title: 'Hat', price: { amount: 48.2, currency: 'USD' } }),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
why: 'a readable price is normalised',
|
|
60
|
+
raw: env({
|
|
61
|
+
type: 'card',
|
|
62
|
+
title: 'Hat',
|
|
63
|
+
price: { amount: 4820, currency: 'USD', compareAt: 6000 },
|
|
64
|
+
}),
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
why: 'a carousel keeps its layout',
|
|
68
|
+
raw: env({ type: 'card_grid', layout: 'carousel', cards: [{ title: 'a' }, { title: 'b' }] }),
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
why: 'an unknown layout is dropped so the grid stacks',
|
|
72
|
+
raw: env({ type: 'card_grid', layout: 'spiral', cards: [{ title: 'a' }] }),
|
|
73
|
+
},
|
|
74
|
+
{ why: 'an empty grid is dropped', raw: env({ type: 'card_grid', cards: [] }) },
|
|
75
|
+
{
|
|
76
|
+
why: 'a grid drops only the unreadable cards',
|
|
77
|
+
raw: env({ type: 'card_grid', cards: [{ title: 'a' }, { subtitle: 'no title' }] }),
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
why: 'quick replies',
|
|
81
|
+
raw: env({ type: 'quick_replies', options: [{ label: 'a', value: 'b' }] }),
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
why: 'quick replies with nothing readable is dropped',
|
|
85
|
+
raw: env({ type: 'quick_replies', options: [] }),
|
|
86
|
+
},
|
|
87
|
+
{ why: 'a status block', raw: env({ type: 'status', id: 's1', text: 'Looking…' }) },
|
|
88
|
+
{ why: 'a status block with no id is dropped', raw: env({ type: 'status', text: 'Looking…' }) },
|
|
89
|
+
{ why: 'a divider', raw: env({ type: 'divider' }) },
|
|
90
|
+
{ why: 'an unknown type is dropped silently', raw: env({ type: 'hologram', spin: true }) },
|
|
91
|
+
{
|
|
92
|
+
why: 'the wrong protocol version is refused whole',
|
|
93
|
+
raw: { v: 2, blocks: [{ type: 'divider' }] },
|
|
94
|
+
},
|
|
95
|
+
{ why: 'no blocks at all is null, not an empty envelope', raw: env() },
|
|
96
|
+
{ why: 'a non-object is null', raw: 'nope' },
|
|
97
|
+
{ why: 'null is null', raw: null },
|
|
98
|
+
{
|
|
99
|
+
why: 'a postback action',
|
|
100
|
+
raw: env({ type: 'actions', actions: [{ kind: 'postback', label: 'Yes', value: 'yes' }] }),
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
why: 'a host_event action — the one the native app exists for',
|
|
104
|
+
raw: env({ type: 'actions', actions: [{ kind: 'host_event', label: 'Pay', event: 'pay' }] }),
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
why: 'an action with no label is dropped',
|
|
108
|
+
raw: env({ type: 'actions', actions: [{ kind: 'postback', value: 'yes' }] }),
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
why: 'an actions block with nothing readable is dropped',
|
|
112
|
+
raw: env({ type: 'actions', actions: [{ kind: 'teleport', label: 'Go' }] }),
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
describe('RN block reader ≡ shared block reader', () => {
|
|
116
|
+
for (const c of CASES) {
|
|
117
|
+
it(c.why, () => {
|
|
118
|
+
expect(readBlocksEnvelope(c.raw)).toEqual(sharedRead(c.raw));
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
it('is a real comparison — the shared module is actually loaded', () => {
|
|
122
|
+
expect(sharedRead).not.toBe(readBlocksEnvelope);
|
|
123
|
+
expect(sharedRead(env({ type: 'divider' }))).toEqual({ v: 1, blocks: [{ type: 'divider' }] });
|
|
124
|
+
});
|
|
125
|
+
/**
|
|
126
|
+
* The ONE place the two readers differ, stated as a test so it cannot drift
|
|
127
|
+
* into an accident.
|
|
128
|
+
*
|
|
129
|
+
* A `url` action is opened here with `Linking.openURL`, which launches
|
|
130
|
+
* whatever scheme the OS has a handler for — `tel:`, `itms-apps:`, a
|
|
131
|
+
* third-party app's own scheme. In a browser the widget's own href policy
|
|
132
|
+
* (`permittedDecodedHref`) runs at RENDER time and admits a wider set
|
|
133
|
+
* safely because an `<a>` is inert until clicked and the browser itself
|
|
134
|
+
* refuses the dangerous schemes. On a phone neither is true, so this reader
|
|
135
|
+
* refuses anything but `https:` at READ time.
|
|
136
|
+
*/
|
|
137
|
+
it('refuses a non-https url action that the shared reader admits', () => {
|
|
138
|
+
const raw = env({
|
|
139
|
+
type: 'actions',
|
|
140
|
+
actions: [{ kind: 'url', label: 'Call', href: 'tel:+15550100' }],
|
|
141
|
+
});
|
|
142
|
+
expect(sharedRead(raw)).not.toBeNull();
|
|
143
|
+
expect(readBlocksEnvelope(raw)).toBeNull();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
//# sourceMappingURL=read-envelope.parity.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-envelope.parity.spec.js","sourceRoot":"","sources":["../../src/blocks/read-envelope.parity.spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,kBAAkB,IAAI,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,GAAG,GAAG,CAAC,GAAG,MAAiB,EAAW,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AAElE,MAAM,KAAK,GAAyC;IAClD,EAAE,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE;IAC5E,EAAE,GAAG,EAAE,kCAAkC,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE;IACvE;QACE,GAAG,EAAE,gBAAgB;QACrB,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,wBAAwB,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;KACpF;IACD;QACE,GAAG,EAAE,mDAAmD;QACxD,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,uBAAuB,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;KACnF;IACD;QACE,GAAG,EAAE,sCAAsC;QAC3C,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,wBAAwB,EAAE,CAAC;KAC1E;IACD;QACE,GAAG,EAAE,kCAAkC;QACvC,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,wBAAwB,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;KACpF;IACD;QACE,GAAG,EAAE,+BAA+B;QACpC,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;KAClE;IACD;QACE,GAAG,EAAE,yCAAyC;QAC9C,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;KACtD;IACD,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE;IAC7E,EAAE,GAAG,EAAE,iCAAiC,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE;IACxF;QACE,GAAG,EAAE,oDAAoD;QACzD,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC;KACvE;IACD;QACE,GAAG,EAAE,mDAAmD;QACxD,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC;KACnF;IACD;QACE,GAAG,EAAE,gCAAgC;QACrC,GAAG,EAAE,GAAG,CAAC;YACP,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE;SAC1D,CAAC;KACH;IACD;QACE,GAAG,EAAE,6BAA6B;QAClC,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;KAC7F;IACD;QACE,GAAG,EAAE,iDAAiD;QACtD,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;KAC3E;IACD,EAAE,GAAG,EAAE,0BAA0B,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,EAAE;IAC/E;QACE,GAAG,EAAE,wCAAwC;QAC7C,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;KACnF;IACD;QACE,GAAG,EAAE,eAAe;QACpB,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;KAC3E;IACD;QACE,GAAG,EAAE,gDAAgD;QACrD,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;KACjD;IACD,EAAE,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE;IACnF,EAAE,GAAG,EAAE,sCAAsC,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE;IAC/F,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE;IACnD,EAAE,GAAG,EAAE,qCAAqC,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE;IAC1F;QACE,GAAG,EAAE,6CAA6C;QAClD,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE;KAC7C;IACD,EAAE,GAAG,EAAE,iDAAiD,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACtE,EAAE,GAAG,EAAE,sBAAsB,EAAE,GAAG,EAAE,MAAM,EAAE;IAC5C,EAAE,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,IAAI,EAAE;IAClC;QACE,GAAG,EAAE,mBAAmB;QACxB,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;KAC3F;IACD;QACE,GAAG,EAAE,yDAAyD;QAC9D,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;KAC7F;IACD;QACE,GAAG,EAAE,oCAAoC;QACzC,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;KAC7E;IACD;QACE,GAAG,EAAE,mDAAmD;QACxD,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;KAC5E;CACF,CAAC;AAEF,QAAQ,CAAC,uCAAuC,EAAE,GAAG,EAAE;IACrD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE;YACb,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;IACL,CAAC;IAED,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAChD,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;IAChG,CAAC,CAAC,CAAC;IAEH;;;;;;;;;;;OAWG;IACH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,GAAG,GAAG,GAAG,CAAC;YACd,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;SACjE,CAAC,CAAC;QACH,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QACvC,MAAM,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|