@proveanything/smartlinks 2.0.2 → 2.0.3
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/docs/API_SUMMARY.md
CHANGED
|
@@ -134,7 +134,7 @@ Content-Type: application/json
|
|
|
134
134
|
|
|
135
135
|
| Status | Meaning |
|
|
136
136
|
|---|---|
|
|
137
|
-
| `200` | Registered. Body: `{ ok: true, appId, channel, version, functions: [names], registeredAt }` |
|
|
137
|
+
| `200` | Registered. Body: `{ ok: true, appId, channel, version, functions: [names], registeredAt, warnings? }` — `warnings` is present for non-blocking issues (e.g. `APPID_IGNORED` when the manifest declared a different id). |
|
|
138
138
|
| `422` | Validation failed — **the build should fail**. Body: `{ ok: false, errors: [...] }` |
|
|
139
139
|
| `401` | Missing/invalid deploy key |
|
|
140
140
|
| `403` | Key not permitted for that channel (e.g. a dev key targeting `prod`) — `CHANNEL_FORBIDDEN` |
|
|
@@ -159,7 +159,9 @@ interface ServerFunctionContext {
|
|
|
159
159
|
/** Outbound HTTP — present only if you declared `network` (host-scoped if `network:<host>`). */
|
|
160
160
|
fetch: typeof fetch
|
|
161
161
|
|
|
162
|
-
/** Structured logging
|
|
162
|
+
/** Structured logging — returned inline on the admin surface and recorded as an
|
|
163
|
+
* execution activity event (owner Errors & Activity console). See "Seeing your
|
|
164
|
+
* function's runs and logs" below. */
|
|
163
165
|
log: (message: string, data?: Record<string, any>) => void
|
|
164
166
|
}
|
|
165
167
|
```
|
|
@@ -171,6 +173,73 @@ the only way authority stays correct.
|
|
|
171
173
|
|
|
172
174
|
---
|
|
173
175
|
|
|
176
|
+
## Runtime — what your function can use
|
|
177
|
+
|
|
178
|
+
Your function runs in a **web-standard sandbox** (think Cloudflare Workers / Deno), **not
|
|
179
|
+
Node**. Concretely it targets the **WinterTC Minimum Common Web Platform API** (WinterTC is the
|
|
180
|
+
Ecma International technical committee for this, formerly WinterCG) — the same surface those
|
|
181
|
+
runtimes guarantee — so what you can rely on is portable, and the future isolated runner will
|
|
182
|
+
host the same bundle unchanged.
|
|
183
|
+
|
|
184
|
+
**Available globals** (no import needed):
|
|
185
|
+
- **Data / encoding:** `JSON`, `URL`, `URLSearchParams`, `TextEncoder` / `TextDecoder`,
|
|
186
|
+
`atob` / `btoa`, `Buffer`, `structuredClone`.
|
|
187
|
+
- **Crypto:** `crypto` (Web Crypto) — `crypto.subtle` for hashing / HMAC / encrypt / sign /
|
|
188
|
+
verify, `crypto.getRandomValues`, `crypto.randomUUID`.
|
|
189
|
+
- **HTTP:** `fetch`, `Headers`, `Request`, `Response`, `FormData`, `Blob`, `File`,
|
|
190
|
+
`AbortController` / `AbortSignal` (use one for **request timeouts**).
|
|
191
|
+
- **Streams & timing:** `ReadableStream` / `WritableStream` / `TransformStream`,
|
|
192
|
+
`CompressionStream` / `DecompressionStream` (gzip/deflate for third-party payloads), the timer
|
|
193
|
+
functions, `queueMicrotask`, `performance`, `console`.
|
|
194
|
+
- **Runtime identity:** `navigator.userAgent` reports the runtime key (currently
|
|
195
|
+
`"SmartLinks-Functions"`) — use it if a portable dependency needs to feature-detect the host.
|
|
196
|
+
It stays stable when functions move to the isolated runner.
|
|
197
|
+
|
|
198
|
+
**Not available:** `require` / `import` of platform or Node modules, `process`, `fs`, and the
|
|
199
|
+
Node built-ins (`node:crypto`, `node:http`, …). There is no ambient database, key, or network
|
|
200
|
+
handle — everything the platform gives you comes through **`ctx`**.
|
|
201
|
+
|
|
202
|
+
**Dependencies:** bundle them. Your build must produce a **self-contained** UMD (deps compiled
|
|
203
|
+
in), and those deps must be **edge-compatible** — pure JS / Web APIs. A library that reaches for
|
|
204
|
+
Node built-ins (e.g. `axios`'s Node adapter, anything using `node:crypto`) will fail to load.
|
|
205
|
+
Prefer the platform primitives above over a dependency; when you do need one, pick edge-safe:
|
|
206
|
+
|
|
207
|
+
| Need | Native? | Recommended (bundle, edge-safe) |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| JSON | **native** — `JSON.parse/stringify` | — |
|
|
210
|
+
| XML parse / build | no | `fast-xml-parser` |
|
|
211
|
+
| CSV | no | `papaparse` |
|
|
212
|
+
| Schema validation of inputs | no | `zod` |
|
|
213
|
+
| JWT (sign/verify for a third-party API) | Web Crypto can, verbosely | `jose` |
|
|
214
|
+
| Hash / HMAC / encrypt | **native** — `crypto.subtle` | — |
|
|
215
|
+
|
|
216
|
+
**The common actions, and how to do each:**
|
|
217
|
+
|
|
218
|
+
| You want to… | Use | Requires |
|
|
219
|
+
|---|---|---|
|
|
220
|
+
| Call a third-party API (GET/POST) | `ctx.fetch(url, init)` — the standard Fetch API | capability `network` or `network:<host>` |
|
|
221
|
+
| Read a secret (API key, signing key) | `await ctx.secrets.get('<ref>')` | capability `secrets:<ref>` |
|
|
222
|
+
| Hash / HMAC-sign / verify / encrypt | `crypto.subtle` (Web Crypto) | — |
|
|
223
|
+
| Random id / bytes | `crypto.randomUUID()` / `crypto.getRandomValues()` | — |
|
|
224
|
+
| Read or write SmartLinks data | `ctx.sl.*` (records, products, attestations, …) | the matching `sl:<res>:<read\|write>` |
|
|
225
|
+
|
|
226
|
+
Notes:
|
|
227
|
+
- **Talking to the SmartLinks core is `ctx.sl`, not HTTP.** The common pattern — *validate /
|
|
228
|
+
process, then act on the core* — is: check the input, then call `ctx.sl.appRecords.create(…)`,
|
|
229
|
+
`ctx.sl.products.update(…)`, etc. `ctx.sl` is already authenticated as your declared authority
|
|
230
|
+
and capped by your capabilities, so you never construct a SmartLinks API URL or carry a key.
|
|
231
|
+
Use `fetch`/`ctx.fetch` for *third-party* servers; use `ctx.sl` for SmartLinks itself.
|
|
232
|
+
- **There is one `fetch`, and it is capability-gated.** Whether you call the global `fetch` or
|
|
233
|
+
`ctx.fetch`, the behaviour is identical: the call **throws** unless your declared `network`
|
|
234
|
+
(or `network:<host>`) capability covers the target host — the same way Deno's `fetch` throws
|
|
235
|
+
without `--allow-net`. There is no ungated escape hatch. Declare the hosts you need.
|
|
236
|
+
- **Secrets are read-only at runtime.** You fetch a secret you declared; you do **not** set or
|
|
237
|
+
rotate secrets from a function — that's an admin/deploy-time operation on the platform.
|
|
238
|
+
- **Signing a webhook / verifying a signature** is `crypto.subtle` with an HMAC key imported
|
|
239
|
+
from a secret — no Node `crypto` needed.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
174
243
|
## Worked example — a public competition entry
|
|
175
244
|
|
|
176
245
|
```ts
|
|
@@ -220,14 +289,41 @@ POST /public/collection/:collectionId/functions/:name # visibility: public (a
|
|
|
220
289
|
The request body is delivered to the handler as `event.body` (query string as
|
|
221
290
|
`event.query`). A function only runs on its own surface — calling an `admin` function on
|
|
222
291
|
the public endpoint is a `403`. The response is `{ ok: true, result }` on success, or
|
|
223
|
-
`{ error, message }` (HTTP 400) if the handler returned an error.
|
|
224
|
-
|
|
292
|
+
`{ error, message }` (HTTP 400) if the handler returned an error. On the **admin surface**
|
|
293
|
+
the response also includes `logs` (your `ctx.log` lines) and `durationMs` for quick
|
|
294
|
+
debugging; the **public surface returns only `result`** (a public caller never sees your
|
|
295
|
+
internal logs). `GET` on either endpoint lists the functions callable on that surface.
|
|
225
296
|
|
|
226
297
|
The admin surface is collection-admin gated, so an admin function's `caller` authority runs
|
|
227
298
|
at admin level, attributed to the signed-in admin. The public surface resolves auth if a
|
|
228
299
|
token is present (→ `owner`) and treats its absence as anonymous (→ `public`); a
|
|
229
300
|
`collection`-authority function runs elevated regardless.
|
|
230
301
|
|
|
302
|
+
### Before it will resolve
|
|
303
|
+
|
|
304
|
+
An http call returns `404 FUNCTION_NOT_FOUND` unless **both** of these are true (this is the
|
|
305
|
+
most common first-run surprise):
|
|
306
|
+
|
|
307
|
+
1. **The app's release is registered on the channel** the collection follows (see
|
|
308
|
+
[deploying-apps.md](deploying-apps.md)) — that's what publishes the function bundle into
|
|
309
|
+
the registry the runtime loads from.
|
|
310
|
+
2. **The app is enabled on the collection** (`appConfig.apps[]`, on that same channel — see
|
|
311
|
+
[appConfig.md](appConfig.md)).
|
|
312
|
+
|
|
313
|
+
So the end-to-end path is: *write → register the release → enable on a collection → call.*
|
|
314
|
+
|
|
315
|
+
### Seeing your function's runs and logs
|
|
316
|
+
|
|
317
|
+
Every invocation is recorded as an **execution** activity event, carrying your `ctx.log`
|
|
318
|
+
lines. Where to look, easiest first:
|
|
319
|
+
|
|
320
|
+
- **Admin-surface response** — `logs` + `durationMs` come straight back in the JSON.
|
|
321
|
+
- **Deployed test mode** — see below; real run, isolated logging.
|
|
322
|
+
- **Owner console** — the collection's **Advanced → Errors & Activity → events** tab,
|
|
323
|
+
filtered to **source = execution**: each run shows as `function <name> ok` (or an error),
|
|
324
|
+
and the detail carries your `ctx.log` output. (Telemetry is streamed, so allow a few
|
|
325
|
+
seconds.)
|
|
326
|
+
|
|
231
327
|
## Testing & preview
|
|
232
328
|
|
|
233
329
|
You don't have to deploy to find out whether a function works. There are three levels of
|
package/docs/API_SUMMARY.md
CHANGED
package/docs/deploying-apps.md
CHANGED
|
@@ -134,7 +134,7 @@ Content-Type: application/json
|
|
|
134
134
|
|
|
135
135
|
| Status | Meaning |
|
|
136
136
|
|---|---|
|
|
137
|
-
| `200` | Registered. Body: `{ ok: true, appId, channel, version, functions: [names], registeredAt }` |
|
|
137
|
+
| `200` | Registered. Body: `{ ok: true, appId, channel, version, functions: [names], registeredAt, warnings? }` — `warnings` is present for non-blocking issues (e.g. `APPID_IGNORED` when the manifest declared a different id). |
|
|
138
138
|
| `422` | Validation failed — **the build should fail**. Body: `{ ok: false, errors: [...] }` |
|
|
139
139
|
| `401` | Missing/invalid deploy key |
|
|
140
140
|
| `403` | Key not permitted for that channel (e.g. a dev key targeting `prod`) — `CHANNEL_FORBIDDEN` |
|
package/docs/server-functions.md
CHANGED
|
@@ -159,7 +159,9 @@ interface ServerFunctionContext {
|
|
|
159
159
|
/** Outbound HTTP — present only if you declared `network` (host-scoped if `network:<host>`). */
|
|
160
160
|
fetch: typeof fetch
|
|
161
161
|
|
|
162
|
-
/** Structured logging
|
|
162
|
+
/** Structured logging — returned inline on the admin surface and recorded as an
|
|
163
|
+
* execution activity event (owner Errors & Activity console). See "Seeing your
|
|
164
|
+
* function's runs and logs" below. */
|
|
163
165
|
log: (message: string, data?: Record<string, any>) => void
|
|
164
166
|
}
|
|
165
167
|
```
|
|
@@ -171,6 +173,73 @@ the only way authority stays correct.
|
|
|
171
173
|
|
|
172
174
|
---
|
|
173
175
|
|
|
176
|
+
## Runtime — what your function can use
|
|
177
|
+
|
|
178
|
+
Your function runs in a **web-standard sandbox** (think Cloudflare Workers / Deno), **not
|
|
179
|
+
Node**. Concretely it targets the **WinterTC Minimum Common Web Platform API** (WinterTC is the
|
|
180
|
+
Ecma International technical committee for this, formerly WinterCG) — the same surface those
|
|
181
|
+
runtimes guarantee — so what you can rely on is portable, and the future isolated runner will
|
|
182
|
+
host the same bundle unchanged.
|
|
183
|
+
|
|
184
|
+
**Available globals** (no import needed):
|
|
185
|
+
- **Data / encoding:** `JSON`, `URL`, `URLSearchParams`, `TextEncoder` / `TextDecoder`,
|
|
186
|
+
`atob` / `btoa`, `Buffer`, `structuredClone`.
|
|
187
|
+
- **Crypto:** `crypto` (Web Crypto) — `crypto.subtle` for hashing / HMAC / encrypt / sign /
|
|
188
|
+
verify, `crypto.getRandomValues`, `crypto.randomUUID`.
|
|
189
|
+
- **HTTP:** `fetch`, `Headers`, `Request`, `Response`, `FormData`, `Blob`, `File`,
|
|
190
|
+
`AbortController` / `AbortSignal` (use one for **request timeouts**).
|
|
191
|
+
- **Streams & timing:** `ReadableStream` / `WritableStream` / `TransformStream`,
|
|
192
|
+
`CompressionStream` / `DecompressionStream` (gzip/deflate for third-party payloads), the timer
|
|
193
|
+
functions, `queueMicrotask`, `performance`, `console`.
|
|
194
|
+
- **Runtime identity:** `navigator.userAgent` reports the runtime key (currently
|
|
195
|
+
`"SmartLinks-Functions"`) — use it if a portable dependency needs to feature-detect the host.
|
|
196
|
+
It stays stable when functions move to the isolated runner.
|
|
197
|
+
|
|
198
|
+
**Not available:** `require` / `import` of platform or Node modules, `process`, `fs`, and the
|
|
199
|
+
Node built-ins (`node:crypto`, `node:http`, …). There is no ambient database, key, or network
|
|
200
|
+
handle — everything the platform gives you comes through **`ctx`**.
|
|
201
|
+
|
|
202
|
+
**Dependencies:** bundle them. Your build must produce a **self-contained** UMD (deps compiled
|
|
203
|
+
in), and those deps must be **edge-compatible** — pure JS / Web APIs. A library that reaches for
|
|
204
|
+
Node built-ins (e.g. `axios`'s Node adapter, anything using `node:crypto`) will fail to load.
|
|
205
|
+
Prefer the platform primitives above over a dependency; when you do need one, pick edge-safe:
|
|
206
|
+
|
|
207
|
+
| Need | Native? | Recommended (bundle, edge-safe) |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| JSON | **native** — `JSON.parse/stringify` | — |
|
|
210
|
+
| XML parse / build | no | `fast-xml-parser` |
|
|
211
|
+
| CSV | no | `papaparse` |
|
|
212
|
+
| Schema validation of inputs | no | `zod` |
|
|
213
|
+
| JWT (sign/verify for a third-party API) | Web Crypto can, verbosely | `jose` |
|
|
214
|
+
| Hash / HMAC / encrypt | **native** — `crypto.subtle` | — |
|
|
215
|
+
|
|
216
|
+
**The common actions, and how to do each:**
|
|
217
|
+
|
|
218
|
+
| You want to… | Use | Requires |
|
|
219
|
+
|---|---|---|
|
|
220
|
+
| Call a third-party API (GET/POST) | `ctx.fetch(url, init)` — the standard Fetch API | capability `network` or `network:<host>` |
|
|
221
|
+
| Read a secret (API key, signing key) | `await ctx.secrets.get('<ref>')` | capability `secrets:<ref>` |
|
|
222
|
+
| Hash / HMAC-sign / verify / encrypt | `crypto.subtle` (Web Crypto) | — |
|
|
223
|
+
| Random id / bytes | `crypto.randomUUID()` / `crypto.getRandomValues()` | — |
|
|
224
|
+
| Read or write SmartLinks data | `ctx.sl.*` (records, products, attestations, …) | the matching `sl:<res>:<read\|write>` |
|
|
225
|
+
|
|
226
|
+
Notes:
|
|
227
|
+
- **Talking to the SmartLinks core is `ctx.sl`, not HTTP.** The common pattern — *validate /
|
|
228
|
+
process, then act on the core* — is: check the input, then call `ctx.sl.appRecords.create(…)`,
|
|
229
|
+
`ctx.sl.products.update(…)`, etc. `ctx.sl` is already authenticated as your declared authority
|
|
230
|
+
and capped by your capabilities, so you never construct a SmartLinks API URL or carry a key.
|
|
231
|
+
Use `fetch`/`ctx.fetch` for *third-party* servers; use `ctx.sl` for SmartLinks itself.
|
|
232
|
+
- **There is one `fetch`, and it is capability-gated.** Whether you call the global `fetch` or
|
|
233
|
+
`ctx.fetch`, the behaviour is identical: the call **throws** unless your declared `network`
|
|
234
|
+
(or `network:<host>`) capability covers the target host — the same way Deno's `fetch` throws
|
|
235
|
+
without `--allow-net`. There is no ungated escape hatch. Declare the hosts you need.
|
|
236
|
+
- **Secrets are read-only at runtime.** You fetch a secret you declared; you do **not** set or
|
|
237
|
+
rotate secrets from a function — that's an admin/deploy-time operation on the platform.
|
|
238
|
+
- **Signing a webhook / verifying a signature** is `crypto.subtle` with an HMAC key imported
|
|
239
|
+
from a secret — no Node `crypto` needed.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
174
243
|
## Worked example — a public competition entry
|
|
175
244
|
|
|
176
245
|
```ts
|
|
@@ -220,14 +289,41 @@ POST /public/collection/:collectionId/functions/:name # visibility: public (a
|
|
|
220
289
|
The request body is delivered to the handler as `event.body` (query string as
|
|
221
290
|
`event.query`). A function only runs on its own surface — calling an `admin` function on
|
|
222
291
|
the public endpoint is a `403`. The response is `{ ok: true, result }` on success, or
|
|
223
|
-
`{ error, message }` (HTTP 400) if the handler returned an error.
|
|
224
|
-
|
|
292
|
+
`{ error, message }` (HTTP 400) if the handler returned an error. On the **admin surface**
|
|
293
|
+
the response also includes `logs` (your `ctx.log` lines) and `durationMs` for quick
|
|
294
|
+
debugging; the **public surface returns only `result`** (a public caller never sees your
|
|
295
|
+
internal logs). `GET` on either endpoint lists the functions callable on that surface.
|
|
225
296
|
|
|
226
297
|
The admin surface is collection-admin gated, so an admin function's `caller` authority runs
|
|
227
298
|
at admin level, attributed to the signed-in admin. The public surface resolves auth if a
|
|
228
299
|
token is present (→ `owner`) and treats its absence as anonymous (→ `public`); a
|
|
229
300
|
`collection`-authority function runs elevated regardless.
|
|
230
301
|
|
|
302
|
+
### Before it will resolve
|
|
303
|
+
|
|
304
|
+
An http call returns `404 FUNCTION_NOT_FOUND` unless **both** of these are true (this is the
|
|
305
|
+
most common first-run surprise):
|
|
306
|
+
|
|
307
|
+
1. **The app's release is registered on the channel** the collection follows (see
|
|
308
|
+
[deploying-apps.md](deploying-apps.md)) — that's what publishes the function bundle into
|
|
309
|
+
the registry the runtime loads from.
|
|
310
|
+
2. **The app is enabled on the collection** (`appConfig.apps[]`, on that same channel — see
|
|
311
|
+
[appConfig.md](appConfig.md)).
|
|
312
|
+
|
|
313
|
+
So the end-to-end path is: *write → register the release → enable on a collection → call.*
|
|
314
|
+
|
|
315
|
+
### Seeing your function's runs and logs
|
|
316
|
+
|
|
317
|
+
Every invocation is recorded as an **execution** activity event, carrying your `ctx.log`
|
|
318
|
+
lines. Where to look, easiest first:
|
|
319
|
+
|
|
320
|
+
- **Admin-surface response** — `logs` + `durationMs` come straight back in the JSON.
|
|
321
|
+
- **Deployed test mode** — see below; real run, isolated logging.
|
|
322
|
+
- **Owner console** — the collection's **Advanced → Errors & Activity → events** tab,
|
|
323
|
+
filtered to **source = execution**: each run shows as `function <name> ok` (or an error),
|
|
324
|
+
and the detail carries your `ctx.log` output. (Telemetry is streamed, so allow a few
|
|
325
|
+
seconds.)
|
|
326
|
+
|
|
231
327
|
## Testing & preview
|
|
232
328
|
|
|
233
329
|
You don't have to deploy to find out whether a function works. There are three levels of
|