@softr/sdk 0.0.42 → 0.0.44
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/boot-error.js +42 -2
- package/dist/package.js +1 -1
- package/package.json +1 -1
- package/skills/softr-sdk/SKILL.md +1 -1
- package/skills/softr-sdk/auth/SKILL.md +1 -1
- package/skills/softr-sdk/charts-and-metrics/SKILL.md +1 -1
- package/skills/softr-sdk/current-user/SKILL.md +1 -1
- package/skills/softr-sdk/file-uploads/SKILL.md +7 -6
- package/skills/softr-sdk/integrations/SKILL.md +5 -1
- package/skills/softr-sdk/record-mutations/SKILL.md +1 -1
- package/skills/softr-sdk/record-queries/SKILL.md +1 -1
- package/skills/softr-sdk/routes-and-visibility/SKILL.md +4 -4
- package/skills/softr-sdk/workflows/SKILL.md +5 -3
package/dist/boot-error.js
CHANGED
|
@@ -23,6 +23,17 @@ function bootErrorMiddleware() {
|
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Faults of the dev server rather than the app: a module graph swapped under a render already in
|
|
28
|
+
* flight, so React's dispatcher or a split chunk belongs to a generation that is gone. The next
|
|
29
|
+
* request reads the settled graph and succeeds, which is why these clear on their own — and why
|
|
30
|
+
* showing one is worse than useless. The page invites the reader to copy it to the agent, and an
|
|
31
|
+
* agent handed a phantom spends a repair round looking for it in code that is correct.
|
|
32
|
+
*/
|
|
33
|
+
const TRANSIENT = [/Cannot read properties of (?:null|undefined) \(reading 'use[A-Z]/, /Failed to fetch dynamically imported module/];
|
|
34
|
+
function isTransientBootError(message) {
|
|
35
|
+
return TRANSIENT.some((pattern) => pattern.test(message));
|
|
36
|
+
}
|
|
26
37
|
function errorPage(error) {
|
|
27
38
|
if (process.env.NODE_ENV === "production") return page("Something went wrong", "This page couldn’t be shown. Try again in a moment, and if it keeps happening, reach out to the owner of this app.", { note: "If you are the owner, you can check what happened in Studio." });
|
|
28
39
|
const cause = error instanceof Error ? error.cause : void 0;
|
|
@@ -31,6 +42,7 @@ function errorPage(error) {
|
|
|
31
42
|
const stack = root instanceof Error ? root.stack : void 0;
|
|
32
43
|
return page("This app crashed", "The app stopped before it could start. The technical details below show where it failed and can be copied to the agent to help fix the issue.", {
|
|
33
44
|
note: "These details are only shown because the app is running in development mode.",
|
|
45
|
+
autoRetry: isTransientBootError(message),
|
|
34
46
|
detail: `<pre class="message">${escapeHtml(message)}</pre>`,
|
|
35
47
|
actions: stack ? `<button type="button" id="toggle" aria-expanded="true" aria-controls="panel">
|
|
36
48
|
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
|
|
@@ -71,7 +83,7 @@ function errorPage(error) {
|
|
|
71
83
|
<\/script>` : ""
|
|
72
84
|
});
|
|
73
85
|
}
|
|
74
|
-
function page(title, description, { note = "", detail = "", actions = "", panel = "" } = {}) {
|
|
86
|
+
function page(title, description, { note = "", detail = "", actions = "", panel = "", autoRetry = false } = {}) {
|
|
75
87
|
return `<!doctype html>
|
|
76
88
|
<html lang="en">
|
|
77
89
|
<head>
|
|
@@ -222,7 +234,7 @@ function page(title, description, { note = "", detail = "", actions = "", panel
|
|
|
222
234
|
</style>
|
|
223
235
|
</head>
|
|
224
236
|
<body>
|
|
225
|
-
<main>
|
|
237
|
+
<main${autoRetry ? " hidden" : ""}>
|
|
226
238
|
<div class="alert">
|
|
227
239
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>
|
|
228
240
|
</div>
|
|
@@ -241,9 +253,37 @@ function page(title, description, { note = "", detail = "", actions = "", panel
|
|
|
241
253
|
</div>
|
|
242
254
|
${panel}
|
|
243
255
|
</main>
|
|
256
|
+
${autoRetry ? RETRY_SCRIPT : ""}
|
|
244
257
|
</body>
|
|
245
258
|
</html>`;
|
|
246
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* One reload, then the page reveals itself. The reload is what the reader would have done by hand,
|
|
262
|
+
* so a fault that clears is never seen; a fault that survives it shows the same page it always did.
|
|
263
|
+
* Session storage is what remembers the attempt, and a preview frame may refuse it — then there is
|
|
264
|
+
* no way to count, and the honest move is to show the error rather than risk reloading forever.
|
|
265
|
+
*/
|
|
266
|
+
const RETRY_SCRIPT = `<noscript><style>main[hidden] { display: flex !important }</style></noscript>
|
|
267
|
+
<script>
|
|
268
|
+
(() => {
|
|
269
|
+
const KEY = 'softr:boot-retry'
|
|
270
|
+
const reveal = () => { document.querySelector('main').hidden = false }
|
|
271
|
+
|
|
272
|
+
let store = null
|
|
273
|
+
try { store = sessionStorage } catch { /* blocked in this frame */ }
|
|
274
|
+
if (!store) return reveal()
|
|
275
|
+
|
|
276
|
+
const now = Date.now()
|
|
277
|
+
if (now - Number(store.getItem(KEY) ?? 0) < 15000) {
|
|
278
|
+
store.removeItem(KEY)
|
|
279
|
+
return reveal()
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
store.setItem(KEY, String(now))
|
|
283
|
+
// A beat, so the reload lands after the dev server has finished rebuilding rather than during.
|
|
284
|
+
setTimeout(() => location.reload(), 500)
|
|
285
|
+
})()
|
|
286
|
+
<\/script>`;
|
|
247
287
|
function escapeHtml(value) {
|
|
248
288
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
249
289
|
}
|
package/dist/package.js
CHANGED
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@ description: >-
|
|
|
16
16
|
metadata:
|
|
17
17
|
type: sub-skill
|
|
18
18
|
library: softr-sdk
|
|
19
|
-
library_version: 0.0.
|
|
19
|
+
library_version: 0.0.43
|
|
20
20
|
---
|
|
21
21
|
|
|
22
22
|
**Boundary.** `auth` lives on `@softr/sdk/server` and runs only inside a `createServerFn` handler — importing it into a component fails the build. `defineVisibility`, the hooks and every type come from `@softr/sdk`. The app is already wired (`src/start.ts`, `src/router.tsx`, `src/routes/__root.tsx`); never add a second registration. Load the core `softr-sdk` skill only if you are unsure this is the right sub-skill.
|
|
@@ -12,7 +12,7 @@ description: >-
|
|
|
12
12
|
metadata:
|
|
13
13
|
type: sub-skill
|
|
14
14
|
library: softr-sdk
|
|
15
|
-
library_version: 0.0.
|
|
15
|
+
library_version: 0.0.43
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
**Boundary.** `ds` lives on `@softr/sdk/server` and runs only inside a `createServerFn` handler or a server-side route hook — importing it into a component fails the build. `defineQuery` and every type come from `@softr/sdk` and can be imported anywhere. The app is already wired (`src/start.ts`, `src/router.tsx`, `src/routes/__root.tsx`); never add a second registration. Load the core `softr-sdk` skill only if you are unsure this is the right sub-skill.
|
|
@@ -10,7 +10,7 @@ description: >-
|
|
|
10
10
|
metadata:
|
|
11
11
|
type: sub-skill
|
|
12
12
|
library: softr-sdk
|
|
13
|
-
library_version: 0.0.
|
|
13
|
+
library_version: 0.0.43
|
|
14
14
|
---
|
|
15
15
|
|
|
16
16
|
**Boundary.** `useCurrentUser`, `usePermissions` and every type come from `@softr/sdk` and work anywhere; `ds` and `auth` live on `@softr/sdk/server` and run only inside a `createServerFn` handler. The app is already wired — `src/start.ts` registers `softrAuthMiddleware`, `src/routes/__root.tsx` publishes the visitor as route context — never add a second registration. Load the core `softr-sdk` skill only if you are unsure this is the right sub-skill.
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: file-uploads
|
|
3
3
|
description: >-
|
|
4
|
-
Upload files from a form or edit view: useFileUpload(action) from @softr/sdk
|
|
5
|
-
everything — XHR upload with real progress, per-upload cancel, and
|
|
6
|
-
form submission. No route registration needed; the SDK
|
|
7
|
-
For server-side uploads or plain forms, use
|
|
8
|
-
For edit forms pass
|
|
4
|
+
Upload files from a form or edit view: useFileUpload(action) from @softr/sdk
|
|
5
|
+
does everything — XHR upload with real progress, per-upload cancel, and
|
|
6
|
+
isUploading to block form submission. No route registration needed; the SDK
|
|
7
|
+
ships its own server function. For server-side uploads or plain forms, use
|
|
8
|
+
upload(actionId, file) from @softr/sdk/server. For edit forms pass
|
|
9
|
+
initialAttachments to pre-load existing attachments.
|
|
9
10
|
metadata:
|
|
10
11
|
type: sub-skill
|
|
11
12
|
library: softr-sdk
|
|
12
|
-
library_version: 0.0.
|
|
13
|
+
library_version: 0.0.43
|
|
13
14
|
---
|
|
14
15
|
|
|
15
16
|
**Boundary.** `upload` lives on `@softr/sdk/server` and runs inside server functions. `useFileUpload` and `FileItem` live on `@softr/sdk` and run in components. `FileAttachment` comes from `@softr/sdk` and can be imported anywhere.
|
|
@@ -9,7 +9,7 @@ description: >-
|
|
|
9
9
|
metadata:
|
|
10
10
|
type: sub-skill
|
|
11
11
|
library: softr-sdk
|
|
12
|
-
library_version: 0.0.
|
|
12
|
+
library_version: 0.0.43
|
|
13
13
|
---
|
|
14
14
|
|
|
15
15
|
**Boundary.** `integration` lives on `@softr/sdk/server` and runs only inside a `createServerFn` handler — importing it into a component fails the build. `defineIntegration` and every type come from `@softr/sdk` and can be imported anywhere. Load the core `softr-sdk` skill only if you are unsure this is the right sub-skill.
|
|
@@ -148,6 +148,10 @@ Match the `integrationType` from the attached integrations section to find the b
|
|
|
148
148
|
| XERO | `https://api.xero.com` |
|
|
149
149
|
| ZOOM | `https://api.zoom.us` |
|
|
150
150
|
|
|
151
|
+
## When the type is in the table but nothing is attached
|
|
152
|
+
|
|
153
|
+
The service is not out of reach, it is not connected yet. Load `connect-a-service` and ask the user for it: the answer carries the `integrationId` this skill needs.
|
|
154
|
+
|
|
151
155
|
## When the service is not a Softr integration
|
|
152
156
|
|
|
153
157
|
If the API the user needs is not in the table above, call it directly from a server function using the native `fetch`. The user supplies their own credentials and the call lives entirely in the server function handler.
|
|
@@ -11,7 +11,7 @@ description: >-
|
|
|
11
11
|
metadata:
|
|
12
12
|
type: sub-skill
|
|
13
13
|
library: softr-sdk
|
|
14
|
-
library_version: 0.0.
|
|
14
|
+
library_version: 0.0.43
|
|
15
15
|
---
|
|
16
16
|
|
|
17
17
|
**Boundary.** `ds` lives on `@softr/sdk/server` and runs only inside a `createServerFn` handler — importing it into a component fails the build. `defineMutation` and every type come from `@softr/sdk` and can be imported anywhere. The app is already wired (`src/start.ts`, `src/router.tsx`, `src/routes/__root.tsx`); never add a second registration. Load the core `softr-sdk` skill only if you are unsure this is the right sub-skill.
|
|
@@ -13,7 +13,7 @@ description: >-
|
|
|
13
13
|
metadata:
|
|
14
14
|
type: sub-skill
|
|
15
15
|
library: softr-sdk
|
|
16
|
-
library_version: 0.0.
|
|
16
|
+
library_version: 0.0.43
|
|
17
17
|
---
|
|
18
18
|
|
|
19
19
|
**Boundary.** `ds` lives on `@softr/sdk/server` and runs only inside a `createServerFn` handler or a server-side route hook — importing it into a component fails the build. `defineQuery` and every type come from `@softr/sdk` and can be imported anywhere. The app is already wired (`src/start.ts`, `src/router.tsx`, `src/routes/__root.tsx`); never add a second registration. Load the core `softr-sdk` skill only if you are unsure this is the right sub-skill.
|
|
@@ -6,13 +6,13 @@ description: >-
|
|
|
6
6
|
four ways to enforce a rule (guard, assert, isAuthorized, check). Covers
|
|
7
7
|
per-reason overrides, the redirect and throw strategies, rendering an access
|
|
8
8
|
error with isAccessError, isUnauthenticatedError, isForbiddenError and
|
|
9
|
-
isAlreadyAuthenticatedError, guarding a layout, and the lint rule that
|
|
10
|
-
declaration alongside the rules that reject a duplicate, computed
|
|
11
|
-
root-level one.
|
|
9
|
+
isAlreadyAuthenticatedError, guarding a layout, and the lint rule that
|
|
10
|
+
requires a declaration alongside the rules that reject a duplicate, computed
|
|
11
|
+
or root-level one.
|
|
12
12
|
metadata:
|
|
13
13
|
type: sub-skill
|
|
14
14
|
library: softr-sdk
|
|
15
|
-
library_version: 0.0.
|
|
15
|
+
library_version: 0.0.43
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
**Boundary.** `defineVisibility`, the error predicates and every type come from `@softr/sdk` and work anywhere; `ds` and `auth` live on `@softr/sdk/server` and run only inside a `createServerFn` handler. The app is already wired — `src/start.ts` registers `softrAuthMiddleware`, `src/routes/__root.tsx` publishes the visitor as route context — never add a second registration. Load the core `softr-sdk` skill only if you are unsure this is the right sub-skill.
|
|
@@ -9,7 +9,7 @@ description: >-
|
|
|
9
9
|
metadata:
|
|
10
10
|
type: sub-skill
|
|
11
11
|
library: softr-sdk
|
|
12
|
-
library_version: 0.0.
|
|
12
|
+
library_version: 0.0.43
|
|
13
13
|
---
|
|
14
14
|
|
|
15
15
|
**Boundary.** `workflow` lives on `@softr/sdk/server` and runs only inside a `createServerFn` handler — importing it into a component fails the build. `defineWorkflow` and every type come from `@softr/sdk` and can be imported anywhere. Load the core `softr-sdk` skill only if you are unsure this is the right sub-skill.
|
|
@@ -28,7 +28,7 @@ import { workflow } from '@softr/sdk/server'
|
|
|
28
28
|
|
|
29
29
|
## Declare the workflow action
|
|
30
30
|
|
|
31
|
-
Every trigger needs a workflow action: which workflow, which trigger step, who may run it, and any data that is always sent.
|
|
31
|
+
Every trigger needs a workflow action: which workflow, which trigger step, who may run it, and any data that is always sent. The workflow must have a **Run custom workflow** trigger — no other trigger type can be fired from app code.
|
|
32
32
|
|
|
33
33
|
```ts
|
|
34
34
|
const sendWelcomeEmail = defineWorkflow({
|
|
@@ -45,7 +45,7 @@ const sendWelcomeEmail = defineWorkflow({
|
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
- `id` is a name you choose, unique across the app.
|
|
48
|
-
- `workflowId` and `triggerId` are opaque UUIDs
|
|
48
|
+
- `workflowId` and `triggerId` are opaque UUIDs identifying the workflow and its **Run custom workflow** trigger. `list_workflows` returns both. Pass them as inline string literals — never derive them at runtime.
|
|
49
49
|
- `permissions` declares who may run the trigger. Uses the same format as queries and mutations.
|
|
50
50
|
- `body` is hardcoded data the server resolves before calling the workflow. It is **not** passed by the caller at runtime — the workflow always receives it. Template values draw from the signed-in user and the current time (see table below).
|
|
51
51
|
|
|
@@ -107,6 +107,8 @@ const submit = useServerFn(run)
|
|
|
107
107
|
|
|
108
108
|
**Computing `workflowId` or `triggerId` at runtime.** Both are static UUIDs from the workflow definition. Derive them from user input and the call fails at request time.
|
|
109
109
|
|
|
110
|
+
**Pointing at a workflow with no Run custom workflow trigger.** A workflow that runs on a schedule, a table event or an integration event has no trigger the app can call, and `workflow.trigger` fails at request time. Confirm the workflow has one before writing the `defineWorkflow`. If it does not, say which trigger it has instead — never that the workflow is missing — and name the ones that do have a Run custom workflow trigger.
|
|
111
|
+
|
|
110
112
|
**Passing nested objects in the runtime payload.** The second argument to `workflow.trigger` only accepts flat values — `string`, `number`, `boolean`, or `null`. Put anything that needs server-side resolution in the action's `body` instead.
|
|
111
113
|
|
|
112
114
|
**Not awaiting the result.** `workflow.trigger` is async. Fire-and-forget loses the result and swallows any error.
|