@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.
Files changed (79) hide show
  1. package/dist/cjs/shared-lib/dto/business/businessTypeCreate.request.dto.d.ts +1 -1
  2. package/dist/cjs/shared-lib/dto/business/businessTypeCreate.request.dto.d.ts.map +1 -1
  3. package/dist/cjs/shared-lib/dto/business/businessTypeCreate.request.dto.js +2 -2
  4. package/dist/cjs/shared-lib/dto/business/businessTypeCreate.request.dto.js.map +1 -1
  5. package/dist/cjs/shared-lib/dto/redemption/redemptionRedeem.request.dto.d.ts.map +1 -1
  6. package/dist/cjs/shared-lib/dto/redemption/redemptionRedeem.request.dto.js +11 -1
  7. package/dist/cjs/shared-lib/dto/redemption/redemptionRedeem.request.dto.js.map +1 -1
  8. package/dist/cjs/shared-lib/enum/signing-account-provider.enum.d.ts +1 -0
  9. package/dist/cjs/shared-lib/enum/signing-account-provider.enum.d.ts.map +1 -1
  10. package/dist/cjs/shared-lib/enum/signing-account-provider.enum.js +1 -0
  11. package/dist/cjs/shared-lib/enum/signing-account-provider.enum.js.map +1 -1
  12. package/dist/cjs/shared-lib/interfaces/eligibility.interface.d.ts +38 -0
  13. package/dist/cjs/shared-lib/interfaces/eligibility.interface.d.ts.map +1 -0
  14. package/dist/cjs/shared-lib/interfaces/eligibility.interface.js +10 -0
  15. package/dist/cjs/shared-lib/interfaces/eligibility.interface.js.map +1 -0
  16. package/dist/cjs/shared-lib/interfaces/eligible-business-summary.interface.d.ts +3 -0
  17. package/dist/cjs/shared-lib/interfaces/eligible-business-summary.interface.d.ts.map +1 -1
  18. package/dist/cjs/shared-lib/interfaces/index.d.ts +1 -0
  19. package/dist/cjs/shared-lib/interfaces/index.d.ts.map +1 -1
  20. package/dist/cjs/shared-lib/interfaces/index.js +1 -0
  21. package/dist/cjs/shared-lib/interfaces/index.js.map +1 -1
  22. package/dist/cjs/shared-lib/types/data-source.type.d.ts +56 -10
  23. package/dist/cjs/shared-lib/types/data-source.type.d.ts.map +1 -1
  24. package/dist/cjs/shared-lib/types/data-source.type.js +123 -18
  25. package/dist/cjs/shared-lib/types/data-source.type.js.map +1 -1
  26. package/dist/cjs/shared-lib/validation/index.d.ts +2 -0
  27. package/dist/cjs/shared-lib/validation/index.d.ts.map +1 -1
  28. package/dist/cjs/shared-lib/validation/index.js +4 -0
  29. package/dist/cjs/shared-lib/validation/index.js.map +1 -1
  30. package/dist/cjs/shared-lib/validation/redemption-eligibility.d.ts +107 -0
  31. package/dist/cjs/shared-lib/validation/redemption-eligibility.d.ts.map +1 -0
  32. package/dist/cjs/shared-lib/validation/redemption-eligibility.js +74 -0
  33. package/dist/cjs/shared-lib/validation/redemption-eligibility.js.map +1 -0
  34. package/dist/cjs/shared-lib/validation/status-hierarchy.d.ts +33 -0
  35. package/dist/cjs/shared-lib/validation/status-hierarchy.d.ts.map +1 -0
  36. package/dist/cjs/shared-lib/validation/status-hierarchy.js +73 -0
  37. package/dist/cjs/shared-lib/validation/status-hierarchy.js.map +1 -0
  38. package/dist/cjs/shared-patterns/functions/token-metadata.functions.js +2 -2
  39. package/dist/cjs/shared-patterns/functions/token-metadata.functions.js.map +1 -1
  40. package/dist/esm/shared-lib/dto/business/businessTypeCreate.request.dto.d.ts +1 -1
  41. package/dist/esm/shared-lib/dto/business/businessTypeCreate.request.dto.d.ts.map +1 -1
  42. package/dist/esm/shared-lib/dto/business/businessTypeCreate.request.dto.js +2 -2
  43. package/dist/esm/shared-lib/dto/business/businessTypeCreate.request.dto.js.map +1 -1
  44. package/dist/esm/shared-lib/dto/redemption/redemptionRedeem.request.dto.d.ts.map +1 -1
  45. package/dist/esm/shared-lib/dto/redemption/redemptionRedeem.request.dto.js +11 -1
  46. package/dist/esm/shared-lib/dto/redemption/redemptionRedeem.request.dto.js.map +1 -1
  47. package/dist/esm/shared-lib/enum/signing-account-provider.enum.d.ts +1 -0
  48. package/dist/esm/shared-lib/enum/signing-account-provider.enum.d.ts.map +1 -1
  49. package/dist/esm/shared-lib/enum/signing-account-provider.enum.js +1 -0
  50. package/dist/esm/shared-lib/enum/signing-account-provider.enum.js.map +1 -1
  51. package/dist/esm/shared-lib/interfaces/eligibility.interface.d.ts +38 -0
  52. package/dist/esm/shared-lib/interfaces/eligibility.interface.d.ts.map +1 -0
  53. package/dist/esm/shared-lib/interfaces/eligibility.interface.js +9 -0
  54. package/dist/esm/shared-lib/interfaces/eligibility.interface.js.map +1 -0
  55. package/dist/esm/shared-lib/interfaces/eligible-business-summary.interface.d.ts +3 -0
  56. package/dist/esm/shared-lib/interfaces/eligible-business-summary.interface.d.ts.map +1 -1
  57. package/dist/esm/shared-lib/interfaces/index.d.ts +1 -0
  58. package/dist/esm/shared-lib/interfaces/index.d.ts.map +1 -1
  59. package/dist/esm/shared-lib/interfaces/index.js +1 -0
  60. package/dist/esm/shared-lib/interfaces/index.js.map +1 -1
  61. package/dist/esm/shared-lib/types/data-source.type.d.ts +56 -10
  62. package/dist/esm/shared-lib/types/data-source.type.d.ts.map +1 -1
  63. package/dist/esm/shared-lib/types/data-source.type.js +122 -18
  64. package/dist/esm/shared-lib/types/data-source.type.js.map +1 -1
  65. package/dist/esm/shared-lib/validation/index.d.ts +2 -0
  66. package/dist/esm/shared-lib/validation/index.d.ts.map +1 -1
  67. package/dist/esm/shared-lib/validation/index.js +4 -0
  68. package/dist/esm/shared-lib/validation/index.js.map +1 -1
  69. package/dist/esm/shared-lib/validation/redemption-eligibility.d.ts +107 -0
  70. package/dist/esm/shared-lib/validation/redemption-eligibility.d.ts.map +1 -0
  71. package/dist/esm/shared-lib/validation/redemption-eligibility.js +70 -0
  72. package/dist/esm/shared-lib/validation/redemption-eligibility.js.map +1 -0
  73. package/dist/esm/shared-lib/validation/status-hierarchy.d.ts +33 -0
  74. package/dist/esm/shared-lib/validation/status-hierarchy.d.ts.map +1 -0
  75. package/dist/esm/shared-lib/validation/status-hierarchy.js +70 -0
  76. package/dist/esm/shared-lib/validation/status-hierarchy.js.map +1 -0
  77. package/dist/esm/shared-patterns/functions/token-metadata.functions.js +2 -2
  78. package/dist/esm/shared-patterns/functions/token-metadata.functions.js.map +1 -1
  79. package/package.json +1 -1
@@ -19,14 +19,15 @@
19
19
  *
20
20
  * ## Example Scenarios
21
21
  *
22
- * | Scenario | channel | medium | campaign | source |
23
- * |-----------------------------|-----------|------------|--------------|--------------|
24
- * | User clicks email link | `web` | `email` | `summer_promo`| `mailchimp` |
25
- * | Mobile app registration | `mobile` | `app` | - | - |
26
- * | Partner API integration | `api` | `affiliate`| - | `partner_xyz`|
27
- * | QR code scan in hotel | `mobile` | `referral` | `lobby_qr` | `qr_code` |
28
- * | Admin creates user | `admin` | - | - | - |
29
- * | Webhook from Stripe | `webhook` | - | - | `stripe` |
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":"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"}
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 | medium | campaign | source |
23
- * |-----------------------------|-----------|------------|--------------|--------------|
24
- * | User clicks email link | `web` | `email` | `summer_promo`| `mailchimp` |
25
- * | Mobile app registration | `mobile` | `app` | - | - |
26
- * | Partner API integration | `api` | `affiliate`| - | `partner_xyz`|
27
- * | QR code scan in hotel | `mobile` | `referral` | `lobby_qr` | `qr_code` |
28
- * | Admin creates user | `admin` | - | - | - |
29
- * | Webhook from Stripe | `webhook` | - | - | `stripe` |
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 application/browser
49
- 'mobile', // Mobile app (iOS/Android)
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
- // Default to web for browser-like user agents
204
- return 'web';
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":"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"}
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,4 +1,6 @@
1
1
  export * from './validation.types';
2
2
  export * from './validators';
3
3
  export * from './field-validation';
4
+ export * from './status-hierarchy';
5
+ export * from './redemption-eligibility';
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -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"}