@proveanything/smartlinks 2.0.21 → 2.0.24

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.
@@ -248,10 +248,21 @@ To call a *different* app's function, pass the appId explicitly:
248
248
  await SL.functions.call(collectionId, 'pressCounter', {}, { appId: 'some-other-app' })
249
249
  ```
250
250
 
251
+ **The app does NOT have to be enabled on the collection.** Because the appId is explicit, the
252
+ function is resolved directly from the app's release — so you can test an app on any collection
253
+ before installing it (and without it showing up in that collection's menus). Enabling an app is
254
+ currently just a UX courtesy (dropdowns/menus), not a gate on running its functions. For a build
255
+ that isn't the default `stable` channel — e.g. a **dev** app you haven't installed — pass the
256
+ channel:
257
+
258
+ ```ts
259
+ await SL.functions.call(collectionId, 'pressCounter', {}, { appId: 'my-counter-app', channel: 'dev' })
260
+ ```
261
+
251
262
  If no appId is available (not set on init, none passed), the call falls back to the **deprecated
252
- flat path** `/collection/:c/functions/:name`, which the server resolves by bare name and **rejects
253
- with `409 AMBIGUOUS_FUNCTION`** when more than one installed app defines that name (a first-party
254
- builtin still wins). Always prefer an appId.
263
+ flat path** `/collection/:c/functions/:name`, which searches only the collection's **enabled** apps,
264
+ resolves by bare name, and **rejects with `409 AMBIGUOUS_FUNCTION`** when more than one defines that
265
+ name (a first-party builtin still wins). Always prefer an appId.
255
266
 
256
267
  > There is no such thing as an app-less function. First-party builtins (e.g. the `functions.ping`
257
268
  > diagnostic) belong to the reserved `smartlinks` app. A "global" function is just an app enabled on
@@ -309,7 +320,8 @@ Prefer the platform primitives above over a dependency; when you do need one, pi
309
320
  | Hash / HMAC-sign / verify / encrypt | `crypto.subtle` (Web Crypto) | — |
310
321
  | Random id / bytes | `crypto.randomUUID()` / `crypto.getRandomValues()` | — |
311
322
  | Read or write SmartLinks data | `ctx.sl.*` (records, products, attestations, …) | the matching `sl:<res>:<read\|write>` |
312
- | Read/write general or app-wide data (config, URLs, counters, arrays) | `ctx.sl.appData.get()` / `ctx.sl.appData.set({…})` — pass `{ scope: 'global' }` for app-wide (shared by every install; global writes need collection authority) | `sl:data:read` / `sl:data: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` |
313
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` |
314
326
 
315
327
  Notes:
@@ -376,18 +388,41 @@ POST /public/collection/:collectionId/app/:appId/functions/:name # visibility:
376
388
  GET /{admin|public}/collection/:collectionId/app/:appId/functions # list this app's functions on the surface
377
389
  ```
378
390
 
391
+ The app-scoped route resolves the app **directly by id**, so the app need not be enabled on the
392
+ collection — handy for testing an un-installed (dev) app. Add `?channel=dev` (default `stable`) to
393
+ pick the release. Enablement (`appConfig.apps[]`) currently only controls menus/discovery, not
394
+ whether a function can run.
395
+
379
396
  The bare-name form (`/collection/:c/functions/:name`, no `/app/:appId`) is a **deprecated alias**:
380
- it resolves by name across all installed apps, lets a first-party builtin win, and returns
381
- `409 AMBIGUOUS_FUNCTION` when two apps define the same name. Prefer the app-scoped route (the SDK
382
- emits it automatically once an appId is set — see "Calling a function from your app").
383
-
384
- The request body is delivered to the handler as `event.body` (query string as
385
- `event.query`). A function only runs on its own surface — calling an `admin` function on
386
- the public endpoint is a `403`. The response is `{ ok: true, result }` on success, or
387
- `{ error, message }` (HTTP 400) if the handler returned an error. On the **admin surface**
388
- the response also includes `logs` (your `ctx.log` lines) and `durationMs` for quick
389
- debugging; the **public surface returns only `result`** (a public caller never sees your
390
- internal logs). `GET` on either endpoint lists the functions callable on that surface.
397
+ it searches only the collection's **enabled** apps, resolves by name, lets a first-party builtin
398
+ win, and returns `409 AMBIGUOUS_FUNCTION` when two enabled apps define the same name. Prefer the
399
+ app-scoped route (the SDK emits it automatically once an appId is set — see "Calling a function
400
+ from your app").
401
+
402
+ > **Security note (first-party model, today):** because enablement is not an auth gate, any app's
403
+ > function can be invoked on any collection by id. That's fine while all apps are first-party and
404
+ > trusted. When untrusted third-party apps arrive, gate *elevated* (`public` + `collection`)
405
+ > invocation of a **non-enabled** app behind install/consent — the app-scoped resolver is the seam.
406
+
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.
391
426
 
392
427
  The admin surface is collection-admin gated, so an admin function's `caller` authority runs
393
428
  at admin level, attributed to the signed-in admin. The public surface resolves auth if a
@@ -412,7 +447,7 @@ So the end-to-end path is: *write → register the release → enable on a colle
412
447
  Every invocation is recorded as an **execution** activity event, carrying your `ctx.log`
413
448
  lines. Where to look, easiest first:
414
449
 
415
- - **Admin-surface response** — `logs` + `durationMs` come straight back in the JSON.
450
+ - **Admin-surface response** — timing comes back in the `X-SL-Function-Duration-Ms` header (logs are in Errors & Activity, not the body).
416
451
  - **Deployed test mode** — see below; real run, isolated logging.
417
452
  - **Owner console** — the collection's **Advanced → Errors & Activity → events** tab,
418
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,40 @@ 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
+ kind:
18484
+ $ref: "#/components/schemas/PublicViewKind"
18485
+ route:
18486
+ type: string
18487
+ set:
18488
+ type: object
18489
+ additionalProperties:
18490
+ type: string
18491
+ params:
18492
+ $ref: "#/components/schemas/PublicViewParams"
18493
+ default:
18494
+ type: boolean
18495
+ required:
18496
+ - id
18497
+ - title
18498
+ - kind
18400
18499
  AppManifest:
18401
18500
  type: object
18402
18501
  properties:
@@ -18472,6 +18571,10 @@ components:
18472
18571
  type: array
18473
18572
  items:
18474
18573
  $ref: "#/components/schemas/DeepLinkEntry"
18574
+ publicViews:
18575
+ type: array
18576
+ items:
18577
+ $ref: "#/components/schemas/PublicView"
18475
18578
  executor:
18476
18579
  $ref: "#/components/schemas/AppManifestExecutor"
18477
18580
  functions:
@@ -18532,6 +18635,11 @@ components:
18532
18635
  enum:
18533
18636
  - caller
18534
18637
  - collection
18638
+ PublicViewKind:
18639
+ type: string
18640
+ enum:
18641
+ - contextual
18642
+ - standalone
18535
18643
  PaginatedResponse:
18536
18644
  type: object
18537
18645
  properties:
@@ -29290,7 +29398,8 @@ components:
29290
29398
  type: object
29291
29399
  properties:
29292
29400
  appId:
29293
- type: string
29401
+ type: object
29402
+ additionalProperties: true
29294
29403
  AllocateSequenceInput:
29295
29404
  type: object
29296
29405
  properties:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proveanything/smartlinks",
3
- "version": "2.0.21",
3
+ "version": "2.0.24",
4
4
  "description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",