@linkforty/core 1.44.1 → 1.44.3

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.
Files changed (175) hide show
  1. package/README.md +20 -588
  2. package/llms.txt +1 -1
  3. package/package.json +2 -1
  4. package/dist/index.config.test.d.ts +0 -2
  5. package/dist/index.config.test.d.ts.map +0 -1
  6. package/dist/index.config.test.js +0 -87
  7. package/dist/index.config.test.js.map +0 -1
  8. package/dist/lib/bot-detection.test.d.ts +0 -2
  9. package/dist/lib/bot-detection.test.d.ts.map +0 -1
  10. package/dist/lib/bot-detection.test.js +0 -65
  11. package/dist/lib/bot-detection.test.js.map +0 -1
  12. package/dist/lib/client-ip.test.d.ts +0 -7
  13. package/dist/lib/client-ip.test.d.ts.map +0 -1
  14. package/dist/lib/client-ip.test.js +0 -114
  15. package/dist/lib/client-ip.test.js.map +0 -1
  16. package/dist/lib/config.test.d.ts +0 -2
  17. package/dist/lib/config.test.d.ts.map +0 -1
  18. package/dist/lib/config.test.js +0 -126
  19. package/dist/lib/config.test.js.map +0 -1
  20. package/dist/lib/database.test.d.ts +0 -2
  21. package/dist/lib/database.test.d.ts.map +0 -1
  22. package/dist/lib/database.test.js +0 -180
  23. package/dist/lib/database.test.js.map +0 -1
  24. package/dist/lib/deep-link-payload.test.d.ts +0 -2
  25. package/dist/lib/deep-link-payload.test.d.ts.map +0 -1
  26. package/dist/lib/deep-link-payload.test.js +0 -202
  27. package/dist/lib/deep-link-payload.test.js.map +0 -1
  28. package/dist/lib/event-emitter.test.d.ts +0 -2
  29. package/dist/lib/event-emitter.test.d.ts.map +0 -1
  30. package/dist/lib/event-emitter.test.js +0 -162
  31. package/dist/lib/event-emitter.test.js.map +0 -1
  32. package/dist/lib/fingerprint.test.d.ts +0 -2
  33. package/dist/lib/fingerprint.test.d.ts.map +0 -1
  34. package/dist/lib/fingerprint.test.js +0 -750
  35. package/dist/lib/fingerprint.test.js.map +0 -1
  36. package/dist/lib/launchpad.test.d.ts +0 -2
  37. package/dist/lib/launchpad.test.d.ts.map +0 -1
  38. package/dist/lib/launchpad.test.js +0 -368
  39. package/dist/lib/launchpad.test.js.map +0 -1
  40. package/dist/lib/link-lookup.test.d.ts +0 -2
  41. package/dist/lib/link-lookup.test.d.ts.map +0 -1
  42. package/dist/lib/link-lookup.test.js +0 -61
  43. package/dist/lib/link-lookup.test.js.map +0 -1
  44. package/dist/lib/link-resolution-cache.test.d.ts +0 -2
  45. package/dist/lib/link-resolution-cache.test.d.ts.map +0 -1
  46. package/dist/lib/link-resolution-cache.test.js +0 -45
  47. package/dist/lib/link-resolution-cache.test.js.map +0 -1
  48. package/dist/lib/link-safety.test.d.ts +0 -2
  49. package/dist/lib/link-safety.test.d.ts.map +0 -1
  50. package/dist/lib/link-safety.test.js +0 -211
  51. package/dist/lib/link-safety.test.js.map +0 -1
  52. package/dist/lib/not-found-page.test.d.ts +0 -2
  53. package/dist/lib/not-found-page.test.d.ts.map +0 -1
  54. package/dist/lib/not-found-page.test.js +0 -151
  55. package/dist/lib/not-found-page.test.js.map +0 -1
  56. package/dist/lib/portable/cli.test.d.ts +0 -2
  57. package/dist/lib/portable/cli.test.d.ts.map +0 -1
  58. package/dist/lib/portable/cli.test.js +0 -98
  59. package/dist/lib/portable/cli.test.js.map +0 -1
  60. package/dist/lib/portable/portable.integration.test.d.ts +0 -2
  61. package/dist/lib/portable/portable.integration.test.d.ts.map +0 -1
  62. package/dist/lib/portable/portable.integration.test.js +0 -139
  63. package/dist/lib/portable/portable.integration.test.js.map +0 -1
  64. package/dist/lib/portable/registry.schema.test.d.ts +0 -2
  65. package/dist/lib/portable/registry.schema.test.d.ts.map +0 -1
  66. package/dist/lib/portable/registry.schema.test.js +0 -59
  67. package/dist/lib/portable/registry.schema.test.js.map +0 -1
  68. package/dist/lib/scanner-detection.test.d.ts +0 -2
  69. package/dist/lib/scanner-detection.test.d.ts.map +0 -1
  70. package/dist/lib/scanner-detection.test.js +0 -124
  71. package/dist/lib/scanner-detection.test.js.map +0 -1
  72. package/dist/lib/schema-guard.test.d.ts +0 -2
  73. package/dist/lib/schema-guard.test.d.ts.map +0 -1
  74. package/dist/lib/schema-guard.test.js +0 -83
  75. package/dist/lib/schema-guard.test.js.map +0 -1
  76. package/dist/lib/utils.test.d.ts +0 -2
  77. package/dist/lib/utils.test.d.ts.map +0 -1
  78. package/dist/lib/utils.test.js +0 -261
  79. package/dist/lib/utils.test.js.map +0 -1
  80. package/dist/lib/webhook.test.d.ts +0 -2
  81. package/dist/lib/webhook.test.d.ts.map +0 -1
  82. package/dist/lib/webhook.test.js +0 -340
  83. package/dist/lib/webhook.test.js.map +0 -1
  84. package/dist/routes/health.test.d.ts +0 -2
  85. package/dist/routes/health.test.d.ts.map +0 -1
  86. package/dist/routes/health.test.js +0 -82
  87. package/dist/routes/health.test.js.map +0 -1
  88. package/dist/routes/preview.test.d.ts +0 -2
  89. package/dist/routes/preview.test.d.ts.map +0 -1
  90. package/dist/routes/preview.test.js +0 -111
  91. package/dist/routes/preview.test.js.map +0 -1
  92. package/dist/routes/qr.test.d.ts +0 -2
  93. package/dist/routes/qr.test.d.ts.map +0 -1
  94. package/dist/routes/qr.test.js +0 -67
  95. package/dist/routes/qr.test.js.map +0 -1
  96. package/dist/routes/redirect.click-event.test.d.ts +0 -2
  97. package/dist/routes/redirect.click-event.test.d.ts.map +0 -1
  98. package/dist/routes/redirect.click-event.test.js +0 -85
  99. package/dist/routes/redirect.click-event.test.js.map +0 -1
  100. package/dist/routes/redirect.host-scope.integration.test.d.ts +0 -2
  101. package/dist/routes/redirect.host-scope.integration.test.d.ts.map +0 -1
  102. package/dist/routes/redirect.host-scope.integration.test.js +0 -105
  103. package/dist/routes/redirect.host-scope.integration.test.js.map +0 -1
  104. package/dist/routes/redirect.interstitial.test.d.ts +0 -2
  105. package/dist/routes/redirect.interstitial.test.d.ts.map +0 -1
  106. package/dist/routes/redirect.interstitial.test.js +0 -128
  107. package/dist/routes/redirect.interstitial.test.js.map +0 -1
  108. package/dist/routes/redirect.launchpad.test.d.ts +0 -2
  109. package/dist/routes/redirect.launchpad.test.d.ts.map +0 -1
  110. package/dist/routes/redirect.launchpad.test.js +0 -515
  111. package/dist/routes/redirect.launchpad.test.js.map +0 -1
  112. package/dist/routes/redirect.no-web-destination.test.d.ts +0 -2
  113. package/dist/routes/redirect.no-web-destination.test.d.ts.map +0 -1
  114. package/dist/routes/redirect.no-web-destination.test.js +0 -176
  115. package/dist/routes/redirect.no-web-destination.test.js.map +0 -1
  116. package/dist/routes/redirect.not-found.test.d.ts +0 -2
  117. package/dist/routes/redirect.not-found.test.d.ts.map +0 -1
  118. package/dist/routes/redirect.not-found.test.js +0 -280
  119. package/dist/routes/redirect.not-found.test.js.map +0 -1
  120. package/dist/routes/redirect.safety.test.d.ts +0 -2
  121. package/dist/routes/redirect.safety.test.d.ts.map +0 -1
  122. package/dist/routes/redirect.safety.test.js +0 -284
  123. package/dist/routes/redirect.safety.test.js.map +0 -1
  124. package/dist/routes/redirect.store-url.test.d.ts +0 -2
  125. package/dist/routes/redirect.store-url.test.d.ts.map +0 -1
  126. package/dist/routes/redirect.store-url.test.js +0 -239
  127. package/dist/routes/redirect.store-url.test.js.map +0 -1
  128. package/dist/routes/redirect.test.d.ts +0 -2
  129. package/dist/routes/redirect.test.d.ts.map +0 -1
  130. package/dist/routes/redirect.test.js +0 -158
  131. package/dist/routes/redirect.test.js.map +0 -1
  132. package/dist/routes/redirect.web-link.test.d.ts +0 -2
  133. package/dist/routes/redirect.web-link.test.d.ts.map +0 -1
  134. package/dist/routes/redirect.web-link.test.js +0 -138
  135. package/dist/routes/redirect.web-link.test.js.map +0 -1
  136. package/dist/routes/sdk-cache-bypass.test.d.ts +0 -2
  137. package/dist/routes/sdk-cache-bypass.test.d.ts.map +0 -1
  138. package/dist/routes/sdk-cache-bypass.test.js +0 -175
  139. package/dist/routes/sdk-cache-bypass.test.js.map +0 -1
  140. package/dist/routes/sdk.device-values.integration.test.d.ts +0 -2
  141. package/dist/routes/sdk.device-values.integration.test.d.ts.map +0 -1
  142. package/dist/routes/sdk.device-values.integration.test.js +0 -132
  143. package/dist/routes/sdk.device-values.integration.test.js.map +0 -1
  144. package/dist/routes/sdk.event.test.d.ts +0 -2
  145. package/dist/routes/sdk.event.test.d.ts.map +0 -1
  146. package/dist/routes/sdk.event.test.js +0 -284
  147. package/dist/routes/sdk.event.test.js.map +0 -1
  148. package/dist/routes/sdk.install-certainty.integration.test.d.ts +0 -2
  149. package/dist/routes/sdk.install-certainty.integration.test.d.ts.map +0 -1
  150. package/dist/routes/sdk.install-certainty.integration.test.js +0 -104
  151. package/dist/routes/sdk.install-certainty.integration.test.js.map +0 -1
  152. package/dist/routes/sdk.install-live-links.integration.test.d.ts +0 -2
  153. package/dist/routes/sdk.install-live-links.integration.test.d.ts.map +0 -1
  154. package/dist/routes/sdk.install-live-links.integration.test.js +0 -92
  155. package/dist/routes/sdk.install-live-links.integration.test.js.map +0 -1
  156. package/dist/routes/sdk.install-referrer.integration.test.d.ts +0 -2
  157. package/dist/routes/sdk.install-referrer.integration.test.d.ts.map +0 -1
  158. package/dist/routes/sdk.install-referrer.integration.test.js +0 -187
  159. package/dist/routes/sdk.install-referrer.integration.test.js.map +0 -1
  160. package/dist/routes/sdk.install-window-once.integration.test.d.ts +0 -2
  161. package/dist/routes/sdk.install-window-once.integration.test.d.ts.map +0 -1
  162. package/dist/routes/sdk.install-window-once.integration.test.js +0 -147
  163. package/dist/routes/sdk.install-window-once.integration.test.js.map +0 -1
  164. package/dist/routes/sdk.install.test.d.ts +0 -2
  165. package/dist/routes/sdk.install.test.d.ts.map +0 -1
  166. package/dist/routes/sdk.install.test.js +0 -99
  167. package/dist/routes/sdk.install.test.js.map +0 -1
  168. package/dist/routes/sdk.resolve-click.test.d.ts +0 -2
  169. package/dist/routes/sdk.resolve-click.test.d.ts.map +0 -1
  170. package/dist/routes/sdk.resolve-click.test.js +0 -79
  171. package/dist/routes/sdk.resolve-click.test.js.map +0 -1
  172. package/dist/routes/sdk.resolve-scope.test.d.ts +0 -2
  173. package/dist/routes/sdk.resolve-scope.test.d.ts.map +0 -1
  174. package/dist/routes/sdk.resolve-scope.test.js +0 -105
  175. package/dist/routes/sdk.resolve-scope.test.js.map +0 -1
@@ -1,158 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { isIOSInAppBrowser, isAndroidInAppBrowser, pickMobileFallbackUrl, } from './redirect.js';
3
- // Real-world UA strings (truncated where helpful) for use across test cases.
4
- const UA = {
5
- // Regular browsers — should NOT be detected as in-app
6
- iosSafari: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1',
7
- iosChrome: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/123.0.6312.52 Mobile/15E148 Safari/604.1',
8
- androidChrome: 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36',
9
- androidFirefox: 'Mozilla/5.0 (Android 14; Mobile; rv:125.0) Gecko/125.0 Firefox/125.0',
10
- // iOS in-app browsers
11
- iosGmail: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 GSA/322.0.616052181 Safari/604.1',
12
- iosFacebook: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBAV/450.0.0.0]',
13
- iosInstagram: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Instagram 320.0.0.0',
14
- iosOutlook: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Outlook-iOS/2.0',
15
- iosTwitter: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Twitter for iPhone/10.0',
16
- // Android in-app browsers
17
- androidWebview: 'Mozilla/5.0 (Linux; Android 14; Pixel 8; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/124.0.0.0 Mobile Safari/537.36',
18
- androidFacebook: 'Mozilla/5.0 (Linux; Android 14; Pixel 8; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/124.0.0.0 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/450.0.0.0]',
19
- androidInstagram: 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 Instagram 320.0.0.0',
20
- androidLine: 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 Line/13.0.0',
21
- androidWhatsapp: 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 WhatsApp/2.24.0',
22
- };
23
- const URLS = {
24
- iosStore: 'https://apps.apple.com/app/example/id123',
25
- androidStore: 'https://play.google.com/store/apps/details?id=com.example',
26
- webFallback: 'https://example.com/landing',
27
- };
28
- describe('isIOSInAppBrowser', () => {
29
- it('detects Gmail (GSA token)', () => {
30
- expect(isIOSInAppBrowser(UA.iosGmail)).toBe(true);
31
- });
32
- it('detects Facebook (FBAN/FBAV)', () => {
33
- expect(isIOSInAppBrowser(UA.iosFacebook)).toBe(true);
34
- });
35
- it('detects Instagram', () => {
36
- expect(isIOSInAppBrowser(UA.iosInstagram)).toBe(true);
37
- });
38
- it('detects Outlook', () => {
39
- expect(isIOSInAppBrowser(UA.iosOutlook)).toBe(true);
40
- });
41
- it('detects Twitter', () => {
42
- expect(isIOSInAppBrowser(UA.iosTwitter)).toBe(true);
43
- });
44
- it('does not flag Safari as in-app', () => {
45
- expect(isIOSInAppBrowser(UA.iosSafari)).toBe(false);
46
- });
47
- it('does not flag Chrome on iOS as in-app', () => {
48
- expect(isIOSInAppBrowser(UA.iosChrome)).toBe(false);
49
- });
50
- });
51
- describe('isAndroidInAppBrowser', () => {
52
- it('detects generic Android WebView via wv) marker', () => {
53
- expect(isAndroidInAppBrowser(UA.androidWebview)).toBe(true);
54
- });
55
- it('detects Facebook (FB_IAB/FBAN/FBAV)', () => {
56
- expect(isAndroidInAppBrowser(UA.androidFacebook)).toBe(true);
57
- });
58
- it('detects Instagram', () => {
59
- expect(isAndroidInAppBrowser(UA.androidInstagram)).toBe(true);
60
- });
61
- it('detects Line', () => {
62
- expect(isAndroidInAppBrowser(UA.androidLine)).toBe(true);
63
- });
64
- it('detects WhatsApp', () => {
65
- expect(isAndroidInAppBrowser(UA.androidWhatsapp)).toBe(true);
66
- });
67
- it('does not flag Chrome as in-app', () => {
68
- expect(isAndroidInAppBrowser(UA.androidChrome)).toBe(false);
69
- });
70
- it('does not flag Firefox as in-app', () => {
71
- expect(isAndroidInAppBrowser(UA.androidFirefox)).toBe(false);
72
- });
73
- });
74
- describe('pickMobileFallbackUrl — iOS regular browser (Safari/Chrome)', () => {
75
- it('prefers iOS store URL over web fallback', () => {
76
- const r = pickMobileFallbackUrl('ios', UA.iosSafari, URLS.iosStore, URLS.androidStore, URLS.webFallback);
77
- expect(r).toEqual({ url: URLS.iosStore, reason: 'ios_app_store_url' });
78
- });
79
- it('falls back to web fallback when iOS store URL is absent', () => {
80
- const r = pickMobileFallbackUrl('ios', UA.iosSafari, null, URLS.androidStore, URLS.webFallback);
81
- expect(r).toEqual({ url: URLS.webFallback, reason: 'web_fallback_url' });
82
- });
83
- it('uses iOS store URL when web fallback is absent', () => {
84
- const r = pickMobileFallbackUrl('ios', UA.iosSafari, URLS.iosStore, URLS.androidStore, null);
85
- expect(r).toEqual({ url: URLS.iosStore, reason: 'ios_app_store_url' });
86
- });
87
- it('returns null when both iOS store URL and web fallback are absent', () => {
88
- expect(pickMobileFallbackUrl('ios', UA.iosSafari, null, URLS.androidStore, null)).toBeNull();
89
- });
90
- });
91
- describe('pickMobileFallbackUrl — iOS in-app browser (Gmail/FB/Instagram/etc.)', () => {
92
- it('prefers web fallback over iOS store URL', () => {
93
- const r = pickMobileFallbackUrl('ios', UA.iosGmail, URLS.iosStore, URLS.androidStore, URLS.webFallback);
94
- expect(r).toEqual({ url: URLS.webFallback, reason: 'web_fallback_url' });
95
- });
96
- it('falls back to iOS store URL when web fallback is absent', () => {
97
- const r = pickMobileFallbackUrl('ios', UA.iosGmail, URLS.iosStore, URLS.androidStore, null);
98
- expect(r).toEqual({ url: URLS.iosStore, reason: 'ios_app_store_url' });
99
- });
100
- it('returns null when both iOS store URL and web fallback are absent', () => {
101
- expect(pickMobileFallbackUrl('ios', UA.iosGmail, null, URLS.androidStore, null)).toBeNull();
102
- });
103
- });
104
- describe('pickMobileFallbackUrl — Android regular browser (Chrome/Firefox)', () => {
105
- it('prefers Android store URL over web fallback', () => {
106
- const r = pickMobileFallbackUrl('android', UA.androidChrome, URLS.iosStore, URLS.androidStore, URLS.webFallback);
107
- expect(r).toEqual({ url: URLS.androidStore, reason: 'android_app_store_url' });
108
- });
109
- it('falls back to web fallback when Android store URL is absent', () => {
110
- const r = pickMobileFallbackUrl('android', UA.androidChrome, URLS.iosStore, null, URLS.webFallback);
111
- expect(r).toEqual({ url: URLS.webFallback, reason: 'web_fallback_url' });
112
- });
113
- it('uses Android store URL when web fallback is absent', () => {
114
- const r = pickMobileFallbackUrl('android', UA.androidChrome, URLS.iosStore, URLS.androidStore, null);
115
- expect(r).toEqual({ url: URLS.androidStore, reason: 'android_app_store_url' });
116
- });
117
- it('returns null when both Android store URL and web fallback are absent', () => {
118
- expect(pickMobileFallbackUrl('android', UA.androidChrome, URLS.iosStore, null, null)).toBeNull();
119
- });
120
- });
121
- describe('pickMobileFallbackUrl — Android in-app browser (FB/Instagram/Line/WebView)', () => {
122
- it('prefers web fallback over Android store URL', () => {
123
- const r = pickMobileFallbackUrl('android', UA.androidFacebook, URLS.iosStore, URLS.androidStore, URLS.webFallback);
124
- expect(r).toEqual({ url: URLS.webFallback, reason: 'web_fallback_url' });
125
- });
126
- it('detects WebView via wv) marker and prefers web fallback', () => {
127
- const r = pickMobileFallbackUrl('android', UA.androidWebview, URLS.iosStore, URLS.androidStore, URLS.webFallback);
128
- expect(r).toEqual({ url: URLS.webFallback, reason: 'web_fallback_url' });
129
- });
130
- it('falls back to Android store URL when web fallback is absent', () => {
131
- const r = pickMobileFallbackUrl('android', UA.androidFacebook, URLS.iosStore, URLS.androidStore, null);
132
- expect(r).toEqual({ url: URLS.androidStore, reason: 'android_app_store_url' });
133
- });
134
- });
135
- describe('pickMobileFallbackUrl — reporter scenario regression test', () => {
136
- // Reproduces a reported regression: a link with iOS, Android and web fallback
137
- // URLs served the web fallback to mobile visitors, who expect the App/Play Store.
138
- it('iOS Safari → iOS App Store (was: web fallback)', () => {
139
- const r = pickMobileFallbackUrl('ios', UA.iosSafari, URLS.iosStore, URLS.androidStore, URLS.webFallback);
140
- expect(r?.url).toBe(URLS.iosStore);
141
- expect(r?.reason).toBe('ios_app_store_url');
142
- });
143
- it('Android Chrome → Play Store (was: web fallback)', () => {
144
- const r = pickMobileFallbackUrl('android', UA.androidChrome, URLS.iosStore, URLS.androidStore, URLS.webFallback);
145
- expect(r?.url).toBe(URLS.androidStore);
146
- expect(r?.reason).toBe('android_app_store_url');
147
- });
148
- // And the email-marketing flow that the prior fix protected stays correct:
149
- it('iOS Gmail in-app → web fallback (preserves UL second-chance)', () => {
150
- const r = pickMobileFallbackUrl('ios', UA.iosGmail, URLS.iosStore, URLS.androidStore, URLS.webFallback);
151
- expect(r?.url).toBe(URLS.webFallback);
152
- });
153
- it('Android Facebook in-app → web fallback (preserves UL second-chance)', () => {
154
- const r = pickMobileFallbackUrl('android', UA.androidFacebook, URLS.iosStore, URLS.androidStore, URLS.webFallback);
155
- expect(r?.url).toBe(URLS.webFallback);
156
- });
157
- });
158
- //# sourceMappingURL=redirect.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"redirect.test.js","sourceRoot":"","sources":["../../src/routes/redirect.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=redirect.web-link.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"redirect.web-link.test.d.ts","sourceRoot":"","sources":["../../src/routes/redirect.web-link.test.ts"],"names":[],"mappings":""}
@@ -1,138 +0,0 @@
1
- /**
2
- * On a host the deployment reserves for web links (`isWebLinkRequest`), every
3
- * link opens the website on every device: a plain 302 to the destination
4
- * desktop gets, never the Launchpad page, the scheme interstitial or a store.
5
- * Follows redirect.launchpad.test.ts.
6
- */
7
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
8
- import Fastify from 'fastify';
9
- import { redirectRoutes } from './redirect.js';
10
- const query = vi.fn();
11
- vi.mock('../lib/database.js', () => ({
12
- db: {
13
- query: (...args) => query(...args),
14
- },
15
- }));
16
- const DESKTOP_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36';
17
- const IPHONE_UA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1';
18
- const ANDROID_UA = 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Mobile Safari/537.36';
19
- const FB_IOS_UA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBAV/450.0.0.0.0]';
20
- const WEB_HOST = 'links.example';
21
- /** The deployment's rule: one host is for web links. */
22
- const options = {
23
- isWebLinkRequest: (request) => request.headers.host === WEB_HOST,
24
- };
25
- /** Launchpad page mode: without the flag, a mobile visitor to an app-less link gets the page. */
26
- const PAGE_MODE = { launchpad: { mobile: 'page' } };
27
- function linkRow(overrides = {}) {
28
- return {
29
- id: '00000000-0000-0000-0000-0000000000aa',
30
- short_code: 'abc123',
31
- title: null,
32
- description: null,
33
- og_title: null,
34
- og_description: null,
35
- og_image_url: null,
36
- original_url: 'https://example.com/podcast',
37
- web_fallback_url: null,
38
- ios_app_store_url: null,
39
- android_app_store_url: null,
40
- ios_universal_link: null,
41
- android_app_link: null,
42
- app_scheme: null,
43
- deep_link_path: null,
44
- launchpad_mode: 'inherit',
45
- is_active: true,
46
- warn_at: null,
47
- owner_suspended_at: null,
48
- expires_at: null,
49
- targeting_rules: null,
50
- template_settings: null,
51
- org_settings: PAGE_MODE,
52
- utm_parameters: null,
53
- deep_link_parameters: null,
54
- append_click_id: false,
55
- ...overrides,
56
- };
57
- }
58
- function mockDb(row) {
59
- query.mockReset();
60
- query.mockImplementation(async (sql) => {
61
- if (/information_schema\.columns/i.test(sql)) {
62
- return { rows: [{ '?column?': 1 }], rowCount: 1 };
63
- }
64
- if (/^\s*SELECT\s+l\.\*/i.test(sql) || /FROM links l/i.test(sql)) {
65
- return { rows: row ? [row] : [], rowCount: row ? 1 : 0 };
66
- }
67
- return { rows: [], rowCount: 0 };
68
- });
69
- }
70
- const get = (app, ua, host = WEB_HOST) => app.inject({ method: 'GET', url: '/abc123', headers: { 'user-agent': ua, host } });
71
- let app;
72
- beforeEach(async () => {
73
- app = Fastify();
74
- await app.register(redirectRoutes, options);
75
- await app.ready();
76
- });
77
- afterEach(async () => {
78
- await new Promise((r) => setImmediate(r));
79
- await app.close();
80
- });
81
- describe('web-link host', () => {
82
- it('302s every device to the web destination, even with the workspace in Launchpad page mode', async () => {
83
- mockDb(linkRow());
84
- for (const ua of [IPHONE_UA, ANDROID_UA, FB_IOS_UA, DESKTOP_UA]) {
85
- const res = await get(app, ua);
86
- expect(res.statusCode, ua).toBe(302);
87
- expect(res.headers.location, ua).toBe('https://example.com/podcast');
88
- }
89
- });
90
- it('ignores app destinations the link or workspace carries: no store, scheme or Universal Link', async () => {
91
- mockDb(linkRow({
92
- ios_app_store_url: 'https://apps.apple.com/app/id1',
93
- android_app_store_url: 'https://play.google.com/store/apps/details?id=demo',
94
- ios_universal_link: 'https://go.example/app/1',
95
- android_app_link: 'https://go.example/app/1',
96
- app_scheme: 'demo',
97
- deep_link_path: '/p/1',
98
- }));
99
- for (const ua of [IPHONE_UA, ANDROID_UA]) {
100
- const res = await get(app, ua);
101
- expect(res.statusCode, ua).toBe(302);
102
- expect(res.headers.location, ua).toBe('https://example.com/podcast');
103
- }
104
- });
105
- it('uses the same destination desktop does: web fallback first, with UTM parameters', async () => {
106
- mockDb(linkRow({
107
- web_fallback_url: 'https://example.com/landing',
108
- utm_parameters: { source: 'email', campaign: 'oct' },
109
- }));
110
- const res = await get(app, IPHONE_UA);
111
- expect(res.statusCode).toBe(302);
112
- const location = new URL(res.headers.location);
113
- expect(location.origin + location.pathname).toBe('https://example.com/landing');
114
- expect(location.searchParams.get('utm_source')).toBe('email');
115
- expect(location.searchParams.get('utm_campaign')).toBe('oct');
116
- });
117
- it('leaves every other host alone: the same link gets the Launchpad page on an app-link host', async () => {
118
- mockDb(linkRow({ ios_app_store_url: 'https://apps.apple.com/app/id1' }));
119
- const res = await get(app, IPHONE_UA, 'go.example');
120
- expect(res.statusCode).toBe(200);
121
- expect(res.headers['content-type']).toMatch(/text\/html/);
122
- });
123
- it('treats a hook that throws as an app-link host rather than failing the click', async () => {
124
- await app.close();
125
- app = Fastify();
126
- await app.register(redirectRoutes, {
127
- isWebLinkRequest: () => {
128
- throw new Error('lookup failed');
129
- },
130
- });
131
- await app.ready();
132
- mockDb(linkRow({ ios_app_store_url: 'https://apps.apple.com/app/id1' }));
133
- const res = await get(app, IPHONE_UA);
134
- expect(res.statusCode).toBe(200);
135
- expect(res.headers['content-type']).toMatch(/text\/html/);
136
- });
137
- });
138
- //# sourceMappingURL=redirect.web-link.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"redirect.web-link.test.js","sourceRoot":"","sources":["../../src/routes/redirect.web-link.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=sdk-cache-bypass.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sdk-cache-bypass.test.d.ts","sourceRoot":"","sources":["../../src/routes/sdk-cache-bypass.test.ts"],"names":[],"mappings":""}
@@ -1,175 +0,0 @@
1
- /**
2
- * Regression test for the owner-restriction bypass via the SDK resolve endpoint.
3
- *
4
- * The redirect and `/api/sdk/v1/resolve/:shortCode` write the SAME Redis key. The SDK
5
- * query used to omit the `organizations` join, so the row it cached carried no
6
- * `owner_suspended_at`. The redirect then read `undefined`, its gate treated that as
7
- * "not restricted", and a restricted owner's link redirected for the rest of the TTL.
8
- *
9
- * Both plugins share one fake Redis here, because sharing the cache is the mechanism —
10
- * testing them in isolation cannot reproduce it.
11
- */
12
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
13
- import Fastify from 'fastify';
14
- const query = vi.fn();
15
- vi.mock('../lib/database.js', () => ({ db: { query: (...a) => query(...a) } }));
16
- import { redirectRoutes } from './redirect.js';
17
- import { sdkRoutes } from './sdk.js';
18
- /** Minimal shared Redis stand-in — get/set/setex/del over one Map. */
19
- function fakeRedis() {
20
- const store = new Map();
21
- return {
22
- store,
23
- get: async (k) => store.get(k) ?? null,
24
- set: async (k, v) => void store.set(k, v),
25
- setex: async (k, _ttl, v) => void store.set(k, v),
26
- del: async (k) => void store.delete(k),
27
- };
28
- }
29
- const RESTRICTED = '2026-08-10T00:00:00Z';
30
- function row(overrides = {}) {
31
- return {
32
- id: '00000000-0000-0000-0000-0000000000aa',
33
- short_code: 'abc123',
34
- organization_id: '00000000-0000-0000-0000-0000000000bb',
35
- original_url: 'https://example.com/landing',
36
- web_fallback_url: null,
37
- deep_link_path: null,
38
- is_active: true,
39
- warn_at: null,
40
- owner_suspended_at: RESTRICTED,
41
- expires_at: null,
42
- targeting_rules: null,
43
- template_settings: null,
44
- org_settings: null,
45
- utm_parameters: null,
46
- append_click_id: false,
47
- ...overrides,
48
- };
49
- }
50
- /**
51
- * Mock the database so the row's shape follows the SELECT, exactly as Postgres would:
52
- * `owner_suspended_at` is only present when the query asked for it. That is what makes
53
- * this a real reproduction rather than a restatement of the fix.
54
- */
55
- function mockDb() {
56
- query.mockReset();
57
- query.mockImplementation(async (sql) => {
58
- if (/information_schema\.columns/i.test(sql))
59
- return { rows: [{ x: 1 }], rowCount: 1 };
60
- if (/FROM links/i.test(sql)) {
61
- // The row's shape follows the SELECT, exactly as Postgres would: a column is
62
- // present only if the query asked for it. That is what makes these real
63
- // reproductions rather than restatements of the fix.
64
- const r = row();
65
- if (!/owner_suspended_at/i.test(sql))
66
- delete r.owner_suspended_at;
67
- if (!/template_settings/i.test(sql))
68
- delete r.template_settings;
69
- if (!/org_settings/i.test(sql))
70
- delete r.org_settings;
71
- return { rows: [r], rowCount: 1 };
72
- }
73
- return { rows: [], rowCount: 0 };
74
- });
75
- }
76
- let redirectApp;
77
- let sdkApp;
78
- let redis;
79
- beforeEach(async () => {
80
- mockDb();
81
- redis = fakeRedis();
82
- redirectApp = Fastify();
83
- redirectApp.decorate('redis', redis);
84
- await redirectApp.register(redirectRoutes);
85
- await redirectApp.ready();
86
- sdkApp = Fastify();
87
- sdkApp.decorate('redis', redis);
88
- await sdkApp.register(sdkRoutes);
89
- await sdkApp.ready();
90
- });
91
- afterEach(async () => {
92
- await new Promise((r) => setImmediate(r));
93
- await redirectApp.close();
94
- await sdkApp.close();
95
- });
96
- describe('owner restriction cannot be bypassed through the SDK cache', () => {
97
- /**
98
- * Asserted as "does not resolve", not as a status code. A restricted owner's link
99
- * now answers 410 with a notice rather than 404, and this file is about the
100
- * bypass, not the wording — pinning the code here would make every future change
101
- * to the blocked response look like a security regression.
102
- */
103
- it('blocks a restricted owner when the redirect populates the cache itself', async () => {
104
- const res = await redirectApp.inject({ method: 'GET', url: '/abc123' });
105
- expect(res.statusCode).not.toBe(302);
106
- expect(res.headers.location).toBeUndefined();
107
- expect(res.body).not.toContain('example.com');
108
- });
109
- it('still blocks after an SDK resolve has primed the same cache key', async () => {
110
- // This is the bypass: prime via the public SDK endpoint, then hit the redirect.
111
- await sdkApp.inject({ method: 'GET', url: '/api/sdk/v1/resolve/abc123' });
112
- expect(redis.store.has('link:abc123')).toBe(true);
113
- const res = await redirectApp.inject({ method: 'GET', url: '/abc123' });
114
- expect(res.statusCode, 'a restricted owner must stay blocked on a cache hit').not.toBe(302);
115
- expect(res.headers.location).toBeUndefined();
116
- });
117
- it('the cached row carries owner_suspended_at whichever path wrote it', async () => {
118
- await sdkApp.inject({ method: 'GET', url: '/api/sdk/v1/resolve/abc123' });
119
- const cached = JSON.parse(redis.store.get('link:abc123'));
120
- // Absent (not merely null) is what made the redirect's gate pass.
121
- expect(Object.prototype.hasOwnProperty.call(cached, 'owner_suspended_at')).toBe(true);
122
- });
123
- it('the SDK endpoint itself refuses to resolve a restricted link', async () => {
124
- // It returns the destination directly, so it must enforce the same policy — that
125
- // is the information the redirect is refusing to disclose.
126
- const res = await sdkApp.inject({ method: 'GET', url: '/api/sdk/v1/resolve/abc123' });
127
- expect(res.statusCode).toBe(404);
128
- expect(res.body).not.toContain('example.com/landing');
129
- });
130
- });
131
- /**
132
- * The same cache-shape divergence, one field over.
133
- *
134
- * The redirect reads `template_settings` and `org_settings` from this shared key for
135
- * its URL fallback chain (link, then template, then workspace). While the SDK query
136
- * omitted them, an SDK resolve left the next redirect unable to see a template-level
137
- * fallback, so a link relying on one silently fell through to `original_url`.
138
- */
139
- describe('the URL fallback chain survives an SDK resolve', () => {
140
- /** No link-level web fallback; the destination lives on the template. */
141
- function templateFallbackRow() {
142
- return {
143
- ...row({ owner_suspended_at: null }),
144
- original_url: 'https://example.com/ORIGINAL',
145
- web_fallback_url: null,
146
- template_settings: { defaultWebFallbackUrl: 'https://cdn.example.com/template-default' },
147
- org_settings: null,
148
- };
149
- }
150
- beforeEach(() => {
151
- query.mockReset();
152
- query.mockImplementation(async (sql) => {
153
- if (/information_schema\.columns/i.test(sql))
154
- return { rows: [{ x: 1 }], rowCount: 1 };
155
- if (/FROM links/i.test(sql)) {
156
- const r = templateFallbackRow();
157
- if (!/owner_suspended_at/i.test(sql))
158
- delete r.owner_suspended_at;
159
- if (!/template_settings/i.test(sql))
160
- delete r.template_settings;
161
- if (!/org_settings/i.test(sql))
162
- delete r.org_settings;
163
- return { rows: [r], rowCount: 1 };
164
- }
165
- return { rows: [], rowCount: 0 };
166
- });
167
- });
168
- it('caches template_settings whichever path writes the key', async () => {
169
- await sdkApp.inject({ method: 'GET', url: '/api/sdk/v1/resolve/abc123' });
170
- const cached = JSON.parse(redis.store.get('link:abc123'));
171
- expect(Object.prototype.hasOwnProperty.call(cached, 'template_settings')).toBe(true);
172
- expect(Object.prototype.hasOwnProperty.call(cached, 'org_settings')).toBe(true);
173
- });
174
- });
175
- //# sourceMappingURL=sdk-cache-bypass.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sdk-cache-bypass.test.js","sourceRoot":"","sources":["../../src/routes/sdk-cache-bypass.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,OAAiC,MAAM,SAAS,CAAC;AAExD,MAAM,KAAK,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;AACtB,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG,CAAY,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,sEAAsE;AACtE,SAAS,SAAS;IAChB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,OAAO;QACL,KAAK;QACL,GAAG,EAAE,KAAK,EAAE,CAAS,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI;QAC9C,GAAG,EAAE,KAAK,EAAE,CAAS,EAAE,CAAS,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;QACzD,KAAK,EAAE,KAAK,EAAE,CAAS,EAAE,IAAY,EAAE,CAAS,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;QACzE,GAAG,EAAE,KAAK,EAAE,CAAS,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,GAAG,sBAAsB,CAAC;AAE1C,SAAS,GAAG,CAAC,YAAqC,EAAE;IAClD,OAAO;QACL,EAAE,EAAE,sCAAsC;QAC1C,UAAU,EAAE,QAAQ;QACpB,eAAe,EAAE,sCAAsC;QACvD,YAAY,EAAE,6BAA6B;QAC3C,gBAAgB,EAAE,IAAI;QACtB,cAAc,EAAE,IAAI;QACpB,SAAS,EAAE,IAAI;QACf,OAAO,EAAE,IAAI;QACb,kBAAkB,EAAE,UAAU;QAC9B,UAAU,EAAE,IAAI;QAChB,eAAe,EAAE,IAAI;QACrB,iBAAiB,EAAE,IAAI;QACvB,YAAY,EAAE,IAAI;QAClB,cAAc,EAAE,IAAI;QACpB,eAAe,EAAE,KAAK;QACtB,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,MAAM;IACb,KAAK,CAAC,SAAS,EAAE,CAAC;IAClB,KAAK,CAAC,kBAAkB,CAAC,KAAK,EAAE,GAAW,EAAE,EAAE;QAC7C,IAAI,8BAA8B,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;QACvF,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,6EAA6E;YAC7E,wEAAwE;YACxE,qDAAqD;YACrD,MAAM,CAAC,GAA4B,GAAG,EAAE,CAAC;YACzC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC;YAClE,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,OAAO,CAAC,CAAC,iBAAiB,CAAC;YAChE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,OAAO,CAAC,CAAC,YAAY,CAAC;YACtD,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;QACpC,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,IAAI,WAA4B,CAAC;AACjC,IAAI,MAAuB,CAAC;AAC5B,IAAI,KAAmC,CAAC;AAExC,UAAU,CAAC,KAAK,IAAI,EAAE;IACpB,MAAM,EAAE,CAAC;IACT,KAAK,GAAG,SAAS,EAAE,CAAC;IACpB,WAAW,GAAG,OAAO,EAAE,CAAC;IACxB,WAAW,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAY,CAAC,CAAC;IAC5C,MAAM,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IAC3C,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC;IAE1B,MAAM,GAAG,OAAO,EAAE,CAAC;IACnB,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAY,CAAC,CAAC;IACvC,MAAM,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACjC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC;IAC1B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4DAA4D,EAAE,GAAG,EAAE;IAC1E;;;;;OAKG;IACH,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACtF,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;QACxE,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,aAAa,EAAE,CAAC;QAC7C,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,gFAAgF;QAChF,MAAM,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,4BAA4B,EAAE,CAAC,CAAC;QAC1E,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAElD,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;QACxE,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,qDAAqD,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5F,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,aAAa,EAAE,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,4BAA4B,EAAE,CAAC,CAAC;QAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,CAAE,CAAC,CAAC;QAC3D,kEAAkE;QAClE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,iFAAiF;QACjF,2DAA2D;QAC3D,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,4BAA4B,EAAE,CAAC,CAAC;QACtF,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAGH;;;;;;;GAOG;AACH,QAAQ,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC9D,yEAAyE;IACzE,SAAS,mBAAmB;QAC1B,OAAO;YACL,GAAG,GAAG,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC;YACpC,YAAY,EAAE,8BAA8B;YAC5C,gBAAgB,EAAE,IAAI;YACtB,iBAAiB,EAAE,EAAE,qBAAqB,EAAE,0CAA0C,EAAE;YACxF,YAAY,EAAE,IAAI;SACnB,CAAC;IACJ,CAAC;IAED,UAAU,CAAC,GAAG,EAAE;QACd,KAAK,CAAC,SAAS,EAAE,CAAC;QAClB,KAAK,CAAC,kBAAkB,CAAC,KAAK,EAAE,GAAW,EAAE,EAAE;YAC7C,IAAI,8BAA8B,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;YACvF,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC5B,MAAM,CAAC,GAA4B,mBAAmB,EAAE,CAAC;gBACzD,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;oBAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC;gBAClE,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC;oBAAE,OAAO,CAAC,CAAC,iBAAiB,CAAC;gBAChE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC;oBAAE,OAAO,CAAC,CAAC,YAAY,CAAC;gBACtD,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;YACpC,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,4BAA4B,EAAE,CAAC,CAAC;QAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,CAAE,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrF,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=sdk.device-values.integration.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sdk.device-values.integration.test.d.ts","sourceRoot":"","sources":["../../src/routes/sdk.device-values.integration.test.ts"],"names":[],"mappings":""}