@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 +2 -2
- package/dist/types.d.ts +12 -9
- package/package.json +1 -1
- package/skills/softr-sdk/SKILL.md +2 -2
- package/skills/softr-sdk/auth/SKILL.md +1 -1
- package/skills/softr-sdk/current-user/SKILL.md +1 -1
- package/skills/softr-sdk/data-mutations/SKILL.md +1 -1
- package/skills/softr-sdk/reading-records/SKILL.md +32 -2
- package/skills/softr-sdk/reading-records/references/field-types.md +70 -0
- package/skills/softr-sdk/routes-and-visibility/SKILL.md +1 -1
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
@@ -10,7 +10,7 @@ description: >-
|
|
|
10
10
|
metadata:
|
|
11
11
|
type: core
|
|
12
12
|
library: softr-sdk
|
|
13
|
-
library_version: 0.0.
|
|
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.
|
|
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.
|
|
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.
|