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