@proveanything/smartlinks 1.13.1 → 1.13.3
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/asset.js +7 -7
- package/dist/docs/API_SUMMARY.md +11 -1
- package/dist/docs/assets.md +8 -8
- package/dist/docs/deep-link-discovery.md +74 -0
- package/dist/openapi.yaml +7 -4
- package/dist/types/appManifest.d.ts +12 -0
- package/docs/API_SUMMARY.md +11 -1
- package/docs/assets.md +8 -8
- package/docs/deep-link-discovery.md +74 -0
- package/openapi.yaml +7 -4
- package/package.json +1 -1
package/dist/api/asset.js
CHANGED
|
@@ -316,7 +316,7 @@ export var asset;
|
|
|
316
316
|
if (typeof options.offset === 'number')
|
|
317
317
|
params.set('offset', String(options.offset));
|
|
318
318
|
const qs = params.toString();
|
|
319
|
-
const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/
|
|
319
|
+
const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/asset${qs ? `?${qs}` : ''}`;
|
|
320
320
|
return request(path);
|
|
321
321
|
}
|
|
322
322
|
asset.listAdmin = listAdmin;
|
|
@@ -324,7 +324,7 @@ export var asset;
|
|
|
324
324
|
* Get a single asset by ID (admin).
|
|
325
325
|
*/
|
|
326
326
|
async function getAdmin(collectionId, assetId) {
|
|
327
|
-
const path = `/admin/collection/${encodeURIComponent(collectionId)}/
|
|
327
|
+
const path = `/admin/collection/${encodeURIComponent(collectionId)}/asset/${encodeURIComponent(assetId)}`;
|
|
328
328
|
return request(path);
|
|
329
329
|
}
|
|
330
330
|
asset.getAdmin = getAdmin;
|
|
@@ -332,7 +332,7 @@ export var asset;
|
|
|
332
332
|
* Update asset metadata (admin). Use `replaceFile` to swap the file.
|
|
333
333
|
*/
|
|
334
334
|
async function updateAdmin(options) {
|
|
335
|
-
const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/
|
|
335
|
+
const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/asset/${encodeURIComponent(options.assetId)}`;
|
|
336
336
|
const { collectionId: _c, assetId: _a } = options, body = __rest(options, ["collectionId", "assetId"]);
|
|
337
337
|
return put(path, body);
|
|
338
338
|
}
|
|
@@ -342,7 +342,7 @@ export var asset;
|
|
|
342
342
|
* into `versions[]` on the asset.
|
|
343
343
|
*/
|
|
344
344
|
async function replaceFile(options) {
|
|
345
|
-
const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/
|
|
345
|
+
const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/asset/${encodeURIComponent(options.assetId)}/replace`;
|
|
346
346
|
const formData = new FormData();
|
|
347
347
|
formData.append('file', options.file);
|
|
348
348
|
if (options.onProgress && typeof window !== 'undefined' && !isProxyEnabled()) {
|
|
@@ -395,7 +395,7 @@ export var asset;
|
|
|
395
395
|
if (typeof options.graceDays === 'number')
|
|
396
396
|
params.set('graceDays', String(options.graceDays));
|
|
397
397
|
const qs = params.toString();
|
|
398
|
-
const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/
|
|
398
|
+
const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/asset/${encodeURIComponent(options.assetId)}${qs ? `?${qs}` : ''}`;
|
|
399
399
|
return del(path);
|
|
400
400
|
}
|
|
401
401
|
asset.deleteAdmin = deleteAdmin;
|
|
@@ -403,7 +403,7 @@ export var asset;
|
|
|
403
403
|
* Restore a soft-deleted asset (clears `deletedAt`).
|
|
404
404
|
*/
|
|
405
405
|
async function restoreAdmin(collectionId, assetId) {
|
|
406
|
-
const path = `/admin/collection/${encodeURIComponent(collectionId)}/
|
|
406
|
+
const path = `/admin/collection/${encodeURIComponent(collectionId)}/asset/${encodeURIComponent(assetId)}/restore`;
|
|
407
407
|
return post(path, {});
|
|
408
408
|
}
|
|
409
409
|
asset.restoreAdmin = restoreAdmin;
|
|
@@ -411,7 +411,7 @@ export var asset;
|
|
|
411
411
|
* Soft-delete multiple assets in one request.
|
|
412
412
|
*/
|
|
413
413
|
async function bulkDelete(options) {
|
|
414
|
-
const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/
|
|
414
|
+
const path = `/admin/collection/${encodeURIComponent(options.collectionId)}/asset/bulk-delete`;
|
|
415
415
|
const body = { assetIds: options.assetIds };
|
|
416
416
|
if (typeof options.graceDays === 'number')
|
|
417
417
|
body.graceDays = options.graceDays;
|
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.13.
|
|
3
|
+
Version: 1.13.3 | Generated: 2026-05-08T10:30:25.776Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -1439,6 +1439,16 @@ interface DeepLinkEntry {
|
|
|
1439
1439
|
* Do NOT include platform context params (collectionId, appId, productId, etc.) —
|
|
1440
1440
|
* those are injected by the platform automatically.
|
|
1441
1441
|
params?: Record<string, string>;
|
|
1442
|
+
* When `true`, this entry is also available as a dynamic data context for widgets
|
|
1443
|
+
* (in addition to being a navigable page / container route).
|
|
1444
|
+
*
|
|
1445
|
+
* Entries with `widget: true` appear in the widget config picker so an admin can
|
|
1446
|
+
* select this dataset to drive how the widget renders. The widget receives `params`
|
|
1447
|
+
* and decides its own presentation — no separate rendering contract is required.
|
|
1448
|
+
*
|
|
1449
|
+
* Omit (or `false`) for entries that are only meaningful as full-page navigation
|
|
1450
|
+
* (e.g. multi-step forms, settings pages, checkout flows).
|
|
1451
|
+
widget?: true;
|
|
1442
1452
|
}
|
|
1443
1453
|
```
|
|
1444
1454
|
|
package/dist/docs/assets.md
CHANGED
|
@@ -103,7 +103,7 @@ All admin endpoints require authentication. Base path: `/api/admin/collection/:c
|
|
|
103
103
|
### List assets
|
|
104
104
|
|
|
105
105
|
```
|
|
106
|
-
GET /
|
|
106
|
+
GET /asset
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
| Parameter | Type | Description |
|
|
@@ -140,7 +140,7 @@ const { data, total } = await Api.asset.listAdmin({
|
|
|
140
140
|
### Get asset
|
|
141
141
|
|
|
142
142
|
```
|
|
143
|
-
GET /
|
|
143
|
+
GET /asset/:assetId
|
|
144
144
|
```
|
|
145
145
|
|
|
146
146
|
Returns `Asset` or `404`.
|
|
@@ -152,7 +152,7 @@ Returns `Asset` or `404`.
|
|
|
152
152
|
### Upload asset
|
|
153
153
|
|
|
154
154
|
```
|
|
155
|
-
POST /
|
|
155
|
+
POST /asset
|
|
156
156
|
```
|
|
157
157
|
|
|
158
158
|
Use the existing `Api.asset.upload()` method (file) or `Api.asset.uploadFromUrl()` (URL import).
|
|
@@ -162,7 +162,7 @@ Use the existing `Api.asset.upload()` method (file) or `Api.asset.uploadFromUrl(
|
|
|
162
162
|
### Update asset metadata
|
|
163
163
|
|
|
164
164
|
```
|
|
165
|
-
PUT /
|
|
165
|
+
PUT /asset/:assetId
|
|
166
166
|
```
|
|
167
167
|
|
|
168
168
|
Updates metadata only. Use `/replace` to swap the file.
|
|
@@ -182,7 +182,7 @@ await Api.asset.updateAdmin({
|
|
|
182
182
|
### Replace file
|
|
183
183
|
|
|
184
184
|
```
|
|
185
|
-
POST /
|
|
185
|
+
POST /asset/:assetId/replace
|
|
186
186
|
```
|
|
187
187
|
|
|
188
188
|
Replaces the file; the previous URL is snapshotted into `versions[]`.
|
|
@@ -201,7 +201,7 @@ await Api.asset.replaceFile({
|
|
|
201
201
|
### Delete asset (soft)
|
|
202
202
|
|
|
203
203
|
```
|
|
204
|
-
DELETE /
|
|
204
|
+
DELETE /asset/:assetId?graceDays=30
|
|
205
205
|
```
|
|
206
206
|
|
|
207
207
|
Sets `deletedAt` and schedules CDN purge after `graceDays` (default 30). Recoverable until purge.
|
|
@@ -215,7 +215,7 @@ await Api.asset.deleteAdmin({ collectionId: 'my-collection', assetId: 'abc123',
|
|
|
215
215
|
### Restore asset
|
|
216
216
|
|
|
217
217
|
```
|
|
218
|
-
POST /
|
|
218
|
+
POST /asset/:assetId/restore
|
|
219
219
|
```
|
|
220
220
|
|
|
221
221
|
Clears `deletedAt`. Asset becomes active again.
|
|
@@ -229,7 +229,7 @@ await Api.asset.restoreAdmin('my-collection', 'abc123')
|
|
|
229
229
|
### Bulk delete
|
|
230
230
|
|
|
231
231
|
```
|
|
232
|
-
POST /
|
|
232
|
+
POST /asset/bulk-delete
|
|
233
233
|
```
|
|
234
234
|
|
|
235
235
|
```typescript
|
|
@@ -21,6 +21,7 @@ This doc covers both sides of the contract: **suppliers** declare their navigabl
|
|
|
21
21
|
- [Portal Navigation Menu](#portal-navigation-menu)
|
|
22
22
|
- [AI Orchestration](#ai-orchestration)
|
|
23
23
|
- [Cross-App Navigation](#cross-app-navigation)
|
|
24
|
+
- [Widget Config Picker — Dynamic Data Context](#widget-config-picker--dynamic-data-context)
|
|
24
25
|
- [Link Picker — Admin Configuration](#link-picker--admin-configuration)
|
|
25
26
|
- [Rendering Links at Runtime](#rendering-links-at-runtime)
|
|
26
27
|
- [TypeScript Types](#typescript-types)
|
|
@@ -191,6 +192,18 @@ interface DeepLinkEntry {
|
|
|
191
192
|
* are injected automatically — do NOT include them here.
|
|
192
193
|
*/
|
|
193
194
|
params?: Record<string, string>;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* When `true`, this entry is also available as a dynamic data context for widgets.
|
|
198
|
+
*
|
|
199
|
+
* Entries with `widget: true` appear in the widget config picker so an admin can
|
|
200
|
+
* select this dataset to drive how the widget renders. The widget receives `params`
|
|
201
|
+
* and decides its own presentation — no additional contract is required.
|
|
202
|
+
*
|
|
203
|
+
* Omit for entries that are only meaningful as full-page navigation
|
|
204
|
+
* (e.g. multi-step forms, settings pages, checkout flows).
|
|
205
|
+
*/
|
|
206
|
+
widget?: true;
|
|
194
207
|
}
|
|
195
208
|
```
|
|
196
209
|
|
|
@@ -199,9 +212,12 @@ interface DeepLinkEntry {
|
|
|
199
212
|
| `title` | ✅ | Displayed in menus and offered to AI agents. Should be concise and human-readable. |
|
|
200
213
|
| `path` | ❌ | Hash route within the app. Defaults to `/` if omitted. |
|
|
201
214
|
| `params` | ❌ | App-specific query params only. Platform params are injected automatically. |
|
|
215
|
+
| `widget` | ❌ | Set to `true` to make this entry selectable in the widget config picker as a dynamic data context. |
|
|
202
216
|
|
|
203
217
|
> **Tip:** Different entries can share the same `path` if they differ by `params` — for example, two entries pointing at `/settings` with different `tab` params.
|
|
204
218
|
|
|
219
|
+
> **Container vs widget:** All entries are navigable via a container (full-page render). `widget: true` additionally opts the entry into the widget picker — use it for datasets that make sense as a compact card, e.g. a specific gallery view or a named product showcase. Don't set it on management/settings pages.
|
|
220
|
+
|
|
205
221
|
---
|
|
206
222
|
|
|
207
223
|
## Quick Start
|
|
@@ -500,6 +516,58 @@ if (entry) {
|
|
|
500
516
|
|
|
501
517
|
---
|
|
502
518
|
|
|
519
|
+
### Widget Config Picker — Dynamic Data Context
|
|
520
|
+
|
|
521
|
+
When a widget's admin settings include a "which view/dataset to show" picker, use the `widget: true` entries from `appConfig.linkable` as the source. Filter to entries with `widget: true` across all installed apps (or just the widget's own app) and let the admin select one. Persist only the `params` — the title is ephemeral display.
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
// Fetch linkable entries for a specific app and filter to widget-eligible ones
|
|
525
|
+
const config = await SL.appConfiguration.getConfig({ collectionId, appId: 'media-gallery' });
|
|
526
|
+
const widgetViews = (config?.linkable ?? []).filter(e => e.widget === true);
|
|
527
|
+
|
|
528
|
+
// widgetViews might be:
|
|
529
|
+
// [
|
|
530
|
+
// { title: "Gallery: Summer 2026", params: { viewId: "sum26" }, widget: true },
|
|
531
|
+
// { title: "Gallery: Archive", params: { viewId: "archive" }, widget: true },
|
|
532
|
+
// ]
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
```tsx
|
|
536
|
+
// Widget admin settings — view picker
|
|
537
|
+
<select
|
|
538
|
+
value={JSON.stringify(settings.viewParams ?? {})}
|
|
539
|
+
onChange={e => saveSettings({ ...settings, viewParams: JSON.parse(e.target.value) })}
|
|
540
|
+
>
|
|
541
|
+
<option value="{}">— Default view —</option>
|
|
542
|
+
{widgetViews.map(entry => (
|
|
543
|
+
<option key={entry.title} value={JSON.stringify(entry.params)}>
|
|
544
|
+
{entry.title}
|
|
545
|
+
</option>
|
|
546
|
+
))}
|
|
547
|
+
</select>
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
The widget then reads `settings.viewParams` at render time and uses it to fetch or filter its data. The widget owns all rendering decisions — `params` is just a key.
|
|
551
|
+
|
|
552
|
+
**Supply side** — when the media gallery creates or renames a gallery view, it syncs `appConfig.linkable` as usual, marking card-appropriate views with `widget: true`:
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
const linkable = galleryViews.map(view => ({
|
|
556
|
+
title: view.name,
|
|
557
|
+
params: { viewId: view.id },
|
|
558
|
+
widget: view.isCardReady ? true : undefined, // only opt in views that make sense as cards
|
|
559
|
+
}));
|
|
560
|
+
|
|
561
|
+
await SL.appConfiguration.setConfig({
|
|
562
|
+
collectionId, appId: 'media-gallery', admin: true,
|
|
563
|
+
config: { ...current, linkable },
|
|
564
|
+
});
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
> **Rule of thumb:** set `widget: true` on dataset entries (gallery views, product showcases, named feeds). Leave it off on management/utility pages (settings, upload config, multi-step forms).
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
503
571
|
### Link Picker — Admin Configuration
|
|
504
572
|
|
|
505
573
|
When an admin needs to configure any outbound link — to another installed app, a specific page within an app, or an external URL — **never use a bare text input**. Use `<LinkPicker />` from `@proveanything/smartlinks-utils-ui`. It handles all three `LinkTarget` kinds and produces a single `LinkTarget` value to persist.
|
|
@@ -656,6 +724,12 @@ export interface DeepLinkEntry {
|
|
|
656
724
|
* Do NOT include platform context params (collectionId, appId, etc.).
|
|
657
725
|
*/
|
|
658
726
|
params?: Record<string, string>;
|
|
727
|
+
/**
|
|
728
|
+
* When `true`, this entry is also available as a dynamic data context for widgets
|
|
729
|
+
* (in addition to being a navigable page / container route).
|
|
730
|
+
* Omit for entries that are only meaningful as full-page navigation.
|
|
731
|
+
*/
|
|
732
|
+
widget?: true;
|
|
659
733
|
}
|
|
660
734
|
|
|
661
735
|
/** Convenience alias for an array of DeepLinkEntry */
|
package/dist/openapi.yaml
CHANGED
|
@@ -105,7 +105,7 @@ paths:
|
|
|
105
105
|
application/json:
|
|
106
106
|
schema:
|
|
107
107
|
$ref: "#/components/schemas/CollectionCreateRequest"
|
|
108
|
-
/admin/collection/
|
|
108
|
+
/admin/collection/asset:
|
|
109
109
|
get:
|
|
110
110
|
tags:
|
|
111
111
|
- asset
|
|
@@ -216,7 +216,7 @@ paths:
|
|
|
216
216
|
application/json:
|
|
217
217
|
schema:
|
|
218
218
|
$ref: "#/components/schemas/UpdateAssetOptions"
|
|
219
|
-
/admin/collection/
|
|
219
|
+
/admin/collection/asset/replace:
|
|
220
220
|
post:
|
|
221
221
|
tags:
|
|
222
222
|
- asset
|
|
@@ -1050,7 +1050,7 @@ paths:
|
|
|
1050
1050
|
application/json:
|
|
1051
1051
|
schema:
|
|
1052
1052
|
$ref: "#/components/schemas/ResponsesRequest"
|
|
1053
|
-
/admin/collection/{collectionId}/
|
|
1053
|
+
/admin/collection/{collectionId}/asset/{assetId}:
|
|
1054
1054
|
get:
|
|
1055
1055
|
tags:
|
|
1056
1056
|
- asset
|
|
@@ -1082,7 +1082,7 @@ paths:
|
|
|
1082
1082
|
description: Unauthorized
|
|
1083
1083
|
404:
|
|
1084
1084
|
description: Not found
|
|
1085
|
-
/admin/collection/{collectionId}/
|
|
1085
|
+
/admin/collection/{collectionId}/asset/{assetId}/restore:
|
|
1086
1086
|
post:
|
|
1087
1087
|
tags:
|
|
1088
1088
|
- asset
|
|
@@ -15058,6 +15058,9 @@ components:
|
|
|
15058
15058
|
type: object
|
|
15059
15059
|
additionalProperties:
|
|
15060
15060
|
type: string
|
|
15061
|
+
widget:
|
|
15062
|
+
type: object
|
|
15063
|
+
additionalProperties: true
|
|
15061
15064
|
required:
|
|
15062
15065
|
- title
|
|
15063
15066
|
ExecutorContext:
|
|
@@ -96,6 +96,18 @@ export interface DeepLinkEntry {
|
|
|
96
96
|
* those are injected by the platform automatically.
|
|
97
97
|
*/
|
|
98
98
|
params?: Record<string, string>;
|
|
99
|
+
/**
|
|
100
|
+
* When `true`, this entry is also available as a dynamic data context for widgets
|
|
101
|
+
* (in addition to being a navigable page / container route).
|
|
102
|
+
*
|
|
103
|
+
* Entries with `widget: true` appear in the widget config picker so an admin can
|
|
104
|
+
* select this dataset to drive how the widget renders. The widget receives `params`
|
|
105
|
+
* and decides its own presentation — no separate rendering contract is required.
|
|
106
|
+
*
|
|
107
|
+
* Omit (or `false`) for entries that are only meaningful as full-page navigation
|
|
108
|
+
* (e.g. multi-step forms, settings pages, checkout flows).
|
|
109
|
+
*/
|
|
110
|
+
widget?: true;
|
|
99
111
|
}
|
|
100
112
|
/**
|
|
101
113
|
* Context object passed to every executor factory function.
|
package/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.13.
|
|
3
|
+
Version: 1.13.3 | Generated: 2026-05-08T10:30:25.776Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -1439,6 +1439,16 @@ interface DeepLinkEntry {
|
|
|
1439
1439
|
* Do NOT include platform context params (collectionId, appId, productId, etc.) —
|
|
1440
1440
|
* those are injected by the platform automatically.
|
|
1441
1441
|
params?: Record<string, string>;
|
|
1442
|
+
* When `true`, this entry is also available as a dynamic data context for widgets
|
|
1443
|
+
* (in addition to being a navigable page / container route).
|
|
1444
|
+
*
|
|
1445
|
+
* Entries with `widget: true` appear in the widget config picker so an admin can
|
|
1446
|
+
* select this dataset to drive how the widget renders. The widget receives `params`
|
|
1447
|
+
* and decides its own presentation — no separate rendering contract is required.
|
|
1448
|
+
*
|
|
1449
|
+
* Omit (or `false`) for entries that are only meaningful as full-page navigation
|
|
1450
|
+
* (e.g. multi-step forms, settings pages, checkout flows).
|
|
1451
|
+
widget?: true;
|
|
1442
1452
|
}
|
|
1443
1453
|
```
|
|
1444
1454
|
|
package/docs/assets.md
CHANGED
|
@@ -103,7 +103,7 @@ All admin endpoints require authentication. Base path: `/api/admin/collection/:c
|
|
|
103
103
|
### List assets
|
|
104
104
|
|
|
105
105
|
```
|
|
106
|
-
GET /
|
|
106
|
+
GET /asset
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
| Parameter | Type | Description |
|
|
@@ -140,7 +140,7 @@ const { data, total } = await Api.asset.listAdmin({
|
|
|
140
140
|
### Get asset
|
|
141
141
|
|
|
142
142
|
```
|
|
143
|
-
GET /
|
|
143
|
+
GET /asset/:assetId
|
|
144
144
|
```
|
|
145
145
|
|
|
146
146
|
Returns `Asset` or `404`.
|
|
@@ -152,7 +152,7 @@ Returns `Asset` or `404`.
|
|
|
152
152
|
### Upload asset
|
|
153
153
|
|
|
154
154
|
```
|
|
155
|
-
POST /
|
|
155
|
+
POST /asset
|
|
156
156
|
```
|
|
157
157
|
|
|
158
158
|
Use the existing `Api.asset.upload()` method (file) or `Api.asset.uploadFromUrl()` (URL import).
|
|
@@ -162,7 +162,7 @@ Use the existing `Api.asset.upload()` method (file) or `Api.asset.uploadFromUrl(
|
|
|
162
162
|
### Update asset metadata
|
|
163
163
|
|
|
164
164
|
```
|
|
165
|
-
PUT /
|
|
165
|
+
PUT /asset/:assetId
|
|
166
166
|
```
|
|
167
167
|
|
|
168
168
|
Updates metadata only. Use `/replace` to swap the file.
|
|
@@ -182,7 +182,7 @@ await Api.asset.updateAdmin({
|
|
|
182
182
|
### Replace file
|
|
183
183
|
|
|
184
184
|
```
|
|
185
|
-
POST /
|
|
185
|
+
POST /asset/:assetId/replace
|
|
186
186
|
```
|
|
187
187
|
|
|
188
188
|
Replaces the file; the previous URL is snapshotted into `versions[]`.
|
|
@@ -201,7 +201,7 @@ await Api.asset.replaceFile({
|
|
|
201
201
|
### Delete asset (soft)
|
|
202
202
|
|
|
203
203
|
```
|
|
204
|
-
DELETE /
|
|
204
|
+
DELETE /asset/:assetId?graceDays=30
|
|
205
205
|
```
|
|
206
206
|
|
|
207
207
|
Sets `deletedAt` and schedules CDN purge after `graceDays` (default 30). Recoverable until purge.
|
|
@@ -215,7 +215,7 @@ await Api.asset.deleteAdmin({ collectionId: 'my-collection', assetId: 'abc123',
|
|
|
215
215
|
### Restore asset
|
|
216
216
|
|
|
217
217
|
```
|
|
218
|
-
POST /
|
|
218
|
+
POST /asset/:assetId/restore
|
|
219
219
|
```
|
|
220
220
|
|
|
221
221
|
Clears `deletedAt`. Asset becomes active again.
|
|
@@ -229,7 +229,7 @@ await Api.asset.restoreAdmin('my-collection', 'abc123')
|
|
|
229
229
|
### Bulk delete
|
|
230
230
|
|
|
231
231
|
```
|
|
232
|
-
POST /
|
|
232
|
+
POST /asset/bulk-delete
|
|
233
233
|
```
|
|
234
234
|
|
|
235
235
|
```typescript
|
|
@@ -21,6 +21,7 @@ This doc covers both sides of the contract: **suppliers** declare their navigabl
|
|
|
21
21
|
- [Portal Navigation Menu](#portal-navigation-menu)
|
|
22
22
|
- [AI Orchestration](#ai-orchestration)
|
|
23
23
|
- [Cross-App Navigation](#cross-app-navigation)
|
|
24
|
+
- [Widget Config Picker — Dynamic Data Context](#widget-config-picker--dynamic-data-context)
|
|
24
25
|
- [Link Picker — Admin Configuration](#link-picker--admin-configuration)
|
|
25
26
|
- [Rendering Links at Runtime](#rendering-links-at-runtime)
|
|
26
27
|
- [TypeScript Types](#typescript-types)
|
|
@@ -191,6 +192,18 @@ interface DeepLinkEntry {
|
|
|
191
192
|
* are injected automatically — do NOT include them here.
|
|
192
193
|
*/
|
|
193
194
|
params?: Record<string, string>;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* When `true`, this entry is also available as a dynamic data context for widgets.
|
|
198
|
+
*
|
|
199
|
+
* Entries with `widget: true` appear in the widget config picker so an admin can
|
|
200
|
+
* select this dataset to drive how the widget renders. The widget receives `params`
|
|
201
|
+
* and decides its own presentation — no additional contract is required.
|
|
202
|
+
*
|
|
203
|
+
* Omit for entries that are only meaningful as full-page navigation
|
|
204
|
+
* (e.g. multi-step forms, settings pages, checkout flows).
|
|
205
|
+
*/
|
|
206
|
+
widget?: true;
|
|
194
207
|
}
|
|
195
208
|
```
|
|
196
209
|
|
|
@@ -199,9 +212,12 @@ interface DeepLinkEntry {
|
|
|
199
212
|
| `title` | ✅ | Displayed in menus and offered to AI agents. Should be concise and human-readable. |
|
|
200
213
|
| `path` | ❌ | Hash route within the app. Defaults to `/` if omitted. |
|
|
201
214
|
| `params` | ❌ | App-specific query params only. Platform params are injected automatically. |
|
|
215
|
+
| `widget` | ❌ | Set to `true` to make this entry selectable in the widget config picker as a dynamic data context. |
|
|
202
216
|
|
|
203
217
|
> **Tip:** Different entries can share the same `path` if they differ by `params` — for example, two entries pointing at `/settings` with different `tab` params.
|
|
204
218
|
|
|
219
|
+
> **Container vs widget:** All entries are navigable via a container (full-page render). `widget: true` additionally opts the entry into the widget picker — use it for datasets that make sense as a compact card, e.g. a specific gallery view or a named product showcase. Don't set it on management/settings pages.
|
|
220
|
+
|
|
205
221
|
---
|
|
206
222
|
|
|
207
223
|
## Quick Start
|
|
@@ -500,6 +516,58 @@ if (entry) {
|
|
|
500
516
|
|
|
501
517
|
---
|
|
502
518
|
|
|
519
|
+
### Widget Config Picker — Dynamic Data Context
|
|
520
|
+
|
|
521
|
+
When a widget's admin settings include a "which view/dataset to show" picker, use the `widget: true` entries from `appConfig.linkable` as the source. Filter to entries with `widget: true` across all installed apps (or just the widget's own app) and let the admin select one. Persist only the `params` — the title is ephemeral display.
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
// Fetch linkable entries for a specific app and filter to widget-eligible ones
|
|
525
|
+
const config = await SL.appConfiguration.getConfig({ collectionId, appId: 'media-gallery' });
|
|
526
|
+
const widgetViews = (config?.linkable ?? []).filter(e => e.widget === true);
|
|
527
|
+
|
|
528
|
+
// widgetViews might be:
|
|
529
|
+
// [
|
|
530
|
+
// { title: "Gallery: Summer 2026", params: { viewId: "sum26" }, widget: true },
|
|
531
|
+
// { title: "Gallery: Archive", params: { viewId: "archive" }, widget: true },
|
|
532
|
+
// ]
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
```tsx
|
|
536
|
+
// Widget admin settings — view picker
|
|
537
|
+
<select
|
|
538
|
+
value={JSON.stringify(settings.viewParams ?? {})}
|
|
539
|
+
onChange={e => saveSettings({ ...settings, viewParams: JSON.parse(e.target.value) })}
|
|
540
|
+
>
|
|
541
|
+
<option value="{}">— Default view —</option>
|
|
542
|
+
{widgetViews.map(entry => (
|
|
543
|
+
<option key={entry.title} value={JSON.stringify(entry.params)}>
|
|
544
|
+
{entry.title}
|
|
545
|
+
</option>
|
|
546
|
+
))}
|
|
547
|
+
</select>
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
The widget then reads `settings.viewParams` at render time and uses it to fetch or filter its data. The widget owns all rendering decisions — `params` is just a key.
|
|
551
|
+
|
|
552
|
+
**Supply side** — when the media gallery creates or renames a gallery view, it syncs `appConfig.linkable` as usual, marking card-appropriate views with `widget: true`:
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
const linkable = galleryViews.map(view => ({
|
|
556
|
+
title: view.name,
|
|
557
|
+
params: { viewId: view.id },
|
|
558
|
+
widget: view.isCardReady ? true : undefined, // only opt in views that make sense as cards
|
|
559
|
+
}));
|
|
560
|
+
|
|
561
|
+
await SL.appConfiguration.setConfig({
|
|
562
|
+
collectionId, appId: 'media-gallery', admin: true,
|
|
563
|
+
config: { ...current, linkable },
|
|
564
|
+
});
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
> **Rule of thumb:** set `widget: true` on dataset entries (gallery views, product showcases, named feeds). Leave it off on management/utility pages (settings, upload config, multi-step forms).
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
503
571
|
### Link Picker — Admin Configuration
|
|
504
572
|
|
|
505
573
|
When an admin needs to configure any outbound link — to another installed app, a specific page within an app, or an external URL — **never use a bare text input**. Use `<LinkPicker />` from `@proveanything/smartlinks-utils-ui`. It handles all three `LinkTarget` kinds and produces a single `LinkTarget` value to persist.
|
|
@@ -656,6 +724,12 @@ export interface DeepLinkEntry {
|
|
|
656
724
|
* Do NOT include platform context params (collectionId, appId, etc.).
|
|
657
725
|
*/
|
|
658
726
|
params?: Record<string, string>;
|
|
727
|
+
/**
|
|
728
|
+
* When `true`, this entry is also available as a dynamic data context for widgets
|
|
729
|
+
* (in addition to being a navigable page / container route).
|
|
730
|
+
* Omit for entries that are only meaningful as full-page navigation.
|
|
731
|
+
*/
|
|
732
|
+
widget?: true;
|
|
659
733
|
}
|
|
660
734
|
|
|
661
735
|
/** Convenience alias for an array of DeepLinkEntry */
|
package/openapi.yaml
CHANGED
|
@@ -105,7 +105,7 @@ paths:
|
|
|
105
105
|
application/json:
|
|
106
106
|
schema:
|
|
107
107
|
$ref: "#/components/schemas/CollectionCreateRequest"
|
|
108
|
-
/admin/collection/
|
|
108
|
+
/admin/collection/asset:
|
|
109
109
|
get:
|
|
110
110
|
tags:
|
|
111
111
|
- asset
|
|
@@ -216,7 +216,7 @@ paths:
|
|
|
216
216
|
application/json:
|
|
217
217
|
schema:
|
|
218
218
|
$ref: "#/components/schemas/UpdateAssetOptions"
|
|
219
|
-
/admin/collection/
|
|
219
|
+
/admin/collection/asset/replace:
|
|
220
220
|
post:
|
|
221
221
|
tags:
|
|
222
222
|
- asset
|
|
@@ -1050,7 +1050,7 @@ paths:
|
|
|
1050
1050
|
application/json:
|
|
1051
1051
|
schema:
|
|
1052
1052
|
$ref: "#/components/schemas/ResponsesRequest"
|
|
1053
|
-
/admin/collection/{collectionId}/
|
|
1053
|
+
/admin/collection/{collectionId}/asset/{assetId}:
|
|
1054
1054
|
get:
|
|
1055
1055
|
tags:
|
|
1056
1056
|
- asset
|
|
@@ -1082,7 +1082,7 @@ paths:
|
|
|
1082
1082
|
description: Unauthorized
|
|
1083
1083
|
404:
|
|
1084
1084
|
description: Not found
|
|
1085
|
-
/admin/collection/{collectionId}/
|
|
1085
|
+
/admin/collection/{collectionId}/asset/{assetId}/restore:
|
|
1086
1086
|
post:
|
|
1087
1087
|
tags:
|
|
1088
1088
|
- asset
|
|
@@ -15058,6 +15058,9 @@ components:
|
|
|
15058
15058
|
type: object
|
|
15059
15059
|
additionalProperties:
|
|
15060
15060
|
type: string
|
|
15061
|
+
widget:
|
|
15062
|
+
type: object
|
|
15063
|
+
additionalProperties: true
|
|
15061
15064
|
required:
|
|
15062
15065
|
- title
|
|
15063
15066
|
ExecutorContext:
|