@proveanything/smartlinks 2.0.60 → 2.0.63
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/README.md +1 -0
- package/dist/adminCardPayload.d.ts +9 -0
- package/dist/adminCardPayload.js +61 -0
- package/dist/adminCardsView.d.ts +72 -0
- package/dist/adminCardsView.js +187 -0
- package/dist/api/adminCards.d.ts +13 -0
- package/dist/api/adminCards.js +36 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/integrations.d.ts +14 -1
- package/dist/api/integrations.js +21 -0
- package/dist/docs/API_SUMMARY.md +199 -1
- package/dist/docs/admin-cards.md +187 -0
- package/dist/docs/integrations.md +14 -3
- package/dist/docs/overview.md +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/openapi.yaml +430 -0
- package/dist/types/adminCards.d.ts +180 -0
- package/dist/types/adminCards.js +15 -0
- package/dist/types/appManifest.d.ts +14 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/integrations.d.ts +97 -0
- package/docs/API_SUMMARY.md +199 -1
- package/docs/admin-cards.md +187 -0
- package/docs/integrations.md +14 -3
- package/docs/overview.md +2 -0
- package/openapi.yaml +430 -0
- package/package.json +1 -1
- package/scripts/doctor.mjs +55 -2
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.63 | Generated: 2026-10-14T09:04:47.371Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -135,6 +135,7 @@ The Smartlinks SDK is organized into the following namespaces:
|
|
|
135
135
|
- **serialNumber** - Assign, lookup, and manage serial numbers across scopes.
|
|
136
136
|
|
|
137
137
|
— Other —
|
|
138
|
+
- **adminCards** - Functions for adminCards operations
|
|
138
139
|
- **analytics** - Functions for analytics operations
|
|
139
140
|
- **async** - Functions for async operations
|
|
140
141
|
- **attestation** - Functions for attestation operations
|
|
@@ -474,6 +475,112 @@ Examples include:
|
|
|
474
475
|
|
|
475
476
|
## Types
|
|
476
477
|
|
|
478
|
+
### adminCards
|
|
479
|
+
|
|
480
|
+
**AdminCardRefresh** (interface)
|
|
481
|
+
```typescript
|
|
482
|
+
interface AdminCardRefresh {
|
|
483
|
+
on?: AdminCardRefreshOn[]
|
|
484
|
+
maxAge?: number | string
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
**AdminCardDeclaration** (interface)
|
|
489
|
+
```typescript
|
|
490
|
+
interface AdminCardDeclaration {
|
|
491
|
+
id: string
|
|
492
|
+
title: string
|
|
493
|
+
description?: string
|
|
494
|
+
kind?: 'data' | 'component'
|
|
495
|
+
function?: string
|
|
496
|
+
component?: string
|
|
497
|
+
sizes?: AdminCardSize[]
|
|
498
|
+
priority?: number
|
|
499
|
+
levels?: Array<'collection'>
|
|
500
|
+
refresh?: AdminCardRefresh
|
|
501
|
+
}
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
**AppManifestAdminCards** (interface)
|
|
505
|
+
```typescript
|
|
506
|
+
interface AppManifestAdminCards {
|
|
507
|
+
cards: AdminCardDeclaration[]
|
|
508
|
+
files?: {
|
|
509
|
+
js: { umd: string; esm?: string }
|
|
510
|
+
css?: string
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**AdminCardAction** (interface)
|
|
516
|
+
```typescript
|
|
517
|
+
interface AdminCardAction {
|
|
518
|
+
label: string
|
|
519
|
+
path?: string
|
|
520
|
+
url?: string
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
**AdminCardPayload** (interface)
|
|
525
|
+
```typescript
|
|
526
|
+
interface AdminCardPayload {
|
|
527
|
+
title?: string
|
|
528
|
+
value?: number | string
|
|
529
|
+
label?: string
|
|
530
|
+
unit?: string
|
|
531
|
+
trend?: { direction: 'up' | 'down' | 'flat'; label?: string; good?: boolean }
|
|
532
|
+
status?: { tone: 'ok' | 'attention' | 'neutral' | 'error'; text: string }
|
|
533
|
+
progress?: { done: number; total: number; label?: string }
|
|
534
|
+
items?: Array<{ title: string; subtitle?: string; meta?: string; path?: string }>
|
|
535
|
+
todo?: Array<{ text: string; action?: AdminCardAction }>
|
|
536
|
+
actions?: AdminCardAction[]
|
|
537
|
+
empty?: { text: string; action?: AdminCardAction }
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
**AdminCard** (interface)
|
|
542
|
+
```typescript
|
|
543
|
+
interface AdminCard {
|
|
544
|
+
key: string
|
|
545
|
+
appId: string
|
|
546
|
+
cardId: string
|
|
547
|
+
title: string
|
|
548
|
+
description?: string
|
|
549
|
+
kind: 'data' | 'component'
|
|
550
|
+
sizes: AdminCardSize[]
|
|
551
|
+
size: AdminCardSize
|
|
552
|
+
priority: number
|
|
553
|
+
state: AdminCardState
|
|
554
|
+
payload: AdminCardPayload | null
|
|
555
|
+
computedAt: number | null
|
|
556
|
+
error?: string
|
|
557
|
+
component?: { name: string; files: NonNullable<AppManifestAdminCards['files']> }
|
|
558
|
+
}
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
**StudioHomeLayout** (interface)
|
|
562
|
+
```typescript
|
|
563
|
+
interface StudioHomeLayout {
|
|
564
|
+
version: 1
|
|
565
|
+
cards: Array<{ key: string; size?: AdminCardSize; hidden?: boolean }>
|
|
566
|
+
}
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
**AdminCardsResponse** (interface)
|
|
570
|
+
```typescript
|
|
571
|
+
interface AdminCardsResponse {
|
|
572
|
+
cards: AdminCard[]
|
|
573
|
+
hidden: AdminCard[]
|
|
574
|
+
layout: StudioHomeLayout | null
|
|
575
|
+
}
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
**AdminCardSize** = `'small' | 'medium' | 'wide' | 'full'`
|
|
579
|
+
|
|
580
|
+
**AdminCardRefreshOn** = ``
|
|
581
|
+
|
|
582
|
+
**AdminCardState** = ``
|
|
583
|
+
|
|
477
584
|
### ai
|
|
478
585
|
|
|
479
586
|
**ContentPart** (interface)
|
|
@@ -2852,6 +2959,7 @@ interface AppManifest {
|
|
|
2852
2959
|
executor?: AppManifestExecutor;
|
|
2853
2960
|
functions?: AppManifestFunctions;
|
|
2854
2961
|
realtime?: { channels: RealtimeChannelDeclaration[] };
|
|
2962
|
+
adminCards?: AppManifestAdminCards;
|
|
2855
2963
|
data?: AppDataDeclaration;
|
|
2856
2964
|
headless?: AppHeadlessDeclaration;
|
|
2857
2965
|
connector?: AppConnectorDeclaration;
|
|
@@ -7138,6 +7246,76 @@ interface FailedRecord {
|
|
|
7138
7246
|
}
|
|
7139
7247
|
```
|
|
7140
7248
|
|
|
7249
|
+
**FlowHealth** (interface)
|
|
7250
|
+
```typescript
|
|
7251
|
+
interface FlowHealth {
|
|
7252
|
+
flowId: string
|
|
7253
|
+
name: string
|
|
7254
|
+
direction: FlowDirection
|
|
7255
|
+
status: FlowStatus
|
|
7256
|
+
paused: { since: string; nextCheckAt: string | null; reason: string | null } | null
|
|
7257
|
+
running: { runId: string; startedAt: string; heartbeatAt: string | null; trigger: string; progress: Record<string, number | string> } | null
|
|
7258
|
+
lastRun: { at: string | null; status: string | null; error: string | null }
|
|
7259
|
+
last24h: {
|
|
7260
|
+
runs: number
|
|
7261
|
+
byStatus: Record<string, number>
|
|
7262
|
+
records: number
|
|
7263
|
+
delivered: number
|
|
7264
|
+
failed: number
|
|
7265
|
+
failureRate: number
|
|
7266
|
+
avgRunMs: number | null
|
|
7267
|
+
recordsPerSecond: number | null
|
|
7268
|
+
}
|
|
7269
|
+
backlog: {
|
|
7270
|
+
landed?: number
|
|
7271
|
+
waiting?: number
|
|
7272
|
+
oldestWaitingSince?: string | null
|
|
7273
|
+
delivered?: number
|
|
7274
|
+
retrying: number
|
|
7275
|
+
retriesDue: number
|
|
7276
|
+
gaveUp: number
|
|
7277
|
+
}
|
|
7278
|
+
}
|
|
7279
|
+
```
|
|
7280
|
+
|
|
7281
|
+
**IntegrationHealth** (interface)
|
|
7282
|
+
```typescript
|
|
7283
|
+
interface IntegrationHealth {
|
|
7284
|
+
flows: FlowHealth[]
|
|
7285
|
+
summary: { flows: number; active: number; paused: number; running: number; failedLast24h: number; gaveUp: number }
|
|
7286
|
+
}
|
|
7287
|
+
```
|
|
7288
|
+
|
|
7289
|
+
**ProductLineage** (interface)
|
|
7290
|
+
```typescript
|
|
7291
|
+
interface ProductLineage {
|
|
7292
|
+
productId: string
|
|
7293
|
+
sources: Array<{
|
|
7294
|
+
source: string
|
|
7295
|
+
externalId: string
|
|
7296
|
+
flow: { id: string; name: string }
|
|
7297
|
+
fetchedAt: string
|
|
7298
|
+
processedAt: string | null
|
|
7299
|
+
processedRunId: string | null
|
|
7300
|
+
status: string
|
|
7301
|
+
lastError: string | null
|
|
7302
|
+
currentMapping: boolean
|
|
7303
|
+
mapping: FieldMapping[]
|
|
7304
|
+
payload?: unknown
|
|
7305
|
+
}>
|
|
7306
|
+
deliveries: Array<{
|
|
7307
|
+
flow: { id: string; name: string }
|
|
7308
|
+
status: 'sent' | 'failed' | 'dead'
|
|
7309
|
+
lastSentAt: string | null
|
|
7310
|
+
attempts: number
|
|
7311
|
+
nextAttemptAt: string | null
|
|
7312
|
+
lastError: string | null
|
|
7313
|
+
lastRunId: string | null
|
|
7314
|
+
}>
|
|
7315
|
+
recentRuns: FlowRun[]
|
|
7316
|
+
}
|
|
7317
|
+
```
|
|
7318
|
+
|
|
7141
7319
|
**FailedRecordList** (interface)
|
|
7142
7320
|
```typescript
|
|
7143
7321
|
interface FailedRecordList {
|
|
@@ -10320,6 +10498,17 @@ interface Gs1DigitalLinkParams {
|
|
|
10320
10498
|
|
|
10321
10499
|
## API Functions
|
|
10322
10500
|
|
|
10501
|
+
### adminCards
|
|
10502
|
+
|
|
10503
|
+
**list**(collectionId: string) → `Promise<AdminCardsResponse>`
|
|
10504
|
+
The collection's dashboard: every card, in display order, with its cached payload and how fresh it is (`state`). Never waits on slow cards — those come back `stale` / `pending` and arrive later as a realtime `cards` change.
|
|
10505
|
+
|
|
10506
|
+
**refresh**(collectionId: string, appId: string, cardId: string) → `Promise<AdminCard>`
|
|
10507
|
+
Recompute one card now (the card's "refresh" button). Rate-limited per card.
|
|
10508
|
+
|
|
10509
|
+
**saveLayout**(collectionId: string, layout: StudioHomeLayout) → `Promise<StudioHomeLayout>`
|
|
10510
|
+
Save the collection's dashboard layout (order, sizes, hidden cards) — every admin sees it.
|
|
10511
|
+
|
|
10323
10512
|
### agent
|
|
10324
10513
|
|
|
10325
10514
|
**run**(collectionId: string, body: AgentRunRequest) → `Promise<AgentRunResult>`
|
|
@@ -11749,6 +11938,15 @@ A flow's failed / dead records. GET /integrations/flows/:id/failed
|
|
|
11749
11938
|
**retryFailed**(collectionId: string, flowId: string, ids?: string[]) → `Promise<`
|
|
11750
11939
|
Retry failed / dead records now (all of them when `ids` is omitted). POST /integrations/flows/:id/failed/retry
|
|
11751
11940
|
|
|
11941
|
+
**getHealth**(collectionId: string) → `Promise<IntegrationHealth>`
|
|
11942
|
+
Every flow at a glance: paused?, running?, last 24h, backlog. GET /integrations/health
|
|
11943
|
+
|
|
11944
|
+
**getFlowHealth**(collectionId: string, flowId: string) → `Promise<FlowHealth>`
|
|
11945
|
+
One flow's health. GET /integrations/flows/:id/health
|
|
11946
|
+
|
|
11947
|
+
**getProductLineage**(collectionId: string, productId: string, opts: { payload?: boolean } = {}) → `Promise<ProductLineage>`
|
|
11948
|
+
Where a product's data came from (the provider record, when it was fetched and imported, by which run, with the current mapping or an older one) and where it went (each outbound flow's last delivery). `payload: true` includes the provider records as received. GET /integrations/lineage/product/:productId
|
|
11949
|
+
|
|
11752
11950
|
**resumeFlow**(collectionId: string, flowId: string) → `Promise<`
|
|
11753
11951
|
Resume a flow that paused itself (circuit open): held records go out now. POST /integrations/flows/:id/resume
|
|
11754
11952
|
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# Admin cards
|
|
2
|
+
|
|
3
|
+
Cards your app adds to a collection's **dashboard** in SmartLinks Studio — the first thing an admin
|
|
4
|
+
sees. "3 new enquiries", "2 questions waiting for an answer", "Your site is live", with a button
|
|
5
|
+
straight into your app's admin.
|
|
6
|
+
|
|
7
|
+
- **Data cards** (the default, any app): you declare the card and an **admin server function** that
|
|
8
|
+
returns a small JSON payload. Studio draws it — a consistent look, no app code in Studio.
|
|
9
|
+
- **Component cards** (first-party and approved publishers): a React component from your app's
|
|
10
|
+
`adminCards` bundle, for things a data card can't show (charts, inline edits). It gets the same
|
|
11
|
+
cached payload as its `data` prop.
|
|
12
|
+
|
|
13
|
+
## Fast by construction
|
|
14
|
+
|
|
15
|
+
Dashboards that load 20 widgets × 50 calls on every visit are the thing to avoid. So:
|
|
16
|
+
|
|
17
|
+
1. **One request per dashboard.** Studio calls `adminCards.list(collectionId)` once and gets every
|
|
18
|
+
card back — never one request per card.
|
|
19
|
+
2. **Served from a cache.** A card's last payload is kept and shown immediately. On a warm dashboard
|
|
20
|
+
**no card function runs at all.**
|
|
21
|
+
3. **Recomputed only when its data changed.** You declare what the card depends on in
|
|
22
|
+
`refresh.on`; when one of those changes (any write through SmartLinks), the card is marked stale and
|
|
23
|
+
recomputed **in the background** the next time someone looks. The new payload arrives over
|
|
24
|
+
SmartLinks Realtime as a `cards` change. Your FAQ count is recounted when an FAQ changes, not on
|
|
25
|
+
every login.
|
|
26
|
+
4. **Budgets.** A card function gets about **2 seconds** and **8KB** of output. A slow or failing card
|
|
27
|
+
shows its last payload marked as updating / couldn't refresh — it never holds up the page.
|
|
28
|
+
|
|
29
|
+
Keep card functions to one cheap query. If a number is expensive (a big aggregate), keep it up to date
|
|
30
|
+
yourself as data changes and have the card function read it.
|
|
31
|
+
|
|
32
|
+
## Declare a card
|
|
33
|
+
|
|
34
|
+
In `app.manifest.json`:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
"adminCards": {
|
|
38
|
+
"cards": [
|
|
39
|
+
{
|
|
40
|
+
"id": "unanswered",
|
|
41
|
+
"title": "FAQ",
|
|
42
|
+
"description": "Questions waiting for an answer",
|
|
43
|
+
"function": "cardUnanswered",
|
|
44
|
+
"sizes": ["small", "medium"],
|
|
45
|
+
"priority": 40,
|
|
46
|
+
"refresh": { "on": ["records:question"], "maxAge": "1d" }
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
"functions": {
|
|
51
|
+
"files": { "js": { "umd": "functions.umd.js", "esm": "functions.es.js" } },
|
|
52
|
+
"definitions": [
|
|
53
|
+
{ "name": "cardUnanswered", "trigger": { "type": "http" }, "visibility": "admin",
|
|
54
|
+
"authority": "collection", "capabilities": ["sl:records:read"] }
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| Field | |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `id` | Unique in the app (letters, digits, `-`, `_`; up to 40). |
|
|
62
|
+
| `title` | The card's heading (the payload can override it). |
|
|
63
|
+
| `description` | One line for the "add a card" picker. |
|
|
64
|
+
| `kind` | `data` (default) or `component`. |
|
|
65
|
+
| `function` | The **admin** http server function returning the payload. Required for data cards. |
|
|
66
|
+
| `component` | Component cards: the export name in the `adminCards` bundle. |
|
|
67
|
+
| `sizes` | `small` (3/12), `medium` (6/12), `wide` (8/12), `full` (12/12). First = default. Default `["medium"]`. |
|
|
68
|
+
| `priority` | Lower first, until the collection arranges its dashboard. Default 100. |
|
|
69
|
+
| `refresh.on` | What the data depends on — see below. |
|
|
70
|
+
| `refresh.maxAge` | Recompute at least this often: seconds, or `"30m"`, `"6h"`, `"2d"`. Default 6h, minimum 1m. |
|
|
71
|
+
|
|
72
|
+
### `refresh.on`
|
|
73
|
+
|
|
74
|
+
| Value | Changes when |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `records` | Any of **this app's** records is created, updated or deleted |
|
|
77
|
+
| `records:<type>` | This app's records of that `recordType` |
|
|
78
|
+
| `products` | A product in the collection |
|
|
79
|
+
| `proofs` | A proof |
|
|
80
|
+
| `assets` | An asset |
|
|
81
|
+
| `apps` | The collection's installed apps |
|
|
82
|
+
| `collection` | The collection itself |
|
|
83
|
+
| `settings` / `settings:<group>` | Any settings group / that one |
|
|
84
|
+
|
|
85
|
+
Data that changes without an event (visit counts, "due this week") — use `maxAge` only.
|
|
86
|
+
|
|
87
|
+
## Return the payload
|
|
88
|
+
|
|
89
|
+
The function returns an `AdminCardPayload` — every field optional; return what the card needs:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import type { AdminCardPayload } from '@proveanything/smartlinks'
|
|
93
|
+
|
|
94
|
+
export async function cardUnanswered(ctx): Promise<AdminCardPayload> {
|
|
95
|
+
// One query: the newest 3 open questions, plus the total.
|
|
96
|
+
const open = await ctx.sl.appRecords.query({ recordType: 'question', status: 'open', limit: 3, sort: '-createdAt' })
|
|
97
|
+
const total = open.pagination.total ?? open.data.length
|
|
98
|
+
if (!total) return { value: 0, label: 'questions waiting', status: { tone: 'ok', text: 'All answered' } }
|
|
99
|
+
return {
|
|
100
|
+
value: total,
|
|
101
|
+
label: total === 1 ? 'question waiting' : 'questions waiting',
|
|
102
|
+
status: { tone: 'attention', text: 'Answer them so they show on your site' },
|
|
103
|
+
items: open.data.map((q) => ({ title: q.data.question, meta: q.createdAt, path: `/questions/${q.id}` })),
|
|
104
|
+
todo: [{ text: `Answer ${total} FAQ question${total === 1 ? '' : 's'}`, action: { label: 'Answer', path: '/questions?status=open' } }],
|
|
105
|
+
actions: [{ label: 'Open FAQ', path: '/' }],
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
| Field | Shown as |
|
|
111
|
+
|---|---|
|
|
112
|
+
| `title` | Heading (overrides the declared title) |
|
|
113
|
+
| `value`, `label`, `unit` | The big number/value and its caption |
|
|
114
|
+
| `trend` | `{ direction: 'up'\|'down'\|'flat', label?, good? }` — `good` picks the colour |
|
|
115
|
+
| `status` | `{ tone: 'ok'\|'attention'\|'neutral'\|'error', text }` — `attention`/`error` also go to the To do strip |
|
|
116
|
+
| `progress` | `{ done, total, label? }` — a progress bar |
|
|
117
|
+
| `items` | Up to 5 rows `{ title, subtitle?, meta?, path? }` |
|
|
118
|
+
| `todo` | `[{ text, action? }]` — collected into the dashboard's To do strip |
|
|
119
|
+
| `actions` | Up to 2 buttons `{ label, path? \| url? }` — `path` opens your app's admin at that path |
|
|
120
|
+
| `empty` | `{ text, action? }` — shown instead of the body when there's nothing yet |
|
|
121
|
+
|
|
122
|
+
No HTML, no styling: Studio draws every card the same way, in the collection's theme.
|
|
123
|
+
|
|
124
|
+
Use **`authority: "collection"`** with read capabilities: a card's payload is cached and shown to every
|
|
125
|
+
admin of the collection, so it shouldn't depend on who happened to load it.
|
|
126
|
+
|
|
127
|
+
## The dashboard and its layout
|
|
128
|
+
|
|
129
|
+
The layout belongs to the **collection** — every admin sees the same dashboard, set up once (by the
|
|
130
|
+
sign-up wizard, or an admin arranging it). It's the `studioHome` settings group:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{ "version": 1, "cards": [ { "key": "faq:unanswered", "size": "small" }, { "key": "platform:products", "hidden": true } ] }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Cards not in the layout follow by `priority`, so a newly installed app's cards appear on their own;
|
|
137
|
+
an uninstalled app's cards drop out. Save it with `adminCards.saveLayout`.
|
|
138
|
+
|
|
139
|
+
## Reading the dashboard (hosts)
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { adminCards, realtime } from '@proveanything/smartlinks'
|
|
143
|
+
|
|
144
|
+
const { cards, hidden, layout } = await adminCards.list(collectionId) // display order
|
|
145
|
+
realtime.onChange({ collectionId }, { type: 'cards' }, () => reload()) // a card was recomputed
|
|
146
|
+
await adminCards.refresh(collectionId, 'faq', 'unanswered') // the card's refresh button
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Draw them with the SDK's renderer, so every host (Studio, Forge's preview) shows a card the same
|
|
150
|
+
way. Pass your React in; the SDK doesn't bundle React:
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
import React from 'react'
|
|
154
|
+
import { createAdminCardViews } from '@proveanything/smartlinks'
|
|
155
|
+
const { AdminCardView, AdminCardGrid, AdminCardTodo } = createAdminCardViews(React)
|
|
156
|
+
|
|
157
|
+
<AdminCardTodo cards={cards} onAction={open} />
|
|
158
|
+
<AdminCardGrid>
|
|
159
|
+
{cards.map((c) => <AdminCardView key={c.key} card={c} onAction={open} onRefresh={refresh} />)}
|
|
160
|
+
</AdminCardGrid>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`onAction(action, card)`: `action.path` opens the card's app admin at that path; `action.url` opens a
|
|
164
|
+
link. Colours follow the SDK theme tokens (`--sl-color-*`); override with `--slc-bg`, `--slc-fg`,
|
|
165
|
+
`--slc-muted`, `--slc-border`, `--slc-accent`, `--slc-ok`, `--slc-attention`, `--slc-error`,
|
|
166
|
+
`--slc-radius`. `normaliseAdminCardPayload(raw)` trims a raw function result exactly as the server
|
|
167
|
+
does (for previews).
|
|
168
|
+
|
|
169
|
+
Each card has `state`: `fresh`, `stale` (last payload, a newer one is coming), `pending` (first
|
|
170
|
+
computation still running) or `error` (last attempt failed; `payload` is the last good one).
|
|
171
|
+
|
|
172
|
+
## Component cards
|
|
173
|
+
|
|
174
|
+
For first-party and approved publishers. Ship an `adminCards` bundle (built like widgets) and declare
|
|
175
|
+
it:
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
"adminCards": {
|
|
179
|
+
"files": { "js": { "umd": "admin-cards.umd.js", "esm": "admin-cards.esm.js" }, "css": "admin-cards.css" },
|
|
180
|
+
"cards": [ { "id": "visits", "title": "Visitors", "kind": "component", "component": "VisitsCard",
|
|
181
|
+
"function": "cardVisits", "sizes": ["wide", "full"], "refresh": { "maxAge": "1h" } } ]
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The component gets `{ collectionId, appId, size, data, SL, openApp(path?), lang }`. Draw from `data`
|
|
186
|
+
(the cached payload of its `function`) — don't fetch on mount; that's what keeps the dashboard fast.
|
|
187
|
+
`smartlinks-doctor` checks your cards.
|
|
@@ -23,9 +23,20 @@ Provider-specific logic (a provider's payload format, its API, its connect form)
|
|
|
23
23
|
destination's `Retry-After`); a permanent failure (most 4xx) or one out of attempts is **dead** and
|
|
24
24
|
waits in `integrations.listFailed` for `integrations.retryFailed`. A destination that keeps failing
|
|
25
25
|
**pauses the flow automatically** (`flow.circuitOpenedAt`): edits are held, small probes check for
|
|
26
|
-
recovery, and it resumes on its own (or with `integrations.resumeFlow`).
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
recovery, and it resumes on its own (or with `integrations.resumeFlow`).
|
|
27
|
+
|
|
28
|
+
**Observability.** `integrations.getHealth` shows every flow at a glance (paused?, running?, last 24h,
|
|
29
|
+
backlog); `getFlowHealth` one flow; `getProductLineage` where a product's data came from and where it
|
|
30
|
+
went. Every run and every record is kept in BigQuery (`analytics.integration_runs`,
|
|
31
|
+
`analytics.integration_run_items`); the API's run history is a 7-day window. Outbound sends carry
|
|
32
|
+
`X-SmartLinks-Run` (the run) and an `Idempotency-Key` (same record, same content → same key).
|
|
33
|
+
|
|
34
|
+
**Change capture.** Every product and record write is captured where it's saved, whatever made it (the
|
|
35
|
+
admin, an import, a server function). A product change *wakes* each subscribed outbound flow, which then
|
|
36
|
+
sweeps every product changed since its last sweep (deletions included). A burst of edits, such as a big
|
|
37
|
+
import, is one sweep, not one send per product. A reconciliation sweep every 15 minutes catches anything
|
|
38
|
+
a wake missed. Events that need one call each (app functions, flows on record events) are recorded
|
|
39
|
+
durably and retried until they're delivered.
|
|
29
40
|
|
|
30
41
|
Credentials are **never** stored on the flow. The connection holds an opaque
|
|
31
42
|
`credentialRef` into the **sealed-secret store** (`secrets` namespace); the value is sealed
|
package/dist/docs/overview.md
CHANGED
|
@@ -69,6 +69,7 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
|
|
|
69
69
|
| **Portal Request Action** | `docs/portal-request-action.md` | Triggering portal built-in actions (__qrScanner, __share, __logout, etc.) from sub-apps |
|
|
70
70
|
| **Interactions** | `docs/interactions.md` | Business events, outcomes, voting, competitions, and journey triggers |
|
|
71
71
|
| **App Config Files** | `docs/app-manifest.md` | Full field-by-field reference for both JSON config files |
|
|
72
|
+
| **Admin cards** | `docs/admin-cards.md` | Cards on the collection's dashboard in Studio: declare a card + an admin function returning its data; cached, refreshed only when its data changes |
|
|
72
73
|
| **Realtime** | `docs/realtime.md` | Live updates: change events (refetch when something changes) and declared app channels (a draw, a status) — no provider library or keys |
|
|
73
74
|
| **Liquid Templates** | `docs/liquid-templates.md` | Dynamic content rendering with LiquidJS |
|
|
74
75
|
| **Iframe Responder** | `docs/iframe-responder.md` | Iframe communication and proxy mode internals |
|
|
@@ -359,6 +360,7 @@ The SmartLinks SDK is organised into namespaces. See `docs/API_SUMMARY.md` for t
|
|
|
359
360
|
- `contact` — Customer contact management
|
|
360
361
|
- `comms` / `broadcasts` — Notifications and campaigns
|
|
361
362
|
- `asset` — File/image uploads
|
|
363
|
+
- `adminCards` — the collection's dashboard: `list`, `refresh`, `saveLayout` (see `docs/admin-cards.md`)
|
|
362
364
|
- `realtime` — SmartLinks Realtime: `onChange`, `channel`, `onMine` (see `docs/realtime.md`)
|
|
363
365
|
|
|
364
366
|
---
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,9 @@ export * as utils from './utils/index.js';
|
|
|
13
13
|
export * as seo from './seo.js';
|
|
14
14
|
export * as site from './site.js';
|
|
15
15
|
export * as headless from './headless.js';
|
|
16
|
+
export { createAdminCardViews, collectAdminCardTodo, adminCardAge, injectAdminCardStyles, ADMIN_CARD_CSS } from './adminCardsView.js';
|
|
17
|
+
export type { AdminCardViewProps, AdminCardTodoItem } from './adminCardsView.js';
|
|
18
|
+
export { normaliseAdminCardPayload, ADMIN_CARD_MAX_BYTES } from './adminCardPayload.js';
|
|
16
19
|
export { SHARED_DEPENDENCY_CONTRACT_VERSION, SHARED_DEPENDENCIES, SHARED_DEPENDENCY_SPECIFIERS, importMapPathFor, getHostSharedDependencies, } from './shared-dependencies.js';
|
|
17
20
|
export type { SharedDependency, HostSharedDependencies } from './shared-dependencies.js';
|
|
18
21
|
export { AI_TOOL_NAMES, BUILTIN_AI_TOOLS, getBuiltinAiTool } from './ai-tools.js';
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,9 @@ import * as site_1 from './site.js';
|
|
|
20
20
|
export { site_1 as site };
|
|
21
21
|
import * as headless_1 from './headless.js';
|
|
22
22
|
export { headless_1 as headless };
|
|
23
|
+
// Admin cards: the shared renderer (bind it to your React) and the payload trimmer — docs/admin-cards.md
|
|
24
|
+
export { createAdminCardViews, collectAdminCardTodo, adminCardAge, injectAdminCardStyles, ADMIN_CARD_CSS } from './adminCardsView.js';
|
|
25
|
+
export { normaliseAdminCardPayload, ADMIN_CARD_MAX_BYTES } from './adminCardPayload.js';
|
|
23
26
|
// Shared dependency contract (host↔app) — one source of truth for externalized deps
|
|
24
27
|
export { SHARED_DEPENDENCY_CONTRACT_VERSION, SHARED_DEPENDENCIES, SHARED_DEPENDENCY_SPECIFIERS, importMapPathFor, getHostSharedDependencies, } from './shared-dependencies.js';
|
|
25
28
|
// Built-in AI tool catalog (design-time discovery of the core agentic toolset)
|