@proveanything/smartlinks 2.0.57 → 2.0.60
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/integrations.d.ts +17 -2
- package/dist/api/integrations.js +20 -1
- package/dist/docs/API_SUMMARY.md +76 -11
- package/dist/docs/app-manifest.md +1 -1
- package/dist/docs/connectors.md +72 -30
- package/dist/docs/integrations.md +8 -0
- package/dist/docs/site-seo.md +69 -68
- package/dist/docs/websites.md +91 -0
- package/dist/openapi.yaml +108 -12
- package/dist/site.d.ts +32 -4
- package/dist/site.js +48 -5
- package/dist/types/appManifest.d.ts +55 -10
- package/dist/types/integrations.d.ts +31 -3
- package/docs/API_SUMMARY.md +76 -11
- package/docs/app-manifest.md +1 -1
- package/docs/connectors.md +72 -30
- package/docs/integrations.md +8 -0
- package/docs/site-seo.md +69 -68
- package/docs/websites.md +91 -0
- package/openapi.yaml +108 -12
- package/package.json +1 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Websites: server-rendered on the SmartLinks site runtime
|
|
2
|
+
|
|
3
|
+
A website built on SmartLinks is an app whose public side is a **server-rendered site** at its own
|
|
4
|
+
address: an automatic `*.smartlinks.host` name, a chosen name, or the business's own domain. Every
|
|
5
|
+
page arrives as finished HTML, so search engines, AI crawlers and link previews see the real
|
|
6
|
+
content, and visitors see it before any JavaScript runs.
|
|
7
|
+
|
|
8
|
+
## How a request is served
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
visitor / crawler / agent
|
|
12
|
+
└─► SmartLinks site router (edge)
|
|
13
|
+
├─ /robots.txt, /sitemap.xml, /llms.txt made by the platform (site-seo.md)
|
|
14
|
+
├─ a file in your build (has an extension) served from the build, cached at the edge
|
|
15
|
+
└─ anything else your site's server renders it
|
|
16
|
+
• runs in an isolated worker, close to the SmartLinks API
|
|
17
|
+
• may call the SmartLinks API and the hosts you declare, nothing else
|
|
18
|
+
• request header x-sl-site says which site it is (SL.site.fromRequest)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The router also adds the canonical or noindex header, the security headers, and
|
|
22
|
+
`window.__SMARTLINKS_SITE__` to every HTML page (read in the browser with `SL.site.current()` or
|
|
23
|
+
`SL.readContext()`).
|
|
24
|
+
|
|
25
|
+
## The build
|
|
26
|
+
|
|
27
|
+
The website template (TanStack Start) produces this; you don't write it by hand.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
dist/
|
|
31
|
+
├── index.html? / assets/… the client build (hashed files: cached for a year)
|
|
32
|
+
├── sitemap-paths.txt your pages, "<path> [Title]" per line (site-seo.md)
|
|
33
|
+
├── _server/ the server bundle: one ES module entry + its chunks
|
|
34
|
+
│ └── index.mjs exports default { fetch(request, env, ctx) } (WinterTC)
|
|
35
|
+
└── app.manifest.json
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Manifest:
|
|
39
|
+
|
|
40
|
+
```jsonc
|
|
41
|
+
"site": {
|
|
42
|
+
"server": {
|
|
43
|
+
"main": "_server/index.mjs", // the server entry, inside the build
|
|
44
|
+
"compatibilityFlags": ["nodejs_compat"] // allowed: nodejs_compat, nodejs_compat_v2, nodejs_als
|
|
45
|
+
},
|
|
46
|
+
"network": ["api.example-weather.com"] // other hosts the server may call (optional)
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
On publish, the server folder becomes the site's server for that channel. Promote copies the same
|
|
51
|
+
bytes. `_server/` is never served to visitors.
|
|
52
|
+
|
|
53
|
+
## The server's rules
|
|
54
|
+
|
|
55
|
+
- **Which site:** `const site = SL.site.fromRequest(request)` gives `{ host, collectionId, appId,
|
|
56
|
+
channel, canonicalHost }`. One build serves every site it's installed on, so always take the
|
|
57
|
+
collection from here, never from a constant.
|
|
58
|
+
- **Data:** call the SmartLinks API with the SDK as you would in the browser. The server reads what
|
|
59
|
+
an anonymous visitor could read on that site, and only that collection. It holds no keys and no
|
|
60
|
+
signed-in user.
|
|
61
|
+
- **Writes** (contact forms, entries, sign-ups) go to your app's **server functions**
|
|
62
|
+
(server-functions.md), called from the browser as today. Their capabilities, secrets and limits
|
|
63
|
+
apply.
|
|
64
|
+
- **Other hosts:** only those in `site.network`. Anything else is refused (`403 Blocked`).
|
|
65
|
+
- **Limits per request:** a CPU-time budget and a cap on outgoing calls. A page that needs more is
|
|
66
|
+
doing work that belongs in a server function or a scheduled job.
|
|
67
|
+
- **No secrets** in the server bundle and no Node built-ins beyond `nodejs_compat`.
|
|
68
|
+
|
|
69
|
+
## Caching
|
|
70
|
+
|
|
71
|
+
The router keeps a rendered page at the edge when your response says it may:
|
|
72
|
+
|
|
73
|
+
| Your response | At the edge |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `Cache-Control: public, s-maxage=300` (the template's default for pages) | kept up to 5 minutes |
|
|
76
|
+
| `private`, `no-store`, `no-cache`, or a `Set-Cookie` | never kept |
|
|
77
|
+
| no `Cache-Control` | never kept |
|
|
78
|
+
|
|
79
|
+
Cached pages are keyed by the release and the collection's **content generation**: when products,
|
|
80
|
+
public app records, app settings or the collection change, the generation moves and every page of
|
|
81
|
+
the collection's sites re-renders on its next visit (within about 30 seconds). Publishing a release
|
|
82
|
+
does the same. Tracking parameters (`utm_*`, `gclid`, `fbclid`…) don't create separate entries.
|
|
83
|
+
|
|
84
|
+
Keep pages the same for every visitor. Anything personal (a signed-in owner's products, a basket)
|
|
85
|
+
renders in the browser after load.
|
|
86
|
+
|
|
87
|
+
## Locally
|
|
88
|
+
|
|
89
|
+
`npm run dev` runs the site with your dev collection (`.env`: `VITE_SL_COLLECTION_ID`), because
|
|
90
|
+
there's no router to say which site it is: `SL.site.fromRequest()` is null, so fall back to it.
|
|
91
|
+
Forge's preview runs the same server code the site runtime does.
|
package/dist/openapi.yaml
CHANGED
|
@@ -19766,7 +19766,9 @@ components:
|
|
|
19766
19766
|
type: string
|
|
19767
19767
|
sample:
|
|
19768
19768
|
type: string
|
|
19769
|
-
|
|
19769
|
+
shape:
|
|
19770
|
+
type: string
|
|
19771
|
+
serialize:
|
|
19770
19772
|
type: string
|
|
19771
19773
|
send:
|
|
19772
19774
|
type: string
|
|
@@ -19778,6 +19780,11 @@ components:
|
|
|
19778
19780
|
$ref: "#/components/schemas/ConnectorPanel"
|
|
19779
19781
|
aiGuide:
|
|
19780
19782
|
type: string
|
|
19783
|
+
recordId:
|
|
19784
|
+
type: string
|
|
19785
|
+
limits:
|
|
19786
|
+
type: object
|
|
19787
|
+
additionalProperties: true
|
|
19781
19788
|
required:
|
|
19782
19789
|
- provider
|
|
19783
19790
|
- directions
|
|
@@ -19860,7 +19867,7 @@ components:
|
|
|
19860
19867
|
type: string
|
|
19861
19868
|
required:
|
|
19862
19869
|
- records
|
|
19863
|
-
|
|
19870
|
+
ConnectorShapeInput:
|
|
19864
19871
|
type: object
|
|
19865
19872
|
properties:
|
|
19866
19873
|
installation:
|
|
@@ -19870,15 +19877,40 @@ components:
|
|
|
19870
19877
|
items:
|
|
19871
19878
|
type: object
|
|
19872
19879
|
additionalProperties: true
|
|
19873
|
-
|
|
19880
|
+
required:
|
|
19881
|
+
- installation
|
|
19882
|
+
- records
|
|
19883
|
+
ConnectorShapeResult:
|
|
19884
|
+
type: object
|
|
19885
|
+
properties:
|
|
19886
|
+
documents:
|
|
19874
19887
|
type: array
|
|
19875
19888
|
items:
|
|
19876
|
-
|
|
19889
|
+
type: object
|
|
19890
|
+
additionalProperties: true
|
|
19891
|
+
skipped:
|
|
19892
|
+
type: array
|
|
19893
|
+
items:
|
|
19894
|
+
type: object
|
|
19895
|
+
additionalProperties: true
|
|
19896
|
+
required:
|
|
19897
|
+
- documents
|
|
19898
|
+
ConnectorSerializeInput:
|
|
19899
|
+
type: object
|
|
19900
|
+
properties:
|
|
19901
|
+
installation:
|
|
19902
|
+
$ref: "#/components/schemas/ConnectorInstallation"
|
|
19903
|
+
documents:
|
|
19904
|
+
type: array
|
|
19905
|
+
items:
|
|
19906
|
+
type: object
|
|
19907
|
+
additionalProperties: true
|
|
19908
|
+
eventType:
|
|
19909
|
+
type: string
|
|
19877
19910
|
required:
|
|
19878
19911
|
- installation
|
|
19879
|
-
-
|
|
19880
|
-
|
|
19881
|
-
ConnectorTransformResult:
|
|
19912
|
+
- documents
|
|
19913
|
+
ConnectorSerializeResult:
|
|
19882
19914
|
type: object
|
|
19883
19915
|
properties:
|
|
19884
19916
|
payloads:
|
|
@@ -19906,12 +19938,26 @@ components:
|
|
|
19906
19938
|
type: object
|
|
19907
19939
|
properties:
|
|
19908
19940
|
results:
|
|
19909
|
-
type:
|
|
19910
|
-
|
|
19911
|
-
|
|
19912
|
-
|
|
19941
|
+
type: object
|
|
19942
|
+
additionalProperties: true
|
|
19943
|
+
index:
|
|
19944
|
+
type: number
|
|
19945
|
+
ok:
|
|
19946
|
+
type: boolean
|
|
19947
|
+
status:
|
|
19948
|
+
type: number
|
|
19949
|
+
error:
|
|
19950
|
+
type: string
|
|
19951
|
+
externalId:
|
|
19952
|
+
type: string
|
|
19953
|
+
retryable:
|
|
19954
|
+
type: boolean
|
|
19955
|
+
retryAfterSeconds:
|
|
19956
|
+
type: number
|
|
19913
19957
|
required:
|
|
19914
19958
|
- results
|
|
19959
|
+
- index
|
|
19960
|
+
- ok
|
|
19915
19961
|
ConnectorFetchInput:
|
|
19916
19962
|
type: object
|
|
19917
19963
|
properties:
|
|
@@ -26251,6 +26297,12 @@ components:
|
|
|
26251
26297
|
type: string
|
|
26252
26298
|
totalSynced:
|
|
26253
26299
|
type: number
|
|
26300
|
+
circuitOpenedAt:
|
|
26301
|
+
type: string
|
|
26302
|
+
circuitProbeAt:
|
|
26303
|
+
type: string
|
|
26304
|
+
circuitReason:
|
|
26305
|
+
type: string
|
|
26254
26306
|
required:
|
|
26255
26307
|
- id
|
|
26256
26308
|
- orgId
|
|
@@ -26267,6 +26319,47 @@ components:
|
|
|
26267
26319
|
- createdBy
|
|
26268
26320
|
- createdAt
|
|
26269
26321
|
- updatedAt
|
|
26322
|
+
FailedRecord:
|
|
26323
|
+
type: object
|
|
26324
|
+
properties:
|
|
26325
|
+
id:
|
|
26326
|
+
type: string
|
|
26327
|
+
entityId:
|
|
26328
|
+
type: string
|
|
26329
|
+
externalId:
|
|
26330
|
+
type: string
|
|
26331
|
+
status:
|
|
26332
|
+
type: string
|
|
26333
|
+
enum:
|
|
26334
|
+
- failed
|
|
26335
|
+
- dead
|
|
26336
|
+
attempts:
|
|
26337
|
+
type: number
|
|
26338
|
+
nextAttemptAt:
|
|
26339
|
+
type: string
|
|
26340
|
+
lastError:
|
|
26341
|
+
type: string
|
|
26342
|
+
updatedAt:
|
|
26343
|
+
type: string
|
|
26344
|
+
required:
|
|
26345
|
+
- id
|
|
26346
|
+
- status
|
|
26347
|
+
- attempts
|
|
26348
|
+
FailedRecordList:
|
|
26349
|
+
type: object
|
|
26350
|
+
properties:
|
|
26351
|
+
direction:
|
|
26352
|
+
$ref: "#/components/schemas/FlowDirection"
|
|
26353
|
+
items:
|
|
26354
|
+
type: array
|
|
26355
|
+
items:
|
|
26356
|
+
$ref: "#/components/schemas/FailedRecord"
|
|
26357
|
+
nextCursor:
|
|
26358
|
+
type: string
|
|
26359
|
+
required:
|
|
26360
|
+
- direction
|
|
26361
|
+
- items
|
|
26362
|
+
- nextCursor
|
|
26270
26363
|
CreateFlowInput:
|
|
26271
26364
|
type: object
|
|
26272
26365
|
properties:
|
|
@@ -26338,6 +26431,8 @@ components:
|
|
|
26338
26431
|
type: number
|
|
26339
26432
|
skipped:
|
|
26340
26433
|
type: number
|
|
26434
|
+
held:
|
|
26435
|
+
type: number
|
|
26341
26436
|
status:
|
|
26342
26437
|
$ref: "#/components/schemas/RunStatus"
|
|
26343
26438
|
runId:
|
|
@@ -26734,7 +26829,8 @@ components:
|
|
|
26734
26829
|
- testConnection
|
|
26735
26830
|
- describeTarget
|
|
26736
26831
|
- sample
|
|
26737
|
-
-
|
|
26832
|
+
- shape
|
|
26833
|
+
- serialize
|
|
26738
26834
|
RunTrigger:
|
|
26739
26835
|
type: string
|
|
26740
26836
|
enum:
|
package/dist/site.d.ts
CHANGED
|
@@ -1,10 +1,38 @@
|
|
|
1
1
|
declare global {
|
|
2
2
|
var __SL_READY__: boolean | undefined;
|
|
3
|
+
var __SMARTLINKS_SITE__: SiteContext | undefined;
|
|
3
4
|
}
|
|
5
|
+
/** The site a page is being served as. */
|
|
6
|
+
export interface SiteContext {
|
|
7
|
+
/** The hostname this request arrived on (an automatic address, a chosen name or a custom domain). */
|
|
8
|
+
host: string;
|
|
9
|
+
/** The collection the site belongs to: pass it to every SDK call. */
|
|
10
|
+
collectionId: string;
|
|
11
|
+
appId: string;
|
|
12
|
+
/** The release channel being served ('stable' on a live address; 'dev' etc. on a test address). */
|
|
13
|
+
channel: string;
|
|
14
|
+
/** Where search engines consolidate the site (custom domain > chosen name > automatic address). */
|
|
15
|
+
canonicalHost: string;
|
|
16
|
+
}
|
|
17
|
+
type HeaderSource = {
|
|
18
|
+
headers: Headers | Record<string, string | string[] | undefined>;
|
|
19
|
+
} | Headers;
|
|
20
|
+
/**
|
|
21
|
+
* On the server: the site this request is rendering. The SmartLinks site router sets it on every
|
|
22
|
+
* request it hands to your site's server (and drops any value a visitor sends). Null outside the
|
|
23
|
+
* site runtime, e.g. in local development without the router: fall back to your dev collection.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // TanStack Start server function / loader
|
|
27
|
+
* const site = SL.site.fromRequest(getRequest())
|
|
28
|
+
* const products = await SL.product.list(site!.collectionId)
|
|
29
|
+
*/
|
|
30
|
+
export declare function fromRequest(request: HeaderSource): SiteContext | null;
|
|
31
|
+
/** In the browser: the site the page is served as (the router puts it in the page). Null elsewhere. */
|
|
32
|
+
export declare function current(): SiteContext | null;
|
|
4
33
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* without it, it waits for the network to go idle (capped). Call once per page, after your data
|
|
8
|
-
* renders. Harmless anywhere else.
|
|
34
|
+
* @deprecated Sites are server-rendered: crawlers get the finished page from the server, so nothing
|
|
35
|
+
* waits for this signal any more. Kept as a harmless no-op signal for older pages.
|
|
9
36
|
*/
|
|
10
37
|
export declare function ready(): void;
|
|
38
|
+
export {};
|
package/dist/site.js
CHANGED
|
@@ -1,11 +1,54 @@
|
|
|
1
1
|
// =============================================================================
|
|
2
|
-
// site — for apps served as websites.
|
|
2
|
+
// site — for apps served as websites (server-rendered on the SmartLinks site runtime).
|
|
3
3
|
// =============================================================================
|
|
4
|
+
function headerOf(source, name) {
|
|
5
|
+
var _a, _b;
|
|
6
|
+
const headers = typeof Headers !== 'undefined' && source instanceof Headers ? source : source.headers;
|
|
7
|
+
if (!headers)
|
|
8
|
+
return null;
|
|
9
|
+
if (typeof headers.get === 'function')
|
|
10
|
+
return headers.get(name);
|
|
11
|
+
const v = (_a = headers[name]) !== null && _a !== void 0 ? _a : headers[name.toLowerCase()];
|
|
12
|
+
return Array.isArray(v) ? (_b = v[0]) !== null && _b !== void 0 ? _b : null : v !== null && v !== void 0 ? v : null;
|
|
13
|
+
}
|
|
14
|
+
function parse(raw) {
|
|
15
|
+
try {
|
|
16
|
+
const v = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
17
|
+
if (!v || typeof v !== 'object' || typeof v.collectionId !== 'string')
|
|
18
|
+
return null;
|
|
19
|
+
const s = v;
|
|
20
|
+
return {
|
|
21
|
+
host: String(s.host || ''),
|
|
22
|
+
collectionId: String(s.collectionId),
|
|
23
|
+
appId: String(s.appId || ''),
|
|
24
|
+
channel: String(s.channel || 'stable'),
|
|
25
|
+
canonicalHost: String(s.canonicalHost || s.host || ''),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
catch (_a) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* On the server: the site this request is rendering. The SmartLinks site router sets it on every
|
|
34
|
+
* request it hands to your site's server (and drops any value a visitor sends). Null outside the
|
|
35
|
+
* site runtime, e.g. in local development without the router: fall back to your dev collection.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // TanStack Start server function / loader
|
|
39
|
+
* const site = SL.site.fromRequest(getRequest())
|
|
40
|
+
* const products = await SL.product.list(site!.collectionId)
|
|
41
|
+
*/
|
|
42
|
+
export function fromRequest(request) {
|
|
43
|
+
return parse(headerOf(request, 'x-sl-site'));
|
|
44
|
+
}
|
|
45
|
+
/** In the browser: the site the page is served as (the router puts it in the page). Null elsewhere. */
|
|
46
|
+
export function current() {
|
|
47
|
+
return parse(globalThis.__SMARTLINKS_SITE__);
|
|
48
|
+
}
|
|
4
49
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* without it, it waits for the network to go idle (capped). Call once per page, after your data
|
|
8
|
-
* renders. Harmless anywhere else.
|
|
50
|
+
* @deprecated Sites are server-rendered: crawlers get the finished page from the server, so nothing
|
|
51
|
+
* waits for this signal any more. Kept as a harmless no-op signal for older pages.
|
|
9
52
|
*/
|
|
10
53
|
export function ready() {
|
|
11
54
|
if (typeof globalThis === 'undefined')
|
|
@@ -2,7 +2,6 @@
|
|
|
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 { FieldMapping } from './integrations.js';
|
|
6
5
|
/**
|
|
7
6
|
* A bundle (widget or container) as returned by the collection widgets endpoint.
|
|
8
7
|
*
|
|
@@ -831,7 +830,9 @@ export interface AppManifest {
|
|
|
831
830
|
/**
|
|
832
831
|
* This app is an integration CONNECTOR for one external provider (Syndigo, OneWorldSync, …): the
|
|
833
832
|
* integrations console lists it, draws its connect form and settings, and the integration engine
|
|
834
|
-
* calls its server functions to test, describe, sample,
|
|
833
|
+
* calls its server functions to test, describe, sample, shape, serialize, send and fetch. The
|
|
834
|
+
* customer's mapping is NOT the connector's job: core maps between the connector's provider-neutral
|
|
835
|
+
* documents and SmartLinks (docs/connectors.md).
|
|
835
836
|
* @see AppConnectorDeclaration · docs/connectors.md
|
|
836
837
|
*/
|
|
837
838
|
connector?: AppConnectorDeclaration;
|
|
@@ -877,6 +878,9 @@ export interface ConnectorPanel {
|
|
|
877
878
|
* The connector contract. Every function named here is one of this app's server functions
|
|
878
879
|
* (manifest `functions.definitions`); the engine calls it with collection authority, capped by the
|
|
879
880
|
* capabilities the function declares. A connector fills in only what it needs.
|
|
881
|
+
*
|
|
882
|
+
* A connector is an ADAPTER: it talks to the provider and converts between the provider's format and
|
|
883
|
+
* a clean, provider-neutral document. Each customer's mapping, routing and rules live in core.
|
|
880
884
|
*/
|
|
881
885
|
export interface AppConnectorDeclaration {
|
|
882
886
|
/** Stable provider key, e.g. 'syndigo', 'oneworldsync'. */
|
|
@@ -892,6 +896,7 @@ export interface AppConnectorDeclaration {
|
|
|
892
896
|
};
|
|
893
897
|
/** Per-installation settings: a JSON Schema the console renders as a form. */
|
|
894
898
|
settings?: Record<string, unknown>;
|
|
899
|
+
/** Ready-made flows; a preset's `flow.config.fieldMappings` is a suggested mapping the customer can edit. */
|
|
895
900
|
presets?: ConnectorPreset[];
|
|
896
901
|
/** Names of this app's server functions implementing each step. */
|
|
897
902
|
functions: {
|
|
@@ -901,8 +906,18 @@ export interface AppConnectorDeclaration {
|
|
|
901
906
|
describeTarget?: string;
|
|
902
907
|
/** → ConnectorRecords: real records for the mapper, the AI assistant and dry runs (inbound). */
|
|
903
908
|
sample?: string;
|
|
904
|
-
/**
|
|
905
|
-
|
|
909
|
+
/**
|
|
910
|
+
* Inbound: (ConnectorShapeInput) → ConnectorShapeResult. Provider records → provider-neutral
|
|
911
|
+
* documents, the same for every customer (no customer mapping). Deterministic — no AI, network or
|
|
912
|
+
* clock. Omit to hand core the provider records as they are.
|
|
913
|
+
*/
|
|
914
|
+
shape?: string;
|
|
915
|
+
/**
|
|
916
|
+
* Outbound: (ConnectorSerializeInput) → ConnectorSerializeResult. Documents core has mapped to this
|
|
917
|
+
* provider's fields (`describeTarget` paths) → the provider's wire format (an envelope, a batch).
|
|
918
|
+
* Deterministic. Omit to send the mapped documents as they are.
|
|
919
|
+
*/
|
|
920
|
+
serialize?: string;
|
|
906
921
|
/** (ConnectorSendInput) → ConnectorSendResult. Omit to use a generic transport (HTTP, Event Hub). */
|
|
907
922
|
send?: string;
|
|
908
923
|
/** (ConnectorFetchInput) → ConnectorRecords: inbound pull, one page per call. */
|
|
@@ -911,6 +926,16 @@ export interface AppConnectorDeclaration {
|
|
|
911
926
|
panels?: ConnectorPanel[];
|
|
912
927
|
/** Path (under the app's public files) of a guide the AI mapping assistant reads. */
|
|
913
928
|
aiGuide?: string;
|
|
929
|
+
/**
|
|
930
|
+
* Path of the provider's own id in a fetched record (e.g. 'gtin', 'item.id'). Inbound records are
|
|
931
|
+
* landed and matched to SmartLinks entities by it, so a re-import updates rather than duplicates.
|
|
932
|
+
* Default 'id'; a flow can override it with `config.upsert.externalIdPath`.
|
|
933
|
+
*/
|
|
934
|
+
recordId?: string;
|
|
935
|
+
/** Requests per second toward the provider, shared by every SmartLinks worker. */
|
|
936
|
+
limits?: {
|
|
937
|
+
requestsPerSecond?: number;
|
|
938
|
+
};
|
|
914
939
|
}
|
|
915
940
|
/**
|
|
916
941
|
* What every connector function receives about the installation it serves. The engine assembles it
|
|
@@ -951,15 +976,31 @@ export interface ConnectorRecords {
|
|
|
951
976
|
/** Inbound paging: pass back to the next fetch; absent when there's no more. */
|
|
952
977
|
nextCursor?: string | null;
|
|
953
978
|
}
|
|
954
|
-
|
|
979
|
+
/** Inbound: provider records, as fetch / sample returned them. */
|
|
980
|
+
export interface ConnectorShapeInput {
|
|
955
981
|
installation: ConnectorInstallation;
|
|
956
|
-
/** SmartLinks records (outbound) or provider records (inbound). */
|
|
957
982
|
records: Array<Record<string, unknown>>;
|
|
958
|
-
/** The flow's saved mapping (`config.fieldMappings`) — the same shape the engine's mapper runs. */
|
|
959
|
-
mapping: FieldMapping[];
|
|
960
983
|
}
|
|
961
|
-
export interface
|
|
962
|
-
/** One
|
|
984
|
+
export interface ConnectorShapeResult {
|
|
985
|
+
/** One provider-neutral document per record, in the same order (null = skip it, with a reason). */
|
|
986
|
+
documents: Array<Record<string, unknown> | null>;
|
|
987
|
+
skipped?: Array<{
|
|
988
|
+
index: number;
|
|
989
|
+
reason: string;
|
|
990
|
+
}>;
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Outbound: documents core has mapped to this provider's fields (keyed by `describeTarget` paths).
|
|
994
|
+
* A deleted SmartLinks entity arrives as `{ id, _deleted: true }`.
|
|
995
|
+
*/
|
|
996
|
+
export interface ConnectorSerializeInput {
|
|
997
|
+
installation: ConnectorInstallation;
|
|
998
|
+
documents: Array<Record<string, unknown>>;
|
|
999
|
+
/** The event that triggered the run, e.g. 'product.updated' (absent for manual / scheduled runs). */
|
|
1000
|
+
eventType?: string;
|
|
1001
|
+
}
|
|
1002
|
+
export interface ConnectorSerializeResult {
|
|
1003
|
+
/** One provider payload per document, in the same order (null = skip it, with a reason). */
|
|
963
1004
|
payloads: Array<unknown | null>;
|
|
964
1005
|
skipped?: Array<{
|
|
965
1006
|
index: number;
|
|
@@ -977,6 +1018,10 @@ export interface ConnectorSendResult {
|
|
|
977
1018
|
status?: number;
|
|
978
1019
|
error?: string;
|
|
979
1020
|
externalId?: string;
|
|
1021
|
+
/** Worth retrying? Default: by status (408/425/429/5xx/none yes; other 4xx no). */
|
|
1022
|
+
retryable?: boolean;
|
|
1023
|
+
/** The provider asked to wait this long before retrying (e.g. its Retry-After). */
|
|
1024
|
+
retryAfterSeconds?: number;
|
|
980
1025
|
}>;
|
|
981
1026
|
}
|
|
982
1027
|
export interface ConnectorFetchInput {
|
|
@@ -71,6 +71,32 @@ 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
|
+
export interface FailedRecordList {
|
|
97
|
+
direction: FlowDirection;
|
|
98
|
+
items: FailedRecord[];
|
|
99
|
+
nextCursor: string | null;
|
|
74
100
|
}
|
|
75
101
|
export interface CreateFlowInput {
|
|
76
102
|
appId: string;
|
|
@@ -109,10 +135,12 @@ export interface RunFlowSummary {
|
|
|
109
135
|
/** Outbound: records delivered. */
|
|
110
136
|
sent?: number;
|
|
111
137
|
failed: number;
|
|
112
|
-
/** Inbound: products created / updated, and records the connector skipped. */
|
|
138
|
+
/** Inbound: products created / updated, and records the connector skipped (outbound: unchanged). */
|
|
113
139
|
created?: number;
|
|
114
140
|
updated?: number;
|
|
115
141
|
skipped?: number;
|
|
142
|
+
/** Outbound: changes held because the destination is paused (circuit open); sent after it recovers. */
|
|
143
|
+
held?: number;
|
|
116
144
|
status: RunStatus;
|
|
117
145
|
/** Id of the persisted run row (deep-link into its detail). */
|
|
118
146
|
runId?: string;
|
|
@@ -141,8 +169,8 @@ export interface SaveConnectorInstallationInput {
|
|
|
141
169
|
connection?: Record<string, unknown>;
|
|
142
170
|
settings?: Record<string, unknown>;
|
|
143
171
|
}
|
|
144
|
-
/** Steps the console may run directly (send and fetch only run inside a flow). */
|
|
145
|
-
export type ConnectorConsoleStep = 'testConnection' | 'describeTarget' | 'sample' | '
|
|
172
|
+
/** Steps the console may run directly (send and fetch only run inside a flow; shape/serialize as dry runs). */
|
|
173
|
+
export type ConnectorConsoleStep = 'testConnection' | 'describeTarget' | 'sample' | 'shape' | 'serialize';
|
|
146
174
|
export type RunTrigger = 'manual' | 'event' | 'schedule' | 'test';
|
|
147
175
|
export type FlowRunStatus = 'running' | 'success' | 'partial' | 'failed' | 'skipped';
|
|
148
176
|
export type FlowRunItemStatus = 'sent' | 'failed' | 'skipped';
|