@proveanything/smartlinks 2.0.3 → 2.0.5
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 +1 -1
- package/dist/docs/app-manifest.md +33 -0
- package/dist/docs/deploying-apps.md +60 -0
- package/dist/docs/overview.md +2 -0
- package/dist/docs/server-functions.md +41 -0
- package/docs/API_SUMMARY.md +1 -1
- package/docs/app-manifest.md +33 -0
- package/docs/deploying-apps.md +60 -0
- package/docs/overview.md +2 -0
- package/docs/server-functions.md +41 -0
- package/package.json +4 -2
- package/scripts/publish.mjs +99 -0
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -147,6 +147,21 @@ The manifest is loaded automatically by the platform for every collection page.
|
|
|
147
147
|
| `platformRevision` | string | ❌ | ISO date string marking the platform API revision this build targets |
|
|
148
148
|
| `seo.priority` | number | ❌ | Controls which app's `title`/`description`/`ogImage` wins when multiple apps are on the same page. Default `0`; higher wins. See the [Executor guide](executor.md). |
|
|
149
149
|
|
|
150
|
+
#### `build`
|
|
151
|
+
|
|
152
|
+
Optional build provenance. Recommended for the **Lovable dev publish** flow: stamp the content
|
|
153
|
+
hash your build already produces here so the platform can tell when a new version is live before it
|
|
154
|
+
registers (see [Deploying & registering](deploying-apps.md#a-from-lovable--hit-publish-no-key-anywhere-recommended-for-lovable-apps)).
|
|
155
|
+
|
|
156
|
+
| Field | Type | Description |
|
|
157
|
+
|-------|------|-------------|
|
|
158
|
+
| `build.hash` | string | Unique build/content hash — the platform waits for this to appear before registering, and uses it as the dev release version |
|
|
159
|
+
| `build.at` | string | ISO timestamp of the build (optional) |
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
"build": { "hash": "a1b2c3d4", "at": "2026-09-20T10:00:00Z" }
|
|
163
|
+
```
|
|
164
|
+
|
|
150
165
|
#### `admin`
|
|
151
166
|
|
|
152
167
|
A relative path (from the app's public root) to the `app.admin.json` file. Omit entirely if the app has no admin UI.
|
|
@@ -312,6 +327,24 @@ See the **[Executor Model guide](executor.md)** for the full build setup, SEO co
|
|
|
312
327
|
| `llmContent.function` | string | Name of the `getLLMContent` export |
|
|
313
328
|
| `llmContent.timeout` | number | Timeout in ms (default 500) |
|
|
314
329
|
|
|
330
|
+
#### `functions`
|
|
331
|
+
|
|
332
|
+
Declares app-authored **server functions** — server-side handlers `(ctx, event) ⇒ result`
|
|
333
|
+
(http / event / cron), shipped in a self-contained UMD bundle. Omit if the app has no server
|
|
334
|
+
functions. The security model (`visibility`/`authority`/`capabilities`), the runtime surface, how
|
|
335
|
+
to build the bundle, and how to invoke are all in the **[Server Functions guide](server-functions.md)** —
|
|
336
|
+
this is just the manifest shape.
|
|
337
|
+
|
|
338
|
+
| Field | Type | Description |
|
|
339
|
+
|-------|------|-------------|
|
|
340
|
+
| `files.js.umd` | string | UMD bundle path (e.g. `dist/functions.umd.js`) — a self-contained bundle exporting each handler by name |
|
|
341
|
+
| `definitions[]` | object[] | One entry per function |
|
|
342
|
+
| `definitions[].name` | string | Function name; also the handler export name (override with `handler`) |
|
|
343
|
+
| `definitions[].trigger` | object | `{ type: "http"|"event"|"cron", … }` |
|
|
344
|
+
| `definitions[].visibility` | string | `admin` \| `public` (http only) — who may call it |
|
|
345
|
+
| `definitions[].authority` | string | `caller` \| `collection` — whose authority it runs as |
|
|
346
|
+
| `definitions[].capabilities` | string[] | Least-privilege grants (`sl:<res>:<read\|write>`, `network[:<host>]`, `secrets:<ref>`) |
|
|
347
|
+
|
|
315
348
|
---
|
|
316
349
|
|
|
317
350
|
## `app.admin.json`
|
|
@@ -16,6 +16,66 @@ This guide covers registration and how to wire it into your build.
|
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
+
## Fast dev publish
|
|
20
|
+
|
|
21
|
+
Getting a **dev** build live should be instant and secret-free. There are two paths depending on
|
|
22
|
+
**where you build** — pick the one that matches your setup. Both end the same way: the dev release
|
|
23
|
+
is registered and served **`no-store`** (every reload is instantly fresh — no cache-busting, no CDN
|
|
24
|
+
invalidation).
|
|
25
|
+
|
|
26
|
+
### A. From Lovable — hit Publish, no key anywhere *(recommended for Lovable apps)*
|
|
27
|
+
|
|
28
|
+
Lovable builds and hosts your app on **Publish**. You don't push to us and you **don't put any
|
|
29
|
+
secret in Lovable or your repo** — instead your build *pings* us and we pull your just-published
|
|
30
|
+
bundle. Two small additions to your build, both non-secret:
|
|
31
|
+
|
|
32
|
+
1. **Stamp the build hash into `app.manifest.json`.** In the same build step that content-hashes
|
|
33
|
+
your JS, write the hash into the manifest so we can tell when your new version is live:
|
|
34
|
+
```jsonc
|
|
35
|
+
// app.manifest.json
|
|
36
|
+
"build": { "hash": "a1b2c3d4", "at": "2026-09-20T10:00:00Z" }
|
|
37
|
+
```
|
|
38
|
+
2. **Ping us on publish** (a `postbuild` step — `SMARTLINKS_APP_ID` is an *identifier, not a
|
|
39
|
+
secret*, so it's fine in the repo):
|
|
40
|
+
```jsonc
|
|
41
|
+
// package.json
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "vite build && … && node scripts/hash-bundles.mjs", // your existing hashing step
|
|
44
|
+
"postbuild": "curl -fsS -X POST \"$SMARTLINKS_API/api/v1/apps/$SMARTLINKS_APP_ID/refresh-dev\" -H 'content-type: application/json' -d \"{\\\"hash\\\":\\\"$BUILD_HASH\\\"}\""
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
That's it — no deploy key. When the ping arrives, SmartLinks looks up your app's published URL
|
|
49
|
+
(from the catalog — **not** from the ping, so it's safe), **waits until your manifest reports that
|
|
50
|
+
exact `build.hash`** (so it never grabs a half-propagated build), validates the manifest + functions,
|
|
51
|
+
and registers the dev release (the hash becomes the version). `POST /apps/{appId}/refresh-dev`
|
|
52
|
+
authorises nothing on its own — worst case it re-fetches your app's own public bundle — so it needs
|
|
53
|
+
no secret; it's rate-limited and de-duped per app.
|
|
54
|
+
|
|
55
|
+
**One-time setup:** the app must exist in the catalog with its **id** and its **Lovable URL**
|
|
56
|
+
recorded (that's how we know what to fetch, and the `id` you ping with must match). Ask the platform
|
|
57
|
+
owner to register the app once; after that, every Publish auto-updates dev.
|
|
58
|
+
|
|
59
|
+
### B. From local / CI / Claude — `smartlinks-publish`
|
|
60
|
+
|
|
61
|
+
If you build somewhere you control (so you can hold a dev key locally — never committed), push the
|
|
62
|
+
built bundle straight to SmartLinks, which hosts + registers it:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# build dist/ first, then:
|
|
66
|
+
smartlinks-publish # uploads dist/ → SmartLinks hosts + registers the dev release
|
|
67
|
+
smartlinks-publish --watch # re-publish on every change (save → live)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Env: `SMARTLINKS_APP_ID`, `SMARTLINKS_DEPLOY_KEY` (dev key — dev-only, safe to keep locally),
|
|
71
|
+
optional `SMARTLINKS_API` / `--dir`. It POSTs the files to `POST /apps/{appId}/publish?channel=dev`;
|
|
72
|
+
the server validates and writes them to `smartlinks.app/apps/{appId}/dev/…` (`no-store`).
|
|
73
|
+
|
|
74
|
+
**Which to use:** building in Lovable → **A** (ping, no key). Building anywhere you control →
|
|
75
|
+
**B** (push with a local key). For beta/stable, see the register-a-URL flow below.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
19
79
|
## Two "wheres": the API host vs the bundle CDN
|
|
20
80
|
|
|
21
81
|
Deploying touches two different locations — keep them distinct:
|
package/dist/docs/overview.md
CHANGED
|
@@ -63,6 +63,8 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
|
|
|
63
63
|
| **Containers** | `docs/containers.md` | Building full-app embeddable containers (lazy-loaded) |
|
|
64
64
|
| **Mobile Admin Container** | `docs/mobile-admin-container.md` | Building a separate Capacitor-aware mobile admin bundle for field operators |
|
|
65
65
|
| **Executors** | `docs/executor.md` | Building executor bundles for SEO, LLM content, programmatic config |
|
|
66
|
+
| **Server Functions** | `docs/server-functions.md` | App-authored server-side functions `(ctx, event) ⇒ result`: security model, runtime surface, invoking |
|
|
67
|
+
| **Deploying & Registering** | `docs/deploying-apps.md` | Getting your app into the platform: fast dev publish, channels, deploy keys, registering releases |
|
|
66
68
|
| **Deep Linking** | `docs/deep-link-discovery.md` | URL state management, navigable states, portal menus, AI nav |
|
|
67
69
|
| **Portal Back Button** | `docs/portal-back-button.md` | Hierarchy-aware "up" navigation inside embedded apps |
|
|
68
70
|
| **Portal Request Action** | `docs/portal-request-action.md` | Triggering portal built-in actions (__qrScanner, __share, __logout, etc.) from sub-apps |
|
|
@@ -63,6 +63,47 @@ in a bundle alongside your widgets/containers:
|
|
|
63
63
|
|
|
64
64
|
Each definition maps to an exported handler of the same `name` (override with `handler`).
|
|
65
65
|
|
|
66
|
+
## Building the functions bundle
|
|
67
|
+
|
|
68
|
+
The handlers ship as a **self-contained UMD** — one file, all dependencies compiled in, no
|
|
69
|
+
`require`/`import` of platform or Node modules (see [Runtime](#runtime--what-your-function-can-use)).
|
|
70
|
+
Your functions entry just **named-exports** each handler:
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
// src/functions/index.js — the functions entry
|
|
74
|
+
export async function submitCompetitionEntry(ctx, event) { /* … */ }
|
|
75
|
+
export async function onProofCreated(ctx, event) { /* … */ }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Build it to `dist/functions.umd.js` with a Vite lib build — **UMD format, nothing externalised**
|
|
79
|
+
(so it's self-contained), and don't wipe the widgets you built into the same `dist/`:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// vite.functions.config.ts
|
|
83
|
+
import { defineConfig } from 'vite'
|
|
84
|
+
export default defineConfig({
|
|
85
|
+
build: {
|
|
86
|
+
lib: { entry: 'src/functions/index.js', formats: ['umd'], name: 'functions',
|
|
87
|
+
fileName: () => 'functions.umd.js' },
|
|
88
|
+
outDir: 'dist',
|
|
89
|
+
emptyOutDir: false, // co-exist with the widgets/containers build
|
|
90
|
+
rollupOptions: { external: [] } // bundle everything — no Node/platform externals
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```jsonc
|
|
96
|
+
// package.json — build widgets/containers as usual, then the functions bundle
|
|
97
|
+
"scripts": {
|
|
98
|
+
"build": "vite build && vite build --config vite.functions.config.ts"
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
That emits `dist/functions.umd.js` (matching `functions.files.js.umd`), whose named exports the
|
|
103
|
+
platform pairs with your manifest `definitions`. Then **deploy** it — for dev,
|
|
104
|
+
`smartlinks-publish` uploads `dist/` and registers in one step (see
|
|
105
|
+
[Deploying & registering](deploying-apps.md)).
|
|
106
|
+
|
|
66
107
|
---
|
|
67
108
|
|
|
68
109
|
## The two security questions every function answers
|
package/docs/API_SUMMARY.md
CHANGED
package/docs/app-manifest.md
CHANGED
|
@@ -147,6 +147,21 @@ The manifest is loaded automatically by the platform for every collection page.
|
|
|
147
147
|
| `platformRevision` | string | ❌ | ISO date string marking the platform API revision this build targets |
|
|
148
148
|
| `seo.priority` | number | ❌ | Controls which app's `title`/`description`/`ogImage` wins when multiple apps are on the same page. Default `0`; higher wins. See the [Executor guide](executor.md). |
|
|
149
149
|
|
|
150
|
+
#### `build`
|
|
151
|
+
|
|
152
|
+
Optional build provenance. Recommended for the **Lovable dev publish** flow: stamp the content
|
|
153
|
+
hash your build already produces here so the platform can tell when a new version is live before it
|
|
154
|
+
registers (see [Deploying & registering](deploying-apps.md#a-from-lovable--hit-publish-no-key-anywhere-recommended-for-lovable-apps)).
|
|
155
|
+
|
|
156
|
+
| Field | Type | Description |
|
|
157
|
+
|-------|------|-------------|
|
|
158
|
+
| `build.hash` | string | Unique build/content hash — the platform waits for this to appear before registering, and uses it as the dev release version |
|
|
159
|
+
| `build.at` | string | ISO timestamp of the build (optional) |
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
"build": { "hash": "a1b2c3d4", "at": "2026-09-20T10:00:00Z" }
|
|
163
|
+
```
|
|
164
|
+
|
|
150
165
|
#### `admin`
|
|
151
166
|
|
|
152
167
|
A relative path (from the app's public root) to the `app.admin.json` file. Omit entirely if the app has no admin UI.
|
|
@@ -312,6 +327,24 @@ See the **[Executor Model guide](executor.md)** for the full build setup, SEO co
|
|
|
312
327
|
| `llmContent.function` | string | Name of the `getLLMContent` export |
|
|
313
328
|
| `llmContent.timeout` | number | Timeout in ms (default 500) |
|
|
314
329
|
|
|
330
|
+
#### `functions`
|
|
331
|
+
|
|
332
|
+
Declares app-authored **server functions** — server-side handlers `(ctx, event) ⇒ result`
|
|
333
|
+
(http / event / cron), shipped in a self-contained UMD bundle. Omit if the app has no server
|
|
334
|
+
functions. The security model (`visibility`/`authority`/`capabilities`), the runtime surface, how
|
|
335
|
+
to build the bundle, and how to invoke are all in the **[Server Functions guide](server-functions.md)** —
|
|
336
|
+
this is just the manifest shape.
|
|
337
|
+
|
|
338
|
+
| Field | Type | Description |
|
|
339
|
+
|-------|------|-------------|
|
|
340
|
+
| `files.js.umd` | string | UMD bundle path (e.g. `dist/functions.umd.js`) — a self-contained bundle exporting each handler by name |
|
|
341
|
+
| `definitions[]` | object[] | One entry per function |
|
|
342
|
+
| `definitions[].name` | string | Function name; also the handler export name (override with `handler`) |
|
|
343
|
+
| `definitions[].trigger` | object | `{ type: "http"|"event"|"cron", … }` |
|
|
344
|
+
| `definitions[].visibility` | string | `admin` \| `public` (http only) — who may call it |
|
|
345
|
+
| `definitions[].authority` | string | `caller` \| `collection` — whose authority it runs as |
|
|
346
|
+
| `definitions[].capabilities` | string[] | Least-privilege grants (`sl:<res>:<read\|write>`, `network[:<host>]`, `secrets:<ref>`) |
|
|
347
|
+
|
|
315
348
|
---
|
|
316
349
|
|
|
317
350
|
## `app.admin.json`
|
package/docs/deploying-apps.md
CHANGED
|
@@ -16,6 +16,66 @@ This guide covers registration and how to wire it into your build.
|
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
+
## Fast dev publish
|
|
20
|
+
|
|
21
|
+
Getting a **dev** build live should be instant and secret-free. There are two paths depending on
|
|
22
|
+
**where you build** — pick the one that matches your setup. Both end the same way: the dev release
|
|
23
|
+
is registered and served **`no-store`** (every reload is instantly fresh — no cache-busting, no CDN
|
|
24
|
+
invalidation).
|
|
25
|
+
|
|
26
|
+
### A. From Lovable — hit Publish, no key anywhere *(recommended for Lovable apps)*
|
|
27
|
+
|
|
28
|
+
Lovable builds and hosts your app on **Publish**. You don't push to us and you **don't put any
|
|
29
|
+
secret in Lovable or your repo** — instead your build *pings* us and we pull your just-published
|
|
30
|
+
bundle. Two small additions to your build, both non-secret:
|
|
31
|
+
|
|
32
|
+
1. **Stamp the build hash into `app.manifest.json`.** In the same build step that content-hashes
|
|
33
|
+
your JS, write the hash into the manifest so we can tell when your new version is live:
|
|
34
|
+
```jsonc
|
|
35
|
+
// app.manifest.json
|
|
36
|
+
"build": { "hash": "a1b2c3d4", "at": "2026-09-20T10:00:00Z" }
|
|
37
|
+
```
|
|
38
|
+
2. **Ping us on publish** (a `postbuild` step — `SMARTLINKS_APP_ID` is an *identifier, not a
|
|
39
|
+
secret*, so it's fine in the repo):
|
|
40
|
+
```jsonc
|
|
41
|
+
// package.json
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "vite build && … && node scripts/hash-bundles.mjs", // your existing hashing step
|
|
44
|
+
"postbuild": "curl -fsS -X POST \"$SMARTLINKS_API/api/v1/apps/$SMARTLINKS_APP_ID/refresh-dev\" -H 'content-type: application/json' -d \"{\\\"hash\\\":\\\"$BUILD_HASH\\\"}\""
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
That's it — no deploy key. When the ping arrives, SmartLinks looks up your app's published URL
|
|
49
|
+
(from the catalog — **not** from the ping, so it's safe), **waits until your manifest reports that
|
|
50
|
+
exact `build.hash`** (so it never grabs a half-propagated build), validates the manifest + functions,
|
|
51
|
+
and registers the dev release (the hash becomes the version). `POST /apps/{appId}/refresh-dev`
|
|
52
|
+
authorises nothing on its own — worst case it re-fetches your app's own public bundle — so it needs
|
|
53
|
+
no secret; it's rate-limited and de-duped per app.
|
|
54
|
+
|
|
55
|
+
**One-time setup:** the app must exist in the catalog with its **id** and its **Lovable URL**
|
|
56
|
+
recorded (that's how we know what to fetch, and the `id` you ping with must match). Ask the platform
|
|
57
|
+
owner to register the app once; after that, every Publish auto-updates dev.
|
|
58
|
+
|
|
59
|
+
### B. From local / CI / Claude — `smartlinks-publish`
|
|
60
|
+
|
|
61
|
+
If you build somewhere you control (so you can hold a dev key locally — never committed), push the
|
|
62
|
+
built bundle straight to SmartLinks, which hosts + registers it:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# build dist/ first, then:
|
|
66
|
+
smartlinks-publish # uploads dist/ → SmartLinks hosts + registers the dev release
|
|
67
|
+
smartlinks-publish --watch # re-publish on every change (save → live)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Env: `SMARTLINKS_APP_ID`, `SMARTLINKS_DEPLOY_KEY` (dev key — dev-only, safe to keep locally),
|
|
71
|
+
optional `SMARTLINKS_API` / `--dir`. It POSTs the files to `POST /apps/{appId}/publish?channel=dev`;
|
|
72
|
+
the server validates and writes them to `smartlinks.app/apps/{appId}/dev/…` (`no-store`).
|
|
73
|
+
|
|
74
|
+
**Which to use:** building in Lovable → **A** (ping, no key). Building anywhere you control →
|
|
75
|
+
**B** (push with a local key). For beta/stable, see the register-a-URL flow below.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
19
79
|
## Two "wheres": the API host vs the bundle CDN
|
|
20
80
|
|
|
21
81
|
Deploying touches two different locations — keep them distinct:
|
package/docs/overview.md
CHANGED
|
@@ -63,6 +63,8 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
|
|
|
63
63
|
| **Containers** | `docs/containers.md` | Building full-app embeddable containers (lazy-loaded) |
|
|
64
64
|
| **Mobile Admin Container** | `docs/mobile-admin-container.md` | Building a separate Capacitor-aware mobile admin bundle for field operators |
|
|
65
65
|
| **Executors** | `docs/executor.md` | Building executor bundles for SEO, LLM content, programmatic config |
|
|
66
|
+
| **Server Functions** | `docs/server-functions.md` | App-authored server-side functions `(ctx, event) ⇒ result`: security model, runtime surface, invoking |
|
|
67
|
+
| **Deploying & Registering** | `docs/deploying-apps.md` | Getting your app into the platform: fast dev publish, channels, deploy keys, registering releases |
|
|
66
68
|
| **Deep Linking** | `docs/deep-link-discovery.md` | URL state management, navigable states, portal menus, AI nav |
|
|
67
69
|
| **Portal Back Button** | `docs/portal-back-button.md` | Hierarchy-aware "up" navigation inside embedded apps |
|
|
68
70
|
| **Portal Request Action** | `docs/portal-request-action.md` | Triggering portal built-in actions (__qrScanner, __share, __logout, etc.) from sub-apps |
|
package/docs/server-functions.md
CHANGED
|
@@ -63,6 +63,47 @@ in a bundle alongside your widgets/containers:
|
|
|
63
63
|
|
|
64
64
|
Each definition maps to an exported handler of the same `name` (override with `handler`).
|
|
65
65
|
|
|
66
|
+
## Building the functions bundle
|
|
67
|
+
|
|
68
|
+
The handlers ship as a **self-contained UMD** — one file, all dependencies compiled in, no
|
|
69
|
+
`require`/`import` of platform or Node modules (see [Runtime](#runtime--what-your-function-can-use)).
|
|
70
|
+
Your functions entry just **named-exports** each handler:
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
// src/functions/index.js — the functions entry
|
|
74
|
+
export async function submitCompetitionEntry(ctx, event) { /* … */ }
|
|
75
|
+
export async function onProofCreated(ctx, event) { /* … */ }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Build it to `dist/functions.umd.js` with a Vite lib build — **UMD format, nothing externalised**
|
|
79
|
+
(so it's self-contained), and don't wipe the widgets you built into the same `dist/`:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// vite.functions.config.ts
|
|
83
|
+
import { defineConfig } from 'vite'
|
|
84
|
+
export default defineConfig({
|
|
85
|
+
build: {
|
|
86
|
+
lib: { entry: 'src/functions/index.js', formats: ['umd'], name: 'functions',
|
|
87
|
+
fileName: () => 'functions.umd.js' },
|
|
88
|
+
outDir: 'dist',
|
|
89
|
+
emptyOutDir: false, // co-exist with the widgets/containers build
|
|
90
|
+
rollupOptions: { external: [] } // bundle everything — no Node/platform externals
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```jsonc
|
|
96
|
+
// package.json — build widgets/containers as usual, then the functions bundle
|
|
97
|
+
"scripts": {
|
|
98
|
+
"build": "vite build && vite build --config vite.functions.config.ts"
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
That emits `dist/functions.umd.js` (matching `functions.files.js.umd`), whose named exports the
|
|
103
|
+
platform pairs with your manifest `definitions`. Then **deploy** it — for dev,
|
|
104
|
+
`smartlinks-publish` uploads `dist/` and registers in one step (see
|
|
105
|
+
[Deploying & registering](deploying-apps.md)).
|
|
106
|
+
|
|
66
107
|
---
|
|
67
108
|
|
|
68
109
|
## The two security questions every function answers
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proveanything/smartlinks",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -15,12 +15,14 @@
|
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"bin": {
|
|
18
|
-
"smartlinks-register-release": "scripts/register-release.mjs"
|
|
18
|
+
"smartlinks-register-release": "scripts/register-release.mjs",
|
|
19
|
+
"smartlinks-publish": "scripts/publish.mjs"
|
|
19
20
|
},
|
|
20
21
|
"files": [
|
|
21
22
|
"dist/",
|
|
22
23
|
"docs/",
|
|
23
24
|
"scripts/register-release.mjs",
|
|
25
|
+
"scripts/publish.mjs",
|
|
24
26
|
"openapi.yaml",
|
|
25
27
|
"README.md"
|
|
26
28
|
],
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* publish — the FAST dev publish for a SmartLinks app. Builds nothing itself: it takes an already-
|
|
4
|
+
* built `dist/`, uploads it to the SmartLinks publish endpoint, which writes it straight to the CDN
|
|
5
|
+
* (dev = no-store, instantly fresh) and registers the release. One call = host + register, no Cloud
|
|
6
|
+
* Build, no Lovable. Works from anywhere: local, Claude, CI, or a build step.
|
|
7
|
+
*
|
|
8
|
+
* smartlinks-publish # build dist/ yourself first, then this
|
|
9
|
+
* smartlinks-publish --watch # re-publish on file change (save → live)
|
|
10
|
+
*
|
|
11
|
+
* Env:
|
|
12
|
+
* SMARTLINKS_APP_ID the platform app id (required)
|
|
13
|
+
* SMARTLINKS_DEPLOY_KEY channel-scoped deploy key (dev key is dev-only, safe to keep locally)
|
|
14
|
+
* SMARTLINKS_API API host (default https://smartlinks.app)
|
|
15
|
+
* SMARTLINKS_CHANNEL channel (default dev; only dev is supported by this fast path today)
|
|
16
|
+
* Flags: --dir <dist> --watch --channel <dev>
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { readFileSync, readdirSync, statSync, watch } from 'node:fs'
|
|
20
|
+
import { join, relative, sep } from 'node:path'
|
|
21
|
+
|
|
22
|
+
const args = process.argv.slice(2)
|
|
23
|
+
const flag = (name, def) => { const i = args.indexOf(name); return i >= 0 ? (args[i + 1] && !args[i + 1].startsWith('--') ? args[i + 1] : true) : def }
|
|
24
|
+
|
|
25
|
+
const API = process.env.SMARTLINKS_API || 'https://smartlinks.app'
|
|
26
|
+
const KEY = process.env.SMARTLINKS_DEPLOY_KEY
|
|
27
|
+
const APP_ID = process.env.SMARTLINKS_APP_ID
|
|
28
|
+
const CHANNEL = flag('--channel', process.env.SMARTLINKS_CHANNEL || 'dev')
|
|
29
|
+
const DIR = flag('--dir', 'dist')
|
|
30
|
+
const WATCH = args.includes('--watch')
|
|
31
|
+
|
|
32
|
+
// Mirror of the server's write allowlist — a bundle is static web assets only.
|
|
33
|
+
const ALLOWED = new Set(['.js', '.mjs', '.css', '.json', '.map', '.wasm', '.html', '.txt',
|
|
34
|
+
'.svg', '.png', '.jpg', '.jpeg', '.gif', '.webp', '.avif', '.ico', '.woff', '.woff2', '.ttf', '.otf'])
|
|
35
|
+
const extOf = (p) => { const i = p.lastIndexOf('.'); return i < 0 ? '' : p.slice(i).toLowerCase() }
|
|
36
|
+
|
|
37
|
+
function fail(msg) { console.error(`❌ ${msg}`); process.exit(1) }
|
|
38
|
+
if (!APP_ID) fail('SMARTLINKS_APP_ID is required')
|
|
39
|
+
if (!KEY) fail('SMARTLINKS_DEPLOY_KEY is required')
|
|
40
|
+
|
|
41
|
+
function collectFiles(dir) {
|
|
42
|
+
const out = {}
|
|
43
|
+
const walk = (d) => {
|
|
44
|
+
for (const e of readdirSync(d, { withFileTypes: true })) {
|
|
45
|
+
const full = join(d, e.name)
|
|
46
|
+
if (e.isDirectory()) { walk(full); continue }
|
|
47
|
+
if (!ALLOWED.has(extOf(e.name))) continue
|
|
48
|
+
const rel = relative(dir, full).split(sep).join('/') // forward slashes for the bucket
|
|
49
|
+
out[rel] = readFileSync(full).toString('base64')
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
walk(dir)
|
|
53
|
+
return out
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function publishOnce() {
|
|
57
|
+
let stat
|
|
58
|
+
try { stat = statSync(DIR) } catch { fail(`build dir not found: ${DIR} (build first)`) }
|
|
59
|
+
if (!stat.isDirectory()) fail(`${DIR} is not a directory`)
|
|
60
|
+
|
|
61
|
+
const files = collectFiles(DIR)
|
|
62
|
+
if (!files['app.manifest.json']) fail(`${DIR}/app.manifest.json not found — is this a built app bundle?`)
|
|
63
|
+
|
|
64
|
+
const t0 = Date.now()
|
|
65
|
+
const res = await fetch(`${API}/api/v1/apps/${APP_ID}/publish?channel=${encodeURIComponent(CHANNEL)}`, {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: { 'content-type': 'application/json', 'x-smartlinks-deploy-key': KEY },
|
|
68
|
+
body: JSON.stringify({ files }),
|
|
69
|
+
}).catch((e) => fail(`request failed — ${e.message}`))
|
|
70
|
+
|
|
71
|
+
const b = await res.json().catch(() => ({}))
|
|
72
|
+
if (!res.ok || !b.ok) {
|
|
73
|
+
console.error(`❌ publish failed (${res.status}):`)
|
|
74
|
+
for (const e of b.errors || []) console.error(` • ${e.path || ''}: ${e.message}`)
|
|
75
|
+
if (WATCH) return false // keep watching; don't exit
|
|
76
|
+
process.exit(1)
|
|
77
|
+
}
|
|
78
|
+
for (const w of b.warnings || []) console.warn(` ⚠︎ ${w.path}: ${w.message}`)
|
|
79
|
+
console.log(`✅ ${APP_ID}@${b.version} → ${CHANNEL} (${Object.keys(files).length} files, ${Date.now() - t0}ms) — functions: ${(b.functions || []).join(', ') || 'none'}`)
|
|
80
|
+
console.log(` ${b.bundleBaseUrl}`)
|
|
81
|
+
return true
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!WATCH) {
|
|
85
|
+
await publishOnce()
|
|
86
|
+
} else {
|
|
87
|
+
console.log(`👀 watching ${DIR} — publishing ${APP_ID} to ${CHANNEL} on change (Ctrl+C to stop)`)
|
|
88
|
+
await publishOnce()
|
|
89
|
+
let timer = null
|
|
90
|
+
let publishing = false
|
|
91
|
+
watch(DIR, { recursive: true }, () => {
|
|
92
|
+
clearTimeout(timer)
|
|
93
|
+
timer = setTimeout(async () => {
|
|
94
|
+
if (publishing) return
|
|
95
|
+
publishing = true
|
|
96
|
+
try { await publishOnce() } finally { publishing = false }
|
|
97
|
+
}, 300) // debounce a burst of file writes from one build
|
|
98
|
+
})
|
|
99
|
+
}
|