@proveanything/smartlinks 1.3.26 → 1.3.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/dist/api/ai.d.ts +6 -380
- package/dist/api/ai.js +15 -15
- package/dist/api/index.d.ts +0 -1
- package/dist/docs/API_SUMMARY.md +2257 -2270
- package/dist/docs/utils.md +675 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/types/ai.d.ts +379 -0
- package/dist/types/ai.js +5 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/conditions.d.ts +287 -0
- package/dist/utils/conditions.js +453 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/paths.d.ts +95 -0
- package/dist/utils/paths.js +178 -0
- package/docs/API_SUMMARY.md +2257 -2270
- package/docs/utils.md +675 -0
- package/package.json +1 -1
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
// src/utils/conditions.ts
|
|
2
|
+
/**
|
|
3
|
+
* Geographic region definitions for country-based conditions
|
|
4
|
+
*/
|
|
5
|
+
export const REGION_COUNTRIES = {
|
|
6
|
+
// European Union (27 member states as of 2026)
|
|
7
|
+
eu: ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE'],
|
|
8
|
+
// European Economic Area (EU + EFTA countries in EEA)
|
|
9
|
+
eea: ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'IS', 'LI', 'NO'],
|
|
10
|
+
// United Kingdom
|
|
11
|
+
uk: ['GB'],
|
|
12
|
+
// North America
|
|
13
|
+
northamerica: ['US', 'CA', 'MX'],
|
|
14
|
+
// Asia Pacific (major markets)
|
|
15
|
+
asiapacific: ['AU', 'NZ', 'JP', 'KR', 'SG', 'HK', 'TW', 'TH', 'MY', 'PH', 'ID', 'VN', 'IN']
|
|
16
|
+
};
|
|
17
|
+
// Condition cache
|
|
18
|
+
const conditionCache = {};
|
|
19
|
+
/**
|
|
20
|
+
* Validates if a condition set passes based on the provided parameters.
|
|
21
|
+
*
|
|
22
|
+
* Conditions are commonly used for controlling page rendering, content visibility,
|
|
23
|
+
* and feature access based on various criteria like geography, device type, user status, etc.
|
|
24
|
+
*
|
|
25
|
+
* Supports multiple condition types:
|
|
26
|
+
* - **country** - Geographic restrictions (countries or regions like EU, EEA)
|
|
27
|
+
* - **version** - Version-based display (e.g., A/B testing)
|
|
28
|
+
* - **device** - Platform/device targeting (iOS, Android, desktop, mobile)
|
|
29
|
+
* - **user** - User authentication status (logged in, owner, admin)
|
|
30
|
+
* - **product** - Product-specific conditions
|
|
31
|
+
* - **tag** - Product tag-based conditions
|
|
32
|
+
* - **date** - Time-based conditions (before, after, between dates)
|
|
33
|
+
* - **geofence** - Location-based restrictions
|
|
34
|
+
* - **value** - Custom field comparisons
|
|
35
|
+
* - **itemStatus** - Proof/item status checks (claimable, virtual, etc.)
|
|
36
|
+
* - **condition** - Nested condition references
|
|
37
|
+
*
|
|
38
|
+
* Conditions can be combined with AND or OR logic.
|
|
39
|
+
*
|
|
40
|
+
* @param params - Validation parameters including condition and context
|
|
41
|
+
* @returns Promise that resolves to true if condition passes, false otherwise
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* import { validateCondition } from '@proveanything/smartlinks'
|
|
46
|
+
*
|
|
47
|
+
* // Simple country check
|
|
48
|
+
* const canShow = await validateCondition({
|
|
49
|
+
* condition: {
|
|
50
|
+
* type: 'and',
|
|
51
|
+
* conditions: [{
|
|
52
|
+
* type: 'country',
|
|
53
|
+
* useRegions: true,
|
|
54
|
+
* regions: ['eu'],
|
|
55
|
+
* contains: true
|
|
56
|
+
* }]
|
|
57
|
+
* },
|
|
58
|
+
* user: { valid: true, location: { country: 'DE' } }
|
|
59
|
+
* })
|
|
60
|
+
*
|
|
61
|
+
* // Multiple conditions with AND logic
|
|
62
|
+
* const result = await validateCondition({
|
|
63
|
+
* condition: {
|
|
64
|
+
* type: 'and',
|
|
65
|
+
* conditions: [
|
|
66
|
+
* { type: 'user', userType: 'valid' },
|
|
67
|
+
* { type: 'device', displays: ['mobile'], contains: true }
|
|
68
|
+
* ]
|
|
69
|
+
* },
|
|
70
|
+
* user: { valid: true },
|
|
71
|
+
* stats: { mobile: true }
|
|
72
|
+
* })
|
|
73
|
+
*
|
|
74
|
+
* // Date-based condition
|
|
75
|
+
* const isActive = await validateCondition({
|
|
76
|
+
* condition: {
|
|
77
|
+
* type: 'and',
|
|
78
|
+
* conditions: [{
|
|
79
|
+
* type: 'date',
|
|
80
|
+
* dateTest: 'between',
|
|
81
|
+
* rangeDate: ['2026-01-01', '2026-12-31']
|
|
82
|
+
* }]
|
|
83
|
+
* }
|
|
84
|
+
* })
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export async function validateCondition(params) {
|
|
88
|
+
var _a, _b;
|
|
89
|
+
// If no condition specified, pass by default
|
|
90
|
+
if (!params.conditionId && !params.condition) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
let cond = params.condition;
|
|
94
|
+
// Load condition by ID if specified
|
|
95
|
+
if (params.conditionId) {
|
|
96
|
+
// Check cache first
|
|
97
|
+
if (!conditionCache[params.conditionId]) {
|
|
98
|
+
// Try to fetch if function provided
|
|
99
|
+
if (params.fetchCondition && params.collection) {
|
|
100
|
+
const fetchedCond = await params.fetchCondition(params.collection.id, params.conditionId);
|
|
101
|
+
if (fetchedCond) {
|
|
102
|
+
conditionCache[params.conditionId] = fetchedCond;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (!conditionCache[params.conditionId]) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
// Prevent infinite recursion
|
|
110
|
+
(_a = params.conditionStack) !== null && _a !== void 0 ? _a : (params.conditionStack = []);
|
|
111
|
+
params.conditionStack.push(params.conditionId);
|
|
112
|
+
cond = conditionCache[params.conditionId];
|
|
113
|
+
}
|
|
114
|
+
if (!cond) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
// Default to AND logic
|
|
118
|
+
(_b = cond.type) !== null && _b !== void 0 ? _b : (cond.type = 'and');
|
|
119
|
+
// Empty condition set passes
|
|
120
|
+
if (!cond.conditions || cond.conditions.length === 0) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
// Evaluate each condition
|
|
124
|
+
for (const c of cond.conditions) {
|
|
125
|
+
let result = false;
|
|
126
|
+
switch (c.type) {
|
|
127
|
+
case 'country':
|
|
128
|
+
result = await validateCountry(c, params);
|
|
129
|
+
break;
|
|
130
|
+
case 'version':
|
|
131
|
+
result = await validateVersion(c, params);
|
|
132
|
+
break;
|
|
133
|
+
case 'device':
|
|
134
|
+
result = await validateDevice(c, params);
|
|
135
|
+
break;
|
|
136
|
+
case 'condition':
|
|
137
|
+
result = await validateNestedCondition(c, params);
|
|
138
|
+
break;
|
|
139
|
+
case 'user':
|
|
140
|
+
result = await validateUser(c, params);
|
|
141
|
+
break;
|
|
142
|
+
case 'product':
|
|
143
|
+
result = await validateProduct(c, params);
|
|
144
|
+
break;
|
|
145
|
+
case 'tag':
|
|
146
|
+
result = await validateTag(c, params);
|
|
147
|
+
break;
|
|
148
|
+
case 'date':
|
|
149
|
+
result = await validateDate(c, params);
|
|
150
|
+
break;
|
|
151
|
+
case 'geofence':
|
|
152
|
+
result = await validateGeofence(c, params);
|
|
153
|
+
break;
|
|
154
|
+
case 'value':
|
|
155
|
+
result = await validateValue(c, params);
|
|
156
|
+
break;
|
|
157
|
+
case 'itemStatus':
|
|
158
|
+
result = await validateItemStatus(c, params);
|
|
159
|
+
break;
|
|
160
|
+
default:
|
|
161
|
+
// Unknown condition type, skip
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
// AND logic: if any condition fails, entire set fails
|
|
165
|
+
if (!result && cond.type === 'and') {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
// OR logic: if any condition passes, entire set passes
|
|
169
|
+
if (result && cond.type === 'or') {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// AND: all passed
|
|
174
|
+
if (cond.type === 'and') {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
// OR: all failed
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Validate country-based condition
|
|
182
|
+
*/
|
|
183
|
+
async function validateCountry(condition, params) {
|
|
184
|
+
var _a, _b, _c;
|
|
185
|
+
const country = (_b = (_a = params.user) === null || _a === void 0 ? void 0 : _a.location) === null || _b === void 0 ? void 0 : _b.country;
|
|
186
|
+
if (!country) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
// Build country list from regions or direct country list
|
|
190
|
+
let countryList = condition.countries || [];
|
|
191
|
+
if (condition.useRegions && ((_c = condition.regions) === null || _c === void 0 ? void 0 : _c.length)) {
|
|
192
|
+
countryList = [];
|
|
193
|
+
for (const region of condition.regions) {
|
|
194
|
+
if (REGION_COUNTRIES[region]) {
|
|
195
|
+
countryList.push(...REGION_COUNTRIES[region]);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Remove duplicates
|
|
199
|
+
countryList = [...new Set(countryList)];
|
|
200
|
+
}
|
|
201
|
+
if (!countryList.length) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
const inList = countryList.includes(country);
|
|
205
|
+
// contains=true: pass if country IS in list
|
|
206
|
+
// contains=false: pass if country is NOT in list
|
|
207
|
+
return condition.contains ? inList : !inList;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Validate version-based condition
|
|
211
|
+
*/
|
|
212
|
+
async function validateVersion(condition, params) {
|
|
213
|
+
var _a, _b;
|
|
214
|
+
const version = (_b = (_a = params.stats) === null || _a === void 0 ? void 0 : _a.version) !== null && _b !== void 0 ? _b : null;
|
|
215
|
+
const inList = condition.versions.includes(version);
|
|
216
|
+
return condition.contains ? inList : !inList;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Validate device/platform condition
|
|
220
|
+
*/
|
|
221
|
+
async function validateDevice(condition, params) {
|
|
222
|
+
var _a, _b;
|
|
223
|
+
const displays = condition.displays;
|
|
224
|
+
const platform = (_a = params.stats) === null || _a === void 0 ? void 0 : _a.platform;
|
|
225
|
+
const mobile = (_b = params.stats) === null || _b === void 0 ? void 0 : _b.mobile;
|
|
226
|
+
for (const display of displays) {
|
|
227
|
+
if (display === 'android' && (platform === null || platform === void 0 ? void 0 : platform.android)) {
|
|
228
|
+
return condition.contains;
|
|
229
|
+
}
|
|
230
|
+
if (display === 'ios' && (platform === null || platform === void 0 ? void 0 : platform.ios)) {
|
|
231
|
+
return condition.contains;
|
|
232
|
+
}
|
|
233
|
+
if (display === 'win' && (platform === null || platform === void 0 ? void 0 : platform.win)) {
|
|
234
|
+
return condition.contains;
|
|
235
|
+
}
|
|
236
|
+
if (display === 'mac' && (platform === null || platform === void 0 ? void 0 : platform.mac)) {
|
|
237
|
+
return condition.contains;
|
|
238
|
+
}
|
|
239
|
+
if (display === 'desktop' && !mobile) {
|
|
240
|
+
return condition.contains;
|
|
241
|
+
}
|
|
242
|
+
if (display === 'mobile' && mobile) {
|
|
243
|
+
return condition.contains;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return !condition.contains;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Validate nested condition reference
|
|
250
|
+
*/
|
|
251
|
+
async function validateNestedCondition(condition, params) {
|
|
252
|
+
const newParams = Object.assign({}, params);
|
|
253
|
+
newParams.conditionId = condition.conditionId;
|
|
254
|
+
// Prevent infinite recursion
|
|
255
|
+
newParams.conditionStack = [...(newParams.conditionStack || [])];
|
|
256
|
+
if (newParams.conditionStack.includes(condition.conditionId)) {
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
const result = await validateCondition(newParams);
|
|
260
|
+
return condition.passes ? result : !result;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Validate user-based condition
|
|
264
|
+
*/
|
|
265
|
+
async function validateUser(condition, params) {
|
|
266
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
267
|
+
const userType = condition.userType;
|
|
268
|
+
switch (userType) {
|
|
269
|
+
case 'valid':
|
|
270
|
+
// User is logged in
|
|
271
|
+
return (_b = (_a = params.user) === null || _a === void 0 ? void 0 : _a.valid) !== null && _b !== void 0 ? _b : false;
|
|
272
|
+
case 'invalid':
|
|
273
|
+
// User is not logged in
|
|
274
|
+
return !((_d = (_c = params.user) === null || _c === void 0 ? void 0 : _c.valid) !== null && _d !== void 0 ? _d : false);
|
|
275
|
+
case 'owner':
|
|
276
|
+
// User owns the proof
|
|
277
|
+
return !!(params.proof &&
|
|
278
|
+
((_e = params.user) === null || _e === void 0 ? void 0 : _e.valid) &&
|
|
279
|
+
params.user.uid &&
|
|
280
|
+
params.user.uid === params.proof.userId);
|
|
281
|
+
case 'admin':
|
|
282
|
+
// User is admin of the collection
|
|
283
|
+
return !!(params.collection &&
|
|
284
|
+
((_f = params.user) === null || _f === void 0 ? void 0 : _f.valid) &&
|
|
285
|
+
params.user.uid &&
|
|
286
|
+
params.collection.roles &&
|
|
287
|
+
params.collection.roles[params.user.uid]);
|
|
288
|
+
case 'group':
|
|
289
|
+
// User is member of specific group
|
|
290
|
+
// TODO: Implement group membership check
|
|
291
|
+
return !!(params.collection &&
|
|
292
|
+
((_g = params.user) === null || _g === void 0 ? void 0 : _g.valid) &&
|
|
293
|
+
params.user.groups);
|
|
294
|
+
default:
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Validate product-based condition
|
|
300
|
+
*/
|
|
301
|
+
async function validateProduct(condition, params) {
|
|
302
|
+
var _a;
|
|
303
|
+
const productId = (_a = params.product) === null || _a === void 0 ? void 0 : _a.id;
|
|
304
|
+
// No product ID available
|
|
305
|
+
if (!productId) {
|
|
306
|
+
return !condition.contains;
|
|
307
|
+
}
|
|
308
|
+
const inList = condition.productIds.includes(productId);
|
|
309
|
+
return condition.contains ? inList : !inList;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Validate tag-based condition
|
|
313
|
+
*/
|
|
314
|
+
async function validateTag(condition, params) {
|
|
315
|
+
var _a, _b;
|
|
316
|
+
const productId = (_a = params.product) === null || _a === void 0 ? void 0 : _a.id;
|
|
317
|
+
// No product
|
|
318
|
+
if (!productId) {
|
|
319
|
+
return !condition.contains;
|
|
320
|
+
}
|
|
321
|
+
// No tags on product
|
|
322
|
+
if (!((_b = params.product) === null || _b === void 0 ? void 0 : _b.tags)) {
|
|
323
|
+
return !condition.contains;
|
|
324
|
+
}
|
|
325
|
+
// Check if any condition tag exists on product
|
|
326
|
+
for (const tag of condition.tags) {
|
|
327
|
+
if (params.product.tags[tag]) {
|
|
328
|
+
return condition.contains;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return !condition.contains;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Validate date-based condition
|
|
335
|
+
*/
|
|
336
|
+
async function validateDate(condition, params) {
|
|
337
|
+
const now = Date.now();
|
|
338
|
+
switch (condition.dateTest) {
|
|
339
|
+
case 'before':
|
|
340
|
+
if (!condition.beforeDate)
|
|
341
|
+
return false;
|
|
342
|
+
return now < Date.parse(condition.beforeDate);
|
|
343
|
+
case 'after':
|
|
344
|
+
if (!condition.afterDate)
|
|
345
|
+
return false;
|
|
346
|
+
return now > Date.parse(condition.afterDate);
|
|
347
|
+
case 'between':
|
|
348
|
+
if (!condition.rangeDate || condition.rangeDate.length !== 2)
|
|
349
|
+
return false;
|
|
350
|
+
const start = Date.parse(condition.rangeDate[0]);
|
|
351
|
+
const end = Date.parse(condition.rangeDate[1]);
|
|
352
|
+
return now > start && now < end;
|
|
353
|
+
default:
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Validate geofence-based condition
|
|
359
|
+
*/
|
|
360
|
+
async function validateGeofence(condition, params) {
|
|
361
|
+
var _a;
|
|
362
|
+
let lat;
|
|
363
|
+
let lng;
|
|
364
|
+
// Try to get location from params first
|
|
365
|
+
if ((_a = params.user) === null || _a === void 0 ? void 0 : _a.location) {
|
|
366
|
+
lat = params.user.location.latitude;
|
|
367
|
+
lng = params.user.location.longitude;
|
|
368
|
+
}
|
|
369
|
+
// If not available and getLocation function provided, fetch it
|
|
370
|
+
if ((lat === undefined || lng === undefined) && params.getLocation) {
|
|
371
|
+
try {
|
|
372
|
+
const location = await params.getLocation();
|
|
373
|
+
lat = location.latitude;
|
|
374
|
+
lng = location.longitude;
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (lat === undefined || lng === undefined) {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
// Check if outside bounding box
|
|
384
|
+
const outside = lat > condition.top ||
|
|
385
|
+
lat < condition.bottom ||
|
|
386
|
+
lng < condition.left ||
|
|
387
|
+
lng > condition.right;
|
|
388
|
+
return condition.contains ? !outside : outside;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Validate value comparison condition
|
|
392
|
+
*/
|
|
393
|
+
async function validateValue(condition, params) {
|
|
394
|
+
// Navigate to field value using dot notation
|
|
395
|
+
const fieldPath = condition.field.split('.');
|
|
396
|
+
let base = params;
|
|
397
|
+
for (const field of fieldPath) {
|
|
398
|
+
if (base && typeof base === 'object' && field in base && typeof base[field] !== 'undefined') {
|
|
399
|
+
base = base[field];
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// Convert value to correct type
|
|
406
|
+
let val = condition.value;
|
|
407
|
+
if (typeof val === 'string' && condition.fieldType === 'boolean') {
|
|
408
|
+
val = val.toLowerCase() === 'true';
|
|
409
|
+
}
|
|
410
|
+
else if (typeof val === 'string' && condition.fieldType === 'integer') {
|
|
411
|
+
val = parseInt(val, 10);
|
|
412
|
+
}
|
|
413
|
+
// Perform comparison
|
|
414
|
+
switch (condition.validationType) {
|
|
415
|
+
case 'equal':
|
|
416
|
+
return base == val;
|
|
417
|
+
case 'not':
|
|
418
|
+
return base != val;
|
|
419
|
+
case 'greater':
|
|
420
|
+
return base > val;
|
|
421
|
+
case 'less':
|
|
422
|
+
return base < val;
|
|
423
|
+
default:
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Validate item status condition
|
|
429
|
+
*/
|
|
430
|
+
async function validateItemStatus(condition, params) {
|
|
431
|
+
switch (condition.statusType) {
|
|
432
|
+
case 'isClaimable':
|
|
433
|
+
return !!(params.proof && params.proof.claimable);
|
|
434
|
+
case 'notClaimable':
|
|
435
|
+
return !!(params.proof && !params.proof.claimable);
|
|
436
|
+
case 'noProof':
|
|
437
|
+
return !params.proof;
|
|
438
|
+
case 'hasProof':
|
|
439
|
+
return !!params.proof;
|
|
440
|
+
case 'isVirtual':
|
|
441
|
+
return !!(params.proof && params.proof.virtual);
|
|
442
|
+
case 'notVirtual':
|
|
443
|
+
return !!(params.proof && !params.proof.virtual);
|
|
444
|
+
default:
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Clear the condition cache
|
|
450
|
+
*/
|
|
451
|
+
export function clearConditionCache() {
|
|
452
|
+
Object.keys(conditionCache).forEach(key => delete conditionCache[key]);
|
|
453
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { Product } from '../types/product';
|
|
2
|
+
import type { Collection } from '../types/collection';
|
|
3
|
+
import type { BatchResponse } from '../types/batch';
|
|
4
|
+
import type { Proof } from '../types/proof';
|
|
5
|
+
/**
|
|
6
|
+
* Parameters for building a portal path.
|
|
7
|
+
* Pass in objects where available - the function will extract the needed properties.
|
|
8
|
+
*/
|
|
9
|
+
export interface PortalPathParams {
|
|
10
|
+
/** Collection object (required) - provides shortId and optional portalUrl */
|
|
11
|
+
collection: Collection | {
|
|
12
|
+
shortId: string;
|
|
13
|
+
portalUrl?: string;
|
|
14
|
+
};
|
|
15
|
+
/** Full product object (extracts id, gtin, and ownGtin from the product) */
|
|
16
|
+
product?: Product;
|
|
17
|
+
/** Just a product ID (if you don't have the full product) */
|
|
18
|
+
productId?: string;
|
|
19
|
+
/** Batch object (extracts id and expiryDate) */
|
|
20
|
+
batch?: BatchResponse;
|
|
21
|
+
/** Just a batch ID string (if you don't have the full batch object) */
|
|
22
|
+
batchId?: string;
|
|
23
|
+
/** Variant object OR just a variant ID string */
|
|
24
|
+
variant?: {
|
|
25
|
+
id: string;
|
|
26
|
+
} | string;
|
|
27
|
+
/** Proof object OR just a proof ID string */
|
|
28
|
+
proof?: Proof | string;
|
|
29
|
+
/** Additional query parameters */
|
|
30
|
+
queryParams?: Record<string, string>;
|
|
31
|
+
/** Return only the path without domain (default: false, returns full URL) */
|
|
32
|
+
pathOnly?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Builds a portal path/URL based on the provided parameters.
|
|
36
|
+
*
|
|
37
|
+
* Returns a full URL by default using collection.portalUrl or the default smartlinks domain.
|
|
38
|
+
* Set pathOnly: true to return just the path without the domain.
|
|
39
|
+
*
|
|
40
|
+
* Pass in objects where available (collection, product, batch, etc.) and the function
|
|
41
|
+
* will extract the needed properties. You can also pass just IDs if you don't have the full objects.
|
|
42
|
+
*
|
|
43
|
+
* Supports multiple path formats:
|
|
44
|
+
* - Basic product: `/c/{shortId}/{productId}`
|
|
45
|
+
* - With proof: `/c/{shortId}/{productId}/{proofId}`
|
|
46
|
+
* - GTIN (own): `/01/{gtin}` - ownGtin is read from the product object
|
|
47
|
+
* - GTIN (not own): `/gc/{shortId}/01/{gtin}`
|
|
48
|
+
* - With batch: adds `/10/{batchId}` and optionally `?17={expiryDate}`
|
|
49
|
+
* - With variant: adds `/22/{variantId}`
|
|
50
|
+
*
|
|
51
|
+
* @param params - Path parameters
|
|
52
|
+
* @returns The built portal URL (default) or path (if pathOnly: true)
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* // Returns full URL by default
|
|
57
|
+
* buildPortalPath({
|
|
58
|
+
* collection: myCollection, // uses collection.portalUrl
|
|
59
|
+
* product: myProduct
|
|
60
|
+
* })
|
|
61
|
+
* // Returns: https://portal.smartlinks.io/c/abc123/prod1
|
|
62
|
+
*
|
|
63
|
+
* // Return just the path
|
|
64
|
+
* buildPortalPath({
|
|
65
|
+
* collection: myCollection,
|
|
66
|
+
* product: myProduct,
|
|
67
|
+
* pathOnly: true
|
|
68
|
+
* })
|
|
69
|
+
* // Returns: /c/abc123/prod1
|
|
70
|
+
*
|
|
71
|
+
* // GTIN path (ownGtin read from product)
|
|
72
|
+
* buildPortalPath({
|
|
73
|
+
* collection: myCollection,
|
|
74
|
+
* product: myProduct // if product.ownGtin is true, uses /01/ path
|
|
75
|
+
* })
|
|
76
|
+
* // Returns: https://portal.smartlinks.io/01/1234567890123
|
|
77
|
+
*
|
|
78
|
+
* // With batch object (includes expiry date)
|
|
79
|
+
* buildPortalPath({
|
|
80
|
+
* collection: myCollection,
|
|
81
|
+
* product: myProduct,
|
|
82
|
+
* batch: myBatch // extracts id and expiryDate
|
|
83
|
+
* })
|
|
84
|
+
* // Returns: https://portal.smartlinks.io/01/1234567890123/10/batch1?17=260630
|
|
85
|
+
*
|
|
86
|
+
* // Or just pass IDs
|
|
87
|
+
* buildPortalPath({
|
|
88
|
+
* collection: { shortId: 'abc123' },
|
|
89
|
+
* productId: 'prod1',
|
|
90
|
+
* batchId: 'batch1' // just the ID, no expiry
|
|
91
|
+
* })
|
|
92
|
+
* // Returns: https://smartlinks.app/c/abc123/prod1
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare function buildPortalPath(params: PortalPathParams): string;
|