@explorins/pers-shared 2.1.190 → 2.1.192
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/dist/cjs/shared-lib/dto/business/businessTypeCreate.request.dto.d.ts +1 -1
- package/dist/cjs/shared-lib/dto/business/businessTypeCreate.request.dto.d.ts.map +1 -1
- package/dist/cjs/shared-lib/dto/business/businessTypeCreate.request.dto.js +2 -2
- package/dist/cjs/shared-lib/dto/business/businessTypeCreate.request.dto.js.map +1 -1
- package/dist/cjs/shared-lib/dto/redemption/redemptionRedeem.request.dto.d.ts.map +1 -1
- package/dist/cjs/shared-lib/dto/redemption/redemptionRedeem.request.dto.js +11 -1
- package/dist/cjs/shared-lib/dto/redemption/redemptionRedeem.request.dto.js.map +1 -1
- package/dist/cjs/shared-lib/enum/signing-account-provider.enum.d.ts +1 -0
- package/dist/cjs/shared-lib/enum/signing-account-provider.enum.d.ts.map +1 -1
- package/dist/cjs/shared-lib/enum/signing-account-provider.enum.js +1 -0
- package/dist/cjs/shared-lib/enum/signing-account-provider.enum.js.map +1 -1
- package/dist/cjs/shared-lib/interfaces/eligibility.interface.d.ts +38 -0
- package/dist/cjs/shared-lib/interfaces/eligibility.interface.d.ts.map +1 -0
- package/dist/cjs/shared-lib/interfaces/eligibility.interface.js +10 -0
- package/dist/cjs/shared-lib/interfaces/eligibility.interface.js.map +1 -0
- package/dist/cjs/shared-lib/interfaces/eligible-business-summary.interface.d.ts +3 -0
- package/dist/cjs/shared-lib/interfaces/eligible-business-summary.interface.d.ts.map +1 -1
- package/dist/cjs/shared-lib/interfaces/index.d.ts +1 -0
- package/dist/cjs/shared-lib/interfaces/index.d.ts.map +1 -1
- package/dist/cjs/shared-lib/interfaces/index.js +1 -0
- package/dist/cjs/shared-lib/interfaces/index.js.map +1 -1
- package/dist/cjs/shared-lib/types/data-source.type.d.ts +56 -10
- package/dist/cjs/shared-lib/types/data-source.type.d.ts.map +1 -1
- package/dist/cjs/shared-lib/types/data-source.type.js +123 -18
- package/dist/cjs/shared-lib/types/data-source.type.js.map +1 -1
- package/dist/cjs/shared-lib/validation/index.d.ts +2 -0
- package/dist/cjs/shared-lib/validation/index.d.ts.map +1 -1
- package/dist/cjs/shared-lib/validation/index.js +4 -0
- package/dist/cjs/shared-lib/validation/index.js.map +1 -1
- package/dist/cjs/shared-lib/validation/redemption-eligibility.d.ts +107 -0
- package/dist/cjs/shared-lib/validation/redemption-eligibility.d.ts.map +1 -0
- package/dist/cjs/shared-lib/validation/redemption-eligibility.js +74 -0
- package/dist/cjs/shared-lib/validation/redemption-eligibility.js.map +1 -0
- package/dist/cjs/shared-lib/validation/status-hierarchy.d.ts +33 -0
- package/dist/cjs/shared-lib/validation/status-hierarchy.d.ts.map +1 -0
- package/dist/cjs/shared-lib/validation/status-hierarchy.js +73 -0
- package/dist/cjs/shared-lib/validation/status-hierarchy.js.map +1 -0
- package/dist/cjs/shared-patterns/functions/token-metadata.functions.js +2 -2
- package/dist/cjs/shared-patterns/functions/token-metadata.functions.js.map +1 -1
- package/dist/esm/shared-lib/dto/business/businessTypeCreate.request.dto.d.ts +1 -1
- package/dist/esm/shared-lib/dto/business/businessTypeCreate.request.dto.d.ts.map +1 -1
- package/dist/esm/shared-lib/dto/business/businessTypeCreate.request.dto.js +2 -2
- package/dist/esm/shared-lib/dto/business/businessTypeCreate.request.dto.js.map +1 -1
- package/dist/esm/shared-lib/dto/redemption/redemptionRedeem.request.dto.d.ts.map +1 -1
- package/dist/esm/shared-lib/dto/redemption/redemptionRedeem.request.dto.js +11 -1
- package/dist/esm/shared-lib/dto/redemption/redemptionRedeem.request.dto.js.map +1 -1
- package/dist/esm/shared-lib/enum/signing-account-provider.enum.d.ts +1 -0
- package/dist/esm/shared-lib/enum/signing-account-provider.enum.d.ts.map +1 -1
- package/dist/esm/shared-lib/enum/signing-account-provider.enum.js +1 -0
- package/dist/esm/shared-lib/enum/signing-account-provider.enum.js.map +1 -1
- package/dist/esm/shared-lib/interfaces/eligibility.interface.d.ts +38 -0
- package/dist/esm/shared-lib/interfaces/eligibility.interface.d.ts.map +1 -0
- package/dist/esm/shared-lib/interfaces/eligibility.interface.js +9 -0
- package/dist/esm/shared-lib/interfaces/eligibility.interface.js.map +1 -0
- package/dist/esm/shared-lib/interfaces/eligible-business-summary.interface.d.ts +3 -0
- package/dist/esm/shared-lib/interfaces/eligible-business-summary.interface.d.ts.map +1 -1
- package/dist/esm/shared-lib/interfaces/index.d.ts +1 -0
- package/dist/esm/shared-lib/interfaces/index.d.ts.map +1 -1
- package/dist/esm/shared-lib/interfaces/index.js +1 -0
- package/dist/esm/shared-lib/interfaces/index.js.map +1 -1
- package/dist/esm/shared-lib/types/data-source.type.d.ts +56 -10
- package/dist/esm/shared-lib/types/data-source.type.d.ts.map +1 -1
- package/dist/esm/shared-lib/types/data-source.type.js +122 -18
- package/dist/esm/shared-lib/types/data-source.type.js.map +1 -1
- package/dist/esm/shared-lib/validation/index.d.ts +2 -0
- package/dist/esm/shared-lib/validation/index.d.ts.map +1 -1
- package/dist/esm/shared-lib/validation/index.js +4 -0
- package/dist/esm/shared-lib/validation/index.js.map +1 -1
- package/dist/esm/shared-lib/validation/redemption-eligibility.d.ts +107 -0
- package/dist/esm/shared-lib/validation/redemption-eligibility.d.ts.map +1 -0
- package/dist/esm/shared-lib/validation/redemption-eligibility.js +70 -0
- package/dist/esm/shared-lib/validation/redemption-eligibility.js.map +1 -0
- package/dist/esm/shared-lib/validation/status-hierarchy.d.ts +33 -0
- package/dist/esm/shared-lib/validation/status-hierarchy.d.ts.map +1 -0
- package/dist/esm/shared-lib/validation/status-hierarchy.js +70 -0
- package/dist/esm/shared-lib/validation/status-hierarchy.js.map +1 -0
- package/dist/esm/shared-patterns/functions/token-metadata.functions.js +2 -2
- package/dist/esm/shared-patterns/functions/token-metadata.functions.js.map +1 -1
- package/package.json +1 -1
|
@@ -19,14 +19,15 @@
|
|
|
19
19
|
*
|
|
20
20
|
* ## Example Scenarios
|
|
21
21
|
*
|
|
22
|
-
* | Scenario | channel |
|
|
23
|
-
*
|
|
24
|
-
* |
|
|
25
|
-
* | Mobile
|
|
26
|
-
* |
|
|
27
|
-
* |
|
|
28
|
-
* |
|
|
29
|
-
* |
|
|
22
|
+
* | Scenario | channel | platform.deviceType | platform.app/browser | Notes |
|
|
23
|
+
* |-----------------------------|-----------|---------------------|----------------------|-------|
|
|
24
|
+
* | Desktop browser | `web` | `desktop` | `browser: Chrome` | - |
|
|
25
|
+
* | Mobile browser (Safari) | `web` | `phone` | `browser: Safari` | NOT 'mobile' channel |
|
|
26
|
+
* | Native iOS app | `mobile` | `phone` | `app: MyApp` | Requires x-source-channel |
|
|
27
|
+
* | Native Android app | `mobile` | `phone` | `app: MyApp` | Requires x-source-channel |
|
|
28
|
+
* | Partner API integration | `api` | - | - | - |
|
|
29
|
+
* | Admin creates user | `admin` | - | - | - |
|
|
30
|
+
* | Webhook from Stripe | `webhook` | - | - | - |
|
|
30
31
|
*
|
|
31
32
|
* @example
|
|
32
33
|
* // Simple usage - just channel
|
|
@@ -43,6 +44,10 @@
|
|
|
43
44
|
*/
|
|
44
45
|
/**
|
|
45
46
|
* Primary ingestion channels for data source tracking.
|
|
47
|
+
*
|
|
48
|
+
* IMPORTANT:
|
|
49
|
+
* - 'web' = ANY browser (desktop OR mobile) - use platform.deviceType to distinguish
|
|
50
|
+
* - 'mobile' = Native apps ONLY (iOS/Android) - must include platform.app
|
|
46
51
|
*/
|
|
47
52
|
export declare const DATA_SOURCE_CHANNELS: readonly ["web", "mobile", "api", "admin", "system", "import", "webhook", "sync", "migration", "unknown"];
|
|
48
53
|
export type DataSourceChannel = typeof DATA_SOURCE_CHANNELS[number];
|
|
@@ -51,11 +56,19 @@ export type DataSourceChannel = typeof DATA_SOURCE_CHANNELS[number];
|
|
|
51
56
|
*
|
|
52
57
|
* Provides both simple channel categorization and optional
|
|
53
58
|
* detailed attribution fields for analytics.
|
|
59
|
+
*
|
|
60
|
+
* CHANNEL USAGE:
|
|
61
|
+
* - 'web': ANY browser (desktop/mobile) - check platform.deviceType and platform.browser
|
|
62
|
+
* - 'mobile': Native apps ONLY - check platform.app and platform.appVersion
|
|
63
|
+
* - Use platform field to get detailed device/environment information
|
|
54
64
|
*/
|
|
55
65
|
export interface DataSource {
|
|
56
66
|
/**
|
|
57
67
|
* Primary ingestion channel.
|
|
58
68
|
* Required field for basic source categorization.
|
|
69
|
+
*
|
|
70
|
+
* - 'web' = Browser (desktop or mobile) - use platform.deviceType to distinguish
|
|
71
|
+
* - 'mobile' = Native mobile app - requires platform.app to be set
|
|
59
72
|
*/
|
|
60
73
|
channel: DataSourceChannel;
|
|
61
74
|
/**
|
|
@@ -83,6 +96,19 @@ export interface DataSource {
|
|
|
83
96
|
* SDK provides this - backend stores as-is.
|
|
84
97
|
*/
|
|
85
98
|
platform?: Platform;
|
|
99
|
+
/**
|
|
100
|
+
* Source of platform data.
|
|
101
|
+
* Indicates whether platform info came from SDK headers or auto-detection.
|
|
102
|
+
*
|
|
103
|
+
* - 'sdk': Explicitly sent via x-platform header (authoritative, trusted)
|
|
104
|
+
* - 'auto': Parsed from User-Agent header (best-effort, may be inaccurate)
|
|
105
|
+
*
|
|
106
|
+
* Use this to:
|
|
107
|
+
* - Identify incomplete SDK integrations (platformSource='auto' or missing)
|
|
108
|
+
* - Filter for high-quality data (platformSource='sdk')
|
|
109
|
+
* - Track SDK adoption progress
|
|
110
|
+
*/
|
|
111
|
+
platformSource?: 'sdk' | 'auto';
|
|
86
112
|
}
|
|
87
113
|
/**
|
|
88
114
|
* Platform/device information for analytics.
|
|
@@ -135,17 +161,37 @@ export declare const KNOWN_SOURCE_DOMAINS: Record<string, string>;
|
|
|
135
161
|
* detectSourceFromReferrer('https://partner.example.com') // 'example'
|
|
136
162
|
*/
|
|
137
163
|
export declare function detectSourceFromReferrer(referrer?: string): string | undefined;
|
|
164
|
+
/**
|
|
165
|
+
* Parse User-Agent string for platform information (fallback when SDK doesn't provide).
|
|
166
|
+
*
|
|
167
|
+
* Extracts OS, browser, device type from standard User-Agent patterns.
|
|
168
|
+
* This is best-effort - SDK headers are authoritative.
|
|
169
|
+
*
|
|
170
|
+
* @param userAgent - User-Agent header value
|
|
171
|
+
* @returns Platform object with detected fields, or undefined if unparseable
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* parseUserAgentForPlatform('Mozilla/5.0 (iPhone; CPU iPhone OS 17_4...')
|
|
175
|
+
* // { os: 'iOS', osVersion: '17.4', browser: 'Safari', deviceType: 'phone' }
|
|
176
|
+
*/
|
|
177
|
+
export declare function parseUserAgentForPlatform(userAgent?: string): Platform | undefined;
|
|
138
178
|
/**
|
|
139
179
|
* Auto-detect channel from User-Agent string.
|
|
140
180
|
*
|
|
181
|
+
* IMPORTANT DESIGN:
|
|
182
|
+
* - Mobile browsers (Safari on iPhone, Chrome on Android) → 'web' (NOT 'mobile')
|
|
183
|
+
* - Native apps MUST send explicit x-source-channel: mobile header
|
|
184
|
+
* - Use platform.deviceType to distinguish mobile web from desktop web
|
|
185
|
+
*
|
|
141
186
|
* @param userAgent - User-Agent header value
|
|
142
187
|
* @param path - Optional request path for webhook detection
|
|
143
188
|
* @returns Detected channel
|
|
144
189
|
*
|
|
145
190
|
* @example
|
|
146
|
-
* detectChannelFromUserAgent('Mozilla/5.0 (iPhone; ...)') // 'mobile
|
|
147
|
-
* detectChannelFromUserAgent('Mozilla/5.0 (Windows NT...)') // 'web'
|
|
191
|
+
* detectChannelFromUserAgent('Mozilla/5.0 (iPhone; ...) Safari') // 'web' (mobile browser)
|
|
192
|
+
* detectChannelFromUserAgent('Mozilla/5.0 (Windows NT...)') // 'web' (desktop browser)
|
|
148
193
|
* detectChannelFromUserAgent('', '/webhook/stripe') // 'webhook'
|
|
194
|
+
* detectChannelFromUserAgent('MyApp/1.0') // 'unknown' (native app without header)
|
|
149
195
|
*/
|
|
150
196
|
export declare function detectChannelFromUserAgent(userAgent?: string, path?: string): DataSourceChannel;
|
|
151
197
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-source.type.d.ts","sourceRoot":"","sources":["../../../../src/shared-lib/types/data-source.type.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"data-source.type.d.ts","sourceRoot":"","sources":["../../../../src/shared-lib/types/data-source.type.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,14 +19,15 @@
|
|
|
19
19
|
*
|
|
20
20
|
* ## Example Scenarios
|
|
21
21
|
*
|
|
22
|
-
* | Scenario | channel |
|
|
23
|
-
*
|
|
24
|
-
* |
|
|
25
|
-
* | Mobile
|
|
26
|
-
* |
|
|
27
|
-
* |
|
|
28
|
-
* |
|
|
29
|
-
* |
|
|
22
|
+
* | Scenario | channel | platform.deviceType | platform.app/browser | Notes |
|
|
23
|
+
* |-----------------------------|-----------|---------------------|----------------------|-------|
|
|
24
|
+
* | Desktop browser | `web` | `desktop` | `browser: Chrome` | - |
|
|
25
|
+
* | Mobile browser (Safari) | `web` | `phone` | `browser: Safari` | NOT 'mobile' channel |
|
|
26
|
+
* | Native iOS app | `mobile` | `phone` | `app: MyApp` | Requires x-source-channel |
|
|
27
|
+
* | Native Android app | `mobile` | `phone` | `app: MyApp` | Requires x-source-channel |
|
|
28
|
+
* | Partner API integration | `api` | - | - | - |
|
|
29
|
+
* | Admin creates user | `admin` | - | - | - |
|
|
30
|
+
* | Webhook from Stripe | `webhook` | - | - | - |
|
|
30
31
|
*
|
|
31
32
|
* @example
|
|
32
33
|
* // Simple usage - just channel
|
|
@@ -43,10 +44,14 @@
|
|
|
43
44
|
*/
|
|
44
45
|
/**
|
|
45
46
|
* Primary ingestion channels for data source tracking.
|
|
47
|
+
*
|
|
48
|
+
* IMPORTANT:
|
|
49
|
+
* - 'web' = ANY browser (desktop OR mobile) - use platform.deviceType to distinguish
|
|
50
|
+
* - 'mobile' = Native apps ONLY (iOS/Android) - must include platform.app
|
|
46
51
|
*/
|
|
47
52
|
export const DATA_SOURCE_CHANNELS = [
|
|
48
|
-
'web', // Web
|
|
49
|
-
'mobile', //
|
|
53
|
+
'web', // Web browser (desktop or mobile) - check platform.deviceType
|
|
54
|
+
'mobile', // Native mobile app ONLY (iOS/Android) - requires platform.app
|
|
50
55
|
'api', // Direct API integration
|
|
51
56
|
'admin', // Admin panel/back-office
|
|
52
57
|
'system', // System-generated (automated)
|
|
@@ -174,17 +179,114 @@ export function detectSourceFromReferrer(referrer) {
|
|
|
174
179
|
return undefined;
|
|
175
180
|
}
|
|
176
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Parse User-Agent string for platform information (fallback when SDK doesn't provide).
|
|
184
|
+
*
|
|
185
|
+
* Extracts OS, browser, device type from standard User-Agent patterns.
|
|
186
|
+
* This is best-effort - SDK headers are authoritative.
|
|
187
|
+
*
|
|
188
|
+
* @param userAgent - User-Agent header value
|
|
189
|
+
* @returns Platform object with detected fields, or undefined if unparseable
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* parseUserAgentForPlatform('Mozilla/5.0 (iPhone; CPU iPhone OS 17_4...')
|
|
193
|
+
* // { os: 'iOS', osVersion: '17.4', browser: 'Safari', deviceType: 'phone' }
|
|
194
|
+
*/
|
|
195
|
+
export function parseUserAgentForPlatform(userAgent) {
|
|
196
|
+
if (!userAgent)
|
|
197
|
+
return undefined;
|
|
198
|
+
const ua = userAgent;
|
|
199
|
+
const platform = {};
|
|
200
|
+
// Detect OS and device type
|
|
201
|
+
if (/iPhone|iPod/i.test(ua)) {
|
|
202
|
+
platform.os = 'iOS';
|
|
203
|
+
const match = ua.match(/OS (\d+)[_\.](\d+)/);
|
|
204
|
+
if (match)
|
|
205
|
+
platform.osVersion = `${match[1]}.${match[2]}`;
|
|
206
|
+
platform.deviceType = 'phone';
|
|
207
|
+
}
|
|
208
|
+
else if (/iPad/i.test(ua)) {
|
|
209
|
+
platform.os = 'iOS';
|
|
210
|
+
const match = ua.match(/OS (\d+)[_\.](\d+)/);
|
|
211
|
+
if (match)
|
|
212
|
+
platform.osVersion = `${match[1]}.${match[2]}`;
|
|
213
|
+
platform.deviceType = 'tablet';
|
|
214
|
+
}
|
|
215
|
+
else if (/Android/i.test(ua)) {
|
|
216
|
+
platform.os = 'Android';
|
|
217
|
+
const match = ua.match(/Android ([\d\.]+)/);
|
|
218
|
+
if (match)
|
|
219
|
+
platform.osVersion = match[1];
|
|
220
|
+
platform.deviceType = /Mobile/i.test(ua) ? 'phone' : 'tablet';
|
|
221
|
+
}
|
|
222
|
+
else if (/Windows NT/i.test(ua)) {
|
|
223
|
+
platform.os = 'Windows';
|
|
224
|
+
const match = ua.match(/Windows NT ([\d\.]+)/);
|
|
225
|
+
if (match)
|
|
226
|
+
platform.osVersion = match[1];
|
|
227
|
+
platform.deviceType = 'desktop';
|
|
228
|
+
}
|
|
229
|
+
else if (/Macintosh|Mac OS X/i.test(ua)) {
|
|
230
|
+
platform.os = 'macOS';
|
|
231
|
+
const match = ua.match(/Mac OS X ([\d_]+)/);
|
|
232
|
+
if (match)
|
|
233
|
+
platform.osVersion = match[1].replace(/_/g, '.');
|
|
234
|
+
platform.deviceType = 'desktop';
|
|
235
|
+
}
|
|
236
|
+
else if (/Linux/i.test(ua) && !/Android/i.test(ua)) {
|
|
237
|
+
platform.os = 'Linux';
|
|
238
|
+
platform.deviceType = 'desktop';
|
|
239
|
+
}
|
|
240
|
+
// Detect browser (web channel)
|
|
241
|
+
if (/Edg\//i.test(ua)) {
|
|
242
|
+
platform.browser = 'Edge';
|
|
243
|
+
const match = ua.match(/Edg\/([\d\.]+)/);
|
|
244
|
+
if (match)
|
|
245
|
+
platform.browserVersion = match[1];
|
|
246
|
+
}
|
|
247
|
+
else if (/Chrome\//i.test(ua) && !/Edg/i.test(ua)) {
|
|
248
|
+
platform.browser = 'Chrome';
|
|
249
|
+
const match = ua.match(/Chrome\/([\d\.]+)/);
|
|
250
|
+
if (match)
|
|
251
|
+
platform.browserVersion = match[1];
|
|
252
|
+
}
|
|
253
|
+
else if (/Safari\//i.test(ua) && !/Chrome/i.test(ua)) {
|
|
254
|
+
platform.browser = 'Safari';
|
|
255
|
+
const match = ua.match(/Version\/([\d\.]+)/);
|
|
256
|
+
if (match)
|
|
257
|
+
platform.browserVersion = match[1];
|
|
258
|
+
}
|
|
259
|
+
else if (/Firefox\//i.test(ua)) {
|
|
260
|
+
platform.browser = 'Firefox';
|
|
261
|
+
const match = ua.match(/Firefox\/([\d\.]+)/);
|
|
262
|
+
if (match)
|
|
263
|
+
platform.browserVersion = match[1];
|
|
264
|
+
}
|
|
265
|
+
else if (/Opera|OPR\//i.test(ua)) {
|
|
266
|
+
platform.browser = 'Opera';
|
|
267
|
+
const match = ua.match(/(?:Opera|OPR)\/([\d\.]+)/);
|
|
268
|
+
if (match)
|
|
269
|
+
platform.browserVersion = match[1];
|
|
270
|
+
}
|
|
271
|
+
return Object.keys(platform).length > 0 ? platform : undefined;
|
|
272
|
+
}
|
|
177
273
|
/**
|
|
178
274
|
* Auto-detect channel from User-Agent string.
|
|
179
275
|
*
|
|
276
|
+
* IMPORTANT DESIGN:
|
|
277
|
+
* - Mobile browsers (Safari on iPhone, Chrome on Android) → 'web' (NOT 'mobile')
|
|
278
|
+
* - Native apps MUST send explicit x-source-channel: mobile header
|
|
279
|
+
* - Use platform.deviceType to distinguish mobile web from desktop web
|
|
280
|
+
*
|
|
180
281
|
* @param userAgent - User-Agent header value
|
|
181
282
|
* @param path - Optional request path for webhook detection
|
|
182
283
|
* @returns Detected channel
|
|
183
284
|
*
|
|
184
285
|
* @example
|
|
185
|
-
* detectChannelFromUserAgent('Mozilla/5.0 (iPhone; ...)') // 'mobile
|
|
186
|
-
* detectChannelFromUserAgent('Mozilla/5.0 (Windows NT...)') // 'web'
|
|
286
|
+
* detectChannelFromUserAgent('Mozilla/5.0 (iPhone; ...) Safari') // 'web' (mobile browser)
|
|
287
|
+
* detectChannelFromUserAgent('Mozilla/5.0 (Windows NT...)') // 'web' (desktop browser)
|
|
187
288
|
* detectChannelFromUserAgent('', '/webhook/stripe') // 'webhook'
|
|
289
|
+
* detectChannelFromUserAgent('MyApp/1.0') // 'unknown' (native app without header)
|
|
188
290
|
*/
|
|
189
291
|
export function detectChannelFromUserAgent(userAgent, path) {
|
|
190
292
|
// Check for webhook paths
|
|
@@ -192,16 +294,18 @@ export function detectChannelFromUserAgent(userAgent, path) {
|
|
|
192
294
|
return 'webhook';
|
|
193
295
|
}
|
|
194
296
|
const ua = (userAgent || '').toLowerCase();
|
|
195
|
-
// Check for mobile user-agents
|
|
196
|
-
if (/mobile|android|iphone|ipad|ipod|blackberry|windows phone/i.test(ua)) {
|
|
197
|
-
return 'mobile';
|
|
198
|
-
}
|
|
199
297
|
// Check for bot/crawler patterns (system)
|
|
200
298
|
if (/bot|crawler|spider|scraper|curl|wget|python|node-fetch|axios/i.test(ua)) {
|
|
201
299
|
return 'api';
|
|
202
300
|
}
|
|
203
|
-
//
|
|
204
|
-
|
|
301
|
+
// Check for ANY browser pattern (desktop OR mobile)
|
|
302
|
+
// Mobile browsers are 'web' channel - use platform.deviceType to distinguish
|
|
303
|
+
if (ua && /mozilla|chrome|safari|firefox|edge|opera|msie|trident/i.test(ua)) {
|
|
304
|
+
return 'web';
|
|
305
|
+
}
|
|
306
|
+
// Unknown: Native apps MUST send x-source-channel: mobile header
|
|
307
|
+
// If we see 'unknown', it means SDK integration is incomplete
|
|
308
|
+
return 'unknown';
|
|
205
309
|
}
|
|
206
310
|
/**
|
|
207
311
|
* Parse UTM parameters from a URL (deep link, referrer, etc.).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-source.type.js","sourceRoot":"","sources":["../../../../src/shared-lib/types/data-source.type.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"data-source.type.js","sourceRoot":"","sources":["../../../../src/shared-lib/types/data-source.type.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/shared-lib/validation/index.ts"],"names":[],"mappings":"AACA,cAAc,oBAAoB,CAAC;AAGnC,cAAc,cAAc,CAAC;AAG7B,cAAc,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/shared-lib/validation/index.ts"],"names":[],"mappings":"AACA,cAAc,oBAAoB,CAAC;AAGnC,cAAc,cAAc,CAAC;AAG7B,cAAc,oBAAoB,CAAC;AAGnC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,0BAA0B,CAAC"}
|
|
@@ -4,4 +4,8 @@ export * from './validation.types.js';
|
|
|
4
4
|
export * from './validators.js';
|
|
5
5
|
// Field validation functions (also re-exports types and validators)
|
|
6
6
|
export * from './field-validation.js';
|
|
7
|
+
// Status hierarchy resolution (pure)
|
|
8
|
+
export * from './status-hierarchy.js';
|
|
9
|
+
// Redemption eligibility types + pure aggregator
|
|
10
|
+
export * from './redemption-eligibility.js';
|
|
7
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/shared-lib/validation/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,cAAc,oBAAoB,CAAC;AAEnC,wBAAwB;AACxB,cAAc,cAAc,CAAC;AAE7B,oEAAoE;AACpE,cAAc,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/shared-lib/validation/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,cAAc,oBAAoB,CAAC;AAEnC,wBAAwB;AACxB,cAAc,cAAc,CAAC;AAE7B,oEAAoE;AACpE,cAAc,oBAAoB,CAAC;AAEnC,qCAAqC;AACrC,cAAc,oBAAoB,CAAC;AAEnC,iDAAiD;AACjD,cAAc,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redemption eligibility (pure, framework-agnostic).
|
|
3
|
+
*
|
|
4
|
+
* Specializes the generic {@link Eligibility} primitive for the redeem-time gates
|
|
5
|
+
* enforced by the backend redemption workflow, and provides a pure aggregator so
|
|
6
|
+
* backend and frontend can derive the same verdict from already-known signals.
|
|
7
|
+
*
|
|
8
|
+
* NOTE: the live credit-token balance gate is intentionally excluded — it is
|
|
9
|
+
* volatile and validated at redeem time, not as a pre-flight check.
|
|
10
|
+
*/
|
|
11
|
+
import type { Eligibility, EligibilityCheck } from "../interfaces/eligibility.interface";
|
|
12
|
+
import { type StatusHierarchyNode } from "./status-hierarchy";
|
|
13
|
+
import type { UserStatusTypeDTO } from "../dto/user-status-type.dto";
|
|
14
|
+
/** Minimal user shape needed to evaluate eligibility. */
|
|
15
|
+
type EligibilityUser = {
|
|
16
|
+
isActive?: boolean;
|
|
17
|
+
included?: {
|
|
18
|
+
statusTypes?: UserStatusTypeDTO[];
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
/** Minimal redemption shape needed to evaluate eligibility. */
|
|
22
|
+
type EligibilityRedemption = {
|
|
23
|
+
maxPerUser?: number | null;
|
|
24
|
+
minUserStatusType?: UserStatusTypeDTO | null;
|
|
25
|
+
included?: {
|
|
26
|
+
requiredUserInfo?: {
|
|
27
|
+
isValid: boolean;
|
|
28
|
+
fieldDefinitions?: Array<{
|
|
29
|
+
isMissing?: boolean;
|
|
30
|
+
key?: string;
|
|
31
|
+
id?: string;
|
|
32
|
+
}>;
|
|
33
|
+
} | null;
|
|
34
|
+
booking?: {
|
|
35
|
+
isValid: boolean;
|
|
36
|
+
} | null;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Canonical list of redemption eligibility gates.
|
|
41
|
+
*
|
|
42
|
+
* ⚠️ SYNC CONTRACT: These gates must mirror the validation steps in
|
|
43
|
+
* `RedemptionRedeemWorkflow` (backend). If you add or remove a gate here,
|
|
44
|
+
* update the workflow too — and vice versa.
|
|
45
|
+
*
|
|
46
|
+
* Use this constant in tests to assert that `evaluateRedemptionEligibility`
|
|
47
|
+
* returns exactly these keys (no more, no less):
|
|
48
|
+
*
|
|
49
|
+
* expect(Object.keys(result.checks)).toEqual([...REDEMPTION_ELIGIBILITY_GATES]);
|
|
50
|
+
*/
|
|
51
|
+
export declare const REDEMPTION_ELIGIBILITY_GATES: readonly ["userActive", "requiredFields", "booking", "userStatus", "maxPerUser"];
|
|
52
|
+
export type RedemptionEligibilityGate = typeof REDEMPTION_ELIGIBILITY_GATES[number];
|
|
53
|
+
/**
|
|
54
|
+
* Stable machine-readable codes for a failed redemption eligibility check.
|
|
55
|
+
*/
|
|
56
|
+
export type RedemptionEligibilityReasonCode = 'USER_INACTIVE' | 'MISSING_REQUIRED_FIELDS' | 'BOOKING_REQUIRED' | 'INSUFFICIENT_USER_STATUS' | 'MAX_PER_USER_REACHED';
|
|
57
|
+
/** Structured detail emitted by redemption eligibility gates on failure. */
|
|
58
|
+
export type RedemptionEligibilityDetail = {
|
|
59
|
+
missingFieldKeys: string[];
|
|
60
|
+
} | {
|
|
61
|
+
minRequiredStatusTypeId: number;
|
|
62
|
+
userStatusTypeIds: number[];
|
|
63
|
+
} | {
|
|
64
|
+
maxPerUser: number;
|
|
65
|
+
userRedeemCount: number;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Result of a single redemption eligibility gate.
|
|
69
|
+
* Specializes the generic {@link EligibilityCheck} with redemption-specific codes/detail.
|
|
70
|
+
*/
|
|
71
|
+
export type RedemptionEligibilityCheck = EligibilityCheck<RedemptionEligibilityReasonCode, RedemptionEligibilityDetail>;
|
|
72
|
+
/**
|
|
73
|
+
* Aggregate pre-flight eligibility verdict for a user redeeming a redemption.
|
|
74
|
+
* Specializes the generic {@link Eligibility} with the redeem-time gates.
|
|
75
|
+
* Excludes the live credit-token balance check (validated at redeem time).
|
|
76
|
+
*/
|
|
77
|
+
export type RedemptionEligibility = Eligibility<{
|
|
78
|
+
userActive: RedemptionEligibilityCheck;
|
|
79
|
+
requiredFields: RedemptionEligibilityCheck;
|
|
80
|
+
booking: RedemptionEligibilityCheck;
|
|
81
|
+
userStatus: RedemptionEligibilityCheck;
|
|
82
|
+
maxPerUser: RedemptionEligibilityCheck;
|
|
83
|
+
}>;
|
|
84
|
+
/** Signals required to derive a redemption eligibility verdict. */
|
|
85
|
+
export interface RedemptionEligibilityInput {
|
|
86
|
+
user: EligibilityUser;
|
|
87
|
+
redemption: EligibilityRedemption;
|
|
88
|
+
/** All status types — needed for hierarchical status comparison. */
|
|
89
|
+
allStatusTypes: StatusHierarchyNode[];
|
|
90
|
+
/**
|
|
91
|
+
* How many times this user has already redeemed this redemption.
|
|
92
|
+
* Required for the maxPerUser gate. Fetch from GET /redemptions/redeems?userId=&redemptionId=
|
|
93
|
+
*/
|
|
94
|
+
userRedeemCount: number;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Derives an aggregate redemption eligibility verdict from the user, redemption and status list.
|
|
98
|
+
*
|
|
99
|
+
* Pure and side-effect free: pass the DTOs directly from your API responses; the function
|
|
100
|
+
* computes all per-gate verdicts (including status hierarchy and maxPerUser math) internally.
|
|
101
|
+
*
|
|
102
|
+
* Requires `?include=requiredUserInfo,booking` on the redemption fetch and
|
|
103
|
+
* `?include=status` on the user fetch for full accuracy.
|
|
104
|
+
*/
|
|
105
|
+
export declare function evaluateRedemptionEligibility(input: RedemptionEligibilityInput): RedemptionEligibility;
|
|
106
|
+
export {};
|
|
107
|
+
//# sourceMappingURL=redemption-eligibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redemption-eligibility.d.ts","sourceRoot":"","sources":["../../../../src/shared-lib/validation/redemption-eligibility.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AACzF,OAAO,EAAiC,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC7F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE,yDAAyD;AACzD,KAAK,eAAe,GAAG;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,iBAAiB,EAAE,CAAA;KAAE,CAAC;CACpD,CAAC;AAEF,+DAA+D;AAC/D,KAAK,qBAAqB,GAAG;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC7C,QAAQ,CAAC,EAAE;QACP,gBAAgB,CAAC,EAAE;YAAE,OAAO,EAAE,OAAO,CAAC;YAAC,gBAAgB,CAAC,EAAE,KAAK,CAAC;gBAAE,SAAS,CAAC,EAAE,OAAO,CAAC;gBAAC,GAAG,CAAC,EAAE,MAAM,CAAC;gBAAC,EAAE,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC,CAAA;SAAE,GAAG,IAAI,CAAC;QAC7H,OAAO,CAAC,EAAE;YAAE,OAAO,EAAE,OAAO,CAAA;SAAE,GAAG,IAAI,CAAC;KACzC,CAAC;CACL,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,4BAA4B,kFAM/B,CAAC;AAEX,MAAM,MAAM,yBAAyB,GAAG,OAAO,4BAA4B,CAAC,MAAM,CAAC,CAAC;AAEpF;;GAEG;AACH,MAAM,MAAM,+BAA+B,GACrC,eAAe,GACf,yBAAyB,GACzB,kBAAkB,GAClB,0BAA0B,GAC1B,sBAAsB,CAAC;AAE7B,4EAA4E;AAC5E,MAAM,MAAM,2BAA2B,GACjC;IAAE,gBAAgB,EAAE,MAAM,EAAE,CAAA;CAAE,GAC9B;IAAE,uBAAuB,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,EAAE,CAAA;CAAE,GAChE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD;;;GAGG;AACH,MAAM,MAAM,0BAA0B,GAAG,gBAAgB,CAAC,+BAA+B,EAAE,2BAA2B,CAAC,CAAC;AAExH;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,WAAW,CAAC;IAC5C,UAAU,EAAE,0BAA0B,CAAC;IACvC,cAAc,EAAE,0BAA0B,CAAC;IAC3C,OAAO,EAAE,0BAA0B,CAAC;IACpC,UAAU,EAAE,0BAA0B,CAAC;IACvC,UAAU,EAAE,0BAA0B,CAAC;CAC1C,CAAC,CAAC;AAEH,mEAAmE;AACnE,MAAM,WAAW,0BAA0B;IACvC,IAAI,EAAE,eAAe,CAAC;IACtB,UAAU,EAAE,qBAAqB,CAAC;IAClC,oEAAoE;IACpE,cAAc,EAAE,mBAAmB,EAAE,CAAC;IACtC;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;CAC3B;AAaD;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,0BAA0B,GAAG,qBAAqB,CA2DtG"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { meetsMinimumStatusRequirement } from "./status-hierarchy.js";
|
|
2
|
+
/**
|
|
3
|
+
* Canonical list of redemption eligibility gates.
|
|
4
|
+
*
|
|
5
|
+
* ⚠️ SYNC CONTRACT: These gates must mirror the validation steps in
|
|
6
|
+
* `RedemptionRedeemWorkflow` (backend). If you add or remove a gate here,
|
|
7
|
+
* update the workflow too — and vice versa.
|
|
8
|
+
*
|
|
9
|
+
* Use this constant in tests to assert that `evaluateRedemptionEligibility`
|
|
10
|
+
* returns exactly these keys (no more, no less):
|
|
11
|
+
*
|
|
12
|
+
* expect(Object.keys(result.checks)).toEqual([...REDEMPTION_ELIGIBILITY_GATES]);
|
|
13
|
+
*/
|
|
14
|
+
export const REDEMPTION_ELIGIBILITY_GATES = [
|
|
15
|
+
'userActive',
|
|
16
|
+
'requiredFields',
|
|
17
|
+
'booking',
|
|
18
|
+
'userStatus',
|
|
19
|
+
'maxPerUser',
|
|
20
|
+
];
|
|
21
|
+
function makeCheck(passed, code, message, detail) {
|
|
22
|
+
return passed
|
|
23
|
+
? { passed: true, reason: null }
|
|
24
|
+
: { passed: false, reason: { code, message }, ...(detail ? { detail } : {}) };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Derives an aggregate redemption eligibility verdict from the user, redemption and status list.
|
|
28
|
+
*
|
|
29
|
+
* Pure and side-effect free: pass the DTOs directly from your API responses; the function
|
|
30
|
+
* computes all per-gate verdicts (including status hierarchy and maxPerUser math) internally.
|
|
31
|
+
*
|
|
32
|
+
* Requires `?include=requiredUserInfo,booking` on the redemption fetch and
|
|
33
|
+
* `?include=status` on the user fetch for full accuracy.
|
|
34
|
+
*/
|
|
35
|
+
export function evaluateRedemptionEligibility(input) {
|
|
36
|
+
const { user, redemption, allStatusTypes, userRedeemCount } = input;
|
|
37
|
+
const userStatusTypeIds = (user.included?.statusTypes ?? []).map(s => s.id).filter((id) => id !== undefined);
|
|
38
|
+
const minUserStatusTypeId = redemption.minUserStatusType?.id ?? null;
|
|
39
|
+
const requiredUserInfo = redemption.included?.requiredUserInfo;
|
|
40
|
+
const booking = redemption.included?.booking;
|
|
41
|
+
// userStatus — delegate to shared pure hierarchy check
|
|
42
|
+
const userStatusPassed = !minUserStatusTypeId ||
|
|
43
|
+
meetsMinimumStatusRequirement(minUserStatusTypeId, userStatusTypeIds, allStatusTypes);
|
|
44
|
+
// maxPerUser — pure numeric comparison
|
|
45
|
+
const maxPerUserPassed = !redemption.maxPerUser || userRedeemCount < redemption.maxPerUser;
|
|
46
|
+
// requiredFields — use isValid from the include; derive missing keys from field definitions
|
|
47
|
+
const requiredFieldsPassed = !requiredUserInfo || requiredUserInfo.isValid;
|
|
48
|
+
const missingFieldKeys = requiredUserInfo?.fieldDefinitions
|
|
49
|
+
?.filter(f => f.isMissing)
|
|
50
|
+
.map(f => f.key ?? f.id ?? '')
|
|
51
|
+
.filter(Boolean) ?? [];
|
|
52
|
+
const checks = {
|
|
53
|
+
userActive: makeCheck(user.isActive !== false, 'USER_INACTIVE', 'User account is inactive'),
|
|
54
|
+
requiredFields: makeCheck(requiredFieldsPassed, 'MISSING_REQUIRED_FIELDS', 'User is missing one or more required fields', requiredFieldsPassed ? undefined : { missingFieldKeys }),
|
|
55
|
+
booking: makeCheck(!booking || booking.isValid, 'BOOKING_REQUIRED', 'User does not have a valid booking meeting the requirements'),
|
|
56
|
+
userStatus: makeCheck(userStatusPassed, 'INSUFFICIENT_USER_STATUS', 'User does not meet the minimum status requirement', userStatusPassed || !minUserStatusTypeId ? undefined : {
|
|
57
|
+
minRequiredStatusTypeId: minUserStatusTypeId,
|
|
58
|
+
userStatusTypeIds,
|
|
59
|
+
}),
|
|
60
|
+
maxPerUser: makeCheck(maxPerUserPassed, 'MAX_PER_USER_REACHED', 'User has reached the maximum number of redeems allowed', maxPerUserPassed || !redemption.maxPerUser ? undefined : {
|
|
61
|
+
maxPerUser: redemption.maxPerUser,
|
|
62
|
+
userRedeemCount,
|
|
63
|
+
}),
|
|
64
|
+
};
|
|
65
|
+
return {
|
|
66
|
+
isEligible: Object.values(checks).every(c => c.passed),
|
|
67
|
+
checks,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=redemption-eligibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redemption-eligibility.js","sourceRoot":"","sources":["../../../../src/shared-lib/validation/redemption-eligibility.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User status hierarchy resolution (pure, framework-agnostic).
|
|
3
|
+
*
|
|
4
|
+
* Centralizes the rule for "does a user's earned status satisfy a minimum
|
|
5
|
+
* requirement?" so backend and frontend evaluate it identically.
|
|
6
|
+
*
|
|
7
|
+
* Hierarchy semantics:
|
|
8
|
+
* - Exact match always qualifies.
|
|
9
|
+
* - Otherwise a user qualifies if they hold a status in the SAME hierarchy with
|
|
10
|
+
* an `order` greater than or equal to the required status `order`.
|
|
11
|
+
* - Hierarchies (tracks) are separated by tags: statuses with no tags share the
|
|
12
|
+
* global hierarchy; tagged statuses share a track only when their tags overlap.
|
|
13
|
+
* - A required status without an `order` requires an exact match (backward compatible).
|
|
14
|
+
*/
|
|
15
|
+
/** Minimal status-type shape needed to resolve hierarchy. */
|
|
16
|
+
export interface StatusHierarchyNode {
|
|
17
|
+
id: number;
|
|
18
|
+
/** Higher = more prestigious. Null/undefined = no hierarchy ranking. */
|
|
19
|
+
order?: number | null;
|
|
20
|
+
/** Track tags. Empty/undefined = global hierarchy. */
|
|
21
|
+
tags?: string[] | null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Determines whether a user meets a minimum status requirement, honoring the
|
|
25
|
+
* tag-scoped status hierarchy.
|
|
26
|
+
*
|
|
27
|
+
* @param requiredStatusId - The minimum status type ID required.
|
|
28
|
+
* @param userStatusIds - Status type IDs the user has earned.
|
|
29
|
+
* @param allStatusTypes - All status types (must include the required and user statuses).
|
|
30
|
+
* @returns True if the user satisfies the requirement (exact match or higher in same track).
|
|
31
|
+
*/
|
|
32
|
+
export declare function meetsMinimumStatusRequirement(requiredStatusId: number, userStatusIds: number[], allStatusTypes: StatusHierarchyNode[]): boolean;
|
|
33
|
+
//# sourceMappingURL=status-hierarchy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-hierarchy.d.ts","sourceRoot":"","sources":["../../../../src/shared-lib/validation/status-hierarchy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,6DAA6D;AAC7D,MAAM,WAAW,mBAAmB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAC1B;AAYD;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CACzC,gBAAgB,EAAE,MAAM,EACxB,aAAa,EAAE,MAAM,EAAE,EACvB,cAAc,EAAE,mBAAmB,EAAE,GACtC,OAAO,CA4CT"}
|