@softr/sdk 0.0.3 → 0.0.5
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 +35 -82
- package/dist/index.js +153 -56
- package/dist/query-C6022JhD.js +528 -0
- package/dist/tanstack.d.ts +1 -1
- package/dist/tanstack.js +162 -26
- package/dist/types-BYfW8LV3.d.ts +640 -0
- package/package.json +9 -5
- package/dist/types-BLLUI6MI.d.ts +0 -75
package/dist/index.d.ts
CHANGED
|
@@ -1,84 +1,37 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { A as defineVisibility, C as AccessReason, D as SoftrUserGroups, E as SoftrUserGroupId, F as q, M as isAlreadySignedInError, N as isForbiddenError, O as User, P as isUnauthenticatedError, S as AccessError, T as SoftrContext, _ as SoftrUser, a as CreateOptions, b as AccessBehaviour, c as GetOptions, d as MetricOptions, f as MetricResult, g as SoftrSDK, h as SoftrRecord, i as ChartDataResult, j as isAccessError, k as VisibilityHandle, l as ListOptions, m as SoftrPermissions, n as AuthAPI, o as DatasourceAPI, p as SoftrMe, r as ChartDataOptions, s as DatasourcePermissions, t as AggregationFunction, u as ListResult, v as UpdateOptions, w as RouteVisibility, x as AccessCheck, y as WorkflowAPI } from "./types-BYfW8LV3.js";
|
|
2
|
+
import * as _tanstack_react_start0 from "@tanstack/react-start";
|
|
2
3
|
|
|
3
|
-
//#region src/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
readonly reason: RefusalReason;
|
|
36
|
-
readonly requiredGroupIds: Array<SoftrUserGroupId> | undefined;
|
|
37
|
-
constructor(reason: RefusalReason, requiredGroupIds?: Array<SoftrUserGroupId>);
|
|
38
|
-
}
|
|
39
|
-
interface VisibilityHandle {
|
|
40
|
-
/** Structured check — returns { ok: true } or { ok: false, reason }. */
|
|
41
|
-
check(context: SoftrContext): CheckResult;
|
|
42
|
-
/** Returns true if the visitor passes the rule. */
|
|
43
|
-
isAuthorized(context: SoftrContext): boolean;
|
|
44
|
-
/**
|
|
45
|
-
* Throws a VisibilityError if not authorized. Acts as a TypeScript assertion,
|
|
46
|
-
* narrowing context.user to non-null for code that follows.
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* beforeLoad: ({ context }) => {
|
|
50
|
-
* visibility.assert(context)
|
|
51
|
-
* if (context.user.trialExpired) throw redirect({ to: '/renew' })
|
|
52
|
-
* }
|
|
53
|
-
*/
|
|
54
|
-
assert(context: SoftrContext): asserts context is SoftrContext & {
|
|
55
|
-
user: SoftrUser;
|
|
56
|
-
};
|
|
57
|
-
/**
|
|
58
|
-
* Returns a TanStack beforeLoad handler. On refusal, throws a VisibilityError
|
|
59
|
-
* caught by the route's errorComponent.
|
|
60
|
-
*
|
|
61
|
-
* For redirect-on-refusal, use isAuthorized or check directly and throw
|
|
62
|
-
* TanStack's redirect() yourself.
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* beforeLoad: visibility.guard()
|
|
66
|
-
* errorComponent: ({ error }) => isVisibilityError(error) ? <NoAccess /> : <AppError />
|
|
67
|
-
*/
|
|
68
|
-
guard(overrides?: GuardOverrides): (params: {
|
|
69
|
-
context: SoftrContext;
|
|
70
|
-
}) => void;
|
|
71
|
-
}
|
|
72
|
-
declare function defineVisibility(rule: RouteVisibility): VisibilityHandle;
|
|
73
|
-
declare function isVisibilityError(error: unknown): error is VisibilityError;
|
|
74
|
-
declare function isUnauthenticatedError(error: unknown): error is VisibilityError & {
|
|
75
|
-
reason: "unauthenticated";
|
|
76
|
-
};
|
|
77
|
-
declare function isForbiddenError(error: unknown): error is VisibilityError & {
|
|
78
|
-
reason: "forbidden";
|
|
79
|
-
};
|
|
80
|
-
declare function isAlreadySignedInError(error: unknown): error is VisibilityError & {
|
|
81
|
-
reason: "alreadySignedIn";
|
|
82
|
-
};
|
|
4
|
+
//#region src/auth/session.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns the signed-in visitor, or `null` when nobody is signed in.
|
|
8
|
+
*
|
|
9
|
+
* Await it in the root route's `beforeLoad` to publish the visitor as route
|
|
10
|
+
* context, which every `defineVisibility` guard then reads. It requires
|
|
11
|
+
* {@link softrAuthMiddleware} to be registered.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* export const Route = createRootRouteWithContext<SoftrContext>()({
|
|
15
|
+
* beforeLoad: async () => ({ user: await getSoftrUser() }),
|
|
16
|
+
* })
|
|
17
|
+
*/
|
|
18
|
+
declare const getSoftrUser: _tanstack_react_start0.OptionalFetcher<undefined, undefined, Promise<User | null>>;
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/auth/middleware.d.ts
|
|
21
|
+
/**
|
|
22
|
+
* Resolves the Softr visitor for every request, so that `getSoftrUser` and the
|
|
23
|
+
* route guards have someone to check.
|
|
24
|
+
*
|
|
25
|
+
* Register it once, as a global request middleware in the app's `src/start.ts`.
|
|
26
|
+
* It touches nothing but the visitor.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* export const startInstance = createStart(() => ({
|
|
30
|
+
* requestMiddleware: [softrAuthMiddleware()],
|
|
31
|
+
* }))
|
|
32
|
+
*/
|
|
33
|
+
declare function softrAuthMiddleware(): _tanstack_react_start0.RequestMiddlewareAfterServer<{}, undefined, {
|
|
34
|
+
user: User | null;
|
|
35
|
+
}>;
|
|
83
36
|
//#endregion
|
|
84
|
-
export { type AuthAPI, type
|
|
37
|
+
export { type AccessBehaviour, type AccessCheck, type AccessError, type AccessReason, type AggregationFunction, type AuthAPI, type ChartDataOptions, type ChartDataResult, type CreateOptions, type DatasourceAPI, type DatasourcePermissions, type GetOptions, type ListOptions, type ListResult, type MetricOptions, type MetricResult, type RouteVisibility, type SoftrContext, type SoftrMe, type SoftrPermissions, type SoftrRecord, type SoftrSDK, type SoftrUser, type SoftrUserGroupId, type SoftrUserGroups, type UpdateOptions, type User, type VisibilityHandle, type WorkflowAPI, defineVisibility, getSoftrUser, isAccessError, isAlreadySignedInError, isForbiddenError, isUnauthenticatedError, q, softrAuthMiddleware };
|
package/dist/index.js
CHANGED
|
@@ -1,73 +1,170 @@
|
|
|
1
|
+
import { a as q } from "./query-C6022JhD.js";
|
|
2
|
+
import { redirect } from "@tanstack/react-router";
|
|
3
|
+
import { createMiddleware, createServerFn, getGlobalStartContext } from "@tanstack/react-start";
|
|
4
|
+
|
|
1
5
|
//#region src/routes/visibility.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Why a visitor was denied.
|
|
8
|
+
*
|
|
9
|
+
* - `unauthenticated` — the route needs a signed-in visitor and there is none.
|
|
10
|
+
* - `forbidden` — signed in, but in none of the groups the route names.
|
|
11
|
+
* - `alreadySignedIn` — the route is for signed-out visitors only.
|
|
12
|
+
*/
|
|
13
|
+
const ACCESS_REASONS = [
|
|
14
|
+
"unauthenticated",
|
|
15
|
+
"forbidden",
|
|
16
|
+
"alreadySignedIn"
|
|
17
|
+
];
|
|
18
|
+
/** Decides whether a visitor passes a visibility rule, without throwing. */
|
|
19
|
+
function checkAccess(rule, context) {
|
|
13
20
|
const { user } = context;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
ok:
|
|
17
|
-
reason: "alreadySignedIn"
|
|
18
|
-
} : { ok: true };
|
|
19
|
-
if (!user) return {
|
|
20
|
-
ok: false,
|
|
21
|
-
reason: "unauthenticated"
|
|
22
|
-
};
|
|
23
|
-
const { userGroupIds } = rule;
|
|
24
|
-
if (userGroupIds?.length) {
|
|
25
|
-
if (!userGroupIds.some((id) => user.userGroupIds.includes(id))) return {
|
|
21
|
+
switch (rule.userGroup) {
|
|
22
|
+
case "ALL_USERS": return { ok: true };
|
|
23
|
+
case "NON_LOGGED_IN_USERS": return user === null ? { ok: true } : {
|
|
26
24
|
ok: false,
|
|
27
|
-
reason: "
|
|
28
|
-
requiredGroupIds: userGroupIds
|
|
25
|
+
reason: "alreadySignedIn"
|
|
29
26
|
};
|
|
27
|
+
case "LOGGED_IN_USERS": {
|
|
28
|
+
if (user === null) return {
|
|
29
|
+
ok: false,
|
|
30
|
+
reason: "unauthenticated"
|
|
31
|
+
};
|
|
32
|
+
const required = rule.userGroupIds ?? [];
|
|
33
|
+
if (required.length === 0) return { ok: true };
|
|
34
|
+
return required.some((id) => user.userGroupIds.includes(id)) ? { ok: true } : {
|
|
35
|
+
ok: false,
|
|
36
|
+
reason: "forbidden"
|
|
37
|
+
};
|
|
38
|
+
}
|
|
30
39
|
}
|
|
31
|
-
return { ok: true };
|
|
32
40
|
}
|
|
33
|
-
function
|
|
34
|
-
|
|
35
|
-
throw new VisibilityError(result.reason, result.reason === "forbidden" ? result.requiredGroupIds : void 0);
|
|
41
|
+
function isAccessReason(value) {
|
|
42
|
+
return ACCESS_REASONS.some((reason) => reason === value);
|
|
36
43
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return ({ context }) => {
|
|
49
|
-
throwIfDenied(runCheck(rule, context));
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
check,
|
|
54
|
-
isAuthorized,
|
|
55
|
-
assert,
|
|
56
|
-
guard
|
|
57
|
-
};
|
|
44
|
+
/**
|
|
45
|
+
* True for any access error thrown by a guard, whatever its reason.
|
|
46
|
+
*
|
|
47
|
+
* An access error arrives as `unknown` — hydrated from the page, or thrown by
|
|
48
|
+
* anything else at all — so every field is checked before it is narrowed.
|
|
49
|
+
*/
|
|
50
|
+
function isAccessError(error) {
|
|
51
|
+
if (typeof error !== "object" || error === null) return false;
|
|
52
|
+
const candidate = error;
|
|
53
|
+
if (candidate.type !== "access_error") return false;
|
|
54
|
+
return isAccessReason(candidate.reason);
|
|
58
55
|
}
|
|
59
|
-
function
|
|
60
|
-
return error
|
|
56
|
+
function isReason(error, reason) {
|
|
57
|
+
return isAccessError(error) && error.reason === reason;
|
|
61
58
|
}
|
|
59
|
+
/** True for an access error thrown because nobody was signed in. */
|
|
62
60
|
function isUnauthenticatedError(error) {
|
|
63
|
-
return error
|
|
61
|
+
return isReason(error, "unauthenticated");
|
|
64
62
|
}
|
|
63
|
+
/** True for an access error thrown because the visitor was in none of the groups. */
|
|
65
64
|
function isForbiddenError(error) {
|
|
66
|
-
return error
|
|
65
|
+
return isReason(error, "forbidden");
|
|
67
66
|
}
|
|
67
|
+
/** True for an access error thrown because a signed-out-only route saw a user. */
|
|
68
68
|
function isAlreadySignedInError(error) {
|
|
69
|
-
return error
|
|
69
|
+
return isReason(error, "alreadySignedIn");
|
|
70
|
+
}
|
|
71
|
+
const DEFAULT_ACCESS_BEHAVIOUR = {
|
|
72
|
+
unauthenticated: {
|
|
73
|
+
strategy: "redirect",
|
|
74
|
+
to: "/login"
|
|
75
|
+
},
|
|
76
|
+
forbidden: { strategy: "throw" },
|
|
77
|
+
alreadySignedIn: {
|
|
78
|
+
strategy: "redirect",
|
|
79
|
+
to: "/"
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
/** What a guard throws for a reason: a redirect, or an {@link AccessError}. */
|
|
83
|
+
function thrownFor(reason, overrides) {
|
|
84
|
+
const behaviour = overrides?.[reason] ?? DEFAULT_ACCESS_BEHAVIOUR[reason];
|
|
85
|
+
const resolved = typeof behaviour === "string" ? {
|
|
86
|
+
strategy: "redirect",
|
|
87
|
+
to: behaviour
|
|
88
|
+
} : behaviour;
|
|
89
|
+
if (resolved.strategy === "redirect") return redirect({ to: resolved.to });
|
|
90
|
+
return {
|
|
91
|
+
type: "access_error",
|
|
92
|
+
reason
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Declares who may see a route, and returns the handle that enforces it.
|
|
97
|
+
*
|
|
98
|
+
* One call per route file, at module scope: the build reads these declarations
|
|
99
|
+
* to publish the app's route visibility, so the rule must be a literal.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* const visibility = defineVisibility({ userGroup: "LOGGED_IN_USERS" })
|
|
103
|
+
*
|
|
104
|
+
* export const Route = createFileRoute("/about")({
|
|
105
|
+
* beforeLoad: visibility.guard(),
|
|
106
|
+
* component: About,
|
|
107
|
+
* })
|
|
108
|
+
*/
|
|
109
|
+
function defineVisibility(rule) {
|
|
110
|
+
return {
|
|
111
|
+
guard: (overrides) => ({ context }) => {
|
|
112
|
+
const result = checkAccess(rule, context);
|
|
113
|
+
if (result.ok) return;
|
|
114
|
+
throw thrownFor(result.reason, overrides);
|
|
115
|
+
},
|
|
116
|
+
assert: (context) => {
|
|
117
|
+
const result = checkAccess(rule, context);
|
|
118
|
+
if (result.ok) return;
|
|
119
|
+
throw thrownFor(result.reason, void 0);
|
|
120
|
+
},
|
|
121
|
+
isAuthorized: (context) => checkAccess(rule, context).ok,
|
|
122
|
+
check: (context) => checkAccess(rule, context)
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/auth/session.ts
|
|
128
|
+
async function resolveVisitor(_request) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
function visitorFromContext(context) {
|
|
132
|
+
if (typeof context !== "object" || context === null || !("user" in context)) throw new Error("softrAuthMiddleware has not run: the Start global context carries no `user`. Register it as a global request middleware, e.g. createStart(() => ({ requestMiddleware: [softrAuthMiddleware()] })).");
|
|
133
|
+
return context.user ?? null;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Returns the signed-in visitor, or `null` when nobody is signed in.
|
|
137
|
+
*
|
|
138
|
+
* Await it in the root route's `beforeLoad` to publish the visitor as route
|
|
139
|
+
* context, which every `defineVisibility` guard then reads. It requires
|
|
140
|
+
* {@link softrAuthMiddleware} to be registered.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* export const Route = createRootRouteWithContext<SoftrContext>()({
|
|
144
|
+
* beforeLoad: async () => ({ user: await getSoftrUser() }),
|
|
145
|
+
* })
|
|
146
|
+
*/
|
|
147
|
+
const getSoftrUser = createServerFn({ method: "GET" }).handler(async () => visitorFromContext(getGlobalStartContext()));
|
|
148
|
+
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region src/auth/middleware.ts
|
|
151
|
+
/**
|
|
152
|
+
* Resolves the Softr visitor for every request, so that `getSoftrUser` and the
|
|
153
|
+
* route guards have someone to check.
|
|
154
|
+
*
|
|
155
|
+
* Register it once, as a global request middleware in the app's `src/start.ts`.
|
|
156
|
+
* It touches nothing but the visitor.
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* export const startInstance = createStart(() => ({
|
|
160
|
+
* requestMiddleware: [softrAuthMiddleware()],
|
|
161
|
+
* }))
|
|
162
|
+
*/
|
|
163
|
+
function softrAuthMiddleware() {
|
|
164
|
+
return createMiddleware({ type: "request" }).server(async ({ request, next }) => {
|
|
165
|
+
return next({ context: { user: await resolveVisitor(request) } });
|
|
166
|
+
});
|
|
70
167
|
}
|
|
71
168
|
|
|
72
169
|
//#endregion
|
|
73
|
-
export {
|
|
170
|
+
export { defineVisibility, getSoftrUser, isAccessError, isAlreadySignedInError, isForbiddenError, isUnauthenticatedError, q, softrAuthMiddleware };
|