@proveanything/smartlinks 2.0.58 → 2.0.62
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/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 +28 -1
- package/dist/api/integrations.js +39 -0
- package/dist/docs/API_SUMMARY.md +248 -2
- package/dist/docs/admin-cards.md +167 -0
- package/dist/docs/connectors.md +7 -2
- package/dist/docs/integrations.md +19 -0
- package/dist/docs/overview.md +2 -0
- package/dist/openapi.yaml +502 -4
- package/dist/types/adminCards.d.ts +180 -0
- package/dist/types/adminCards.js +15 -0
- package/dist/types/appManifest.d.ts +28 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/integrations.d.ts +126 -1
- package/docs/API_SUMMARY.md +248 -2
- package/docs/admin-cards.md +167 -0
- package/docs/connectors.md +7 -2
- package/docs/integrations.md +19 -0
- package/docs/overview.md +2 -0
- package/openapi.yaml +502 -4
- package/package.json +1 -1
- package/scripts/doctor.mjs +55 -2
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ If you're new to the SDK, this is the easiest path:
|
|
|
28
28
|
- [docs/translations.md](docs/translations.md) — runtime translation lookup, browser-side caching, and translation admin flows
|
|
29
29
|
- [docs/widgets.md](docs/widgets.md) — embeddable React components
|
|
30
30
|
- [docs/realtime.md](docs/realtime.md) — subscriptions and live updates
|
|
31
|
+
- [docs/admin-cards.md](docs/admin-cards.md) — cards on the collection's Studio dashboard
|
|
31
32
|
- [docs/iframe-responder.md](docs/iframe-responder.md) — iframe integration and parent/child messaging
|
|
32
33
|
- [docs/utils.md](docs/utils.md) — helper utilities and portal path generation
|
|
33
34
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AdminCard, AdminCardsResponse, StudioHomeLayout } from "../types/adminCards.js";
|
|
2
|
+
export declare namespace adminCards {
|
|
3
|
+
/**
|
|
4
|
+
* The collection's dashboard: every card, in display order, with its cached payload and how fresh
|
|
5
|
+
* it is (`state`). Never waits on slow cards — those come back `stale` / `pending` and arrive later
|
|
6
|
+
* as a realtime `cards` change.
|
|
7
|
+
*/
|
|
8
|
+
function list(collectionId: string): Promise<AdminCardsResponse>;
|
|
9
|
+
/** Recompute one card now (the card's "refresh" button). Rate-limited per card. */
|
|
10
|
+
function refresh(collectionId: string, appId: string, cardId: string): Promise<AdminCard>;
|
|
11
|
+
/** Save the collection's dashboard layout (order, sizes, hidden cards) — every admin sees it. */
|
|
12
|
+
function saveLayout(collectionId: string, layout: StudioHomeLayout): Promise<StudioHomeLayout>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/api/adminCards.ts
|
|
2
|
+
//
|
|
3
|
+
// Admin cards — a collection's dashboard, built from cards its installed apps contribute (plus the
|
|
4
|
+
// platform's own: products, contacts, visitors). See docs/admin-cards.md.
|
|
5
|
+
//
|
|
6
|
+
// One call loads the whole dashboard, served from a cache; stale cards are recomputed in the
|
|
7
|
+
// background and announced over SmartLinks Realtime as a `cards` change event:
|
|
8
|
+
//
|
|
9
|
+
// const { cards } = await adminCards.list(collectionId)
|
|
10
|
+
// realtime.onChange({ collectionId }, { type: 'cards' }, () => reload())
|
|
11
|
+
//
|
|
12
|
+
// Endpoints: /admin/collection/:collectionId/cards
|
|
13
|
+
import { request, post, put } from "../http.js";
|
|
14
|
+
const base = (collectionId) => `/admin/collection/${encodeURIComponent(collectionId)}/cards`;
|
|
15
|
+
export var adminCards;
|
|
16
|
+
(function (adminCards) {
|
|
17
|
+
/**
|
|
18
|
+
* The collection's dashboard: every card, in display order, with its cached payload and how fresh
|
|
19
|
+
* it is (`state`). Never waits on slow cards — those come back `stale` / `pending` and arrive later
|
|
20
|
+
* as a realtime `cards` change.
|
|
21
|
+
*/
|
|
22
|
+
async function list(collectionId) {
|
|
23
|
+
return request(base(collectionId));
|
|
24
|
+
}
|
|
25
|
+
adminCards.list = list;
|
|
26
|
+
/** Recompute one card now (the card's "refresh" button). Rate-limited per card. */
|
|
27
|
+
async function refresh(collectionId, appId, cardId) {
|
|
28
|
+
return post(`${base(collectionId)}/${encodeURIComponent(appId)}/${encodeURIComponent(cardId)}/refresh`, {});
|
|
29
|
+
}
|
|
30
|
+
adminCards.refresh = refresh;
|
|
31
|
+
/** Save the collection's dashboard layout (order, sizes, hidden cards) — every admin sees it. */
|
|
32
|
+
async function saveLayout(collectionId, layout) {
|
|
33
|
+
return put(`${base(collectionId)}/layout`, layout);
|
|
34
|
+
}
|
|
35
|
+
adminCards.saveLayout = saveLayout;
|
|
36
|
+
})(adminCards || (adminCards = {}));
|
package/dist/api/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export { analytics } from "./analytics.js";
|
|
|
30
30
|
export { functions } from "./functions.js";
|
|
31
31
|
export { location } from "./location.js";
|
|
32
32
|
export * as realtime from "./realtime.js";
|
|
33
|
+
export { adminCards } from "./adminCards.js";
|
|
33
34
|
export { tags } from "./tags.js";
|
|
34
35
|
export { order } from "./order.js";
|
|
35
36
|
export { app } from "./appObjects.js";
|
package/dist/api/index.js
CHANGED
|
@@ -33,6 +33,7 @@ export { functions } from "./functions.js";
|
|
|
33
33
|
export { location } from "./location.js";
|
|
34
34
|
import * as realtime_1 from "./realtime.js";
|
|
35
35
|
export { realtime_1 as realtime };
|
|
36
|
+
export { adminCards } from "./adminCards.js";
|
|
36
37
|
export { tags } from "./tags.js";
|
|
37
38
|
export { order } from "./order.js";
|
|
38
39
|
export { app } from "./appObjects.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IntegrationFlow, CreateFlowInput, UpdateFlowInput, ListFlowsQuery, FlowList, RunFlowInput, RunFlowResult, RunFlowSummary, RunFlowEnqueued, RecordTypesResponse, FlowRun, ListRunsQuery, RunList, ListRunItemsQuery, RunItemList, LoggingWindow, SetLoggingInput, InstalledConnector, SaveConnectorInstallationInput, ConnectorConsoleStep } from "../types/integrations.js";
|
|
1
|
+
import type { IntegrationFlow, CreateFlowInput, UpdateFlowInput, ListFlowsQuery, FlowList, RunFlowInput, RunFlowResult, RunFlowSummary, RunFlowEnqueued, RecordTypesResponse, FlowRun, ListRunsQuery, RunList, ListRunItemsQuery, RunItemList, LoggingWindow, SetLoggingInput, InstalledConnector, SaveConnectorInstallationInput, ConnectorConsoleStep, FailedRecordList, FlowHealth, IntegrationHealth, ProductLineage } from "../types/integrations.js";
|
|
2
2
|
export declare namespace integrations {
|
|
3
3
|
/** List flows in a collection. GET /integrations/flows */
|
|
4
4
|
function listFlows(collectionId: string, query?: ListFlowsQuery): Promise<FlowList>;
|
|
@@ -53,6 +53,33 @@ export declare namespace integrations {
|
|
|
53
53
|
function setLogging(collectionId: string, connectionId: string, input?: SetLoggingInput): Promise<{
|
|
54
54
|
window: LoggingWindow;
|
|
55
55
|
}>;
|
|
56
|
+
/** A flow's failed / dead records. GET /integrations/flows/:id/failed */
|
|
57
|
+
function listFailed(collectionId: string, flowId: string, query?: {
|
|
58
|
+
status?: 'failed' | 'dead';
|
|
59
|
+
limit?: number;
|
|
60
|
+
cursor?: string;
|
|
61
|
+
}): Promise<FailedRecordList>;
|
|
62
|
+
/** Retry failed / dead records now (all of them when `ids` is omitted). POST /integrations/flows/:id/failed/retry */
|
|
63
|
+
function retryFailed(collectionId: string, flowId: string, ids?: string[]): Promise<{
|
|
64
|
+
retrying: number;
|
|
65
|
+
}>;
|
|
66
|
+
/** Every flow at a glance: paused?, running?, last 24h, backlog. GET /integrations/health */
|
|
67
|
+
function getHealth(collectionId: string): Promise<IntegrationHealth>;
|
|
68
|
+
/** One flow's health. GET /integrations/flows/:id/health */
|
|
69
|
+
function getFlowHealth(collectionId: string, flowId: string): Promise<FlowHealth>;
|
|
70
|
+
/**
|
|
71
|
+
* Where a product's data came from (the provider record, when it was fetched and imported, by which
|
|
72
|
+
* run, with the current mapping or an older one) and where it went (each outbound flow's last
|
|
73
|
+
* delivery). `payload: true` includes the provider records as received.
|
|
74
|
+
* GET /integrations/lineage/product/:productId
|
|
75
|
+
*/
|
|
76
|
+
function getProductLineage(collectionId: string, productId: string, opts?: {
|
|
77
|
+
payload?: boolean;
|
|
78
|
+
}): Promise<ProductLineage>;
|
|
79
|
+
/** Resume a flow that paused itself (circuit open): held records go out now. POST /integrations/flows/:id/resume */
|
|
80
|
+
function resumeFlow(collectionId: string, flowId: string): Promise<{
|
|
81
|
+
resumed: boolean;
|
|
82
|
+
}>;
|
|
56
83
|
/** Cancel enhanced logging now. DELETE /integrations/logging/:connectionId */
|
|
57
84
|
function cancelLogging(collectionId: string, connectionId: string): Promise<{
|
|
58
85
|
canceled: boolean;
|
package/dist/api/integrations.js
CHANGED
|
@@ -130,6 +130,45 @@ export var integrations;
|
|
|
130
130
|
return post(`/admin/collection/${enc(collectionId)}/integrations/logging/${enc(connectionId)}`, input);
|
|
131
131
|
}
|
|
132
132
|
integrations.setLogging = setLogging;
|
|
133
|
+
// --- Failed records + resume -----------------------------------------------
|
|
134
|
+
// A failed record is retried on its own with backoff; a permanent failure, or one out of attempts, is
|
|
135
|
+
// 'dead' and waits for someone to retry it (or for the record to change).
|
|
136
|
+
/** A flow's failed / dead records. GET /integrations/flows/:id/failed */
|
|
137
|
+
async function listFailed(collectionId, flowId, query = {}) {
|
|
138
|
+
return request(`${base(collectionId)}/${enc(flowId)}/failed${encodeQuery(query)}`);
|
|
139
|
+
}
|
|
140
|
+
integrations.listFailed = listFailed;
|
|
141
|
+
/** Retry failed / dead records now (all of them when `ids` is omitted). POST /integrations/flows/:id/failed/retry */
|
|
142
|
+
async function retryFailed(collectionId, flowId, ids) {
|
|
143
|
+
return post(`${base(collectionId)}/${enc(flowId)}/failed/retry`, ids ? { ids } : {});
|
|
144
|
+
}
|
|
145
|
+
integrations.retryFailed = retryFailed;
|
|
146
|
+
// --- Health + lineage ---------------------------------------------------------
|
|
147
|
+
/** Every flow at a glance: paused?, running?, last 24h, backlog. GET /integrations/health */
|
|
148
|
+
async function getHealth(collectionId) {
|
|
149
|
+
return request(`/admin/collection/${enc(collectionId)}/integrations/health`);
|
|
150
|
+
}
|
|
151
|
+
integrations.getHealth = getHealth;
|
|
152
|
+
/** One flow's health. GET /integrations/flows/:id/health */
|
|
153
|
+
async function getFlowHealth(collectionId, flowId) {
|
|
154
|
+
return request(`${base(collectionId)}/${enc(flowId)}/health`);
|
|
155
|
+
}
|
|
156
|
+
integrations.getFlowHealth = getFlowHealth;
|
|
157
|
+
/**
|
|
158
|
+
* Where a product's data came from (the provider record, when it was fetched and imported, by which
|
|
159
|
+
* run, with the current mapping or an older one) and where it went (each outbound flow's last
|
|
160
|
+
* delivery). `payload: true` includes the provider records as received.
|
|
161
|
+
* GET /integrations/lineage/product/:productId
|
|
162
|
+
*/
|
|
163
|
+
async function getProductLineage(collectionId, productId, opts = {}) {
|
|
164
|
+
return request(`/admin/collection/${enc(collectionId)}/integrations/lineage/product/${enc(productId)}${opts.payload ? '?payload=1' : ''}`);
|
|
165
|
+
}
|
|
166
|
+
integrations.getProductLineage = getProductLineage;
|
|
167
|
+
/** Resume a flow that paused itself (circuit open): held records go out now. POST /integrations/flows/:id/resume */
|
|
168
|
+
async function resumeFlow(collectionId, flowId) {
|
|
169
|
+
return post(`${base(collectionId)}/${enc(flowId)}/resume`, {});
|
|
170
|
+
}
|
|
171
|
+
integrations.resumeFlow = resumeFlow;
|
|
133
172
|
/** Cancel enhanced logging now. DELETE /integrations/logging/:connectionId */
|
|
134
173
|
async function cancelLogging(collectionId, connectionId) {
|
|
135
174
|
return del(`/admin/collection/${enc(collectionId)}/integrations/logging/${enc(connectionId)}`);
|
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.62 | Generated: 2026-10-13T20:05:22.753Z
|
|
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;
|
|
@@ -2915,6 +3023,8 @@ interface AppConnectorDeclaration {
|
|
|
2915
3023
|
}
|
|
2916
3024
|
panels?: ConnectorPanel[]
|
|
2917
3025
|
aiGuide?: string
|
|
3026
|
+
recordId?: string
|
|
3027
|
+
limits?: { requestsPerSecond?: number }
|
|
2918
3028
|
}
|
|
2919
3029
|
```
|
|
2920
3030
|
|
|
@@ -3008,7 +3118,15 @@ interface ConnectorSendInput {
|
|
|
3008
3118
|
**ConnectorSendResult** (interface)
|
|
3009
3119
|
```typescript
|
|
3010
3120
|
interface ConnectorSendResult {
|
|
3011
|
-
results: Array<{
|
|
3121
|
+
results: Array<{
|
|
3122
|
+
index: number
|
|
3123
|
+
ok: boolean
|
|
3124
|
+
status?: number
|
|
3125
|
+
error?: string
|
|
3126
|
+
externalId?: string
|
|
3127
|
+
retryable?: boolean
|
|
3128
|
+
retryAfterSeconds?: number
|
|
3129
|
+
}>
|
|
3012
3130
|
}
|
|
3013
3131
|
```
|
|
3014
3132
|
|
|
@@ -7108,6 +7226,102 @@ interface IntegrationFlow {
|
|
|
7108
7226
|
lastPollAt?: string | null
|
|
7109
7227
|
lastCursor?: string | null
|
|
7110
7228
|
totalSynced?: number | null
|
|
7229
|
+
circuitOpenedAt?: string | null
|
|
7230
|
+
circuitProbeAt?: string | null
|
|
7231
|
+
circuitReason?: string | null
|
|
7232
|
+
}
|
|
7233
|
+
```
|
|
7234
|
+
|
|
7235
|
+
**FailedRecord** (interface)
|
|
7236
|
+
```typescript
|
|
7237
|
+
interface FailedRecord {
|
|
7238
|
+
id: string
|
|
7239
|
+
entityId?: string | null
|
|
7240
|
+
externalId?: string
|
|
7241
|
+
status: 'failed' | 'dead'
|
|
7242
|
+
attempts: number
|
|
7243
|
+
nextAttemptAt?: string | null
|
|
7244
|
+
lastError?: string | null
|
|
7245
|
+
updatedAt?: string
|
|
7246
|
+
}
|
|
7247
|
+
```
|
|
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
|
+
|
|
7319
|
+
**FailedRecordList** (interface)
|
|
7320
|
+
```typescript
|
|
7321
|
+
interface FailedRecordList {
|
|
7322
|
+
direction: FlowDirection
|
|
7323
|
+
items: FailedRecord[]
|
|
7324
|
+
nextCursor: string | null
|
|
7111
7325
|
}
|
|
7112
7326
|
```
|
|
7113
7327
|
|
|
@@ -7161,6 +7375,7 @@ interface RunFlowSummary {
|
|
|
7161
7375
|
created?: number
|
|
7162
7376
|
updated?: number
|
|
7163
7377
|
skipped?: number
|
|
7378
|
+
held?: number
|
|
7164
7379
|
status: RunStatus
|
|
7165
7380
|
runId?: string
|
|
7166
7381
|
}
|
|
@@ -10283,6 +10498,17 @@ interface Gs1DigitalLinkParams {
|
|
|
10283
10498
|
|
|
10284
10499
|
## API Functions
|
|
10285
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
|
+
|
|
10286
10512
|
### agent
|
|
10287
10513
|
|
|
10288
10514
|
**run**(collectionId: string, body: AgentRunRequest) → `Promise<AgentRunResult>`
|
|
@@ -11704,6 +11930,26 @@ Is enhanced logging active for a connection? GET /integrations/logging/:connecti
|
|
|
11704
11930
|
**setLogging**(collectionId: string, connectionId: string, input: SetLoggingInput = {}) → `Promise<`
|
|
11705
11931
|
Enable enhanced logging. POST /integrations/logging/:connectionId (omit ttlMinutes = until cancelled)
|
|
11706
11932
|
|
|
11933
|
+
**listFailed**(collectionId: string,
|
|
11934
|
+
flowId: string,
|
|
11935
|
+
query: { status?: 'failed' | 'dead'; limit?: number; cursor?: string } = {}) → `Promise<FailedRecordList>`
|
|
11936
|
+
A flow's failed / dead records. GET /integrations/flows/:id/failed
|
|
11937
|
+
|
|
11938
|
+
**retryFailed**(collectionId: string, flowId: string, ids?: string[]) → `Promise<`
|
|
11939
|
+
Retry failed / dead records now (all of them when `ids` is omitted). POST /integrations/flows/:id/failed/retry
|
|
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
|
+
|
|
11950
|
+
**resumeFlow**(collectionId: string, flowId: string) → `Promise<`
|
|
11951
|
+
Resume a flow that paused itself (circuit open): held records go out now. POST /integrations/flows/:id/resume
|
|
11952
|
+
|
|
11707
11953
|
**cancelLogging**(collectionId: string, connectionId: string) → `Promise<`
|
|
11708
11954
|
Cancel enhanced logging now. DELETE /integrations/logging/:connectionId
|
|
11709
11955
|
|
|
@@ -0,0 +1,167 @@
|
|
|
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
|
+
Each card has `state`: `fresh`, `stale` (last payload, a newer one is coming), `pending` (first
|
|
150
|
+
computation still running) or `error` (last attempt failed; `payload` is the last good one).
|
|
151
|
+
|
|
152
|
+
## Component cards
|
|
153
|
+
|
|
154
|
+
For first-party and approved publishers. Ship an `adminCards` bundle (built like widgets) and declare
|
|
155
|
+
it:
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
"adminCards": {
|
|
159
|
+
"files": { "js": { "umd": "admin-cards.umd.js", "esm": "admin-cards.esm.js" }, "css": "admin-cards.css" },
|
|
160
|
+
"cards": [ { "id": "visits", "title": "Visitors", "kind": "component", "component": "VisitsCard",
|
|
161
|
+
"function": "cardVisits", "sizes": ["wide", "full"], "refresh": { "maxAge": "1h" } } ]
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The component gets `{ collectionId, appId, size, data, SL, openApp(path?), lang }`. Draw from `data`
|
|
166
|
+
(the cached payload of its `function`) — don't fetch on mount; that's what keeps the dashboard fast.
|
|
167
|
+
`smartlinks-doctor` checks your cards.
|
package/dist/docs/connectors.md
CHANGED
|
@@ -90,6 +90,8 @@ Types: `AppConnectorDeclaration` and friends in `@proveanything/smartlinks` (`sr
|
|
|
90
90
|
| `functions` | Which of this app's server functions implement each step (below). Fill in only what applies. |
|
|
91
91
|
| `panels[]` | Provider-specific screens the console mounts (an API explorer, a status page). |
|
|
92
92
|
| `aiGuide` | A guide the mapping assistant reads: the provider's quirks, required fields, good defaults. |
|
|
93
|
+
| `recordId` | Path of the provider's own id in a fetched record (e.g. `gtin`). Inbound records are landed and matched by it, so a re-import updates the same product instead of creating another. Default `id`; a flow can override it (`config.upsert.externalIdPath`). |
|
|
94
|
+
| `limits` | `{ requestsPerSecond }` toward the provider, enforced across every SmartLinks worker. |
|
|
93
95
|
|
|
94
96
|
---
|
|
95
97
|
|
|
@@ -112,9 +114,12 @@ declare the provider's hosts (`network:<host>`) and the secrets it reads (`secre
|
|
|
112
114
|
| `send` | Every outbound run | `ConnectorSendInput` `{ installation, payloads }` | `ConnectorSendResult` `{ results: [{ index, ok, status, error, externalId }] }` |
|
|
113
115
|
|
|
114
116
|
**How a run flows:**
|
|
115
|
-
- **Inbound:** `fetch` (paged) → `shape` → core maps the documents
|
|
117
|
+
- **Inbound:** `fetch` (paged; every record is landed as received) → `shape` → core maps the documents
|
|
118
|
+
with the flow's mapping → upsert, matched by `recordId`. Unchanged records aren't processed again; a
|
|
119
|
+
changed flow configuration replays the landed records without fetching.
|
|
116
120
|
- **Outbound:** gather the SmartLinks records → core maps them to the provider's fields → `serialize`
|
|
117
|
-
→ `send` (or a generic transport).
|
|
121
|
+
→ `send` (or a generic transport). A record identical to its last successful delivery isn't sent again.
|
|
122
|
+
- Large runs are processed in short slices that resume after an interruption; there's no record cap.
|
|
118
123
|
|
|
119
124
|
**What happens when a step is left out:**
|
|
120
125
|
- **No `shape`:** core maps the provider records as they come.
|