@proveanything/smartlinks 2.0.22 → 2.0.25
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/context.d.ts +18 -0
- package/dist/context.js +48 -0
- package/dist/docs/API_SUMMARY.md +69 -2
- package/dist/docs/app-manifest.md +42 -0
- package/dist/docs/server-functions.md +22 -9
- package/dist/http.d.ts +8 -0
- package/dist/http.js +33 -0
- package/dist/iframe.d.ts +14 -0
- package/dist/iframe.js +43 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/openapi.yaml +99 -1
- package/dist/testing/index.d.ts +19 -0
- package/dist/testing/index.js +30 -0
- package/dist/types/appManifest.d.ts +136 -6
- package/docs/API_SUMMARY.md +69 -2
- package/docs/app-manifest.md +42 -0
- package/docs/server-functions.md +22 -9
- package/openapi.yaml +99 -1
- package/package.json +2 -3
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SmartLinksContext {
|
|
2
|
+
collectionId?: string;
|
|
3
|
+
appId?: string;
|
|
4
|
+
productId?: string;
|
|
5
|
+
proofId?: string;
|
|
6
|
+
/** Any other declared view params (e.g. pageId, voteId, orientation, tvMode). */
|
|
7
|
+
[key: string]: string | undefined;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Merge the app's context from `overrides` (e.g. container props) → URL hash query → URL search,
|
|
11
|
+
* with the FIRST source winning (props override the URL, hash overrides search). Safe in non-browser
|
|
12
|
+
* environments (returns just the overrides). Empty-string values are kept (a param that was set to "").
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // Works identically whether embedded as a page (URL) or a container (props):
|
|
16
|
+
* const { collectionId, productId } = SL.readContext(containerProps)
|
|
17
|
+
*/
|
|
18
|
+
export declare function readContext(overrides?: Record<string, string | undefined>): SmartLinksContext;
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// src/context.ts
|
|
2
|
+
// Read the app's runtime CONTEXT (collectionId, appId, productId, proofId, and any app-specific
|
|
3
|
+
// params) regardless of how the app was delivered. A public app renders the SAME page whether it is:
|
|
4
|
+
// - a PAGE (index.html / HashRouter) — context arrives in the URL search and/or the hash query, or
|
|
5
|
+
// - a COMPONENT (PublicContainer) — context arrives as props from the parent.
|
|
6
|
+
// This helper merges those sources with a clear precedence so apps stop hand-rolling the
|
|
7
|
+
// `containerProps || hash || search` chain. See docs/design/public-views.md.
|
|
8
|
+
function readSearchParamsInto(out, usp) {
|
|
9
|
+
usp.forEach((value, key) => {
|
|
10
|
+
if (out[key] == null && value != null)
|
|
11
|
+
out[key] = value; // first writer wins
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Merge the app's context from `overrides` (e.g. container props) → URL hash query → URL search,
|
|
16
|
+
* with the FIRST source winning (props override the URL, hash overrides search). Safe in non-browser
|
|
17
|
+
* environments (returns just the overrides). Empty-string values are kept (a param that was set to "").
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // Works identically whether embedded as a page (URL) or a container (props):
|
|
21
|
+
* const { collectionId, productId } = SL.readContext(containerProps)
|
|
22
|
+
*/
|
|
23
|
+
export function readContext(overrides) {
|
|
24
|
+
const out = {};
|
|
25
|
+
// 1. overrides (container props) win.
|
|
26
|
+
if (overrides) {
|
|
27
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
28
|
+
if (value != null && out[key] == null)
|
|
29
|
+
out[key] = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
if (typeof window !== 'undefined' && window.location) {
|
|
34
|
+
// 2. hash query (e.g. "#/preview?collectionId=..&pageId=..") — the CDN/hash-router form.
|
|
35
|
+
const hash = window.location.hash || '';
|
|
36
|
+
const q = hash.indexOf('?');
|
|
37
|
+
if (q >= 0)
|
|
38
|
+
readSearchParamsInto(out, new URLSearchParams(hash.slice(q + 1)));
|
|
39
|
+
// 3. search string.
|
|
40
|
+
if (window.location.search)
|
|
41
|
+
readSearchParamsInto(out, new URLSearchParams(window.location.search));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (_a) {
|
|
45
|
+
/* non-browser / bad URL — return what we have */
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
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.25 | Generated: 2026-09-27T12:31:16.701Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -2132,6 +2132,7 @@ interface AppFunctionDef {
|
|
|
2132
2132
|
authority?: AppFunctionAuthority;
|
|
2133
2133
|
elevated?: boolean;
|
|
2134
2134
|
capabilities?: string[];
|
|
2135
|
+
dataScope?: 'collection' | 'global';
|
|
2135
2136
|
apiVersion?: string;
|
|
2136
2137
|
handler?: string;
|
|
2137
2138
|
}
|
|
@@ -2145,6 +2146,40 @@ interface AppManifestFunctions {
|
|
|
2145
2146
|
}
|
|
2146
2147
|
```
|
|
2147
2148
|
|
|
2149
|
+
**AppDataOpts** (interface)
|
|
2150
|
+
```typescript
|
|
2151
|
+
interface AppDataOpts {
|
|
2152
|
+
scope?: 'collection' | 'global';
|
|
2153
|
+
productId?: string;
|
|
2154
|
+
variantId?: string;
|
|
2155
|
+
batchId?: string;
|
|
2156
|
+
dataId?: string;
|
|
2157
|
+
queries?: Record<string, any>;
|
|
2158
|
+
}
|
|
2159
|
+
```
|
|
2160
|
+
|
|
2161
|
+
**AppDataHandle** (interface)
|
|
2162
|
+
```typescript
|
|
2163
|
+
interface AppDataHandle {
|
|
2164
|
+
get(opts?: AppDataOpts): Promise<any>;
|
|
2165
|
+
set(data: any, opts?: AppDataOpts): Promise<any>;
|
|
2166
|
+
getData(opts?: AppDataOpts): Promise<any>;
|
|
2167
|
+
setData(data: any, opts?: AppDataOpts): Promise<any>;
|
|
2168
|
+
delete(opts?: AppDataOpts): Promise<any>;
|
|
2169
|
+
}
|
|
2170
|
+
```
|
|
2171
|
+
|
|
2172
|
+
**ServerFunctionSl** (interface)
|
|
2173
|
+
```typescript
|
|
2174
|
+
interface ServerFunctionSl {
|
|
2175
|
+
appRecords: any;
|
|
2176
|
+
products: any;
|
|
2177
|
+
attestations: any;
|
|
2178
|
+
appData: AppDataHandle & { global: AppDataHandle; collection: AppDataHandle };
|
|
2179
|
+
app(appId: string): { data: Pick<AppDataHandle, 'get' | 'getData'> };
|
|
2180
|
+
}
|
|
2181
|
+
```
|
|
2182
|
+
|
|
2148
2183
|
**ServerFunctionCaller** (interface)
|
|
2149
2184
|
```typescript
|
|
2150
2185
|
interface ServerFunctionCaller {
|
|
@@ -2161,7 +2196,7 @@ interface ServerFunctionCaller {
|
|
|
2161
2196
|
interface ServerFunctionContext {
|
|
2162
2197
|
collectionId: string;
|
|
2163
2198
|
appId: string;
|
|
2164
|
-
sl:
|
|
2199
|
+
sl: ServerFunctionSl;
|
|
2165
2200
|
secrets: {
|
|
2166
2201
|
get(ref: string): Promise<string | null>;
|
|
2167
2202
|
app(ref: string): Promise<string | null>;
|
|
@@ -2172,6 +2207,18 @@ interface ServerFunctionContext {
|
|
|
2172
2207
|
}
|
|
2173
2208
|
```
|
|
2174
2209
|
|
|
2210
|
+
**ServerFunctionHttpEvent<TBody = any>** (interface)
|
|
2211
|
+
```typescript
|
|
2212
|
+
interface ServerFunctionHttpEvent<TBody = any> {
|
|
2213
|
+
method: string;
|
|
2214
|
+
body: TBody;
|
|
2215
|
+
query: Record<string, any>;
|
|
2216
|
+
headers: Record<string, any>;
|
|
2217
|
+
rawBody?: string | Buffer | null;
|
|
2218
|
+
contentType?: string | null;
|
|
2219
|
+
}
|
|
2220
|
+
```
|
|
2221
|
+
|
|
2175
2222
|
**AppAdminConfig** (interface)
|
|
2176
2223
|
```typescript
|
|
2177
2224
|
interface AppAdminConfig {
|
|
@@ -2233,6 +2280,25 @@ interface AppAdminConfig {
|
|
|
2233
2280
|
}
|
|
2234
2281
|
```
|
|
2235
2282
|
|
|
2283
|
+
**PublicViewParams** (interface)
|
|
2284
|
+
```typescript
|
|
2285
|
+
interface PublicViewParams {
|
|
2286
|
+
required?: string[];
|
|
2287
|
+
optional?: string[];
|
|
2288
|
+
}
|
|
2289
|
+
```
|
|
2290
|
+
|
|
2291
|
+
**PublicView** (interface)
|
|
2292
|
+
```typescript
|
|
2293
|
+
interface PublicView {
|
|
2294
|
+
id: string;
|
|
2295
|
+
title: string;
|
|
2296
|
+
route?: string;
|
|
2297
|
+
set?: Record<string, string>;
|
|
2298
|
+
params?: PublicViewParams;
|
|
2299
|
+
}
|
|
2300
|
+
```
|
|
2301
|
+
|
|
2236
2302
|
**AppManifest** (interface)
|
|
2237
2303
|
```typescript
|
|
2238
2304
|
interface AppManifest {
|
|
@@ -2267,6 +2333,7 @@ interface AppManifest {
|
|
|
2267
2333
|
components: AppContainerComponent[];
|
|
2268
2334
|
};
|
|
2269
2335
|
linkable?: DeepLinkEntry[];
|
|
2336
|
+
publicViews?: PublicView[];
|
|
2270
2337
|
executor?: AppManifestExecutor;
|
|
2271
2338
|
functions?: AppManifestFunctions;
|
|
2272
2339
|
[key: string]: any;
|
|
@@ -332,6 +332,48 @@ See the [Deep Link Discovery guide](deep-link-discovery.md) for the full dual-so
|
|
|
332
332
|
| `path` | string | ❌ | Hash route within the app (defaults to `"/"` if omitted) |
|
|
333
333
|
| `params` | object | ❌ | App-specific query params appended to the URL — do **not** include platform params (`collectionId`, `productId`, etc.) |
|
|
334
334
|
|
|
335
|
+
#### `publicViews`
|
|
336
|
+
|
|
337
|
+
Declares the app's **public views** — **standalone, external screens** served over your single public
|
|
338
|
+
bundle (`index.html` → HashRouter): a display board, a kiosk/TV screen, a dashboard, a public share
|
|
339
|
+
page. Without this, those routes/modes are invisible to the platform and the Dev Hub. Each view is a
|
|
340
|
+
`route` + fixed `set` params + caller `params`.
|
|
341
|
+
|
|
342
|
+
> **Public views are NOT portal.** They are completely external and independent. Their only input is
|
|
343
|
+
> **URL parameters** (often a `collectionId`, as a plain param). They expect **no** portal wrapper, **no**
|
|
344
|
+
> host-managed auth/session, and **no** physical-twin context (no QR/NFC/tag scan). They open at a URL
|
|
345
|
+
> and stand alone. For the two **portal** surfaces — where the app renders *inside* portal, and the
|
|
346
|
+
> entry points portal menus link to — see below.
|
|
347
|
+
|
|
348
|
+
```json
|
|
349
|
+
"publicViews": [
|
|
350
|
+
{ "id": "board", "title": "Display board", "route": "/display",
|
|
351
|
+
"params": { "required": ["collectionId"], "optional": ["orientation"] } },
|
|
352
|
+
{ "id": "tv", "title": "TV / big screen", "route": "/", "set": { "tvMode": "true" },
|
|
353
|
+
"params": { "required": ["collectionId", "voteId"] } }
|
|
354
|
+
]
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
| Field | Type | Required | Description |
|
|
358
|
+
|-------|------|----------|-------------|
|
|
359
|
+
| `id` | string | ✅ | Stable id, unique within the app |
|
|
360
|
+
| `title` | string | ✅ | Human label (Dev Hub dropdown, platform pickers) |
|
|
361
|
+
| `route` | string | ❌ | Hash route within the public bundle (defaults to `"/"`) |
|
|
362
|
+
| `set` | object | ❌ | Query params this view PINS (e.g. `{ "tvMode": "true" }`), merged under caller params |
|
|
363
|
+
| `params` | `{ required?: string[]; optional?: string[] }` | ❌ | The params the caller supplies via the URL |
|
|
364
|
+
|
|
365
|
+
Read the URL params with **`SL.readContext()`** (merges hash → search) instead of hand-rolling it.
|
|
366
|
+
|
|
367
|
+
##### Public views vs the two portal surfaces
|
|
368
|
+
|
|
369
|
+
An app can have any combination of three setup styles. Only public views are external:
|
|
370
|
+
|
|
371
|
+
| Style | Declared by | Lives | Context |
|
|
372
|
+
|-------|-------------|-------|---------|
|
|
373
|
+
| **Portal components** — where the app renders inside portal (collection / product / batch / variant / proof) | module registry `components.*` (set at publish) | Inside portal | Physical twin (QR/NFC scan) |
|
|
374
|
+
| **Portal deep links** — the entry points portal menus/tabs/side-menus link to (e.g. list view vs calendar view) | `linkable` / `DeepLinkEntry` | Inside portal | Portal (menu picks the entry) |
|
|
375
|
+
| **Public views** — standalone external screens | `publicViews` (this block) | Outside portal | URL params only |
|
|
376
|
+
|
|
335
377
|
#### `records`
|
|
336
378
|
|
|
337
379
|
Declares which `app.records` record types the app stores, and which scopes each type supports. Required for any app that follows the [App Records Pattern](app-records-pattern.md). Omit if the app does not use scoped records.
|
|
@@ -320,7 +320,8 @@ Prefer the platform primitives above over a dependency; when you do need one, pi
|
|
|
320
320
|
| Hash / HMAC-sign / verify / encrypt | `crypto.subtle` (Web Crypto) | — |
|
|
321
321
|
| Random id / bytes | `crypto.randomUUID()` / `crypto.getRandomValues()` | — |
|
|
322
322
|
| Read or write SmartLinks data | `ctx.sl.*` (records, products, attestations, …) | the matching `sl:<res>:<read\|write>` |
|
|
323
|
-
| Read/write
|
|
323
|
+
| Read/write your app's own data (config, URLs, counters, arrays) | `ctx.sl.appData.get()` / `ctx.sl.appData.set({…})` — collection-scoped by default; `ctx.sl.appData.global.*` (or `{ scope:'global' }`, or declare `dataScope:'global'`) for the app-wide bucket. It's your OWN namespace, so **any** authority may read/write it | `sl:data:read` / `sl:data:write` |
|
|
324
|
+
| Read ANOTHER app's data (as the caller may see it) | `ctx.sl.app('other-app').data.get()` — caller-authority, this collection, public-filtered, read-only | `sl:data:read` |
|
|
324
325
|
| Guarantee a UNIQUE claim (pool of numbers, one-per-user, idempotency key) | `ctx.sl.appRecords.create({ ref: 'ball:57' })` — the DB unique index rejects a duplicate `ref` (catch = "already taken") | `sl:records:write` |
|
|
325
326
|
|
|
326
327
|
Notes:
|
|
@@ -403,13 +404,25 @@ from your app").
|
|
|
403
404
|
> trusted. When untrusted third-party apps arrive, gate *elevated* (`public` + `collection`)
|
|
404
405
|
> invocation of a **non-enabled** app behind install/consent — the app-scoped resolver is the seam.
|
|
405
406
|
|
|
406
|
-
The
|
|
407
|
-
`event.
|
|
408
|
-
|
|
409
|
-
`
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
407
|
+
The handler receives the request as `event`: `event.body` (parsed JSON), `event.query`,
|
|
408
|
+
`event.headers`, `event.rawBody` (raw bytes, for XML/form/other inputs), and `event.contentType`.
|
|
409
|
+
A function only runs on its own surface — calling an `admin` function on the public endpoint is a
|
|
410
|
+
`403`. `GET` on either endpoint lists the functions callable on that surface.
|
|
411
|
+
|
|
412
|
+
**Response contract — your return IS the response (no envelope):**
|
|
413
|
+
- Return a **plain value** → it becomes the JSON body, HTTP `200`. (`SL.functions.call()` gives you
|
|
414
|
+
that value directly — `res.value`, not `res.result.value`.)
|
|
415
|
+
- Return a web-standard **`Response`** → passed through verbatim: your status, headers, content-type
|
|
416
|
+
and body (XML, CSV, text, binary, redirect, custom status). `Response` is a runtime global.
|
|
417
|
+
```js
|
|
418
|
+
return new Response(toXml(data), { status: 200, headers: { "content-type": "application/xml" } })
|
|
419
|
+
```
|
|
420
|
+
- **Throw** → HTTP `500` `{ error: "FUNCTION_ERROR", message, code }`. For *expected* errors (400/404/
|
|
421
|
+
409…), return a `Response` with your own status + body.
|
|
422
|
+
|
|
423
|
+
One rule: **plain object ⇒ 200; want any other status/headers/content-type ⇒ return a `Response`.**
|
|
424
|
+
Timing comes back in an `X-SL-Function-Duration-Ms` header (admin surface); your `ctx.log` lines land
|
|
425
|
+
in the collection's Errors & Activity feed — the body stays purely your output.
|
|
413
426
|
|
|
414
427
|
The admin surface is collection-admin gated, so an admin function's `caller` authority runs
|
|
415
428
|
at admin level, attributed to the signed-in admin. The public surface resolves auth if a
|
|
@@ -434,7 +447,7 @@ So the end-to-end path is: *write → register the release → enable on a colle
|
|
|
434
447
|
Every invocation is recorded as an **execution** activity event, carrying your `ctx.log`
|
|
435
448
|
lines. Where to look, easiest first:
|
|
436
449
|
|
|
437
|
-
- **Admin-surface response** —
|
|
450
|
+
- **Admin-surface response** — timing comes back in the `X-SL-Function-Duration-Ms` header (logs are in Errors & Activity, not the body).
|
|
438
451
|
- **Deployed test mode** — see below; real run, isolated logging.
|
|
439
452
|
- **Owner console** — the collection's **Advanced → Errors & Activity → events** tab,
|
|
440
453
|
filtered to **source = execution**: each run shows as `function <name> ok` (or an error),
|
package/dist/http.d.ts
CHANGED
|
@@ -52,6 +52,14 @@ export declare function initializeApi(options: {
|
|
|
52
52
|
* Preserved across re-initialization when not supplied.
|
|
53
53
|
*/
|
|
54
54
|
appId?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Declares that a bearer token will arrive asynchronously (e.g. handed by the host over
|
|
57
|
+
* postMessage in a dev/direct embed). Until setBearerToken() is called, outgoing requests wait
|
|
58
|
+
* rather than firing unauthenticated. Ignored if a bearerToken is already present.
|
|
59
|
+
*/
|
|
60
|
+
awaitAuth?: boolean;
|
|
61
|
+
/** How long (ms) to wait for the async token before letting requests proceed anyway. Default 8000. */
|
|
62
|
+
awaitAuthTimeoutMs?: number;
|
|
55
63
|
iframeAutoResize?: boolean;
|
|
56
64
|
logger?: Logger;
|
|
57
65
|
/**
|
package/dist/http.js
CHANGED
|
@@ -52,6 +52,16 @@ let grantToken = undefined;
|
|
|
52
52
|
* app-scoped when this is set. Explicit `appId` on a call always overrides it.
|
|
53
53
|
*/
|
|
54
54
|
let appContextId = undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Auth-ready gate. When an app declares (initializeApi({ awaitAuth: true })) that its bearer token
|
|
57
|
+
* will arrive ASYNCHRONOUSLY — e.g. a dev app embedded in the console that receives its token over
|
|
58
|
+
* postMessage — outgoing requests await this until setBearerToken() is called, so the app's first
|
|
59
|
+
* calls don't race the token and 401. Resolved by default (no gating); a timeout resolves it anyway
|
|
60
|
+
* so a token that never arrives can't hang the app forever.
|
|
61
|
+
*/
|
|
62
|
+
let authReady = Promise.resolve();
|
|
63
|
+
let resolveAuthReady = null;
|
|
64
|
+
function ensureAuthReady() { return authReady; }
|
|
55
65
|
/** Whether initializeApi has been successfully called at least once. */
|
|
56
66
|
let initialized = false;
|
|
57
67
|
/** Safely returns the current browser hostname, or an empty string in non-browser / Node environments. */
|
|
@@ -481,6 +491,18 @@ export function initializeApi(options) {
|
|
|
481
491
|
}
|
|
482
492
|
}
|
|
483
493
|
// else: preserve the existing runtime bearerToken.
|
|
494
|
+
// Open the auth-ready gate when the caller declares a token is coming but none is set yet, so
|
|
495
|
+
// requests wait for setBearerToken() instead of firing unauthenticated. A timeout releases it.
|
|
496
|
+
if (options.awaitAuth && !bearerToken && !resolveAuthReady) {
|
|
497
|
+
authReady = new Promise((res) => {
|
|
498
|
+
var _a;
|
|
499
|
+
resolveAuthReady = res;
|
|
500
|
+
setTimeout(() => { if (resolveAuthReady) {
|
|
501
|
+
resolveAuthReady = null;
|
|
502
|
+
res();
|
|
503
|
+
} }, (_a = options.awaitAuthTimeoutMs) !== null && _a !== void 0 ? _a : 8000);
|
|
504
|
+
});
|
|
505
|
+
}
|
|
484
506
|
proxyMode = !!options.proxyMode;
|
|
485
507
|
// Auto-enable ngrok skip header if domain contains .ngrok.io and user did not explicitly set the flag.
|
|
486
508
|
// Infer ngrok usage from common domains (.ngrok.io or .ngrok-free.dev)
|
|
@@ -530,6 +552,12 @@ export function setExtraHeaders(headers) {
|
|
|
530
552
|
* login or logout event.
|
|
531
553
|
*/
|
|
532
554
|
export function setBearerToken(token) {
|
|
555
|
+
// Release any auth-ready gate the moment a real token arrives (even if the value is unchanged),
|
|
556
|
+
// so requests that were waiting for the async handoff can proceed.
|
|
557
|
+
if (token && resolveAuthReady) {
|
|
558
|
+
resolveAuthReady();
|
|
559
|
+
resolveAuthReady = null;
|
|
560
|
+
}
|
|
533
561
|
if (token === bearerToken)
|
|
534
562
|
return;
|
|
535
563
|
bearerToken = token;
|
|
@@ -1189,6 +1217,7 @@ export async function proxyUploadFormData(path, formData, onProgress) {
|
|
|
1189
1217
|
* Node-safe: IndexedDB calls are no-ops when IDB is unavailable.
|
|
1190
1218
|
*/
|
|
1191
1219
|
export async function request(path) {
|
|
1220
|
+
await ensureAuthReady();
|
|
1192
1221
|
const skipCache = shouldSkipCache(path);
|
|
1193
1222
|
const cacheKey = buildCacheKey(path);
|
|
1194
1223
|
const ttl = skipCache ? 0 : getTtlForPath(path);
|
|
@@ -1314,6 +1343,7 @@ export async function request(path) {
|
|
|
1314
1343
|
* Returns the parsed JSON as T, or throws an Error.
|
|
1315
1344
|
*/
|
|
1316
1345
|
export async function post(path, body, extraHeaders) {
|
|
1346
|
+
await ensureAuthReady();
|
|
1317
1347
|
if (proxyMode) {
|
|
1318
1348
|
logDebug('[smartlinks] POST via proxy', { path, body: safeBodyPreview(body) });
|
|
1319
1349
|
const result = await proxyRequest("POST", path, body, extraHeaders);
|
|
@@ -1375,6 +1405,7 @@ export async function post(path, body, extraHeaders) {
|
|
|
1375
1405
|
* Returns the parsed JSON as T, or throws an Error.
|
|
1376
1406
|
*/
|
|
1377
1407
|
export async function put(path, body, extraHeaders) {
|
|
1408
|
+
await ensureAuthReady();
|
|
1378
1409
|
if (proxyMode) {
|
|
1379
1410
|
logDebug('[smartlinks] PUT via proxy', { path, body: safeBodyPreview(body) });
|
|
1380
1411
|
const result = await proxyRequest("PUT", path, body, extraHeaders);
|
|
@@ -1436,6 +1467,7 @@ export async function put(path, body, extraHeaders) {
|
|
|
1436
1467
|
* Returns the parsed JSON as T, or throws an Error.
|
|
1437
1468
|
*/
|
|
1438
1469
|
export async function patch(path, body, extraHeaders) {
|
|
1470
|
+
await ensureAuthReady();
|
|
1439
1471
|
if (proxyMode) {
|
|
1440
1472
|
logDebug('[smartlinks] PATCH via proxy', { path, body: safeBodyPreview(body) });
|
|
1441
1473
|
const result = await proxyRequest("PATCH", path, body, extraHeaders);
|
|
@@ -1648,6 +1680,7 @@ export async function requestStream(path, options) {
|
|
|
1648
1680
|
* Returns the parsed JSON as T, or throws an Error.
|
|
1649
1681
|
*/
|
|
1650
1682
|
export async function del(path, extraHeaders) {
|
|
1683
|
+
await ensureAuthReady();
|
|
1651
1684
|
if (proxyMode) {
|
|
1652
1685
|
logDebug('[smartlinks] DELETE via proxy', { path });
|
|
1653
1686
|
const result = await proxyRequest("DELETE", path, undefined, extraHeaders);
|
package/dist/iframe.d.ts
CHANGED
|
@@ -21,6 +21,20 @@ export declare namespace iframe {
|
|
|
21
21
|
export function disableAutoIframeResize(): void;
|
|
22
22
|
/** Send a custom message to parent (browser-only). */
|
|
23
23
|
export function sendParentCustom(type: string, payload: Record<string, any>): void;
|
|
24
|
+
/**
|
|
25
|
+
* Ask the embedding host (parent window) to hand this app a bearer token, for DIRECT (non-proxied)
|
|
26
|
+
* mode. Posts `{ type: 'smartlinks:request-auth' }` and resolves with the token from the host's
|
|
27
|
+
* `{ type: 'smartlinks:auth', bearerToken }` reply, or `null` on timeout / when not embedded.
|
|
28
|
+
*
|
|
29
|
+
* Use in a first-party/dev embed where the host owns the session and hands it down (the console dev
|
|
30
|
+
* preview), then feed it to the SDK:
|
|
31
|
+
* SL.initializeApi({ baseURL, proxyMode: false, awaitAuth: true })
|
|
32
|
+
* SL.iframe.requestParentAuth().then(t => t && SL.setBearerToken(t))
|
|
33
|
+
* Untrusted third-party embeds should use proxyMode instead (never hold the user's token).
|
|
34
|
+
*/
|
|
35
|
+
export function requestParentAuth(opts?: {
|
|
36
|
+
timeoutMs?: number;
|
|
37
|
+
}): Promise<string | null>;
|
|
24
38
|
/** Returns true if running inside an iframe (browser). */
|
|
25
39
|
export function isIframe(): boolean;
|
|
26
40
|
/** Returns true if ResizeObserver is supported in current environment. */
|
package/dist/iframe.js
CHANGED
|
@@ -122,6 +122,49 @@ export var iframe;
|
|
|
122
122
|
postParentMessage(type, payload);
|
|
123
123
|
}
|
|
124
124
|
iframe.sendParentCustom = sendParentCustom;
|
|
125
|
+
/**
|
|
126
|
+
* Ask the embedding host (parent window) to hand this app a bearer token, for DIRECT (non-proxied)
|
|
127
|
+
* mode. Posts `{ type: 'smartlinks:request-auth' }` and resolves with the token from the host's
|
|
128
|
+
* `{ type: 'smartlinks:auth', bearerToken }` reply, or `null` on timeout / when not embedded.
|
|
129
|
+
*
|
|
130
|
+
* Use in a first-party/dev embed where the host owns the session and hands it down (the console dev
|
|
131
|
+
* preview), then feed it to the SDK:
|
|
132
|
+
* SL.initializeApi({ baseURL, proxyMode: false, awaitAuth: true })
|
|
133
|
+
* SL.iframe.requestParentAuth().then(t => t && SL.setBearerToken(t))
|
|
134
|
+
* Untrusted third-party embeds should use proxyMode instead (never hold the user's token).
|
|
135
|
+
*/
|
|
136
|
+
function requestParentAuth(opts) {
|
|
137
|
+
if (!inIframe())
|
|
138
|
+
return Promise.resolve(null);
|
|
139
|
+
return new Promise((resolve) => {
|
|
140
|
+
var _a;
|
|
141
|
+
let done = false;
|
|
142
|
+
const finish = (v) => {
|
|
143
|
+
if (done)
|
|
144
|
+
return;
|
|
145
|
+
done = true;
|
|
146
|
+
clearTimeout(timer);
|
|
147
|
+
try {
|
|
148
|
+
window.removeEventListener('message', onMsg);
|
|
149
|
+
}
|
|
150
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
151
|
+
resolve(v);
|
|
152
|
+
};
|
|
153
|
+
const onMsg = (e) => {
|
|
154
|
+
const d = e && e.data;
|
|
155
|
+
if (d && d.type === 'smartlinks:auth' && typeof d.bearerToken === 'string' && d.bearerToken) {
|
|
156
|
+
finish(d.bearerToken);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const timer = setTimeout(() => finish(null), (_a = opts === null || opts === void 0 ? void 0 : opts.timeoutMs) !== null && _a !== void 0 ? _a : 8000);
|
|
160
|
+
try {
|
|
161
|
+
window.addEventListener('message', onMsg);
|
|
162
|
+
}
|
|
163
|
+
catch ( /* ignore */_b) { /* ignore */ }
|
|
164
|
+
postParentMessage('smartlinks:request-auth', {});
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
iframe.requestParentAuth = requestParentAuth;
|
|
125
168
|
/** Returns true if running inside an iframe (browser). */
|
|
126
169
|
function isIframe() {
|
|
127
170
|
return inIframe();
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export { initializeApi, isInitialized, hasAuthCredentials, configureSdkCache, in
|
|
|
2
2
|
export * from "./api/index.js";
|
|
3
3
|
export * from "./types/index.js";
|
|
4
4
|
export { iframe } from "./iframe.js";
|
|
5
|
+
export { readContext } from "./context.js";
|
|
6
|
+
export type { SmartLinksContext } from "./context.js";
|
|
5
7
|
export type { InvalidateCacheOptions } from "./http.js";
|
|
6
8
|
export * as cache from './cache.js';
|
|
7
9
|
export { IframeResponder, isAdminFromRoles, buildIframeSrc, } from './iframeResponder.js';
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,8 @@ export * from "./api/index.js";
|
|
|
5
5
|
export * from "./types/index.js";
|
|
6
6
|
// Iframe namespace
|
|
7
7
|
export { iframe } from "./iframe.js";
|
|
8
|
+
// App context reader (props → hash → search), for pages and containers alike
|
|
9
|
+
export { readContext } from "./context.js";
|
|
8
10
|
import * as cache_1 from './cache.js';
|
|
9
11
|
export { cache_1 as cache };
|
|
10
12
|
// IframeResponder (also exported via iframe namespace)
|
package/dist/openapi.yaml
CHANGED
|
@@ -18255,6 +18255,11 @@ components:
|
|
|
18255
18255
|
type: array
|
|
18256
18256
|
items:
|
|
18257
18257
|
type: string
|
|
18258
|
+
dataScope:
|
|
18259
|
+
type: string
|
|
18260
|
+
enum:
|
|
18261
|
+
- collection
|
|
18262
|
+
- global
|
|
18258
18263
|
apiVersion:
|
|
18259
18264
|
type: string
|
|
18260
18265
|
handler:
|
|
@@ -18274,6 +18279,41 @@ components:
|
|
|
18274
18279
|
required:
|
|
18275
18280
|
- files
|
|
18276
18281
|
- definitions
|
|
18282
|
+
AppDataOpts:
|
|
18283
|
+
type: object
|
|
18284
|
+
properties:
|
|
18285
|
+
scope:
|
|
18286
|
+
type: string
|
|
18287
|
+
enum:
|
|
18288
|
+
- collection
|
|
18289
|
+
- global
|
|
18290
|
+
productId:
|
|
18291
|
+
type: string
|
|
18292
|
+
variantId:
|
|
18293
|
+
type: string
|
|
18294
|
+
batchId:
|
|
18295
|
+
type: string
|
|
18296
|
+
dataId:
|
|
18297
|
+
type: string
|
|
18298
|
+
queries:
|
|
18299
|
+
type: object
|
|
18300
|
+
additionalProperties: true
|
|
18301
|
+
AppDataHandle:
|
|
18302
|
+
type: object
|
|
18303
|
+
properties: {}
|
|
18304
|
+
ServerFunctionSl:
|
|
18305
|
+
type: object
|
|
18306
|
+
properties:
|
|
18307
|
+
appRecords: {}
|
|
18308
|
+
products: {}
|
|
18309
|
+
attestations: {}
|
|
18310
|
+
appData:
|
|
18311
|
+
$ref: "#/components/schemas/AppDataHandle"
|
|
18312
|
+
required:
|
|
18313
|
+
- appRecords
|
|
18314
|
+
- products
|
|
18315
|
+
- attestations
|
|
18316
|
+
- appData
|
|
18277
18317
|
ServerFunctionCaller:
|
|
18278
18318
|
type: object
|
|
18279
18319
|
properties:
|
|
@@ -18298,7 +18338,8 @@ components:
|
|
|
18298
18338
|
type: string
|
|
18299
18339
|
appId:
|
|
18300
18340
|
type: string
|
|
18301
|
-
sl:
|
|
18341
|
+
sl:
|
|
18342
|
+
$ref: "#/components/schemas/ServerFunctionSl"
|
|
18302
18343
|
secrets:
|
|
18303
18344
|
type: object
|
|
18304
18345
|
additionalProperties: true
|
|
@@ -18314,6 +18355,30 @@ components:
|
|
|
18314
18355
|
- secrets
|
|
18315
18356
|
- caller
|
|
18316
18357
|
- fetch
|
|
18358
|
+
ServerFunctionHttpEvent:
|
|
18359
|
+
type: object
|
|
18360
|
+
properties:
|
|
18361
|
+
method:
|
|
18362
|
+
type: string
|
|
18363
|
+
body:
|
|
18364
|
+
type: object
|
|
18365
|
+
additionalProperties: true
|
|
18366
|
+
query:
|
|
18367
|
+
type: object
|
|
18368
|
+
additionalProperties: true
|
|
18369
|
+
headers:
|
|
18370
|
+
type: object
|
|
18371
|
+
additionalProperties: true
|
|
18372
|
+
rawBody:
|
|
18373
|
+
type: object
|
|
18374
|
+
additionalProperties: true
|
|
18375
|
+
contentType:
|
|
18376
|
+
type: string
|
|
18377
|
+
required:
|
|
18378
|
+
- method
|
|
18379
|
+
- body
|
|
18380
|
+
- query
|
|
18381
|
+
- headers
|
|
18317
18382
|
AppAdminConfig:
|
|
18318
18383
|
type: object
|
|
18319
18384
|
properties:
|
|
@@ -18397,6 +18462,35 @@ components:
|
|
|
18397
18462
|
- scope
|
|
18398
18463
|
- name
|
|
18399
18464
|
- type
|
|
18465
|
+
PublicViewParams:
|
|
18466
|
+
type: object
|
|
18467
|
+
properties:
|
|
18468
|
+
required:
|
|
18469
|
+
type: array
|
|
18470
|
+
items:
|
|
18471
|
+
type: string
|
|
18472
|
+
optional:
|
|
18473
|
+
type: array
|
|
18474
|
+
items:
|
|
18475
|
+
type: string
|
|
18476
|
+
PublicView:
|
|
18477
|
+
type: object
|
|
18478
|
+
properties:
|
|
18479
|
+
id:
|
|
18480
|
+
type: string
|
|
18481
|
+
title:
|
|
18482
|
+
type: string
|
|
18483
|
+
route:
|
|
18484
|
+
type: string
|
|
18485
|
+
set:
|
|
18486
|
+
type: object
|
|
18487
|
+
additionalProperties:
|
|
18488
|
+
type: string
|
|
18489
|
+
params:
|
|
18490
|
+
$ref: "#/components/schemas/PublicViewParams"
|
|
18491
|
+
required:
|
|
18492
|
+
- id
|
|
18493
|
+
- title
|
|
18400
18494
|
AppManifest:
|
|
18401
18495
|
type: object
|
|
18402
18496
|
properties:
|
|
@@ -18472,6 +18566,10 @@ components:
|
|
|
18472
18566
|
type: array
|
|
18473
18567
|
items:
|
|
18474
18568
|
$ref: "#/components/schemas/DeepLinkEntry"
|
|
18569
|
+
publicViews:
|
|
18570
|
+
type: array
|
|
18571
|
+
items:
|
|
18572
|
+
$ref: "#/components/schemas/PublicView"
|
|
18475
18573
|
executor:
|
|
18476
18574
|
$ref: "#/components/schemas/AppManifestExecutor"
|
|
18477
18575
|
functions:
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -24,6 +24,25 @@ export interface TestSlImpl {
|
|
|
24
24
|
attestations?: {
|
|
25
25
|
create?(fields: any): any;
|
|
26
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* THIS app's own data. Provide impls to assert calls, or omit to use a built-in in-memory store
|
|
29
|
+
* (so a counter test actually persists across calls within the test). `.global`/`.collection` and
|
|
30
|
+
* per-call `{ scope }` share the same store in the harness.
|
|
31
|
+
*/
|
|
32
|
+
appData?: {
|
|
33
|
+
get?(opts?: any): any;
|
|
34
|
+
set?(data: any, opts?: any): any;
|
|
35
|
+
getData?(opts?: any): any;
|
|
36
|
+
setData?(data: any, opts?: any): any;
|
|
37
|
+
delete?(opts?: any): any;
|
|
38
|
+
};
|
|
39
|
+
/** Cross-app reads: return another app's data as the caller would see it. Keyed by appId. */
|
|
40
|
+
app?: (appId: string) => {
|
|
41
|
+
data?: {
|
|
42
|
+
get?(opts?: any): any;
|
|
43
|
+
getData?(opts?: any): any;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
27
46
|
}
|
|
28
47
|
export interface TestCaller {
|
|
29
48
|
userId?: string | null;
|
package/dist/testing/index.js
CHANGED
|
@@ -102,6 +102,36 @@ export function createFunctionTestContext(opts) {
|
|
|
102
102
|
create: gated('attestations', 'write', at.create && at.create.bind(at), 'create'),
|
|
103
103
|
},
|
|
104
104
|
};
|
|
105
|
+
// appData — own-data (capability-gated only, no role gate). Delegates to a provided impl, else a
|
|
106
|
+
// built-in in-memory store so read-modify-write (e.g. a counter) works across calls in one test.
|
|
107
|
+
const ad = impl.appData || {};
|
|
108
|
+
const memConfig = {};
|
|
109
|
+
const memData = {};
|
|
110
|
+
const deepMerge = (t, s) => { for (const k of Object.keys(s || {}))
|
|
111
|
+
t[k] = (s[k] && typeof s[k] === 'object' && !Array.isArray(s[k])) ? deepMerge(t[k] || {}, s[k]) : s[k]; return t; };
|
|
112
|
+
const makeAppData = () => ({
|
|
113
|
+
get: gated('data', 'read', ad.get ? ad.get.bind(ad) : (async () => (Object.assign({}, memConfig))), 'get'),
|
|
114
|
+
set: gated('data', 'write', ad.set ? ad.set.bind(ad) : (async (data) => deepMerge(memConfig, data)), 'set'),
|
|
115
|
+
getData: gated('data', 'read', ad.getData ? ad.getData.bind(ad) : (async (opts = {}) => (opts.dataId ? memData[opts.dataId] : Object.values(memData))), 'getData'),
|
|
116
|
+
setData: gated('data', 'write', ad.setData ? ad.setData.bind(ad) : (async (data, opts = {}) => { if (opts.dataId)
|
|
117
|
+
memData[opts.dataId] = data; return data; }), 'setData'),
|
|
118
|
+
delete: gated('data', 'write', ad.delete ? ad.delete.bind(ad) : (async (opts = {}) => { if (opts.dataId)
|
|
119
|
+
delete memData[opts.dataId];
|
|
120
|
+
else
|
|
121
|
+
for (const k of Object.keys(memConfig))
|
|
122
|
+
delete memConfig[k]; }), 'delete'),
|
|
123
|
+
});
|
|
124
|
+
sl.appData = Object.assign(makeAppData(), { global: makeAppData(), collection: makeAppData() });
|
|
125
|
+
sl.app = (appId) => {
|
|
126
|
+
const other = (impl.app && impl.app(appId)) || {};
|
|
127
|
+
const od = other.data || {};
|
|
128
|
+
return {
|
|
129
|
+
data: {
|
|
130
|
+
get: gated('data', 'read', od.get ? od.get.bind(od) : (async () => null), 'app.get'),
|
|
131
|
+
getData: gated('data', 'read', od.getData ? od.getData.bind(od) : (async () => []), 'app.getData'),
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
};
|
|
105
135
|
const secretsMap = opts.secrets || {};
|
|
106
136
|
const baseFetch = opts.fetch || (typeof fetch !== 'undefined' ? fetch : undefined);
|
|
107
137
|
const via = (def.trigger && def.trigger.type) || 'http';
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
1
3
|
/**
|
|
2
4
|
* A bundle (widget or container) as returned by the collection widgets endpoint.
|
|
3
5
|
*
|
|
@@ -258,6 +260,13 @@ export interface AppFunctionDef {
|
|
|
258
260
|
* Grammar: `sl:<resource>:<read|write>`, `network` (or `network:<host>`), `secrets:<ref>`.
|
|
259
261
|
*/
|
|
260
262
|
capabilities?: string[];
|
|
263
|
+
/**
|
|
264
|
+
* Default scope for `ctx.sl.appData.*` — `'collection'` (this collection; the default) or
|
|
265
|
+
* `'global'` (the app's app-wide bucket, shared by every collection that installed it). A function
|
|
266
|
+
* whose data is global-by-intent declares `'global'` and then just uses `appData.get()/set()`.
|
|
267
|
+
* Per-call `{ scope }` and the `appData.global` / `appData.collection` handles override this.
|
|
268
|
+
*/
|
|
269
|
+
dataScope?: 'collection' | 'global';
|
|
261
270
|
/** SDK/manifest API version this function targets (pinned for runtime compatibility). */
|
|
262
271
|
apiVersion?: string;
|
|
263
272
|
/** Exported handler name in the functions bundle. Defaults to `name`. */
|
|
@@ -268,6 +277,57 @@ export interface AppManifestFunctions {
|
|
|
268
277
|
files: AppManifestFiles;
|
|
269
278
|
definitions: AppFunctionDef[];
|
|
270
279
|
}
|
|
280
|
+
/** Scope + selector options for `ctx.sl.appData.*`. */
|
|
281
|
+
export interface AppDataOpts {
|
|
282
|
+
/** `'collection'` (this collection) or `'global'` (the app's app-wide bucket). Defaults to the function's `dataScope`. */
|
|
283
|
+
scope?: 'collection' | 'global';
|
|
284
|
+
productId?: string;
|
|
285
|
+
variantId?: string;
|
|
286
|
+
batchId?: string;
|
|
287
|
+
/** For `getData`/`setData`/`delete`: the keyed data item id. */
|
|
288
|
+
dataId?: string;
|
|
289
|
+
queries?: Record<string, any>;
|
|
290
|
+
}
|
|
291
|
+
/** Read/write handle for an app's own durable data. `set` deep-merges the singleton config blob. */
|
|
292
|
+
export interface AppDataHandle {
|
|
293
|
+
get(opts?: AppDataOpts): Promise<any>;
|
|
294
|
+
set(data: any, opts?: AppDataOpts): Promise<any>;
|
|
295
|
+
getData(opts?: AppDataOpts): Promise<any>;
|
|
296
|
+
setData(data: any, opts?: AppDataOpts): Promise<any>;
|
|
297
|
+
delete(opts?: AppDataOpts): Promise<any>;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* The capability-gated SmartLinks surface handed to a server function. Two access modes:
|
|
301
|
+
* - by APP identity — `appData` / `appRecords` are THIS app's OWN data (any scope), gated by the
|
|
302
|
+
* declared capability only (the caller's role is irrelevant — it's the app's own namespace).
|
|
303
|
+
* - by CALLER identity — everything else (products, attestations, and `app(id)` cross-app reads)
|
|
304
|
+
* is bounded by what the invoking user could do through the permissioned API.
|
|
305
|
+
*/
|
|
306
|
+
export interface ServerFunctionSl {
|
|
307
|
+
/** THIS app's records (own namespace). */
|
|
308
|
+
appRecords: any;
|
|
309
|
+
/** Products, scoped by caller authority + declared capability. */
|
|
310
|
+
products: any;
|
|
311
|
+
/** Attestations, scoped by caller authority + declared capability. */
|
|
312
|
+
attestations: any;
|
|
313
|
+
/**
|
|
314
|
+
* THIS app's durable data/config. Default scope follows the function's declared `dataScope`
|
|
315
|
+
* (else `collection`). `appData.global` / `appData.collection` force a scope regardless.
|
|
316
|
+
* Own-data access is gated by `sl:data:read` / `sl:data:write` only.
|
|
317
|
+
*/
|
|
318
|
+
appData: AppDataHandle & {
|
|
319
|
+
global: AppDataHandle;
|
|
320
|
+
collection: AppDataHandle;
|
|
321
|
+
};
|
|
322
|
+
/**
|
|
323
|
+
* Read ANOTHER app's data in this collection, by CALLER identity — only what the invoking user
|
|
324
|
+
* could read (admin sees all; otherwise public-filtered, private fields stripped). Read-only;
|
|
325
|
+
* never another app's private/global store, never another collection. Gated by `sl:data:read`.
|
|
326
|
+
*/
|
|
327
|
+
app(appId: string): {
|
|
328
|
+
data: Pick<AppDataHandle, 'get' | 'getData'>;
|
|
329
|
+
};
|
|
330
|
+
}
|
|
271
331
|
/** Identity of whoever invoked a server function. */
|
|
272
332
|
export interface ServerFunctionCaller {
|
|
273
333
|
/** Authenticated user id, or `null` for anonymous/public/system invocations. */
|
|
@@ -297,7 +357,7 @@ export interface ServerFunctionContext {
|
|
|
297
357
|
* only — never a global superuser.
|
|
298
358
|
* Declared `capabilities` cap what these calls may do.
|
|
299
359
|
*/
|
|
300
|
-
sl:
|
|
360
|
+
sl: ServerFunctionSl;
|
|
301
361
|
/**
|
|
302
362
|
* Capability-gated secret access (both require the `secrets:<ref>` capability).
|
|
303
363
|
* `get(ref)` resolves the collection's OWN secret first (a client's credential), then falls back
|
|
@@ -315,7 +375,29 @@ export interface ServerFunctionContext {
|
|
|
315
375
|
/** Structured logging captured into run telemetry. */
|
|
316
376
|
log: (message: string, data?: Record<string, any>) => void;
|
|
317
377
|
}
|
|
318
|
-
/** The
|
|
378
|
+
/** The HTTP request handed to an `http`-trigger function as `event`. */
|
|
379
|
+
export interface ServerFunctionHttpEvent<TBody = any> {
|
|
380
|
+
method: string;
|
|
381
|
+
/** Parsed JSON body (when the request was JSON). */
|
|
382
|
+
body: TBody;
|
|
383
|
+
query: Record<string, any>;
|
|
384
|
+
/** Raw request headers (lower-cased keys). */
|
|
385
|
+
headers: Record<string, any>;
|
|
386
|
+
/** Raw request body bytes, when captured — for non-JSON inputs (XML, form, …). */
|
|
387
|
+
rawBody?: string | Buffer | null;
|
|
388
|
+
/** The request `content-type`, if any. */
|
|
389
|
+
contentType?: string | null;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* The signature every SmartLinks server function implements.
|
|
393
|
+
*
|
|
394
|
+
* RETURN CONTRACT (http trigger): whatever you return **is** the HTTP response.
|
|
395
|
+
* - a plain value → JSON body, HTTP 200 (no `{ ok, result }` envelope);
|
|
396
|
+
* - a web-standard `Response` → passed through verbatim (your status, headers, content-type, body —
|
|
397
|
+
* XML, CSV, binary, redirect, custom status);
|
|
398
|
+
* - throwing → HTTP 500 `{ error, message, code }`. For expected errors, return a `Response` with
|
|
399
|
+
* your own 4xx/5xx.
|
|
400
|
+
*/
|
|
319
401
|
export type ServerFunctionHandler<TEvent = any, TResult = any> = (ctx: ServerFunctionContext, event: TEvent) => Promise<TResult> | TResult;
|
|
320
402
|
/**
|
|
321
403
|
* Shape of `app.admin.json` -- the separate admin configuration file pointed to
|
|
@@ -403,6 +485,43 @@ export interface AppAdminConfig {
|
|
|
403
485
|
* Setup, import, tunable, and metrics configuration lives in a separate
|
|
404
486
|
* `app.admin.json` file. Use the `admin` field to locate and fetch it.
|
|
405
487
|
*/
|
|
488
|
+
/** The caller-supplied params a public view expects. */
|
|
489
|
+
export interface PublicViewParams {
|
|
490
|
+
/** Params the view REQUIRES to render (e.g. `['collectionId','pageId']`). */
|
|
491
|
+
required?: string[];
|
|
492
|
+
/** Params the view can use if present (e.g. `['orientation','theme']`). */
|
|
493
|
+
optional?: string[];
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* A declared PUBLIC VIEW of the app — a STANDALONE, freeform screen served over the app's single
|
|
497
|
+
* public bundle (`index.html` → HashRouter), enumerable/targetable by the platform + Dev Hub instead
|
|
498
|
+
* of guessing at undeclared hash routes. A view is `route` + fixed params (`set`) + caller `params`.
|
|
499
|
+
*
|
|
500
|
+
* PUBLIC VIEWS ARE NOT PORTAL. They are completely external, independent pages — display boards,
|
|
501
|
+
* projector/kiosk screens, dashboards, public share pages. Their ONLY input is URL parameters (often a
|
|
502
|
+
* `collectionId`, as a plain param). They expect NO portal wrapper, NO host-managed auth/session, and
|
|
503
|
+
* NO physical-twin context (no QR/NFC/tag scan). They open at a URL and stand alone.
|
|
504
|
+
*
|
|
505
|
+
* This is distinct from the two PORTAL-side surfaces, which are declared elsewhere and are context-fed
|
|
506
|
+
* by the physical twin the portal resolves:
|
|
507
|
+
* - WHERE the app renders inside portal (collection/product/batch/variant/proof) → the module
|
|
508
|
+
* registry `components.*` (see prove docs/design/module-registry-fields.md).
|
|
509
|
+
* - The different entry points portal MENUS/TABS/SIDE-MENUS link to (e.g. one app with a list view
|
|
510
|
+
* and a calendar view) → `linkable`/DeepLinkEntry below.
|
|
511
|
+
* See docs/design/public-views.md.
|
|
512
|
+
*/
|
|
513
|
+
export interface PublicView {
|
|
514
|
+
/** Stable id, unique within the app. */
|
|
515
|
+
id: string;
|
|
516
|
+
/** Human label (Dev Hub dropdown, platform pickers). */
|
|
517
|
+
title: string;
|
|
518
|
+
/** Hash route within the public bundle. Defaults to `/`. */
|
|
519
|
+
route?: string;
|
|
520
|
+
/** Query params this view PINS (e.g. `{ tvMode: 'true' }`), merged under the caller's params. */
|
|
521
|
+
set?: Record<string, string>;
|
|
522
|
+
/** The params the caller supplies via the URL. */
|
|
523
|
+
params?: PublicViewParams;
|
|
524
|
+
}
|
|
406
525
|
export interface AppManifest {
|
|
407
526
|
$schema?: string;
|
|
408
527
|
meta?: {
|
|
@@ -484,13 +603,24 @@ export interface AppManifest {
|
|
|
484
603
|
components: AppContainerComponent[];
|
|
485
604
|
};
|
|
486
605
|
/**
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
606
|
+
* PORTAL deep-linkable states built into this app — the entry points the portal's menu system
|
|
607
|
+
* (bottom menu, side menus, tabs) can link to. Use these when one app has several ways to enter/load
|
|
608
|
+
* its portal component (e.g. a list view and a calendar view, or a viewer and an editor) that
|
|
609
|
+
* different parts of portal should link to independently. These live INSIDE portal.
|
|
610
|
+
* Fixed routes are declared here at build time; dynamic content entries (e.g. CMS pages) are stored
|
|
611
|
+
* separately in `appConfig.linkable` — merge both for the full navigable set.
|
|
491
612
|
* @see DeepLinkEntry
|
|
492
613
|
*/
|
|
493
614
|
linkable?: DeepLinkEntry[];
|
|
615
|
+
/**
|
|
616
|
+
* The app's PUBLIC VIEWS — STANDALONE, external screens (display boards, kiosks/TVs, dashboards,
|
|
617
|
+
* public pages) served over the single public bundle, so the platform + Dev Hub can enumerate,
|
|
618
|
+
* preview, and target them. Declares `route` + fixed `set` params + caller `params` per view.
|
|
619
|
+
* NOT portal: no portal wrapper, no host auth/session, no physical-twin context — URL params only.
|
|
620
|
+
* For portal surfaces use `components.*` (where it renders) and `linkable` (portal menu entries).
|
|
621
|
+
* See PublicView + docs/design/public-views.md.
|
|
622
|
+
*/
|
|
623
|
+
publicViews?: PublicView[];
|
|
494
624
|
/**
|
|
495
625
|
* Executor bundle declaration. Present when the app ships a programmatic executor
|
|
496
626
|
* for AI-driven configuration, server-side SEO, and LLM content generation.
|
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.25 | Generated: 2026-09-27T12:31:16.701Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -2132,6 +2132,7 @@ interface AppFunctionDef {
|
|
|
2132
2132
|
authority?: AppFunctionAuthority;
|
|
2133
2133
|
elevated?: boolean;
|
|
2134
2134
|
capabilities?: string[];
|
|
2135
|
+
dataScope?: 'collection' | 'global';
|
|
2135
2136
|
apiVersion?: string;
|
|
2136
2137
|
handler?: string;
|
|
2137
2138
|
}
|
|
@@ -2145,6 +2146,40 @@ interface AppManifestFunctions {
|
|
|
2145
2146
|
}
|
|
2146
2147
|
```
|
|
2147
2148
|
|
|
2149
|
+
**AppDataOpts** (interface)
|
|
2150
|
+
```typescript
|
|
2151
|
+
interface AppDataOpts {
|
|
2152
|
+
scope?: 'collection' | 'global';
|
|
2153
|
+
productId?: string;
|
|
2154
|
+
variantId?: string;
|
|
2155
|
+
batchId?: string;
|
|
2156
|
+
dataId?: string;
|
|
2157
|
+
queries?: Record<string, any>;
|
|
2158
|
+
}
|
|
2159
|
+
```
|
|
2160
|
+
|
|
2161
|
+
**AppDataHandle** (interface)
|
|
2162
|
+
```typescript
|
|
2163
|
+
interface AppDataHandle {
|
|
2164
|
+
get(opts?: AppDataOpts): Promise<any>;
|
|
2165
|
+
set(data: any, opts?: AppDataOpts): Promise<any>;
|
|
2166
|
+
getData(opts?: AppDataOpts): Promise<any>;
|
|
2167
|
+
setData(data: any, opts?: AppDataOpts): Promise<any>;
|
|
2168
|
+
delete(opts?: AppDataOpts): Promise<any>;
|
|
2169
|
+
}
|
|
2170
|
+
```
|
|
2171
|
+
|
|
2172
|
+
**ServerFunctionSl** (interface)
|
|
2173
|
+
```typescript
|
|
2174
|
+
interface ServerFunctionSl {
|
|
2175
|
+
appRecords: any;
|
|
2176
|
+
products: any;
|
|
2177
|
+
attestations: any;
|
|
2178
|
+
appData: AppDataHandle & { global: AppDataHandle; collection: AppDataHandle };
|
|
2179
|
+
app(appId: string): { data: Pick<AppDataHandle, 'get' | 'getData'> };
|
|
2180
|
+
}
|
|
2181
|
+
```
|
|
2182
|
+
|
|
2148
2183
|
**ServerFunctionCaller** (interface)
|
|
2149
2184
|
```typescript
|
|
2150
2185
|
interface ServerFunctionCaller {
|
|
@@ -2161,7 +2196,7 @@ interface ServerFunctionCaller {
|
|
|
2161
2196
|
interface ServerFunctionContext {
|
|
2162
2197
|
collectionId: string;
|
|
2163
2198
|
appId: string;
|
|
2164
|
-
sl:
|
|
2199
|
+
sl: ServerFunctionSl;
|
|
2165
2200
|
secrets: {
|
|
2166
2201
|
get(ref: string): Promise<string | null>;
|
|
2167
2202
|
app(ref: string): Promise<string | null>;
|
|
@@ -2172,6 +2207,18 @@ interface ServerFunctionContext {
|
|
|
2172
2207
|
}
|
|
2173
2208
|
```
|
|
2174
2209
|
|
|
2210
|
+
**ServerFunctionHttpEvent<TBody = any>** (interface)
|
|
2211
|
+
```typescript
|
|
2212
|
+
interface ServerFunctionHttpEvent<TBody = any> {
|
|
2213
|
+
method: string;
|
|
2214
|
+
body: TBody;
|
|
2215
|
+
query: Record<string, any>;
|
|
2216
|
+
headers: Record<string, any>;
|
|
2217
|
+
rawBody?: string | Buffer | null;
|
|
2218
|
+
contentType?: string | null;
|
|
2219
|
+
}
|
|
2220
|
+
```
|
|
2221
|
+
|
|
2175
2222
|
**AppAdminConfig** (interface)
|
|
2176
2223
|
```typescript
|
|
2177
2224
|
interface AppAdminConfig {
|
|
@@ -2233,6 +2280,25 @@ interface AppAdminConfig {
|
|
|
2233
2280
|
}
|
|
2234
2281
|
```
|
|
2235
2282
|
|
|
2283
|
+
**PublicViewParams** (interface)
|
|
2284
|
+
```typescript
|
|
2285
|
+
interface PublicViewParams {
|
|
2286
|
+
required?: string[];
|
|
2287
|
+
optional?: string[];
|
|
2288
|
+
}
|
|
2289
|
+
```
|
|
2290
|
+
|
|
2291
|
+
**PublicView** (interface)
|
|
2292
|
+
```typescript
|
|
2293
|
+
interface PublicView {
|
|
2294
|
+
id: string;
|
|
2295
|
+
title: string;
|
|
2296
|
+
route?: string;
|
|
2297
|
+
set?: Record<string, string>;
|
|
2298
|
+
params?: PublicViewParams;
|
|
2299
|
+
}
|
|
2300
|
+
```
|
|
2301
|
+
|
|
2236
2302
|
**AppManifest** (interface)
|
|
2237
2303
|
```typescript
|
|
2238
2304
|
interface AppManifest {
|
|
@@ -2267,6 +2333,7 @@ interface AppManifest {
|
|
|
2267
2333
|
components: AppContainerComponent[];
|
|
2268
2334
|
};
|
|
2269
2335
|
linkable?: DeepLinkEntry[];
|
|
2336
|
+
publicViews?: PublicView[];
|
|
2270
2337
|
executor?: AppManifestExecutor;
|
|
2271
2338
|
functions?: AppManifestFunctions;
|
|
2272
2339
|
[key: string]: any;
|
package/docs/app-manifest.md
CHANGED
|
@@ -332,6 +332,48 @@ See the [Deep Link Discovery guide](deep-link-discovery.md) for the full dual-so
|
|
|
332
332
|
| `path` | string | ❌ | Hash route within the app (defaults to `"/"` if omitted) |
|
|
333
333
|
| `params` | object | ❌ | App-specific query params appended to the URL — do **not** include platform params (`collectionId`, `productId`, etc.) |
|
|
334
334
|
|
|
335
|
+
#### `publicViews`
|
|
336
|
+
|
|
337
|
+
Declares the app's **public views** — **standalone, external screens** served over your single public
|
|
338
|
+
bundle (`index.html` → HashRouter): a display board, a kiosk/TV screen, a dashboard, a public share
|
|
339
|
+
page. Without this, those routes/modes are invisible to the platform and the Dev Hub. Each view is a
|
|
340
|
+
`route` + fixed `set` params + caller `params`.
|
|
341
|
+
|
|
342
|
+
> **Public views are NOT portal.** They are completely external and independent. Their only input is
|
|
343
|
+
> **URL parameters** (often a `collectionId`, as a plain param). They expect **no** portal wrapper, **no**
|
|
344
|
+
> host-managed auth/session, and **no** physical-twin context (no QR/NFC/tag scan). They open at a URL
|
|
345
|
+
> and stand alone. For the two **portal** surfaces — where the app renders *inside* portal, and the
|
|
346
|
+
> entry points portal menus link to — see below.
|
|
347
|
+
|
|
348
|
+
```json
|
|
349
|
+
"publicViews": [
|
|
350
|
+
{ "id": "board", "title": "Display board", "route": "/display",
|
|
351
|
+
"params": { "required": ["collectionId"], "optional": ["orientation"] } },
|
|
352
|
+
{ "id": "tv", "title": "TV / big screen", "route": "/", "set": { "tvMode": "true" },
|
|
353
|
+
"params": { "required": ["collectionId", "voteId"] } }
|
|
354
|
+
]
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
| Field | Type | Required | Description |
|
|
358
|
+
|-------|------|----------|-------------|
|
|
359
|
+
| `id` | string | ✅ | Stable id, unique within the app |
|
|
360
|
+
| `title` | string | ✅ | Human label (Dev Hub dropdown, platform pickers) |
|
|
361
|
+
| `route` | string | ❌ | Hash route within the public bundle (defaults to `"/"`) |
|
|
362
|
+
| `set` | object | ❌ | Query params this view PINS (e.g. `{ "tvMode": "true" }`), merged under caller params |
|
|
363
|
+
| `params` | `{ required?: string[]; optional?: string[] }` | ❌ | The params the caller supplies via the URL |
|
|
364
|
+
|
|
365
|
+
Read the URL params with **`SL.readContext()`** (merges hash → search) instead of hand-rolling it.
|
|
366
|
+
|
|
367
|
+
##### Public views vs the two portal surfaces
|
|
368
|
+
|
|
369
|
+
An app can have any combination of three setup styles. Only public views are external:
|
|
370
|
+
|
|
371
|
+
| Style | Declared by | Lives | Context |
|
|
372
|
+
|-------|-------------|-------|---------|
|
|
373
|
+
| **Portal components** — where the app renders inside portal (collection / product / batch / variant / proof) | module registry `components.*` (set at publish) | Inside portal | Physical twin (QR/NFC scan) |
|
|
374
|
+
| **Portal deep links** — the entry points portal menus/tabs/side-menus link to (e.g. list view vs calendar view) | `linkable` / `DeepLinkEntry` | Inside portal | Portal (menu picks the entry) |
|
|
375
|
+
| **Public views** — standalone external screens | `publicViews` (this block) | Outside portal | URL params only |
|
|
376
|
+
|
|
335
377
|
#### `records`
|
|
336
378
|
|
|
337
379
|
Declares which `app.records` record types the app stores, and which scopes each type supports. Required for any app that follows the [App Records Pattern](app-records-pattern.md). Omit if the app does not use scoped records.
|
package/docs/server-functions.md
CHANGED
|
@@ -320,7 +320,8 @@ Prefer the platform primitives above over a dependency; when you do need one, pi
|
|
|
320
320
|
| Hash / HMAC-sign / verify / encrypt | `crypto.subtle` (Web Crypto) | — |
|
|
321
321
|
| Random id / bytes | `crypto.randomUUID()` / `crypto.getRandomValues()` | — |
|
|
322
322
|
| Read or write SmartLinks data | `ctx.sl.*` (records, products, attestations, …) | the matching `sl:<res>:<read\|write>` |
|
|
323
|
-
| Read/write
|
|
323
|
+
| Read/write your app's own data (config, URLs, counters, arrays) | `ctx.sl.appData.get()` / `ctx.sl.appData.set({…})` — collection-scoped by default; `ctx.sl.appData.global.*` (or `{ scope:'global' }`, or declare `dataScope:'global'`) for the app-wide bucket. It's your OWN namespace, so **any** authority may read/write it | `sl:data:read` / `sl:data:write` |
|
|
324
|
+
| Read ANOTHER app's data (as the caller may see it) | `ctx.sl.app('other-app').data.get()` — caller-authority, this collection, public-filtered, read-only | `sl:data:read` |
|
|
324
325
|
| Guarantee a UNIQUE claim (pool of numbers, one-per-user, idempotency key) | `ctx.sl.appRecords.create({ ref: 'ball:57' })` — the DB unique index rejects a duplicate `ref` (catch = "already taken") | `sl:records:write` |
|
|
325
326
|
|
|
326
327
|
Notes:
|
|
@@ -403,13 +404,25 @@ from your app").
|
|
|
403
404
|
> trusted. When untrusted third-party apps arrive, gate *elevated* (`public` + `collection`)
|
|
404
405
|
> invocation of a **non-enabled** app behind install/consent — the app-scoped resolver is the seam.
|
|
405
406
|
|
|
406
|
-
The
|
|
407
|
-
`event.
|
|
408
|
-
|
|
409
|
-
`
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
407
|
+
The handler receives the request as `event`: `event.body` (parsed JSON), `event.query`,
|
|
408
|
+
`event.headers`, `event.rawBody` (raw bytes, for XML/form/other inputs), and `event.contentType`.
|
|
409
|
+
A function only runs on its own surface — calling an `admin` function on the public endpoint is a
|
|
410
|
+
`403`. `GET` on either endpoint lists the functions callable on that surface.
|
|
411
|
+
|
|
412
|
+
**Response contract — your return IS the response (no envelope):**
|
|
413
|
+
- Return a **plain value** → it becomes the JSON body, HTTP `200`. (`SL.functions.call()` gives you
|
|
414
|
+
that value directly — `res.value`, not `res.result.value`.)
|
|
415
|
+
- Return a web-standard **`Response`** → passed through verbatim: your status, headers, content-type
|
|
416
|
+
and body (XML, CSV, text, binary, redirect, custom status). `Response` is a runtime global.
|
|
417
|
+
```js
|
|
418
|
+
return new Response(toXml(data), { status: 200, headers: { "content-type": "application/xml" } })
|
|
419
|
+
```
|
|
420
|
+
- **Throw** → HTTP `500` `{ error: "FUNCTION_ERROR", message, code }`. For *expected* errors (400/404/
|
|
421
|
+
409…), return a `Response` with your own status + body.
|
|
422
|
+
|
|
423
|
+
One rule: **plain object ⇒ 200; want any other status/headers/content-type ⇒ return a `Response`.**
|
|
424
|
+
Timing comes back in an `X-SL-Function-Duration-Ms` header (admin surface); your `ctx.log` lines land
|
|
425
|
+
in the collection's Errors & Activity feed — the body stays purely your output.
|
|
413
426
|
|
|
414
427
|
The admin surface is collection-admin gated, so an admin function's `caller` authority runs
|
|
415
428
|
at admin level, attributed to the signed-in admin. The public surface resolves auth if a
|
|
@@ -434,7 +447,7 @@ So the end-to-end path is: *write → register the release → enable on a colle
|
|
|
434
447
|
Every invocation is recorded as an **execution** activity event, carrying your `ctx.log`
|
|
435
448
|
lines. Where to look, easiest first:
|
|
436
449
|
|
|
437
|
-
- **Admin-surface response** —
|
|
450
|
+
- **Admin-surface response** — timing comes back in the `X-SL-Function-Duration-Ms` header (logs are in Errors & Activity, not the body).
|
|
438
451
|
- **Deployed test mode** — see below; real run, isolated logging.
|
|
439
452
|
- **Owner console** — the collection's **Advanced → Errors & Activity → events** tab,
|
|
440
453
|
filtered to **source = execution**: each run shows as `function <name> ok` (or an error),
|
package/openapi.yaml
CHANGED
|
@@ -18255,6 +18255,11 @@ components:
|
|
|
18255
18255
|
type: array
|
|
18256
18256
|
items:
|
|
18257
18257
|
type: string
|
|
18258
|
+
dataScope:
|
|
18259
|
+
type: string
|
|
18260
|
+
enum:
|
|
18261
|
+
- collection
|
|
18262
|
+
- global
|
|
18258
18263
|
apiVersion:
|
|
18259
18264
|
type: string
|
|
18260
18265
|
handler:
|
|
@@ -18274,6 +18279,41 @@ components:
|
|
|
18274
18279
|
required:
|
|
18275
18280
|
- files
|
|
18276
18281
|
- definitions
|
|
18282
|
+
AppDataOpts:
|
|
18283
|
+
type: object
|
|
18284
|
+
properties:
|
|
18285
|
+
scope:
|
|
18286
|
+
type: string
|
|
18287
|
+
enum:
|
|
18288
|
+
- collection
|
|
18289
|
+
- global
|
|
18290
|
+
productId:
|
|
18291
|
+
type: string
|
|
18292
|
+
variantId:
|
|
18293
|
+
type: string
|
|
18294
|
+
batchId:
|
|
18295
|
+
type: string
|
|
18296
|
+
dataId:
|
|
18297
|
+
type: string
|
|
18298
|
+
queries:
|
|
18299
|
+
type: object
|
|
18300
|
+
additionalProperties: true
|
|
18301
|
+
AppDataHandle:
|
|
18302
|
+
type: object
|
|
18303
|
+
properties: {}
|
|
18304
|
+
ServerFunctionSl:
|
|
18305
|
+
type: object
|
|
18306
|
+
properties:
|
|
18307
|
+
appRecords: {}
|
|
18308
|
+
products: {}
|
|
18309
|
+
attestations: {}
|
|
18310
|
+
appData:
|
|
18311
|
+
$ref: "#/components/schemas/AppDataHandle"
|
|
18312
|
+
required:
|
|
18313
|
+
- appRecords
|
|
18314
|
+
- products
|
|
18315
|
+
- attestations
|
|
18316
|
+
- appData
|
|
18277
18317
|
ServerFunctionCaller:
|
|
18278
18318
|
type: object
|
|
18279
18319
|
properties:
|
|
@@ -18298,7 +18338,8 @@ components:
|
|
|
18298
18338
|
type: string
|
|
18299
18339
|
appId:
|
|
18300
18340
|
type: string
|
|
18301
|
-
sl:
|
|
18341
|
+
sl:
|
|
18342
|
+
$ref: "#/components/schemas/ServerFunctionSl"
|
|
18302
18343
|
secrets:
|
|
18303
18344
|
type: object
|
|
18304
18345
|
additionalProperties: true
|
|
@@ -18314,6 +18355,30 @@ components:
|
|
|
18314
18355
|
- secrets
|
|
18315
18356
|
- caller
|
|
18316
18357
|
- fetch
|
|
18358
|
+
ServerFunctionHttpEvent:
|
|
18359
|
+
type: object
|
|
18360
|
+
properties:
|
|
18361
|
+
method:
|
|
18362
|
+
type: string
|
|
18363
|
+
body:
|
|
18364
|
+
type: object
|
|
18365
|
+
additionalProperties: true
|
|
18366
|
+
query:
|
|
18367
|
+
type: object
|
|
18368
|
+
additionalProperties: true
|
|
18369
|
+
headers:
|
|
18370
|
+
type: object
|
|
18371
|
+
additionalProperties: true
|
|
18372
|
+
rawBody:
|
|
18373
|
+
type: object
|
|
18374
|
+
additionalProperties: true
|
|
18375
|
+
contentType:
|
|
18376
|
+
type: string
|
|
18377
|
+
required:
|
|
18378
|
+
- method
|
|
18379
|
+
- body
|
|
18380
|
+
- query
|
|
18381
|
+
- headers
|
|
18317
18382
|
AppAdminConfig:
|
|
18318
18383
|
type: object
|
|
18319
18384
|
properties:
|
|
@@ -18397,6 +18462,35 @@ components:
|
|
|
18397
18462
|
- scope
|
|
18398
18463
|
- name
|
|
18399
18464
|
- type
|
|
18465
|
+
PublicViewParams:
|
|
18466
|
+
type: object
|
|
18467
|
+
properties:
|
|
18468
|
+
required:
|
|
18469
|
+
type: array
|
|
18470
|
+
items:
|
|
18471
|
+
type: string
|
|
18472
|
+
optional:
|
|
18473
|
+
type: array
|
|
18474
|
+
items:
|
|
18475
|
+
type: string
|
|
18476
|
+
PublicView:
|
|
18477
|
+
type: object
|
|
18478
|
+
properties:
|
|
18479
|
+
id:
|
|
18480
|
+
type: string
|
|
18481
|
+
title:
|
|
18482
|
+
type: string
|
|
18483
|
+
route:
|
|
18484
|
+
type: string
|
|
18485
|
+
set:
|
|
18486
|
+
type: object
|
|
18487
|
+
additionalProperties:
|
|
18488
|
+
type: string
|
|
18489
|
+
params:
|
|
18490
|
+
$ref: "#/components/schemas/PublicViewParams"
|
|
18491
|
+
required:
|
|
18492
|
+
- id
|
|
18493
|
+
- title
|
|
18400
18494
|
AppManifest:
|
|
18401
18495
|
type: object
|
|
18402
18496
|
properties:
|
|
@@ -18472,6 +18566,10 @@ components:
|
|
|
18472
18566
|
type: array
|
|
18473
18567
|
items:
|
|
18474
18568
|
$ref: "#/components/schemas/DeepLinkEntry"
|
|
18569
|
+
publicViews:
|
|
18570
|
+
type: array
|
|
18571
|
+
items:
|
|
18572
|
+
$ref: "#/components/schemas/PublicView"
|
|
18475
18573
|
executor:
|
|
18476
18574
|
$ref: "#/components/schemas/AppManifestExecutor"
|
|
18477
18575
|
functions:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proveanything/smartlinks",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.25",
|
|
4
4
|
"description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -65,8 +65,7 @@
|
|
|
65
65
|
"author": "Glenn Shoosmith",
|
|
66
66
|
"license": "MIT",
|
|
67
67
|
"publishConfig": {
|
|
68
|
-
"access": "public"
|
|
69
|
-
"tag": "next"
|
|
68
|
+
"access": "public"
|
|
70
69
|
},
|
|
71
70
|
"dependencies": {
|
|
72
71
|
"cross-fetch": "^3.1.5",
|