@proveanything/smartlinks 2.0.5 → 2.0.9
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/api/ai.d.ts +1 -1
- package/dist/api/ai.js +1 -1
- package/dist/api/analytics.d.ts +1 -1
- package/dist/api/analytics.js +1 -1
- package/dist/api/appConfiguration.d.ts +3 -3
- package/dist/api/appConfiguration.js +3 -3
- package/dist/api/appObjects.d.ts +1 -1
- package/dist/api/appObjects.js +1 -1
- package/dist/api/asset.d.ts +1 -1
- package/dist/api/asset.js +2 -2
- package/dist/api/async.d.ts +1 -1
- package/dist/api/async.js +1 -1
- package/dist/api/attestation.d.ts +1 -1
- package/dist/api/attestation.js +1 -1
- package/dist/api/attestations.d.ts +1 -1
- package/dist/api/attestations.js +1 -1
- package/dist/api/auth.d.ts +2 -2
- package/dist/api/auth.js +2 -2
- package/dist/api/authKit.d.ts +1 -1
- package/dist/api/authKit.js +1 -1
- package/dist/api/batch.d.ts +1 -1
- package/dist/api/batch.js +1 -1
- package/dist/api/broadcasts.d.ts +2 -2
- package/dist/api/broadcasts.js +1 -1
- package/dist/api/claimSet.d.ts +1 -1
- package/dist/api/claimSet.js +1 -1
- package/dist/api/collection.d.ts +1 -1
- package/dist/api/collection.js +1 -1
- package/dist/api/comms.d.ts +15 -15
- package/dist/api/comms.js +1 -1
- package/dist/api/config.d.ts +1 -1
- package/dist/api/config.js +1 -1
- package/dist/api/contact.d.ts +1 -1
- package/dist/api/contact.js +1 -1
- package/dist/api/containers.d.ts +1 -1
- package/dist/api/containers.js +1 -1
- package/dist/api/crate.d.ts +1 -1
- package/dist/api/crate.js +1 -1
- package/dist/api/facets.d.ts +1 -1
- package/dist/api/facets.js +1 -1
- package/dist/api/form.js +1 -1
- package/dist/api/http.js +1 -1
- package/dist/api/index.d.ts +46 -46
- package/dist/api/index.js +46 -46
- package/dist/api/integrations.d.ts +1 -1
- package/dist/api/integrations.js +1 -1
- package/dist/api/interactions.d.ts +1 -1
- package/dist/api/interactions.js +1 -1
- package/dist/api/jobs.d.ts +1 -1
- package/dist/api/jobs.js +1 -1
- package/dist/api/journeys.d.ts +1 -1
- package/dist/api/journeys.js +1 -1
- package/dist/api/journeysAnalytics.d.ts +1 -1
- package/dist/api/journeysAnalytics.js +1 -1
- package/dist/api/location.d.ts +1 -1
- package/dist/api/location.js +1 -1
- package/dist/api/lots.d.ts +1 -1
- package/dist/api/lots.js +1 -1
- package/dist/api/loyalty.d.ts +1 -1
- package/dist/api/loyalty.js +1 -1
- package/dist/api/navigation.d.ts +1 -1
- package/dist/api/navigation.js +1 -1
- package/dist/api/nfc.d.ts +1 -1
- package/dist/api/nfc.js +1 -1
- package/dist/api/order.d.ts +1 -1
- package/dist/api/order.js +1 -1
- package/dist/api/product.d.ts +1 -1
- package/dist/api/product.js +1 -1
- package/dist/api/products.d.ts +1 -1
- package/dist/api/products.js +1 -1
- package/dist/api/proof.d.ts +1 -1
- package/dist/api/proof.js +1 -1
- package/dist/api/qr.d.ts +1 -1
- package/dist/api/qr.js +1 -1
- package/dist/api/realtime.d.ts +1 -1
- package/dist/api/realtime.js +1 -1
- package/dist/api/research.d.ts +1 -1
- package/dist/api/research.js +1 -1
- package/dist/api/secrets.d.ts +1 -1
- package/dist/api/secrets.js +1 -1
- package/dist/api/segments.d.ts +1 -1
- package/dist/api/segments.js +1 -1
- package/dist/api/sequence.js +1 -1
- package/dist/api/tags.d.ts +1 -1
- package/dist/api/tags.js +1 -1
- package/dist/api/template.d.ts +1 -1
- package/dist/api/template.js +1 -1
- package/dist/api/translations.d.ts +1 -1
- package/dist/api/translations.js +2 -2
- package/dist/api/variant.d.ts +1 -1
- package/dist/api/variant.js +1 -1
- package/dist/containers/types.d.ts +1 -1
- package/dist/docs/API_SUMMARY.md +7 -7
- package/dist/docs/agent-tools.md +111 -0
- package/dist/docs/ai.md +14 -520
- package/dist/docs/analytics.md +41 -2
- package/dist/docs/app-data-storage.md +0 -38
- package/dist/docs/app-manifest.md +104 -7
- package/dist/docs/app-objects.md +0 -148
- package/dist/docs/app-records-pattern.md +2 -2
- package/dist/docs/building-react-components.md +6 -14
- package/dist/docs/caching.md +20 -21
- package/dist/docs/container-tracking.md +2 -0
- package/dist/docs/containers.md +14 -66
- package/dist/docs/deploying-apps.md +8 -3
- package/dist/docs/executor.md +4 -4
- package/dist/docs/host-dependency-contract.md +159 -0
- package/dist/docs/iframe-responder.md +308 -0
- package/dist/docs/item-context.md +0 -2
- package/dist/docs/manifests.md +3 -3
- package/dist/docs/mobile-admin-container.md +4 -4
- package/dist/docs/mpa.md +5 -5
- package/dist/docs/native-facade.md +1 -1
- package/dist/docs/overview.md +36 -15
- package/dist/docs/portal-back-button.md +2 -3
- package/dist/docs/sequences.md +1 -1
- package/dist/docs/server-functions.md +2 -3
- package/dist/docs/widgets.md +11 -69
- package/dist/http.d.ts +24 -8
- package/dist/http.js +32 -14
- package/dist/iframe.d.ts +2 -2
- package/dist/iframe.js +1 -1
- package/dist/iframeResponder.d.ts +7 -1
- package/dist/iframeResponder.js +45 -4
- package/dist/index.d.ts +30 -27
- package/dist/index.js +10 -8
- package/dist/mobile-admin/errors.d.ts +1 -1
- package/dist/mobile-admin/types.d.ts +2 -2
- package/dist/openapi.yaml +12 -0
- package/dist/shared-dependencies.d.ts +37 -0
- package/dist/shared-dependencies.js +79 -0
- package/dist/testing/index.d.ts +1 -1
- package/dist/translationCache.d.ts +1 -1
- package/dist/types/appManifest.d.ts +23 -0
- package/dist/types/broadcasts.d.ts +1 -1
- package/dist/types/collection.d.ts +2 -2
- package/dist/types/comms.d.ts +5 -5
- package/dist/types/contact.d.ts +1 -1
- package/dist/types/facets.d.ts +1 -1
- package/dist/types/iframeResponder.d.ts +3 -3
- package/dist/types/index.d.ts +44 -44
- package/dist/types/index.js +44 -44
- package/dist/types/interaction.d.ts +1 -1
- package/dist/types/itemContext.d.ts +1 -1
- package/dist/types/journeysAnalytics.d.ts +1 -1
- package/dist/types/navigation.d.ts +1 -1
- package/dist/types/product.d.ts +1 -1
- package/dist/types/proof.d.ts +1 -1
- package/dist/types/segments.d.ts +1 -1
- package/dist/types/widgets.d.ts +2 -2
- package/dist/utils/conditions.d.ts +1 -1
- package/dist/utils/index.d.ts +3 -3
- package/dist/utils/index.js +3 -3
- package/dist/utils/paths.d.ts +4 -4
- package/docs/API_SUMMARY.md +7 -7
- package/docs/agent-tools.md +111 -0
- package/docs/ai.md +14 -520
- package/docs/analytics.md +41 -2
- package/docs/app-data-storage.md +0 -38
- package/docs/app-manifest.md +104 -7
- package/docs/app-objects.md +0 -148
- package/docs/app-records-pattern.md +2 -2
- package/docs/building-react-components.md +6 -14
- package/docs/caching.md +20 -21
- package/docs/container-tracking.md +2 -0
- package/docs/containers.md +14 -66
- package/docs/deploying-apps.md +8 -3
- package/docs/executor.md +4 -4
- package/docs/host-dependency-contract.md +159 -0
- package/docs/iframe-responder.md +308 -0
- package/docs/item-context.md +0 -2
- package/docs/mobile-admin-container.md +4 -4
- package/docs/mpa.md +5 -5
- package/docs/native-facade.md +1 -1
- package/docs/overview.md +36 -15
- package/docs/portal-back-button.md +2 -3
- package/docs/sequences.md +1 -1
- package/docs/server-functions.md +2 -3
- package/docs/widgets.md +11 -69
- package/openapi.yaml +12 -0
- package/package.json +17 -6
- package/scripts/doctor.mjs +171 -0
- package/docs/analytics-metadata-conventions.md +0 -88
- package/docs/iframe-streaming-parent-changes.md +0 -308
- package/docs/manifests.md +0 -204
package/dist/docs/analytics.md
CHANGED
|
@@ -46,7 +46,7 @@ There are two analytics domains:
|
|
|
46
46
|
|
|
47
47
|
The backend stores custom analytics dimensions in `metadata`, but promoted analytics fields now belong at top level and are queried from real columns.
|
|
48
48
|
|
|
49
|
-
See [
|
|
49
|
+
See [Metadata conventions](#metadata-conventions) below for the recommended key set.
|
|
50
50
|
|
|
51
51
|
### A note on server-written events
|
|
52
52
|
|
|
@@ -663,7 +663,7 @@ Analytics metadata filtering currently works best with top-level scalar keys suc
|
|
|
663
663
|
- `utmCampaign`
|
|
664
664
|
- `pagePath`
|
|
665
665
|
|
|
666
|
-
See [
|
|
666
|
+
See [Metadata conventions](#metadata-conventions) for the recommended shared vocabulary.
|
|
667
667
|
|
|
668
668
|
### 4. Prefer generic endpoints for custom dashboards
|
|
669
669
|
|
|
@@ -719,3 +719,42 @@ async function loadScanDashboard(collectionId: string) {
|
|
|
719
719
|
}
|
|
720
720
|
}
|
|
721
721
|
```
|
|
722
|
+
|
|
723
|
+
---
|
|
724
|
+
|
|
725
|
+
## Metadata conventions
|
|
726
|
+
|
|
727
|
+
Recommended standard analytics keys — a shared vocabulary so teams don't invent divergent names. Some are promoted top-level fields; others are good `metadata` keys for custom dimensions.
|
|
728
|
+
|
|
729
|
+
### Promoted top-level fields
|
|
730
|
+
|
|
731
|
+
Send these at the **top level**, not inside `metadata`: `visitorId`, `referrerHost`, `entryType`, `pageId`, `scanMethod`, `source` (collection/web-events only — free-form client id like `'portal'`/`'hub'`; not on tag events), `redirectMode` (tag-events only; usually server-written).
|
|
732
|
+
|
|
733
|
+
### Metadata-friendly keys
|
|
734
|
+
|
|
735
|
+
`referrer`, `utmSource`, `utmMedium`, `utmCampaign`, `utmContent`, `utmTerm`, `group`, `tag`, `campaign`, `placement`, `linkGroup`, `linkPlacement`, `linkPosition`, `linkTitle`, `destinationDomain`, `pagePath`, `qrCodeId`.
|
|
736
|
+
|
|
737
|
+
### Guidance
|
|
738
|
+
|
|
739
|
+
- Treat these as reserved standard keys; prefer them before inventing alternatives.
|
|
740
|
+
- Keep values flat and scalar so they're easy to filter/break-down later.
|
|
741
|
+
- Promote a field to a first-class backend column only when it becomes a hot platform-wide dimension.
|
|
742
|
+
- `source` (the event column) and the query-time `source` parameter (`'events' | 'tag'`, which table to query) are unrelated fields that share a name — filter the column via the **plural** `sources` array (there is no singular `source` filter).
|
|
743
|
+
|
|
744
|
+
```typescript
|
|
745
|
+
analytics.collection.track({
|
|
746
|
+
sessionId: 1234567890,
|
|
747
|
+
eventType: 'click_link',
|
|
748
|
+
collectionId: 'demo-collection',
|
|
749
|
+
visitorId: 'visitor_123',
|
|
750
|
+
linkId: 'hero-cta',
|
|
751
|
+
href: 'https://example.com/buy',
|
|
752
|
+
referrerHost: 'instagram.com',
|
|
753
|
+
placement: 'hero',
|
|
754
|
+
campaign: 'summer-launch',
|
|
755
|
+
utmSource: 'email',
|
|
756
|
+
pageId: 'QR123',
|
|
757
|
+
source: 'portal',
|
|
758
|
+
metadata: { pagePath: '/c/demo-collection' },
|
|
759
|
+
})
|
|
760
|
+
```
|
|
@@ -249,44 +249,6 @@ If the object starts needing richer semantics, migrate that use case to `app.rec
|
|
|
249
249
|
|
|
250
250
|
---
|
|
251
251
|
|
|
252
|
-
## Migration from Old SDK
|
|
253
|
-
|
|
254
|
-
### Old SDK → New SDK
|
|
255
|
-
|
|
256
|
-
```typescript
|
|
257
|
-
// OLD: Get user config
|
|
258
|
-
RemoteApi.get({ path: `public/auth/app/${appId}` })
|
|
259
|
-
// NEW:
|
|
260
|
-
userAppData.getConfig(appId)
|
|
261
|
-
|
|
262
|
-
// OLD: Set user config
|
|
263
|
-
RemoteApi.post({ path: `public/auth/app/${appId}`, data })
|
|
264
|
-
// NEW:
|
|
265
|
-
userAppData.setConfig(appId, data)
|
|
266
|
-
|
|
267
|
-
// OLD: Get user data items
|
|
268
|
-
RemoteApi.get({ path: `public/auth/app/${appId}/data` })
|
|
269
|
-
// NEW:
|
|
270
|
-
userAppData.list(appId)
|
|
271
|
-
|
|
272
|
-
// OLD: Get user data item
|
|
273
|
-
RemoteApi.get({ path: `public/auth/app/${appId}/data/${itemId}` })
|
|
274
|
-
// NEW:
|
|
275
|
-
userAppData.get(appId, itemId)
|
|
276
|
-
|
|
277
|
-
// OLD: Set user data item
|
|
278
|
-
RemoteApi.post({ path: `public/auth/app/${appId}/data`, data: item })
|
|
279
|
-
// NEW:
|
|
280
|
-
userAppData.set(appId, item)
|
|
281
|
-
|
|
282
|
-
// OLD: Delete user data item
|
|
283
|
-
RemoteApi.delete({ path: `public/auth/app/${appId}/data/${itemId}` })
|
|
284
|
-
// NEW:
|
|
285
|
-
userAppData.remove(appId, itemId)
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
---
|
|
289
|
-
|
|
290
252
|
## Complete API Reference
|
|
291
253
|
|
|
292
254
|
### `userAppData` (User-Specific Data)
|
|
@@ -40,7 +40,9 @@ The manifest is loaded automatically by the platform for every collection page.
|
|
|
40
40
|
"name": "My App",
|
|
41
41
|
"description": "A short human-readable description of what this app does.",
|
|
42
42
|
"version": "1.2.0",
|
|
43
|
-
"platformRevision": "
|
|
43
|
+
"platformRevision": "R5",
|
|
44
|
+
"moduleFormat": "dual",
|
|
45
|
+
"sharedDependencies": "v5"
|
|
44
46
|
},
|
|
45
47
|
|
|
46
48
|
"admin": "app.admin.json",
|
|
@@ -51,7 +53,7 @@ The manifest is loaded automatically by the platform for every collection page.
|
|
|
51
53
|
"files": {
|
|
52
54
|
"js": {
|
|
53
55
|
"umd": "dist/widgets.umd.js",
|
|
54
|
-
"esm": "dist/widgets.
|
|
56
|
+
"esm": "dist/widgets.esm.js"
|
|
55
57
|
},
|
|
56
58
|
"css": "dist/widgets.css"
|
|
57
59
|
},
|
|
@@ -75,7 +77,7 @@ The manifest is loaded automatically by the platform for every collection page.
|
|
|
75
77
|
"files": {
|
|
76
78
|
"js": {
|
|
77
79
|
"umd": "dist/containers.umd.js",
|
|
78
|
-
"esm": "dist/containers.
|
|
80
|
+
"esm": "dist/containers.esm.js"
|
|
79
81
|
},
|
|
80
82
|
"css": "dist/containers.css"
|
|
81
83
|
},
|
|
@@ -95,7 +97,7 @@ The manifest is loaded automatically by the platform for every collection page.
|
|
|
95
97
|
"files": {
|
|
96
98
|
"js": {
|
|
97
99
|
"umd": "dist/mobile-admin.umd.js",
|
|
98
|
-
"esm": "dist/mobile-admin.
|
|
100
|
+
"esm": "dist/mobile-admin.esm.js"
|
|
99
101
|
},
|
|
100
102
|
"css": null
|
|
101
103
|
},
|
|
@@ -144,9 +146,56 @@ The manifest is loaded automatically by the platform for every collection page.
|
|
|
144
146
|
| `name` | string | ✅ | Human-readable display name |
|
|
145
147
|
| `description` | string | ❌ | Short description shown in app directories and AI context |
|
|
146
148
|
| `version` | string | ✅ | SemVer string, e.g. `"1.2.0"` |
|
|
147
|
-
| `platformRevision` | string | ❌ |
|
|
149
|
+
| `platformRevision` | string | ❌ | Platform revision tag this build targets, e.g. `"R5"` (see [host-dependency-contract.md](host-dependency-contract.md)) |
|
|
150
|
+
| `moduleFormat` | `"umd"` \| `"esm"` \| `"dual"` | ❌ | How the host loads this app's bundles. Absent = `"umd"`. See [Module format](#module-format-umd-vs-esm) below. |
|
|
151
|
+
| `sharedDependencies` | string | ❌ | Shared-dependency contract version the bundle was built against, e.g. `"v5"`. Used by the host to pick a compatible ESM import map. |
|
|
152
|
+
| `globals` | object | ❌ | Per-app namespaced UMD globals (R4.7+), e.g. `{ "widgets": "MyAppWidgets" }`. UMD-only; ESM bundles don't need it. |
|
|
148
153
|
| `seo.priority` | number | ❌ | Controls which app's `title`/`description`/`ogImage` wins when multiple apps are on the same page. Default `0`; higher wins. See the [Executor guide](executor.md). |
|
|
149
154
|
|
|
155
|
+
#### Module format (UMD vs ESM)
|
|
156
|
+
|
|
157
|
+
The host provides shared libraries (React, Radix, the SmartLinks SDK, LiquidJS…) as **singletons**
|
|
158
|
+
so apps never bundle their own. Two delivery mechanisms exist, and `meta.moduleFormat` tells the host
|
|
159
|
+
which to use:
|
|
160
|
+
|
|
161
|
+
| `moduleFormat` | Host behaviour |
|
|
162
|
+
|---|---|
|
|
163
|
+
| `"umd"` *(default)* | Loads `files.js.umd` via the CommonJS `require` shim; shared deps resolve from **window globals**. Every existing app works unchanged. |
|
|
164
|
+
| `"dual"` | Prefers `files.js.esm` when the host has an **import map** for the declared `sharedDependencies` version; **falls back to UMD** otherwise. The safe transition setting. |
|
|
165
|
+
| `"esm"` | Loads `files.js.esm` **natively**; if the host has no matching import map it fails with an actionable error rather than a bare-specifier crash. Use only once you know your hosts are on the contract. |
|
|
166
|
+
|
|
167
|
+
The ESM bundle is declared in the **same** `files.js` block as `esm` (there is no separate `jsEsm`
|
|
168
|
+
field):
|
|
169
|
+
|
|
170
|
+
```jsonc
|
|
171
|
+
"widgets": {
|
|
172
|
+
"files": {
|
|
173
|
+
"js": { "umd": "dist/widgets.umd.js", "esm": "dist/widgets.esm.js" },
|
|
174
|
+
"css": "dist/widgets.css"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
An ESM bundle **must externalize exactly the shared-dependency contract** — read it from the SDK
|
|
180
|
+
(`SHARED_DEPENDENCY_SPECIFIERS`) rather than hard-coding it, and stamp the version you built against
|
|
181
|
+
into `meta.sharedDependencies`. See [host-dependency-contract.md](host-dependency-contract.md).
|
|
182
|
+
|
|
183
|
+
##### Validate before you ship: `smartlinks doctor`
|
|
184
|
+
|
|
185
|
+
Run the checker (shipped with the SDK) against your built app — it reads the same contract the host
|
|
186
|
+
serves, so the two can't drift:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
npx smartlinks-doctor # in the app dir, after building
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
It reads your manifest, and for every ESM surface confirms **every bare import in the bundle is a
|
|
193
|
+
contract entry**. A correctly-externalized ESM bundle inlines everything except the host singletons,
|
|
194
|
+
so anything else left as a bare import will either fail to resolve through the import map or silently
|
|
195
|
+
double-load (the duplicate-React class of bug). It also warns when a UMD app still declares stale
|
|
196
|
+
`*.es.js`/`*.esm.js` bundles the host will never load. Exit code is non-zero on violations, so it
|
|
197
|
+
drops straight into CI.
|
|
198
|
+
|
|
150
199
|
#### `build`
|
|
151
200
|
|
|
152
201
|
Optional build provenance. Recommended for the **Lovable dev publish** flow: stamp the content
|
|
@@ -194,7 +243,7 @@ Apps such as widget toolkits often store reusable widget instances in collection
|
|
|
194
243
|
"files": {
|
|
195
244
|
"js": {
|
|
196
245
|
"umd": "dist/widgets.umd.js",
|
|
197
|
-
"esm": "dist/widgets.
|
|
246
|
+
"esm": "dist/widgets.esm.js"
|
|
198
247
|
},
|
|
199
248
|
"css": null
|
|
200
249
|
},
|
|
@@ -247,7 +296,7 @@ See [mobile-admin-container.md](mobile-admin-container.md) for the `AdminMobileH
|
|
|
247
296
|
"files": {
|
|
248
297
|
"js": {
|
|
249
298
|
"umd": "dist/mobile-admin.umd.js",
|
|
250
|
-
"esm": "dist/mobile-admin.
|
|
299
|
+
"esm": "dist/mobile-admin.esm.js"
|
|
251
300
|
},
|
|
252
301
|
"css": null
|
|
253
302
|
},
|
|
@@ -549,6 +598,54 @@ Declares what interactions and KPIs the app reports. Used by the platform's anal
|
|
|
549
598
|
|
|
550
599
|
---
|
|
551
600
|
|
|
601
|
+
## Widget settings schema (JSON Schema)
|
|
602
|
+
|
|
603
|
+
Each widget component's `settings` object uses **JSON Schema** to describe its configurable props, so schema-form renderers *and* AI orchestrators can auto-generate a config UI without per-widget code:
|
|
604
|
+
|
|
605
|
+
```json
|
|
606
|
+
"components": [
|
|
607
|
+
{
|
|
608
|
+
"name": "MyWidget",
|
|
609
|
+
"description": "What this widget does",
|
|
610
|
+
"sizes": ["compact", "standard", "large"],
|
|
611
|
+
"settings": {
|
|
612
|
+
"type": "object",
|
|
613
|
+
"properties": {
|
|
614
|
+
"displayMode": {
|
|
615
|
+
"type": "string", "title": "Display Mode",
|
|
616
|
+
"description": "How the widget renders",
|
|
617
|
+
"enum": ["compact", "standard", "large"],
|
|
618
|
+
"enumLabels": { "compact": "Icons only", "standard": "With names", "large": "Full cards" },
|
|
619
|
+
"default": "standard", "order": 1
|
|
620
|
+
},
|
|
621
|
+
"showImage": { "type": "boolean", "title": "Show Product Image", "default": true, "order": 2 }
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
]
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
| Field | Purpose |
|
|
629
|
+
|-------|---------|
|
|
630
|
+
| `type`, `enum` | Standard JSON Schema validation |
|
|
631
|
+
| `title` | Human-readable form label |
|
|
632
|
+
| `description` | Help text shown alongside the field |
|
|
633
|
+
| `enumLabels` | Friendly display names for enum values (`{ value → label }`) |
|
|
634
|
+
| `default` | Pre-selected value when no config exists |
|
|
635
|
+
| `order` | Field display order (lower number = higher) |
|
|
636
|
+
|
|
637
|
+
The schema serves both AI orchestrators (understand what a widget accepts, configure it conversationally) and schema-form renderers (auto-generate settings UIs).
|
|
638
|
+
|
|
639
|
+
## AI workflows the manifest enables
|
|
640
|
+
|
|
641
|
+
SmartLinks manifests are **AI-discoverable, -configurable, and -importable**: the structured manifest plus the prose `ai-guide.md` (start from [ai-guide-template.md](ai-guide-template.md)) let AI systems set up and populate apps without custom integration code. Three workflows read the manifest:
|
|
642
|
+
|
|
643
|
+
1. **Widget Builder** — reads `widgets.components[]` and each `settings` schema to embed a widget with correct props and auto-render its configuration UI.
|
|
644
|
+
2. **Setup Wizard** — reads `setup` from `app.admin.json`: walks `setup.questions[]`, validates against `setup.configSchema`, optionally auto-generates content via `SL.ai`, then saves per `setup.saveWith`.
|
|
645
|
+
3. **Data Importer** — reads `import` from `app.admin.json`: builds a CSV template from `import.fields[]`, normalises rows, and calls `import.saveWith.method` per row (multi-app imports merge fields into one CSV).
|
|
646
|
+
|
|
647
|
+
When you change your config shape, keep all three in sync: `app.manifest.json` (widget `settings`, containers, executor, linkable), `app.admin.json` (setup / import / tunable), and `ai-guide.md` (prose guidance).
|
|
648
|
+
|
|
552
649
|
## Reading the Files at Runtime
|
|
553
650
|
|
|
554
651
|
### Manifest — available from the widgets endpoint
|
package/dist/docs/app-objects.md
CHANGED
|
@@ -291,32 +291,6 @@ const summary = await app.cases.summary(collectionId, appId, {
|
|
|
291
291
|
// Returns: { total: 142, byStatus: { open: 12, resolved: 130 }, ... }
|
|
292
292
|
```
|
|
293
293
|
|
|
294
|
-
### Use Case: Support Dashboard
|
|
295
|
-
|
|
296
|
-
Build a live support dashboard showing open cases by priority:
|
|
297
|
-
|
|
298
|
-
```typescript
|
|
299
|
-
const openCases = await app.cases.list(collectionId, appId, {
|
|
300
|
-
status: 'open',
|
|
301
|
-
sort: 'priority:desc',
|
|
302
|
-
limit: 50
|
|
303
|
-
}, true);
|
|
304
|
-
|
|
305
|
-
// Aggregate by category
|
|
306
|
-
const stats = await app.cases.aggregate(collectionId, appId, {
|
|
307
|
-
filters: { status: 'open' },
|
|
308
|
-
groupBy: ['category', 'priority'],
|
|
309
|
-
metrics: ['count']
|
|
310
|
-
}, true);
|
|
311
|
-
|
|
312
|
-
// Time series: cases created per week
|
|
313
|
-
const trend = await app.cases.aggregate(collectionId, appId, {
|
|
314
|
-
timeSeriesField: 'created_at',
|
|
315
|
-
timeSeriesInterval: 'week',
|
|
316
|
-
metrics: ['count']
|
|
317
|
-
}, true);
|
|
318
|
-
```
|
|
319
|
-
|
|
320
294
|
---
|
|
321
295
|
|
|
322
296
|
## Threads
|
|
@@ -382,53 +356,6 @@ await app.threads.update(collectionId, appId, question.id, {
|
|
|
382
356
|
}, true);
|
|
383
357
|
```
|
|
384
358
|
|
|
385
|
-
### Use Case: Forum-Style Discussions
|
|
386
|
-
|
|
387
|
-
List recent discussions with reply counts:
|
|
388
|
-
|
|
389
|
-
```typescript
|
|
390
|
-
// Get active threads
|
|
391
|
-
const activeThreads = await app.threads.list(collectionId, appId, {
|
|
392
|
-
status: 'open',
|
|
393
|
-
sort: 'lastReplyAt:desc',
|
|
394
|
-
limit: 20
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
// Filter by tag
|
|
398
|
-
const cleaningThreads = await app.threads.list(collectionId, appId, {
|
|
399
|
-
tag: 'cleaning'
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
// Aggregate: most active discussion topics
|
|
403
|
-
const topicStats = await app.threads.aggregate(collectionId, appId, {
|
|
404
|
-
groupBy: ['status'],
|
|
405
|
-
metrics: ['count', 'reply_count']
|
|
406
|
-
});
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
### Use Case: Product Comments
|
|
410
|
-
|
|
411
|
-
Attach comments to a specific product:
|
|
412
|
-
|
|
413
|
-
```typescript
|
|
414
|
-
// Create a comment thread for a product
|
|
415
|
-
await app.threads.create(collectionId, appId, {
|
|
416
|
-
visibility: 'public',
|
|
417
|
-
parentType: 'product',
|
|
418
|
-
parentId: product.id,
|
|
419
|
-
authorId: user.contactId,
|
|
420
|
-
body: { text: 'Love this product! Best purchase ever.' },
|
|
421
|
-
tags: ['positive']
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
// List all comments for a product
|
|
425
|
-
const productComments = await app.threads.list(collectionId, appId, {
|
|
426
|
-
parentType: 'product',
|
|
427
|
-
parentId: product.id,
|
|
428
|
-
sort: 'createdAt:desc'
|
|
429
|
-
});
|
|
430
|
-
```
|
|
431
|
-
|
|
432
359
|
### Anchoring to app entities and proofs
|
|
433
360
|
|
|
434
361
|
Anchor a thread to your own entity with `parentType` + `parentId`. `parentId` is a
|
|
@@ -948,81 +875,6 @@ const expiringSoon = await app.records.list(collectionId, appId, {
|
|
|
948
875
|
}, true);
|
|
949
876
|
```
|
|
950
877
|
|
|
951
|
-
### Example: Appointment Booking
|
|
952
|
-
|
|
953
|
-
```typescript
|
|
954
|
-
// Customer books a service appointment
|
|
955
|
-
const booking = await app.records.create(collectionId, appId, {
|
|
956
|
-
recordType: 'service_appointment',
|
|
957
|
-
visibility: 'owner',
|
|
958
|
-
contactId: user.contactId,
|
|
959
|
-
startsAt: '2026-03-15T10:00:00Z',
|
|
960
|
-
expiresAt: '2026-03-15T11:00:00Z', // 1-hour appointment
|
|
961
|
-
data: {
|
|
962
|
-
serviceType: 'installation',
|
|
963
|
-
location: 'Customer site',
|
|
964
|
-
technician: null // assigned later
|
|
965
|
-
},
|
|
966
|
-
owner: {
|
|
967
|
-
address: '123 Main St',
|
|
968
|
-
phone: '555-1234',
|
|
969
|
-
notes: 'Call before arrival'
|
|
970
|
-
}
|
|
971
|
-
});
|
|
972
|
-
|
|
973
|
-
// Admin assigns technician
|
|
974
|
-
await app.records.update(collectionId, appId, booking.id, {
|
|
975
|
-
data: {
|
|
976
|
-
serviceType: 'installation',
|
|
977
|
-
location: 'Customer site',
|
|
978
|
-
technician: 'tech_john'
|
|
979
|
-
},
|
|
980
|
-
admin: {
|
|
981
|
-
cost: 150.00,
|
|
982
|
-
travelTime: 30
|
|
983
|
-
}
|
|
984
|
-
}, true);
|
|
985
|
-
|
|
986
|
-
// List today's appointments
|
|
987
|
-
const today = new Date().toISOString().split('T')[0];
|
|
988
|
-
const todaysAppointments = await app.records.list(collectionId, appId, {
|
|
989
|
-
recordType: 'service_appointment',
|
|
990
|
-
startsAt: `gte:${today}T00:00:00Z`,
|
|
991
|
-
sort: 'startsAt:asc'
|
|
992
|
-
}, true);
|
|
993
|
-
```
|
|
994
|
-
|
|
995
|
-
### Example: Usage Tracking
|
|
996
|
-
|
|
997
|
-
```typescript
|
|
998
|
-
// Log product usage (could be triggered by IoT device)
|
|
999
|
-
await app.records.create(collectionId, appId, {
|
|
1000
|
-
recordType: 'usage_log',
|
|
1001
|
-
visibility: 'admin',
|
|
1002
|
-
productId: product.id,
|
|
1003
|
-
proofId: proof.id,
|
|
1004
|
-
startsAt: new Date().toISOString(),
|
|
1005
|
-
data: {
|
|
1006
|
-
metric: 'power_on',
|
|
1007
|
-
duration: 3600, // seconds
|
|
1008
|
-
location: 'geo:37.7749,-122.4194'
|
|
1009
|
-
}
|
|
1010
|
-
}, true);
|
|
1011
|
-
|
|
1012
|
-
// Aggregate usage metrics
|
|
1013
|
-
const usageStats = await app.records.aggregate(collectionId, appId, {
|
|
1014
|
-
filters: {
|
|
1015
|
-
record_type: 'usage_log',
|
|
1016
|
-
created_at: {
|
|
1017
|
-
gte: '2026-02-01',
|
|
1018
|
-
lte: '2026-02-28'
|
|
1019
|
-
}
|
|
1020
|
-
},
|
|
1021
|
-
groupBy: ['product_id'],
|
|
1022
|
-
metrics: ['count']
|
|
1023
|
-
}, true);
|
|
1024
|
-
```
|
|
1025
|
-
|
|
1026
878
|
---
|
|
1027
879
|
|
|
1028
880
|
## Public Create Policies
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
>
|
|
7
7
|
> Status: **standard**. New apps MUST follow this contract; existing apps SHOULD migrate.
|
|
8
8
|
>
|
|
9
|
-
> SDK: `@proveanything/smartlinks` ≥ **
|
|
9
|
+
> SDK: `@proveanything/smartlinks` ≥ **2.0** (R5).
|
|
10
10
|
> Admin shell (React only): `@proveanything/smartlinks-utils-ui` ≥ **0.7.6** — required for the admin side if using the React shell; not needed in public widgets.
|
|
11
11
|
|
|
12
12
|
---
|
|
@@ -347,7 +347,7 @@ interface EditorContext<TData> {
|
|
|
347
347
|
|
|
348
348
|
## 7. Migration checklist (existing apps)
|
|
349
349
|
|
|
350
|
-
1. **Update SDKs:** `@proveanything/smartlinks@^
|
|
350
|
+
1. **Update SDKs:** `@proveanything/smartlinks@^2.0`, `@proveanything/smartlinks-utils-ui@^2.0`.
|
|
351
351
|
2. **Add `cardinality` and `allowFacetRules`** to every entry under `records` in `app.admin.json`.
|
|
352
352
|
3. **Add `'rule'` (and `'collection'` if missing) to `scopes`** wherever `allowFacetRules: true`.
|
|
353
353
|
4. **Pass `cardinality`** to `<RecordsAdminShell>`.
|
|
@@ -212,26 +212,18 @@ In **direct-component mode**, there are no URL search params — context comes a
|
|
|
212
212
|
|
|
213
213
|
**Cause:** Your bundle includes its own copy of React instead of using the parent's shared instance.
|
|
214
214
|
|
|
215
|
-
**Fix:**
|
|
215
|
+
**Fix:** Externalize the shared-dependency contract — read it from the SDK so it can't drift:
|
|
216
216
|
|
|
217
217
|
```ts
|
|
218
218
|
// vite.config.container.ts or vite.config.widget.ts
|
|
219
|
+
import { SHARED_DEPENDENCY_SPECIFIERS } from '@proveanything/smartlinks'
|
|
219
220
|
export default defineConfig({
|
|
220
|
-
build: {
|
|
221
|
-
rollupOptions: {
|
|
222
|
-
external: [
|
|
223
|
-
'react',
|
|
224
|
-
'react-dom',
|
|
225
|
-
'react/jsx-runtime',
|
|
226
|
-
'react-router-dom',
|
|
227
|
-
'@proveanything/smartlinks',
|
|
228
|
-
// ... other shared dependencies
|
|
229
|
-
],
|
|
230
|
-
},
|
|
231
|
-
},
|
|
221
|
+
build: { rollupOptions: { external: [...SHARED_DEPENDENCY_SPECIFIERS] } },
|
|
232
222
|
});
|
|
233
223
|
```
|
|
234
224
|
|
|
225
|
+
> The full list + `globals` map is canonical in [host-dependency-contract.md](host-dependency-contract.md). Never bundle your own React (two instances = a hard crash).
|
|
226
|
+
|
|
235
227
|
---
|
|
236
228
|
|
|
237
229
|
### ❌ Context values are undefined in direct-component mode
|
|
@@ -320,4 +312,4 @@ Now that you understand the core concepts, see the implementation guides:
|
|
|
320
312
|
- [deep-link-discovery.md](./deep-link-discovery.md) — Deep linking patterns
|
|
321
313
|
- [portal-back-button.md](./portal-back-button.md) — Portal shell back-button behavior for hierarchical embeds
|
|
322
314
|
- [portal-auth-broadcast.md](./portal-auth-broadcast.md) — Custom authentication and session broadcast
|
|
323
|
-
- [
|
|
315
|
+
- [app-manifest.md](./app-manifest.md) — App configuration files (manifest + admin)
|
package/dist/docs/caching.md
CHANGED
|
@@ -119,13 +119,32 @@ Different API resources have different cache lifetimes:
|
|
|
119
119
|
```typescript
|
|
120
120
|
import { invalidateCache } from '@smartlinks/sdk';
|
|
121
121
|
|
|
122
|
-
//
|
|
122
|
+
// Substring match (default): clears the collection AND everything under it
|
|
123
|
+
// (settings, widgets, products, proofs) — a prefix wipe.
|
|
123
124
|
invalidateCache('/collection/abc123');
|
|
124
125
|
|
|
126
|
+
// Exact match: clears ONLY that entry, not its sub-resources. Use this to avoid
|
|
127
|
+
// accidental cascade wipes.
|
|
128
|
+
invalidateCache('/collection/abc123', { exact: true });
|
|
129
|
+
|
|
125
130
|
// Clear all product caches
|
|
126
131
|
invalidateCache('/product/');
|
|
127
132
|
```
|
|
128
133
|
|
|
134
|
+
### Don't force-refresh from a container-hosted app
|
|
135
|
+
|
|
136
|
+
`getWidgets(collectionId, { force: true })` (and `getAppConfig(..., { force: true })`)
|
|
137
|
+
bypass the cache and re-fetch. Inside a **container/iframe app**, the host has usually
|
|
138
|
+
already fetched that data and shares its own cache — forcing a refetch duplicates the
|
|
139
|
+
request the host just made. `force: true` was a **polling-era workaround** for not being
|
|
140
|
+
able to tell when something changed; **prefer the default (cached) path** in hosted apps.
|
|
141
|
+
|
|
142
|
+
> **Direction:** the platform is moving to a **push-based** model — app/config updates are
|
|
143
|
+
> pushed centrally and invalidate the relevant caches immediately, so a normal GET returns
|
|
144
|
+
> fresh data (and, with conditional requests/ETags, returns "not modified" cheaply) without
|
|
145
|
+
> anyone polling or forcing. As that lands, `force: true` should disappear from app code
|
|
146
|
+
> entirely. Don't build new flows around it.
|
|
147
|
+
|
|
129
148
|
### Clear All Caches
|
|
130
149
|
|
|
131
150
|
```typescript
|
|
@@ -164,23 +183,3 @@ try {
|
|
|
164
183
|
}
|
|
165
184
|
```
|
|
166
185
|
|
|
167
|
-
## Migration from Old Behavior
|
|
168
|
-
|
|
169
|
-
If you previously relied on caches persisting across refreshes:
|
|
170
|
-
|
|
171
|
-
```typescript
|
|
172
|
-
// Old (implicit) behavior:
|
|
173
|
-
// - SessionStorage survived refreshes
|
|
174
|
-
// - Apps might see stale data after F5
|
|
175
|
-
|
|
176
|
-
// New (explicit) behavior:
|
|
177
|
-
configureSdkCache({
|
|
178
|
-
clearOnPageLoad: true, // default - fresh data on refresh
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// If you REALLY need old behavior (not recommended):
|
|
182
|
-
configureSdkCache({
|
|
183
|
-
clearOnPageLoad: false,
|
|
184
|
-
persistence: 'indexeddb', // move to IndexedDB instead
|
|
185
|
-
});
|
|
186
|
-
```
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Container Tracking
|
|
2
2
|
|
|
3
3
|
> Physical or logical groupings with hierarchical nesting, item membership, and attestation history.
|
|
4
|
+
>
|
|
5
|
+
> **Not [containers.md](containers.md)** — that's the embeddable app *bundle*. This doc is about grouping physical/logical **items** (cases, pallets, shipments…).
|
|
4
6
|
|
|
5
7
|
---
|
|
6
8
|
|