@proveanything/smartlinks 1.15.10 → 1.15.11
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/dist/docs/API_SUMMARY.md +8 -1
- package/dist/docs/appConfig.md +46 -5
- package/dist/docs/item-context.md +1 -2
- package/dist/docs/utils.md +2 -2
- package/dist/openapi.yaml +3 -0
- package/dist/types/appConfiguration.d.ts +16 -0
- package/dist/utils/conditions.d.ts +7 -11
- package/dist/utils/conditions.js +4 -10
- package/docs/API_SUMMARY.md +8 -1
- package/docs/appConfig.md +46 -5
- package/docs/item-context.md +1 -2
- package/docs/utils.md +2 -2
- package/openapi.yaml +3 -0
- package/package.json +1 -1
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.15.
|
|
3
|
+
Version: 1.15.11 | Generated: 2026-07-20T13:46:09.781Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -1419,6 +1419,11 @@ interface AppliedOverridesSummary {
|
|
|
1419
1419
|
```typescript
|
|
1420
1420
|
interface SystemBlock {
|
|
1421
1421
|
basePlanId?: string
|
|
1422
|
+
* Stable capability tier microapps should branch on instead of
|
|
1423
|
+
* `basePlanId` — see docs/appConfig.md §4.1. Known tiers are `ProductMode`;
|
|
1424
|
+
* an unrecognised value is a future tier your code doesn't know about yet
|
|
1425
|
+
* — fail closed to the nearest tier you do understand rather than erroring.
|
|
1426
|
+
productMode?: ProductMode | (string & {})
|
|
1422
1427
|
addOnKeys?: string[]
|
|
1423
1428
|
apps?: string[]
|
|
1424
1429
|
* Explicit overrides only — an absent key is NOT "off". Resolve with
|
|
@@ -1451,6 +1456,8 @@ interface AppConfigSettings {
|
|
|
1451
1456
|
}
|
|
1452
1457
|
```
|
|
1453
1458
|
|
|
1459
|
+
**ProductMode** = `'simple_redirect' | 'rich_redirect' | 'inform' | 'engage'`
|
|
1460
|
+
|
|
1454
1461
|
### appManifest
|
|
1455
1462
|
|
|
1456
1463
|
**AppBundle** (interface)
|
package/dist/docs/appConfig.md
CHANGED
|
@@ -60,9 +60,15 @@ export interface AppEntry {
|
|
|
60
60
|
[k: string]: unknown; // per-module inline settings preserved
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/** Known `productMode` tiers, low → high — see §4.1. Not exhaustive; unrecognised values are future tiers. */
|
|
64
|
+
export type ProductMode = 'simple_redirect' | 'rich_redirect' | 'inform' | 'engage';
|
|
65
|
+
|
|
63
66
|
/** Resolved entitlements — safe for every client to read. */
|
|
64
67
|
export interface SystemBlock {
|
|
68
|
+
/** Internal billing identifier — not a capability signal, see §4. */
|
|
65
69
|
basePlanId?: string;
|
|
70
|
+
/** Stable capability tier — see §4.1. Prefer this over basePlanId. */
|
|
71
|
+
productMode?: ProductMode | (string & {});
|
|
66
72
|
addOnKeys?: string[];
|
|
67
73
|
apps?: string[]; // apps unlocked by add-ons
|
|
68
74
|
features?: Record<string, boolean>; // explicit overrides only — see §4.4 for absent-key resolution
|
|
@@ -184,12 +190,46 @@ Written only by `entitlements-reconcile`. Represents the union of:
|
|
|
184
190
|
- every add-on line item on their Stripe subscription
|
|
185
191
|
- `systemPrivate.overrides` (applied last, always wins)
|
|
186
192
|
|
|
187
|
-
|
|
193
|
+
`basePlanId` is an internal Stripe billing/plan identifier (string,
|
|
194
|
+
sourced from subscription metadata) — a billing concern, not a capability
|
|
195
|
+
signal. Don't branch app behavior on its literal value or its internal
|
|
196
|
+
plan names; that's what `productMode` is for.
|
|
197
|
+
|
|
198
|
+
### 4.1 `productMode`
|
|
199
|
+
|
|
200
|
+
Stable capability tier the app should branch on instead of `basePlanId`
|
|
201
|
+
(a billing identity that can change independently of capability). Written
|
|
202
|
+
by `entitlements-reconcile` on every sync. Typed as `ProductMode` in the
|
|
203
|
+
SDK (`src/types/appConfiguration.ts`) — a union of the known tiers below,
|
|
204
|
+
plus any other string, since future tiers will show up as new values
|
|
205
|
+
before the SDK type is updated to know their names.
|
|
206
|
+
|
|
207
|
+
Ladder, low → high:
|
|
208
|
+
|
|
209
|
+
| `productMode` | Unlocks (roughly) |
|
|
210
|
+
| ------------------ | --------------------------------------------------------------------------------- |
|
|
211
|
+
| `simple_redirect` | Redirect-only: scanning a tag/QR sends the user straight to a URL — no product/proof page, no catalog UI. |
|
|
212
|
+
| `rich_redirect` | Adds a rendered product/proof page (image, description, spec data) — still no deeper apps. |
|
|
213
|
+
| `inform` | Adds informational surfaces beyond the basic page — documentation, provenance, facts-style apps. |
|
|
214
|
+
| `engage` | Adds engagement/ownership capabilities — claim, CRM, loyalty, interaction tracking. Top of the ladder today. |
|
|
215
|
+
|
|
216
|
+
> These four one-liners are inferred from the tier names and ladder
|
|
217
|
+
> ordering, not copied from an authoritative source — worth a sanity check
|
|
218
|
+
> against the actual capability gating before treating them as precise.
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
const mode = cfg.system?.productMode;
|
|
222
|
+
if (mode === 'simple_redirect' || mode === 'rich_redirect') {
|
|
223
|
+
// redirect-only surface — hide catalog UI
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Unknown values (future tiers your code predates) should fail closed to
|
|
228
|
+
the nearest tier you understand, not throw or assume the top tier.
|
|
188
229
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
`hub_enrich`, `hub_engage`.
|
|
230
|
+
Not a feature flag and not a billing key — per-capability gating still
|
|
231
|
+
goes through `features` (§4.4); Stripe price selection still goes
|
|
232
|
+
through `basePlanId` + add-ons.
|
|
193
233
|
|
|
194
234
|
### 4.2 `addOnKeys[]`
|
|
195
235
|
|
|
@@ -253,6 +293,7 @@ about:
|
|
|
253
293
|
| Flag | Gates |
|
|
254
294
|
| -------------- | ---------------------------------------------------------------------- |
|
|
255
295
|
| `analytics` | Analytics surfaces (dashboards, reporting, event exploration). |
|
|
296
|
+
| `authenticity` | Live authenticity tracking — flags genuine vs. counterfeit items and enables auditing/tracing on them (e.g. impossible-travel detection). |
|
|
256
297
|
| `crm` | Customer interaction / CRM — contact records, broadcasts, segments. Sub-features hang off the `crm.*` dotted prefix, e.g. `crm.broadcasts`, `crm.segments`. |
|
|
257
298
|
| `hub` | The Hub module. |
|
|
258
299
|
| `portal` | QR code scan portals. |
|
|
@@ -163,8 +163,7 @@ const showFakeWarning = await validateCondition({
|
|
|
163
163
|
- `isAuthentic` — passes when `itemContext.isAuthentic` is true (`valid`/`rescan`)
|
|
164
164
|
- `notAuthentic` — passes when an `itemContext` was resolved but isn't authentic (`invalid`/`not-found`/`error`)
|
|
165
165
|
- `invalidProof` — passes specifically when resolution was attempted and came back `invalid` or `not-found` — the "someone scanned a fake tag / typed a bad serial" case, as distinct from `noProof` ("nothing was on the URL to check at all")
|
|
166
|
-
- `
|
|
167
|
-
- `isRescan` — authentic but a duplicate/replayed tap (`status === 'rescan'`) — use this to suppress "first scan" celebration UX without treating the tag as fake; `isAuthentic` alone is true for both `isFirstScan` and `isRescan` cases
|
|
166
|
+
- `isRescan` — authentic but a duplicate/replayed tap (`status === 'rescan'`) — use this to suppress "first scan" celebration UX without treating the tag as fake; `isAuthentic` alone is true for both a fresh tap and a rescan
|
|
168
167
|
|
|
169
168
|
## Deprecated
|
|
170
169
|
|
package/dist/docs/utils.md
CHANGED
|
@@ -530,9 +530,9 @@ await utils.validateCondition({
|
|
|
530
530
|
Status types:
|
|
531
531
|
- Claim/ownership (reads `proof`): `'isClaimable'`, `'notClaimable'`, `'isVirtual'`, `'notVirtual'`
|
|
532
532
|
- Presence (reads `proof`): `'hasProof'`, `'noProof'` — `noProof` only means *nothing was attempted*, not that an attempt failed
|
|
533
|
-
- Authenticity (reads `itemContext`): `'isAuthentic'`, `'notAuthentic'`, `'invalidProof'`, `'
|
|
533
|
+
- Authenticity (reads `itemContext`): `'isAuthentic'`, `'notAuthentic'`, `'invalidProof'`, `'isRescan'`
|
|
534
534
|
- `invalidProof` is specifically "an identifier was passed and resolution came back `invalid`/`not-found`", distinct from `noProof`'s "nothing on the URL to check at all"
|
|
535
|
-
- `isAuthentic` is true for both a fresh tap and a rescan. Use `
|
|
535
|
+
- `isAuthentic` is true for both a fresh tap and a rescan. Use `isRescan` (authentic but `status === 'rescan'`) to suppress "first scan" celebration UX without treating the tag as fake
|
|
536
536
|
|
|
537
537
|
#### Version Conditions
|
|
538
538
|
For A/B testing or versioned content:
|
package/dist/openapi.yaml
CHANGED
|
@@ -15666,6 +15666,9 @@ components:
|
|
|
15666
15666
|
properties:
|
|
15667
15667
|
basePlanId:
|
|
15668
15668
|
type: string
|
|
15669
|
+
productMode:
|
|
15670
|
+
type: object
|
|
15671
|
+
additionalProperties: true
|
|
15669
15672
|
addOnKeys:
|
|
15670
15673
|
type: array
|
|
15671
15674
|
items:
|
|
@@ -69,9 +69,25 @@ export interface AppliedOverridesSummary {
|
|
|
69
69
|
accountType?: boolean;
|
|
70
70
|
note?: string;
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Capability ladder for `system.productMode`, low → high. See
|
|
74
|
+
* docs/appConfig.md §4.1 for what each tier unlocks. Not exhaustive going
|
|
75
|
+
* forward — `SystemBlock.productMode` accepts this union for autocomplete
|
|
76
|
+
* but also any other string, since future tiers will show up as new values
|
|
77
|
+
* before this type is updated to know about them.
|
|
78
|
+
*/
|
|
79
|
+
export type ProductMode = 'simple_redirect' | 'rich_redirect' | 'inform' | 'engage';
|
|
72
80
|
/** Resolved entitlements — written only by `entitlements-reconcile`, safe for every client to read. */
|
|
73
81
|
export interface SystemBlock {
|
|
82
|
+
/** Internal billing/plan identifier — see docs/appConfig.md §4.1. Not a capability signal; use `productMode` for that. */
|
|
74
83
|
basePlanId?: string;
|
|
84
|
+
/**
|
|
85
|
+
* Stable capability tier microapps should branch on instead of
|
|
86
|
+
* `basePlanId` — see docs/appConfig.md §4.1. Known tiers are `ProductMode`;
|
|
87
|
+
* an unrecognised value is a future tier your code doesn't know about yet
|
|
88
|
+
* — fail closed to the nearest tier you do understand rather than erroring.
|
|
89
|
+
*/
|
|
90
|
+
productMode?: ProductMode | (string & {});
|
|
75
91
|
addOnKeys?: string[];
|
|
76
92
|
/** Apps unlocked by add-ons. */
|
|
77
93
|
apps?: string[];
|
|
@@ -122,23 +122,19 @@ export interface ValueCondition extends BaseCondition {
|
|
|
122
122
|
* passed but didn't resolve" — see `invalidProof` below for that case.
|
|
123
123
|
* - **Authenticity** (reads `params.itemContext` — see `ItemContext`,
|
|
124
124
|
* docs/item-context.md): `isAuthentic`, `notAuthentic`, `invalidProof`,
|
|
125
|
-
* `
|
|
125
|
+
* `isRescan`.
|
|
126
126
|
* - `invalidProof` is specifically "an identifier was passed and resolution
|
|
127
127
|
* was attempted, but it came back invalid or not-found" — the "someone
|
|
128
128
|
* scanned a fake tag / typed a bad serial" case, as opposed to `noProof`
|
|
129
129
|
* ("nothing was on the URL to check at all").
|
|
130
|
-
* - `isAuthentic` is true for both a fresh tap and a rescan
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* `isRescan` is authentic but a duplicate/replayed tap
|
|
135
|
-
* (`status === 'rescan'`) — e.g. a page refresh or the back button —
|
|
136
|
-
* for suppressing "first scan" celebration UX without treating the tag
|
|
137
|
-
* as fake.
|
|
130
|
+
* - `isAuthentic` is true for both a fresh tap and a rescan. `isRescan` is
|
|
131
|
+
* authentic but a duplicate/replayed tap (`status === 'rescan'`) — e.g.
|
|
132
|
+
* a page refresh or the back button — for suppressing "first scan"
|
|
133
|
+
* celebration UX without treating the tag as fake.
|
|
138
134
|
*/
|
|
139
135
|
export interface ItemStatusCondition extends BaseCondition {
|
|
140
136
|
type: 'itemStatus';
|
|
141
|
-
statusType: 'isClaimable' | 'notClaimable' | 'noProof' | 'hasProof' | 'isVirtual' | 'notVirtual' | 'isAuthentic' | 'notAuthentic' | 'invalidProof' | '
|
|
137
|
+
statusType: 'isClaimable' | 'notClaimable' | 'noProof' | 'hasProof' | 'isVirtual' | 'notVirtual' | 'isAuthentic' | 'notAuthentic' | 'invalidProof' | 'isRescan';
|
|
142
138
|
}
|
|
143
139
|
/**
|
|
144
140
|
* Facet-based condition — gates on the facet values assigned to the current product.
|
|
@@ -330,7 +326,7 @@ export interface ConditionDebugOptions {
|
|
|
330
326
|
* - **value** - Custom field comparisons
|
|
331
327
|
* - **itemStatus** - Proof/item status checks: claimable, virtual, presence
|
|
332
328
|
* (`hasProof`/`noProof`), and authenticity (`isAuthentic`/`notAuthentic`/
|
|
333
|
-
* `invalidProof`/`
|
|
329
|
+
* `invalidProof`/`isRescan`)
|
|
334
330
|
* - **condition** - Nested condition references
|
|
335
331
|
*
|
|
336
332
|
* Conditions can be combined with AND or OR logic.
|
package/dist/utils/conditions.js
CHANGED
|
@@ -175,7 +175,7 @@ async function evaluateConditionEntry(condition, params) {
|
|
|
175
175
|
* - **value** - Custom field comparisons
|
|
176
176
|
* - **itemStatus** - Proof/item status checks: claimable, virtual, presence
|
|
177
177
|
* (`hasProof`/`noProof`), and authenticity (`isAuthentic`/`notAuthentic`/
|
|
178
|
-
* `invalidProof`/`
|
|
178
|
+
* `invalidProof`/`isRescan`)
|
|
179
179
|
* - **condition** - Nested condition references
|
|
180
180
|
*
|
|
181
181
|
* Conditions can be combined with AND or OR logic.
|
|
@@ -819,7 +819,7 @@ async function validateFacet(condition, params) {
|
|
|
819
819
|
* Validate item status condition
|
|
820
820
|
*/
|
|
821
821
|
async function validateItemStatus(condition, params) {
|
|
822
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j
|
|
822
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
823
823
|
switch (condition.statusType) {
|
|
824
824
|
case 'isClaimable':
|
|
825
825
|
return {
|
|
@@ -869,17 +869,11 @@ async function validateItemStatus(condition, params) {
|
|
|
869
869
|
detail: 'Checked that resolution was attempted and came back invalid or not-found (as opposed to noProof, where nothing was attempted at all).',
|
|
870
870
|
context: { itemContextStatus: (_f = params.itemContext) === null || _f === void 0 ? void 0 : _f.status },
|
|
871
871
|
};
|
|
872
|
-
case 'isFirstScan':
|
|
873
|
-
return {
|
|
874
|
-
passed: ((_g = params.itemContext) === null || _g === void 0 ? void 0 : _g.status) === 'valid',
|
|
875
|
-
detail: 'Checked that itemContext is authentic and this is the first time it has been seen (status === valid, not rescan).',
|
|
876
|
-
context: { itemContextStatus: (_h = params.itemContext) === null || _h === void 0 ? void 0 : _h.status },
|
|
877
|
-
};
|
|
878
872
|
case 'isRescan':
|
|
879
873
|
return {
|
|
880
|
-
passed: ((
|
|
874
|
+
passed: ((_g = params.itemContext) === null || _g === void 0 ? void 0 : _g.status) === 'rescan' || !!((_h = params.itemContext) === null || _h === void 0 ? void 0 : _h.isRescan),
|
|
881
875
|
detail: 'Checked that itemContext is authentic but a duplicate/replayed tap (status === rescan).',
|
|
882
|
-
context: { itemContextStatus: (
|
|
876
|
+
context: { itemContextStatus: (_j = params.itemContext) === null || _j === void 0 ? void 0 : _j.status },
|
|
883
877
|
};
|
|
884
878
|
default:
|
|
885
879
|
return {
|
package/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.15.
|
|
3
|
+
Version: 1.15.11 | Generated: 2026-07-20T13:46:09.781Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -1419,6 +1419,11 @@ interface AppliedOverridesSummary {
|
|
|
1419
1419
|
```typescript
|
|
1420
1420
|
interface SystemBlock {
|
|
1421
1421
|
basePlanId?: string
|
|
1422
|
+
* Stable capability tier microapps should branch on instead of
|
|
1423
|
+
* `basePlanId` — see docs/appConfig.md §4.1. Known tiers are `ProductMode`;
|
|
1424
|
+
* an unrecognised value is a future tier your code doesn't know about yet
|
|
1425
|
+
* — fail closed to the nearest tier you do understand rather than erroring.
|
|
1426
|
+
productMode?: ProductMode | (string & {})
|
|
1422
1427
|
addOnKeys?: string[]
|
|
1423
1428
|
apps?: string[]
|
|
1424
1429
|
* Explicit overrides only — an absent key is NOT "off". Resolve with
|
|
@@ -1451,6 +1456,8 @@ interface AppConfigSettings {
|
|
|
1451
1456
|
}
|
|
1452
1457
|
```
|
|
1453
1458
|
|
|
1459
|
+
**ProductMode** = `'simple_redirect' | 'rich_redirect' | 'inform' | 'engage'`
|
|
1460
|
+
|
|
1454
1461
|
### appManifest
|
|
1455
1462
|
|
|
1456
1463
|
**AppBundle** (interface)
|
package/docs/appConfig.md
CHANGED
|
@@ -60,9 +60,15 @@ export interface AppEntry {
|
|
|
60
60
|
[k: string]: unknown; // per-module inline settings preserved
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/** Known `productMode` tiers, low → high — see §4.1. Not exhaustive; unrecognised values are future tiers. */
|
|
64
|
+
export type ProductMode = 'simple_redirect' | 'rich_redirect' | 'inform' | 'engage';
|
|
65
|
+
|
|
63
66
|
/** Resolved entitlements — safe for every client to read. */
|
|
64
67
|
export interface SystemBlock {
|
|
68
|
+
/** Internal billing identifier — not a capability signal, see §4. */
|
|
65
69
|
basePlanId?: string;
|
|
70
|
+
/** Stable capability tier — see §4.1. Prefer this over basePlanId. */
|
|
71
|
+
productMode?: ProductMode | (string & {});
|
|
66
72
|
addOnKeys?: string[];
|
|
67
73
|
apps?: string[]; // apps unlocked by add-ons
|
|
68
74
|
features?: Record<string, boolean>; // explicit overrides only — see §4.4 for absent-key resolution
|
|
@@ -184,12 +190,46 @@ Written only by `entitlements-reconcile`. Represents the union of:
|
|
|
184
190
|
- every add-on line item on their Stripe subscription
|
|
185
191
|
- `systemPrivate.overrides` (applied last, always wins)
|
|
186
192
|
|
|
187
|
-
|
|
193
|
+
`basePlanId` is an internal Stripe billing/plan identifier (string,
|
|
194
|
+
sourced from subscription metadata) — a billing concern, not a capability
|
|
195
|
+
signal. Don't branch app behavior on its literal value or its internal
|
|
196
|
+
plan names; that's what `productMode` is for.
|
|
197
|
+
|
|
198
|
+
### 4.1 `productMode`
|
|
199
|
+
|
|
200
|
+
Stable capability tier the app should branch on instead of `basePlanId`
|
|
201
|
+
(a billing identity that can change independently of capability). Written
|
|
202
|
+
by `entitlements-reconcile` on every sync. Typed as `ProductMode` in the
|
|
203
|
+
SDK (`src/types/appConfiguration.ts`) — a union of the known tiers below,
|
|
204
|
+
plus any other string, since future tiers will show up as new values
|
|
205
|
+
before the SDK type is updated to know their names.
|
|
206
|
+
|
|
207
|
+
Ladder, low → high:
|
|
208
|
+
|
|
209
|
+
| `productMode` | Unlocks (roughly) |
|
|
210
|
+
| ------------------ | --------------------------------------------------------------------------------- |
|
|
211
|
+
| `simple_redirect` | Redirect-only: scanning a tag/QR sends the user straight to a URL — no product/proof page, no catalog UI. |
|
|
212
|
+
| `rich_redirect` | Adds a rendered product/proof page (image, description, spec data) — still no deeper apps. |
|
|
213
|
+
| `inform` | Adds informational surfaces beyond the basic page — documentation, provenance, facts-style apps. |
|
|
214
|
+
| `engage` | Adds engagement/ownership capabilities — claim, CRM, loyalty, interaction tracking. Top of the ladder today. |
|
|
215
|
+
|
|
216
|
+
> These four one-liners are inferred from the tier names and ladder
|
|
217
|
+
> ordering, not copied from an authoritative source — worth a sanity check
|
|
218
|
+
> against the actual capability gating before treating them as precise.
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
const mode = cfg.system?.productMode;
|
|
222
|
+
if (mode === 'simple_redirect' || mode === 'rich_redirect') {
|
|
223
|
+
// redirect-only surface — hide catalog UI
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Unknown values (future tiers your code predates) should fail closed to
|
|
228
|
+
the nearest tier you understand, not throw or assume the top tier.
|
|
188
229
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
`hub_enrich`, `hub_engage`.
|
|
230
|
+
Not a feature flag and not a billing key — per-capability gating still
|
|
231
|
+
goes through `features` (§4.4); Stripe price selection still goes
|
|
232
|
+
through `basePlanId` + add-ons.
|
|
193
233
|
|
|
194
234
|
### 4.2 `addOnKeys[]`
|
|
195
235
|
|
|
@@ -253,6 +293,7 @@ about:
|
|
|
253
293
|
| Flag | Gates |
|
|
254
294
|
| -------------- | ---------------------------------------------------------------------- |
|
|
255
295
|
| `analytics` | Analytics surfaces (dashboards, reporting, event exploration). |
|
|
296
|
+
| `authenticity` | Live authenticity tracking — flags genuine vs. counterfeit items and enables auditing/tracing on them (e.g. impossible-travel detection). |
|
|
256
297
|
| `crm` | Customer interaction / CRM — contact records, broadcasts, segments. Sub-features hang off the `crm.*` dotted prefix, e.g. `crm.broadcasts`, `crm.segments`. |
|
|
257
298
|
| `hub` | The Hub module. |
|
|
258
299
|
| `portal` | QR code scan portals. |
|
package/docs/item-context.md
CHANGED
|
@@ -163,8 +163,7 @@ const showFakeWarning = await validateCondition({
|
|
|
163
163
|
- `isAuthentic` — passes when `itemContext.isAuthentic` is true (`valid`/`rescan`)
|
|
164
164
|
- `notAuthentic` — passes when an `itemContext` was resolved but isn't authentic (`invalid`/`not-found`/`error`)
|
|
165
165
|
- `invalidProof` — passes specifically when resolution was attempted and came back `invalid` or `not-found` — the "someone scanned a fake tag / typed a bad serial" case, as distinct from `noProof` ("nothing was on the URL to check at all")
|
|
166
|
-
- `
|
|
167
|
-
- `isRescan` — authentic but a duplicate/replayed tap (`status === 'rescan'`) — use this to suppress "first scan" celebration UX without treating the tag as fake; `isAuthentic` alone is true for both `isFirstScan` and `isRescan` cases
|
|
166
|
+
- `isRescan` — authentic but a duplicate/replayed tap (`status === 'rescan'`) — use this to suppress "first scan" celebration UX without treating the tag as fake; `isAuthentic` alone is true for both a fresh tap and a rescan
|
|
168
167
|
|
|
169
168
|
## Deprecated
|
|
170
169
|
|
package/docs/utils.md
CHANGED
|
@@ -530,9 +530,9 @@ await utils.validateCondition({
|
|
|
530
530
|
Status types:
|
|
531
531
|
- Claim/ownership (reads `proof`): `'isClaimable'`, `'notClaimable'`, `'isVirtual'`, `'notVirtual'`
|
|
532
532
|
- Presence (reads `proof`): `'hasProof'`, `'noProof'` — `noProof` only means *nothing was attempted*, not that an attempt failed
|
|
533
|
-
- Authenticity (reads `itemContext`): `'isAuthentic'`, `'notAuthentic'`, `'invalidProof'`, `'
|
|
533
|
+
- Authenticity (reads `itemContext`): `'isAuthentic'`, `'notAuthentic'`, `'invalidProof'`, `'isRescan'`
|
|
534
534
|
- `invalidProof` is specifically "an identifier was passed and resolution came back `invalid`/`not-found`", distinct from `noProof`'s "nothing on the URL to check at all"
|
|
535
|
-
- `isAuthentic` is true for both a fresh tap and a rescan. Use `
|
|
535
|
+
- `isAuthentic` is true for both a fresh tap and a rescan. Use `isRescan` (authentic but `status === 'rescan'`) to suppress "first scan" celebration UX without treating the tag as fake
|
|
536
536
|
|
|
537
537
|
#### Version Conditions
|
|
538
538
|
For A/B testing or versioned content:
|
package/openapi.yaml
CHANGED
|
@@ -15666,6 +15666,9 @@ components:
|
|
|
15666
15666
|
properties:
|
|
15667
15667
|
basePlanId:
|
|
15668
15668
|
type: string
|
|
15669
|
+
productMode:
|
|
15670
|
+
type: object
|
|
15671
|
+
additionalProperties: true
|
|
15669
15672
|
addOnKeys:
|
|
15670
15673
|
type: array
|
|
15671
15674
|
items:
|