@proveanything/smartlinks 2.0.55 → 2.0.57
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 +21 -1
- package/dist/api/integrations.js +25 -0
- package/dist/api/realtime.d.ts +31 -88
- package/dist/api/realtime.js +239 -93
- package/dist/docs/API_SUMMARY.md +264 -4
- package/dist/docs/app-manifest.md +7 -0
- package/dist/docs/connectors.md +204 -0
- package/dist/docs/integrations.md +4 -0
- package/dist/docs/overview.md +3 -2
- package/dist/docs/realtime.md +118 -765
- package/dist/http.js +3 -0
- package/dist/openapi.yaml +435 -3
- package/dist/types/appManifest.d.ts +166 -0
- package/dist/types/integrations.d.ts +43 -1
- package/dist/types/realtime.d.ts +44 -0
- package/docs/API_SUMMARY.md +264 -4
- package/docs/app-manifest.md +7 -0
- package/docs/connectors.md +204 -0
- package/docs/integrations.md +4 -0
- package/docs/overview.md +3 -2
- package/docs/realtime.md +118 -765
- package/openapi.yaml +435 -3
- package/package.json +1 -1
- package/scripts/doctor.mjs +25 -0
|
@@ -29,9 +29,20 @@ export interface FlowConnection {
|
|
|
29
29
|
defaultHeaders?: Record<string, string>;
|
|
30
30
|
auth?: FlowConnectionAuth;
|
|
31
31
|
}
|
|
32
|
+
/** A flow driven by a connector app (docs/connectors.md). */
|
|
33
|
+
export interface FlowConnectorRef {
|
|
34
|
+
/** The connector app (manifest `connector` block), installed on the collection. */
|
|
35
|
+
appId: string;
|
|
36
|
+
/** The preset the flow was created from, if any. */
|
|
37
|
+
preset?: string;
|
|
38
|
+
/** Per-flow settings, layered over the installation's. */
|
|
39
|
+
settings?: Record<string, unknown>;
|
|
40
|
+
}
|
|
32
41
|
export interface IntegrationFlowConfig {
|
|
33
42
|
connection?: FlowConnection;
|
|
34
43
|
fieldMappings?: FieldMapping[];
|
|
44
|
+
/** Run this flow through a connector app's functions instead of the built-in mapper/transport. */
|
|
45
|
+
connector?: FlowConnectorRef;
|
|
35
46
|
[key: string]: any;
|
|
36
47
|
}
|
|
37
48
|
export interface IntegrationFlow {
|
|
@@ -95,12 +106,43 @@ export interface RunFlowSummary {
|
|
|
95
106
|
flowId: string;
|
|
96
107
|
direction: FlowDirection;
|
|
97
108
|
records: number;
|
|
98
|
-
|
|
109
|
+
/** Outbound: records delivered. */
|
|
110
|
+
sent?: number;
|
|
99
111
|
failed: number;
|
|
112
|
+
/** Inbound: products created / updated, and records the connector skipped. */
|
|
113
|
+
created?: number;
|
|
114
|
+
updated?: number;
|
|
115
|
+
skipped?: number;
|
|
100
116
|
status: RunStatus;
|
|
101
117
|
/** Id of the persisted run row (deep-link into its detail). */
|
|
102
118
|
runId?: string;
|
|
103
119
|
}
|
|
120
|
+
/** One installed connector app, as the integrations console sees it. */
|
|
121
|
+
export interface InstalledConnector {
|
|
122
|
+
appId: string;
|
|
123
|
+
name: string;
|
|
124
|
+
/** The release channel installed on this collection. */
|
|
125
|
+
channel: string;
|
|
126
|
+
version: string | null;
|
|
127
|
+
connector: import('./appManifest.js').AppConnectorDeclaration;
|
|
128
|
+
/** Non-secret Connect-form values + installation settings. */
|
|
129
|
+
installation: {
|
|
130
|
+
connection: Record<string, unknown>;
|
|
131
|
+
settings: Record<string, unknown>;
|
|
132
|
+
};
|
|
133
|
+
/** Each secret the Connect form declares: whether it is set (+ masked hint). Never a value. */
|
|
134
|
+
secrets: Record<string, {
|
|
135
|
+
set: boolean;
|
|
136
|
+
hint?: string | null;
|
|
137
|
+
}>;
|
|
138
|
+
}
|
|
139
|
+
export interface SaveConnectorInstallationInput {
|
|
140
|
+
/** Non-secret fields only; secret fields are set with `secrets.put` for the connector's appId. */
|
|
141
|
+
connection?: Record<string, unknown>;
|
|
142
|
+
settings?: Record<string, unknown>;
|
|
143
|
+
}
|
|
144
|
+
/** Steps the console may run directly (send and fetch only run inside a flow). */
|
|
145
|
+
export type ConnectorConsoleStep = 'testConnection' | 'describeTarget' | 'sample' | 'transform';
|
|
104
146
|
export type RunTrigger = 'manual' | 'event' | 'schedule' | 'test';
|
|
105
147
|
export type FlowRunStatus = 'running' | 'success' | 'partial' | 'failed' | 'skipped';
|
|
106
148
|
export type FlowRunItemStatus = 'sent' | 'failed' | 'skipped';
|
package/dist/types/realtime.d.ts
CHANGED
|
@@ -42,3 +42,47 @@ export interface AblyTokenRequest {
|
|
|
42
42
|
* - collection:abc:users:active
|
|
43
43
|
*/
|
|
44
44
|
export type RealtimeChannelPattern = string;
|
|
45
|
+
/** Where a realtime listener lives: the collection, and (for app channels) the app. */
|
|
46
|
+
export interface RealtimeScope {
|
|
47
|
+
collectionId: string;
|
|
48
|
+
appId?: string;
|
|
49
|
+
}
|
|
50
|
+
/** A message on a channel. `channel` is the logical name ("draw", "feed:admin"). */
|
|
51
|
+
export interface RealtimeMessage {
|
|
52
|
+
channel: string;
|
|
53
|
+
event: string;
|
|
54
|
+
data: unknown;
|
|
55
|
+
/** When it was sent (ms since epoch), when known. */
|
|
56
|
+
at?: number;
|
|
57
|
+
}
|
|
58
|
+
/** "This changed" — ids only; refetch through the normal API. */
|
|
59
|
+
export interface RealtimeChangeEvent {
|
|
60
|
+
/** 'product' | 'proof' | 'asset' | 'record' | 'collection' | 'collection.apps' | 'settings' | … */
|
|
61
|
+
type: string;
|
|
62
|
+
id?: string;
|
|
63
|
+
appId?: string;
|
|
64
|
+
recordType?: string;
|
|
65
|
+
productId?: string;
|
|
66
|
+
proofId?: string;
|
|
67
|
+
/** 'created' | 'updated' | 'deleted' (records) or 'create' | 'update' | 'delete' (products, proofs…) */
|
|
68
|
+
status?: string;
|
|
69
|
+
at: number;
|
|
70
|
+
}
|
|
71
|
+
/** Which changes to hear about — every field given must match. */
|
|
72
|
+
export interface RealtimeChangeFilter {
|
|
73
|
+
type?: string;
|
|
74
|
+
id?: string;
|
|
75
|
+
appId?: string;
|
|
76
|
+
recordType?: string;
|
|
77
|
+
productId?: string;
|
|
78
|
+
proofId?: string;
|
|
79
|
+
}
|
|
80
|
+
/** A channel an app declares in its manifest (`realtime.channels`). */
|
|
81
|
+
export interface RealtimeChannelDeclaration {
|
|
82
|
+
/** Lowercase letters, numbers, - and _; up to 40 characters. */
|
|
83
|
+
name: string;
|
|
84
|
+
/** Who may listen. Default 'admin'. */
|
|
85
|
+
subscribe?: "public" | "signedIn" | "admin";
|
|
86
|
+
/** Who may send: those levels, or 'server' (only the app's server functions). Default 'server'. */
|
|
87
|
+
publish?: "public" | "signedIn" | "admin" | "server";
|
|
88
|
+
}
|
package/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.57 | Generated: 2026-10-13T11:42:19.247Z
|
|
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: a manifest `connector` block + server functions (testConnection/describeTarget/sample/transform/send/fetch) that the integration engine and console call for one external provider
|
|
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
|
|
@@ -2850,12 +2851,159 @@ interface AppManifest {
|
|
|
2850
2851
|
publicViews?: PublicView[];
|
|
2851
2852
|
executor?: AppManifestExecutor;
|
|
2852
2853
|
functions?: AppManifestFunctions;
|
|
2854
|
+
realtime?: { channels: RealtimeChannelDeclaration[] };
|
|
2853
2855
|
data?: AppDataDeclaration;
|
|
2854
2856
|
headless?: AppHeadlessDeclaration;
|
|
2857
|
+
connector?: AppConnectorDeclaration;
|
|
2855
2858
|
[key: string]: any;
|
|
2856
2859
|
}
|
|
2857
2860
|
```
|
|
2858
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
|
+
transform?: string
|
|
2912
|
+
send?: string
|
|
2913
|
+
fetch?: string
|
|
2914
|
+
}
|
|
2915
|
+
panels?: ConnectorPanel[]
|
|
2916
|
+
aiGuide?: string
|
|
2917
|
+
}
|
|
2918
|
+
```
|
|
2919
|
+
|
|
2920
|
+
**ConnectorInstallation** (interface)
|
|
2921
|
+
```typescript
|
|
2922
|
+
interface ConnectorInstallation {
|
|
2923
|
+
collectionId: string
|
|
2924
|
+
appId: string
|
|
2925
|
+
flowId?: string | null
|
|
2926
|
+
connection: Record<string, unknown>
|
|
2927
|
+
settings: Record<string, unknown>
|
|
2928
|
+
}
|
|
2929
|
+
```
|
|
2930
|
+
|
|
2931
|
+
**ConnectorTestResult** (interface)
|
|
2932
|
+
```typescript
|
|
2933
|
+
interface ConnectorTestResult {
|
|
2934
|
+
ok: boolean
|
|
2935
|
+
message: string
|
|
2936
|
+
}
|
|
2937
|
+
```
|
|
2938
|
+
|
|
2939
|
+
**ConnectorTargetField** (interface)
|
|
2940
|
+
```typescript
|
|
2941
|
+
interface ConnectorTargetField {
|
|
2942
|
+
path: string
|
|
2943
|
+
label?: string
|
|
2944
|
+
type?: 'string' | 'number' | 'boolean' | 'date' | 'object' | 'array'
|
|
2945
|
+
required?: boolean
|
|
2946
|
+
description?: string
|
|
2947
|
+
enum?: string[]
|
|
2948
|
+
}
|
|
2949
|
+
```
|
|
2950
|
+
|
|
2951
|
+
**ConnectorTargetSchema** (interface)
|
|
2952
|
+
```typescript
|
|
2953
|
+
interface ConnectorTargetSchema {
|
|
2954
|
+
fields: ConnectorTargetField[]
|
|
2955
|
+
}
|
|
2956
|
+
```
|
|
2957
|
+
|
|
2958
|
+
**ConnectorRecords** (interface)
|
|
2959
|
+
```typescript
|
|
2960
|
+
interface ConnectorRecords {
|
|
2961
|
+
records: Array<Record<string, unknown>>
|
|
2962
|
+
nextCursor?: string | null
|
|
2963
|
+
}
|
|
2964
|
+
```
|
|
2965
|
+
|
|
2966
|
+
**ConnectorTransformInput** (interface)
|
|
2967
|
+
```typescript
|
|
2968
|
+
interface ConnectorTransformInput {
|
|
2969
|
+
installation: ConnectorInstallation
|
|
2970
|
+
records: Array<Record<string, unknown>>
|
|
2971
|
+
mapping: FieldMapping[]
|
|
2972
|
+
}
|
|
2973
|
+
```
|
|
2974
|
+
|
|
2975
|
+
**ConnectorTransformResult** (interface)
|
|
2976
|
+
```typescript
|
|
2977
|
+
interface ConnectorTransformResult {
|
|
2978
|
+
payloads: Array<unknown | null>
|
|
2979
|
+
skipped?: Array<{ index: number; reason: string }>
|
|
2980
|
+
}
|
|
2981
|
+
```
|
|
2982
|
+
|
|
2983
|
+
**ConnectorSendInput** (interface)
|
|
2984
|
+
```typescript
|
|
2985
|
+
interface ConnectorSendInput {
|
|
2986
|
+
installation: ConnectorInstallation
|
|
2987
|
+
payloads: unknown[]
|
|
2988
|
+
}
|
|
2989
|
+
```
|
|
2990
|
+
|
|
2991
|
+
**ConnectorSendResult** (interface)
|
|
2992
|
+
```typescript
|
|
2993
|
+
interface ConnectorSendResult {
|
|
2994
|
+
results: Array<{ index: number; ok: boolean; status?: number; error?: string; externalId?: string }>
|
|
2995
|
+
}
|
|
2996
|
+
```
|
|
2997
|
+
|
|
2998
|
+
**ConnectorFetchInput** (interface)
|
|
2999
|
+
```typescript
|
|
3000
|
+
interface ConnectorFetchInput {
|
|
3001
|
+
installation: ConnectorInstallation
|
|
3002
|
+
cursor?: string | null
|
|
3003
|
+
limit?: number
|
|
3004
|
+
}
|
|
3005
|
+
```
|
|
3006
|
+
|
|
2859
3007
|
**CollectionAppWidget** (interface)
|
|
2860
3008
|
```typescript
|
|
2861
3009
|
interface CollectionAppWidget {
|
|
@@ -6898,11 +7046,21 @@ interface FlowConnection {
|
|
|
6898
7046
|
}
|
|
6899
7047
|
```
|
|
6900
7048
|
|
|
7049
|
+
**FlowConnectorRef** (interface)
|
|
7050
|
+
```typescript
|
|
7051
|
+
interface FlowConnectorRef {
|
|
7052
|
+
appId: string
|
|
7053
|
+
preset?: string
|
|
7054
|
+
settings?: Record<string, unknown>
|
|
7055
|
+
}
|
|
7056
|
+
```
|
|
7057
|
+
|
|
6901
7058
|
**IntegrationFlowConfig** (interface)
|
|
6902
7059
|
```typescript
|
|
6903
7060
|
interface IntegrationFlowConfig {
|
|
6904
7061
|
connection?: FlowConnection
|
|
6905
7062
|
fieldMappings?: FieldMapping[]
|
|
7063
|
+
connector?: FlowConnectorRef
|
|
6906
7064
|
[key: string]: any
|
|
6907
7065
|
}
|
|
6908
7066
|
```
|
|
@@ -6981,13 +7139,37 @@ interface RunFlowSummary {
|
|
|
6981
7139
|
flowId: string
|
|
6982
7140
|
direction: FlowDirection
|
|
6983
7141
|
records: number
|
|
6984
|
-
sent
|
|
7142
|
+
sent?: number
|
|
6985
7143
|
failed: number
|
|
7144
|
+
created?: number
|
|
7145
|
+
updated?: number
|
|
7146
|
+
skipped?: number
|
|
6986
7147
|
status: RunStatus
|
|
6987
7148
|
runId?: string
|
|
6988
7149
|
}
|
|
6989
7150
|
```
|
|
6990
7151
|
|
|
7152
|
+
**InstalledConnector** (interface)
|
|
7153
|
+
```typescript
|
|
7154
|
+
interface InstalledConnector {
|
|
7155
|
+
appId: string
|
|
7156
|
+
name: string
|
|
7157
|
+
channel: string
|
|
7158
|
+
version: string | null
|
|
7159
|
+
connector: import('./appManifest').AppConnectorDeclaration
|
|
7160
|
+
installation: { connection: Record<string, unknown>; settings: Record<string, unknown> }
|
|
7161
|
+
secrets: Record<string, { set: boolean; hint?: string | null }>
|
|
7162
|
+
}
|
|
7163
|
+
```
|
|
7164
|
+
|
|
7165
|
+
**SaveConnectorInstallationInput** (interface)
|
|
7166
|
+
```typescript
|
|
7167
|
+
interface SaveConnectorInstallationInput {
|
|
7168
|
+
connection?: Record<string, unknown>
|
|
7169
|
+
settings?: Record<string, unknown>
|
|
7170
|
+
}
|
|
7171
|
+
```
|
|
7172
|
+
|
|
6991
7173
|
**CapturedRequest** (interface)
|
|
6992
7174
|
```typescript
|
|
6993
7175
|
interface CapturedRequest {
|
|
@@ -7192,6 +7374,8 @@ interface RecordTypesResponse {
|
|
|
7192
7374
|
|
|
7193
7375
|
**UpdateFlowInput** = `Partial<Omit<CreateFlowInput, 'direction'>> & {`
|
|
7194
7376
|
|
|
7377
|
+
**ConnectorConsoleStep** = `'testConnection' | 'describeTarget' | 'sample' | 'transform'`
|
|
7378
|
+
|
|
7195
7379
|
**RunTrigger** = `'manual' | 'event' | 'schedule' | 'test'`
|
|
7196
7380
|
|
|
7197
7381
|
**FlowRunStatus** = `'running' | 'success' | 'partial' | 'failed' | 'skipped'`
|
|
@@ -9141,6 +9325,59 @@ interface AblyTokenRequest {
|
|
|
9141
9325
|
}
|
|
9142
9326
|
```
|
|
9143
9327
|
|
|
9328
|
+
**RealtimeScope** (interface)
|
|
9329
|
+
```typescript
|
|
9330
|
+
interface RealtimeScope {
|
|
9331
|
+
collectionId: string
|
|
9332
|
+
appId?: string
|
|
9333
|
+
}
|
|
9334
|
+
```
|
|
9335
|
+
|
|
9336
|
+
**RealtimeMessage** (interface)
|
|
9337
|
+
```typescript
|
|
9338
|
+
interface RealtimeMessage {
|
|
9339
|
+
channel: string
|
|
9340
|
+
event: string
|
|
9341
|
+
data: unknown
|
|
9342
|
+
at?: number
|
|
9343
|
+
}
|
|
9344
|
+
```
|
|
9345
|
+
|
|
9346
|
+
**RealtimeChangeEvent** (interface)
|
|
9347
|
+
```typescript
|
|
9348
|
+
interface RealtimeChangeEvent {
|
|
9349
|
+
type: string
|
|
9350
|
+
id?: string
|
|
9351
|
+
appId?: string
|
|
9352
|
+
recordType?: string
|
|
9353
|
+
productId?: string
|
|
9354
|
+
proofId?: string
|
|
9355
|
+
status?: string
|
|
9356
|
+
at: number
|
|
9357
|
+
}
|
|
9358
|
+
```
|
|
9359
|
+
|
|
9360
|
+
**RealtimeChangeFilter** (interface)
|
|
9361
|
+
```typescript
|
|
9362
|
+
interface RealtimeChangeFilter {
|
|
9363
|
+
type?: string
|
|
9364
|
+
id?: string
|
|
9365
|
+
appId?: string
|
|
9366
|
+
recordType?: string
|
|
9367
|
+
productId?: string
|
|
9368
|
+
proofId?: string
|
|
9369
|
+
}
|
|
9370
|
+
```
|
|
9371
|
+
|
|
9372
|
+
**RealtimeChannelDeclaration** (interface)
|
|
9373
|
+
```typescript
|
|
9374
|
+
interface RealtimeChannelDeclaration {
|
|
9375
|
+
name: string
|
|
9376
|
+
subscribe?: "public" | "signedIn" | "admin"
|
|
9377
|
+
publish?: "public" | "signedIn" | "admin" | "server"
|
|
9378
|
+
}
|
|
9379
|
+
```
|
|
9380
|
+
|
|
9144
9381
|
**RealtimeChannelPattern** = `string`
|
|
9145
9382
|
|
|
9146
9383
|
### research
|
|
@@ -11453,6 +11690,20 @@ Enable enhanced logging. POST /integrations/logging/:connectionId (omit ttlMinut
|
|
|
11453
11690
|
**cancelLogging**(collectionId: string, connectionId: string) → `Promise<`
|
|
11454
11691
|
Cancel enhanced logging now. DELETE /integrations/logging/:connectionId
|
|
11455
11692
|
|
|
11693
|
+
**listConnectors**(collectionId: string) → `Promise<`
|
|
11694
|
+
The collection's installed connector apps. GET /integrations/connectors
|
|
11695
|
+
|
|
11696
|
+
**saveConnectorInstallation**(collectionId: string,
|
|
11697
|
+
appId: string,
|
|
11698
|
+
input: SaveConnectorInstallationInput) → `Promise<`
|
|
11699
|
+
Save a connector's non-secret Connect-form values and/or settings. PUT /integrations/connectors/:appId/installation
|
|
11700
|
+
|
|
11701
|
+
**runConnectorStep**(collectionId: string,
|
|
11702
|
+
appId: string,
|
|
11703
|
+
step: ConnectorConsoleStep,
|
|
11704
|
+
input: Record<string, unknown> & { flowId?: string } = {}) → `Promise<T>`
|
|
11705
|
+
Run a connector step from the console: `testConnection` → ConnectorTestResult, `describeTarget` → ConnectorTargetSchema, `sample` → ConnectorRecords, `transform` (a dry run) → ConnectorTransformResult. The server adds the installation; pass `flowId` to layer that flow's settings. POST /integrations/connectors/:appId/steps/:step
|
|
11706
|
+
|
|
11456
11707
|
### interactions
|
|
11457
11708
|
|
|
11458
11709
|
**query**(collectionId: string,
|
|
@@ -12168,11 +12419,20 @@ Reset rate limit for a user
|
|
|
12168
12419
|
|
|
12169
12420
|
### realtime
|
|
12170
12421
|
|
|
12422
|
+
**channel**(scope: RealtimeScope & { appId: string }, name: string) → `void`
|
|
12423
|
+
One of your app's declared channels (manifest `realtime.channels`). ```ts const draw = realtime.channel({ collectionId, appId }, "draw") const off = draw.subscribe("status", (m) => setStatus(m.data)) await draw.publish("status", { phase: "spinning" }) // if the channel lets this viewer publish ```
|
|
12424
|
+
|
|
12425
|
+
**onChange**(scope: RealtimeScope, filter: RealtimeChangeFilter, onChangeFn: (change: RealtimeChangeEvent) → `void`
|
|
12426
|
+
Something changed: refetch it. IDs only — never data. Admins of the collection see every change; everyone else sees changes to public things (public app records, products). Filter by `type` ('product' | 'proof' | 'asset' | 'record' | 'collection' | 'collection.apps' | 'settings'), `id`, `appId`, `recordType`, `productId`, `proofId`. ```ts const off = realtime.onChange({ collectionId }, { type: "record", appId, recordType: "entry" }, () => refetch()) ```
|
|
12427
|
+
|
|
12428
|
+
**onMine**(scope: RealtimeScope, onMessage: (msg: RealtimeMessage) → `void`
|
|
12429
|
+
Messages for the signed-in person only (e.g. "scan" — a tag they just scanned on another device).
|
|
12430
|
+
|
|
12171
12431
|
**getPublicToken**(params: RealtimeTokenRequest) → `Promise<AblyTokenRequest>`
|
|
12172
|
-
|
|
12432
|
+
`collection:{collectionId}:*` (subscribe; publish only for the collection's admins).
|
|
12173
12433
|
|
|
12174
12434
|
**getAdminToken**() → `Promise<AblyTokenRequest>`
|
|
12175
|
-
|
|
12435
|
+
(subscribe only).
|
|
12176
12436
|
|
|
12177
12437
|
### research
|
|
12178
12438
|
|
package/docs/app-manifest.md
CHANGED
|
@@ -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`, `transform`, `send`, `fetch`). The full
|
|
444
|
+
contract is in the **[Connectors guide](connectors.md)**.
|
|
445
|
+
|
|
439
446
|
---
|
|
440
447
|
|
|
441
448
|
## `app.admin.json`
|
|
@@ -0,0 +1,204 @@
|
|
|
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 deterministic field mapper, 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: its connect form, settings, target schema, transform, send/fetch, and any provider-specific panels. |
|
|
15
|
+
|
|
16
|
+
A flow that only posts JSON to a webhook or an Event Hub needs no connector; it uses the engine's
|
|
17
|
+
generic transports (see [integrations.md](integrations.md)).
|
|
18
|
+
|
|
19
|
+
**No AI at run time.** The AI helps an admin *author* a mapping in the console (from the connector's
|
|
20
|
+
target schema plus real sample records); a person approves it, and it is saved as data and runs
|
|
21
|
+
deterministically. A connector's `transform` must be deterministic too.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## The manifest block
|
|
26
|
+
|
|
27
|
+
```jsonc
|
|
28
|
+
// app.manifest.json
|
|
29
|
+
"connector": {
|
|
30
|
+
"provider": "oneworldsync",
|
|
31
|
+
"title": "1WorldSync",
|
|
32
|
+
"directions": ["inbound"],
|
|
33
|
+
"entities": ["product"],
|
|
34
|
+
"connection": {
|
|
35
|
+
"fields": [
|
|
36
|
+
{ "name": "appId", "label": "App ID", "secret": "oneworldsync-app-id", "required": true },
|
|
37
|
+
{ "name": "secretKey", "label": "Secret key", "secret": "oneworldsync-secret-key", "required": true },
|
|
38
|
+
{ "name": "environment", "label": "Environment", "type": "select",
|
|
39
|
+
"options": [ { "value": "preprod", "label": "Pre-production" }, { "value": "production", "label": "Production" } ] }
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"settings": { // JSON Schema → a form in the console
|
|
43
|
+
"type": "object",
|
|
44
|
+
"properties": { "targetMarket": { "type": "string", "default": "GB" } }
|
|
45
|
+
},
|
|
46
|
+
"presets": [
|
|
47
|
+
{ "id": "import-products", "title": "Import products", "direction": "inbound", "entity": "product" }
|
|
48
|
+
],
|
|
49
|
+
"functions": { // names of this app's server functions
|
|
50
|
+
"testConnection": "testConnection",
|
|
51
|
+
"describeTarget": "describeTarget",
|
|
52
|
+
"sample": "sample",
|
|
53
|
+
"fetch": "fetch"
|
|
54
|
+
},
|
|
55
|
+
"panels": [ { "id": "explorer", "title": "Explorer", "component": "Explorer" } ],
|
|
56
|
+
"aiGuide": "connector-ai-guide.md"
|
|
57
|
+
},
|
|
58
|
+
"functions": {
|
|
59
|
+
"files": { "js": { "umd": "dist/functions.umd.js" } },
|
|
60
|
+
"definitions": [
|
|
61
|
+
{ "name": "testConnection", "trigger": { "type": "http", "method": "POST" },
|
|
62
|
+
"visibility": "admin", "authority": "collection",
|
|
63
|
+
"capabilities": ["network:content1-api.1worldsync.com", "secrets:oneworldsync-app-id", "secrets:oneworldsync-secret-key"] }
|
|
64
|
+
// …one definition per function named above
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Types: `AppConnectorDeclaration` and friends in `@proveanything/smartlinks` (`src/types/appManifest.ts`).
|
|
70
|
+
|
|
71
|
+
| Field | Meaning |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `provider` | Stable provider key (`syndigo`, `oneworldsync`). |
|
|
74
|
+
| `title` | Provider display name; defaults to the app name. |
|
|
75
|
+
| `directions` | `inbound` (provider → SmartLinks), `outbound` (SmartLinks → provider), or both. |
|
|
76
|
+
| `entities` | SmartLinks entities it maps, e.g. `["product"]`. |
|
|
77
|
+
| `connection.fields[]` | The console's **Connect** form. A field with `secret` is stored as this app's secret of that name (write-only; never returned to the browser). Other fields are saved in the installation's connection settings. |
|
|
78
|
+
| `settings` | JSON Schema for installation settings; the console renders it as a form. |
|
|
79
|
+
| `presets[]` | Ready-made flows ("Products → provider") with flow defaults. |
|
|
80
|
+
| `functions` | Which of this app's server functions implement each step (below). Fill in only what applies. |
|
|
81
|
+
| `panels[]` | Provider-specific screens the console mounts (an API explorer, a status page). |
|
|
82
|
+
| `aiGuide` | A guide the mapping assistant reads: the provider's quirks, required fields, good defaults. |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## The functions
|
|
87
|
+
|
|
88
|
+
Each step names one of the app's server functions. Declare them as **http POST, `visibility:
|
|
89
|
+
"admin"`, `authority: "collection"`**: the engine calls them with no signed-in user (a product edit, a
|
|
90
|
+
schedule), and the console calls the same functions while an admin configures the flow
|
|
91
|
+
(`functions.callAdmin(collectionId, name, input, { appId })`). The input arrives as `event.body`;
|
|
92
|
+
return the result as JSON. Capabilities still cap what each one can do: declare the provider's hosts
|
|
93
|
+
(`network:<host>`) and the secrets it reads (`secrets:<name>`).
|
|
94
|
+
|
|
95
|
+
| Step | Called by | Input (`event.body`) | Returns |
|
|
96
|
+
|---|---|---|---|
|
|
97
|
+
| `testConnection` | Connect form, **Test** | `{ installation }` | `ConnectorTestResult` `{ ok, message }` |
|
|
98
|
+
| `describeTarget` | Mapper opens | `{ installation }` | `ConnectorTargetSchema` `{ fields: [{ path, label, type, required, enum }] }`: the provider's fields *for this customer* |
|
|
99
|
+
| `sample` | Mapper, AI assist, dry run | `{ installation, limit }` | `ConnectorRecords` `{ records }`: real provider records |
|
|
100
|
+
| `transform` | Every run | `ConnectorTransformInput` `{ installation, records, mapping }` | `ConnectorTransformResult` `{ payloads, skipped }`: one payload per record, same order; `null` skips one |
|
|
101
|
+
| `send` | Every outbound run | `ConnectorSendInput` `{ installation, payloads }` | `ConnectorSendResult` `{ results: [{ index, ok, status, error, externalId }] }` |
|
|
102
|
+
| `fetch` | Every inbound run | `ConnectorFetchInput` `{ installation, cursor, limit }` | `ConnectorRecords` `{ records, nextCursor }`: one page per call |
|
|
103
|
+
|
|
104
|
+
What happens when a step is left out:
|
|
105
|
+
|
|
106
|
+
- **No `transform`:** the engine's own mapper runs the flow's `fieldMappings` (direct, static, Liquid
|
|
107
|
+
template).
|
|
108
|
+
- **No `send`:** the flow's own transport: HTTP (`config.connection`), or Azure Event Hub with
|
|
109
|
+
`config.transport: 'eventhub'` and `config.eventHub`.
|
|
110
|
+
- **No `describeTarget`:** the mapper offers free-text target paths.
|
|
111
|
+
|
|
112
|
+
### `installation`
|
|
113
|
+
|
|
114
|
+
Every step receives the installation it serves:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
interface ConnectorInstallation {
|
|
118
|
+
collectionId: string
|
|
119
|
+
appId: string // the connector app
|
|
120
|
+
flowId?: string | null // when called for a flow
|
|
121
|
+
connection: Record<string, unknown> // non-secret Connect-form fields
|
|
122
|
+
settings: Record<string, unknown> // installation settings, with the flow's overrides on top
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Secrets are never in it. Read them in the function with `ctx.secrets.get('<name>')`, which needs the
|
|
127
|
+
matching `secrets:<name>` capability.
|
|
128
|
+
|
|
129
|
+
### Example: `fetch`
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
// src/functions/index.ts (the app's functions bundle)
|
|
133
|
+
import type { ConnectorFetchInput, ConnectorRecords } from '@proveanything/smartlinks'
|
|
134
|
+
|
|
135
|
+
export async function fetch(ctx: any, event: any): Promise<ConnectorRecords> {
|
|
136
|
+
const { installation, cursor, limit = 50 } = event.body as ConnectorFetchInput
|
|
137
|
+
const appId = await ctx.secrets.get('provider-app-id')
|
|
138
|
+
const key = await ctx.secrets.get('provider-secret-key')
|
|
139
|
+
const res = await ctx.fetch(buildSignedUrl(installation, { cursor, limit }, appId, key))
|
|
140
|
+
const page = await res.json()
|
|
141
|
+
return { records: page.items, nextCursor: page.next ?? null }
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Where configuration lives
|
|
148
|
+
|
|
149
|
+
| What | Where |
|
|
150
|
+
|---|---|
|
|
151
|
+
| Connect-form values (non-secret), installation settings | The connector app's config on the collection: `appConfiguration` for `appId`, under `connector.connection` and `connector.settings`. |
|
|
152
|
+
| Credentials | The connector app's secrets, by the names its fields declare. |
|
|
153
|
+
| One flow's use of the connector | The flow: `config.connector = { appId, preset?, settings? }` (per-flow settings override the installation's), plus `config.fieldMappings`. |
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
await integrations.createFlow(collectionId, {
|
|
157
|
+
appId: 'core-integrations',
|
|
158
|
+
direction: 'inbound',
|
|
159
|
+
name: 'Import products from 1WorldSync',
|
|
160
|
+
status: 'active',
|
|
161
|
+
schedule: '0 * * * *',
|
|
162
|
+
targetEntity: 'product',
|
|
163
|
+
config: {
|
|
164
|
+
connector: { appId: 'oneworldsync', preset: 'import-products' },
|
|
165
|
+
fieldMappings: [ { targetPath: 'name', sourcePath: 'item.tradeItemDescription', transformType: 'direct' } ],
|
|
166
|
+
},
|
|
167
|
+
})
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## From the console
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { integrations, secrets } from '@proveanything/smartlinks'
|
|
176
|
+
|
|
177
|
+
const { connectors } = await integrations.listConnectors(collectionId)
|
|
178
|
+
// → [{ appId, name, version, connector, installation: { connection, settings }, secrets: { 'provider-key': { set, hint } } }]
|
|
179
|
+
|
|
180
|
+
await secrets.put(collectionId, 'provider-key', value, { appId }) // a secret field
|
|
181
|
+
await integrations.saveConnectorInstallation(collectionId, appId, { connection: { environment: 'production' } })
|
|
182
|
+
const test = await integrations.runConnectorStep(collectionId, appId, 'testConnection')
|
|
183
|
+
const target = await integrations.runConnectorStep(collectionId, appId, 'describeTarget')
|
|
184
|
+
const dryRun = await integrations.runConnectorStep(collectionId, appId, 'transform', { records, mapping })
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`send` and `fetch` only run inside a flow. A flow run's summary carries `sent` (outbound) or
|
|
188
|
+
`created` / `updated` / `skipped` (inbound), and every run, inbound or outbound, is in the run history.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Panels
|
|
193
|
+
|
|
194
|
+
A connector can contribute screens to the integrations console (an API explorer, say). Each
|
|
195
|
+
`panels[]` entry names a component exported from the app's widgets bundle; the console mounts it host-side and passes it the
|
|
196
|
+
collection and the installation. Panels are admin-only and call the connector's own functions.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Who can build connectors
|
|
201
|
+
|
|
202
|
+
First-party connectors run in-process, like any app server function. Third-party connectors will run
|
|
203
|
+
in the isolated runner (see [server-functions.md](server-functions.md)) once that opens up; the contract
|
|
204
|
+
does not change.
|