@proveanything/smartlinks 1.3.26 → 1.3.27

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.
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Builds a portal path/URL based on the provided parameters.
3
+ *
4
+ * Pass in objects where available (collection, product, batch, etc.) and the function
5
+ * will extract the needed properties. You can also pass just IDs if you don't have the full objects.
6
+ *
7
+ * Supports multiple path formats:
8
+ * - Basic product: `/c/{shortId}/{productId}`
9
+ * - With proof: `/c/{shortId}/{productId}/{proofId}`
10
+ * - GTIN (own): `/01/{gtin}` - ownGtin is read from the product object
11
+ * - GTIN (not own): `/gc/{shortId}/01/{gtin}`
12
+ * - With batch: adds `/10/{batchId}` and optionally `?17={expiryDate}`
13
+ * - With variant: adds `/22/{variantId}`
14
+ *
15
+ * @param params - Path parameters
16
+ * @returns The built portal path or URL
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * // Basic product path (pass objects)
21
+ * buildPortalPath({
22
+ * collection: myCollection,
23
+ * product: myProduct // reads ownGtin from product if present
24
+ * })
25
+ * // Returns: https://portal.smartlinks.io/c/abc123/prod1
26
+ *
27
+ * // GTIN path (ownGtin read from product)
28
+ * buildPortalPath({
29
+ * collection: myCollection,
30
+ * product: myProduct // if product.ownGtin is true, uses /01/ path
31
+ * })
32
+ * // Returns: https://portal.smartlinks.io/01/1234567890123
33
+ *
34
+ * // With batch object (includes expiry date)
35
+ * buildPortalPath({
36
+ * collection: myCollection,
37
+ * product: myProduct,
38
+ * batch: myBatch // extracts id and expiryDate
39
+ * })
40
+ * // Returns: /01/1234567890123/10/batch1?17=260630
41
+ *
42
+ * // Or just pass IDs
43
+ * buildPortalPath({
44
+ * collection: { shortId: 'abc123' },
45
+ * productId: 'prod1',
46
+ * batchId: 'batch1' // just the ID, no expiry
47
+ * })
48
+ * // Returns: /c/abc123/prod1
49
+ * ```
50
+ */
51
+ export function buildPortalPath(params) {
52
+ const { collection, product, productId, batch, batchId, variant, proof, queryParams = {} } = params;
53
+ // Extract values from collection
54
+ const shortId = collection.shortId;
55
+ const baseUrl = 'portalUrl' in collection ? collection.portalUrl : undefined;
56
+ // Extract product values
57
+ let gtin;
58
+ let ownGtin;
59
+ let extractedProductId;
60
+ if (product) {
61
+ extractedProductId = product.id;
62
+ gtin = product.gtin;
63
+ // ownGtin is a critical product setting - only read from product, never override
64
+ ownGtin = 'ownGtin' in product ? product.ownGtin : undefined;
65
+ }
66
+ else if (productId) {
67
+ extractedProductId = productId;
68
+ }
69
+ // Extract batch values
70
+ let extractedBatchId;
71
+ let expiryDate;
72
+ if (batch) {
73
+ // Batch object - extract id and expiryDate
74
+ extractedBatchId = batch.id;
75
+ if (batch.expiryDate) {
76
+ // Handle Firebase timestamp or Date
77
+ if (typeof batch.expiryDate === 'object' && 'seconds' in batch.expiryDate) {
78
+ expiryDate = new Date(batch.expiryDate.seconds * 1000);
79
+ }
80
+ else {
81
+ expiryDate = batch.expiryDate;
82
+ }
83
+ }
84
+ }
85
+ else if (batchId) {
86
+ // Just batch ID string - no expiry date
87
+ extractedBatchId = batchId;
88
+ }
89
+ // Extract variant ID
90
+ const variantId = variant
91
+ ? typeof variant === 'string'
92
+ ? variant
93
+ : variant.id
94
+ : undefined;
95
+ // Extract proof ID
96
+ const proofId = proof
97
+ ? typeof proof === 'string'
98
+ ? proof
99
+ : proof.id
100
+ : undefined;
101
+ let pathname = '';
102
+ const searchParams = new URLSearchParams();
103
+ // Build pathname based on GTIN or product ID
104
+ if (gtin) {
105
+ // GS1 Digital Link format
106
+ if (ownGtin) {
107
+ pathname = `/01/${gtin}`;
108
+ }
109
+ else {
110
+ pathname = `/gc/${shortId}/01/${gtin}`;
111
+ }
112
+ // Add batch (GS1 AI 10)
113
+ if (extractedBatchId) {
114
+ pathname += `/10/${extractedBatchId}`;
115
+ // Add expiry date as query param (GS1 AI 17)
116
+ if (expiryDate) {
117
+ const dateStr = formatExpiryDate(expiryDate);
118
+ searchParams.append('17', dateStr);
119
+ }
120
+ }
121
+ // Add variant (GS1 AI 22)
122
+ if (variantId) {
123
+ pathname += `/22/${variantId}`;
124
+ }
125
+ }
126
+ else if (extractedProductId) {
127
+ // Regular product path
128
+ pathname = `/c/${shortId}/${extractedProductId}`;
129
+ // Add proof to path
130
+ if (proofId) {
131
+ pathname += `/${proofId}`;
132
+ }
133
+ }
134
+ // Add any additional query params
135
+ for (const [key, value] of Object.entries(queryParams)) {
136
+ searchParams.append(key, value);
137
+ }
138
+ // Build final URL
139
+ const queryString = searchParams.toString();
140
+ const fullPath = pathname + (queryString ? `?${queryString}` : '');
141
+ if (baseUrl) {
142
+ const cleanBaseUrl = baseUrl.replace(/\/$/, '');
143
+ return cleanBaseUrl + fullPath;
144
+ }
145
+ return fullPath;
146
+ }
147
+ /**
148
+ * Formats an expiry date to YYMMDD format.
149
+ * @internal
150
+ */
151
+ function formatExpiryDate(date) {
152
+ if (typeof date === 'string') {
153
+ // Already in YYMMDD format
154
+ if (/^\d{6}$/.test(date)) {
155
+ return date;
156
+ }
157
+ // Try to parse as ISO date
158
+ date = new Date(date);
159
+ }
160
+ const year = date.getFullYear().toString().slice(-2);
161
+ const month = (date.getMonth() + 1).toString().padStart(2, '0');
162
+ const day = date.getDate().toString().padStart(2, '0');
163
+ return `${year}${month}${day}`;
164
+ }