@softr/sdk 0.0.4 → 0.0.6

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.
@@ -1,237 +0,0 @@
1
- //#region src/routes/visibility.d.ts
2
- /**
3
- * Declare this interface in your app to narrow every group id the SDK takes and
4
- * returns. Without a declaration, a group id is a plain `string`.
5
- *
6
- * @example
7
- * declare module "@softr/sdk" {
8
- * interface SoftrUserGroups {
9
- * ids:
10
- * | "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
11
- * | "f9e8d7c6-5b4a-4938-8271-605f4e3d2c1b"
12
- * }
13
- * }
14
- */
15
- interface SoftrUserGroups {}
16
- /** A Softr user group id, narrowed to your own ids if you declare {@link SoftrUserGroups}. */
17
- type SoftrUserGroupId = SoftrUserGroups extends {
18
- ids: infer Id extends string;
19
- } ? Id : string;
20
- /**
21
- * Who may see a route.
22
- *
23
- * - `ALL_USERS` — everyone, signed in or not.
24
- * - `NON_LOGGED_IN_USERS` — signed-out visitors only.
25
- * - `LOGGED_IN_USERS` — signed-in visitors; with `userGroupIds`, only members of
26
- * at least one of those groups.
27
- */
28
- type RouteVisibility = {
29
- userGroup: "ALL_USERS";
30
- } | {
31
- userGroup: "NON_LOGGED_IN_USERS";
32
- } | {
33
- userGroup: "LOGGED_IN_USERS";
34
- userGroupIds?: Array<SoftrUserGroupId>;
35
- };
36
- /** The signed-in visitor. */
37
- type User = {
38
- id: string;
39
- email: string;
40
- name: string;
41
- userGroupIds: Array<SoftrUserGroupId>;
42
- };
43
- /** The route context the root route publishes: the visitor, or `null` when signed out. */
44
- type SoftrContext = {
45
- user: User | null;
46
- };
47
- /**
48
- * Why a visitor was denied.
49
- *
50
- * - `unauthenticated` — the route needs a signed-in visitor and there is none.
51
- * - `forbidden` — signed in, but in none of the groups the route names.
52
- * - `alreadySignedIn` — the route is for signed-out visitors only.
53
- */
54
- declare const ACCESS_REASONS: readonly ["unauthenticated", "forbidden", "alreadySignedIn"];
55
- type AccessReason = (typeof ACCESS_REASONS)[number];
56
- /**
57
- * What a guard does when it refuses a visitor: redirect to a path, or throw an
58
- * {@link AccessError} for the route's `errorComponent` to render. A bare string
59
- * is shorthand for redirecting there.
60
- */
61
- type AccessBehaviour = string | {
62
- strategy: "redirect";
63
- to: string;
64
- } | {
65
- strategy: "throw";
66
- };
67
- /** Per-reason overrides of the default {@link AccessBehaviour}. */
68
- type GuardOverrides = Partial<Record<AccessReason, AccessBehaviour>>;
69
- /** The result of {@link VisibilityHandle.check}: passed, or denied with a reason. */
70
- type AccessCheck = {
71
- ok: true;
72
- } | {
73
- ok: false;
74
- reason: AccessReason;
75
- };
76
- /**
77
- * The access error a guard throws. It is plain data, not an `Error` subclass, so
78
- * take it as `unknown` in an `errorComponent` and narrow it with
79
- * {@link isAccessError}. It carries the reason and nothing else: the error is
80
- * serialized into the page, so it never names the groups the route wanted.
81
- *
82
- * @example
83
- * errorComponent: ({ error }: { error: unknown }) =>
84
- * isAccessError(error) ? <NoAccess /> : <AppError />
85
- */
86
- type AccessError = {
87
- type: "access_error";
88
- reason: AccessReason;
89
- };
90
- /**
91
- * True for any access error thrown by a guard, whatever its reason.
92
- *
93
- * An access error arrives as `unknown` — hydrated from the page, or thrown by
94
- * anything else at all — so every field is checked before it is narrowed.
95
- */
96
- declare function isAccessError(error: unknown): error is AccessError;
97
- /** True for an access error thrown because nobody was signed in. */
98
- declare function isUnauthenticatedError(error: unknown): error is AccessError;
99
- /** True for an access error thrown because the visitor was in none of the groups. */
100
- declare function isForbiddenError(error: unknown): error is AccessError;
101
- /** True for an access error thrown because a signed-out-only route saw a user. */
102
- declare function isAlreadySignedInError(error: unknown): error is AccessError;
103
- type Narrowed<R extends RouteVisibility> = R extends {
104
- userGroup: "LOGGED_IN_USERS";
105
- } ? SoftrContext & {
106
- user: User;
107
- } : R extends {
108
- userGroup: "NON_LOGGED_IN_USERS";
109
- } ? SoftrContext & {
110
- user: null;
111
- } : SoftrContext;
112
- /** The four ways to apply a rule, returned by {@link defineVisibility}. */
113
- type VisibilityHandle<R extends RouteVisibility = RouteVisibility> = {
114
- /**
115
- * A `beforeLoad` handler that denies a visitor who fails the rule.
116
- *
117
- * By default an unauthenticated visitor is redirected to `/login`, an already
118
- * signed-in one to `/`, and a forbidden one gets an {@link AccessError} thrown
119
- * in place, for the route's `errorComponent` to render. Pass `overrides` to
120
- * change what any one reason does.
121
- *
122
- * @example
123
- * beforeLoad: visibility.guard({ unauthenticated: "/sign-in" })
124
- */
125
- guard(overrides?: GuardOverrides): (opts: {
126
- context: SoftrContext;
127
- }) => void;
128
- /**
129
- * Throws the same access error or redirect `guard()` would, for code that
130
- * needs to run its own checks after the rule.
131
- *
132
- * @example
133
- * beforeLoad: ({ context }) => {
134
- * visibility.assert(context)
135
- * if (context.user.trialExpired) throw redirect({ to: "/renew" })
136
- * }
137
- */
138
- assert(context: SoftrContext): void;
139
- /**
140
- * Whether the visitor passes the rule. Narrows `context.user` where the rule
141
- * allows it.
142
- */
143
- isAuthorized(context: SoftrContext): context is Narrowed<R>;
144
- /** The decision and, when a visitor is refused, the reason. Throws nothing. */
145
- check(context: SoftrContext): AccessCheck;
146
- };
147
- /**
148
- * Declares who may see a route, and returns the handle that enforces it.
149
- *
150
- * One call per route file, at module scope: the build reads these declarations
151
- * to publish the app's route visibility, so the rule must be a literal.
152
- *
153
- * @example
154
- * const visibility = defineVisibility({ userGroup: "LOGGED_IN_USERS" })
155
- *
156
- * export const Route = createFileRoute("/about")({
157
- * beforeLoad: visibility.guard(),
158
- * component: About,
159
- * })
160
- */
161
- declare function defineVisibility<const R extends RouteVisibility>(rule: R): VisibilityHandle<R>;
162
- //#endregion
163
- //#region src/types.d.ts
164
- interface SoftrUser {
165
- id: string;
166
- email: string;
167
- name: string;
168
- userGroupIds: Array<SoftrUserGroupId>;
169
- }
170
- interface DatasourcePermissions {
171
- read: boolean;
172
- create: boolean;
173
- update: boolean;
174
- delete: boolean;
175
- }
176
- interface SoftrPermissions {
177
- datasources: Record<string, DatasourcePermissions>;
178
- workflows: Record<string, boolean>;
179
- }
180
- interface SoftrMe {
181
- user: SoftrUser | null;
182
- permissions: SoftrPermissions;
183
- }
184
- interface ListOptions {
185
- page?: number;
186
- limit?: number;
187
- filters?: Array<{
188
- field: string;
189
- operator: string;
190
- value: unknown;
191
- }>;
192
- sort?: Array<{
193
- field: string;
194
- direction: 'asc' | 'desc';
195
- }>;
196
- }
197
- interface ListResult<T = SoftrRecord> {
198
- records: T[];
199
- total: number;
200
- page: number;
201
- limit: number;
202
- }
203
- interface SoftrRecord {
204
- id: string;
205
- [field: string]: unknown;
206
- }
207
- interface DatasourceAPI {
208
- list(options?: ListOptions): Promise<ListResult>;
209
- get(recordId: string): Promise<SoftrRecord>;
210
- create(data: Record<string, unknown>): Promise<SoftrRecord>;
211
- update(recordId: string, data: Record<string, unknown>): Promise<SoftrRecord>;
212
- delete(recordId: string): Promise<void>;
213
- }
214
- interface WorkflowAPI {
215
- trigger(payload?: Record<string, unknown>): Promise<void>;
216
- }
217
- interface AuthAPI {
218
- /** Sign in with email and password. Clears the cached me() result. */
219
- login(credentials: {
220
- email: string;
221
- password: string;
222
- }): Promise<void>;
223
- /** Sign out the current user. Clears the cached me() result. */
224
- logout(): Promise<void>;
225
- }
226
- interface SoftrSDK {
227
- /**
228
- * Fetch the current user and their permissions from /me.
229
- * Cached for the lifetime of the instance — create a new instance per request on the server.
230
- */
231
- me(): Promise<SoftrMe>;
232
- auth: AuthAPI;
233
- datasource(id: string): DatasourceAPI;
234
- workflow(id: string): WorkflowAPI;
235
- }
236
- //#endregion
237
- export { isAccessError as C, isUnauthenticatedError as E, defineVisibility as S, isForbiddenError as T, SoftrContext as _, ListResult as a, User as b, SoftrRecord as c, WorkflowAPI as d, AccessBehaviour as f, RouteVisibility as g, AccessReason as h, ListOptions as i, SoftrSDK as l, AccessError as m, DatasourceAPI as n, SoftrMe as o, AccessCheck as p, DatasourcePermissions as r, SoftrPermissions as s, AuthAPI as t, SoftrUser as u, SoftrUserGroupId as v, isAlreadySignedInError as w, VisibilityHandle as x, SoftrUserGroups as y };