@softr/sdk 0.0.68-props.0 → 0.0.68

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.
Files changed (54) hide show
  1. package/dist/auth/current-user-row.d.ts +20 -0
  2. package/dist/auth/current-user-row.js +22 -0
  3. package/dist/auth/platform.d.ts +1 -0
  4. package/dist/auth/platform.js +13 -33
  5. package/dist/auth/server.d.ts +2 -0
  6. package/dist/auth/server.js +3 -1
  7. package/dist/auth/session.js +1 -1
  8. package/dist/auth/user.d.ts +1 -8
  9. package/dist/auth/user.js +1 -2
  10. package/dist/cli.js +18 -26
  11. package/dist/codegen/datasource-docs.js +1 -93
  12. package/dist/codegen/datasource-types.js +1 -16
  13. package/dist/codegen/fetch-metadata.js +17 -44
  14. package/dist/codegen/softr-env.js +1 -2
  15. package/dist/codegen/softr-fields.js +41 -0
  16. package/dist/cookies.js +2 -10
  17. package/dist/core/options.d.ts +36 -0
  18. package/dist/core/options.js +39 -0
  19. package/dist/core/use-overrides.d.ts +17 -0
  20. package/dist/core/use-overrides.js +30 -0
  21. package/dist/datasource/datasource.d.ts +2 -0
  22. package/dist/datasource/datasource.js +3 -1
  23. package/dist/datasource/fields.js +3 -2
  24. package/dist/datasource/record.d.ts +2 -1
  25. package/dist/fields/format.d.ts +45 -0
  26. package/dist/fields/format.js +140 -0
  27. package/dist/formatting.d.ts +4 -4
  28. package/dist/formatting.js +1 -1
  29. package/dist/index.d.ts +4 -2
  30. package/dist/index.js +3 -1
  31. package/dist/mcp.js +2 -2
  32. package/dist/package.js +1 -1
  33. package/dist/pwa.d.ts +2 -2
  34. package/dist/pwa.js +1 -1
  35. package/dist/server.d.ts +2 -1
  36. package/dist/softr-config.schema.js +24 -45
  37. package/dist/visibility/route.js +1 -1
  38. package/dist/vite.js +19 -1
  39. package/package.json +10 -33
  40. package/schema/softr.schema.json +0 -13
  41. package/skills/softr-sdk/SKILL.md +2 -2
  42. package/skills/softr-sdk/auth/SKILL.md +1 -1
  43. package/skills/softr-sdk/charts-and-metrics/SKILL.md +1 -1
  44. package/skills/softr-sdk/current-user/SKILL.md +3 -24
  45. package/skills/softr-sdk/file-uploads/SKILL.md +1 -1
  46. package/skills/softr-sdk/formatting/SKILL.md +19 -1
  47. package/skills/softr-sdk/integrations/SKILL.md +1 -1
  48. package/skills/softr-sdk/mcp/SKILL.md +1 -1
  49. package/skills/softr-sdk/pwa/SKILL.md +1 -1
  50. package/skills/softr-sdk/record-mutations/SKILL.md +13 -1
  51. package/skills/softr-sdk/record-queries/SKILL.md +21 -1
  52. package/skills/softr-sdk/region-visibility/SKILL.md +1 -1
  53. package/skills/softr-sdk/routes-and-visibility/SKILL.md +1 -1
  54. package/skills/softr-sdk/workflows/SKILL.md +1 -1
package/package.json CHANGED
@@ -1,37 +1,16 @@
1
1
  {
2
2
  "name": "@softr/sdk",
3
- "version": "0.0.68-props.0",
3
+ "version": "0.0.68",
4
4
  "type": "module",
5
5
  "exports": {
6
- ".": {
7
- "types": "./dist/index.d.ts",
8
- "import": "./dist/index.js"
9
- },
10
- "./client": {
11
- "types": "./dist/client.d.ts",
12
- "import": "./dist/client.js"
13
- },
14
- "./server": {
15
- "types": "./dist/server.d.ts",
16
- "import": "./dist/server.js"
17
- },
18
- "./vite": {
19
- "types": "./dist/vite.d.ts",
20
- "import": "./dist/vite.js"
21
- }
22
- },
23
- "bin": {
24
- "softr-sdk": "./dist/cli.js"
25
- },
26
- "files": [
27
- "dist",
28
- "schema",
29
- "skills",
30
- "!skills/_artifacts"
31
- ],
32
- "publishConfig": {
33
- "access": "public"
34
- },
6
+ ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" },
7
+ "./client": { "types": "./dist/client.d.ts", "import": "./dist/client.js" },
8
+ "./server": { "types": "./dist/server.d.ts", "import": "./dist/server.js" },
9
+ "./vite": { "types": "./dist/vite.d.ts", "import": "./dist/vite.js" }
10
+ },
11
+ "bin": { "softr-sdk": "./dist/cli.js" },
12
+ "files": ["dist", "schema", "skills", "!skills/_artifacts"],
13
+ "publishConfig": { "access": "public" },
35
14
  "scripts": {
36
15
  "build": "tsdown",
37
16
  "test": "node --import ./src/test-utils/setup.ts --test",
@@ -63,9 +42,7 @@
63
42
  "optional": true
64
43
  }
65
44
  },
66
- "keywords": [
67
- "tanstack-intent"
68
- ],
45
+ "keywords": ["tanstack-intent"],
69
46
  "intent": {
70
47
  "version": 1,
71
48
  "repo": "@softr/sdk",
@@ -54,19 +54,6 @@
54
54
  },
55
55
  "required": ["routes", "guard"],
56
56
  "additionalProperties": false
57
- },
58
- "user": {
59
- "type": "object",
60
- "description": "The signed-in user.",
61
- "properties": {
62
- "properties": {
63
- "type": "object",
64
- "description": "Fields of the users table the app reads as user.properties, keyed by the name the app uses. Each value is a field id. No other field leaves the server.",
65
- "additionalProperties": { "type": "string" }
66
- }
67
- },
68
- "required": ["properties"],
69
- "additionalProperties": false
70
57
  }
71
58
  },
72
59
  "required": ["auth"],
@@ -12,7 +12,7 @@ description: >-
12
12
  metadata:
13
13
  type: core
14
14
  library: softr-sdk
15
- library_version: 0.0.68-props.0
15
+ library_version: 0.0.68
16
16
  ---
17
17
 
18
18
  # Softr SDK core
@@ -30,7 +30,7 @@ Each sub-skill stands alone for its task and repeats the boundary above, so most
30
30
 
31
31
  | Import | Runs | Holds |
32
32
  | --- | --- | --- |
33
- | `@softr/sdk` | Both | `defineQuery`, `defineMutation`, `defineVisibility`, `guardRoute`, `assertRouteAccess`, `isAuthorized`, `checkAccess`, `Audience`, `useCurrentUser`, `useUserTimezone`, `FormattedDate`, `FormattingScope`, `useFormatting`, `formatDate`, `formatDateTime`, `formatNumber`, `SoftrPwaHead`, `usePwaInstall`, `getSoftrContext`, `initialSoftrContext`, `softrAuthMiddleware`, the error predicates, and every type |
33
+ | `@softr/sdk` | Both | `defineQuery`, `defineMutation`, `defineVisibility`, `guardRoute`, `assertRouteAccess`, `isAuthorized`, `checkAccess`, `Audience`, `useCurrentUser`, `useUserTimezone`, `FormattedDate`, `FormattingScope`, `useFormatting`, `formatDate`, `formatDateTime`, `formatNumber`, `formatValue`, `formatField`, `fieldShape`, `registerSoftrFields`, `useOverrides`, `SoftrPwaHead`, `usePwaInstall`, `getSoftrContext`, `initialSoftrContext`, `softrAuthMiddleware`, the error predicates, and every type |
34
34
  | `@softr/sdk/server` | Server only | `ds`, `auth`, `workflow`, `integration` |
35
35
  | `@softr/sdk/client` | Browser only | Nothing yet, reserved for browser-only utilities |
36
36
  | `@softr/sdk/vite` | Build | `softr()`, already in `vite.config.ts` |
@@ -19,7 +19,7 @@ description: >-
19
19
  metadata:
20
20
  type: sub-skill
21
21
  library: softr-sdk
22
- library_version: 0.0.68-props.0
22
+ library_version: 0.0.68
23
23
  ---
24
24
 
25
25
  **Boundary.** `auth` lives on `@softr/sdk/server` and runs only inside a `createServerFn` handler — importing it into a component fails the build.
@@ -12,7 +12,7 @@ description: >-
12
12
  metadata:
13
13
  type: sub-skill
14
14
  library: softr-sdk
15
- library_version: 0.0.68-props.0
15
+ library_version: 0.0.68
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.
@@ -4,13 +4,12 @@ description: >-
4
4
  Read the signed-in Softr visitor: useCurrentUser and the User type,
5
5
  context.user in a route's beforeLoad, and context.user inside a server
6
6
  function handler, where Start types it from the app's registered request
7
- middleware. Covers the null guest, the generated SoftrUserGroups that narrows
8
- group ids to the app's own, and user.properties, the fields of the visitor's
9
- row that softr.json declares.
7
+ middleware. Covers the null guest and the generated SoftrUserGroups that
8
+ narrows group ids to the app's own.
10
9
  metadata:
11
10
  type: sub-skill
12
11
  library: softr-sdk
13
- library_version: 0.0.68-props.0
12
+ library_version: 0.0.68
14
13
  ---
15
14
 
16
15
  # The current user
@@ -32,7 +31,6 @@ type User = {
32
31
  email: string
33
32
  fullName: string
34
33
  userGroupIds: Array<SoftrUserGroupId> // the app's own group ids
35
- properties: SoftrUserProperties // the aliases softr.json declares
36
34
  }
37
35
  ```
38
36
 
@@ -92,25 +90,6 @@ For every group's name and id, read `docs/user-groups.md`. It lists every group
92
90
 
93
91
  `user.userGroupIds` and every `customGroups` the SDK accepts take only those ids, so an id the app does not have stops compiling. A new group appears in the list once it exists on the app.
94
92
 
95
- ### Fields of the visitor's row: `user.properties`
96
-
97
- To read more of the visitor's row in the users table, give each field an alias in `softr.json`, under `user.properties`. The value is the field's id:
98
-
99
- ```json
100
- {
101
- "user": {
102
- "properties": {
103
- "plan": "OeIz1",
104
- "seats": "1iHKE"
105
- }
106
- }
107
- }
108
- ```
109
-
110
- `user.properties` then holds exactly those aliases. To read them, see `docs/user-properties.md`.
111
-
112
- Attachment and long text fields, and linked record and lookup fields with multiple entries, cannot be properties. Read those with `defineQuery` on the users table. See [record-queries](../record-queries/SKILL.md).
113
-
114
93
  ## References
115
94
 
116
95
  - [routes-and-visibility/SKILL.md](../routes-and-visibility/SKILL.md): `isAuthorized` narrows `context.user` while applying the route's rule
@@ -10,7 +10,7 @@ description: >-
10
10
  metadata:
11
11
  type: sub-skill
12
12
  library: softr-sdk
13
- library_version: 0.0.68-props.0
13
+ library_version: 0.0.68
14
14
  ---
15
15
 
16
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.
@@ -10,7 +10,7 @@ description: >-
10
10
  metadata:
11
11
  type: sub-skill
12
12
  library: softr-sdk
13
- library_version: 0.0.68-props.0
13
+ library_version: 0.0.68
14
14
  ---
15
15
 
16
16
  # Dates, times and numbers
@@ -116,6 +116,24 @@ The currency comes from the field or from the user, never from the locale. A Ger
116
116
 
117
117
  `formatDate` and `formatDateTime` show a timestamp in UTC while `formatting.timeZone` is `null`. In JSX, prefer `<FormattedDate />`, which waits instead.
118
118
 
119
+ ### A field's own text: `formatValue`
120
+
121
+ A value a query read already knows how it should look: money with its symbol and cents, a percent, a rating out of its scale, a duration in hours and minutes, a date in the app's format, a choice or a linked record by its label, a checkbox as Yes or No, several values joined by a comma, `''` for nothing. `formatValue` reads the field's type and options from the generated `src/softr-fields.ts` and does all of it, so a page never branches on a type itself.
122
+
123
+ ```tsx
124
+ import { formatValue, useFormatting } from '@softr/sdk'
125
+ import { dealsQuery } from '#/server/deals.ts'
126
+
127
+ function Amount({ deal }: { deal: Deal }) {
128
+ const formatting = useFormatting()
129
+ return <span>{formatValue(dealsQuery, 'amount', deal.fields.amount, formatting)}</span>
130
+ }
131
+ ```
132
+
133
+ The second argument is the alias the query holds the field under. `formatField(shape, value, formatting)` does the same for a shape in hand, and `fieldShape(query, alias)` reads one.
134
+
135
+ The SDK's codegen writes `src/softr-fields.ts` beside `softr-env.d.ts` after every schema change; it registers the shapes as a side effect of being imported, and the root route imports it once. `formatValue` throws when that import is missing.
136
+
119
137
  ### Outside render: a server function or `beforeLoad`
120
138
 
121
139
  Not every date ends up in a component. A CSV or spreadsheet export, a text the server sends, or a value a loader prepares are formatted before React runs. There, `context.formatting` holds the same `Formatting` that `useFormatting()` returns. See [current-user](../current-user/SKILL.md) for how `context` reaches a handler.
@@ -10,7 +10,7 @@ description: >-
10
10
  metadata:
11
11
  type: sub-skill
12
12
  library: softr-sdk
13
- library_version: 0.0.68-props.0
13
+ library_version: 0.0.68
14
14
  ---
15
15
 
16
16
  **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.
@@ -9,7 +9,7 @@ description: >-
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.68-props.0
12
+ library_version: 0.0.68
13
13
  ---
14
14
 
15
15
  **Boundary.** `softrMcpMiddleware` lives on `@softr/sdk` and is registered once, in `src/start.ts`. The actions you hand it are the same `defineQuery` / `defineMutation` values the app's own pages use — declared wherever they already live, not duplicated for MCP.
@@ -9,7 +9,7 @@ description: >-
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.68-props.0
12
+ library_version: 0.0.68
13
13
  ---
14
14
 
15
15
  # Installing the app
@@ -11,7 +11,7 @@ description: >-
11
11
  metadata:
12
12
  type: sub-skill
13
13
  library: softr-sdk
14
- library_version: 0.0.68-props.0
14
+ library_version: 0.0.68
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.
@@ -135,6 +135,18 @@ await ds.update(updateProject, {
135
135
 
136
136
  A read answers a SELECT as `{ id, label }` and a write takes `{ id }`, so pass the id through rather than the whole value.
137
137
 
138
+ ### Show the edit before the loader re-runs
139
+
140
+ A list whose rows a drawer or an inline control edits keeps the saved values over the loader's rows until a fresh read replaces them. `useOverrides` holds them by record id:
141
+
142
+ ```tsx
143
+ const overrides = useOverrides<Partial<Deal['fields']>>()
144
+ const rows = deals.map((deal) => ({ ...deal, fields: { ...deal.fields, ...overrides.of(deal.id) } }))
145
+ <DealsDrawer onSaved={overrides.onSaved} />
146
+ ```
147
+
148
+ `onSaved(recordId, patch)` is what a drawer calls before the write and again with the old value if the write fails; `clear(recordId)` drops a record's patch once the page has reloaded it.
149
+
138
150
  ### Form dropdowns for linked record fields
139
151
 
140
152
  A form select for a `LINKED_RECORD` field needs its options from the linked table, not from the record being edited. Query the linked table with its own `defineQuery` and `ds.list`; each result's `id` is what you pass to the write payload. See [record-queries](../record-queries/SKILL.md).
@@ -13,7 +13,7 @@ description: >-
13
13
  metadata:
14
14
  type: sub-skill
15
15
  library: softr-sdk
16
- library_version: 0.0.68-props.0
16
+ library_version: 0.0.68
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. The same holds for any helper that calls `ds`: declare it inside the handler. A function at module level that calls `ds` keeps the server-only import in the browser bundle, and every page stops loading until it moves. `defineQuery` and every type come from `@softr/sdk` and can be imported anywhere.
@@ -146,6 +146,18 @@ const listMyInvoices = createServerFn().handler(async ({ context }) => {
146
146
  })
147
147
  ```
148
148
 
149
+ When the owner is a link to the Users table, the row to match is the visitor's own row there, not `context.user.id`. `auth.currentUserRow(context.user, usersQuery, 'email')` finds it by email, or answers `null` when they are signed out or have no row:
150
+
151
+ ```ts
152
+ import { auth, ds } from '@softr/sdk/server'
153
+
154
+ const listMyDeals = createServerFn().handler(async ({ context }) => {
155
+ const me = await auth.currentUserRow(context.user, usersQuery, 'email')
156
+ if (me === null) return { records: [], nextOffset: null }
157
+ return await ds.list(deals).where((q) => q.owner.isOneOf([me.id]))
158
+ })
159
+ ```
160
+
149
161
  The SDK enforces `permissions`, so a visitor outside the declared audience never reaches the records. Which of those records belong to one visitor is a separate question, and the `where` above answers it: a group says nothing about who owns a row, so a per-visitor read filters on `context.user`. See [current-user](../current-user/SKILL.md) for how the visitor gets onto `context`.
150
162
 
151
163
  ### Populate a control from a linked table
@@ -178,6 +190,14 @@ Each returned record is one option: `record.id` is the value a `where` filter an
178
190
  ds.list(products).where((q) => q.status.is(selectedStatusId))
179
191
  ```
180
192
 
193
+ For a picker that searches and pages, `ds.options` answers the shape a linked-record picker's `load` takes, `{ options: [{ id, label }], nextOffset }`, searched on the label field when a term is given:
194
+
195
+ ```ts
196
+ const loadProductStatuses = createServerFn()
197
+ .validator(z.object({ term: z.string(), offset: z.number().nullable() }))
198
+ .handler(({ data }) => ds.options(productStatuses, { label: 'name', ...data }))
199
+ ```
200
+
181
201
  Skip this request when the options are predefined and fixed — hardcode them instead.
182
202
 
183
203
  A read the datasource turns down throws rather than coming back empty, and it throws a plain `Error` carrying the status, followed by the reason the datasource gives when it sends one. There is no typed predicate to narrow it with. Handle it where you would handle any other failed fetch: an `errorComponent` on the route, or a check in the handler.
@@ -9,7 +9,7 @@ description: >-
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.68-props.0
12
+ library_version: 0.0.68
13
13
  ---
14
14
 
15
15
  **Boundary.** `Audience` and `defineVisibility` come from `@softr/sdk` and work anywhere.
@@ -13,7 +13,7 @@ description: >-
13
13
  metadata:
14
14
  type: sub-skill
15
15
  library: softr-sdk
16
- library_version: 0.0.68-props.0
16
+ library_version: 0.0.68
17
17
  ---
18
18
 
19
19
  **Boundary.** `defineVisibility`, the access functions and the error predicates come from `@softr/sdk` and work anywhere.
@@ -9,7 +9,7 @@ description: >-
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.68-props.0
12
+ library_version: 0.0.68
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.