@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
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Host dependency contract (R5)
|
|
2
|
+
|
|
3
|
+
> **SmartLinks SDK 2.x** (current `latest`). The portal host (R5) provides a fixed set of runtime
|
|
4
|
+
> libraries as window globals. Micro-apps **externalise** these and resolve them from the host —
|
|
5
|
+
> they must **not** bundle their own copies. This keeps one instance of React (and friends) on the
|
|
6
|
+
> page and keeps bundles small.
|
|
7
|
+
|
|
8
|
+
## The one rule that matters: externalise, never bundle
|
|
9
|
+
|
|
10
|
+
A container/widget/executor bundle **must externalise `react`, `react-dom`, and every shared
|
|
11
|
+
dependency below**, resolving them from the host globals. **Bundling your own React is the one
|
|
12
|
+
hard failure** — two React instances on the page → hooks break → crash. (Bundling `liquidjs` or
|
|
13
|
+
another shared lib is wasteful and can double-load, but React is the fatal one.)
|
|
14
|
+
|
|
15
|
+
React-18-built bundles keep working on the R5 host: they externalise React and run against the
|
|
16
|
+
host's **React 19** runtime. A bundle compiled against React 18 *typings* runs fine on the 19
|
|
17
|
+
*runtime* — see backwards-compatibility below.
|
|
18
|
+
|
|
19
|
+
## Vite / Rollup config
|
|
20
|
+
|
|
21
|
+
Externalise the shared deps and map each to its window global:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// vite.config.ts (library build for a container/widget/executor)
|
|
25
|
+
import { defineConfig } from 'vite'
|
|
26
|
+
export default defineConfig({
|
|
27
|
+
build: {
|
|
28
|
+
lib: { entry: 'src/index.tsx', formats: ['umd'], name: 'MyApp', fileName: () => 'widgets.umd.js' },
|
|
29
|
+
rollupOptions: {
|
|
30
|
+
// Everything the host provides — do NOT bundle these.
|
|
31
|
+
external: [
|
|
32
|
+
'react', 'react-dom', 'react/jsx-runtime',
|
|
33
|
+
'@proveanything/smartlinks',
|
|
34
|
+
'react-router-dom', '@tanstack/react-query',
|
|
35
|
+
'lucide-react', 'date-fns', 'liquidjs', 'class-variance-authority',
|
|
36
|
+
'@radix-ui/react-slot', '@radix-ui/react-dialog', '@radix-ui/react-popover',
|
|
37
|
+
'@radix-ui/react-tooltip', '@radix-ui/react-tabs', '@radix-ui/react-accordion',
|
|
38
|
+
'@radix-ui/react-select', '@radix-ui/react-scroll-area', '@radix-ui/react-label',
|
|
39
|
+
'@radix-ui/react-toast', '@radix-ui/react-progress', '@radix-ui/react-avatar',
|
|
40
|
+
],
|
|
41
|
+
output: {
|
|
42
|
+
globals: {
|
|
43
|
+
'react': 'React', 'react-dom': 'ReactDOM', 'react/jsx-runtime': 'jsxRuntime',
|
|
44
|
+
'@proveanything/smartlinks': 'SL',
|
|
45
|
+
'react-router-dom': 'ReactRouterDOM', '@tanstack/react-query': 'ReactQuery',
|
|
46
|
+
'lucide-react': 'LucideReact', 'date-fns': 'dateFns', 'liquidjs': 'LiquidJS',
|
|
47
|
+
'class-variance-authority': 'CVA',
|
|
48
|
+
'@radix-ui/react-slot': 'RadixSlot', '@radix-ui/react-dialog': 'RadixDialog',
|
|
49
|
+
'@radix-ui/react-popover': 'RadixPopover', '@radix-ui/react-tooltip': 'RadixTooltip',
|
|
50
|
+
'@radix-ui/react-tabs': 'RadixTabs', '@radix-ui/react-accordion': 'RadixAccordion',
|
|
51
|
+
'@radix-ui/react-select': 'RadixSelect', '@radix-ui/react-scroll-area': 'RadixScrollArea',
|
|
52
|
+
'@radix-ui/react-label': 'RadixLabel', '@radix-ui/react-toast': 'RadixToast',
|
|
53
|
+
'@radix-ui/react-progress': 'RadixProgress', '@radix-ui/react-avatar': 'RadixAvatar',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Host-provided globals (build against versions ≤ these)
|
|
62
|
+
|
|
63
|
+
| Import | Window global | Host provides (R5) |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `react` | `React` | 19.3 (accepts 18.3 builds) |
|
|
66
|
+
| `react-dom` | `ReactDOM` | 19.3 (accepts 18.3 builds) |
|
|
67
|
+
| `react/jsx-runtime` | `jsxRuntime` | 19.3 |
|
|
68
|
+
| `@proveanything/smartlinks` | `SL` | 2.0.5 |
|
|
69
|
+
| `react-router-dom` | `ReactRouterDOM` | 7.18 (accepts 6.x builds) |
|
|
70
|
+
| `@tanstack/react-query` | `ReactQuery` | 5.103 |
|
|
71
|
+
| `lucide-react` | `LucideReact` | 1.47 |
|
|
72
|
+
| `date-fns` | `dateFns` | 4.4 |
|
|
73
|
+
| `liquidjs` | `LiquidJS` | 10.27+ |
|
|
74
|
+
| `class-variance-authority` | `CVA` | 0.7 |
|
|
75
|
+
| `@radix-ui/react-slot` | `RadixSlot` | 1.2.4 |
|
|
76
|
+
| `@radix-ui/react-dialog` | `RadixDialog` | 1.1.23 |
|
|
77
|
+
| `@radix-ui/react-popover` | `RadixPopover` | 1.1.1 |
|
|
78
|
+
| `@radix-ui/react-tooltip` | `RadixTooltip` | 1.2.8 |
|
|
79
|
+
| `@radix-ui/react-tabs` | `RadixTabs` | 1.1.13 |
|
|
80
|
+
| `@radix-ui/react-accordion` | `RadixAccordion` | 1.2.12 |
|
|
81
|
+
| `@radix-ui/react-select` | `RadixSelect` | 2.3.7 |
|
|
82
|
+
| `@radix-ui/react-scroll-area` | `RadixScrollArea` | 1.2.10 |
|
|
83
|
+
| `@radix-ui/react-label` | `RadixLabel` | 2.1.8 |
|
|
84
|
+
| `@radix-ui/react-toast` | `RadixToast` | 1.2.15 |
|
|
85
|
+
| `@radix-ui/react-progress` | `RadixProgress` | 1.1.8 |
|
|
86
|
+
| `@radix-ui/react-avatar` | `RadixAvatar` | 1.1.11 |
|
|
87
|
+
|
|
88
|
+
`liquidjs` is **host-provided** — externalise it, don't ship a second copy (frequently missed).
|
|
89
|
+
|
|
90
|
+
## Backwards compatibility
|
|
91
|
+
|
|
92
|
+
React-18-built containers and widgets keep working unchanged on R5. A pre-existing bundle only
|
|
93
|
+
breaks if it:
|
|
94
|
+
|
|
95
|
+
- calls `ReactDOM.render` / `hydrate` / `unmountComponentAtNode` (self-mounting — containers are
|
|
96
|
+
mounted by the host, so this only affects apps that mount themselves);
|
|
97
|
+
- relies on `defaultProps` / `propTypes` on **function** components (React 19 silently ignores
|
|
98
|
+
these → missing defaults, not a crash);
|
|
99
|
+
- uses string refs, `findDOMNode`, or legacy context;
|
|
100
|
+
- **bundles its own React** instead of externalising it → two instances → crash (the one hard
|
|
101
|
+
failure);
|
|
102
|
+
- imports a `lucide-react` icon renamed/removed in the 0.x → 1.x move.
|
|
103
|
+
|
|
104
|
+
## Tailwind is *not* part of the contract
|
|
105
|
+
|
|
106
|
+
Bundles ship their own compiled CSS, so the host's Tailwind version is irrelevant to them. A
|
|
107
|
+
micro-app can stay on **Tailwind 3 indefinitely**, or adopt Tailwind 4 — its choice. (The starter
|
|
108
|
+
app ships the Tailwind 4 CSS-first layout as the default; see its README.)
|
|
109
|
+
|
|
110
|
+
## The R5 host stack (reference)
|
|
111
|
+
|
|
112
|
+
React **19.3** · Vite **8.3** · react-router-dom **7.18** · Tailwind **4.3** (CSS-first) ·
|
|
113
|
+
TypeScript **6.0** · ESLint **10.11** · `@proveanything/smartlinks` **2.0.5** ·
|
|
114
|
+
`@proveanything/smartlinks-utils-ui` **1.16.4** · liquidjs **10.29**.
|
|
115
|
+
|
|
116
|
+
> **TypeScript 7** (the native/Go compiler) is **deliberately deferred** — tooling hasn't settled.
|
|
117
|
+
> Target **TS 6** for R5; it compiles existing code with no source changes.
|
|
118
|
+
|
|
119
|
+
## Security floor
|
|
120
|
+
|
|
121
|
+
An R5 app must ship with **`npm audit` reporting zero vulnerabilities**. Run it against the real
|
|
122
|
+
registry — `npm audit --registry=https://registry.npmjs.org` — because the sandbox mirror doesn't
|
|
123
|
+
implement the audit endpoint. Two high-severity advisories are already pinned out in the R5 set and
|
|
124
|
+
must stay pinned:
|
|
125
|
+
|
|
126
|
+
- **`react-router` 7.12.0–7.18.1** — RSC-mode CSRF bypass. Build against **7.18.4+** (the host
|
|
127
|
+
provides ≥7.18.4). Never ship a router below 7.18.4.
|
|
128
|
+
- **`browserslist` ≤4.28.6** — unbounded memory growth / prototype write. It's a transitive build
|
|
129
|
+
dependency, so pin it with an `overrides` entry (`"overrides": { "browserslist": "^4.29" }`), not
|
|
130
|
+
a direct dependency.
|
|
131
|
+
|
|
132
|
+
Both are compile-time/build-time concerns for the app's own toolchain; neither is a host global.
|
|
133
|
+
|
|
134
|
+
## Reading the contract programmatically (one source of truth)
|
|
135
|
+
|
|
136
|
+
Don't hard-code the externalized list — import it from the SDK, so hosts and apps
|
|
137
|
+
never drift:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import {
|
|
141
|
+
SHARED_DEPENDENCY_CONTRACT_VERSION, // 'v5'
|
|
142
|
+
SHARED_DEPENDENCIES, // [{ specifier, globalName, minVersion, importMapPath }, …] (25)
|
|
143
|
+
SHARED_DEPENDENCY_SPECIFIERS, // bare specifiers — drop straight into a bundler `external` list
|
|
144
|
+
getHostSharedDependencies, // what the live host advertises at runtime, or null
|
|
145
|
+
} from '@proveanything/smartlinks'
|
|
146
|
+
|
|
147
|
+
// Build config: externalize exactly the contract.
|
|
148
|
+
export const external = [...SHARED_DEPENDENCY_SPECIFIERS]
|
|
149
|
+
|
|
150
|
+
// Runtime: degrade gracefully on an older host that lacks the import map.
|
|
151
|
+
const host = getHostSharedDependencies()
|
|
152
|
+
if (host && host.version !== SHARED_DEPENDENCY_CONTRACT_VERSION) {
|
|
153
|
+
console.warn(`Built against ${SHARED_DEPENDENCY_CONTRACT_VERSION}, host serves ${host.version}`)
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The host publishes its live contract on `window.__SMARTLINKS_SHARED__` (`{ version, specifiers }`),
|
|
158
|
+
which `getHostSharedDependencies()` reads. Import-map shim paths follow `importMapPathFor(specifier)`
|
|
159
|
+
(`/sl-shared/<version>/<slug>.js`), so both the portal and the SDK generate identical paths.
|
package/docs/iframe-responder.md
CHANGED
|
@@ -468,3 +468,311 @@ import type {
|
|
|
468
468
|
### Portal back exits the app too early
|
|
469
469
|
- Set `state.parentPath` on route changes for screens that should navigate "up" inside the app.
|
|
470
470
|
- Make sure the receiving app listens for `smartlinks-navigate` if the SDK version in use does not already handle it.
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
## Streaming in a hand-rolled parent (without IframeResponder)
|
|
475
|
+
|
|
476
|
+
The following applies **only if you build your own parent proxy** instead of using the `IframeResponder` class above — which already handles AI streaming for you. It is the wire protocol for forwarding SSE/streaming responses to a child app in proxy mode.
|
|
477
|
+
### Goal
|
|
478
|
+
|
|
479
|
+
Keep the existing architecture:
|
|
480
|
+
|
|
481
|
+
- local mode: child calls API directly
|
|
482
|
+
- iframe proxy mode: child never owns auth state and streams through the parent
|
|
483
|
+
|
|
484
|
+
This keeps user/session authority in the parent while making AI streaming behave like the rest of the SDK transport.
|
|
485
|
+
|
|
486
|
+
### What changed
|
|
487
|
+
|
|
488
|
+
Previously, proxy mode only supported one-shot request/response messages:
|
|
489
|
+
|
|
490
|
+
- `_smartlinksProxyRequest`
|
|
491
|
+
- `_smartlinksProxyResponse`
|
|
492
|
+
|
|
493
|
+
Streaming now adds a second protocol for long-lived responses:
|
|
494
|
+
|
|
495
|
+
- `_smartlinksProxyStreamRequest`
|
|
496
|
+
- `_smartlinksProxyStream`
|
|
497
|
+
- `_smartlinksProxyStreamAbort`
|
|
498
|
+
|
|
499
|
+
### New parent message handling
|
|
500
|
+
|
|
501
|
+
#### 1. Listen for stream requests
|
|
502
|
+
|
|
503
|
+
The iframe child may now send this message:
|
|
504
|
+
|
|
505
|
+
```ts
|
|
506
|
+
{
|
|
507
|
+
_smartlinksProxyStreamRequest: true,
|
|
508
|
+
id: string,
|
|
509
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
|
|
510
|
+
path: string,
|
|
511
|
+
body?: any,
|
|
512
|
+
headers?: Record<string, string>
|
|
513
|
+
}
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
Parent behavior:
|
|
517
|
+
|
|
518
|
+
- treat this like a proxied API request
|
|
519
|
+
- build the real API URL from your configured base URL plus `path`
|
|
520
|
+
- send the request using the parent's current auth/session context
|
|
521
|
+
- expect an SSE / streaming response body
|
|
522
|
+
- keep the request open until the stream ends or is aborted
|
|
523
|
+
|
|
524
|
+
#### 2. Forward stream lifecycle messages back to the child
|
|
525
|
+
|
|
526
|
+
The parent should send messages back to the iframe using this envelope:
|
|
527
|
+
|
|
528
|
+
```ts
|
|
529
|
+
{
|
|
530
|
+
_smartlinksProxyStream: true,
|
|
531
|
+
id: string,
|
|
532
|
+
phase: 'open' | 'event' | 'end' | 'error',
|
|
533
|
+
data?: any,
|
|
534
|
+
error?: string,
|
|
535
|
+
status?: number
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
Phases:
|
|
540
|
+
|
|
541
|
+
- `open`
|
|
542
|
+
- optional but recommended
|
|
543
|
+
- indicates the upstream streaming request was accepted and a body exists
|
|
544
|
+
- `event`
|
|
545
|
+
- contains one parsed JSON event from an SSE `data:` frame
|
|
546
|
+
- send one message per logical event payload
|
|
547
|
+
- `end`
|
|
548
|
+
- sent once when the stream finishes normally
|
|
549
|
+
- `error`
|
|
550
|
+
- sent if the upstream request fails before or during streaming
|
|
551
|
+
|
|
552
|
+
#### 3. Support abort from the child
|
|
553
|
+
|
|
554
|
+
The child may stop reading early and send:
|
|
555
|
+
|
|
556
|
+
```ts
|
|
557
|
+
{
|
|
558
|
+
_smartlinksProxyStreamAbort: true,
|
|
559
|
+
id: string
|
|
560
|
+
}
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
Parent behavior:
|
|
564
|
+
|
|
565
|
+
- look up the active stream by `id`
|
|
566
|
+
- abort the underlying fetch / reader
|
|
567
|
+
- clean up any local state for that stream
|
|
568
|
+
- do not keep streaming after abort
|
|
569
|
+
|
|
570
|
+
### SSE forwarding rules
|
|
571
|
+
|
|
572
|
+
The upstream AI endpoints return SSE-like frames. The parent should:
|
|
573
|
+
|
|
574
|
+
- read the response body as a stream
|
|
575
|
+
- buffer text until line boundaries
|
|
576
|
+
- collect `data:` lines for a single event
|
|
577
|
+
- join multi-line `data:` payloads with `\n`
|
|
578
|
+
- ignore blank events
|
|
579
|
+
- stop on `data: [DONE]`
|
|
580
|
+
- JSON-parse each event payload
|
|
581
|
+
- forward parsed payloads to the iframe as `_smartlinksProxyStream` with `phase: 'event'`
|
|
582
|
+
|
|
583
|
+
Minimal parsing behavior:
|
|
584
|
+
|
|
585
|
+
1. accumulate bytes into text
|
|
586
|
+
2. split on `\r?\n`
|
|
587
|
+
3. collect each `data:` line
|
|
588
|
+
4. on blank line, finalize the event
|
|
589
|
+
5. if payload is `[DONE]`, finish
|
|
590
|
+
6. otherwise `JSON.parse(payload)` and forward
|
|
591
|
+
|
|
592
|
+
### Auth and session expectations
|
|
593
|
+
|
|
594
|
+
The parent remains the source of truth for auth.
|
|
595
|
+
|
|
596
|
+
That means the parent stream handler should:
|
|
597
|
+
|
|
598
|
+
- use the same auth headers/token source as normal proxied requests
|
|
599
|
+
- not require the iframe to know the bearer token or API key
|
|
600
|
+
- naturally pick up the current logged-in user when the stream starts
|
|
601
|
+
- cancel active streams if your app invalidates session state on logout or account switch
|
|
602
|
+
|
|
603
|
+
In practice, the stream request should use the same header-building logic as your normal parent proxy transport.
|
|
604
|
+
|
|
605
|
+
### Error handling expectations
|
|
606
|
+
|
|
607
|
+
If the upstream fetch returns a non-2xx status:
|
|
608
|
+
|
|
609
|
+
- try to read the JSON error body
|
|
610
|
+
- derive a useful message
|
|
611
|
+
- send one `_smartlinksProxyStream` message with `phase: 'error'`
|
|
612
|
+
- include `status` when available
|
|
613
|
+
- do not send `end` afterward
|
|
614
|
+
|
|
615
|
+
If the stream body is missing unexpectedly:
|
|
616
|
+
|
|
617
|
+
- send `phase: 'error'`
|
|
618
|
+
|
|
619
|
+
If JSON parsing fails for a single event chunk:
|
|
620
|
+
|
|
621
|
+
- safest behavior is to ignore that malformed chunk and continue
|
|
622
|
+
|
|
623
|
+
### State the parent should keep
|
|
624
|
+
|
|
625
|
+
Track active streams in a map keyed by `id`:
|
|
626
|
+
|
|
627
|
+
```ts
|
|
628
|
+
Map<string, AbortController>
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
Recommended cleanup points:
|
|
632
|
+
|
|
633
|
+
- on normal stream end
|
|
634
|
+
- on error
|
|
635
|
+
- on child abort
|
|
636
|
+
- on iframe detach/unmount
|
|
637
|
+
- on parent auth reset/logout if you want all in-flight streams cancelled immediately
|
|
638
|
+
|
|
639
|
+
### Parent implementation outline
|
|
640
|
+
|
|
641
|
+
```ts
|
|
642
|
+
const activeStreams = new Map<string, AbortController>()
|
|
643
|
+
|
|
644
|
+
window.addEventListener('message', async (event) => {
|
|
645
|
+
const msg = event.data
|
|
646
|
+
|
|
647
|
+
if (msg?._smartlinksProxyStreamAbort && msg.id) {
|
|
648
|
+
activeStreams.get(msg.id)?.abort()
|
|
649
|
+
activeStreams.delete(msg.id)
|
|
650
|
+
return
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (msg?._smartlinksProxyStreamRequest && msg.id) {
|
|
654
|
+
const controller = new AbortController()
|
|
655
|
+
activeStreams.set(msg.id, controller)
|
|
656
|
+
|
|
657
|
+
try {
|
|
658
|
+
const response = await fetch(buildUrl(msg.path), {
|
|
659
|
+
method: msg.method,
|
|
660
|
+
headers: msg.headers,
|
|
661
|
+
body: msg.body ? JSON.stringify(msg.body) : undefined,
|
|
662
|
+
signal: controller.signal,
|
|
663
|
+
})
|
|
664
|
+
|
|
665
|
+
if (!response.ok || !response.body) {
|
|
666
|
+
postError(...)
|
|
667
|
+
return
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
postOpen(...)
|
|
671
|
+
await forwardSse(response.body, parsed => postEvent(...parsed))
|
|
672
|
+
postEnd(...)
|
|
673
|
+
} catch (err) {
|
|
674
|
+
if (err?.name !== 'AbortError') postError(...)
|
|
675
|
+
} finally {
|
|
676
|
+
activeStreams.delete(msg.id)
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
})
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
### Exact protocol summary
|
|
683
|
+
|
|
684
|
+
#### Child → parent
|
|
685
|
+
|
|
686
|
+
Standard stream request:
|
|
687
|
+
|
|
688
|
+
```ts
|
|
689
|
+
{
|
|
690
|
+
_smartlinksProxyStreamRequest: true,
|
|
691
|
+
id,
|
|
692
|
+
method,
|
|
693
|
+
path,
|
|
694
|
+
body,
|
|
695
|
+
headers
|
|
696
|
+
}
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
Abort request:
|
|
700
|
+
|
|
701
|
+
```ts
|
|
702
|
+
{
|
|
703
|
+
_smartlinksProxyStreamAbort: true,
|
|
704
|
+
id
|
|
705
|
+
}
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
#### Parent → child
|
|
709
|
+
|
|
710
|
+
Open:
|
|
711
|
+
|
|
712
|
+
```ts
|
|
713
|
+
{
|
|
714
|
+
_smartlinksProxyStream: true,
|
|
715
|
+
id,
|
|
716
|
+
phase: 'open'
|
|
717
|
+
}
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
Event:
|
|
721
|
+
|
|
722
|
+
```ts
|
|
723
|
+
{
|
|
724
|
+
_smartlinksProxyStream: true,
|
|
725
|
+
id,
|
|
726
|
+
phase: 'event',
|
|
727
|
+
data: parsedJsonEvent
|
|
728
|
+
}
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
End:
|
|
732
|
+
|
|
733
|
+
```ts
|
|
734
|
+
{
|
|
735
|
+
_smartlinksProxyStream: true,
|
|
736
|
+
id,
|
|
737
|
+
phase: 'end'
|
|
738
|
+
}
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
Error:
|
|
742
|
+
|
|
743
|
+
```ts
|
|
744
|
+
{
|
|
745
|
+
_smartlinksProxyStream: true,
|
|
746
|
+
id,
|
|
747
|
+
phase: 'error',
|
|
748
|
+
error: 'message',
|
|
749
|
+
status?: number
|
|
750
|
+
}
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
### What does not change
|
|
754
|
+
|
|
755
|
+
These parts of the parent iframe integration stay the same:
|
|
756
|
+
|
|
757
|
+
- normal `_smartlinksProxyRequest` request/response flow
|
|
758
|
+
- upload proxy flow
|
|
759
|
+
- auth login/logout postMessage handling
|
|
760
|
+
- route/deep-link handling
|
|
761
|
+
- resize handling
|
|
762
|
+
|
|
763
|
+
This is an additive protocol, not a replacement.
|
|
764
|
+
|
|
765
|
+
### Current SDK reference
|
|
766
|
+
|
|
767
|
+
The SDK implementation lives in:
|
|
768
|
+
|
|
769
|
+
- [src/http.ts](src/http.ts)
|
|
770
|
+
- [src/iframeResponder.ts](src/iframeResponder.ts)
|
|
771
|
+
- [src/types/iframeResponder.ts](src/types/iframeResponder.ts)
|
|
772
|
+
- [src/api/ai.ts](src/api/ai.ts)
|
|
773
|
+
|
|
774
|
+
### Practical recommendation
|
|
775
|
+
|
|
776
|
+
If your parent already uses `IframeResponder`, prefer upgrading to the SDK version with these changes instead of re-implementing the protocol manually.
|
|
777
|
+
|
|
778
|
+
If your parent has a custom iframe bridge, implement exactly the three new message types above and reuse your existing auth/header logic from normal proxied requests.
|
package/docs/item-context.md
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# Item Context (container prop)
|
|
2
2
|
|
|
3
|
-
> **Copy this file into `node_modules/@proveanything/smartlinks/docs/item-context.md`** in the published SDK package.
|
|
4
|
-
|
|
5
3
|
When the URL points at a specific item — either a **serial proof URL** or an
|
|
6
4
|
**NFC tap** — the portal derives an `ItemContext` describing what it found
|
|
7
5
|
and hands it to the container as the **`itemContext`** prop.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Mobile Admin Container SDK
|
|
2
2
|
|
|
3
|
-
> **Version:** 1.0 · **Platform:** SmartLinks
|
|
3
|
+
> **Version:** 1.0 · **Platform:** SmartLinks R5 · **Last updated:** 2026-09-21
|
|
4
4
|
|
|
5
5
|
This document describes how to build a **Mobile Admin Container** — a SmartLinks microapp that provides an in-the-field operator/admin surface optimised for mobile devices. These containers ship as a **separate `mobileAdmin` bundle** (not inside the `containers` bundle) so that Capacitor plugins, offline helpers, and operator-only code never reach the public consumer bundle.
|
|
6
6
|
|
|
@@ -271,7 +271,7 @@ Declare the bundle under the top-level `mobileAdmin` key in `app.manifest.json`.
|
|
|
271
271
|
"meta": { "appId": "my-app", "name": "My App", "version": "1.0.0" },
|
|
272
272
|
|
|
273
273
|
"containers": {
|
|
274
|
-
"files": { "js": { "umd": "dist/containers.umd.js", "esm": "dist/containers.
|
|
274
|
+
"files": { "js": { "umd": "dist/containers.umd.js", "esm": "dist/containers.esm.js" }, "css": "dist/containers.css" },
|
|
275
275
|
"components": [
|
|
276
276
|
{ "name": "PublicContainer", "description": "Default consumer experience" }
|
|
277
277
|
]
|
|
@@ -281,7 +281,7 @@ Declare the bundle under the top-level `mobileAdmin` key in `app.manifest.json`.
|
|
|
281
281
|
"files": {
|
|
282
282
|
"js": {
|
|
283
283
|
"umd": "dist/mobile-admin.umd.js",
|
|
284
|
-
"esm": "dist/mobile-admin.
|
|
284
|
+
"esm": "dist/mobile-admin.esm.js"
|
|
285
285
|
},
|
|
286
286
|
"css": null
|
|
287
287
|
},
|
|
@@ -416,7 +416,7 @@ The mobile admin bundle has its own Vite config: `vite.config.mobile-admin.ts`.
|
|
|
416
416
|
|
|
417
417
|
```
|
|
418
418
|
dist/mobile-admin.umd.js
|
|
419
|
-
dist/mobile-admin.
|
|
419
|
+
dist/mobile-admin.esm.js
|
|
420
420
|
dist/mobile-admin.css (if needed)
|
|
421
421
|
```
|
|
422
422
|
|
package/docs/mpa.md
CHANGED
|
@@ -56,9 +56,9 @@ vite build
|
|
|
56
56
|
| Step | Config / Script | Gate env var | Output |
|
|
57
57
|
|------|----------------|-------------|--------|
|
|
58
58
|
| 1 | `vite.config.ts` | Always runs | `index.html`, `admin.html`, `assets/*` |
|
|
59
|
-
| 2 | `vite.config.widget.ts` | `VITE_ENABLE_WIDGETS=true` | `widgets.umd.js`, `widgets.
|
|
60
|
-
| 3 | `vite.config.container.ts` | `VITE_ENABLE_CONTAINERS=true` | `containers.umd.js`, `containers.
|
|
61
|
-
| 4 | `vite.config.executor.ts` | `VITE_ENABLE_EXECUTOR!=false` | `executor.umd.js`, `executor.
|
|
59
|
+
| 2 | `vite.config.widget.ts` | `VITE_ENABLE_WIDGETS=true` | `widgets.umd.js`, `widgets.esm.js`, `widgets.css` |
|
|
60
|
+
| 3 | `vite.config.container.ts` | `VITE_ENABLE_CONTAINERS=true` | `containers.umd.js`, `containers.esm.js`, `containers.css` |
|
|
61
|
+
| 4 | `vite.config.executor.ts` | `VITE_ENABLE_EXECUTOR!=false` | `executor.umd.js`, `executor.esm.js` |
|
|
62
62
|
| 5 | `scripts/hash-bundles.mjs` | Always runs | Renames bundles with content hashes; patches `dist/app.manifest.json` |
|
|
63
63
|
|
|
64
64
|
Steps 2–4 produce a harmless stub file when their gate env var is not set. Step 5 detects and skips stub files automatically.
|
|
@@ -107,7 +107,7 @@ dist/
|
|
|
107
107
|
└── executor-[hash].es.js ← Executor bundle (ESM)
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
> Widget and container CSS files are only present when the bundle ships custom styles. Most apps set `"css": null` in the manifest because they rely entirely on Tailwind/shadcn from the parent. See the [
|
|
110
|
+
> Widget and container CSS files are only present when the bundle ships custom styles. Most apps set `"css": null` in the manifest because they rely entirely on Tailwind/shadcn from the parent. See the [App Configuration Files](app-manifest.md) for the CSS null warning.
|
|
111
111
|
|
|
112
112
|
---
|
|
113
113
|
|
|
@@ -133,6 +133,6 @@ If the embedded app exposes nested screens, document its "up" navigation path wi
|
|
|
133
133
|
| [Widgets](widgets.md) | Widget bundle: components, props, settings |
|
|
134
134
|
| [Containers](containers.md) | Container bundle: full-app embeds |
|
|
135
135
|
| [Executor Model](executor.md) | Executor bundle: SEO, LLM content, config mutations |
|
|
136
|
-
| [
|
|
136
|
+
| [App Configuration Files](app-manifest.md) | Manifest + admin reference; widget settings schema; AI workflows |
|
|
137
137
|
| [iframe Responder](iframe-responder.md) | Reading context params inside the iframe |
|
|
138
138
|
| [Portal Back Button](portal-back-button.md) | Hierarchy-aware back navigation for embedded apps |
|
package/docs/native-facade.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Native Capability Facade (`host.native` / `SL.native`)
|
|
2
2
|
|
|
3
|
-
> **Version:** 1.12 · **Platform:** SmartLinks
|
|
3
|
+
> **Version:** 1.12 · **Platform:** SmartLinks R5 · **Last updated:** 2026-09-21
|
|
4
4
|
|
|
5
5
|
The `NativeFacade` is a thin contract layer between microapps and the device capabilities available on the current host shell (Kotlin, Capacitor iOS/Android, PWA, or browser). It lets a microapp call `host.native.share.share({...})` without knowing whether it's running over `window.SmartlinksScanner`, a Capacitor plugin, or `navigator.share`.
|
|
6
6
|
|
package/docs/overview.md
CHANGED
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
# SmartLinks Microapp Development Guide
|
|
2
2
|
|
|
3
|
-
> **Platform revision:**
|
|
4
|
-
>
|
|
3
|
+
> **Platform revision:** R5 · **SDK:** `@proveanything/smartlinks@^2` · React 19 · Vite 8 · Router 7 · Tailwind 4 · TS 6.
|
|
4
|
+
> The canonical version/host stack lives in [host-dependency-contract.md](host-dependency-contract.md).
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
## What Is a SmartLinks Microapp?
|
|
9
9
|
|
|
10
|
-
SmartLinks
|
|
10
|
+
SmartLinks connects **physical products to digital experiences**: each item has a digital identity — a **proof** — that a consumer reaches by scanning a QR / NFC tag, then claims and enriches over time. A **microapp** is a focused, embeddable React experience a brand adds to that product's digital life: product info, warranty registration, authenticity checks, manuals, competitions, loyalty, post-purchase support, and more.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Rather than baking every feature into the core platform, functionality is distributed across purpose-built apps, so a brand adds exactly the experiences its product needs. Each app:
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
- **
|
|
19
|
-
- **Share context** through URL parameters (collection, product, or proof being viewed)
|
|
20
|
-
- **Inherit identity** from the parent platform's authentication system
|
|
21
|
-
- **Adapt visually** to the brand's theme configuration
|
|
22
|
-
- **Communicate bidirectionally** with the parent via postMessage for deep linking and navigation
|
|
14
|
+
- **Embeds** in the SmartLinks Portal (public) and Admin Console (management)
|
|
15
|
+
- **Shares context** through URL parameters (collection, product, proof)
|
|
16
|
+
- **Inherits identity** from the parent platform's auth
|
|
17
|
+
- **Adapts** to the brand's theme
|
|
18
|
+
- **Communicates** with the parent via postMessage for deep linking and navigation
|
|
23
19
|
|
|
24
20
|
### Deployment Modes
|
|
25
21
|
|
|
@@ -45,7 +41,7 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
|
|
|
45
41
|
|
|
46
42
|
> Product endpoints: use `products` (plural) for new integrations. The older `product` (singular) namespace remains for backward compatibility and is deprecated.
|
|
47
43
|
|
|
48
|
-
> **
|
|
44
|
+
> **SDK baseline: `@proveanything/smartlinks@^2.0`** (R5). Install/update with `npm install @proveanything/smartlinks@^2.0`. Individual features may need a higher minor — see the relevant doc. The single source of truth for the R5 host/version stack is [`host-dependency-contract.md`](host-dependency-contract.md).
|
|
49
45
|
|
|
50
46
|
| Topic | File | When to Use |
|
|
51
47
|
|-------|------|-------------|
|
|
@@ -64,12 +60,13 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
|
|
|
64
60
|
| **Mobile Admin Container** | `docs/mobile-admin-container.md` | Building a separate Capacitor-aware mobile admin bundle for field operators |
|
|
65
61
|
| **Executors** | `docs/executor.md` | Building executor bundles for SEO, LLM content, programmatic config |
|
|
66
62
|
| **Server Functions** | `docs/server-functions.md` | App-authored server-side functions `(ctx, event) ⇒ result`: security model, runtime surface, invoking |
|
|
63
|
+
| **Agent Tools** | `docs/agent-tools.md` | Exposing your app's actions to the SmartLinks agent — an MCP facade over server functions; declare now, live loop staged; replaces `app.admin.json` AI setup |
|
|
67
64
|
| **Deploying & Registering** | `docs/deploying-apps.md` | Getting your app into the platform: fast dev publish, channels, deploy keys, registering releases |
|
|
65
|
+
| **Host Dependency Contract (R5)** | `docs/host-dependency-contract.md` | The libraries the host provides (React 19, Router 7, Radix, liquidjs, …), the externalise-don't-bundle rule + Vite config, and React-18 backwards-compat |
|
|
68
66
|
| **Deep Linking** | `docs/deep-link-discovery.md` | URL state management, navigable states, portal menus, AI nav |
|
|
69
67
|
| **Portal Back Button** | `docs/portal-back-button.md` | Hierarchy-aware "up" navigation inside embedded apps |
|
|
70
68
|
| **Portal Request Action** | `docs/portal-request-action.md` | Triggering portal built-in actions (__qrScanner, __share, __logout, etc.) from sub-apps |
|
|
71
69
|
| **Interactions** | `docs/interactions.md` | Business events, outcomes, voting, competitions, and journey triggers |
|
|
72
|
-
| **AI-Native Manifests** | `docs/manifests.md` | `app.manifest.json`, `app.admin.json`, `ai-guide.md` structure |
|
|
73
70
|
| **App Config Files** | `docs/app-manifest.md` | Full field-by-field reference for both JSON config files |
|
|
74
71
|
| **Real-time Messaging** | `docs/realtime.md` | Adding Ably real-time features (chat, live updates) |
|
|
75
72
|
| **Liquid Templates** | `docs/liquid-templates.md` | Dynamic content rendering with LiquidJS |
|
|
@@ -87,6 +84,21 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
|
|
|
87
84
|
| **Proof Share Grants** | `docs/proof-share-grants.md` | Delegated, scoped, revocable bearer access to a single proof (read/comment/verify-owner) |
|
|
88
85
|
| **Proof Ownership Transfer** | `docs/proof-ownership-transfer.md` | Moving a proof's single owner (directed transfer / open release), accept/cancel, and the state machine |
|
|
89
86
|
| **appConfig / Feature Flags** | `docs/appConfig.md` | `appConfig` settings contract — installed apps, `system.features`/`entitledAppGroups`/`meters`, `isFeatureEnabled()` helper |
|
|
87
|
+
| **App Objects** | `docs/app-objects.md` | Queryable domain objects — `app.records` / `app.cases` / `app.threads`: JSONB zones, visibility, policies, aggregations |
|
|
88
|
+
| **App Data Storage** | `docs/app-data-storage.md` | Decision guide: pick between `appConfiguration` / `userAppData` / `app.records` |
|
|
89
|
+
| **Attestations** | `docs/attestations.md` | Postgres attestations API (proof-level user/admin data; replaces the legacy Firestore path) |
|
|
90
|
+
| **Caching** | `docs/caching.md` | SDK GET cache tiers + TTLs, `invalidateCache({ exact })`, and `force`/push guidance |
|
|
91
|
+
| **Comms & Broadcasts** | `docs/comms.md` | Transactional comms, campaigns, consent, push |
|
|
92
|
+
| **Container Tracking** | `docs/container-tracking.md` | Physical/logical **item** container groupings — *not* app containers (see containers.md) |
|
|
93
|
+
| **Lots** | `docs/lots.md` | Cross-SKU production "Lot" entity |
|
|
94
|
+
| **Loyalty** | `docs/loyalty.md` | Points, members, earning rules |
|
|
95
|
+
| **Sequences** | `docs/sequences.md` | Atomic monotonic number allocation (raffle / queue / edition) |
|
|
96
|
+
| **Integrations** | `docs/integrations.md` | Inbound/outbound external-system integration flows |
|
|
97
|
+
| **Item Context** | `docs/item-context.md` | The `itemContext` container prop (serial / NFC authenticity context) |
|
|
98
|
+
| **Native Facade** | `docs/native-facade.md` | `host.native` / `SL.native` device-capability facade |
|
|
99
|
+
| **Product Facets** | `docs/PRODUCT_FACETS_SDK.md` | Facet API reference (admin + public endpoints, types) |
|
|
100
|
+
| **AI Tools & Skills** | `docs/ai-tools-and-skills.md` | Platform AI capability registry (web research, extraction, images) |
|
|
101
|
+
| **Proof Comms Triggers** | `docs/proof-comms-triggers.md` | Transactional comms fired as a side-effect of proof actions |
|
|
90
102
|
|
|
91
103
|
---
|
|
92
104
|
|
|
@@ -97,6 +109,15 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
|
|
|
97
109
|
- **Context via URL params** — All contextual data is passed through URL parameters
|
|
98
110
|
- **SmartLinks NPM module** — All data access and platform interaction goes through `@proveanything/smartlinks`
|
|
99
111
|
- **No standalone auth** — Authentication is handled by the parent SmartLinks platform
|
|
112
|
+
|
|
113
|
+
> **Use the host-provided `SL`, don't instantiate your own.** When your app runs in a
|
|
114
|
+
> container/iframe, the host injects an already-initialized, authenticated SDK instance (the
|
|
115
|
+
> `SL` prop / the externalized `@proveanything/smartlinks` singleton). Import and use *that*.
|
|
116
|
+
> Calling `initializeApi()` yourself to spin up a **second** SDK instance is a common source
|
|
117
|
+
> of bugs: it won't share the host's auth (so it looks signed-out), won't share the host's
|
|
118
|
+
> cache (duplicate requests, stale data), and in proxy mode it bypasses the host's request
|
|
119
|
+
> routing. Rule of thumb: **host-provided `SL` for anything the platform owns** (auth, config,
|
|
120
|
+
> data, cache); only app-owned state is yours.
|
|
100
121
|
- **Multi-page build** — Separate bundles for public and admin — see `docs/mpa.md`
|
|
101
122
|
- **Embedded back navigation** — Use `docs/portal-back-button.md` when a sub-app has a real content hierarchy
|
|
102
123
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Portal Back Button — "Up" Navigation Inside Sub-Apps
|
|
2
2
|
|
|
3
|
-
> **For sub-app authors.**
|
|
4
|
-
>
|
|
5
|
-
> microapp authors discover it.
|
|
3
|
+
> **For sub-app authors.** How to cooperate with the portal shell's top-level
|
|
4
|
+
> back/up control. See also [`mpa.md`](mpa.md).
|
|
6
5
|
|
|
7
6
|
When a sub-app is embedded by a portal shell, the shell renders a top-level
|
|
8
7
|
back button. By default that button **exits the app entirely** when tapped —
|