@proveanything/smartlinks 1.9.20 → 1.9.21

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.
@@ -0,0 +1,127 @@
1
+ # SmartLinks UI Utils (`@proveanything/ui-utils`)
2
+
3
+ > Companion module to the SmartLinks SDK. Provides React primitives, hooks, and admin shells for building consistent microapp UIs.
4
+ >
5
+ > Package: `@proveanything/ui-utils`
6
+ > Install: `npm install @proveanything/ui-utils`
7
+
8
+ ---
9
+
10
+ ## What is this module for?
11
+
12
+ `@proveanything/ui-utils` sits on top of `@proveanything/smartlinks`. The core SDK handles data — authentication, records, configurations, interactions. This module handles **UI** — the shared React components, hooks, and shell primitives that translate SDK data into consistent admin and public interfaces.
13
+
14
+ **When do you need it?**
15
+
16
+ - You are building an admin UI for a records-based microapp (see [records-admin-pattern.md](records-admin-pattern.md))
17
+ - You want the standard inheritance/override editor for scoped records
18
+ - You need the `useResolvedRecord` hook on the public widget side
19
+ - You are using `parseRef` / `buildRef` for the standard `ref` convention
20
+
21
+ You do **not** need it for apps that only use `appConfiguration`, basic widgets without scoped data, or executor bundles.
22
+
23
+ ---
24
+
25
+ ## Key exports
26
+
27
+ ### Records admin shell
28
+
29
+ ```tsx
30
+ import { RecordsAdminShell } from '@proveanything/ui-utils';
31
+
32
+ // Drop-in admin interface for any records-based app.
33
+ // Reads the app's manifest `records` block to determine tabs and scopes.
34
+ <RecordsAdminShell
35
+ collectionId={collectionId}
36
+ appId={appId}
37
+ recordType="nutrition"
38
+ renderForm={(record, parentRecord) => <NutritionForm record={record} parent={parentRecord} />}
39
+ />
40
+ ```
41
+
42
+ `RecordsAdminShell` handles: left-rail product/scope browsing, variant/batch tab discovery, the "New record" button, inheritance markers, bulk operations, CSV import/export, and telemetry events. Your app only provides the domain form.
43
+
44
+ ### Record editor primitive
45
+
46
+ ```tsx
47
+ import { RecordEditor } from '@proveanything/ui-utils';
48
+
49
+ // Lower-level editor with inheritance diffing. Use when you need
50
+ // custom layout but still want the inherited/override markers.
51
+ <RecordEditor
52
+ record={variantRecord}
53
+ parentRecord={productRecord}
54
+ fields={nutritionFields}
55
+ onSave={(data) => app.records.update(collectionId, appId, record.id, { data }, true)}
56
+ />
57
+ ```
58
+
59
+ ### `useResolvedRecord` hook
60
+
61
+ ```ts
62
+ import { useResolvedRecord } from '@proveanything/ui-utils/records';
63
+
64
+ const { data, source, isLoading } = useResolvedRecord({
65
+ appId,
66
+ recordType: 'nutrition',
67
+ collectionId,
68
+ productId,
69
+ variantId, // optional
70
+ batchId, // optional
71
+ proofId, // optional
72
+ });
73
+ // source: 'proof' | 'batch' | 'variant' | 'product' | 'facet' | 'default' | null
74
+ ```
75
+
76
+ Walks the resolution chain defined in [records-admin-pattern.md §4](records-admin-pattern.md#4-resolution-order-required) and returns the first matching record plus its source scope.
77
+
78
+ ### `resolveRecord` function
79
+
80
+ ```ts
81
+ import { resolveRecord } from '@proveanything/ui-utils/records';
82
+
83
+ // Async, non-hook version for executors and server-side logic.
84
+ const resolved = await resolveRecord({
85
+ appId,
86
+ recordType: 'nutrition',
87
+ scope: { collectionId, productId, variantId, batchId, proofId },
88
+ supportedScopes: ['product', 'facet'], // optional — skip unsupported scopes
89
+ });
90
+ // → { record: AppRecord, source: string } | null
91
+ ```
92
+
93
+ ### `parseRef` / `buildRef` utilities
94
+
95
+ ```ts
96
+ import { parseRef, buildRef } from '@proveanything/ui-utils/records';
97
+
98
+ const parsed = parseRef('variant:prod_abc:var_500ml');
99
+ // → { kind: 'variant', productId: 'prod_abc', variantId: 'var_500ml' }
100
+
101
+ const ref = buildRef({ kind: 'facet', facetKey: 'bread_type', valueKey: 'sourdough' });
102
+ // → 'facet:bread_type:sourdough'
103
+ ```
104
+
105
+ See [records-admin-pattern.md Appendix A](records-admin-pattern.md#appendix-a--ref-parser-reference) for the full `ParsedRef` type and standalone implementation.
106
+
107
+ ---
108
+
109
+ ## Relationship to the core SDK
110
+
111
+ ```
112
+ @proveanything/smartlinks ← data layer (records, config, interactions, …)
113
+
114
+ @proveanything/ui-utils ← UI layer (components, hooks, admin shells)
115
+
116
+ your microapp ← domain logic and custom forms
117
+ ```
118
+
119
+ `ui-utils` imports from `@proveanything/smartlinks` internally. You should install both packages, but only import directly from each for their respective concerns — don't reach into `ui-utils` for SDK data primitives.
120
+
121
+ ---
122
+
123
+ ## Further reading
124
+
125
+ - [records-admin-pattern.md](records-admin-pattern.md) — the data contract that `RecordsAdminShell` implements
126
+ - [building-react-components.md](building-react-components.md) — dual-mode rendering rules that apply to all ui-utils components
127
+ - [app-manifest.md](app-manifest.md) — the `records` manifest block that drives tab generation
package/dist/openapi.yaml CHANGED
@@ -17134,6 +17134,10 @@ components:
17134
17134
  type: string
17135
17135
  allowAutoGenerateClaims:
17136
17136
  type: boolean
17137
+ variants:
17138
+ type: boolean
17139
+ batches:
17140
+ type: boolean
17137
17141
  defaultAuthKitId:
17138
17142
  type: string
17139
17143
  required:
@@ -17159,6 +17163,8 @@ components:
17159
17163
  - supported
17160
17164
  - roles
17161
17165
  - shortId
17166
+ - variants
17167
+ - batches
17162
17168
  - defaultAuthKitId
17163
17169
  AppConfig:
17164
17170
  type: object
@@ -69,6 +69,8 @@ export interface Collection {
69
69
  portalUrl?: string;
70
70
  /** Allow users to claim products without providing a proof ID (auto-generates serial on-demand) */
71
71
  allowAutoGenerateClaims?: boolean;
72
+ variants: boolean;
73
+ batches: boolean;
72
74
  defaultAuthKitId: string;
73
75
  }
74
76
  export type CollectionResponse = Collection;
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.9.20 | Generated: 2026-04-17T10:13:30.466Z
3
+ Version: 1.9.21 | Generated: 2026-04-25T10:48:10.932Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -2994,6 +2994,8 @@ interface Collection {
2994
2994
  secondaryColor?: string
2995
2995
  portalUrl?: string // URL for the collection's portal (if applicable)
2996
2996
  allowAutoGenerateClaims?: boolean
2997
+ variants: boolean // does this collection support variants?
2998
+ batches: boolean // does this collection support batches?
2997
2999
  defaultAuthKitId: string // default auth kit for this collection, used for auth
2998
3000
  }
2999
3001
  ```
@@ -95,7 +95,20 @@ The manifest is loaded automatically by the platform for every collection page.
95
95
  { "title": "Home", "path": "/" },
96
96
  { "title": "Gallery", "path": "/gallery" },
97
97
  { "title": "Settings", "path": "/settings", "params": { "tab": "advanced" } }
98
- ]
98
+ ],
99
+
100
+ "records": {
101
+ "nutrition": {
102
+ "scopes": ["product", "facet", "batch"],
103
+ "defaultScope": "facet",
104
+ "label": "Nutrition info"
105
+ },
106
+ "cooking_steps": {
107
+ "scopes": ["product", "facet"],
108
+ "defaultScope": "product",
109
+ "label": "Cooking steps"
110
+ }
111
+ }
99
112
  }
100
113
  ```
101
114
 
@@ -188,6 +201,30 @@ See the [Deep Link Discovery guide](deep-link-discovery.md) for the full dual-so
188
201
  | `path` | string | ❌ | Hash route within the app (defaults to `"/"` if omitted) |
189
202
  | `params` | object | ❌ | App-specific query params appended to the URL — do **not** include platform params (`collectionId`, `productId`, etc.) |
190
203
 
204
+ #### `records`
205
+
206
+ Declares which `app.records` record types the app stores, and which scopes each type supports. Required for any app that follows the [Records-Based Admin Pattern](records-admin-pattern.md). Omit if the app does not use scoped records.
207
+
208
+ The platform and the `<RecordsAdminShell>` from `@proveanything/ui-utils` read this block to render only the relevant tabs and affordances.
209
+
210
+ ```json
211
+ "records": {
212
+ "<recordType>": {
213
+ "scopes": ["product", "facet", "batch"],
214
+ "defaultScope": "facet",
215
+ "label": "Human-readable label"
216
+ }
217
+ }
218
+ ```
219
+
220
+ | Field | Type | Required | Description |
221
+ |----------------|----------|----------|-------------|
222
+ | `scopes` | string[] | ✅ | Allowed scope kinds in resolution order. Valid values: `"product"`, `"variant"`, `"batch"`, `"facet"`, `"proof"`, `"default"`. |
223
+ | `defaultScope` | string | ✅ | The scope the "Create new" button targets in the admin shell. Must be one of the declared `scopes`. |
224
+ | `label` | string | ✅ | Human-readable label for the record type, used in headings and tabs. |
225
+
226
+ An app may declare multiple record types under different keys (e.g. `"nutrition"` and `"cooking_steps"`).
227
+
191
228
  #### `executor`
192
229
 
193
230
  Declares the executor bundle — a standalone JS library for programmatic configuration, server-side SEO, and LLM content generation. Omit if the app has no executor.
@@ -0,0 +1,162 @@
1
+ # SmartLinks Auth Kit (`@proveanything/smartlinks` — `authKit` namespace)
2
+
3
+ > End-user authentication flows for SmartLinks microapps. Covers email/password, magic links, phone OTP, Google OAuth, profile management, and password/email change flows.
4
+ >
5
+ > **This is part of the core SDK** — no separate install required. Import from `@proveanything/smartlinks`.
6
+
7
+ ---
8
+
9
+ ## What is Auth Kit for?
10
+
11
+ Auth Kit is the **end-user identity layer** for microapps that need users to sign in. It is distinct from the admin/platform authentication (Bearer tokens) used to call admin endpoints.
12
+
13
+ Use Auth Kit when:
14
+ - Your app has a login/register screen for end users (not collection admins)
15
+ - You need to gate features behind a verified user identity
16
+ - You want to store user-specific data with the `userAppData` API (see [app-data-storage.md](app-data-storage.md))
17
+
18
+ Do **not** use Auth Kit for:
19
+ - Admin-side API calls (those use Bearer tokens set by the platform shell)
20
+ - Claiming proofs (see proof claiming methods)
21
+
22
+ ---
23
+
24
+ ## Setup: creating an Auth Kit client
25
+
26
+ Each app requires an Auth Kit configuration, created by a collection admin:
27
+
28
+ ```ts
29
+ // Admin setup (one-time, done in admin console)
30
+ import { authKit } from '@proveanything/smartlinks';
31
+
32
+ // Returns an authKitId to store in your app config
33
+ const config = await authKit.create(collectionId, {
34
+ name: 'My App Auth',
35
+ loginMethods: ['email', 'google', 'magic_link'],
36
+ redirectUrl: 'https://myapp.example.com/auth/callback',
37
+ });
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Key flows
43
+
44
+ ### Email / password
45
+
46
+ ```ts
47
+ import { authKit } from '@proveanything/smartlinks';
48
+
49
+ // Register
50
+ const session = await authKit.register(clientId, {
51
+ email: 'user@example.com',
52
+ password: 'securePassword123',
53
+ displayName: 'Alice',
54
+ });
55
+
56
+ // Login
57
+ const session = await authKit.login(clientId, 'user@example.com', 'securePassword123');
58
+
59
+ // session.token — store this; pass to initializeApi for subsequent calls
60
+ ```
61
+
62
+ ### Magic link (passwordless email)
63
+
64
+ ```ts
65
+ await authKit.sendMagicLink(clientId, {
66
+ email: 'user@example.com',
67
+ redirectUrl: 'https://myapp.example.com/auth/verify',
68
+ });
69
+
70
+ // On callback page, extract token from URL and verify
71
+ const session = await authKit.verifyMagicLink(clientId, tokenFromUrl);
72
+ ```
73
+
74
+ ### Phone OTP
75
+
76
+ ```ts
77
+ await authKit.sendPhoneCode(clientId, '+61400000000');
78
+ const session = await authKit.verifyPhoneCode(clientId, '+61400000000', '123456');
79
+ ```
80
+
81
+ ### Google OAuth
82
+
83
+ ```ts
84
+ // After Google sign-in, pass the id_token to Auth Kit
85
+ const session = await authKit.googleLogin(clientId, googleIdToken);
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Profile management
91
+
92
+ ```ts
93
+ import { authKit } from '@proveanything/smartlinks';
94
+
95
+ // Get current user's profile
96
+ const profile = await authKit.getProfile(clientId);
97
+
98
+ // Update profile
99
+ await authKit.updateProfile(clientId, { displayName: 'Alice B.', avatarUrl: '...' });
100
+
101
+ // Change password
102
+ await authKit.changePassword(clientId, 'currentPass', 'newPass');
103
+
104
+ // Change email (triggers verification)
105
+ await authKit.changeEmail(clientId, 'newemail@example.com', 'password', redirectUrl);
106
+
107
+ // Delete account
108
+ await authKit.deleteAccount(clientId, 'password', 'DELETE');
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Email verification
114
+
115
+ Auth Kit can send and verify email addresses after registration:
116
+
117
+ ```ts
118
+ await authKit.sendEmailVerification(clientId, {
119
+ userId,
120
+ email: 'user@example.com',
121
+ redirectUrl: 'https://myapp.example.com/auth/verified',
122
+ });
123
+
124
+ // On callback page
125
+ await authKit.verifyEmail(clientId, tokenFromUrl);
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Password reset
131
+
132
+ ```ts
133
+ await authKit.requestPasswordReset(clientId, {
134
+ email: 'user@example.com',
135
+ redirectUrl: 'https://myapp.example.com/auth/reset',
136
+ clientName: 'My App',
137
+ });
138
+
139
+ // On reset page — verify token is still valid before showing the form
140
+ await authKit.verifyResetToken(clientId, tokenFromUrl);
141
+
142
+ // Complete reset
143
+ await authKit.completePasswordReset(clientId, tokenFromUrl, 'newSecurePassword');
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Relationship to other parts of the SDK
149
+
150
+ | Concern | Where it lives |
151
+ |---------|---------------|
152
+ | End-user sign-in / register | `authKit` namespace (this doc) |
153
+ | Admin Bearer token auth | Platform shell — not set by your app |
154
+ | Per-user data storage | `userAppData` namespace — see [app-data-storage.md](app-data-storage.md) |
155
+ | User identity in analytics | `userId` field on `analytics` and `interactions` calls |
156
+
157
+ ---
158
+
159
+ ## Further reading
160
+
161
+ - [app-data-storage.md](app-data-storage.md) — storing user-specific data after login
162
+ - [app-manifest.md](app-manifest.md) — `app.admin.json` setup questions for configuring `clientId`
package/docs/forms.md ADDED
@@ -0,0 +1,113 @@
1
+ # SmartLinks Forms
2
+
3
+ > Platform-managed form definitions and submissions. Covers the `form` data API (core SDK) and the `@proveanything/ui-forms` React package for schema-driven form UIs.
4
+
5
+ ---
6
+
7
+ ## Two layers
8
+
9
+ | Layer | Package | What it does |
10
+ |-------|---------|--------------|
11
+ | **Forms data API** | `@proveanything/smartlinks` (`form` namespace) | CRUD for form definitions; stores submission schema |
12
+ | **Forms UI** | `@proveanything/ui-forms` | React components that render a form definition, validate input, and submit |
13
+
14
+ You can use the data API without the UI package (e.g. in an executor), but the UI package requires both.
15
+
16
+ ---
17
+
18
+ ## Forms data API (`form` namespace)
19
+
20
+ The `form` namespace in the core SDK manages **form definitions** — the schema that describes fields, validation rules, and submission behaviour. This is an admin-side API.
21
+
22
+ ```ts
23
+ import { form } from '@proveanything/smartlinks';
24
+
25
+ // List all forms in a collection
26
+ const forms = await form.list(collectionId, /* admin= */ true);
27
+
28
+ // Get a specific form
29
+ const myForm = await form.get(collectionId, formId);
30
+
31
+ // Create a form (admin)
32
+ const created = await form.create(collectionId, {
33
+ name: 'Warranty Registration',
34
+ fields: [
35
+ { id: 'name', type: 'text', label: 'Full name', required: true },
36
+ { id: 'email', type: 'email', label: 'Email address', required: true },
37
+ { id: 'serial', type: 'text', label: 'Serial number', required: true },
38
+ { id: 'receipt', type: 'file', label: 'Proof of purchase' },
39
+ ],
40
+ });
41
+
42
+ // Update a form (admin)
43
+ await form.update(collectionId, formId, { name: 'Warranty Registration v2' });
44
+
45
+ // Delete a form (admin)
46
+ await form.remove(collectionId, formId);
47
+ ```
48
+
49
+ Form definitions are stored per-collection and referenced by `formId`.
50
+
51
+ ---
52
+
53
+ ## Forms UI (`@proveanything/ui-forms`)
54
+
55
+ `@proveanything/ui-forms` provides React components that consume a form definition from the API and render a complete, accessible, validated form.
56
+
57
+ Install: `npm install @proveanything/ui-forms`
58
+
59
+ ### Rendering a form
60
+
61
+ ```tsx
62
+ import { SmartForm } from '@proveanything/ui-forms';
63
+
64
+ // Fetches the form definition and renders it
65
+ <SmartForm
66
+ collectionId={collectionId}
67
+ formId={formId}
68
+ onSubmit={async (values) => {
69
+ // handle submission — e.g. create an app.records entry or send via comms
70
+ }}
71
+ />
72
+ ```
73
+
74
+ ### Headless usage
75
+
76
+ ```tsx
77
+ import { useFormDefinition, FormRenderer } from '@proveanything/ui-forms';
78
+
79
+ const { fields, isLoading } = useFormDefinition(collectionId, formId);
80
+
81
+ // Render fields yourself using the definition
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Common patterns
87
+
88
+ ### Warranty / registration forms
89
+
90
+ Define the form in `app.admin.json` setup or via the admin API. On the public widget, render with `<SmartForm>` and on submit create an `app.records` entry:
91
+
92
+ ```ts
93
+ onSubmit={async (values) => {
94
+ await app.records.create(collectionId, appId, {
95
+ recordType: 'warranty_registration',
96
+ ref: `proof:${proofId}`,
97
+ data: values,
98
+ });
99
+ }}
100
+ ```
101
+
102
+ ### Competition / feedback forms
103
+
104
+ Same pattern — use `<SmartForm>` for capture and write to `app.records` or trigger an `interactions.appendEvent` on submit.
105
+
106
+ ---
107
+
108
+ ## Further reading
109
+
110
+ - [app-objects.md](app-objects.md) — `app.records` for storing submissions
111
+ - [app-data-storage.md](app-data-storage.md) — choosing the right storage model
112
+ - [interactions.md](interactions.md) — firing events on form submission
113
+ - [comms.md](comms.md) — sending confirmation emails after submission
package/docs/overview.md CHANGED
@@ -64,6 +64,10 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
64
64
  | **Liquid Templates** | `docs/liquid-templates.md` | Dynamic content rendering with LiquidJS |
65
65
  | **Iframe Responder** | `docs/iframe-responder.md` | Iframe communication and proxy mode internals |
66
66
  | **AI Guide Template** | `docs/ai-guide-template.md` | Template for creating `public/ai-guide.md` — customise per app |
67
+ | **Forms** | `docs/forms.md` | Form definitions, schema-driven rendering, submission patterns |
68
+ | **Auth Kit** | `docs/auth-kit.md` | End-user sign-in: email/password, magic links, phone OTP, Google OAuth |
69
+ | **Records Admin Pattern** | `docs/records-admin-pattern.md` | Standard pattern for per-product/facet/variant/batch admin UIs |
70
+ | **UI Utils** | `docs/ui-utils.md` | `@proveanything/ui-utils` — React shells, hooks, and primitives for records-based apps |
67
71
 
68
72
  ---
69
73