@proveanything/smartlinks 2.0.65 → 2.0.67
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/ai-tools.js +1 -1
- package/dist/api/appConfiguration.d.ts +23 -0
- package/dist/api/appConfiguration.js +32 -0
- package/dist/api/tags.d.ts +8 -8
- package/dist/api/tags.js +10 -10
- package/dist/docs/API_SUMMARY.md +43 -7
- package/dist/docs/agent-tools.md +1 -1
- package/dist/docs/ai.md +6 -4
- package/dist/docs/app-manifest.md +1 -1
- package/dist/docs/appConfig.md +37 -0
- package/dist/docs/connectors.md +1 -1
- package/dist/docs/server-functions.md +60 -4
- package/dist/openapi.yaml +71 -3
- package/dist/types/ai.d.ts +40 -2
- package/dist/types/appConfiguration.d.ts +8 -0
- package/dist/types/appManifest.d.ts +10 -0
- package/docs/API_SUMMARY.md +43 -7
- package/docs/agent-tools.md +1 -1
- package/docs/ai.md +6 -4
- package/docs/app-manifest.md +1 -1
- package/docs/appConfig.md +37 -0
- package/docs/connectors.md +1 -1
- package/docs/server-functions.md +60 -4
- package/openapi.yaml +71 -3
- package/package.json +1 -1
package/dist/ai-tools.js
CHANGED
|
@@ -62,7 +62,7 @@ export const BUILTIN_AI_TOOLS = [
|
|
|
62
62
|
{ name: 'image.transform', group: 'media', capabilities: ['media:image'],
|
|
63
63
|
description: 'Transform an image: resize/crop/rotate/flip/grayscale/tint/format-convert/compress → hosted URL.' },
|
|
64
64
|
{ name: 'pdf.create', group: 'media', capabilities: ['media:pdf'],
|
|
65
|
-
description: 'Render HTML to a PDF and
|
|
65
|
+
description: 'Render HTML (or one fragment per page) to a PDF, 1:1: exact page sizes (width/height/unit) for labels, many pages per call, text and SVG kept vector, no scripts. Returns a public URL, an expiring private link, or base64; identical inputs reuse the cached render.' },
|
|
66
66
|
{ name: 'pdf.fill', group: 'media', capabilities: ['media:pdf'],
|
|
67
67
|
description: 'Fill an AcroForm PDF\'s fields from a { field: value } map → hosted URL.' },
|
|
68
68
|
{ name: 'pdf.merge', group: 'media', capabilities: ['media:pdf'],
|
|
@@ -483,4 +483,27 @@ export declare namespace appConfiguration {
|
|
|
483
483
|
* ```
|
|
484
484
|
*/
|
|
485
485
|
function isFeatureEnabledSync(collectionId: string, flag: string): boolean | undefined;
|
|
486
|
+
/**
|
|
487
|
+
* Has the collection SWITCHED ON this part of the platform (`appConfig.enabled[key]`)? A missing
|
|
488
|
+
* key is on — only an explicit `false` is off. This is "does this business use it", not "does the
|
|
489
|
+
* plan allow it": for something a plan gates, check `resolveFeature()` as well. docs/appConfig.md §8a.
|
|
490
|
+
*/
|
|
491
|
+
function resolveEnabled(cfg: {
|
|
492
|
+
enabled?: Record<string, boolean>;
|
|
493
|
+
} | null | undefined, key: string): boolean;
|
|
494
|
+
/**
|
|
495
|
+
* Has the collection switched on `key` (`products`, `crm`, `analytics`, `hub`, `portal`,
|
|
496
|
+
* `printStudio`, or an app's own key)? Hide what's off: a website-only business switches off
|
|
497
|
+
* products and the CRM, so menus, cards and setup steps for them shouldn't appear.
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```typescript
|
|
501
|
+
* if (await appConfiguration.isEnabled(collectionId, 'products')) showProductsMenu();
|
|
502
|
+
* ```
|
|
503
|
+
*/
|
|
504
|
+
function isEnabled(collectionId: string, key: string, options?: {
|
|
505
|
+
force?: boolean;
|
|
506
|
+
}): Promise<boolean>;
|
|
507
|
+
/** Cache-only `isEnabled` — `undefined` until `getAppConfig()` has been read this page load. */
|
|
508
|
+
function isEnabledSync(collectionId: string, key: string): boolean | undefined;
|
|
486
509
|
}
|
|
@@ -620,4 +620,36 @@ export var appConfiguration;
|
|
|
620
620
|
return resolveFeature(cfg.system, flag);
|
|
621
621
|
}
|
|
622
622
|
appConfiguration.isFeatureEnabledSync = isFeatureEnabledSync;
|
|
623
|
+
/**
|
|
624
|
+
* Has the collection SWITCHED ON this part of the platform (`appConfig.enabled[key]`)? A missing
|
|
625
|
+
* key is on — only an explicit `false` is off. This is "does this business use it", not "does the
|
|
626
|
+
* plan allow it": for something a plan gates, check `resolveFeature()` as well. docs/appConfig.md §8a.
|
|
627
|
+
*/
|
|
628
|
+
function resolveEnabled(cfg, key) {
|
|
629
|
+
var _a;
|
|
630
|
+
return ((_a = cfg === null || cfg === void 0 ? void 0 : cfg.enabled) === null || _a === void 0 ? void 0 : _a[key]) !== false;
|
|
631
|
+
}
|
|
632
|
+
appConfiguration.resolveEnabled = resolveEnabled;
|
|
633
|
+
/**
|
|
634
|
+
* Has the collection switched on `key` (`products`, `crm`, `analytics`, `hub`, `portal`,
|
|
635
|
+
* `printStudio`, or an app's own key)? Hide what's off: a website-only business switches off
|
|
636
|
+
* products and the CRM, so menus, cards and setup steps for them shouldn't appear.
|
|
637
|
+
*
|
|
638
|
+
* @example
|
|
639
|
+
* ```typescript
|
|
640
|
+
* if (await appConfiguration.isEnabled(collectionId, 'products')) showProductsMenu();
|
|
641
|
+
* ```
|
|
642
|
+
*/
|
|
643
|
+
async function isEnabled(collectionId, key, options) {
|
|
644
|
+
return resolveEnabled(await getAppConfig(collectionId, options), key);
|
|
645
|
+
}
|
|
646
|
+
appConfiguration.isEnabled = isEnabled;
|
|
647
|
+
/** Cache-only `isEnabled` — `undefined` until `getAppConfig()` has been read this page load. */
|
|
648
|
+
function isEnabledSync(collectionId, key) {
|
|
649
|
+
const cfg = cache.peek(appConfigCacheKey(collectionId));
|
|
650
|
+
if (!cfg)
|
|
651
|
+
return undefined;
|
|
652
|
+
return resolveEnabled(cfg, key);
|
|
653
|
+
}
|
|
654
|
+
appConfiguration.isEnabledSync = isEnabledSync;
|
|
623
655
|
})(appConfiguration || (appConfiguration = {}));
|
package/dist/api/tags.d.ts
CHANGED
|
@@ -6,9 +6,9 @@ import type { TagIndexEntry, CreateTagRequest, CreateTagResponse, BatchCreateTag
|
|
|
6
6
|
* etc.) and products, proofs, or any polymorphic object in the platform.
|
|
7
7
|
*
|
|
8
8
|
* ### Two-tier architecture
|
|
9
|
-
* - **Per-org shard** (`tags` table)
|
|
9
|
+
* - **Per-org shard** (`tags` table) — full tag data; used for all collection-scoped
|
|
10
10
|
* queries.
|
|
11
|
-
* - **Shared shard** (`tag_index` table)
|
|
11
|
+
* - **Shared shard** (`tag_index` table) — `tagId → collectionId` routing only; used
|
|
12
12
|
* when the collection is not yet known.
|
|
13
13
|
*
|
|
14
14
|
* 99% of callers will know the collection and should use collection-scoped endpoints
|
|
@@ -49,7 +49,7 @@ export declare namespace tags {
|
|
|
49
49
|
*
|
|
50
50
|
* Tags with `productId` but no `proofId` automatically get serial numbers.
|
|
51
51
|
* Serial number generation is grouped by `(productId, variantId, batchId)` for
|
|
52
|
-
* efficiency. Partial success is possible
|
|
52
|
+
* efficiency. Partial success is possible — check `results` for individual outcomes.
|
|
53
53
|
*
|
|
54
54
|
* @param collectionId - Collection context
|
|
55
55
|
* @param data - Batch payload; `force` applies to all tags in the batch
|
|
@@ -78,7 +78,7 @@ export declare namespace tags {
|
|
|
78
78
|
/**
|
|
79
79
|
* Update a tag (admin).
|
|
80
80
|
*
|
|
81
|
-
* Partial update
|
|
81
|
+
* Partial update — only provided fields are changed. `metadata` is
|
|
82
82
|
* deep-merged with the existing value. Pass `refType: null, refId: null`
|
|
83
83
|
* to clear the polymorphic ref.
|
|
84
84
|
*
|
|
@@ -130,7 +130,7 @@ export declare namespace tags {
|
|
|
130
130
|
*/
|
|
131
131
|
function list(collectionId: string, params?: ListTagsRequest): Promise<ListTagsResponse>;
|
|
132
132
|
/**
|
|
133
|
-
* Reverse lookup
|
|
133
|
+
* Reverse lookup — find all tags linked to a given object (admin).
|
|
134
134
|
*
|
|
135
135
|
* Uses a compound index on `(orgId, refType, refId)` on the per-org shard.
|
|
136
136
|
* No embed support on the admin side.
|
|
@@ -149,7 +149,7 @@ export declare namespace tags {
|
|
|
149
149
|
*/
|
|
150
150
|
function byRef(collectionId: string, params: ReverseTagLookupParams): Promise<ReverseTagLookupResponse>;
|
|
151
151
|
/**
|
|
152
|
-
* Global tag resolve
|
|
152
|
+
* Global tag resolve — returns `{ tagId, collectionId }` only.
|
|
153
153
|
*
|
|
154
154
|
* Use this **only** when you have a raw `tagId` and do not yet know which
|
|
155
155
|
* collection it belongs to. Queries the shared `tag_index` shard.
|
|
@@ -159,7 +159,7 @@ export declare namespace tags {
|
|
|
159
159
|
* > Use the collection-scoped `publicByRef` instead.
|
|
160
160
|
*
|
|
161
161
|
* @param tagId - Physical tag identifier
|
|
162
|
-
* @returns `{ tagId, collectionId }`
|
|
162
|
+
* @returns `{ tagId, collectionId }` — routing info only, no full tag data
|
|
163
163
|
*
|
|
164
164
|
* @example
|
|
165
165
|
* ```typescript
|
|
@@ -179,7 +179,7 @@ export declare namespace tags {
|
|
|
179
179
|
* `GET /public/collection/:collectionId/tags/:tagId?embed=product,proof,container,ref`
|
|
180
180
|
*
|
|
181
181
|
* Supported `embed` values: `'product'`, `'proof'`, `'container'`, `'ref'`
|
|
182
|
-
* (`'collection'` is not supported
|
|
182
|
+
* (`'collection'` is not supported — the collection is already known from the URL).
|
|
183
183
|
*
|
|
184
184
|
* @param collectionId - Collection context
|
|
185
185
|
* @param tagId - Physical tag identifier
|
package/dist/api/tags.js
CHANGED
|
@@ -7,9 +7,9 @@ import { request, post, put, del } from "../http.js";
|
|
|
7
7
|
* etc.) and products, proofs, or any polymorphic object in the platform.
|
|
8
8
|
*
|
|
9
9
|
* ### Two-tier architecture
|
|
10
|
-
* - **Per-org shard** (`tags` table)
|
|
10
|
+
* - **Per-org shard** (`tags` table) — full tag data; used for all collection-scoped
|
|
11
11
|
* queries.
|
|
12
|
-
* - **Shared shard** (`tag_index` table)
|
|
12
|
+
* - **Shared shard** (`tag_index` table) — `tagId → collectionId` routing only; used
|
|
13
13
|
* when the collection is not yet known.
|
|
14
14
|
*
|
|
15
15
|
* 99% of callers will know the collection and should use collection-scoped endpoints
|
|
@@ -58,7 +58,7 @@ export var tags;
|
|
|
58
58
|
*
|
|
59
59
|
* Tags with `productId` but no `proofId` automatically get serial numbers.
|
|
60
60
|
* Serial number generation is grouped by `(productId, variantId, batchId)` for
|
|
61
|
-
* efficiency. Partial success is possible
|
|
61
|
+
* efficiency. Partial success is possible — check `results` for individual outcomes.
|
|
62
62
|
*
|
|
63
63
|
* @param collectionId - Collection context
|
|
64
64
|
* @param data - Batch payload; `force` applies to all tags in the batch
|
|
@@ -95,7 +95,7 @@ export var tags;
|
|
|
95
95
|
/**
|
|
96
96
|
* Update a tag (admin).
|
|
97
97
|
*
|
|
98
|
-
* Partial update
|
|
98
|
+
* Partial update — only provided fields are changed. `metadata` is
|
|
99
99
|
* deep-merged with the existing value. Pass `refType: null, refId: null`
|
|
100
100
|
* to clear the polymorphic ref.
|
|
101
101
|
*
|
|
@@ -175,7 +175,7 @@ export var tags;
|
|
|
175
175
|
}
|
|
176
176
|
tags.list = list;
|
|
177
177
|
/**
|
|
178
|
-
* Reverse lookup
|
|
178
|
+
* Reverse lookup — find all tags linked to a given object (admin).
|
|
179
179
|
*
|
|
180
180
|
* Uses a compound index on `(orgId, refType, refId)` on the per-org shard.
|
|
181
181
|
* No embed support on the admin side.
|
|
@@ -201,10 +201,10 @@ export var tags;
|
|
|
201
201
|
}
|
|
202
202
|
tags.byRef = byRef;
|
|
203
203
|
// ============================================================================
|
|
204
|
-
// Public Endpoints
|
|
204
|
+
// Public Endpoints — global resolve (collection unknown)
|
|
205
205
|
// ============================================================================
|
|
206
206
|
/**
|
|
207
|
-
* Global tag resolve
|
|
207
|
+
* Global tag resolve — returns `{ tagId, collectionId }` only.
|
|
208
208
|
*
|
|
209
209
|
* Use this **only** when you have a raw `tagId` and do not yet know which
|
|
210
210
|
* collection it belongs to. Queries the shared `tag_index` shard.
|
|
@@ -214,7 +214,7 @@ export var tags;
|
|
|
214
214
|
* > Use the collection-scoped `publicByRef` instead.
|
|
215
215
|
*
|
|
216
216
|
* @param tagId - Physical tag identifier
|
|
217
|
-
* @returns `{ tagId, collectionId }`
|
|
217
|
+
* @returns `{ tagId, collectionId }` — routing info only, no full tag data
|
|
218
218
|
*
|
|
219
219
|
* @example
|
|
220
220
|
* ```typescript
|
|
@@ -233,7 +233,7 @@ export var tags;
|
|
|
233
233
|
}
|
|
234
234
|
tags.resolveTag = resolveTag;
|
|
235
235
|
// ============================================================================
|
|
236
|
-
// Public Endpoints
|
|
236
|
+
// Public Endpoints — collection-scoped
|
|
237
237
|
// ============================================================================
|
|
238
238
|
/**
|
|
239
239
|
* Single tag lookup with optional embedded data (public).
|
|
@@ -241,7 +241,7 @@ export var tags;
|
|
|
241
241
|
* `GET /public/collection/:collectionId/tags/:tagId?embed=product,proof,container,ref`
|
|
242
242
|
*
|
|
243
243
|
* Supported `embed` values: `'product'`, `'proof'`, `'container'`, `'ref'`
|
|
244
|
-
* (`'collection'` is not supported
|
|
244
|
+
* (`'collection'` is not supported — the collection is already known from the URL).
|
|
245
245
|
*
|
|
246
246
|
* @param collectionId - Collection context
|
|
247
247
|
* @param tagId - Physical tag identifier
|
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.67 | Generated: 2026-10-15T17:52:10.614Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -1600,10 +1600,35 @@ interface PdfClip {
|
|
|
1600
1600
|
**PdfCreateArgs** (interface)
|
|
1601
1601
|
```typescript
|
|
1602
1602
|
interface PdfCreateArgs {
|
|
1603
|
-
html
|
|
1603
|
+
html?: string
|
|
1604
|
+
pages?: string[]
|
|
1605
|
+
head?: string
|
|
1606
|
+
width?: number
|
|
1607
|
+
height?: number
|
|
1608
|
+
unit?: 'mm' | 'cm' | 'in' | 'pt' | 'px'
|
|
1604
1609
|
format?: string
|
|
1605
1610
|
landscape?: boolean
|
|
1606
1611
|
margin?: string
|
|
1612
|
+
return?: 'url' | 'signedUrl' | 'base64'
|
|
1613
|
+
expiresIn?: number
|
|
1614
|
+
filename?: string
|
|
1615
|
+
cache?: boolean
|
|
1616
|
+
}
|
|
1617
|
+
```
|
|
1618
|
+
|
|
1619
|
+
**PdfCreateResult** (interface)
|
|
1620
|
+
```typescript
|
|
1621
|
+
interface PdfCreateResult {
|
|
1622
|
+
url?: string
|
|
1623
|
+
hostedUrl?: string
|
|
1624
|
+
base64?: string
|
|
1625
|
+
expiresAt?: string
|
|
1626
|
+
contentType: 'application/pdf'
|
|
1627
|
+
size: number
|
|
1628
|
+
pageCount: number
|
|
1629
|
+
cached: boolean
|
|
1630
|
+
renderer?: string
|
|
1631
|
+
warnings?: string[]
|
|
1607
1632
|
}
|
|
1608
1633
|
```
|
|
1609
1634
|
|
|
@@ -2479,6 +2504,7 @@ interface AppConfigSettings {
|
|
|
2479
2504
|
addOns?: string[]
|
|
2480
2505
|
requestedBasePlanId?: string
|
|
2481
2506
|
itemRecordMode?: 'registered' | 'owned' | null
|
|
2507
|
+
enabled?: Record<string, boolean>
|
|
2482
2508
|
system?: SystemBlock
|
|
2483
2509
|
}
|
|
2484
2510
|
```
|
|
@@ -2785,6 +2811,7 @@ interface ServerFunctionSl {
|
|
|
2785
2811
|
forCaller(): Promise<{ contactId: string } | null>;
|
|
2786
2812
|
};
|
|
2787
2813
|
interactions: { record(event: ServerFunctionInteractionEvent): Promise<{ eventId: string }> };
|
|
2814
|
+
tools: { run<T = any>(name: string, args?: Record<string, any>): Promise<T> };
|
|
2788
2815
|
products: any;
|
|
2789
2816
|
attestations: any;
|
|
2790
2817
|
appData: AppDataHandle & { global: AppDataHandle; collection: AppDataHandle };
|
|
@@ -10886,6 +10913,15 @@ Check whether a feature flag is enabled for a collection, per `appConfig.system.
|
|
|
10886
10913
|
**isFeatureEnabledSync**(collectionId: string, flag: string) → `boolean | undefined`
|
|
10887
10914
|
Synchronous, cache-only check for a feature flag. Returns `undefined` if `getAppConfig()` / `isFeatureEnabled()` hasn't been called yet for this collection this page load (i.e. nothing to read without an await) — treat `undefined` as "not yet known", not as "disabled". ```typescript // Warm the cache once, e.g. in a top-level effect: useEffect(() => { appConfiguration.isFeatureEnabled(collectionId, 'custom_domain') }, [collectionId]); // Elsewhere, read synchronously without an await: const enabled = appConfiguration.isFeatureEnabledSync(collectionId, 'custom_domain'); if (enabled === undefined) { // not warmed yet — fall back to a loading state or the async version } ```
|
|
10888
10915
|
|
|
10916
|
+
**resolveEnabled**(cfg: { enabled?: Record<string, boolean> } | null | undefined, key: string) → `boolean`
|
|
10917
|
+
Has the collection SWITCHED ON this part of the platform (`appConfig.enabled[key]`)? A missing key is on — only an explicit `false` is off. This is "does this business use it", not "does the plan allow it": for something a plan gates, check `resolveFeature()` as well. docs/appConfig.md §8a.
|
|
10918
|
+
|
|
10919
|
+
**isEnabled**(collectionId: string, key: string, options?: { force?: boolean }) → `Promise<boolean>`
|
|
10920
|
+
Has the collection switched on `key` (`products`, `crm`, `analytics`, `hub`, `portal`, `printStudio`, or an app's own key)? Hide what's off: a website-only business switches off products and the CRM, so menus, cards and setup steps for them shouldn't appear. ```typescript if (await appConfiguration.isEnabled(collectionId, 'products')) showProductsMenu(); ```
|
|
10921
|
+
|
|
10922
|
+
**isEnabledSync**(collectionId: string, key: string) → `boolean | undefined`
|
|
10923
|
+
Cache-only `isEnabled` — `undefined` until `getAppConfig()` has been read this page load.
|
|
10924
|
+
|
|
10889
10925
|
### asset
|
|
10890
10926
|
|
|
10891
10927
|
**upload**(options: UploadAssetOptions) → `Promise<Asset>`
|
|
@@ -12805,7 +12841,7 @@ Create a single tag mapping (admin). If `productId` is set without `proofId`, a
|
|
|
12805
12841
|
|
|
12806
12842
|
**createBatch**(collectionId: string,
|
|
12807
12843
|
data: BatchCreateTagRequest) → `Promise<BatchCreateResult>`
|
|
12808
|
-
Batch-create tags (admin). Tags with `productId` but no `proofId` automatically get serial numbers. Serial number generation is grouped by `(productId, variantId, batchId)` for efficiency. Partial success is possible
|
|
12844
|
+
Batch-create tags (admin). Tags with `productId` but no `proofId` automatically get serial numbers. Serial number generation is grouped by `(productId, variantId, batchId)` for efficiency. Partial success is possible — check `results` for individual outcomes. ```typescript const result = await tags.createBatch('coll_123', { tags: [ { tagId: 'NFC-001', productId: 'prod_456', batchId: 'batch_2026_01' }, { tagId: 'NFC-002', productId: 'prod_456', batchId: 'batch_2026_01' }, ], }) console.log(`Created: ${result.summary.created}, Conflicts: ${result.summary.conflicts}`) ```
|
|
12809
12845
|
|
|
12810
12846
|
**get**(collectionId: string,
|
|
12811
12847
|
tagId: string) → `Promise<GetTagResponse>`
|
|
@@ -12814,7 +12850,7 @@ Get a single tag by `tagId` (admin).
|
|
|
12814
12850
|
**update**(collectionId: string,
|
|
12815
12851
|
tagId: string,
|
|
12816
12852
|
data: UpdateTagRequest) → `Promise<UpdateTagResponse>`
|
|
12817
|
-
Update a tag (admin). Partial update
|
|
12853
|
+
Update a tag (admin). Partial update — only provided fields are changed. `metadata` is deep-merged with the existing value. Pass `refType: null, refId: null` to clear the polymorphic ref. ```typescript const updated = await tags.update('coll_123', 'NFC-001', { variantId: 'var_premium', metadata: { notes: 'Updated to premium variant' }, }) // Clear polymorphic ref await tags.update('coll_123', 'NFC-001', { refType: null, refId: null }) ```
|
|
12818
12854
|
|
|
12819
12855
|
**remove**(collectionId: string,
|
|
12820
12856
|
tagId: string) → `Promise<DeleteTagResponse>`
|
|
@@ -12826,15 +12862,15 @@ List tags with optional filters and pagination (admin). ```typescript // All tag
|
|
|
12826
12862
|
|
|
12827
12863
|
**byRef**(collectionId: string,
|
|
12828
12864
|
params: ReverseTagLookupParams) → `Promise<ReverseTagLookupResponse>`
|
|
12829
|
-
Reverse lookup
|
|
12865
|
+
Reverse lookup — find all tags linked to a given object (admin). Uses a compound index on `(orgId, refType, refId)` on the per-org shard. No embed support on the admin side. ```typescript const { tags: linked } = await tags.byRef('coll_123', { refType: 'container', refId: 'container-uuid', }) ```
|
|
12830
12866
|
|
|
12831
12867
|
**resolveTag**(tagId: string) → `Promise<TagIndexEntry>`
|
|
12832
|
-
Global tag resolve
|
|
12868
|
+
Global tag resolve — returns `{ tagId, collectionId }` only. Use this **only** when you have a raw `tagId` and do not yet know which collection it belongs to. Queries the shared `tag_index` shard. Once `collectionId` is resolved, call `publicGetByCollection` for full data. > The global `/public/tags/by-ref` endpoint has been removed. > Use the collection-scoped `publicByRef` instead. ```typescript // Step 1: resolve collection const { collectionId } = await tags.resolveTag('NFC-001') // Step 2: full lookup with embedded data const { tag, embedded } = await tags.publicGetByCollection( collectionId, 'NFC-001', 'product,proof' ) ```
|
|
12833
12869
|
|
|
12834
12870
|
**publicGetByCollection**(collectionId: string,
|
|
12835
12871
|
tagId: string,
|
|
12836
12872
|
embed?: string) → `Promise<PublicGetTagResponse>`
|
|
12837
|
-
Single tag lookup with optional embedded data (public). `GET /public/collection/:collectionId/tags/:tagId?embed=product,proof,container,ref` Supported `embed` values: `'product'`, `'proof'`, `'container'`, `'ref'` (`'collection'` is not supported
|
|
12873
|
+
Single tag lookup with optional embedded data (public). `GET /public/collection/:collectionId/tags/:tagId?embed=product,proof,container,ref` Supported `embed` values: `'product'`, `'proof'`, `'container'`, `'ref'` (`'collection'` is not supported — the collection is already known from the URL). ```typescript const { tag, embedded } = await tags.publicGetByCollection( 'coll_123', 'NFC-001', 'product,proof' ) const product = embedded.products?.[tag.productId!] const proof = embedded.proofs?.[tag.proofId!] ```
|
|
12838
12874
|
|
|
12839
12875
|
**lookupTags**(collectionId: string,
|
|
12840
12876
|
data: LookupTagsRequest) → `Promise<TagLookupResponse>`
|
package/dist/docs/agent-tools.md
CHANGED
package/dist/docs/ai.md
CHANGED
|
@@ -323,7 +323,7 @@ in `input`.
|
|
|
323
323
|
| `image.fromReference` | Image-to-image: generate guided by reference image(s). |
|
|
324
324
|
| `image.searchStock` | Search real stock photos (Unsplash). |
|
|
325
325
|
| `image.transform` | Resize / crop / rotate / grayscale / format-convert / compress → hosted URL. |
|
|
326
|
-
| `pdf.create` | Render HTML → PDF
|
|
326
|
+
| `pdf.create` | Render HTML (or one fragment per page) → PDF, 1:1: exact `width`/`height`/`unit` for labels, `pages[]` for many pages in one render, no scripts, only SmartLinks/Google Fonts/data: resources. `return`: `url` (public), `signedUrl` (expiring), `base64`. Cached by input. |
|
|
327
327
|
| `pdf.fill` | Fill an AcroForm PDF's fields (`{ field: value }`) → hosted URL. |
|
|
328
328
|
| `pdf.merge` | Merge several PDFs into one, in order → hosted URL. |
|
|
329
329
|
| `pdf.inspect` | Cheap, no-AI introspection: page count/sizes, which pages have a real text layer, raster present, and a routing hint (`text` vs `vision`). |
|
|
@@ -402,9 +402,11 @@ if (!ready.result.report.compliant) showFindings(ready.result.report.findings)
|
|
|
402
402
|
- **A review UI that flags guessed fields** → `pdf.extract` with `includeConfidence` (per-field
|
|
403
403
|
confidence + source) and `includeBoxes` (per-field bbox on text-native pages) in `fieldsMeta`.
|
|
404
404
|
- **Produce a PDF** → `pdf.create` (HTML → PDF), `pdf.fill` (populate an AcroForm's fields),
|
|
405
|
-
`pdf.merge` (combine several).
|
|
406
|
-
`
|
|
407
|
-
|
|
405
|
+
`pdf.merge` (combine several). `pdf.create` takes an exact page size (`width`, `height`, `unit`) or a
|
|
406
|
+
`format`, `html` or `pages: [html, …]` (one per page, sharing `head`), and returns `url` (public),
|
|
407
|
+
a `signedUrl` that expires, or `base64` (`return`). JavaScript doesn't run, and images, fonts and
|
|
408
|
+
stylesheets load only from SmartLinks hosts, Google Fonts or `data:` URIs. From a server function:
|
|
409
|
+
`ctx.sl.tools.run('pdf.create', …)` with the `tools:pdf.create` capability (server-functions.md).
|
|
408
410
|
|
|
409
411
|
Every tool is also directly callable without the model loop via `ai.tools.run(collectionId, name,
|
|
410
412
|
args)` — e.g. render a page or extract fields straight from a UI, no agent round-trip:
|
|
@@ -428,7 +428,7 @@ this is just the manifest shape.
|
|
|
428
428
|
|
|
429
429
|
| Field | Type | Description |
|
|
430
430
|
|-------|------|-------------|
|
|
431
|
-
| `files.js.umd` | string | UMD bundle path (e.g. `dist/functions.umd.js`) — a self-contained bundle exporting each handler by name |
|
|
431
|
+
| `files.js.umd` | string | UMD bundle path, relative to the published build like the other bundles (e.g. `functions.umd.js`, built to `dist/functions.umd.js`) — a self-contained bundle exporting each handler by name |
|
|
432
432
|
| `definitions[]` | object[] | One entry per function |
|
|
433
433
|
| `definitions[].name` | string | Function name; also the handler export name (override with `handler`) |
|
|
434
434
|
| `definitions[].trigger` | object | `{ type: "http"|"event"|"cron", … }` |
|
package/dist/docs/appConfig.md
CHANGED
|
@@ -36,6 +36,8 @@ export interface AppConfigSettings {
|
|
|
36
36
|
|
|
37
37
|
/** How the account handles individual physical items. */
|
|
38
38
|
itemRecordMode?: 'registered' | 'owned' | null;
|
|
39
|
+
/** What the collection has switched on (absent key = on) — see §8a. */
|
|
40
|
+
enabled?: Record<string, boolean>;
|
|
39
41
|
|
|
40
42
|
// ── SYSTEM-OWNED, PUBLIC (reconcile writes; client read-only) ─────
|
|
41
43
|
/** Resolved entitlement truth. Read this to gate features. */
|
|
@@ -501,6 +503,41 @@ the customer add-ons page (`src/components/pricing/ItemHandlingSection`)
|
|
|
501
503
|
because these settings change the whole UX shape (claim, ownership,
|
|
502
504
|
CRM visibility), not just a capability flag.
|
|
503
505
|
|
|
506
|
+
## 8a. What's switched on (`enabled`)
|
|
507
|
+
|
|
508
|
+
A root-level, user-owned map (like `itemRecordMode`; reconcile never touches it) saying which parts of
|
|
509
|
+
the platform this collection **uses** — separate from what its plan **allows** (`system.features`):
|
|
510
|
+
|
|
511
|
+
| | Plan allows (`system.features`) | Switched on (`enabled`) |
|
|
512
|
+
|---|---|---|
|
|
513
|
+
| Written by | billing (`entitlements-reconcile`) | the collection: sign-up wizards, admins |
|
|
514
|
+
| Means | "you may have this" | "this business uses this" |
|
|
515
|
+
| Missing key | standard: off · enterprise: on | **on** |
|
|
516
|
+
|
|
517
|
+
```json
|
|
518
|
+
{ "enabled": { "products": false, "crm": false, "hub": false, "portal": false, "printStudio": false } }
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
**A missing key is on**, so a collection that never set `enabled` behaves exactly as before. Only an
|
|
522
|
+
explicit `false` switches something off. A website-only sign-up switches off what it doesn't use
|
|
523
|
+
(products, CRM, Hub, Portal…), and Studio hides the menus, dashboard cards and setup steps for them;
|
|
524
|
+
an admin can switch them back on.
|
|
525
|
+
|
|
526
|
+
Known keys: `products`, `crm`, `analytics`, `hub`, `portal`, `printStudio`. The map is open — an app
|
|
527
|
+
may define its own key.
|
|
528
|
+
|
|
529
|
+
```ts
|
|
530
|
+
if (await SL.appConfiguration.isEnabled(collectionId, 'products')) showProducts()
|
|
531
|
+
// already have the config:
|
|
532
|
+
SL.appConfiguration.resolveEnabled(cfg, 'crm')
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
For something a plan gates, show it only when both hold: `resolveEnabled(cfg, key)` **and**
|
|
536
|
+
`resolveFeature(cfg.system, flag)`.
|
|
537
|
+
|
|
538
|
+
Write it through the admin settings route (`collection.updateSettings(collectionId, 'appConfig',
|
|
539
|
+
{ enabled: { products: true } })`) — keys merge, so set only the ones you change.
|
|
540
|
+
|
|
504
541
|
## 9. Migration notes
|
|
505
542
|
|
|
506
543
|
- Legacy `entitlements` settings group is deprecated. Everything moved
|
package/dist/docs/connectors.md
CHANGED
|
@@ -66,7 +66,7 @@ deterministically. A connector's `shape` and `serialize` must be deterministic t
|
|
|
66
66
|
"aiGuide": "connector-ai-guide.md"
|
|
67
67
|
},
|
|
68
68
|
"functions": {
|
|
69
|
-
"files": { "js": { "umd": "
|
|
69
|
+
"files": { "js": { "umd": "functions.umd.js" } },
|
|
70
70
|
"definitions": [
|
|
71
71
|
{ "name": "testConnection", "trigger": { "type": "http", "method": "POST" },
|
|
72
72
|
"visibility": "admin", "authority": "collection",
|
|
@@ -33,7 +33,7 @@ in a bundle alongside your widgets/containers:
|
|
|
33
33
|
```jsonc
|
|
34
34
|
{
|
|
35
35
|
"functions": {
|
|
36
|
-
"files": { "js": { "umd": "
|
|
36
|
+
"files": { "js": { "umd": "functions.umd.js" } },
|
|
37
37
|
"definitions": [
|
|
38
38
|
{
|
|
39
39
|
"name": "submitCompetitionEntry",
|
|
@@ -98,9 +98,12 @@ export default defineConfig({
|
|
|
98
98
|
}
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
That emits `dist/functions.umd.js
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
That emits `dist/functions.umd.js`, whose named exports the platform pairs with your manifest
|
|
102
|
+
`definitions`. In the manifest, `functions.files.js.umd` is **`functions.umd.js`** — relative to the
|
|
103
|
+
published build (the contents of `dist/`), like the widget and container files; not
|
|
104
|
+
`dist/functions.umd.js`. Publishing refuses a release whose functions bundle isn't there.
|
|
105
|
+
|
|
106
|
+
Then **deploy** it — for dev, `smartlinks-publish` uploads `dist/` and registers in one step (see
|
|
104
107
|
[Deploying & registering](deploying-apps.md)).
|
|
105
108
|
|
|
106
109
|
---
|
|
@@ -169,6 +172,7 @@ Declare the minimum you need.
|
|
|
169
172
|
| `network:<host>` | `ctx.fetch` to that host only (repeat for several). Prefer this over blanket `network`. |
|
|
170
173
|
| `network:*.<domain>` | `ctx.fetch` to any subdomain of `<domain>` (not `<domain>` itself), for a customer's own endpoint at a provider, e.g. `network:*.servicebus.windows.net`. `<domain>` needs at least two labels (`*.com` is not honoured). |
|
|
171
174
|
| `secrets:<name>` | `ctx.secrets.get('<name>')` for that one secret (see [Secrets](#secrets--api-keys-per-collection)). |
|
|
175
|
+
| `tools:<name>` / `tools:<family>.*` | `ctx.sl.tools.run('<name>', args)` for a platform tool, e.g. `tools:pdf.create`, or `tools:pdf.*` for every PDF tool (see [Platform tools](#platform-tools--pdfs-labels-barcodes)). |
|
|
172
176
|
|
|
173
177
|
If you don't declare `network`, `ctx.fetch` is absent. Reading a secret you didn't declare with
|
|
174
178
|
`secrets:<name>` throws.
|
|
@@ -606,6 +610,58 @@ await ctx.sl.interactions.record({
|
|
|
606
610
|
|
|
607
611
|
---
|
|
608
612
|
|
|
613
|
+
## Platform tools — PDFs, labels, barcodes
|
|
614
|
+
|
|
615
|
+
`ctx.sl.tools.run(name, args)` runs one of the platform's deterministic tools from your function: no
|
|
616
|
+
model, no agent loop. Declare each tool as a capability: `tools:pdf.create`, or `tools:pdf.*` for the
|
|
617
|
+
whole family. Open to functions: `pdf.create`, `pdf.merge`, `pdf.fill`, `pdf.inspect`, `pdf.render`,
|
|
618
|
+
`pdf.decodeBarcodes`, `pdf.inspectGraphics`, `pdf.preflight`, `pdf.printReady`, `pdf.edit`,
|
|
619
|
+
`image.transform` (arguments as in [ai.md](ai.md)).
|
|
620
|
+
|
|
621
|
+
- **Identity:** the call runs as the function's collection, whatever the caller. An anonymous caller
|
|
622
|
+
holding only your own key gets the same result; the work is attributed and billed to the collection.
|
|
623
|
+
- **Rate:** per collection (10 calls a second by default); bursts wait rather than fail.
|
|
624
|
+
- **Failure:** throws an `Error` with `code` (`bad_args`, `too_large`, `too_many_pages`,
|
|
625
|
+
`resource_timeout`, `timeout`, `busy`, `TOOL_NOT_AVAILABLE`, or a capability denial).
|
|
626
|
+
|
|
627
|
+
**Labels with `pdf.create`.** Exact page sizes, many pages in one render, and bytes straight back:
|
|
628
|
+
|
|
629
|
+
```ts
|
|
630
|
+
// capabilities: ["tools:pdf.create", "tools:pdf.decodeBarcodes"]
|
|
631
|
+
export async function printLabel(ctx, event) {
|
|
632
|
+
const { gtin, lot, expiry, qty = 1 } = event.body
|
|
633
|
+
const barcode = bwipjs.toSVG({ bcid: 'gs1-128', text: `(01)${gtin}(10)${lot}(17)${expiry}` }) // inline SVG
|
|
634
|
+
const pdf = await ctx.sl.tools.run('pdf.create', {
|
|
635
|
+
width: 100, height: 50, unit: 'mm', // exact; output is 1:1
|
|
636
|
+
head: '<style>.l{position:absolute;font:3mm Arial}</style>',
|
|
637
|
+
pages: Array.from({ length: qty }, () => `<div class="l" style="left:4mm;top:3mm">Lot ${lot}</div>
|
|
638
|
+
<div style="position:absolute;left:4mm;top:10mm;width:92mm">${barcode}</div>`),
|
|
639
|
+
return: 'base64', // or 'signedUrl' + expiresIn
|
|
640
|
+
})
|
|
641
|
+
return { pdf: pdf.base64, pages: pdf.pageCount }
|
|
642
|
+
}
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
- **Page size:** `width` + `height` (+ `unit`: mm, cm, in, pt, px) is exact to the point. Otherwise
|
|
646
|
+
`format` ("A4", "Letter"), or the document's own CSS `@page { size }` when neither is given.
|
|
647
|
+
- **Pages:** `pages: [html, …]` lays one fragment per page; with an exact size and no margin each page
|
|
648
|
+
box is that size (overflow hidden), so a long value can't push onto the next label. Up to 2000 pages
|
|
649
|
+
and 10 MB of HTML per render.
|
|
650
|
+
- **Output:** 1:1 at scale 1 with backgrounds; text stays text and SVG stays vector (render barcodes as
|
|
651
|
+
inline SVG, e.g. bwip-js `toSVG`, never as a canvas bitmap). Colour is RGB; for CMYK pass the result
|
|
652
|
+
through `pdf.printReady`.
|
|
653
|
+
- **What loads:** JavaScript does not run. Images, fonts and stylesheets load only from SmartLinks hosts
|
|
654
|
+
(our CDN), Google Fonts or inline `data:` URIs; anything else is skipped and listed in `warnings`.
|
|
655
|
+
Embed brand fonts as `data:` URIs in `@font-face`, or host them on the SmartLinks CDN.
|
|
656
|
+
- **Delivery:** `return: 'base64'` (nothing stored), `'signedUrl'` (private link that expires after
|
|
657
|
+
`expiresIn` seconds, default 900, max 7 days — use this for anything with lot, expiry or serial
|
|
658
|
+
data), or `'url'` (public and permanent — artwork and documents meant to be shared).
|
|
659
|
+
- **Cache:** the same inputs return the earlier render without rendering again (`cached: true`).
|
|
660
|
+
- **Verify:** `pdf.decodeBarcodes` on a `signedUrl` reads the codes back deterministically, so you can
|
|
661
|
+
confirm the digits before answering.
|
|
662
|
+
|
|
663
|
+
---
|
|
664
|
+
|
|
609
665
|
## Worked example — a public competition entry
|
|
610
666
|
|
|
611
667
|
```ts
|
package/dist/openapi.yaml
CHANGED
|
@@ -8339,7 +8339,7 @@ paths:
|
|
|
8339
8339
|
get:
|
|
8340
8340
|
tags:
|
|
8341
8341
|
- tags
|
|
8342
|
-
summary: Reverse lookup
|
|
8342
|
+
summary: Reverse lookup — find all tags linked to a given object (admin).
|
|
8343
8343
|
operationId: tags_byRef
|
|
8344
8344
|
security:
|
|
8345
8345
|
- bearerAuth: []
|
|
@@ -14108,7 +14108,7 @@ paths:
|
|
|
14108
14108
|
get:
|
|
14109
14109
|
tags:
|
|
14110
14110
|
- tags
|
|
14111
|
-
summary: "Global tag resolve
|
|
14111
|
+
summary: "Global tag resolve — returns `{ tagId, collectionId }` only."
|
|
14112
14112
|
operationId: tags_resolveTag
|
|
14113
14113
|
security: []
|
|
14114
14114
|
parameters:
|
|
@@ -17499,14 +17499,74 @@ components:
|
|
|
17499
17499
|
properties:
|
|
17500
17500
|
html:
|
|
17501
17501
|
type: string
|
|
17502
|
+
pages:
|
|
17503
|
+
type: array
|
|
17504
|
+
items:
|
|
17505
|
+
type: string
|
|
17506
|
+
head:
|
|
17507
|
+
type: string
|
|
17508
|
+
width:
|
|
17509
|
+
type: number
|
|
17510
|
+
height:
|
|
17511
|
+
type: number
|
|
17512
|
+
unit:
|
|
17513
|
+
type: string
|
|
17514
|
+
enum:
|
|
17515
|
+
- mm
|
|
17516
|
+
- cm
|
|
17517
|
+
- in
|
|
17518
|
+
- pt
|
|
17519
|
+
- px
|
|
17502
17520
|
format:
|
|
17503
17521
|
type: string
|
|
17504
17522
|
landscape:
|
|
17505
17523
|
type: boolean
|
|
17506
17524
|
margin:
|
|
17507
17525
|
type: string
|
|
17526
|
+
return:
|
|
17527
|
+
type: string
|
|
17528
|
+
enum:
|
|
17529
|
+
- url
|
|
17530
|
+
- signedUrl
|
|
17531
|
+
- base64
|
|
17532
|
+
expiresIn:
|
|
17533
|
+
type: number
|
|
17534
|
+
filename:
|
|
17535
|
+
type: string
|
|
17536
|
+
cache:
|
|
17537
|
+
type: boolean
|
|
17538
|
+
PdfCreateResult:
|
|
17539
|
+
type: object
|
|
17540
|
+
properties:
|
|
17541
|
+
url:
|
|
17542
|
+
type: string
|
|
17543
|
+
hostedUrl:
|
|
17544
|
+
type: string
|
|
17545
|
+
base64:
|
|
17546
|
+
type: string
|
|
17547
|
+
expiresAt:
|
|
17548
|
+
type: string
|
|
17549
|
+
contentType:
|
|
17550
|
+
type: string
|
|
17551
|
+
enum:
|
|
17552
|
+
- application/pdf
|
|
17553
|
+
size:
|
|
17554
|
+
type: number
|
|
17555
|
+
pageCount:
|
|
17556
|
+
type: number
|
|
17557
|
+
cached:
|
|
17558
|
+
type: boolean
|
|
17559
|
+
renderer:
|
|
17560
|
+
type: string
|
|
17561
|
+
warnings:
|
|
17562
|
+
type: array
|
|
17563
|
+
items:
|
|
17564
|
+
type: string
|
|
17508
17565
|
required:
|
|
17509
|
-
-
|
|
17566
|
+
- contentType
|
|
17567
|
+
- size
|
|
17568
|
+
- pageCount
|
|
17569
|
+
- cached
|
|
17510
17570
|
PdfFillArgs:
|
|
17511
17571
|
type: object
|
|
17512
17572
|
properties:
|
|
@@ -19168,6 +19228,10 @@ components:
|
|
|
19168
19228
|
enum:
|
|
19169
19229
|
- registered
|
|
19170
19230
|
- owned
|
|
19231
|
+
enabled:
|
|
19232
|
+
type: object
|
|
19233
|
+
additionalProperties:
|
|
19234
|
+
type: boolean
|
|
19171
19235
|
system:
|
|
19172
19236
|
$ref: "#/components/schemas/SystemBlock"
|
|
19173
19237
|
required:
|
|
@@ -19653,6 +19717,9 @@ components:
|
|
|
19653
19717
|
interactions:
|
|
19654
19718
|
type: object
|
|
19655
19719
|
additionalProperties: true
|
|
19720
|
+
tools:
|
|
19721
|
+
type: object
|
|
19722
|
+
additionalProperties: true
|
|
19656
19723
|
products: {}
|
|
19657
19724
|
attestations: {}
|
|
19658
19725
|
appData:
|
|
@@ -19663,6 +19730,7 @@ components:
|
|
|
19663
19730
|
- tags
|
|
19664
19731
|
- contacts
|
|
19665
19732
|
- interactions
|
|
19733
|
+
- tools
|
|
19666
19734
|
- products
|
|
19667
19735
|
- attestations
|
|
19668
19736
|
- appData
|
package/dist/types/ai.d.ts
CHANGED
|
@@ -945,11 +945,49 @@ export interface PdfClip {
|
|
|
945
945
|
y1: number;
|
|
946
946
|
}
|
|
947
947
|
export interface PdfCreateArgs {
|
|
948
|
-
html
|
|
948
|
+
/** One HTML document (inline CSS). Give `html` or `pages`. */
|
|
949
|
+
html?: string;
|
|
950
|
+
/** One HTML fragment per page, laid out one per page (sharing `head`). With width/height and no margin each page box is exactly that size, overflow hidden. 500 labels = one call. */
|
|
951
|
+
pages?: string[];
|
|
952
|
+
/** With `pages`: shared <head> content (<style>, @font-face). */
|
|
953
|
+
head?: string;
|
|
954
|
+
/** Exact page size (labels, rolls, sheets) — exact to the point. Give both. */
|
|
955
|
+
width?: number;
|
|
956
|
+
height?: number;
|
|
957
|
+
/** Unit for width/height (default 'mm'). */
|
|
958
|
+
unit?: 'mm' | 'cm' | 'in' | 'pt' | 'px';
|
|
959
|
+
/** Named page size ("A4", "Letter") when not giving width/height. With neither, the document's CSS @page size is used (else A4). */
|
|
949
960
|
format?: string;
|
|
950
961
|
landscape?: boolean;
|
|
951
|
-
/** Page margin for all sides, e.g. "0" or "12mm"
|
|
962
|
+
/** Page margin for all sides, e.g. "0" or "12mm". Default 0 with width/height or pages; 18mm/14mm for named formats. */
|
|
952
963
|
margin?: string;
|
|
964
|
+
/** 'url' (public, permanent; default), 'signedUrl' (private link that expires), 'base64' (bytes in the response, nothing stored). */
|
|
965
|
+
return?: 'url' | 'signedUrl' | 'base64';
|
|
966
|
+
/** signedUrl lifetime in seconds: 30 to 604800 (7 days); default 900. */
|
|
967
|
+
expiresIn?: number;
|
|
968
|
+
/** File name for signedUrl downloads (default document.pdf). */
|
|
969
|
+
filename?: string;
|
|
970
|
+
/** Reuse an identical earlier render (default true). */
|
|
971
|
+
cache?: boolean;
|
|
972
|
+
}
|
|
973
|
+
export interface PdfCreateResult {
|
|
974
|
+
/** return 'url' or 'signedUrl'. */
|
|
975
|
+
url?: string;
|
|
976
|
+
/** return 'url': same as url. */
|
|
977
|
+
hostedUrl?: string;
|
|
978
|
+
/** return 'base64'. */
|
|
979
|
+
base64?: string;
|
|
980
|
+
/** return 'signedUrl': when the link stops working. */
|
|
981
|
+
expiresAt?: string;
|
|
982
|
+
contentType: 'application/pdf';
|
|
983
|
+
size: number;
|
|
984
|
+
pageCount: number;
|
|
985
|
+
/** An identical earlier render was reused (no new render). */
|
|
986
|
+
cached: boolean;
|
|
987
|
+
/** The renderer, e.g. "Chrome/141.0…". */
|
|
988
|
+
renderer?: string;
|
|
989
|
+
/** Resources that were not loaded (only images, fonts and stylesheets from SmartLinks hosts, Google Fonts or data: URIs load). */
|
|
990
|
+
warnings?: string[];
|
|
953
991
|
}
|
|
954
992
|
export interface PdfFillArgs {
|
|
955
993
|
url: string;
|
|
@@ -128,6 +128,14 @@ export interface AppConfigSettings {
|
|
|
128
128
|
requestedBasePlanId?: string;
|
|
129
129
|
/** How the account handles individual physical items. */
|
|
130
130
|
itemRecordMode?: 'registered' | 'owned' | null;
|
|
131
|
+
/**
|
|
132
|
+
* What this collection has SWITCHED ON — separate from what its plan allows (`system.features`).
|
|
133
|
+
* A missing key means on, so collections that never set it behave as they always have. A website
|
|
134
|
+
* sign-up switches off what it doesn't use, e.g. `{ products: false, crm: false, hub: false }`.
|
|
135
|
+
* Known keys: `products`, `crm`, `analytics`, `hub`, `portal`, `printStudio`; the map is open.
|
|
136
|
+
* Read it with `appConfiguration.isEnabled()` / `resolveEnabled()` — docs/appConfig.md §8a.
|
|
137
|
+
*/
|
|
138
|
+
enabled?: Record<string, boolean>;
|
|
131
139
|
/** Resolved entitlement truth. Read this to gate features. */
|
|
132
140
|
system?: SystemBlock;
|
|
133
141
|
}
|
|
@@ -457,6 +457,16 @@ export interface ServerFunctionSl {
|
|
|
457
457
|
eventId: string;
|
|
458
458
|
}>;
|
|
459
459
|
};
|
|
460
|
+
/**
|
|
461
|
+
* Platform tools (`tools:<name>` or `tools:<family>.*`, e.g. `tools:pdf.create`, `tools:pdf.*`):
|
|
462
|
+
* pdf.create, pdf.merge, pdf.fill, pdf.inspect, pdf.render, pdf.decodeBarcodes, pdf.inspectGraphics,
|
|
463
|
+
* pdf.preflight, pdf.printReady, pdf.edit, image.transform. Runs as THIS collection whatever the caller
|
|
464
|
+
* (authorised by the declaration, billed to the collection), rate-limited per collection. Returns the
|
|
465
|
+
* tool's result; a failure throws an Error with `code` (e.g. bad_args, busy, too_many_pages).
|
|
466
|
+
*/
|
|
467
|
+
tools: {
|
|
468
|
+
run<T = any>(name: string, args?: Record<string, any>): Promise<T>;
|
|
469
|
+
};
|
|
460
470
|
/** Products, scoped by caller authority + declared capability. */
|
|
461
471
|
products: any;
|
|
462
472
|
/** Attestations, scoped by caller authority + declared capability. */
|
package/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.67 | Generated: 2026-10-15T17:52:10.614Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -1600,10 +1600,35 @@ interface PdfClip {
|
|
|
1600
1600
|
**PdfCreateArgs** (interface)
|
|
1601
1601
|
```typescript
|
|
1602
1602
|
interface PdfCreateArgs {
|
|
1603
|
-
html
|
|
1603
|
+
html?: string
|
|
1604
|
+
pages?: string[]
|
|
1605
|
+
head?: string
|
|
1606
|
+
width?: number
|
|
1607
|
+
height?: number
|
|
1608
|
+
unit?: 'mm' | 'cm' | 'in' | 'pt' | 'px'
|
|
1604
1609
|
format?: string
|
|
1605
1610
|
landscape?: boolean
|
|
1606
1611
|
margin?: string
|
|
1612
|
+
return?: 'url' | 'signedUrl' | 'base64'
|
|
1613
|
+
expiresIn?: number
|
|
1614
|
+
filename?: string
|
|
1615
|
+
cache?: boolean
|
|
1616
|
+
}
|
|
1617
|
+
```
|
|
1618
|
+
|
|
1619
|
+
**PdfCreateResult** (interface)
|
|
1620
|
+
```typescript
|
|
1621
|
+
interface PdfCreateResult {
|
|
1622
|
+
url?: string
|
|
1623
|
+
hostedUrl?: string
|
|
1624
|
+
base64?: string
|
|
1625
|
+
expiresAt?: string
|
|
1626
|
+
contentType: 'application/pdf'
|
|
1627
|
+
size: number
|
|
1628
|
+
pageCount: number
|
|
1629
|
+
cached: boolean
|
|
1630
|
+
renderer?: string
|
|
1631
|
+
warnings?: string[]
|
|
1607
1632
|
}
|
|
1608
1633
|
```
|
|
1609
1634
|
|
|
@@ -2479,6 +2504,7 @@ interface AppConfigSettings {
|
|
|
2479
2504
|
addOns?: string[]
|
|
2480
2505
|
requestedBasePlanId?: string
|
|
2481
2506
|
itemRecordMode?: 'registered' | 'owned' | null
|
|
2507
|
+
enabled?: Record<string, boolean>
|
|
2482
2508
|
system?: SystemBlock
|
|
2483
2509
|
}
|
|
2484
2510
|
```
|
|
@@ -2785,6 +2811,7 @@ interface ServerFunctionSl {
|
|
|
2785
2811
|
forCaller(): Promise<{ contactId: string } | null>;
|
|
2786
2812
|
};
|
|
2787
2813
|
interactions: { record(event: ServerFunctionInteractionEvent): Promise<{ eventId: string }> };
|
|
2814
|
+
tools: { run<T = any>(name: string, args?: Record<string, any>): Promise<T> };
|
|
2788
2815
|
products: any;
|
|
2789
2816
|
attestations: any;
|
|
2790
2817
|
appData: AppDataHandle & { global: AppDataHandle; collection: AppDataHandle };
|
|
@@ -10886,6 +10913,15 @@ Check whether a feature flag is enabled for a collection, per `appConfig.system.
|
|
|
10886
10913
|
**isFeatureEnabledSync**(collectionId: string, flag: string) → `boolean | undefined`
|
|
10887
10914
|
Synchronous, cache-only check for a feature flag. Returns `undefined` if `getAppConfig()` / `isFeatureEnabled()` hasn't been called yet for this collection this page load (i.e. nothing to read without an await) — treat `undefined` as "not yet known", not as "disabled". ```typescript // Warm the cache once, e.g. in a top-level effect: useEffect(() => { appConfiguration.isFeatureEnabled(collectionId, 'custom_domain') }, [collectionId]); // Elsewhere, read synchronously without an await: const enabled = appConfiguration.isFeatureEnabledSync(collectionId, 'custom_domain'); if (enabled === undefined) { // not warmed yet — fall back to a loading state or the async version } ```
|
|
10888
10915
|
|
|
10916
|
+
**resolveEnabled**(cfg: { enabled?: Record<string, boolean> } | null | undefined, key: string) → `boolean`
|
|
10917
|
+
Has the collection SWITCHED ON this part of the platform (`appConfig.enabled[key]`)? A missing key is on — only an explicit `false` is off. This is "does this business use it", not "does the plan allow it": for something a plan gates, check `resolveFeature()` as well. docs/appConfig.md §8a.
|
|
10918
|
+
|
|
10919
|
+
**isEnabled**(collectionId: string, key: string, options?: { force?: boolean }) → `Promise<boolean>`
|
|
10920
|
+
Has the collection switched on `key` (`products`, `crm`, `analytics`, `hub`, `portal`, `printStudio`, or an app's own key)? Hide what's off: a website-only business switches off products and the CRM, so menus, cards and setup steps for them shouldn't appear. ```typescript if (await appConfiguration.isEnabled(collectionId, 'products')) showProductsMenu(); ```
|
|
10921
|
+
|
|
10922
|
+
**isEnabledSync**(collectionId: string, key: string) → `boolean | undefined`
|
|
10923
|
+
Cache-only `isEnabled` — `undefined` until `getAppConfig()` has been read this page load.
|
|
10924
|
+
|
|
10889
10925
|
### asset
|
|
10890
10926
|
|
|
10891
10927
|
**upload**(options: UploadAssetOptions) → `Promise<Asset>`
|
|
@@ -12805,7 +12841,7 @@ Create a single tag mapping (admin). If `productId` is set without `proofId`, a
|
|
|
12805
12841
|
|
|
12806
12842
|
**createBatch**(collectionId: string,
|
|
12807
12843
|
data: BatchCreateTagRequest) → `Promise<BatchCreateResult>`
|
|
12808
|
-
Batch-create tags (admin). Tags with `productId` but no `proofId` automatically get serial numbers. Serial number generation is grouped by `(productId, variantId, batchId)` for efficiency. Partial success is possible
|
|
12844
|
+
Batch-create tags (admin). Tags with `productId` but no `proofId` automatically get serial numbers. Serial number generation is grouped by `(productId, variantId, batchId)` for efficiency. Partial success is possible — check `results` for individual outcomes. ```typescript const result = await tags.createBatch('coll_123', { tags: [ { tagId: 'NFC-001', productId: 'prod_456', batchId: 'batch_2026_01' }, { tagId: 'NFC-002', productId: 'prod_456', batchId: 'batch_2026_01' }, ], }) console.log(`Created: ${result.summary.created}, Conflicts: ${result.summary.conflicts}`) ```
|
|
12809
12845
|
|
|
12810
12846
|
**get**(collectionId: string,
|
|
12811
12847
|
tagId: string) → `Promise<GetTagResponse>`
|
|
@@ -12814,7 +12850,7 @@ Get a single tag by `tagId` (admin).
|
|
|
12814
12850
|
**update**(collectionId: string,
|
|
12815
12851
|
tagId: string,
|
|
12816
12852
|
data: UpdateTagRequest) → `Promise<UpdateTagResponse>`
|
|
12817
|
-
Update a tag (admin). Partial update
|
|
12853
|
+
Update a tag (admin). Partial update — only provided fields are changed. `metadata` is deep-merged with the existing value. Pass `refType: null, refId: null` to clear the polymorphic ref. ```typescript const updated = await tags.update('coll_123', 'NFC-001', { variantId: 'var_premium', metadata: { notes: 'Updated to premium variant' }, }) // Clear polymorphic ref await tags.update('coll_123', 'NFC-001', { refType: null, refId: null }) ```
|
|
12818
12854
|
|
|
12819
12855
|
**remove**(collectionId: string,
|
|
12820
12856
|
tagId: string) → `Promise<DeleteTagResponse>`
|
|
@@ -12826,15 +12862,15 @@ List tags with optional filters and pagination (admin). ```typescript // All tag
|
|
|
12826
12862
|
|
|
12827
12863
|
**byRef**(collectionId: string,
|
|
12828
12864
|
params: ReverseTagLookupParams) → `Promise<ReverseTagLookupResponse>`
|
|
12829
|
-
Reverse lookup
|
|
12865
|
+
Reverse lookup — find all tags linked to a given object (admin). Uses a compound index on `(orgId, refType, refId)` on the per-org shard. No embed support on the admin side. ```typescript const { tags: linked } = await tags.byRef('coll_123', { refType: 'container', refId: 'container-uuid', }) ```
|
|
12830
12866
|
|
|
12831
12867
|
**resolveTag**(tagId: string) → `Promise<TagIndexEntry>`
|
|
12832
|
-
Global tag resolve
|
|
12868
|
+
Global tag resolve — returns `{ tagId, collectionId }` only. Use this **only** when you have a raw `tagId` and do not yet know which collection it belongs to. Queries the shared `tag_index` shard. Once `collectionId` is resolved, call `publicGetByCollection` for full data. > The global `/public/tags/by-ref` endpoint has been removed. > Use the collection-scoped `publicByRef` instead. ```typescript // Step 1: resolve collection const { collectionId } = await tags.resolveTag('NFC-001') // Step 2: full lookup with embedded data const { tag, embedded } = await tags.publicGetByCollection( collectionId, 'NFC-001', 'product,proof' ) ```
|
|
12833
12869
|
|
|
12834
12870
|
**publicGetByCollection**(collectionId: string,
|
|
12835
12871
|
tagId: string,
|
|
12836
12872
|
embed?: string) → `Promise<PublicGetTagResponse>`
|
|
12837
|
-
Single tag lookup with optional embedded data (public). `GET /public/collection/:collectionId/tags/:tagId?embed=product,proof,container,ref` Supported `embed` values: `'product'`, `'proof'`, `'container'`, `'ref'` (`'collection'` is not supported
|
|
12873
|
+
Single tag lookup with optional embedded data (public). `GET /public/collection/:collectionId/tags/:tagId?embed=product,proof,container,ref` Supported `embed` values: `'product'`, `'proof'`, `'container'`, `'ref'` (`'collection'` is not supported — the collection is already known from the URL). ```typescript const { tag, embedded } = await tags.publicGetByCollection( 'coll_123', 'NFC-001', 'product,proof' ) const product = embedded.products?.[tag.productId!] const proof = embedded.proofs?.[tag.proofId!] ```
|
|
12838
12874
|
|
|
12839
12875
|
**lookupTags**(collectionId: string,
|
|
12840
12876
|
data: LookupTagsRequest) → `Promise<TagLookupResponse>`
|
package/docs/agent-tools.md
CHANGED
package/docs/ai.md
CHANGED
|
@@ -323,7 +323,7 @@ in `input`.
|
|
|
323
323
|
| `image.fromReference` | Image-to-image: generate guided by reference image(s). |
|
|
324
324
|
| `image.searchStock` | Search real stock photos (Unsplash). |
|
|
325
325
|
| `image.transform` | Resize / crop / rotate / grayscale / format-convert / compress → hosted URL. |
|
|
326
|
-
| `pdf.create` | Render HTML → PDF
|
|
326
|
+
| `pdf.create` | Render HTML (or one fragment per page) → PDF, 1:1: exact `width`/`height`/`unit` for labels, `pages[]` for many pages in one render, no scripts, only SmartLinks/Google Fonts/data: resources. `return`: `url` (public), `signedUrl` (expiring), `base64`. Cached by input. |
|
|
327
327
|
| `pdf.fill` | Fill an AcroForm PDF's fields (`{ field: value }`) → hosted URL. |
|
|
328
328
|
| `pdf.merge` | Merge several PDFs into one, in order → hosted URL. |
|
|
329
329
|
| `pdf.inspect` | Cheap, no-AI introspection: page count/sizes, which pages have a real text layer, raster present, and a routing hint (`text` vs `vision`). |
|
|
@@ -402,9 +402,11 @@ if (!ready.result.report.compliant) showFindings(ready.result.report.findings)
|
|
|
402
402
|
- **A review UI that flags guessed fields** → `pdf.extract` with `includeConfidence` (per-field
|
|
403
403
|
confidence + source) and `includeBoxes` (per-field bbox on text-native pages) in `fieldsMeta`.
|
|
404
404
|
- **Produce a PDF** → `pdf.create` (HTML → PDF), `pdf.fill` (populate an AcroForm's fields),
|
|
405
|
-
`pdf.merge` (combine several).
|
|
406
|
-
`
|
|
407
|
-
|
|
405
|
+
`pdf.merge` (combine several). `pdf.create` takes an exact page size (`width`, `height`, `unit`) or a
|
|
406
|
+
`format`, `html` or `pages: [html, …]` (one per page, sharing `head`), and returns `url` (public),
|
|
407
|
+
a `signedUrl` that expires, or `base64` (`return`). JavaScript doesn't run, and images, fonts and
|
|
408
|
+
stylesheets load only from SmartLinks hosts, Google Fonts or `data:` URIs. From a server function:
|
|
409
|
+
`ctx.sl.tools.run('pdf.create', …)` with the `tools:pdf.create` capability (server-functions.md).
|
|
408
410
|
|
|
409
411
|
Every tool is also directly callable without the model loop via `ai.tools.run(collectionId, name,
|
|
410
412
|
args)` — e.g. render a page or extract fields straight from a UI, no agent round-trip:
|
package/docs/app-manifest.md
CHANGED
|
@@ -428,7 +428,7 @@ this is just the manifest shape.
|
|
|
428
428
|
|
|
429
429
|
| Field | Type | Description |
|
|
430
430
|
|-------|------|-------------|
|
|
431
|
-
| `files.js.umd` | string | UMD bundle path (e.g. `dist/functions.umd.js`) — a self-contained bundle exporting each handler by name |
|
|
431
|
+
| `files.js.umd` | string | UMD bundle path, relative to the published build like the other bundles (e.g. `functions.umd.js`, built to `dist/functions.umd.js`) — a self-contained bundle exporting each handler by name |
|
|
432
432
|
| `definitions[]` | object[] | One entry per function |
|
|
433
433
|
| `definitions[].name` | string | Function name; also the handler export name (override with `handler`) |
|
|
434
434
|
| `definitions[].trigger` | object | `{ type: "http"|"event"|"cron", … }` |
|
package/docs/appConfig.md
CHANGED
|
@@ -36,6 +36,8 @@ export interface AppConfigSettings {
|
|
|
36
36
|
|
|
37
37
|
/** How the account handles individual physical items. */
|
|
38
38
|
itemRecordMode?: 'registered' | 'owned' | null;
|
|
39
|
+
/** What the collection has switched on (absent key = on) — see §8a. */
|
|
40
|
+
enabled?: Record<string, boolean>;
|
|
39
41
|
|
|
40
42
|
// ── SYSTEM-OWNED, PUBLIC (reconcile writes; client read-only) ─────
|
|
41
43
|
/** Resolved entitlement truth. Read this to gate features. */
|
|
@@ -501,6 +503,41 @@ the customer add-ons page (`src/components/pricing/ItemHandlingSection`)
|
|
|
501
503
|
because these settings change the whole UX shape (claim, ownership,
|
|
502
504
|
CRM visibility), not just a capability flag.
|
|
503
505
|
|
|
506
|
+
## 8a. What's switched on (`enabled`)
|
|
507
|
+
|
|
508
|
+
A root-level, user-owned map (like `itemRecordMode`; reconcile never touches it) saying which parts of
|
|
509
|
+
the platform this collection **uses** — separate from what its plan **allows** (`system.features`):
|
|
510
|
+
|
|
511
|
+
| | Plan allows (`system.features`) | Switched on (`enabled`) |
|
|
512
|
+
|---|---|---|
|
|
513
|
+
| Written by | billing (`entitlements-reconcile`) | the collection: sign-up wizards, admins |
|
|
514
|
+
| Means | "you may have this" | "this business uses this" |
|
|
515
|
+
| Missing key | standard: off · enterprise: on | **on** |
|
|
516
|
+
|
|
517
|
+
```json
|
|
518
|
+
{ "enabled": { "products": false, "crm": false, "hub": false, "portal": false, "printStudio": false } }
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
**A missing key is on**, so a collection that never set `enabled` behaves exactly as before. Only an
|
|
522
|
+
explicit `false` switches something off. A website-only sign-up switches off what it doesn't use
|
|
523
|
+
(products, CRM, Hub, Portal…), and Studio hides the menus, dashboard cards and setup steps for them;
|
|
524
|
+
an admin can switch them back on.
|
|
525
|
+
|
|
526
|
+
Known keys: `products`, `crm`, `analytics`, `hub`, `portal`, `printStudio`. The map is open — an app
|
|
527
|
+
may define its own key.
|
|
528
|
+
|
|
529
|
+
```ts
|
|
530
|
+
if (await SL.appConfiguration.isEnabled(collectionId, 'products')) showProducts()
|
|
531
|
+
// already have the config:
|
|
532
|
+
SL.appConfiguration.resolveEnabled(cfg, 'crm')
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
For something a plan gates, show it only when both hold: `resolveEnabled(cfg, key)` **and**
|
|
536
|
+
`resolveFeature(cfg.system, flag)`.
|
|
537
|
+
|
|
538
|
+
Write it through the admin settings route (`collection.updateSettings(collectionId, 'appConfig',
|
|
539
|
+
{ enabled: { products: true } })`) — keys merge, so set only the ones you change.
|
|
540
|
+
|
|
504
541
|
## 9. Migration notes
|
|
505
542
|
|
|
506
543
|
- Legacy `entitlements` settings group is deprecated. Everything moved
|
package/docs/connectors.md
CHANGED
|
@@ -66,7 +66,7 @@ deterministically. A connector's `shape` and `serialize` must be deterministic t
|
|
|
66
66
|
"aiGuide": "connector-ai-guide.md"
|
|
67
67
|
},
|
|
68
68
|
"functions": {
|
|
69
|
-
"files": { "js": { "umd": "
|
|
69
|
+
"files": { "js": { "umd": "functions.umd.js" } },
|
|
70
70
|
"definitions": [
|
|
71
71
|
{ "name": "testConnection", "trigger": { "type": "http", "method": "POST" },
|
|
72
72
|
"visibility": "admin", "authority": "collection",
|
package/docs/server-functions.md
CHANGED
|
@@ -33,7 +33,7 @@ in a bundle alongside your widgets/containers:
|
|
|
33
33
|
```jsonc
|
|
34
34
|
{
|
|
35
35
|
"functions": {
|
|
36
|
-
"files": { "js": { "umd": "
|
|
36
|
+
"files": { "js": { "umd": "functions.umd.js" } },
|
|
37
37
|
"definitions": [
|
|
38
38
|
{
|
|
39
39
|
"name": "submitCompetitionEntry",
|
|
@@ -98,9 +98,12 @@ export default defineConfig({
|
|
|
98
98
|
}
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
That emits `dist/functions.umd.js
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
That emits `dist/functions.umd.js`, whose named exports the platform pairs with your manifest
|
|
102
|
+
`definitions`. In the manifest, `functions.files.js.umd` is **`functions.umd.js`** — relative to the
|
|
103
|
+
published build (the contents of `dist/`), like the widget and container files; not
|
|
104
|
+
`dist/functions.umd.js`. Publishing refuses a release whose functions bundle isn't there.
|
|
105
|
+
|
|
106
|
+
Then **deploy** it — for dev, `smartlinks-publish` uploads `dist/` and registers in one step (see
|
|
104
107
|
[Deploying & registering](deploying-apps.md)).
|
|
105
108
|
|
|
106
109
|
---
|
|
@@ -169,6 +172,7 @@ Declare the minimum you need.
|
|
|
169
172
|
| `network:<host>` | `ctx.fetch` to that host only (repeat for several). Prefer this over blanket `network`. |
|
|
170
173
|
| `network:*.<domain>` | `ctx.fetch` to any subdomain of `<domain>` (not `<domain>` itself), for a customer's own endpoint at a provider, e.g. `network:*.servicebus.windows.net`. `<domain>` needs at least two labels (`*.com` is not honoured). |
|
|
171
174
|
| `secrets:<name>` | `ctx.secrets.get('<name>')` for that one secret (see [Secrets](#secrets--api-keys-per-collection)). |
|
|
175
|
+
| `tools:<name>` / `tools:<family>.*` | `ctx.sl.tools.run('<name>', args)` for a platform tool, e.g. `tools:pdf.create`, or `tools:pdf.*` for every PDF tool (see [Platform tools](#platform-tools--pdfs-labels-barcodes)). |
|
|
172
176
|
|
|
173
177
|
If you don't declare `network`, `ctx.fetch` is absent. Reading a secret you didn't declare with
|
|
174
178
|
`secrets:<name>` throws.
|
|
@@ -606,6 +610,58 @@ await ctx.sl.interactions.record({
|
|
|
606
610
|
|
|
607
611
|
---
|
|
608
612
|
|
|
613
|
+
## Platform tools — PDFs, labels, barcodes
|
|
614
|
+
|
|
615
|
+
`ctx.sl.tools.run(name, args)` runs one of the platform's deterministic tools from your function: no
|
|
616
|
+
model, no agent loop. Declare each tool as a capability: `tools:pdf.create`, or `tools:pdf.*` for the
|
|
617
|
+
whole family. Open to functions: `pdf.create`, `pdf.merge`, `pdf.fill`, `pdf.inspect`, `pdf.render`,
|
|
618
|
+
`pdf.decodeBarcodes`, `pdf.inspectGraphics`, `pdf.preflight`, `pdf.printReady`, `pdf.edit`,
|
|
619
|
+
`image.transform` (arguments as in [ai.md](ai.md)).
|
|
620
|
+
|
|
621
|
+
- **Identity:** the call runs as the function's collection, whatever the caller. An anonymous caller
|
|
622
|
+
holding only your own key gets the same result; the work is attributed and billed to the collection.
|
|
623
|
+
- **Rate:** per collection (10 calls a second by default); bursts wait rather than fail.
|
|
624
|
+
- **Failure:** throws an `Error` with `code` (`bad_args`, `too_large`, `too_many_pages`,
|
|
625
|
+
`resource_timeout`, `timeout`, `busy`, `TOOL_NOT_AVAILABLE`, or a capability denial).
|
|
626
|
+
|
|
627
|
+
**Labels with `pdf.create`.** Exact page sizes, many pages in one render, and bytes straight back:
|
|
628
|
+
|
|
629
|
+
```ts
|
|
630
|
+
// capabilities: ["tools:pdf.create", "tools:pdf.decodeBarcodes"]
|
|
631
|
+
export async function printLabel(ctx, event) {
|
|
632
|
+
const { gtin, lot, expiry, qty = 1 } = event.body
|
|
633
|
+
const barcode = bwipjs.toSVG({ bcid: 'gs1-128', text: `(01)${gtin}(10)${lot}(17)${expiry}` }) // inline SVG
|
|
634
|
+
const pdf = await ctx.sl.tools.run('pdf.create', {
|
|
635
|
+
width: 100, height: 50, unit: 'mm', // exact; output is 1:1
|
|
636
|
+
head: '<style>.l{position:absolute;font:3mm Arial}</style>',
|
|
637
|
+
pages: Array.from({ length: qty }, () => `<div class="l" style="left:4mm;top:3mm">Lot ${lot}</div>
|
|
638
|
+
<div style="position:absolute;left:4mm;top:10mm;width:92mm">${barcode}</div>`),
|
|
639
|
+
return: 'base64', // or 'signedUrl' + expiresIn
|
|
640
|
+
})
|
|
641
|
+
return { pdf: pdf.base64, pages: pdf.pageCount }
|
|
642
|
+
}
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
- **Page size:** `width` + `height` (+ `unit`: mm, cm, in, pt, px) is exact to the point. Otherwise
|
|
646
|
+
`format` ("A4", "Letter"), or the document's own CSS `@page { size }` when neither is given.
|
|
647
|
+
- **Pages:** `pages: [html, …]` lays one fragment per page; with an exact size and no margin each page
|
|
648
|
+
box is that size (overflow hidden), so a long value can't push onto the next label. Up to 2000 pages
|
|
649
|
+
and 10 MB of HTML per render.
|
|
650
|
+
- **Output:** 1:1 at scale 1 with backgrounds; text stays text and SVG stays vector (render barcodes as
|
|
651
|
+
inline SVG, e.g. bwip-js `toSVG`, never as a canvas bitmap). Colour is RGB; for CMYK pass the result
|
|
652
|
+
through `pdf.printReady`.
|
|
653
|
+
- **What loads:** JavaScript does not run. Images, fonts and stylesheets load only from SmartLinks hosts
|
|
654
|
+
(our CDN), Google Fonts or inline `data:` URIs; anything else is skipped and listed in `warnings`.
|
|
655
|
+
Embed brand fonts as `data:` URIs in `@font-face`, or host them on the SmartLinks CDN.
|
|
656
|
+
- **Delivery:** `return: 'base64'` (nothing stored), `'signedUrl'` (private link that expires after
|
|
657
|
+
`expiresIn` seconds, default 900, max 7 days — use this for anything with lot, expiry or serial
|
|
658
|
+
data), or `'url'` (public and permanent — artwork and documents meant to be shared).
|
|
659
|
+
- **Cache:** the same inputs return the earlier render without rendering again (`cached: true`).
|
|
660
|
+
- **Verify:** `pdf.decodeBarcodes` on a `signedUrl` reads the codes back deterministically, so you can
|
|
661
|
+
confirm the digits before answering.
|
|
662
|
+
|
|
663
|
+
---
|
|
664
|
+
|
|
609
665
|
## Worked example — a public competition entry
|
|
610
666
|
|
|
611
667
|
```ts
|
package/openapi.yaml
CHANGED
|
@@ -8339,7 +8339,7 @@ paths:
|
|
|
8339
8339
|
get:
|
|
8340
8340
|
tags:
|
|
8341
8341
|
- tags
|
|
8342
|
-
summary: Reverse lookup
|
|
8342
|
+
summary: Reverse lookup — find all tags linked to a given object (admin).
|
|
8343
8343
|
operationId: tags_byRef
|
|
8344
8344
|
security:
|
|
8345
8345
|
- bearerAuth: []
|
|
@@ -14108,7 +14108,7 @@ paths:
|
|
|
14108
14108
|
get:
|
|
14109
14109
|
tags:
|
|
14110
14110
|
- tags
|
|
14111
|
-
summary: "Global tag resolve
|
|
14111
|
+
summary: "Global tag resolve — returns `{ tagId, collectionId }` only."
|
|
14112
14112
|
operationId: tags_resolveTag
|
|
14113
14113
|
security: []
|
|
14114
14114
|
parameters:
|
|
@@ -17499,14 +17499,74 @@ components:
|
|
|
17499
17499
|
properties:
|
|
17500
17500
|
html:
|
|
17501
17501
|
type: string
|
|
17502
|
+
pages:
|
|
17503
|
+
type: array
|
|
17504
|
+
items:
|
|
17505
|
+
type: string
|
|
17506
|
+
head:
|
|
17507
|
+
type: string
|
|
17508
|
+
width:
|
|
17509
|
+
type: number
|
|
17510
|
+
height:
|
|
17511
|
+
type: number
|
|
17512
|
+
unit:
|
|
17513
|
+
type: string
|
|
17514
|
+
enum:
|
|
17515
|
+
- mm
|
|
17516
|
+
- cm
|
|
17517
|
+
- in
|
|
17518
|
+
- pt
|
|
17519
|
+
- px
|
|
17502
17520
|
format:
|
|
17503
17521
|
type: string
|
|
17504
17522
|
landscape:
|
|
17505
17523
|
type: boolean
|
|
17506
17524
|
margin:
|
|
17507
17525
|
type: string
|
|
17526
|
+
return:
|
|
17527
|
+
type: string
|
|
17528
|
+
enum:
|
|
17529
|
+
- url
|
|
17530
|
+
- signedUrl
|
|
17531
|
+
- base64
|
|
17532
|
+
expiresIn:
|
|
17533
|
+
type: number
|
|
17534
|
+
filename:
|
|
17535
|
+
type: string
|
|
17536
|
+
cache:
|
|
17537
|
+
type: boolean
|
|
17538
|
+
PdfCreateResult:
|
|
17539
|
+
type: object
|
|
17540
|
+
properties:
|
|
17541
|
+
url:
|
|
17542
|
+
type: string
|
|
17543
|
+
hostedUrl:
|
|
17544
|
+
type: string
|
|
17545
|
+
base64:
|
|
17546
|
+
type: string
|
|
17547
|
+
expiresAt:
|
|
17548
|
+
type: string
|
|
17549
|
+
contentType:
|
|
17550
|
+
type: string
|
|
17551
|
+
enum:
|
|
17552
|
+
- application/pdf
|
|
17553
|
+
size:
|
|
17554
|
+
type: number
|
|
17555
|
+
pageCount:
|
|
17556
|
+
type: number
|
|
17557
|
+
cached:
|
|
17558
|
+
type: boolean
|
|
17559
|
+
renderer:
|
|
17560
|
+
type: string
|
|
17561
|
+
warnings:
|
|
17562
|
+
type: array
|
|
17563
|
+
items:
|
|
17564
|
+
type: string
|
|
17508
17565
|
required:
|
|
17509
|
-
-
|
|
17566
|
+
- contentType
|
|
17567
|
+
- size
|
|
17568
|
+
- pageCount
|
|
17569
|
+
- cached
|
|
17510
17570
|
PdfFillArgs:
|
|
17511
17571
|
type: object
|
|
17512
17572
|
properties:
|
|
@@ -19168,6 +19228,10 @@ components:
|
|
|
19168
19228
|
enum:
|
|
19169
19229
|
- registered
|
|
19170
19230
|
- owned
|
|
19231
|
+
enabled:
|
|
19232
|
+
type: object
|
|
19233
|
+
additionalProperties:
|
|
19234
|
+
type: boolean
|
|
19171
19235
|
system:
|
|
19172
19236
|
$ref: "#/components/schemas/SystemBlock"
|
|
19173
19237
|
required:
|
|
@@ -19653,6 +19717,9 @@ components:
|
|
|
19653
19717
|
interactions:
|
|
19654
19718
|
type: object
|
|
19655
19719
|
additionalProperties: true
|
|
19720
|
+
tools:
|
|
19721
|
+
type: object
|
|
19722
|
+
additionalProperties: true
|
|
19656
19723
|
products: {}
|
|
19657
19724
|
attestations: {}
|
|
19658
19725
|
appData:
|
|
@@ -19663,6 +19730,7 @@ components:
|
|
|
19663
19730
|
- tags
|
|
19664
19731
|
- contacts
|
|
19665
19732
|
- interactions
|
|
19733
|
+
- tools
|
|
19666
19734
|
- products
|
|
19667
19735
|
- attestations
|
|
19668
19736
|
- appData
|