@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
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin cards — what an app contributes to a collection's dashboard in Studio (docs/admin-cards.md).
|
|
3
|
+
*
|
|
4
|
+
* Two kinds, one contract:
|
|
5
|
+
* - **data** (the default, any publisher): the app declares the card and an ADMIN server function that
|
|
6
|
+
* returns an {@link AdminCardPayload}; Studio draws it. No app code runs in Studio.
|
|
7
|
+
* - **component** (first-party and approved publishers): a React component from the app's
|
|
8
|
+
* `adminCards` bundle, given the same cached payload as its `data` prop.
|
|
9
|
+
*
|
|
10
|
+
* Fast by construction: Studio loads every card in ONE request, served from a cache. A card is only
|
|
11
|
+
* recomputed when something it declares in `refresh.on` changed (or `refresh.maxAge` passed), in the
|
|
12
|
+
* background, within a time and size budget — so a dashboard never waits on 20 functions.
|
|
13
|
+
*/
|
|
14
|
+
/** Grid widths on a 12-column dashboard: small 3, medium 6, wide 8, full 12. One column on phones. */
|
|
15
|
+
export type AdminCardSize = 'small' | 'medium' | 'wide' | 'full';
|
|
16
|
+
/**
|
|
17
|
+
* What a card's data depends on — a change of this kind marks it stale. `records` / `records:<type>`
|
|
18
|
+
* are THIS app's records; the rest are the collection's.
|
|
19
|
+
*/
|
|
20
|
+
export type AdminCardRefreshOn = 'records' | `records:${string}` | 'products' | 'proofs' | 'assets' | 'apps' | 'collection' | 'settings' | `settings:${string}`;
|
|
21
|
+
export interface AdminCardRefresh {
|
|
22
|
+
/** Recompute when any of these change. Leave out only for data that changes without an event. */
|
|
23
|
+
on?: AdminCardRefreshOn[];
|
|
24
|
+
/**
|
|
25
|
+
* The longest a payload is shown before it's recomputed anyway: seconds, or "30m" / "6h" / "2d".
|
|
26
|
+
* Default 6h; minimum 1m. Use it for data with no change event (visits, time-based counts).
|
|
27
|
+
*/
|
|
28
|
+
maxAge?: number | string;
|
|
29
|
+
}
|
|
30
|
+
/** One card an app contributes — `app.manifest.json` → `adminCards.cards[]`. */
|
|
31
|
+
export interface AdminCardDeclaration {
|
|
32
|
+
/** Unique within the app: letters, digits, `-` and `_`, up to 40. */
|
|
33
|
+
id: string;
|
|
34
|
+
/** Shown as the card's heading (the payload may override it). */
|
|
35
|
+
title: string;
|
|
36
|
+
/** One line for the "add a card" picker. */
|
|
37
|
+
description?: string;
|
|
38
|
+
/** `data` (default) or `component`. */
|
|
39
|
+
kind?: 'data' | 'component';
|
|
40
|
+
/**
|
|
41
|
+
* The ADMIN http server function that returns this card's {@link AdminCardPayload} (a function
|
|
42
|
+
* declared in `functions.definitions` with `visibility: "admin"`). Required for `data` cards;
|
|
43
|
+
* optional for `component` cards, whose component then gets the payload as `data`.
|
|
44
|
+
*/
|
|
45
|
+
function?: string;
|
|
46
|
+
/** `component` cards: the export name in the `adminCards` bundle. */
|
|
47
|
+
component?: string;
|
|
48
|
+
/** Sizes the card supports; the first is its default. Default `["medium"]`. */
|
|
49
|
+
sizes?: AdminCardSize[];
|
|
50
|
+
/** Lower comes first when the collection hasn't arranged its dashboard. Default 100. */
|
|
51
|
+
priority?: number;
|
|
52
|
+
/** Where the card can appear. Only `collection` (the dashboard) today. */
|
|
53
|
+
levels?: Array<'collection'>;
|
|
54
|
+
refresh?: AdminCardRefresh;
|
|
55
|
+
}
|
|
56
|
+
/** `app.manifest.json` → `adminCards`. */
|
|
57
|
+
export interface AppManifestAdminCards {
|
|
58
|
+
cards: AdminCardDeclaration[];
|
|
59
|
+
/** The `adminCards` bundle — only when the app has `component` cards. */
|
|
60
|
+
files?: {
|
|
61
|
+
js: {
|
|
62
|
+
umd: string;
|
|
63
|
+
esm?: string;
|
|
64
|
+
};
|
|
65
|
+
css?: string;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** A link from a card into the app's admin (`path`, e.g. "/enquiries?status=new") or elsewhere (`url`). */
|
|
69
|
+
export interface AdminCardAction {
|
|
70
|
+
label: string;
|
|
71
|
+
/** A path inside this app's admin. */
|
|
72
|
+
path?: string;
|
|
73
|
+
/** An absolute https URL (opens in a new tab). */
|
|
74
|
+
url?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* What a card's function returns. Every field is optional — return the ones the card needs. Studio
|
|
78
|
+
* draws them in a fixed, consistent layout; there's no HTML or styling. Max ~8KB as JSON.
|
|
79
|
+
*/
|
|
80
|
+
export interface AdminCardPayload {
|
|
81
|
+
/** Overrides the declared title (e.g. "3 new enquiries"). */
|
|
82
|
+
title?: string;
|
|
83
|
+
/** The headline number or short value. */
|
|
84
|
+
value?: number | string;
|
|
85
|
+
/** Shown after/under the value ("enquiries this week"). */
|
|
86
|
+
label?: string;
|
|
87
|
+
unit?: string;
|
|
88
|
+
/** Change against a previous period. `good` says whether this direction is good news (for colour). */
|
|
89
|
+
trend?: {
|
|
90
|
+
direction: 'up' | 'down' | 'flat';
|
|
91
|
+
label?: string;
|
|
92
|
+
good?: boolean;
|
|
93
|
+
};
|
|
94
|
+
/** One status line. `attention` and `error` also surface in the To do strip. */
|
|
95
|
+
status?: {
|
|
96
|
+
tone: 'ok' | 'attention' | 'neutral' | 'error';
|
|
97
|
+
text: string;
|
|
98
|
+
};
|
|
99
|
+
/** Setup or completion progress ("4 of 6 steps"). */
|
|
100
|
+
progress?: {
|
|
101
|
+
done: number;
|
|
102
|
+
total: number;
|
|
103
|
+
label?: string;
|
|
104
|
+
};
|
|
105
|
+
/** A short list — at most 5 are shown. */
|
|
106
|
+
items?: Array<{
|
|
107
|
+
title: string;
|
|
108
|
+
subtitle?: string;
|
|
109
|
+
meta?: string;
|
|
110
|
+
path?: string;
|
|
111
|
+
}>;
|
|
112
|
+
/** Things that need doing — collected into the dashboard's To do strip. */
|
|
113
|
+
todo?: Array<{
|
|
114
|
+
text: string;
|
|
115
|
+
action?: AdminCardAction;
|
|
116
|
+
}>;
|
|
117
|
+
/** At most two buttons. */
|
|
118
|
+
actions?: AdminCardAction[];
|
|
119
|
+
/** Shown instead of the body when there's nothing yet ("No enquiries yet"). */
|
|
120
|
+
empty?: {
|
|
121
|
+
text: string;
|
|
122
|
+
action?: AdminCardAction;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/** How fresh a card's payload is. */
|
|
126
|
+
export type AdminCardState =
|
|
127
|
+
/** Up to date. */
|
|
128
|
+
'fresh'
|
|
129
|
+
/** The last payload, being recomputed in the background (a newer one arrives by realtime). */
|
|
130
|
+
| 'stale'
|
|
131
|
+
/** Never computed yet, and not ready in time — it arrives by realtime. */
|
|
132
|
+
| 'pending'
|
|
133
|
+
/** The last attempt failed; `payload` is the last good one, if any. */
|
|
134
|
+
| 'error';
|
|
135
|
+
/** One card on the dashboard, as `adminCards.list` returns it — in display order. */
|
|
136
|
+
export interface AdminCard {
|
|
137
|
+
/** `<appId>:<cardId>` — the key used in the layout. */
|
|
138
|
+
key: string;
|
|
139
|
+
/** The contributing app, or `platform` for Studio's own cards (products, contacts, visitors). */
|
|
140
|
+
appId: string;
|
|
141
|
+
cardId: string;
|
|
142
|
+
title: string;
|
|
143
|
+
description?: string;
|
|
144
|
+
kind: 'data' | 'component';
|
|
145
|
+
sizes: AdminCardSize[];
|
|
146
|
+
/** The size to draw it at (the layout's choice, else the card's default). */
|
|
147
|
+
size: AdminCardSize;
|
|
148
|
+
priority: number;
|
|
149
|
+
state: AdminCardState;
|
|
150
|
+
payload: AdminCardPayload | null;
|
|
151
|
+
/** When `payload` was computed (ms since epoch). */
|
|
152
|
+
computedAt: number | null;
|
|
153
|
+
/** Why the last attempt failed (state `error`). */
|
|
154
|
+
error?: string;
|
|
155
|
+
/** `component` cards: the bundle to load and the export to mount. */
|
|
156
|
+
component?: {
|
|
157
|
+
name: string;
|
|
158
|
+
files: NonNullable<AppManifestAdminCards['files']>;
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* A collection's dashboard layout — the `studioHome` settings group. Owned by the collection (every
|
|
163
|
+
* admin sees the same dashboard). Cards not listed follow, by priority; installed apps' new cards
|
|
164
|
+
* appear on their own; cards of uninstalled apps drop out.
|
|
165
|
+
*/
|
|
166
|
+
export interface StudioHomeLayout {
|
|
167
|
+
version: 1;
|
|
168
|
+
cards: Array<{
|
|
169
|
+
key: string;
|
|
170
|
+
size?: AdminCardSize;
|
|
171
|
+
hidden?: boolean;
|
|
172
|
+
}>;
|
|
173
|
+
}
|
|
174
|
+
/** `adminCards.list` → the whole dashboard in one call. */
|
|
175
|
+
export interface AdminCardsResponse {
|
|
176
|
+
cards: AdminCard[];
|
|
177
|
+
/** Cards the layout hides (for the "add a card" picker). */
|
|
178
|
+
hidden: AdminCard[];
|
|
179
|
+
layout: StudioHomeLayout | null;
|
|
180
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/types/adminCards.ts
|
|
2
|
+
/**
|
|
3
|
+
* Admin cards — what an app contributes to a collection's dashboard in Studio (docs/admin-cards.md).
|
|
4
|
+
*
|
|
5
|
+
* Two kinds, one contract:
|
|
6
|
+
* - **data** (the default, any publisher): the app declares the card and an ADMIN server function that
|
|
7
|
+
* returns an {@link AdminCardPayload}; Studio draws it. No app code runs in Studio.
|
|
8
|
+
* - **component** (first-party and approved publishers): a React component from the app's
|
|
9
|
+
* `adminCards` bundle, given the same cached payload as its `data` prop.
|
|
10
|
+
*
|
|
11
|
+
* Fast by construction: Studio loads every card in ONE request, served from a cache. A card is only
|
|
12
|
+
* recomputed when something it declares in `refresh.on` changed (or `refresh.maxAge` passed), in the
|
|
13
|
+
* background, within a time and size budget — so a dashboard never waits on 20 functions.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
import type { AppDataDeclaration, AppHeadlessDeclaration } from './headless.js';
|
|
4
4
|
import type { RealtimeChannelDeclaration } from './realtime.js';
|
|
5
|
+
import type { AppManifestAdminCards } from './adminCards.js';
|
|
5
6
|
/**
|
|
6
7
|
* A bundle (widget or container) as returned by the collection widgets endpoint.
|
|
7
8
|
*
|
|
@@ -817,6 +818,19 @@ export interface AppManifest {
|
|
|
817
818
|
realtime?: {
|
|
818
819
|
channels: RealtimeChannelDeclaration[];
|
|
819
820
|
};
|
|
821
|
+
/**
|
|
822
|
+
* Cards this app adds to the collection's dashboard in Studio. A `data` card names an ADMIN server
|
|
823
|
+
* function returning an `AdminCardPayload`; Studio draws it, from a cache, recomputing only when
|
|
824
|
+
* something in `refresh.on` changed. @see AdminCardDeclaration · docs/admin-cards.md
|
|
825
|
+
*
|
|
826
|
+
* ```json
|
|
827
|
+
* "adminCards": { "cards": [
|
|
828
|
+
* { "id": "unanswered", "title": "FAQ", "function": "cardUnanswered", "sizes": ["small", "medium"],
|
|
829
|
+
* "refresh": { "on": ["records:question"], "maxAge": "1d" } }
|
|
830
|
+
* ] }
|
|
831
|
+
* ```
|
|
832
|
+
*/
|
|
833
|
+
adminCards?: AppManifestAdminCards;
|
|
820
834
|
/**
|
|
821
835
|
* The app's public data model: its data types, how each is stored, fields, examples and read
|
|
822
836
|
* recipes. Required for `headless`. @see AppDataDeclaration · docs/headless-providers.md
|
|
@@ -926,6 +940,16 @@ export interface AppConnectorDeclaration {
|
|
|
926
940
|
panels?: ConnectorPanel[];
|
|
927
941
|
/** Path (under the app's public files) of a guide the AI mapping assistant reads. */
|
|
928
942
|
aiGuide?: string;
|
|
943
|
+
/**
|
|
944
|
+
* Path of the provider's own id in a fetched record (e.g. 'gtin', 'item.id'). Inbound records are
|
|
945
|
+
* landed and matched to SmartLinks entities by it, so a re-import updates rather than duplicates.
|
|
946
|
+
* Default 'id'; a flow can override it with `config.upsert.externalIdPath`.
|
|
947
|
+
*/
|
|
948
|
+
recordId?: string;
|
|
949
|
+
/** Requests per second toward the provider, shared by every SmartLinks worker. */
|
|
950
|
+
limits?: {
|
|
951
|
+
requestsPerSecond?: number;
|
|
952
|
+
};
|
|
929
953
|
}
|
|
930
954
|
/**
|
|
931
955
|
* What every connector function receives about the installation it serves. The engine assembles it
|
|
@@ -1008,6 +1032,10 @@ export interface ConnectorSendResult {
|
|
|
1008
1032
|
status?: number;
|
|
1009
1033
|
error?: string;
|
|
1010
1034
|
externalId?: string;
|
|
1035
|
+
/** Worth retrying? Default: by status (408/425/429/5xx/none yes; other 4xx no). */
|
|
1036
|
+
retryable?: boolean;
|
|
1037
|
+
/** The provider asked to wait this long before retrying (e.g. its Retry-After). */
|
|
1038
|
+
retryAfterSeconds?: number;
|
|
1011
1039
|
}>;
|
|
1012
1040
|
}
|
|
1013
1041
|
export interface ConnectorFetchInput {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export * from "./analytics.js";
|
|
|
25
25
|
export * from "./location.js";
|
|
26
26
|
export * from "./jobs.js";
|
|
27
27
|
export * from "./realtime.js";
|
|
28
|
+
export * from "./adminCards.js";
|
|
28
29
|
export * from "./tags.js";
|
|
29
30
|
export * from "./navigation.js";
|
|
30
31
|
export * from "./order.js";
|
package/dist/types/index.js
CHANGED
|
@@ -27,6 +27,7 @@ export * from "./analytics.js";
|
|
|
27
27
|
export * from "./location.js";
|
|
28
28
|
export * from "./jobs.js";
|
|
29
29
|
export * from "./realtime.js";
|
|
30
|
+
export * from "./adminCards.js";
|
|
30
31
|
export * from "./tags.js";
|
|
31
32
|
export * from "./navigation.js";
|
|
32
33
|
export * from "./order.js";
|
|
@@ -71,6 +71,129 @@ export interface IntegrationFlow {
|
|
|
71
71
|
lastPollAt?: string | null;
|
|
72
72
|
lastCursor?: string | null;
|
|
73
73
|
totalSynced?: number | null;
|
|
74
|
+
/**
|
|
75
|
+
* Circuit breaker: set while the flow has paused itself because its destination / provider keeps
|
|
76
|
+
* failing. Nothing is sent or fetched (edits are held as retries) until a probe gets through —
|
|
77
|
+
* `circuitProbeAt` is the next one — or someone resumes it (`integrations.resumeFlow`).
|
|
78
|
+
*/
|
|
79
|
+
circuitOpenedAt?: string | null;
|
|
80
|
+
circuitProbeAt?: string | null;
|
|
81
|
+
circuitReason?: string | null;
|
|
82
|
+
}
|
|
83
|
+
/** A record that failed: retried on its own with backoff ('failed'), or given up on ('dead'). */
|
|
84
|
+
export interface FailedRecord {
|
|
85
|
+
/** Outbound: the SmartLinks entity id. Inbound: the landed record's id (pass it to retryFailed). */
|
|
86
|
+
id: string;
|
|
87
|
+
entityId?: string | null;
|
|
88
|
+
/** Inbound: the provider's id for the record. */
|
|
89
|
+
externalId?: string;
|
|
90
|
+
status: 'failed' | 'dead';
|
|
91
|
+
attempts: number;
|
|
92
|
+
nextAttemptAt?: string | null;
|
|
93
|
+
lastError?: string | null;
|
|
94
|
+
updatedAt?: string;
|
|
95
|
+
}
|
|
96
|
+
/** How one flow is doing (GET /integrations/flows/:id/health). */
|
|
97
|
+
export interface FlowHealth {
|
|
98
|
+
flowId: string;
|
|
99
|
+
name: string;
|
|
100
|
+
direction: FlowDirection;
|
|
101
|
+
status: FlowStatus;
|
|
102
|
+
/** Set while the flow has paused itself (its destination / provider keeps failing). */
|
|
103
|
+
paused: {
|
|
104
|
+
since: string;
|
|
105
|
+
nextCheckAt: string | null;
|
|
106
|
+
reason: string | null;
|
|
107
|
+
} | null;
|
|
108
|
+
/** The sweep in progress, if any (progress counts grow as it goes). */
|
|
109
|
+
running: {
|
|
110
|
+
runId: string;
|
|
111
|
+
startedAt: string;
|
|
112
|
+
heartbeatAt: string | null;
|
|
113
|
+
trigger: string;
|
|
114
|
+
progress: Record<string, number | string>;
|
|
115
|
+
} | null;
|
|
116
|
+
lastRun: {
|
|
117
|
+
at: string | null;
|
|
118
|
+
status: string | null;
|
|
119
|
+
error: string | null;
|
|
120
|
+
};
|
|
121
|
+
last24h: {
|
|
122
|
+
runs: number;
|
|
123
|
+
byStatus: Record<string, number>;
|
|
124
|
+
records: number;
|
|
125
|
+
/** Outbound: sent; inbound: created + updated. */
|
|
126
|
+
delivered: number;
|
|
127
|
+
failed: number;
|
|
128
|
+
/** failed / records, 0..1 */
|
|
129
|
+
failureRate: number;
|
|
130
|
+
avgRunMs: number | null;
|
|
131
|
+
recordsPerSecond: number | null;
|
|
132
|
+
};
|
|
133
|
+
/** Inbound: landed / waiting / oldestWaitingSince / retrying / retriesDue / gaveUp. Outbound: delivered / retrying / retriesDue / gaveUp. */
|
|
134
|
+
backlog: {
|
|
135
|
+
landed?: number;
|
|
136
|
+
waiting?: number;
|
|
137
|
+
oldestWaitingSince?: string | null;
|
|
138
|
+
delivered?: number;
|
|
139
|
+
retrying: number;
|
|
140
|
+
retriesDue: number;
|
|
141
|
+
gaveUp: number;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** Every flow in the collection (GET /integrations/health). */
|
|
145
|
+
export interface IntegrationHealth {
|
|
146
|
+
flows: FlowHealth[];
|
|
147
|
+
summary: {
|
|
148
|
+
flows: number;
|
|
149
|
+
active: number;
|
|
150
|
+
paused: number;
|
|
151
|
+
running: number;
|
|
152
|
+
failedLast24h: number;
|
|
153
|
+
gaveUp: number;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/** Where a product's data came from and where it went (GET /integrations/lineage/product/:id). */
|
|
157
|
+
export interface ProductLineage {
|
|
158
|
+
productId: string;
|
|
159
|
+
sources: Array<{
|
|
160
|
+
/** 'connector:<appId>' or 'flow:<flowId>' */
|
|
161
|
+
source: string;
|
|
162
|
+
/** The provider's id for the record. */
|
|
163
|
+
externalId: string;
|
|
164
|
+
flow: {
|
|
165
|
+
id: string;
|
|
166
|
+
name: string;
|
|
167
|
+
};
|
|
168
|
+
fetchedAt: string;
|
|
169
|
+
processedAt: string | null;
|
|
170
|
+
processedRunId: string | null;
|
|
171
|
+
status: string;
|
|
172
|
+
lastError: string | null;
|
|
173
|
+
/** Written with the flow's current mapping and settings (false: an earlier version). */
|
|
174
|
+
currentMapping: boolean;
|
|
175
|
+
mapping: FieldMapping[];
|
|
176
|
+
/** The provider record as received (with `payload: true`). */
|
|
177
|
+
payload?: unknown;
|
|
178
|
+
}>;
|
|
179
|
+
deliveries: Array<{
|
|
180
|
+
flow: {
|
|
181
|
+
id: string;
|
|
182
|
+
name: string;
|
|
183
|
+
};
|
|
184
|
+
status: 'sent' | 'failed' | 'dead';
|
|
185
|
+
lastSentAt: string | null;
|
|
186
|
+
attempts: number;
|
|
187
|
+
nextAttemptAt: string | null;
|
|
188
|
+
lastError: string | null;
|
|
189
|
+
lastRunId: string | null;
|
|
190
|
+
}>;
|
|
191
|
+
recentRuns: FlowRun[];
|
|
192
|
+
}
|
|
193
|
+
export interface FailedRecordList {
|
|
194
|
+
direction: FlowDirection;
|
|
195
|
+
items: FailedRecord[];
|
|
196
|
+
nextCursor: string | null;
|
|
74
197
|
}
|
|
75
198
|
export interface CreateFlowInput {
|
|
76
199
|
appId: string;
|
|
@@ -109,10 +232,12 @@ export interface RunFlowSummary {
|
|
|
109
232
|
/** Outbound: records delivered. */
|
|
110
233
|
sent?: number;
|
|
111
234
|
failed: number;
|
|
112
|
-
/** Inbound: products created / updated, and records the connector skipped. */
|
|
235
|
+
/** Inbound: products created / updated, and records the connector skipped (outbound: unchanged). */
|
|
113
236
|
created?: number;
|
|
114
237
|
updated?: number;
|
|
115
238
|
skipped?: number;
|
|
239
|
+
/** Outbound: changes held because the destination is paused (circuit open); sent after it recovers. */
|
|
240
|
+
held?: number;
|
|
116
241
|
status: RunStatus;
|
|
117
242
|
/** Id of the persisted run row (deep-link into its detail). */
|
|
118
243
|
runId?: string;
|