@proveanything/smartlinks 1.8.12 → 1.9.1

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,347 @@
1
+ # Product Facets SDK Reference
2
+
3
+ Simple SDK-facing reference for the facet API.
4
+
5
+ This document covers:
6
+
7
+ - admin facet endpoints
8
+ - public facet endpoints
9
+ - collection-level facet routes
10
+ - TypeScript interfaces for facet definitions, values, and aggregation queries
11
+
12
+ ## Current route model
13
+
14
+ The facet API stays under `/api/v1`.
15
+
16
+ Facets are mounted directly under the collection.
17
+
18
+ ### Admin base
19
+
20
+ ```ts
21
+ /api/v1/admin/collection/:collectionId/facets
22
+ ```
23
+
24
+ ### Public base
25
+
26
+ ```ts
27
+ /api/v1/public/collection/:collectionId/facets
28
+ ```
29
+
30
+ ## Admin endpoints
31
+
32
+ ### List facet definitions
33
+
34
+ ```ts
35
+ GET /api/v1/admin/collection/:collectionId/facets
36
+ ```
37
+
38
+ Optional query params:
39
+
40
+ ```ts
41
+ includeValues?: boolean
42
+ includeDeleted?: boolean
43
+ kind?: 'system' | 'custom'
44
+ reserved?: boolean
45
+ ```
46
+
47
+ ### Create facet definition
48
+
49
+ ```ts
50
+ POST /api/v1/admin/collection/:collectionId/facets
51
+ ```
52
+
53
+ ### Get facet definition
54
+
55
+ ```ts
56
+ GET /api/v1/admin/collection/:collectionId/facets/:facetKey
57
+ ```
58
+
59
+ Optional query params:
60
+
61
+ ```ts
62
+ includeValues?: boolean
63
+ includeDeleted?: boolean
64
+ ```
65
+
66
+ ### Update facet definition
67
+
68
+ ```ts
69
+ PUT /api/v1/admin/collection/:collectionId/facets/:facetKey
70
+ ```
71
+
72
+ ### Delete facet definition
73
+
74
+ ```ts
75
+ DELETE /api/v1/admin/collection/:collectionId/facets/:facetKey
76
+ ```
77
+
78
+ Current behavior:
79
+
80
+ - reserved facet definitions cannot be deleted
81
+ - facet definitions with product assignments cannot be deleted
82
+
83
+ ### List facet values
84
+
85
+ ```ts
86
+ GET /api/v1/admin/collection/:collectionId/facets/:facetKey/values
87
+ ```
88
+
89
+ Optional query params:
90
+
91
+ ```ts
92
+ includeDeleted?: boolean
93
+ ```
94
+
95
+ ### Create facet value
96
+
97
+ ```ts
98
+ POST /api/v1/admin/collection/:collectionId/facets/:facetKey/values
99
+ ```
100
+
101
+ ### Get facet value
102
+
103
+ ```ts
104
+ GET /api/v1/admin/collection/:collectionId/facets/:facetKey/values/:valueKey
105
+ ```
106
+
107
+ Optional query params:
108
+
109
+ ```ts
110
+ includeDeleted?: boolean
111
+ ```
112
+
113
+ ### Update facet value
114
+
115
+ ```ts
116
+ PUT /api/v1/admin/collection/:collectionId/facets/:facetKey/values/:valueKey
117
+ ```
118
+
119
+ ### Delete facet value
120
+
121
+ ```ts
122
+ DELETE /api/v1/admin/collection/:collectionId/facets/:facetKey/values/:valueKey
123
+ ```
124
+
125
+ Current behavior:
126
+
127
+ - deleting a facet value removes its product assignments
128
+ - affected product `facets` summaries are synchronized after the delete
129
+
130
+ ### Query facet counts
131
+
132
+ ```ts
133
+ POST /api/v1/admin/collection/:collectionId/facets/query
134
+ ```
135
+
136
+ Supported query filters now:
137
+
138
+ - `search`
139
+ - `status`
140
+ - `schemaType`
141
+ - `type`
142
+ - `productIds`
143
+ - `sku`
144
+ - `gtin`
145
+ - `category`
146
+ - `tags`
147
+ - `facetEquals`
148
+
149
+ ## Public endpoints
150
+
151
+ ### List facet definitions
152
+
153
+ ```ts
154
+ GET /api/v1/public/collection/:collectionId/facets
155
+ ```
156
+
157
+ Optional query params:
158
+
159
+ ```ts
160
+ includeValues?: boolean
161
+ ```
162
+
163
+ ### Get facet definition
164
+
165
+ ```ts
166
+ GET /api/v1/public/collection/:collectionId/facets/:facetKey
167
+ ```
168
+
169
+ Optional query params:
170
+
171
+ ```ts
172
+ includeValues?: boolean
173
+ ```
174
+
175
+ ### List facet values
176
+
177
+ ```ts
178
+ GET /api/v1/public/collection/:collectionId/facets/:facetKey/values
179
+ ```
180
+
181
+ ### Get facet value
182
+
183
+ ```ts
184
+ GET /api/v1/public/collection/:collectionId/facets/:facetKey/values/:valueKey
185
+ ```
186
+
187
+ ### Query facet counts
188
+
189
+ ```ts
190
+ POST /api/v1/public/collection/:collectionId/facets/query
191
+ ```
192
+
193
+ Public endpoints are read-only.
194
+
195
+ ## TypeScript interfaces
196
+
197
+ ## Core JSON types
198
+
199
+ ```ts
200
+ export type JsonPrimitive = string | number | boolean | null
201
+
202
+ export type JsonValue =
203
+ | JsonPrimitive
204
+ | JsonValue[]
205
+ | { [key: string]: JsonValue }
206
+ ```
207
+
208
+ ## Facet definition types
209
+
210
+ ```ts
211
+ export interface FacetDefinition {
212
+ id: string
213
+ orgId: string
214
+ collectionId: string
215
+ key: string
216
+ name: string
217
+ description?: string | null
218
+ cardinality: 'single' | 'multi'
219
+ kind: 'system' | 'custom'
220
+ hierarchical: boolean
221
+ reserved: boolean
222
+ config: Record<string, JsonValue>
223
+ createdAt: string
224
+ updatedAt: string
225
+ deletedAt?: string | null
226
+ values?: FacetValue[]
227
+ }
228
+
229
+ export interface FacetDefinitionWriteInput {
230
+ key?: string
231
+ name: string
232
+ description?: string | null
233
+ cardinality?: 'single' | 'multi'
234
+ kind?: 'system' | 'custom'
235
+ hierarchical?: boolean
236
+ reserved?: boolean
237
+ config?: Record<string, JsonValue>
238
+ }
239
+ ```
240
+
241
+ ## Facet value types
242
+
243
+ ```ts
244
+ export interface FacetValue {
245
+ id: string
246
+ orgId: string
247
+ collectionId: string
248
+ facetDefinitionId: string
249
+ facetKey?: string
250
+ key: string
251
+ slug?: string | null
252
+ name: string
253
+ shortName?: string | null
254
+ description?: string | null
255
+ color?: string | null
256
+ icon?: string | null
257
+ image?: Record<string, JsonValue> | null
258
+ metadata: Record<string, JsonValue>
259
+ sortOrder: number
260
+ parentValueId?: string | null
261
+ parentValueKey?: string | null
262
+ createdAt: string
263
+ updatedAt: string
264
+ deletedAt?: string | null
265
+ count?: number
266
+ }
267
+
268
+ export interface FacetValueWriteInput {
269
+ key?: string
270
+ slug?: string | null
271
+ name: string
272
+ shortName?: string | null
273
+ description?: string | null
274
+ color?: string | null
275
+ icon?: string | null
276
+ image?: Record<string, JsonValue> | null
277
+ metadata?: Record<string, JsonValue>
278
+ sortOrder?: number
279
+ parentValueKey?: string | null
280
+ }
281
+ ```
282
+
283
+ ## List response types
284
+
285
+ ```ts
286
+ export interface FacetListResponse {
287
+ items: FacetDefinition[]
288
+ }
289
+
290
+ export interface FacetValueListResponse {
291
+ facet: FacetDefinition
292
+ items: FacetValue[]
293
+ }
294
+
295
+ export interface FacetValueResponse {
296
+ facet: FacetDefinition
297
+ item: FacetValue
298
+ }
299
+ ```
300
+
301
+ ## Query types
302
+
303
+ ```ts
304
+ export interface FacetQueryRequest {
305
+ facetKeys?: string[]
306
+ includeEmpty?: boolean
307
+ includeDeleted?: boolean
308
+ query?: {
309
+ search?: string
310
+ status?: string[]
311
+ schemaType?: string[]
312
+ type?: string[]
313
+ productIds?: string[]
314
+ sku?: string
315
+ gtin?: string
316
+ category?: string[]
317
+ tags?: string[]
318
+ facetEquals?: Record<string, JsonValue | JsonValue[]>
319
+ }
320
+ }
321
+
322
+ export interface FacetBucket {
323
+ facetKey: string
324
+ valueKey: string
325
+ name?: string
326
+ count: number
327
+ }
328
+
329
+ export interface FacetQueryResponse {
330
+ items: Array<{
331
+ facet: FacetDefinition
332
+ values: FacetValue[]
333
+ }>
334
+ buckets: FacetBucket[]
335
+ meta?: {
336
+ source?: 'postgres'
337
+ matchedProducts?: number
338
+ }
339
+ }
340
+ ```
341
+
342
+ ## Notes for SDK implementers
343
+
344
+ - treat facet definitions and facet values as collection-scoped resources
345
+ - use the facet API to manage definitions and values
346
+ - use product read/write routes to assign facet data onto products
347
+ - treat `label` and `category` as reserved system facets when present
@@ -280,6 +280,8 @@ const path = utils.buildPortalPath(params)
280
280
 
281
281
  The `validateCondition` function helps determine if content should be shown or hidden based on various criteria like geography, device type, user status, dates, and more.
282
282
 
283
+ Enable verbose tracing per invocation with `debugConditions`, or set `globalThis.SMARTLINKS_CONDITION_DEBUG = true` in a browser/devtools session to trace every evaluation.
284
+
283
285
  ### Basic Usage
284
286
 
285
287
  ```typescript
@@ -291,7 +293,6 @@ const canShow = await utils.validateCondition({
291
293
  type: 'and',
292
294
  conditions: [{
293
295
  type: 'country',
294
- useRegions: true,
295
296
  regions: ['eu'],
296
297
  contains: true
297
298
  }]
@@ -328,7 +329,6 @@ await utils.validateCondition({
328
329
  type: 'and',
329
330
  conditions: [{
330
331
  type: 'country',
331
- useRegions: true,
332
332
  regions: ['eu', 'uk'],
333
333
  contains: true // true = show IN these regions, false = hide IN these regions
334
334
  }]
@@ -336,6 +336,20 @@ await utils.validateCondition({
336
336
  user: { valid: true, location: { country: 'FR' } }
337
337
  })
338
338
 
339
+ // Regions and explicit countries can be combined
340
+ await utils.validateCondition({
341
+ condition: {
342
+ type: 'and',
343
+ conditions: [{
344
+ type: 'country',
345
+ regions: ['eu'],
346
+ countries: ['CH'],
347
+ contains: true
348
+ }]
349
+ },
350
+ user: { valid: true, location: { country: 'CH' } }
351
+ })
352
+
339
353
  // Or specific countries
340
354
  await utils.validateCondition({
341
355
  condition: {
@@ -582,7 +596,7 @@ await utils.validateCondition({
582
596
  conditions: [
583
597
  { type: 'user', userType: 'valid' },
584
598
  { type: 'device', displays: ['mobile'], contains: true },
585
- { type: 'country', useRegions: true, regions: ['eu'], contains: true }
599
+ { type: 'country', regions: ['eu'], contains: true }
586
600
  ]
587
601
  },
588
602
  user: { valid: true, location: { country: 'FR' } },
@@ -630,12 +644,49 @@ const showNewFeature = await utils.validateCondition({
630
644
  condition: {
631
645
  type: 'and',
632
646
  conditions: [
633
- { type: 'country', useRegions: true, regions: ['northamerica'], contains: true },
647
+ { type: 'country', regions: ['northamerica'], contains: true },
634
648
  { type: 'date', dateTest: 'after', afterDate: '2026-03-01' }
635
649
  ]
636
650
  },
637
651
  user: { valid: true, location: { country: 'US' } }
638
652
  })
653
+
654
+ ### Debug Logging
655
+
656
+ Trace which condition is being evaluated, whether it passed, and why:
657
+
658
+ ```typescript
659
+ await utils.validateCondition({
660
+ condition: {
661
+ type: 'and',
662
+ conditions: [
663
+ { type: 'user', userType: 'valid' },
664
+ { type: 'country', regions: ['eu'], contains: true }
665
+ ]
666
+ },
667
+ user: { valid: true, location: { country: 'DE' } },
668
+ debugConditions: true
669
+ })
670
+
671
+ // Or enable globally in the browser console/devtools
672
+ globalThis.SMARTLINKS_CONDITION_DEBUG = true
673
+ ```
674
+
675
+ If you want to route logs somewhere specific, pass a logger:
676
+
677
+ ```typescript
678
+ await utils.validateCondition({
679
+ condition: {
680
+ type: 'and',
681
+ conditions: [{ type: 'user', userType: 'valid' }]
682
+ },
683
+ user: { valid: true },
684
+ debugConditions: {
685
+ label: 'checkout-gate',
686
+ logger: (...args) => console.log(...args)
687
+ }
688
+ })
689
+ ```
639
690
  ```
640
691
 
641
692
  #### Mobile-Only Features
package/dist/http.d.ts CHANGED
@@ -117,7 +117,8 @@ export declare function configureSdkCache(options: {
117
117
  * ```ts
118
118
  * invalidateCache() // clear everything
119
119
  * invalidateCache('/collection/abc123') // one specific collection
120
- * invalidateCache('/product/') // all product responses
120
+ * invalidateCache('/product/') // all legacy singular product responses
121
+ * invalidateCache('/products/') // all canonical plural product responses
121
122
  * ```
122
123
  */
123
124
  export declare function invalidateCache(urlPattern?: string): void;
package/dist/http.js CHANGED
@@ -83,7 +83,7 @@ const CACHE_TTL_RULES = [
83
83
  { pattern: /\/proof\/[^/]*(\?.*)?$/i, ttlMs: 30000 },
84
84
  { pattern: /\/attestation\/[^/]*(\?.*)?$/i, ttlMs: 2 * 60000 },
85
85
  // Slow-changing top-level resources — long TTLs, matched only when path ends at the ID
86
- { pattern: /\/product\/[^/]*(\?.*)?$/i, ttlMs: 60 * 60000 },
86
+ { pattern: /\/products?\/[^/]*(\?.*)?$/i, ttlMs: 60 * 60000 },
87
87
  { pattern: /\/variant\/[^/]*(\?.*)?$/i, ttlMs: 60 * 60000 },
88
88
  { pattern: /\/collection\/[^/]*(\?.*)?$/i, ttlMs: 60 * 60000 }, // 1 hour
89
89
  ];
@@ -507,7 +507,8 @@ export function configureSdkCache(options) {
507
507
  * ```ts
508
508
  * invalidateCache() // clear everything
509
509
  * invalidateCache('/collection/abc123') // one specific collection
510
- * invalidateCache('/product/') // all product responses
510
+ * invalidateCache('/product/') // all legacy singular product responses
511
+ * invalidateCache('/products/') // all canonical plural product responses
511
512
  * ```
512
513
  */
513
514
  export function invalidateCache(urlPattern) {
@@ -593,7 +593,7 @@ export class IframeResponder {
593
593
  }
594
594
  // Product request - ONLY match direct product endpoint, not app config endpoints
595
595
  if (this.cache.product && this.options.productId) {
596
- const productMatch = path.match(/^public\/collection\/[^/]+\/product\/([^/]+)$/);
596
+ const productMatch = path.match(/^public\/collection\/[^/]+\/products?\/([^/]+)$/);
597
597
  if (productMatch && productMatch[1] === this.options.productId) {
598
598
  return JSON.parse(JSON.stringify(this.cache.product));
599
599
  }
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { iframe } from "./iframe";
5
5
  export * as cache from './cache';
6
6
  export { IframeResponder, isAdminFromRoles, buildIframeSrc, } from './iframeResponder';
7
7
  export * as utils from './utils';
8
- export type { PortalPathParams, ConditionParams, ConditionSet, Condition, UserInfo, ProductInfo, ProofInfo, CollectionInfo, } from './utils';
8
+ export type { PortalPathParams, ConditionParams, ConditionDebugOptions, ConditionDebugLogger, ConditionSet, Condition, UserInfo, ProductInfo, ProofInfo, CollectionInfo, } from './utils';
9
9
  export type { LoginResponse, VerifyTokenResponse, AccountInfoResponse, AuthLocation, } from "./api/auth";
10
10
  export type { UserAccountRegistrationRequest, } from "./types/auth";
11
11
  export type { CommunicationEvent, CommsQueryByUser, CommsRecipientIdsQuery, CommsRecipientsWithoutActionQuery, CommsRecipientsWithActionQuery, RecipientId, RecipientWithOutcome, LogCommunicationEventBody, LogBulkCommunicationEventsBody, AppendResult, AppendBulkResult, CommsSettings, TopicConfig, CommsSettingsGetResponse, CommsSettingsPatchBody, CommsPublicTopicsResponse, UnsubscribeQuery, UnsubscribeResponse, CommsConsentUpsertRequest, CommsPreferencesUpsertRequest, CommsSubscribeRequest, CommsSubscribeResponse, CommsSubscriptionCheckQuery, CommsSubscriptionCheckResponse, CommsListMethodsQuery, CommsListMethodsResponse, RegisterEmailMethodRequest, RegisterSmsMethodRequest, RegisterMethodResponse, SubscriptionsResolveRequest, SubscriptionsResolveResponse, } from "./types/comms";
@@ -16,7 +16,8 @@ export type { BatchResponse, BatchCreateRequest, BatchUpdateRequest, } from "./t
16
16
  export type { VariantResponse, VariantCreateRequest, VariantUpdateRequest, } from "./types/variant";
17
17
  export type { BroadcastSendRequest } from "./types/broadcasts";
18
18
  export type { AppConfigOptions } from "./api/appConfiguration";
19
- export type { ProductCreateRequest, ProductUpdateRequest, Product, } from "./types/product";
19
+ export type { AdditionalGtin, ISODateString, JsonPrimitive, JsonValue, ProductCreateRequest, ProductClaimCreateInput, ProductClaimCreateRequestBody, ProductClaimLookupInput, ProductFacetMap, ProductFacetValue, ProductImage, ProductImageThumbnails, ProductImageUrlInput, ProductKey, ProductQueryRequest, ProductQueryResponse, ProductUpdateRequest, Product, ProductWriteInput, PublicProduct, } from "./types/product";
20
+ export type { FacetBucket, FacetDefinition, FacetDefinitionWriteInput, FacetGetParams, FacetListParams, FacetListResponse, FacetQueryRequest, FacetQueryResponse, FacetValue, FacetValueDefinition, FacetValueGetParams, FacetValueListParams, FacetValueListResponse, FacetValueResponse, FacetValueWriteInput, PublicFacetListParams, } from "./types/facets";
20
21
  export type { Collection, CollectionResponse, CollectionCreateRequest, CollectionUpdateRequest, } from "./types/collection";
21
22
  export type { Proof, ProofResponse, ProofCreateRequest, ProofUpdateRequest, ProofClaimRequest, } from "./types/proof";
22
23
  export type { QrShortCodeLookupResponse, } from "./types/qr";