@proveanything/smartlinks 2.0.56 → 2.0.58
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 +22 -1
- package/dist/api/integrations.js +26 -0
- package/dist/docs/API_SUMMARY.md +216 -2
- package/dist/docs/app-manifest.md +7 -0
- package/dist/docs/connectors.md +241 -0
- package/dist/docs/integrations.md +4 -0
- package/dist/docs/overview.md +1 -0
- package/dist/docs/site-seo.md +69 -68
- package/dist/docs/websites.md +91 -0
- package/dist/openapi.yaml +358 -1
- package/dist/site.d.ts +32 -4
- package/dist/site.js +48 -5
- package/dist/types/appManifest.d.ts +186 -0
- package/dist/types/integrations.d.ts +43 -1
- package/docs/API_SUMMARY.md +216 -2
- package/docs/app-manifest.md +7 -0
- package/docs/connectors.md +241 -0
- package/docs/integrations.md +4 -0
- package/docs/overview.md +1 -0
- package/docs/site-seo.md +69 -68
- package/docs/websites.md +91 -0
- package/openapi.yaml +358 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IntegrationFlow, CreateFlowInput, UpdateFlowInput, ListFlowsQuery, FlowList, RunFlowInput, RunFlowResult, RunFlowSummary, RunFlowEnqueued, RecordTypesResponse, FlowRun, ListRunsQuery, RunList, ListRunItemsQuery, RunItemList, LoggingWindow, SetLoggingInput } 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 } 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>;
|
|
@@ -57,4 +57,25 @@ export declare namespace integrations {
|
|
|
57
57
|
function cancelLogging(collectionId: string, connectionId: string): Promise<{
|
|
58
58
|
canceled: boolean;
|
|
59
59
|
}>;
|
|
60
|
+
/** The collection's installed connector apps. GET /integrations/connectors */
|
|
61
|
+
function listConnectors(collectionId: string): Promise<{
|
|
62
|
+
connectors: InstalledConnector[];
|
|
63
|
+
}>;
|
|
64
|
+
/** Save a connector's non-secret Connect-form values and/or settings. PUT /integrations/connectors/:appId/installation */
|
|
65
|
+
function saveConnectorInstallation(collectionId: string, appId: string, input: SaveConnectorInstallationInput): Promise<{
|
|
66
|
+
installation: {
|
|
67
|
+
connection: Record<string, unknown>;
|
|
68
|
+
settings: Record<string, unknown>;
|
|
69
|
+
};
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Run a connector step from the console: `testConnection` → ConnectorTestResult, `describeTarget` →
|
|
73
|
+
* ConnectorTargetSchema, `sample` → ConnectorRecords, and dry runs of `shape` ({ records }) →
|
|
74
|
+
* ConnectorShapeResult and `serialize` ({ documents }) → ConnectorSerializeResult.
|
|
75
|
+
* The server adds the installation; pass `flowId` to layer that flow's settings.
|
|
76
|
+
* POST /integrations/connectors/:appId/steps/:step
|
|
77
|
+
*/
|
|
78
|
+
function runConnectorStep<T = unknown>(collectionId: string, appId: string, step: ConnectorConsoleStep, input?: Record<string, unknown> & {
|
|
79
|
+
flowId?: string;
|
|
80
|
+
}): Promise<T>;
|
|
60
81
|
}
|
package/dist/api/integrations.js
CHANGED
|
@@ -135,4 +135,30 @@ export var integrations;
|
|
|
135
135
|
return del(`/admin/collection/${enc(collectionId)}/integrations/logging/${enc(connectionId)}`);
|
|
136
136
|
}
|
|
137
137
|
integrations.cancelLogging = cancelLogging;
|
|
138
|
+
// --- Connectors (docs/connectors.md) ------------------------------------
|
|
139
|
+
// Connector apps installed on the collection: their declared contract, the installation's
|
|
140
|
+
// non-secret settings, and which secrets are set. Secrets themselves go through
|
|
141
|
+
// `secrets.put(collectionId, name, value, { appId })` for the connector's appId.
|
|
142
|
+
const connectorsBase = (collectionId) => `/admin/collection/${enc(collectionId)}/integrations/connectors`;
|
|
143
|
+
/** The collection's installed connector apps. GET /integrations/connectors */
|
|
144
|
+
async function listConnectors(collectionId) {
|
|
145
|
+
return request(connectorsBase(collectionId));
|
|
146
|
+
}
|
|
147
|
+
integrations.listConnectors = listConnectors;
|
|
148
|
+
/** Save a connector's non-secret Connect-form values and/or settings. PUT /integrations/connectors/:appId/installation */
|
|
149
|
+
async function saveConnectorInstallation(collectionId, appId, input) {
|
|
150
|
+
return put(`${connectorsBase(collectionId)}/${enc(appId)}/installation`, input);
|
|
151
|
+
}
|
|
152
|
+
integrations.saveConnectorInstallation = saveConnectorInstallation;
|
|
153
|
+
/**
|
|
154
|
+
* Run a connector step from the console: `testConnection` → ConnectorTestResult, `describeTarget` →
|
|
155
|
+
* ConnectorTargetSchema, `sample` → ConnectorRecords, and dry runs of `shape` ({ records }) →
|
|
156
|
+
* ConnectorShapeResult and `serialize` ({ documents }) → ConnectorSerializeResult.
|
|
157
|
+
* The server adds the installation; pass `flowId` to layer that flow's settings.
|
|
158
|
+
* POST /integrations/connectors/:appId/steps/:step
|
|
159
|
+
*/
|
|
160
|
+
async function runConnectorStep(collectionId, appId, step, input = {}) {
|
|
161
|
+
return post(`${connectorsBase(collectionId)}/${enc(appId)}/steps/${enc(step)}`, input);
|
|
162
|
+
}
|
|
163
|
+
integrations.runConnectorStep = runConnectorStep;
|
|
138
164
|
})(integrations || (integrations = {}));
|
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.58 | Generated: 2026-10-13T14:08:01.791Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -65,6 +65,7 @@ For detailed guides on specific features:
|
|
|
65
65
|
- **[Analytics Metadata Conventions](analytics-metadata-conventions.md)** - Standard recommended keys and conventions for analytics metadata fields
|
|
66
66
|
- **[Loyalty: Points, Members & Earning Rules](loyalty.md)** - Loyalty schemes, automatic point earning via interaction rules, member balances, transaction history, and manual adjustments
|
|
67
67
|
- **[Integrations](integrations.md)** - Inbound/outbound integration flows + the sealed-secret store; triggers (manual/event/schedule), field mappings, and the Syndigo/Event Hub outbound path
|
|
68
|
+
- **[Connectors](connectors.md)** - Connector apps (adapters for one external provider): a manifest `connector` block + server functions (testConnection/describeTarget/sample/fetch/shape/serialize/send); core does the mapping
|
|
68
69
|
- **[AI Tools & Skills](ai-tools-and-skills.md)** - The AI capability catalog: skills apps invoke by name (e.g. research.brand), the tools the AI reaches for (web fetch/screenshot/brand assets/image gen), the agent loop, and how apps discover them
|
|
69
70
|
- **[Deep Link Discovery](deep-link-discovery.md)** - Registering and discovering navigable app states for portal menus and AI orchestration
|
|
70
71
|
- **[AI Guide Template](ai-guide-template.md)** - A sample for an app on how to build an AI setup guide
|
|
@@ -2853,10 +2854,173 @@ interface AppManifest {
|
|
|
2853
2854
|
realtime?: { channels: RealtimeChannelDeclaration[] };
|
|
2854
2855
|
data?: AppDataDeclaration;
|
|
2855
2856
|
headless?: AppHeadlessDeclaration;
|
|
2857
|
+
connector?: AppConnectorDeclaration;
|
|
2856
2858
|
[key: string]: any;
|
|
2857
2859
|
}
|
|
2858
2860
|
```
|
|
2859
2861
|
|
|
2862
|
+
**ConnectorConnectionField** (interface)
|
|
2863
|
+
```typescript
|
|
2864
|
+
interface ConnectorConnectionField {
|
|
2865
|
+
name: string
|
|
2866
|
+
label: string
|
|
2867
|
+
description?: string
|
|
2868
|
+
secret?: string
|
|
2869
|
+
required?: boolean
|
|
2870
|
+
type?: 'text' | 'url' | 'select' | 'number' | 'boolean'
|
|
2871
|
+
options?: Array<{ value: string; label: string }>
|
|
2872
|
+
placeholder?: string
|
|
2873
|
+
}
|
|
2874
|
+
```
|
|
2875
|
+
|
|
2876
|
+
**ConnectorPreset** (interface)
|
|
2877
|
+
```typescript
|
|
2878
|
+
interface ConnectorPreset {
|
|
2879
|
+
id: string
|
|
2880
|
+
title: string
|
|
2881
|
+
description?: string
|
|
2882
|
+
direction: 'inbound' | 'outbound'
|
|
2883
|
+
entity: string
|
|
2884
|
+
flow?: Record<string, unknown>
|
|
2885
|
+
}
|
|
2886
|
+
```
|
|
2887
|
+
|
|
2888
|
+
**ConnectorPanel** (interface)
|
|
2889
|
+
```typescript
|
|
2890
|
+
interface ConnectorPanel {
|
|
2891
|
+
id: string
|
|
2892
|
+
title: string
|
|
2893
|
+
component: string
|
|
2894
|
+
}
|
|
2895
|
+
```
|
|
2896
|
+
|
|
2897
|
+
**AppConnectorDeclaration** (interface)
|
|
2898
|
+
```typescript
|
|
2899
|
+
interface AppConnectorDeclaration {
|
|
2900
|
+
provider: string
|
|
2901
|
+
title?: string
|
|
2902
|
+
directions: Array<'inbound' | 'outbound'>
|
|
2903
|
+
entities: string[]
|
|
2904
|
+
connection?: { fields: ConnectorConnectionField[] }
|
|
2905
|
+
settings?: Record<string, unknown>
|
|
2906
|
+
presets?: ConnectorPreset[]
|
|
2907
|
+
functions: {
|
|
2908
|
+
testConnection?: string
|
|
2909
|
+
describeTarget?: string
|
|
2910
|
+
sample?: string
|
|
2911
|
+
shape?: string
|
|
2912
|
+
serialize?: string
|
|
2913
|
+
send?: string
|
|
2914
|
+
fetch?: string
|
|
2915
|
+
}
|
|
2916
|
+
panels?: ConnectorPanel[]
|
|
2917
|
+
aiGuide?: string
|
|
2918
|
+
}
|
|
2919
|
+
```
|
|
2920
|
+
|
|
2921
|
+
**ConnectorInstallation** (interface)
|
|
2922
|
+
```typescript
|
|
2923
|
+
interface ConnectorInstallation {
|
|
2924
|
+
collectionId: string
|
|
2925
|
+
appId: string
|
|
2926
|
+
flowId?: string | null
|
|
2927
|
+
connection: Record<string, unknown>
|
|
2928
|
+
settings: Record<string, unknown>
|
|
2929
|
+
}
|
|
2930
|
+
```
|
|
2931
|
+
|
|
2932
|
+
**ConnectorTestResult** (interface)
|
|
2933
|
+
```typescript
|
|
2934
|
+
interface ConnectorTestResult {
|
|
2935
|
+
ok: boolean
|
|
2936
|
+
message: string
|
|
2937
|
+
}
|
|
2938
|
+
```
|
|
2939
|
+
|
|
2940
|
+
**ConnectorTargetField** (interface)
|
|
2941
|
+
```typescript
|
|
2942
|
+
interface ConnectorTargetField {
|
|
2943
|
+
path: string
|
|
2944
|
+
label?: string
|
|
2945
|
+
type?: 'string' | 'number' | 'boolean' | 'date' | 'object' | 'array'
|
|
2946
|
+
required?: boolean
|
|
2947
|
+
description?: string
|
|
2948
|
+
enum?: string[]
|
|
2949
|
+
}
|
|
2950
|
+
```
|
|
2951
|
+
|
|
2952
|
+
**ConnectorTargetSchema** (interface)
|
|
2953
|
+
```typescript
|
|
2954
|
+
interface ConnectorTargetSchema {
|
|
2955
|
+
fields: ConnectorTargetField[]
|
|
2956
|
+
}
|
|
2957
|
+
```
|
|
2958
|
+
|
|
2959
|
+
**ConnectorRecords** (interface)
|
|
2960
|
+
```typescript
|
|
2961
|
+
interface ConnectorRecords {
|
|
2962
|
+
records: Array<Record<string, unknown>>
|
|
2963
|
+
nextCursor?: string | null
|
|
2964
|
+
}
|
|
2965
|
+
```
|
|
2966
|
+
|
|
2967
|
+
**ConnectorShapeInput** (interface)
|
|
2968
|
+
```typescript
|
|
2969
|
+
interface ConnectorShapeInput {
|
|
2970
|
+
installation: ConnectorInstallation
|
|
2971
|
+
records: Array<Record<string, unknown>>
|
|
2972
|
+
}
|
|
2973
|
+
```
|
|
2974
|
+
|
|
2975
|
+
**ConnectorShapeResult** (interface)
|
|
2976
|
+
```typescript
|
|
2977
|
+
interface ConnectorShapeResult {
|
|
2978
|
+
documents: Array<Record<string, unknown> | null>
|
|
2979
|
+
skipped?: Array<{ index: number; reason: string }>
|
|
2980
|
+
}
|
|
2981
|
+
```
|
|
2982
|
+
|
|
2983
|
+
**ConnectorSerializeInput** (interface)
|
|
2984
|
+
```typescript
|
|
2985
|
+
interface ConnectorSerializeInput {
|
|
2986
|
+
installation: ConnectorInstallation
|
|
2987
|
+
documents: Array<Record<string, unknown>>
|
|
2988
|
+
eventType?: string
|
|
2989
|
+
}
|
|
2990
|
+
```
|
|
2991
|
+
|
|
2992
|
+
**ConnectorSerializeResult** (interface)
|
|
2993
|
+
```typescript
|
|
2994
|
+
interface ConnectorSerializeResult {
|
|
2995
|
+
payloads: Array<unknown | null>
|
|
2996
|
+
skipped?: Array<{ index: number; reason: string }>
|
|
2997
|
+
}
|
|
2998
|
+
```
|
|
2999
|
+
|
|
3000
|
+
**ConnectorSendInput** (interface)
|
|
3001
|
+
```typescript
|
|
3002
|
+
interface ConnectorSendInput {
|
|
3003
|
+
installation: ConnectorInstallation
|
|
3004
|
+
payloads: unknown[]
|
|
3005
|
+
}
|
|
3006
|
+
```
|
|
3007
|
+
|
|
3008
|
+
**ConnectorSendResult** (interface)
|
|
3009
|
+
```typescript
|
|
3010
|
+
interface ConnectorSendResult {
|
|
3011
|
+
results: Array<{ index: number; ok: boolean; status?: number; error?: string; externalId?: string }>
|
|
3012
|
+
}
|
|
3013
|
+
```
|
|
3014
|
+
|
|
3015
|
+
**ConnectorFetchInput** (interface)
|
|
3016
|
+
```typescript
|
|
3017
|
+
interface ConnectorFetchInput {
|
|
3018
|
+
installation: ConnectorInstallation
|
|
3019
|
+
cursor?: string | null
|
|
3020
|
+
limit?: number
|
|
3021
|
+
}
|
|
3022
|
+
```
|
|
3023
|
+
|
|
2860
3024
|
**CollectionAppWidget** (interface)
|
|
2861
3025
|
```typescript
|
|
2862
3026
|
interface CollectionAppWidget {
|
|
@@ -6899,11 +7063,21 @@ interface FlowConnection {
|
|
|
6899
7063
|
}
|
|
6900
7064
|
```
|
|
6901
7065
|
|
|
7066
|
+
**FlowConnectorRef** (interface)
|
|
7067
|
+
```typescript
|
|
7068
|
+
interface FlowConnectorRef {
|
|
7069
|
+
appId: string
|
|
7070
|
+
preset?: string
|
|
7071
|
+
settings?: Record<string, unknown>
|
|
7072
|
+
}
|
|
7073
|
+
```
|
|
7074
|
+
|
|
6902
7075
|
**IntegrationFlowConfig** (interface)
|
|
6903
7076
|
```typescript
|
|
6904
7077
|
interface IntegrationFlowConfig {
|
|
6905
7078
|
connection?: FlowConnection
|
|
6906
7079
|
fieldMappings?: FieldMapping[]
|
|
7080
|
+
connector?: FlowConnectorRef
|
|
6907
7081
|
[key: string]: any
|
|
6908
7082
|
}
|
|
6909
7083
|
```
|
|
@@ -6982,13 +7156,37 @@ interface RunFlowSummary {
|
|
|
6982
7156
|
flowId: string
|
|
6983
7157
|
direction: FlowDirection
|
|
6984
7158
|
records: number
|
|
6985
|
-
sent
|
|
7159
|
+
sent?: number
|
|
6986
7160
|
failed: number
|
|
7161
|
+
created?: number
|
|
7162
|
+
updated?: number
|
|
7163
|
+
skipped?: number
|
|
6987
7164
|
status: RunStatus
|
|
6988
7165
|
runId?: string
|
|
6989
7166
|
}
|
|
6990
7167
|
```
|
|
6991
7168
|
|
|
7169
|
+
**InstalledConnector** (interface)
|
|
7170
|
+
```typescript
|
|
7171
|
+
interface InstalledConnector {
|
|
7172
|
+
appId: string
|
|
7173
|
+
name: string
|
|
7174
|
+
channel: string
|
|
7175
|
+
version: string | null
|
|
7176
|
+
connector: import('./appManifest').AppConnectorDeclaration
|
|
7177
|
+
installation: { connection: Record<string, unknown>; settings: Record<string, unknown> }
|
|
7178
|
+
secrets: Record<string, { set: boolean; hint?: string | null }>
|
|
7179
|
+
}
|
|
7180
|
+
```
|
|
7181
|
+
|
|
7182
|
+
**SaveConnectorInstallationInput** (interface)
|
|
7183
|
+
```typescript
|
|
7184
|
+
interface SaveConnectorInstallationInput {
|
|
7185
|
+
connection?: Record<string, unknown>
|
|
7186
|
+
settings?: Record<string, unknown>
|
|
7187
|
+
}
|
|
7188
|
+
```
|
|
7189
|
+
|
|
6992
7190
|
**CapturedRequest** (interface)
|
|
6993
7191
|
```typescript
|
|
6994
7192
|
interface CapturedRequest {
|
|
@@ -7193,6 +7391,8 @@ interface RecordTypesResponse {
|
|
|
7193
7391
|
|
|
7194
7392
|
**UpdateFlowInput** = `Partial<Omit<CreateFlowInput, 'direction'>> & {`
|
|
7195
7393
|
|
|
7394
|
+
**ConnectorConsoleStep** = `'testConnection' | 'describeTarget' | 'sample' | 'shape' | 'serialize'`
|
|
7395
|
+
|
|
7196
7396
|
**RunTrigger** = `'manual' | 'event' | 'schedule' | 'test'`
|
|
7197
7397
|
|
|
7198
7398
|
**FlowRunStatus** = `'running' | 'success' | 'partial' | 'failed' | 'skipped'`
|
|
@@ -11507,6 +11707,20 @@ Enable enhanced logging. POST /integrations/logging/:connectionId (omit ttlMinut
|
|
|
11507
11707
|
**cancelLogging**(collectionId: string, connectionId: string) → `Promise<`
|
|
11508
11708
|
Cancel enhanced logging now. DELETE /integrations/logging/:connectionId
|
|
11509
11709
|
|
|
11710
|
+
**listConnectors**(collectionId: string) → `Promise<`
|
|
11711
|
+
The collection's installed connector apps. GET /integrations/connectors
|
|
11712
|
+
|
|
11713
|
+
**saveConnectorInstallation**(collectionId: string,
|
|
11714
|
+
appId: string,
|
|
11715
|
+
input: SaveConnectorInstallationInput) → `Promise<`
|
|
11716
|
+
Save a connector's non-secret Connect-form values and/or settings. PUT /integrations/connectors/:appId/installation
|
|
11717
|
+
|
|
11718
|
+
**runConnectorStep**(collectionId: string,
|
|
11719
|
+
appId: string,
|
|
11720
|
+
step: ConnectorConsoleStep,
|
|
11721
|
+
input: Record<string, unknown> & { flowId?: string } = {}) → `Promise<T>`
|
|
11722
|
+
Run a connector step from the console: `testConnection` → ConnectorTestResult, `describeTarget` → ConnectorTargetSchema, `sample` → ConnectorRecords, and dry runs of `shape` ({ records }) → ConnectorShapeResult and `serialize` ({ documents }) → ConnectorSerializeResult. The server adds the installation; pass `flowId` to layer that flow's settings. POST /integrations/connectors/:appId/steps/:step
|
|
11723
|
+
|
|
11510
11724
|
### interactions
|
|
11511
11725
|
|
|
11512
11726
|
**query**(collectionId: string,
|
|
@@ -436,6 +436,13 @@ this is just the manifest shape.
|
|
|
436
436
|
| `definitions[].authority` | string | `caller` \| `collection` — whose authority it runs as |
|
|
437
437
|
| `definitions[].capabilities` | string[] | Least-privilege grants (`sl:<res>:<read\|write>`, `network[:<host>]`, `secrets:<ref>`) |
|
|
438
438
|
|
|
439
|
+
#### `connector`
|
|
440
|
+
|
|
441
|
+
Marks the app as an integration **connector** for one external provider: the integrations console
|
|
442
|
+
lists it and draws its connect form and settings, and the integration engine calls its server
|
|
443
|
+
functions (`testConnection`, `describeTarget`, `sample`, `fetch`, `shape`, `serialize`, `send`). It adapts the provider's format; core does each customer's mapping. The full
|
|
444
|
+
contract is in the **[Connectors guide](connectors.md)**.
|
|
445
|
+
|
|
439
446
|
---
|
|
440
447
|
|
|
441
448
|
## `app.admin.json`
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Integration connectors
|
|
2
|
+
|
|
3
|
+
A **connector** is an app that knows one external provider (a PIM, a content network, a shop
|
|
4
|
+
platform…). It is installed on a collection like any app, versioned and released like any app, and
|
|
5
|
+
it adds a manifest `connector` block plus a handful of [server functions](server-functions.md) the
|
|
6
|
+
integration engine and the integrations console call by convention.
|
|
7
|
+
|
|
8
|
+
Three layers, each with one job:
|
|
9
|
+
|
|
10
|
+
| Layer | Holds |
|
|
11
|
+
|---|---|
|
|
12
|
+
| **Engine** (the API) | [Integration flows](integrations.md), runs and their logs, triggers (events, schedule, Run now), the secret store, **the mapping** (each customer's field mappings and rules), generic transports (HTTP, Event Hub). No provider code. |
|
|
13
|
+
| **Integrations console** (a core app) | The one admin screen: installed connectors, connect forms, settings, the mapper with AI assist, flows, runs. |
|
|
14
|
+
| **Connector apps** | One per provider: an **adapter**. Talks to the provider and converts between its format and a clean, provider-neutral document. |
|
|
15
|
+
|
|
16
|
+
**A connector adapts; core maps.** A connector never holds a customer's mapping. It turns the
|
|
17
|
+
provider's records into provider-neutral documents (`shape`), or provider-ready payloads out of
|
|
18
|
+
documents core has already mapped (`serialize`). Every customer's mapping, routing and rules live in
|
|
19
|
+
core, so they work the same for every provider and can be replayed and traced. This is the line
|
|
20
|
+
SnapLogic (endpoint Snaps vs the Mapper), MuleSoft and Airbyte all draw.
|
|
21
|
+
|
|
22
|
+
A flow that only posts JSON to a webhook or an Event Hub needs no connector; it uses the engine's
|
|
23
|
+
generic transports (see [integrations.md](integrations.md)).
|
|
24
|
+
|
|
25
|
+
**No AI at run time.** The AI helps an admin *author* a mapping in the console (from the connector's
|
|
26
|
+
target schema plus real sample records); a person approves it, and it is saved as data and runs
|
|
27
|
+
deterministically. A connector's `shape` and `serialize` must be deterministic too.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## The manifest block
|
|
32
|
+
|
|
33
|
+
```jsonc
|
|
34
|
+
// app.manifest.json
|
|
35
|
+
"connector": {
|
|
36
|
+
"provider": "oneworldsync",
|
|
37
|
+
"title": "1WorldSync",
|
|
38
|
+
"directions": ["inbound"],
|
|
39
|
+
"entities": ["product"],
|
|
40
|
+
"connection": {
|
|
41
|
+
"fields": [
|
|
42
|
+
// Optional: SmartLinks' own credentials are the default; a customer may enter theirs.
|
|
43
|
+
{ "name": "appId", "label": "App ID", "secret": "oneworldsync-app-id" },
|
|
44
|
+
{ "name": "secretKey", "label": "Secret key", "secret": "oneworldsync-secret-key" },
|
|
45
|
+
{ "name": "gln", "label": "GLN", "required": true },
|
|
46
|
+
{ "name": "environment", "label": "Environment", "type": "select",
|
|
47
|
+
"options": [ { "value": "preprod", "label": "Pre-production" }, { "value": "production", "label": "Production" } ] }
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
"settings": { // JSON Schema → a form in the console
|
|
51
|
+
"type": "object",
|
|
52
|
+
"properties": { "targetMarket": { "type": "string", "default": "GB" } }
|
|
53
|
+
},
|
|
54
|
+
"presets": [
|
|
55
|
+
{ "id": "import-products", "title": "Import products", "direction": "inbound", "entity": "product",
|
|
56
|
+
"flow": { "config": { "fieldMappings": [ /* a suggested mapping the customer can edit */ ] } } }
|
|
57
|
+
],
|
|
58
|
+
"functions": { // names of this app's server functions
|
|
59
|
+
"testConnection": "testConnection",
|
|
60
|
+
"describeTarget": "describeTarget",
|
|
61
|
+
"sample": "sample",
|
|
62
|
+
"shape": "shape",
|
|
63
|
+
"fetch": "fetch"
|
|
64
|
+
},
|
|
65
|
+
"panels": [ { "id": "explorer", "title": "Explorer", "component": "Explorer" } ],
|
|
66
|
+
"aiGuide": "connector-ai-guide.md"
|
|
67
|
+
},
|
|
68
|
+
"functions": {
|
|
69
|
+
"files": { "js": { "umd": "dist/functions.umd.js" } },
|
|
70
|
+
"definitions": [
|
|
71
|
+
{ "name": "testConnection", "trigger": { "type": "http", "method": "POST" },
|
|
72
|
+
"visibility": "admin", "authority": "collection",
|
|
73
|
+
"capabilities": ["network:content1-api.1worldsync.com", "secrets:oneworldsync-app-id", "secrets:oneworldsync-secret-key"] }
|
|
74
|
+
// …one definition per function named above
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Types: `AppConnectorDeclaration` and friends in `@proveanything/smartlinks` (`src/types/appManifest.ts`).
|
|
80
|
+
|
|
81
|
+
| Field | Meaning |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `provider` | Stable provider key (`syndigo`, `oneworldsync`). |
|
|
84
|
+
| `title` | Provider display name; defaults to the app name. |
|
|
85
|
+
| `directions` | `inbound` (provider → SmartLinks), `outbound` (SmartLinks → provider), or both. |
|
|
86
|
+
| `entities` | SmartLinks entities it maps, e.g. `["product"]`. |
|
|
87
|
+
| `connection.fields[]` | The console's **Connect** form. A field with `secret` is stored as this app's secret of that name on the collection (write-only; never returned to the browser). Other fields are saved in the installation's connection settings. |
|
|
88
|
+
| `settings` | JSON Schema for installation settings; the console renders it as a form. |
|
|
89
|
+
| `presets[]` | Ready-made flows ("Products → provider"). A preset's `flow.config.fieldMappings` is a **suggested** mapping: the connector's knowledge of the provider, which the customer accepts or edits in core. |
|
|
90
|
+
| `functions` | Which of this app's server functions implement each step (below). Fill in only what applies. |
|
|
91
|
+
| `panels[]` | Provider-specific screens the console mounts (an API explorer, a status page). |
|
|
92
|
+
| `aiGuide` | A guide the mapping assistant reads: the provider's quirks, required fields, good defaults. |
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## The functions
|
|
97
|
+
|
|
98
|
+
Each step names one of the app's server functions. Declare them as **http POST, `visibility:
|
|
99
|
+
"admin"`, `authority: "collection"`**: the engine calls them with no signed-in user (a product edit, a
|
|
100
|
+
schedule), and the console calls the same functions while an admin configures the flow. The input
|
|
101
|
+
arrives as `event.body`; return the result as JSON. Capabilities still cap what each one can do:
|
|
102
|
+
declare the provider's hosts (`network:<host>`) and the secrets it reads (`secrets:<name>`).
|
|
103
|
+
|
|
104
|
+
| Step | Called by | Input (`event.body`) | Returns |
|
|
105
|
+
|---|---|---|---|
|
|
106
|
+
| `testConnection` | Connect form, **Test** | `{ installation }` | `ConnectorTestResult` `{ ok, message }` |
|
|
107
|
+
| `describeTarget` | Mapper opens | `{ installation }` | `ConnectorTargetSchema` `{ fields: [{ path, label, type, required, enum }] }`: the provider's fields *for this customer*. Outbound mappings target these paths. |
|
|
108
|
+
| `sample` | Mapper, AI assist, dry run | `{ installation, limit }` | `ConnectorRecords` `{ records }`: real provider records |
|
|
109
|
+
| `fetch` | Every inbound run | `ConnectorFetchInput` `{ installation, cursor, limit }` | `ConnectorRecords` `{ records, nextCursor }`: one page per call |
|
|
110
|
+
| `shape` | Every inbound run (and sample dry runs) | `ConnectorShapeInput` `{ installation, records }` | `ConnectorShapeResult` `{ documents, skipped }`: one provider-neutral document per record, same order; `null` skips one |
|
|
111
|
+
| `serialize` | Every outbound run | `ConnectorSerializeInput` `{ installation, documents, eventType? }`: documents core mapped to `describeTarget` paths; a deletion arrives as `{ id, _deleted: true }` | `ConnectorSerializeResult` `{ payloads, skipped }`: one provider payload per document, same order; `null` skips one |
|
|
112
|
+
| `send` | Every outbound run | `ConnectorSendInput` `{ installation, payloads }` | `ConnectorSendResult` `{ results: [{ index, ok, status, error, externalId }] }` |
|
|
113
|
+
|
|
114
|
+
**How a run flows:**
|
|
115
|
+
- **Inbound:** `fetch` (paged) → `shape` → core maps the documents with the flow's mapping → upsert.
|
|
116
|
+
- **Outbound:** gather the SmartLinks records → core maps them to the provider's fields → `serialize`
|
|
117
|
+
→ `send` (or a generic transport).
|
|
118
|
+
|
|
119
|
+
**What happens when a step is left out:**
|
|
120
|
+
- **No `shape`:** core maps the provider records as they come.
|
|
121
|
+
- **No `serialize`:** the mapped documents are sent as they are.
|
|
122
|
+
- **No `send`:** the flow's own transport is used: HTTP (`config.connection`), or Azure Event Hub with
|
|
123
|
+
`config.transport: 'eventhub'` and `config.eventHub`.
|
|
124
|
+
- **No `describeTarget`:** the mapper offers free-text target paths.
|
|
125
|
+
|
|
126
|
+
**What `shape` should produce:** plain, predictable JSON a person can map without reading the
|
|
127
|
+
provider's documentation. Flatten the provider's envelopes, name things plainly, keep codes alongside
|
|
128
|
+
their meanings, and put repeated sections (ingredients, allergens, nutrients, images) in arrays. The
|
|
129
|
+
same provider record always gives the same document.
|
|
130
|
+
|
|
131
|
+
### `installation`
|
|
132
|
+
|
|
133
|
+
Every step receives the installation it serves:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
interface ConnectorInstallation {
|
|
137
|
+
collectionId: string
|
|
138
|
+
appId: string // the connector app
|
|
139
|
+
flowId?: string | null // when called for a flow
|
|
140
|
+
connection: Record<string, unknown> // non-secret Connect-form fields
|
|
141
|
+
settings: Record<string, unknown> // installation settings, with the flow's overrides on top
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Secrets are never in it. Read them in the function with `ctx.secrets.get('<name>')`, which needs the
|
|
146
|
+
matching `secrets:<name>` capability. A secret the customer set on their collection wins; otherwise the
|
|
147
|
+
connector's own app-level secret of that name (set in Forge) is used, so a connector can ship with
|
|
148
|
+
SmartLinks' credentials as the default.
|
|
149
|
+
|
|
150
|
+
### Example: `fetch` and `shape`
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
// src/functions/index.ts (the app's functions bundle)
|
|
154
|
+
import type { ConnectorFetchInput, ConnectorRecords, ConnectorShapeInput, ConnectorShapeResult } from '@proveanything/smartlinks'
|
|
155
|
+
|
|
156
|
+
export async function fetch(ctx: any, event: any): Promise<ConnectorRecords> {
|
|
157
|
+
const { installation, cursor, limit = 50 } = event.body as ConnectorFetchInput
|
|
158
|
+
const appId = await ctx.secrets.get('provider-app-id')
|
|
159
|
+
const key = await ctx.secrets.get('provider-secret-key')
|
|
160
|
+
const res = await ctx.fetch(buildSignedUrl(installation, { cursor, limit }, appId, key))
|
|
161
|
+
const page = await res.json()
|
|
162
|
+
return { records: page.items, nextCursor: page.next ?? null }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export async function shape(_ctx: any, event: any): Promise<ConnectorShapeResult> {
|
|
166
|
+
const { records } = event.body as ConnectorShapeInput
|
|
167
|
+
return {
|
|
168
|
+
documents: records.map((r: any) => ({
|
|
169
|
+
gtin: r.item?.gtin,
|
|
170
|
+
name: r.item?.tradeItemDescription?.[0]?.value,
|
|
171
|
+
brand: r.item?.brandName,
|
|
172
|
+
ingredients: (r.item?.ingredients ?? []).map((i: any) => ({ name: i.name, percent: i.percentage })),
|
|
173
|
+
})),
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Where configuration lives
|
|
181
|
+
|
|
182
|
+
| What | Where |
|
|
183
|
+
|---|---|
|
|
184
|
+
| Connect-form values (non-secret), installation settings | The connector app's config on the collection, in its admin-only zone (`admin.connector.connection`, `admin.connector.settings`). |
|
|
185
|
+
| Credentials | The connector app's secrets, by the names its fields declare (the customer's on their collection, else the connector's default). |
|
|
186
|
+
| One flow's use of the connector | The flow: `config.connector = { appId, preset?, settings? }` (per-flow settings override the installation's), plus `config.fieldMappings`, which core runs. |
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
await integrations.createFlow(collectionId, {
|
|
190
|
+
appId: 'core-integrations',
|
|
191
|
+
direction: 'inbound',
|
|
192
|
+
name: 'Import products from 1WorldSync',
|
|
193
|
+
status: 'active',
|
|
194
|
+
schedule: '0 * * * *',
|
|
195
|
+
targetEntity: 'product',
|
|
196
|
+
config: {
|
|
197
|
+
connector: { appId: 'oneworldsync', preset: 'import-products' },
|
|
198
|
+
// Maps the connector's shaped documents (not the raw provider records) onto the product.
|
|
199
|
+
fieldMappings: [ { targetPath: 'name', sourcePath: 'name', transformType: 'direct' } ],
|
|
200
|
+
},
|
|
201
|
+
})
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## From the console
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
import { integrations, secrets } from '@proveanything/smartlinks'
|
|
210
|
+
|
|
211
|
+
const { connectors } = await integrations.listConnectors(collectionId)
|
|
212
|
+
// → [{ appId, name, version, connector, installation: { connection, settings }, secrets: { 'provider-key': { set, hint } } }]
|
|
213
|
+
|
|
214
|
+
await secrets.put(collectionId, 'provider-key', value, { appId }) // a secret field
|
|
215
|
+
await integrations.saveConnectorInstallation(collectionId, appId, { connection: { environment: 'production' } })
|
|
216
|
+
const test = await integrations.runConnectorStep(collectionId, appId, 'testConnection')
|
|
217
|
+
const target = await integrations.runConnectorStep(collectionId, appId, 'describeTarget')
|
|
218
|
+
const { records } = await integrations.runConnectorStep(collectionId, appId, 'sample', { limit: 5 })
|
|
219
|
+
const shaped = await integrations.runConnectorStep(collectionId, appId, 'shape', { records }) // dry run
|
|
220
|
+
const wire = await integrations.runConnectorStep(collectionId, appId, 'serialize', { documents }) // dry run
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`send` and `fetch` only run inside a flow. A flow run's summary carries `sent` (outbound) or
|
|
224
|
+
`created` / `updated` / `skipped` (inbound), and every run, inbound or outbound, is in the run history.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Panels
|
|
229
|
+
|
|
230
|
+
A connector can contribute screens to the integrations console (an API explorer, say). Each
|
|
231
|
+
`panels[]` entry names a component exported from the app's widgets bundle; the console mounts it
|
|
232
|
+
host-side and passes it the collection and the installation. Panels are admin-only and call the
|
|
233
|
+
connector's own functions.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Who can build connectors
|
|
238
|
+
|
|
239
|
+
First-party connectors run in-process, like any app server function. Third-party connectors will run
|
|
240
|
+
in the isolated runner (see [server-functions.md](server-functions.md)) once that opens up; the contract
|
|
241
|
+
does not change.
|
|
@@ -15,6 +15,10 @@ Flows are triggered three ways, all converging on the same executor:
|
|
|
15
15
|
automatically when that entity changes.
|
|
16
16
|
- **schedule** — a flow carrying a cron/interval `schedule` is run by the scan job. *(next)*
|
|
17
17
|
|
|
18
|
+
Provider-specific logic (a provider's payload format, its API, its connect form) lives in a
|
|
19
|
+
**connector app**, not in the engine: see **[Connectors](connectors.md)**. A flow uses one with
|
|
20
|
+
`config.connector = { appId }`.
|
|
21
|
+
|
|
18
22
|
Credentials are **never** stored on the flow. The connection holds an opaque
|
|
19
23
|
`credentialRef` into the **sealed-secret store** (`secrets` namespace); the value is sealed
|
|
20
24
|
at rest and resolved server-side only, at execution.
|
package/dist/docs/overview.md
CHANGED
|
@@ -94,6 +94,7 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
|
|
|
94
94
|
| **Lots** | `docs/lots.md` | Cross-SKU production "Lot" entity |
|
|
95
95
|
| **Loyalty** | `docs/loyalty.md` | Points, members, earning rules |
|
|
96
96
|
| **Integrations** | `docs/integrations.md` | Inbound/outbound external-system integration flows |
|
|
97
|
+
| **Connectors** | `docs/connectors.md` | Connector apps: the manifest `connector` block + the functions the integration engine calls |
|
|
97
98
|
| **Item Context** | `docs/item-context.md` | The `itemContext` container prop (serial / NFC authenticity context) |
|
|
98
99
|
| **Native Facade** | `docs/native-facade.md` | `host.native` / `SL.native` device-capability facade |
|
|
99
100
|
| **Product Facets** | `docs/PRODUCT_FACETS_SDK.md` | Facet API reference (admin + public endpoints, types) |
|