@proveanything/smartlinks 1.17.4 → 1.17.6
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 +7 -1
- package/dist/api/integrations.js +9 -0
- package/dist/docs/API_SUMMARY.md +85 -300
- package/dist/docs/ai-tools-and-skills.md +13 -1
- package/dist/docs/server-functions.md +234 -0
- package/dist/openapi.yaml +169 -0
- package/dist/types/appManifest.d.ts +107 -0
- package/dist/types/integrations.d.ts +8 -0
- package/docs/API_SUMMARY.md +85 -300
- package/docs/ai-tools-and-skills.md +13 -1
- package/docs/server-functions.md +234 -0
- package/openapi.yaml +169 -0
- package/package.json +1 -1
|
@@ -147,7 +147,7 @@ _Capabilities: web:read_
|
|
|
147
147
|
|
|
148
148
|
### `image.generate`
|
|
149
149
|
|
|
150
|
-
Generate a new image from a text prompt. Returns
|
|
150
|
+
Generate a new image from a text prompt. Returns a stable hosted image URL (hostedUrl).
|
|
151
151
|
|
|
152
152
|
_Capabilities: ai:image_
|
|
153
153
|
|
|
@@ -156,6 +156,18 @@ _Capabilities: ai:image_
|
|
|
156
156
|
- `size` — string: e.g. "1024x1024".
|
|
157
157
|
- `provider` — `openai` | `gemini`: Image model provider.
|
|
158
158
|
|
|
159
|
+
### `image.fromReference`
|
|
160
|
+
|
|
161
|
+
Generate a new image guided by one or more reference images plus a prompt (image-to-image). Use to restyle, combine, or vary existing images. Returns a stable hosted image URL (hostedUrl).
|
|
162
|
+
|
|
163
|
+
_Capabilities: ai:image_
|
|
164
|
+
|
|
165
|
+
**Parameters**
|
|
166
|
+
- `prompt` _(required)_ — string: How to transform / what to create from the reference(s).
|
|
167
|
+
- `imageUrls` _(required)_ — array: Reference image URL(s) to guide generation.
|
|
168
|
+
- `size` — string: e.g. "1024x1024".
|
|
169
|
+
- `model` — string: Image model (default gpt-image-1).
|
|
170
|
+
|
|
159
171
|
### `image.searchStock`
|
|
160
172
|
|
|
161
173
|
Search stock photography (Unsplash) for real photos matching a query. Returns candidate image URLs.
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Server functions ("edge functions")
|
|
2
|
+
|
|
3
|
+
A **server function** is arbitrary server-side JavaScript your app deploys directly into
|
|
4
|
+
SmartLinks. It runs on the SmartLinks servers — with access to the full SDK, to your app's
|
|
5
|
+
secrets, and to outbound network — so you can do things a browser app can't: validate and
|
|
6
|
+
write on the server, call third-party systems with credentials the client never sees, react
|
|
7
|
+
to events, and run scheduled work.
|
|
8
|
+
|
|
9
|
+
Every server function has the **same shape**, no matter how it's triggered or where it runs:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
export async function myFunction(ctx, event) {
|
|
13
|
+
// ctx — a SmartLinks SDK pre-scoped to your declared authority, plus secrets, caller, fetch, log
|
|
14
|
+
// event — the trigger payload (the HTTP body, the event, or the cron tick)
|
|
15
|
+
return { ok: true } // returned to the caller (http) or recorded as the run result (event/cron)
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
You never receive a raw API key or a superuser client. The platform builds `ctx` fresh for
|
|
20
|
+
each invocation and **pre-scopes every handle to exactly what your function declared** — this
|
|
21
|
+
is how a function stays safe even when it runs with elevated authority.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Declaring a function
|
|
26
|
+
|
|
27
|
+
Functions are declared in your `app.manifest.json` under `functions`, and the handlers ship
|
|
28
|
+
in a bundle alongside your widgets/containers:
|
|
29
|
+
|
|
30
|
+
```jsonc
|
|
31
|
+
{
|
|
32
|
+
"functions": {
|
|
33
|
+
"files": { "js": { "umd": "dist/functions.umd.js" } },
|
|
34
|
+
"definitions": [
|
|
35
|
+
{
|
|
36
|
+
"name": "submitCompetitionEntry",
|
|
37
|
+
"description": "Validate a competition entry and record it server-side.",
|
|
38
|
+
"trigger": { "type": "http", "methods": ["POST"] },
|
|
39
|
+
"visibility": "public", // WHO may call it
|
|
40
|
+
"authority": "collection", // WHOSE authority it runs as
|
|
41
|
+
"capabilities": ["sl:records:write", "network:api.recaptcha.net"],
|
|
42
|
+
"apiVersion": "2026-09"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "onProofCreated",
|
|
46
|
+
"trigger": { "type": "event", "eventTypes": ["proof.created"] },
|
|
47
|
+
"capabilities": ["sl:records:write", "secrets:crm-key", "network"]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "nightlyReconcile",
|
|
51
|
+
"trigger": { "type": "cron", "schedule": "0 2 * * *" },
|
|
52
|
+
"capabilities": ["sl:products:read", "sl:records:write"]
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Each definition maps to an exported handler of the same `name` (override with `handler`).
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## The two security questions every function answers
|
|
64
|
+
|
|
65
|
+
Server-side code needs two questions answered up front. SmartLinks makes both **explicit and
|
|
66
|
+
declarative** — you state them in the manifest, and the platform enforces them. This is the
|
|
67
|
+
same split every extensible platform lands on (Salesforce `with/without sharing`, Shopify
|
|
68
|
+
online/offline tokens, Lambda's invoke-policy vs execution-role).
|
|
69
|
+
|
|
70
|
+
### 1. `visibility` — who is allowed to call it? *(http only)*
|
|
71
|
+
|
|
72
|
+
| Value | Meaning |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `admin` *(default)* | Callable only from an authenticated admin surface. |
|
|
75
|
+
| `public` | Publicly callable — no authenticated user required. |
|
|
76
|
+
|
|
77
|
+
### 2. `authority` — whose authority does it run as?
|
|
78
|
+
|
|
79
|
+
This decides what `ctx.sl` can do.
|
|
80
|
+
|
|
81
|
+
| Value | `ctx.sl` is scoped to | Use when |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `caller` *(default)* | The **invoking user** (their session/JWT). Can only do what that user could. | Admin actions that should respect the user's own permissions and be attributed to them. |
|
|
84
|
+
| `collection` | A **collection-admin principal for _this_ collection only** — never a global superuser. | A privileged server-side action a public/anonymous caller can't be trusted to do directly. |
|
|
85
|
+
|
|
86
|
+
`authority` **defaults to `caller`** — secure by default. `event`- and `cron`-triggered
|
|
87
|
+
functions have no external caller, so they always run as `collection`.
|
|
88
|
+
|
|
89
|
+
### The combinations
|
|
90
|
+
|
|
91
|
+
- **`http` + `admin` + `caller`** — a delegated admin function; runs as the signed-in admin.
|
|
92
|
+
- **`http` + `public` + `caller`** — runs as the anonymous/public user (limited). The safe public default.
|
|
93
|
+
- **`http` + `public` + `collection`** — publicly callable, runs with collection authority.
|
|
94
|
+
The powerful one (e.g. "submit competition entry" needs to validate then write a record no
|
|
95
|
+
anonymous user may write directly). **Your responsibility:** validate the input and prevent
|
|
96
|
+
abuse — see below.
|
|
97
|
+
- **`event` / `cron`** — background work; runs as `collection`.
|
|
98
|
+
|
|
99
|
+
> ### ⚠️ Public + collection authority is the sharp edge
|
|
100
|
+
> A `public` + `collection` function is reachable by anyone and runs with elevated authority.
|
|
101
|
+
> The platform shrinks the blast radius for you — the authority is capped to **this one
|
|
102
|
+
> collection**, and further capped by your declared **capabilities** (a competition-entry
|
|
103
|
+
> function that declares only `sl:records:write` cannot delete products or read other
|
|
104
|
+
> secrets, even though it's "elevated"). But **validating the request is still your job**:
|
|
105
|
+
> check the payload, rate-limit using `ctx.caller`, guard against replay. Treat the function
|
|
106
|
+
> body as a trust boundary.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Capabilities — least privilege, declared and capped
|
|
111
|
+
|
|
112
|
+
`capabilities` is the allow-list of what your function may reach. It is surfaced at install
|
|
113
|
+
time for consent and **enforced at runtime** — including for `collection`-authority functions.
|
|
114
|
+
Declare the minimum you need.
|
|
115
|
+
|
|
116
|
+
| Capability | Grants |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `sl:<resource>:read` / `sl:<resource>:write` | SDK access to that resource, e.g. `sl:products:read`, `sl:records:write`, `sl:attestations:write`, `sl:contacts:write`. |
|
|
119
|
+
| `network` | `ctx.fetch` to any host. |
|
|
120
|
+
| `network:<host>` | `ctx.fetch` to that host only (repeat for several). Prefer this over blanket `network`. |
|
|
121
|
+
| `secrets:<ref>` | `ctx.secrets.get('<ref>')` for that one secret ref. |
|
|
122
|
+
|
|
123
|
+
If you don't declare `network`, `ctx.fetch` is absent. If you don't declare a `secrets:<ref>`,
|
|
124
|
+
`ctx.secrets.get('<ref>')` returns `null`.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## The `ctx` object
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
interface ServerFunctionContext {
|
|
132
|
+
collectionId: string
|
|
133
|
+
appId: string
|
|
134
|
+
|
|
135
|
+
/** SmartLinks SDK, pre-scoped to your declared `authority`. Capabilities cap what it may do. */
|
|
136
|
+
sl: SmartLinks
|
|
137
|
+
|
|
138
|
+
/** Capability-gated secrets. Resolves only refs you declared via `secrets:<ref>`. */
|
|
139
|
+
secrets: { get(ref: string): Promise<string | null> }
|
|
140
|
+
|
|
141
|
+
/** Who invoked this function. */
|
|
142
|
+
caller: {
|
|
143
|
+
userId: string | null // null for anonymous/public/system calls
|
|
144
|
+
anonymous: boolean
|
|
145
|
+
origin?: string | null // http
|
|
146
|
+
ip?: string | null // http
|
|
147
|
+
via: 'http' | 'event' | 'cron'
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Outbound HTTP — present only if you declared `network` (host-scoped if `network:<host>`). */
|
|
151
|
+
fetch: typeof fetch
|
|
152
|
+
|
|
153
|
+
/** Structured logging, captured into your function's run telemetry. */
|
|
154
|
+
log: (message: string, data?: Record<string, any>) => void
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Use `ctx.sl` for anything SmartLinks** — it is the same SDK surface you use client-side,
|
|
159
|
+
already authenticated as your declared authority and scoped to the collection. Do **not** try
|
|
160
|
+
to construct your own SDK client or carry your own key; that's what `ctx.sl` is for, and it's
|
|
161
|
+
the only way authority stays correct.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Worked example — a public competition entry
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
// dist/functions — handler for the manifest definition above
|
|
169
|
+
export async function submitCompetitionEntry(ctx, event) {
|
|
170
|
+
const { name, email, answer, captchaToken } = event.body || {}
|
|
171
|
+
|
|
172
|
+
// 1. Validate — this is YOUR job on a public function.
|
|
173
|
+
if (!email || !answer) return { ok: false, error: 'missing_fields' }
|
|
174
|
+
|
|
175
|
+
// 2. Use a declared secret + declared host to verify a captcha.
|
|
176
|
+
const secret = await ctx.secrets.get('recaptcha-secret')
|
|
177
|
+
const verify = await ctx.fetch('https://api.recaptcha.net/verify', {
|
|
178
|
+
method: 'POST',
|
|
179
|
+
body: new URLSearchParams({ secret, response: captchaToken }),
|
|
180
|
+
}).then(r => r.json())
|
|
181
|
+
if (!verify.success) return { ok: false, error: 'captcha_failed' }
|
|
182
|
+
|
|
183
|
+
// 3. Write with collection authority — something an anonymous caller can't do directly.
|
|
184
|
+
const entry = await ctx.sl.appRecords.create({
|
|
185
|
+
recordType: 'competition-entry',
|
|
186
|
+
data: { name, email, answer, ip: ctx.caller.ip, submittedAt: new Date().toISOString() },
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
ctx.log('entry recorded', { entryId: entry.id })
|
|
190
|
+
return { ok: true, entryId: entry.id }
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Declared as `public` + `collection` + `['sl:records:write', 'secrets:recaptcha-secret',
|
|
195
|
+
'network:api.recaptcha.net']`, this function is publicly callable, verifies the request
|
|
196
|
+
itself, and writes a record no anonymous user could write — but it cannot touch products,
|
|
197
|
+
other secrets, or any other collection.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Invoking an http function
|
|
202
|
+
|
|
203
|
+
An `http` function is called by POSTing to the collection's functions endpoint on the
|
|
204
|
+
surface that matches its `visibility`:
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
POST /admin/collection/:collectionId/functions/:name # visibility: admin (collection-admin auth)
|
|
208
|
+
POST /public/collection/:collectionId/functions/:name # visibility: public (auth optional)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The request body is delivered to the handler as `event.body` (query string as
|
|
212
|
+
`event.query`). A function only runs on its own surface — calling an `admin` function on
|
|
213
|
+
the public endpoint is a `403`. The response is `{ ok: true, result }` on success, or
|
|
214
|
+
`{ error, message }` (HTTP 400) if the handler returned an error. `GET` on either endpoint
|
|
215
|
+
lists the functions callable on that surface.
|
|
216
|
+
|
|
217
|
+
The admin surface is collection-admin gated, so an admin function's `caller` authority runs
|
|
218
|
+
at admin level, attributed to the signed-in admin. The public surface resolves auth if a
|
|
219
|
+
token is present (→ `owner`) and treats its absence as anonymous (→ `public`); a
|
|
220
|
+
`collection`-authority function runs elevated regardless.
|
|
221
|
+
|
|
222
|
+
## Where functions run (and why it doesn't change how you write them)
|
|
223
|
+
|
|
224
|
+
SmartLinks runs first-party (trusted) functions **in-process** and untrusted third-party
|
|
225
|
+
functions in an **isolated runner**. The difference is enforcement, not authoring:
|
|
226
|
+
|
|
227
|
+
- **In-process** trusts the author; `ctx` is built directly.
|
|
228
|
+
- **Isolated runner** enforces the `authority` boundary and `capabilities` at the container
|
|
229
|
+
edge — `ctx.sl` is a proxy over the declared authority, `ctx.fetch` is filtered to the
|
|
230
|
+
declared hosts, and there is no ambient filesystem, network, or environment.
|
|
231
|
+
|
|
232
|
+
Because the **contract is identical**, a function you write today runs unchanged if it later
|
|
233
|
+
moves lanes. Write to the `ctx` contract and declare your capabilities honestly, and the
|
|
234
|
+
platform takes care of the rest.
|
package/dist/openapi.yaml
CHANGED
|
@@ -38,6 +38,7 @@ tags:
|
|
|
38
38
|
- name: crate
|
|
39
39
|
- name: facets
|
|
40
40
|
- name: form
|
|
41
|
+
- name: integrations
|
|
41
42
|
- name: interactions
|
|
42
43
|
- name: jobs
|
|
43
44
|
- name: journeys
|
|
@@ -4164,6 +4165,33 @@ paths:
|
|
|
4164
4165
|
description: Unauthorized
|
|
4165
4166
|
404:
|
|
4166
4167
|
description: Not found
|
|
4168
|
+
/admin/collection/{collectionId}/integrations/record-types:
|
|
4169
|
+
get:
|
|
4170
|
+
tags:
|
|
4171
|
+
- integrations
|
|
4172
|
+
summary: Discover the app-record types present in a collection + which app owns each (introspected), for picking a sub-record source/trigger.
|
|
4173
|
+
operationId: integrations_listRecordTypes
|
|
4174
|
+
security:
|
|
4175
|
+
- bearerAuth: []
|
|
4176
|
+
parameters:
|
|
4177
|
+
- name: collectionId
|
|
4178
|
+
in: path
|
|
4179
|
+
required: true
|
|
4180
|
+
schema:
|
|
4181
|
+
type: string
|
|
4182
|
+
responses:
|
|
4183
|
+
200:
|
|
4184
|
+
description: Success
|
|
4185
|
+
content:
|
|
4186
|
+
application/json:
|
|
4187
|
+
schema:
|
|
4188
|
+
$ref: "#/components/schemas/RecordTypesResponse"
|
|
4189
|
+
400:
|
|
4190
|
+
description: Bad request
|
|
4191
|
+
401:
|
|
4192
|
+
description: Unauthorized
|
|
4193
|
+
404:
|
|
4194
|
+
description: Not found
|
|
4167
4195
|
/admin/collection/{collectionId}/interactions:
|
|
4168
4196
|
get:
|
|
4169
4197
|
tags:
|
|
@@ -17215,6 +17243,107 @@ components:
|
|
|
17215
17243
|
required:
|
|
17216
17244
|
- files
|
|
17217
17245
|
- function
|
|
17246
|
+
AppFunctionTrigger:
|
|
17247
|
+
type: object
|
|
17248
|
+
properties:
|
|
17249
|
+
type:
|
|
17250
|
+
$ref: "#/components/schemas/AppFunctionTriggerType"
|
|
17251
|
+
eventTypes:
|
|
17252
|
+
type: array
|
|
17253
|
+
items:
|
|
17254
|
+
type: string
|
|
17255
|
+
schedule:
|
|
17256
|
+
type: string
|
|
17257
|
+
route:
|
|
17258
|
+
type: string
|
|
17259
|
+
methods:
|
|
17260
|
+
type: array
|
|
17261
|
+
items:
|
|
17262
|
+
type: string
|
|
17263
|
+
enum:
|
|
17264
|
+
- GET
|
|
17265
|
+
- POST
|
|
17266
|
+
- PUT
|
|
17267
|
+
- PATCH
|
|
17268
|
+
- DELETE
|
|
17269
|
+
required:
|
|
17270
|
+
- type
|
|
17271
|
+
AppFunctionDef:
|
|
17272
|
+
type: object
|
|
17273
|
+
properties:
|
|
17274
|
+
name:
|
|
17275
|
+
type: string
|
|
17276
|
+
description:
|
|
17277
|
+
type: string
|
|
17278
|
+
trigger:
|
|
17279
|
+
$ref: "#/components/schemas/AppFunctionTrigger"
|
|
17280
|
+
visibility:
|
|
17281
|
+
$ref: "#/components/schemas/AppFunctionVisibility"
|
|
17282
|
+
authority:
|
|
17283
|
+
$ref: "#/components/schemas/AppFunctionAuthority"
|
|
17284
|
+
capabilities:
|
|
17285
|
+
type: array
|
|
17286
|
+
items:
|
|
17287
|
+
type: string
|
|
17288
|
+
apiVersion:
|
|
17289
|
+
type: string
|
|
17290
|
+
handler:
|
|
17291
|
+
type: string
|
|
17292
|
+
required:
|
|
17293
|
+
- name
|
|
17294
|
+
- trigger
|
|
17295
|
+
AppManifestFunctions:
|
|
17296
|
+
type: object
|
|
17297
|
+
properties:
|
|
17298
|
+
files:
|
|
17299
|
+
$ref: "#/components/schemas/AppManifestFiles"
|
|
17300
|
+
definitions:
|
|
17301
|
+
type: array
|
|
17302
|
+
items:
|
|
17303
|
+
$ref: "#/components/schemas/AppFunctionDef"
|
|
17304
|
+
required:
|
|
17305
|
+
- files
|
|
17306
|
+
- definitions
|
|
17307
|
+
ServerFunctionCaller:
|
|
17308
|
+
type: object
|
|
17309
|
+
properties:
|
|
17310
|
+
userId:
|
|
17311
|
+
type: string
|
|
17312
|
+
anonymous:
|
|
17313
|
+
type: boolean
|
|
17314
|
+
origin:
|
|
17315
|
+
type: string
|
|
17316
|
+
ip:
|
|
17317
|
+
type: string
|
|
17318
|
+
via:
|
|
17319
|
+
$ref: "#/components/schemas/AppFunctionTriggerType"
|
|
17320
|
+
required:
|
|
17321
|
+
- userId
|
|
17322
|
+
- anonymous
|
|
17323
|
+
- via
|
|
17324
|
+
ServerFunctionContext:
|
|
17325
|
+
type: object
|
|
17326
|
+
properties:
|
|
17327
|
+
collectionId:
|
|
17328
|
+
type: string
|
|
17329
|
+
appId:
|
|
17330
|
+
type: string
|
|
17331
|
+
sl: {}
|
|
17332
|
+
secrets:
|
|
17333
|
+
type: object
|
|
17334
|
+
additionalProperties: true
|
|
17335
|
+
caller:
|
|
17336
|
+
$ref: "#/components/schemas/ServerFunctionCaller"
|
|
17337
|
+
fetch:
|
|
17338
|
+
type: object
|
|
17339
|
+
additionalProperties: true
|
|
17340
|
+
required:
|
|
17341
|
+
- collectionId
|
|
17342
|
+
- appId
|
|
17343
|
+
- sl
|
|
17344
|
+
- secrets
|
|
17345
|
+
- caller
|
|
17346
|
+
- fetch
|
|
17218
17347
|
AppAdminConfig:
|
|
17219
17348
|
type: object
|
|
17220
17349
|
properties:
|
|
@@ -17357,6 +17486,8 @@ components:
|
|
|
17357
17486
|
$ref: "#/components/schemas/DeepLinkEntry"
|
|
17358
17487
|
executor:
|
|
17359
17488
|
$ref: "#/components/schemas/AppManifestExecutor"
|
|
17489
|
+
functions:
|
|
17490
|
+
$ref: "#/components/schemas/AppManifestFunctions"
|
|
17360
17491
|
required:
|
|
17361
17492
|
- name
|
|
17362
17493
|
- version
|
|
@@ -17397,6 +17528,22 @@ components:
|
|
|
17397
17528
|
properties:
|
|
17398
17529
|
force:
|
|
17399
17530
|
type: boolean
|
|
17531
|
+
AppFunctionTriggerType:
|
|
17532
|
+
type: string
|
|
17533
|
+
enum:
|
|
17534
|
+
- http
|
|
17535
|
+
- event
|
|
17536
|
+
- cron
|
|
17537
|
+
AppFunctionVisibility:
|
|
17538
|
+
type: string
|
|
17539
|
+
enum:
|
|
17540
|
+
- admin
|
|
17541
|
+
- public
|
|
17542
|
+
AppFunctionAuthority:
|
|
17543
|
+
type: string
|
|
17544
|
+
enum:
|
|
17545
|
+
- caller
|
|
17546
|
+
- collection
|
|
17400
17547
|
PaginatedResponse:
|
|
17401
17548
|
type: object
|
|
17402
17549
|
properties:
|
|
@@ -23429,6 +23576,28 @@ components:
|
|
|
23429
23576
|
properties:
|
|
23430
23577
|
purpose:
|
|
23431
23578
|
type: string
|
|
23579
|
+
RecordTypeInfo:
|
|
23580
|
+
type: object
|
|
23581
|
+
properties:
|
|
23582
|
+
appId:
|
|
23583
|
+
type: string
|
|
23584
|
+
recordType:
|
|
23585
|
+
type: string
|
|
23586
|
+
count:
|
|
23587
|
+
type: number
|
|
23588
|
+
required:
|
|
23589
|
+
- appId
|
|
23590
|
+
- recordType
|
|
23591
|
+
- count
|
|
23592
|
+
RecordTypesResponse:
|
|
23593
|
+
type: object
|
|
23594
|
+
properties:
|
|
23595
|
+
recordTypes:
|
|
23596
|
+
type: array
|
|
23597
|
+
items:
|
|
23598
|
+
$ref: "#/components/schemas/RecordTypeInfo"
|
|
23599
|
+
required:
|
|
23600
|
+
- recordTypes
|
|
23432
23601
|
RunFlowResult:
|
|
23433
23602
|
type: object
|
|
23434
23603
|
additionalProperties: true
|
|
@@ -204,6 +204,106 @@ export interface AppManifestExecutor {
|
|
|
204
204
|
responseShape?: Record<string, any>;
|
|
205
205
|
};
|
|
206
206
|
}
|
|
207
|
+
/** What causes a server function to run. */
|
|
208
|
+
export type AppFunctionTriggerType = 'http' | 'event' | 'cron';
|
|
209
|
+
export interface AppFunctionTrigger {
|
|
210
|
+
type: AppFunctionTriggerType;
|
|
211
|
+
/** `event`: event types this function subscribes to, e.g. `['interaction.submitted:comp-entry']`. */
|
|
212
|
+
eventTypes?: string[];
|
|
213
|
+
/** `cron`: standard 5-field crontab expression, evaluated in UTC. */
|
|
214
|
+
schedule?: string;
|
|
215
|
+
/** `http`: URL path segment the function is exposed at (defaults to the function `name`). */
|
|
216
|
+
route?: string;
|
|
217
|
+
/** `http`: accepted HTTP methods (defaults to `['POST']`). */
|
|
218
|
+
methods?: Array<'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'>;
|
|
219
|
+
}
|
|
220
|
+
/** Who is allowed to invoke an `http`-triggered function. */
|
|
221
|
+
export type AppFunctionVisibility = 'admin' | 'public';
|
|
222
|
+
/**
|
|
223
|
+
* Whose authority the function runs with — i.e. what `ctx.sl` can do.
|
|
224
|
+
* - `caller` — runs as the invoking user (their session/JWT). The secure default.
|
|
225
|
+
* - `collection` — runs with collection-admin authority over THIS collection only
|
|
226
|
+
* (never a global superuser). Required for `public` functions that
|
|
227
|
+
* must perform a privileged server-side action; the author is then
|
|
228
|
+
* responsible for validating input and preventing abuse.
|
|
229
|
+
*/
|
|
230
|
+
export type AppFunctionAuthority = 'caller' | 'collection';
|
|
231
|
+
/** One server function declared in the manifest. */
|
|
232
|
+
export interface AppFunctionDef {
|
|
233
|
+
/** Stable identifier, unique within the app, e.g. `'submitCompetitionEntry'`. */
|
|
234
|
+
name: string;
|
|
235
|
+
description?: string;
|
|
236
|
+
trigger: AppFunctionTrigger;
|
|
237
|
+
/**
|
|
238
|
+
* `http` only. `admin` = authenticated admin surface; `public` = publicly callable.
|
|
239
|
+
* Ignored for `event`/`cron` (no external caller). Defaults to `admin`.
|
|
240
|
+
*/
|
|
241
|
+
visibility?: AppFunctionVisibility;
|
|
242
|
+
/**
|
|
243
|
+
* Whose authority `ctx.sl` carries. Defaults to `caller` (secure by default).
|
|
244
|
+
* `event`/`cron` functions have no caller and always run as `collection`.
|
|
245
|
+
*/
|
|
246
|
+
authority?: AppFunctionAuthority;
|
|
247
|
+
/**
|
|
248
|
+
* Least-privilege capabilities this function needs, surfaced at install for
|
|
249
|
+
* consent and capped at runtime — even for `collection`-authority functions.
|
|
250
|
+
* e.g. `['sl:records:write', 'network:api.example.com', 'secrets:syndigo-key']`.
|
|
251
|
+
* Grammar: `sl:<resource>:<read|write>`, `network` (or `network:<host>`), `secrets:<ref>`.
|
|
252
|
+
*/
|
|
253
|
+
capabilities?: string[];
|
|
254
|
+
/** SDK/manifest API version this function targets (pinned for runtime compatibility). */
|
|
255
|
+
apiVersion?: string;
|
|
256
|
+
/** Exported handler name in the functions bundle. Defaults to `name`. */
|
|
257
|
+
handler?: string;
|
|
258
|
+
}
|
|
259
|
+
/** The `functions` block in `app.manifest.json`. Presence means the app ships server functions. */
|
|
260
|
+
export interface AppManifestFunctions {
|
|
261
|
+
files: AppManifestFiles;
|
|
262
|
+
definitions: AppFunctionDef[];
|
|
263
|
+
}
|
|
264
|
+
/** Identity of whoever invoked a server function. */
|
|
265
|
+
export interface ServerFunctionCaller {
|
|
266
|
+
/** Authenticated user id, or `null` for anonymous/public/system invocations. */
|
|
267
|
+
userId: string | null;
|
|
268
|
+
/** True when no authenticated user is present (public/anonymous call, or event/cron). */
|
|
269
|
+
anonymous: boolean;
|
|
270
|
+
/** Request origin/referer, when available (`http` trigger). */
|
|
271
|
+
origin?: string | null;
|
|
272
|
+
/** Client IP, when available (`http` trigger). */
|
|
273
|
+
ip?: string | null;
|
|
274
|
+
/** How the function was triggered. */
|
|
275
|
+
via: AppFunctionTriggerType;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* The context handed to every SmartLinks server function. The platform builds a
|
|
279
|
+
* fresh one per invocation and pre-scopes each handle to the function's declared
|
|
280
|
+
* authority + capabilities. The function receives only handles already narrowed
|
|
281
|
+
* to what it declared — no raw keys, no ambient superuser client.
|
|
282
|
+
*/
|
|
283
|
+
export interface ServerFunctionContext {
|
|
284
|
+
collectionId: string;
|
|
285
|
+
appId: string;
|
|
286
|
+
/**
|
|
287
|
+
* SmartLinks SDK, pre-scoped to the function's declared `authority`:
|
|
288
|
+
* - `caller` → scoped to the invoking user (their JWT).
|
|
289
|
+
* - `collection` → scoped to a collection-admin principal for THIS collection
|
|
290
|
+
* only — never a global superuser.
|
|
291
|
+
* Declared `capabilities` cap what these calls may do.
|
|
292
|
+
*/
|
|
293
|
+
sl: any;
|
|
294
|
+
/** Capability-gated secret access. `get(ref)` resolves only refs granted via `secrets:<ref>`. */
|
|
295
|
+
secrets: {
|
|
296
|
+
get(ref: string): Promise<string | null>;
|
|
297
|
+
};
|
|
298
|
+
/** Who invoked this function. */
|
|
299
|
+
caller: ServerFunctionCaller;
|
|
300
|
+
/** Outbound HTTP — present only when the `network` capability is declared (host-scoped if `network:<host>`). */
|
|
301
|
+
fetch: typeof fetch;
|
|
302
|
+
/** Structured logging captured into run telemetry. */
|
|
303
|
+
log: (message: string, data?: Record<string, any>) => void;
|
|
304
|
+
}
|
|
305
|
+
/** The signature every SmartLinks server function implements. */
|
|
306
|
+
export type ServerFunctionHandler<TEvent = any, TResult = any> = (ctx: ServerFunctionContext, event: TEvent) => Promise<TResult> | TResult;
|
|
207
307
|
/**
|
|
208
308
|
* Shape of `app.admin.json` -- the separate admin configuration file pointed to
|
|
209
309
|
* by `AppManifest.admin`. Fetch this file yourself when you need setup / import /
|
|
@@ -342,6 +442,13 @@ export interface AppManifest {
|
|
|
342
442
|
* @see AppManifestExecutor
|
|
343
443
|
*/
|
|
344
444
|
executor?: AppManifestExecutor;
|
|
445
|
+
/**
|
|
446
|
+
* Server functions ("edge functions") this app deploys into SmartLinks —
|
|
447
|
+
* arbitrary server-side code triggered by http, events, or cron, each running
|
|
448
|
+
* with a declared authority + least-privilege capabilities.
|
|
449
|
+
* @see AppManifestFunctions
|
|
450
|
+
*/
|
|
451
|
+
functions?: AppManifestFunctions;
|
|
345
452
|
[key: string]: any;
|
|
346
453
|
}
|
|
347
454
|
/**
|
|
@@ -134,3 +134,11 @@ export interface SetSecretResult {
|
|
|
134
134
|
export interface ListSecretsQuery {
|
|
135
135
|
purpose?: string;
|
|
136
136
|
}
|
|
137
|
+
export interface RecordTypeInfo {
|
|
138
|
+
appId: string;
|
|
139
|
+
recordType: string;
|
|
140
|
+
count: number;
|
|
141
|
+
}
|
|
142
|
+
export interface RecordTypesResponse {
|
|
143
|
+
recordTypes: RecordTypeInfo[];
|
|
144
|
+
}
|