@softr/sdk 0.0.19 → 0.0.20

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/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { q } from "./core/query.js";
2
2
  import { SoftrUserGroupId, SoftrUserGroups, User } from "./auth/user.js";
3
- import { AggregationFunction, ChartDataOptions, ChartDataResult, CreateOptions, DatasourcePermissions, GetOptions, ListOptions, ListResult, MetricOptions, MetricResult, SoftrMe, SoftrPermissions, SoftrRecord, SoftrUser, UpdateOptions } from "./types.js";
3
+ import { AggregationFunction, ChartDataOptions, ChartDataResult, CreateOptions, DatasourcePermissions, GetOptions, JsonValue, ListOptions, ListResult, MetricOptions, MetricResult, SoftrMe, SoftrPermissions, SoftrRecord, SoftrUser, UpdateOptions } from "./types.js";
4
4
  import { Permissions } from "./auth/permissions.js";
5
5
  import { SoftrContext } from "./auth/context.js";
6
6
  import { AccessBehaviour, AccessCheck, AccessError, AccessReason, RouteVisibility, VisibilityHandle, defineVisibility, isAccessError, isAlreadySignedInError, isForbiddenError, isUnauthenticatedError } from "./routes/visibility.js";
7
7
  import { getSoftrContext, initialSoftrContext, softrAuthMiddleware, useCurrentUser, usePermissions } from "./auth/isomorphic.js";
8
8
  import { useUserTimezone } from "./timezone.js";
9
- export { type AccessBehaviour, type AccessCheck, type AccessError, type AccessReason, type AggregationFunction, type ChartDataOptions, type ChartDataResult, type CreateOptions, type DatasourcePermissions, type GetOptions, type ListOptions, type ListResult, type MetricOptions, type MetricResult, type Permissions, type RouteVisibility, type SoftrContext, type SoftrMe, type SoftrPermissions, type SoftrRecord, type SoftrUser, type SoftrUserGroupId, type SoftrUserGroups, type UpdateOptions, type User, type VisibilityHandle, defineVisibility, getSoftrContext, initialSoftrContext, isAccessError, isAlreadySignedInError, isForbiddenError, isUnauthenticatedError, q, softrAuthMiddleware, useCurrentUser, usePermissions, useUserTimezone };
9
+ export { type AccessBehaviour, type AccessCheck, type AccessError, type AccessReason, type AggregationFunction, type ChartDataOptions, type ChartDataResult, type CreateOptions, type DatasourcePermissions, type GetOptions, type JsonValue, type ListOptions, type ListResult, type MetricOptions, type MetricResult, type Permissions, type RouteVisibility, type SoftrContext, type SoftrMe, type SoftrPermissions, type SoftrRecord, type SoftrUser, type SoftrUserGroupId, type SoftrUserGroups, type UpdateOptions, type User, type VisibilityHandle, defineVisibility, getSoftrContext, initialSoftrContext, isAccessError, isAlreadySignedInError, isForbiddenError, isUnauthenticatedError, q, softrAuthMiddleware, useCurrentUser, usePermissions, useUserTimezone };
package/dist/types.d.ts CHANGED
@@ -53,16 +53,19 @@ interface ListOptions {
53
53
  /** Override the timezone for this request. */
54
54
  timeZone?: string;
55
55
  }
56
- interface ListResult<T = SoftrRecord> {
56
+ type JsonValue = string | number | boolean | null | JsonValue[] | {
57
+ [key: string]: JsonValue;
58
+ };
59
+ interface SoftrRecord<T extends Record<string, JsonValue> = Record<string, JsonValue>> {
60
+ id: string;
61
+ fields: T;
62
+ }
63
+ interface ListResult<T extends SoftrRecord = SoftrRecord> {
57
64
  records: T[];
58
65
  total: number;
59
66
  /** Offset to pass as `offset` in the next call, or `null` when there are no more pages. */
60
67
  nextOffset: number | null;
61
68
  }
62
- interface SoftrRecord {
63
- id: string;
64
- fields: Record<string, unknown>;
65
- }
66
69
  type AggregationFunction = 'SUM' | 'AVG' | 'MAX' | 'MIN' | 'COUNT' | 'DISTINCT';
67
70
  interface MetricOptions {
68
71
  select?: Record<string, string>;
@@ -91,7 +94,7 @@ interface ChartDataOptions {
91
94
  timeZone?: string;
92
95
  }
93
96
  interface ChartDataResult {
94
- data: Record<string, unknown>[];
97
+ data: Record<string, JsonValue>[];
95
98
  executionTimestamp: Date;
96
99
  }
97
100
  interface CreateOptions {
@@ -100,7 +103,7 @@ interface CreateOptions {
100
103
  * When provided, `data` keys are interpreted as aliases.
101
104
  */
102
105
  select?: Record<string, string>;
103
- data: Record<string, unknown>;
106
+ data: Record<string, JsonValue>;
104
107
  }
105
108
  interface UpdateOptions {
106
109
  id: string;
@@ -109,7 +112,7 @@ interface UpdateOptions {
109
112
  * When provided, `data` keys are interpreted as aliases.
110
113
  */
111
114
  select?: Record<string, string>;
112
- data: Record<string, unknown>;
115
+ data: Record<string, JsonValue>;
113
116
  }
114
117
  //#endregion
115
- export { AggregationFunction, ChartDataOptions, ChartDataResult, CreateOptions, DatasourcePermissions, GetOptions, ListOptions, ListResult, MetricOptions, MetricResult, SoftrMe, SoftrPermissions, SoftrRecord, SoftrUser, UpdateOptions };
118
+ export { AggregationFunction, ChartDataOptions, ChartDataResult, CreateOptions, DatasourcePermissions, GetOptions, JsonValue, ListOptions, ListResult, MetricOptions, MetricResult, SoftrMe, SoftrPermissions, SoftrRecord, SoftrUser, UpdateOptions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softr/sdk",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" },
@@ -10,7 +10,7 @@ description: >-
10
10
  metadata:
11
11
  type: core
12
12
  library: softr-sdk
13
- library_version: 0.0.19
13
+ library_version: 0.0.20
14
14
  ---
15
15
 
16
16
  # Softr SDK core
@@ -123,4 +123,4 @@ type User = {
123
123
 
124
124
  ## Version
125
125
 
126
- Targets `@softr/sdk` v0.0.19.
126
+ Targets `@softr/sdk` v0.0.20.
@@ -10,7 +10,7 @@ description: >-
10
10
  metadata:
11
11
  type: sub-skill
12
12
  library: softr-sdk
13
- library_version: 0.0.19
13
+ library_version: 0.0.20
14
14
  ---
15
15
 
16
16
  This skill builds on softr-sdk. Read it first for the entrypoints and the client/server boundary.
@@ -10,7 +10,7 @@ description: >-
10
10
  metadata:
11
11
  type: sub-skill
12
12
  library: softr-sdk
13
- library_version: 0.0.19
13
+ library_version: 0.0.20
14
14
  ---
15
15
 
16
16
  This skill builds on softr-sdk. Read it first for the entrypoints and the wiring that puts a visitor on route context.
@@ -8,7 +8,7 @@ description: >-
8
8
  metadata:
9
9
  type: sub-skill
10
10
  library: softr-sdk
11
- library_version: 0.0.19
11
+ library_version: 0.0.20
12
12
  ---
13
13
 
14
14
  This skill builds on softr-sdk. Read it first for the entrypoints and the client/server boundary.
@@ -10,7 +10,7 @@ description: >-
10
10
  metadata:
11
11
  type: sub-skill
12
12
  library: softr-sdk
13
- library_version: 0.0.19
13
+ library_version: 0.0.20
14
14
  ---
15
15
 
16
16
  This skill builds on softr-sdk. Read it first for the entrypoints and the client/server boundary.
@@ -29,7 +29,36 @@ import { q } from '@softr/sdk'
29
29
 
30
30
  `ds` is server-only and lives on `@softr/sdk/server`. `q` builds queries and nothing more, so it ships on the root entrypoint and can be imported anywhere.
31
31
 
32
- A record comes back as `{ id: string; fields: Record<string, unknown> }`.
32
+ A record comes back as `{ id: string; fields: Record<string, unknown> }`. When you write a TypeScript type for the fields, the shape of each value depends on the Softr field type — see [references/field-types.md](./references/field-types.md) for the full mapping.
33
+
34
+ ## Typing record fields
35
+
36
+ Declare a local type for the records you fetch using `SoftrRecord<Fields>`. Use the shapes from the field-types reference, not guesses from the field name. The most common mistake is treating SELECT and LINKED_RECORD fields as strings — they are objects:
37
+
38
+ ```ts
39
+ import type { SoftrRecord } from '@softr/sdk'
40
+
41
+ type Product = SoftrRecord<{
42
+ name: string // SINGLE_LINE_TEXT
43
+ price: number | null // CURRENCY
44
+ category: { id: string; label: string } // SELECT — not a string
45
+ supplier: { id: string; label: string } // LINKED_RECORD — not a string
46
+ }>
47
+ ```
48
+
49
+ `SoftrRecord<Fields>` is shorthand for `{ id: string; fields: Fields }` — the `Fields` type parameter constrains every value to `JsonValue`, so TypeScript rejects non-serializable types like `Date` objects.
50
+
51
+ Cast at the `ds.list` call using the `ListResult` generic so the type flows through the loader automatically:
52
+
53
+ ```ts
54
+ import type { ListResult, SoftrRecord } from '@softr/sdk'
55
+
56
+ const fetchProducts = createServerFn().handler(() =>
57
+ ds.list('products', { select, limit: 100 }) as Promise<ListResult<Product>>
58
+ )
59
+ ```
60
+
61
+ `loaderData` is then already typed as `ListResult<Product>` — no second cast needed in the component. All the shapes in [references/field-types.md](./references/field-types.md) satisfy the `JsonValue` constraint.
33
62
 
34
63
  ## Core Patterns
35
64
 
@@ -130,6 +159,7 @@ A read the visitor is not allowed to make throws rather than coming back empty,
130
159
 
131
160
  ## References
132
161
 
162
+ - [references/field-types.md](./references/field-types.md): TypeScript shape for every Softr field type
133
163
  - [references/query-builder.md](./references/query-builder.md): every `q` field type and operator
134
164
  - [current-user/SKILL.md](../current-user/SKILL.md): reading the visitor off `context` to scope a query
135
165
  - [routes-and-visibility/SKILL.md](../routes-and-visibility/SKILL.md): deciding who sees the page the records land on
@@ -0,0 +1,70 @@
1
+ # Softr field types — TypeScript shapes
2
+
3
+ When you write a TypeScript type for a record's fields, use these shapes. The field's **type** (shown in the datasource metadata or described by the user) determines the shape, not the field's name.
4
+
5
+ ```ts
6
+ // Simple scalar types
7
+ SINGLE_LINE_TEXT: string
8
+ LONG_TEXT: string
9
+ EMAIL: string
10
+ PHONE: string
11
+ URL: string
12
+ ADDRESS: string
13
+ LOCATION: string
14
+ BUTTON: string
15
+ RECORD_ID: string
16
+ DATETIME: string // ISO timestamp string
17
+ DATE: string // ISO date string
18
+ TIME: string
19
+ CREATED_AT: string
20
+ UPDATED_AT: string
21
+ CHECKBOX: string | boolean
22
+ AUTONUMBER: number | null
23
+ NUMBER: number | null
24
+ PERCENT: number | null
25
+ CURRENCY: number | null
26
+ PROGRESS: number | null
27
+ DURATION: number | null
28
+ COUNT: number | null
29
+ RATING: string | number | null
30
+ FORMULA: string | number
31
+
32
+ // Object types — these are NOT plain strings
33
+ SELECT: { id: string; label: string }
34
+ LINKED_RECORD: { id: string; label: string }
35
+
36
+ DATE_RANGE: { from: string; to: string }
37
+
38
+ ATTACHMENT: {
39
+ filename: string | null | undefined
40
+ id: string | null | undefined
41
+ type: string | null | undefined
42
+ url: string
43
+ thumbnails?: Array<{ size: 'small' | 'medium' | 'large'; url: string }>
44
+ }
45
+
46
+ USER: { avatarUrl: string | null; id: string; name: string; email: string }
47
+
48
+ CREATED_BY: string | string[] | { avatarUrl: string | null; id: string; name: string; email: string }
49
+ UPDATED_BY: string | string[] | { avatarUrl: string | null; id: string; name: string; email: string }
50
+
51
+ // These depend on what the linked field/aggregation resolves to
52
+ LOOKUP: unknown
53
+ ROLLUP: unknown
54
+ ```
55
+
56
+ ## The non-obvious ones
57
+
58
+ **SELECT and LINKED_RECORD** are the most commonly mistyped. They are never a plain string — always `{ id: string; label: string }`. To display the value, use `.label`. To filter or compare, use `.id`.
59
+
60
+ ```tsx
61
+ // Correct
62
+ <td>{record.fields.status?.label}</td>
63
+
64
+ // Wrong — status is not a string
65
+ <td>{record.fields.status}</td>
66
+ ```
67
+
68
+ **CHECKBOX** can be either a string (`'true'` / `'false'`) or a boolean depending on the datasource. Type it as `string | boolean` and coerce when needed: `!!record.fields.active`.
69
+
70
+ **CREATED_BY / UPDATED_BY** can be a plain id string, an array of ids, or a full user object. Narrow before using.
@@ -12,7 +12,7 @@ description: >-
12
12
  metadata:
13
13
  type: sub-skill
14
14
  library: softr-sdk
15
- library_version: 0.0.19
15
+ library_version: 0.0.20
16
16
  ---
17
17
 
18
18
  This skill builds on softr-sdk. Read it first for the entrypoints and the wiring that puts a visitor on route context.