@proveanything/smartlinks
Version:
Official JavaScript/TypeScript SDK for the Smartlinks API
676 lines (565 loc) • 17 kB
Markdown
# Utility Functions
The smartlinks SDK includes utilities for common tasks like building portal URLs and validating conditional rendering logic.
> **See also:** [API Summary](API_SUMMARY.md) for the complete SDK reference.
## Installation
```typescript
import { utils } from '@proveanything/smartlinks'
```
## Path Builder Utility
Builds full portal URLs by default. Pass in objects (collection, product, batch, etc.) and the function extracts what it needs. Uses `collection.portalUrl` as the domain, or defaults to `https://zt.smartlinks.io`.
### Basic Usage
```typescript
import { utils } from '@proveanything/smartlinks'
// Returns full URL by default (uses collection.portalUrl)
const url = utils.buildPortalPath({
collection: myCollection, // uses collection.portalUrl or default domain
product: myProduct // ownGtin is read from product, not overridden
})
// Returns: https://portal.smartlinks.io/c/abc123/prod123
// Return just the path (no domain)
const path = utils.buildPortalPath({
collection: { shortId: 'abc123' },
productId: 'prod1',
pathOnly: true // Set to true for path only
})
// Returns: /c/abc123/prod1
// Or just IDs if you don't have full objects
const url2 = utils.buildPortalPath({
collection: { shortId: 'abc123' },
productId: 'prod1'
})
// Returns: https://zt.smartlinks.io/c/abc123/prod1 (default domain)
```
## Path Builder Function
### `buildPortalPath(params)`
Builds a full portal URL based on the provided parameters. Returns full URL by default using `collection.portalUrl` or `https://zt.smartlinks.io`.
**Parameters:**
- `collection` (required) - Collection object or `{ shortId: string, portalUrl?: string }`
Extracts: `shortId` and optional `portalUrl` for base domain
- `product` (optional) - Full product object
Extracts: `id`, `gtin`, and `ownGtin` (ownGtin is a critical product setting - read only, never overridden)
- `productId` (optional) - Just a product ID string
Use this if you don't have the full product object
- `batch` (optional) - Batch object
Extracts: `id` and `expiryDate`
- `batchId` (optional) - Just a batch ID string
Use this if you don't have the full batch object (no expiry date)
- `variant` (optional) - Variant object OR just a variant ID string
- If object: extracts `id`
- If string: uses as variant ID
- `proof` (optional) - Proof object OR just a proof ID string
- If object: extracts `id`
- If string: uses as proof ID
- `queryParams` (optional) - Additional query parameters object
- `pathOnly` (optional, default: `false`) - Return only the path without domain
Set to `true` to get `/c/abc/prod` instead of `https://domain.com/c/abc/prod`
**Path Formats:**
- Basic product: `/c/{shortId}/{productId}`
- With proof: `/c/{shortId}/{productId}/{proofId}`
- GTIN (own): `/01/{gtin}` (when product.ownGtin is true)
- GTIN (not own): `/gc/{shortId}/01/{gtin}` (when product.ownGtin is false)
- With batch: adds `/10/{batchId}` and optionally `?17={expiryDate}`
- With variant: adds `/22/{variantId}`
## Examples
### Full URL (Default Behavior)
```typescript
// Returns full URL using collection.portalUrl
utils.buildPortalPath({
collection: myCollection, // uses collection.portalUrl
product: myProduct
})
// Returns: https://portal.smartlinks.io/c/abc123/prod123
// Without portalUrl, uses default domain
utils.buildPortalPath({
collection: { shortId: 'abc123' },
productId: 'prod1'
})
// Returns: https://zt.smartlinks.io/c/abc123/prod1
// With proof object
utils.buildPortalPath({
collection: myCollection,
product: myProduct,
proof: myProof // extracts id
})
// Returns: https://portal.smartlinks.io/c/abc123/prod123/proof789
```
### Path Only (No Domain)
```typescript
// Set pathOnly: true to get just the path
utils.buildPortalPath({
collection: myCollection,
product: myProduct,
pathOnly: true
})
// Returns: /c/abc123/prod123
// Useful when you need to build your own full URL
const path = utils.buildPortalPath({
collection: { shortId: 'abc123' },
productId: 'prod1',
pathOnly: true
})
const customUrl = `https://mycustomdomain.com${path}`
// Result: https://mycustomdomain.com/c/abc123/prod1
```
### GTIN Paths
```typescript
// Product owns GTIN (ownGtin read from product.ownGtin)
utils.buildPortalPath({
collection: myCollection,
product: myProduct // if myProduct.ownGtin === true
})
// Returns: https://portal.smartlinks.io/01/1234567890123
// Product doesn't own GTIN (shared/master GTIN)
utils.buildPortalPath({
collection: myCollection,
product: myProduct // if myProduct.ownGtin === false
})
// Returns: https://portal.smartlinks.io/gc/abc123/01/1234567890123
```
### With Batch Object or ID
```typescript
// Batch object includes expiry date
utils.buildPortalPath({
collection: myCollection,
product: myProduct,
batch: myBatch // extracts id and expiryDate
})
// Returns: https://portal.smartlinks.io/01/1234567890123/10/batch456?17=260630
// Just batch ID (no expiry)
utils.buildPortalPath({
collection: myCollection,
product: myProduct,
batchId: 'batch456' // just string, no expiry date
})
// Returns: https://portal.smartlinks.io/01/1234567890123/10/batch456
```
### With Variant
```typescript
utils.buildPortalPath({
collection: myCollection,
product: myProduct,
batch: myBatch,
variant: myVariant // extracts id
})
// Returns: https://portal.smartlinks.io/01/1234567890123/10/batch456/22/var1?17=260630
// Or just variant ID
utils.buildPortalPath({
collection: myCollection,
product: myProduct,
variant: 'var1'
})
// Returns: https://portal.smartlinks.io/01/1234567890123/22/var1
```
### With Query Parameters
```typescript
utils.buildPortalPath({
collection: myCollection,
product: myProduct,
queryParams: {
utm_source: 'email',
utm_campaign: 'launch',
lang: 'fr'
}
})
// Returns: https://portal.smartlinks.io/c/abc123/prod123?utm_source=email&utm_campaign=launch&lang=fr
```
## Use Cases
### QR Code Generation
```typescript
const qrUrl = utils.buildPortalPath({
collection: myCollection,
product: myProduct, // ownGtin read from product
batch: currentBatch, // includes expiry date
queryParams: { source: 'qr' }
})
// Use qrUrl to generate QR code
```
### Email Campaign Links
```typescript
const emailLink = utils.buildPortalPath({
collection: myCollection,
product: featuredProduct,
queryParams: {
utm_source: 'newsletter',
utm_medium: 'email',
utm_campaign: 'product-launch'
}
})
```
### NFC Tag Programming
```typescript
const nfcUrl = utils.buildPortalPath({
collection: myCollection,
product: myProduct,
queryParams: { nfc: '1' }
})
// Program NFC tag with nfcUrl
```
### Dynamic Navigation
```typescript
function getProductUrl(product: Product, collection: Collection) {
return utils.buildPortalPath({
collection,
product, // ownGtin automatically read from product
batch: product.currentBatch
})
}
```
## TypeScript Support
The utility function is fully typed:
```typescript
import type { PortalPathParams } from '@evrythng/smartlinks'
import type { Product, Collection } from '@evrythng/smartlinks'
const params: PortalPathParams = {
collection: myCollection,
product: myProduct
}
const path = utils.buildPortalPath(params)
```
## Condition Validation Utility
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.
### Basic Usage
```typescript
import { utils } from '@proveanything/smartlinks'
// Check if user is in EU
const canShow = await utils.validateCondition({
condition: {
type: 'and',
conditions: [{
type: 'country',
useRegions: true,
regions: ['eu'],
contains: true
}]
},
user: {
valid: true,
location: { country: 'DE' }
}
})
// Multiple conditions with AND logic
const showFeature = await utils.validateCondition({
condition: {
type: 'and',
conditions: [
{ type: 'user', userType: 'valid' },
{ type: 'device', displays: ['mobile'], contains: true },
{ type: 'date', dateTest: 'after', afterDate: '2026-01-01' }
]
},
user: { valid: true },
stats: { mobile: true }
})
```
### Supported Condition Types
#### Country-Based Conditions
Filter by country codes or predefined regions (EU, EEA, UK, North America, Asia Pacific):
```typescript
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{
type: 'country',
useRegions: true,
regions: ['eu', 'uk'],
contains: true // true = show IN these regions, false = hide IN these regions
}]
},
user: { valid: true, location: { country: 'FR' } }
})
// Or specific countries
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{
type: 'country',
countries: ['US', 'CA', 'MX'],
contains: true
}]
},
user: { valid: true, location: { country: 'US' } }
})
```
#### Device & Platform Conditions
Target specific devices or platforms:
```typescript
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{
type: 'device',
displays: ['ios', 'android', 'mobile'],
contains: true
}]
},
stats: {
platform: { ios: true },
mobile: true
}
})
```
Supported displays: `'android'`, `'ios'`, `'win'`, `'mac'`, `'desktop'`, `'mobile'`
#### User Status Conditions
Check authentication and permissions:
```typescript
// Logged in users only
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{ type: 'user', userType: 'valid' }]
},
user: { valid: true, uid: 'user123' }
})
// Collection admins only
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{ type: 'user', userType: 'admin' }]
},
user: { valid: true, uid: 'user123' },
collection: { id: 'col1', roles: { 'user123': 'admin' } }
})
// Proof owners only
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{ type: 'user', userType: 'owner' }]
},
user: { valid: true, uid: 'user123' },
proof: { userId: 'user123' }
})
```
User types: `'valid'`, `'invalid'`, `'owner'`, `'admin'`, `'group'`
#### Date & Time Conditions
Show/hide content based on dates:
```typescript
// Show after specific date
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{
type: 'date',
dateTest: 'after',
afterDate: '2026-06-01'
}]
}
})
// Show during date range
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{
type: 'date',
dateTest: 'between',
rangeDate: ['2026-01-01', '2026-12-31']
}]
}
})
```
Date tests: `'before'`, `'after'`, `'between'`
#### Product & Tag Conditions
Filter by specific products or product tags:
```typescript
// Specific products
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{
type: 'product',
productIds: ['prod1', 'prod2'],
contains: true
}]
},
product: { id: 'prod1' }
})
// Products with specific tags
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{
type: 'tag',
tags: ['premium', 'featured'],
contains: true
}]
},
product: { id: 'prod1', tags: { premium: true } }
})
```
#### Item Status Conditions
Check proof/item status:
```typescript
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{
type: 'itemStatus',
statusType: 'isClaimable'
}]
},
proof: { claimable: true }
})
```
Status types: `'isClaimable'`, `'notClaimable'`, `'noProof'`, `'hasProof'`, `'isVirtual'`, `'notVirtual'`
#### Version Conditions
For A/B testing or versioned content:
```typescript
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{
type: 'version',
versions: ['v2', 'v3'],
contains: true
}]
},
stats: { version: 'v2' }
})
```
#### Value Comparison Conditions
Compare custom field values:
```typescript
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{
type: 'value',
field: 'product.inventory.quantity', // dot notation
fieldType: 'integer',
validationType: 'greater',
value: 10
}]
},
product: { inventory: { quantity: 25 } }
})
```
Validation types: `'equal'`, `'not'`, `'greater'`, `'less'`
Field types: `'string'`, `'boolean'`, `'integer'`, `'number'`
#### Geofence Conditions
Location-based restrictions using bounding boxes:
```typescript
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{
type: 'geofence',
top: 50.0,
bottom: 40.0,
left: -10.0,
right: 5.0,
contains: true // true = inside box, false = outside box
}]
},
user: {
valid: true,
location: { latitude: 45.0, longitude: 0.0 }
}
})
```
#### Nested Conditions
Reference other condition sets for reusability:
```typescript
await utils.validateCondition({
condition: {
type: 'and',
conditions: [{
type: 'condition',
conditionId: 'mobile-users-condition',
passes: true // true = must pass, false = must fail
}]
},
conditionId: 'mobile-users-condition',
fetchCondition: async (collectionId, conditionId) => {
// Your logic to fetch condition by ID
return { type: 'and', conditions: [...] }
},
collection: { id: 'col1' }
})
```
### Combining Conditions (AND/OR Logic)
```typescript
// AND logic (all must pass)
await utils.validateCondition({
condition: {
type: 'and',
conditions: [
{ type: 'user', userType: 'valid' },
{ type: 'device', displays: ['mobile'], contains: true },
{ type: 'country', useRegions: true, regions: ['eu'], contains: true }
]
},
user: { valid: true, location: { country: 'FR' } },
stats: { mobile: true }
})
// OR logic (any can pass)
await utils.validateCondition({
condition: {
type: 'or',
conditions: [
{ type: 'user', userType: 'admin' },
{ type: 'product', productIds: ['featured1'], contains: true }
]
},
user: { valid: false },
product: { id: 'featured1' }
})
```
### Common Use Cases
#### Page Rendering Control
```typescript
const showPremiumContent = await utils.validateCondition({
condition: {
type: 'and',
conditions: [
{ type: 'user', userType: 'valid' },
{ type: 'tag', tags: ['premium'], contains: true }
]
},
user: { valid: true },
product: { id: 'prod1', tags: { premium: true } }
})
if (showPremiumContent) {
// Render premium content
}
```
#### Regional Feature Rollout
```typescript
const showNewFeature = await utils.validateCondition({
condition: {
type: 'and',
conditions: [
{ type: 'country', useRegions: true, regions: ['northamerica'], contains: true },
{ type: 'date', dateTest: 'after', afterDate: '2026-03-01' }
]
},
user: { valid: true, location: { country: 'US' } }
})
```
#### Mobile-Only Features
```typescript
const showMobileFeature = await utils.validateCondition({
condition: {
type: 'and',
conditions: [
{ type: 'device', displays: ['mobile'], contains: true }
]
},
stats: { mobile: true, platform: { ios: true } }
})
```
### Available Regions
Predefined regions for country conditions:
- **eu** - European Union (27 member states)
- **eea** - European Economic Area (EU + EFTA countries)
- **uk** - United Kingdom
- **northamerica** - US, CA, MX
- **asiapacific** - AU, NZ, JP, KR, SG, HK, TW, TH, MY, PH, ID, VN, IN
## More Examples
See [examples/utils-demo.ts](../examples/utils-demo.ts) for comprehensive examples.
## Related Documentation
- **[API Summary](API_SUMMARY.md)** - Complete SDK reference with all namespaces and functions
- **[QR Codes](API_SUMMARY.md#qr)** - QR code lookup functions that work with generated paths
- **[NFC](API_SUMMARY.md#nfc)** - NFC tag claiming and validation
- **[Collections](API_SUMMARY.md#collection)** - Collection management functions
- **[Products](API_SUMMARY.md#product)** - Product CRUD operations
- **[Batches](API_SUMMARY.md#batch)** - Batch management
- **[Proofs](API_SUMMARY.md#proof)** - Proof creation and claiming