@softr/sdk 0.0.64-alpha.0 → 0.0.64-alpha.1

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.
@@ -2,9 +2,9 @@ import { User } from "./user.js";
2
2
  import { Formatting } from "../formatting.js";
3
3
  import { Permissions } from "./permissions.js";
4
4
  import { SoftrContext } from "./context.js";
5
- import * as react0 from "react";
5
+ import * as react6 from "react";
6
6
  import { ReactNode } from "react";
7
- import * as _tanstack_react_start0 from "@tanstack/react-start";
7
+ import * as _tanstack_react_start1 from "@tanstack/react-start";
8
8
 
9
9
  //#region src/auth/isomorphic.d.ts
10
10
 
@@ -21,7 +21,7 @@ import * as _tanstack_react_start0 from "@tanstack/react-start";
21
21
  * requestMiddleware: [softrAuthMiddleware()],
22
22
  * }))
23
23
  */
24
- declare function softrAuthMiddleware(): _tanstack_react_start0.RequestMiddlewareAfterServer<{}, readonly [_tanstack_react_start0.RequestMiddlewareAfterServer<{}, undefined, undefined>, _tanstack_react_start0.RequestMiddlewareAfterServer<{}, undefined, undefined>], {
24
+ declare function softrAuthMiddleware(): _tanstack_react_start1.RequestMiddlewareAfterServer<{}, readonly [_tanstack_react_start1.RequestMiddlewareAfterServer<{}, undefined, undefined>, _tanstack_react_start1.RequestMiddlewareAfterServer<{}, undefined, undefined>], {
25
25
  user: User | null;
26
26
  permissions: Permissions;
27
27
  formatting: Formatting;
@@ -35,7 +35,7 @@ declare function softrAuthMiddleware(): _tanstack_react_start0.RequestMiddleware
35
35
  * beforeLoad: async () => await getSoftrContext(),
36
36
  * })
37
37
  */
38
- declare const getSoftrContext: _tanstack_react_start0.OptionalFetcher<undefined, undefined, Promise<SoftrContext>>;
38
+ declare const getSoftrContext: _tanstack_react_start1.OptionalFetcher<undefined, undefined, Promise<SoftrContext>>;
39
39
  /**
40
40
  * The context to hand `createRouter`, before any request has resolved one.
41
41
  *
@@ -83,7 +83,7 @@ type AuthOptions = {
83
83
  * staleTime: 60_000,
84
84
  * })
85
85
  */
86
- declare const getAuthSettings: _tanstack_react_start0.OptionalFetcher<undefined, undefined, Promise<AuthOptions>>;
86
+ declare const getAuthSettings: _tanstack_react_start1.OptionalFetcher<undefined, undefined, Promise<AuthOptions>>;
87
87
  /**
88
88
  * Makes the auth options readable with {@link useAuthSettings} anywhere below.
89
89
  *
@@ -103,7 +103,7 @@ declare function AuthSettingsProvider({
103
103
  }: {
104
104
  value: AuthOptions;
105
105
  children?: ReactNode;
106
- }): react0.JSX.Element;
106
+ }): react6.JSX.Element;
107
107
  /**
108
108
  * Which ways in this app offers. Requires an {@link AuthSettingsProvider}
109
109
  * above it.
@@ -1,4 +1,4 @@
1
- import * as react6 from "react";
1
+ import * as react5 from "react";
2
2
 
3
3
  //#region src/default-error-overlay.d.ts
4
4
  type Props = {
@@ -8,6 +8,6 @@ type Props = {
8
8
  declare function DefaultErrorOverlay({
9
9
  error,
10
10
  reset
11
- }: Props): react6.JSX.Element;
11
+ }: Props): react5.JSX.Element;
12
12
  //#endregion
13
13
  export { DefaultErrorOverlay };
package/dist/index.d.ts CHANGED
@@ -6,11 +6,11 @@ import { DateOptions, DateParts, DateSeparator, DateValue, FixedDateFormat, Form
6
6
  import { SoftrContext } from "./auth/context.js";
7
7
  import { AuthSettings } from "./auth/platform.js";
8
8
  import { checkAccess, defineVisibility, isAuthorized } from "./visibility/visibility.js";
9
- import { assertRouteAccess, authRedirectTarget, guardRoute, isAccessError, isAlreadyAuthenticatedError, isForbiddenError, isUnauthenticatedError } from "./visibility/route.js";
9
+ import { assertRouteAccess, guardRoute, isAccessError, isAlreadyAuthenticatedError, isForbiddenError, isUnauthenticatedError, toReturnPath } from "./visibility/route.js";
10
10
  import { AuthOptions, AuthSettingsProvider, getAuthSettings, getSoftrContext, initialSoftrContext, softrAuthMiddleware, useAuthSettings, useCurrentUser } from "./auth/isomorphic.js";
11
11
  import { softrMcpMiddleware } from "./mcp.js";
12
12
  import { useUserTimezone } from "./timezone.js";
13
13
  import { PwaInstall, PwaPlatform, SoftrPwaHead, usePwaInstall } from "./pwa.js";
14
14
  import { DefaultErrorOverlay } from "./default-error-overlay.js";
15
15
  import { Audience } from "./visibility/audience.js";
16
- export { type ActionPermissions, Audience, type AuthOptions, type AuthSettings, AuthSettingsProvider, type DateOptions, type DateParts, type DateSeparator, type DateValue, DefaultErrorOverlay, type FileAttachment, type FileItem, type FixedDateFormat, FormattedDate, type Formatting, FormattingScope, type PwaInstall, type PwaPlatform, type SoftrContext, type SoftrDatasources, SoftrPwaHead, type SoftrUserGroupId, type SoftrUserGroups, type User, type WeekDay, assertRouteAccess, authRedirectTarget, checkAccess, defineIntegration, defineMutation, defineQuery, defineVisibility, defineWorkflow, formatDate, formatDateTime, formatNumber, getAuthSettings, getSoftrContext, guardRoute, initialSoftrContext, isAccessError, isAlreadyAuthenticatedError, isAuthorized, isForbiddenError, isUnauthenticatedError, softrAuthMiddleware, softrMcpMiddleware, useAuthSettings, useCurrentUser, useFileUpload, useFormatting, usePwaInstall, useUserTimezone };
16
+ export { type ActionPermissions, Audience, type AuthOptions, type AuthSettings, AuthSettingsProvider, type DateOptions, type DateParts, type DateSeparator, type DateValue, DefaultErrorOverlay, type FileAttachment, type FileItem, type FixedDateFormat, FormattedDate, type Formatting, FormattingScope, type PwaInstall, type PwaPlatform, type SoftrContext, type SoftrDatasources, SoftrPwaHead, type SoftrUserGroupId, type SoftrUserGroups, type User, type WeekDay, assertRouteAccess, checkAccess, defineIntegration, defineMutation, defineQuery, defineVisibility, defineWorkflow, formatDate, formatDateTime, formatNumber, getAuthSettings, getSoftrContext, guardRoute, initialSoftrContext, isAccessError, isAlreadyAuthenticatedError, isAuthorized, isForbiddenError, isUnauthenticatedError, softrAuthMiddleware, softrMcpMiddleware, toReturnPath, useAuthSettings, useCurrentUser, useFileUpload, useFormatting, usePwaInstall, useUserTimezone };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { useFileUpload } from "./core/use-file-upload.js";
2
2
  import { defineIntegration, defineMutation, defineQuery, defineWorkflow } from "./action.js";
3
3
  import { checkAccess, defineVisibility, isAuthorized } from "./visibility/visibility.js";
4
- import { assertRouteAccess, authRedirectTarget, guardRoute, isAccessError, isAlreadyAuthenticatedError, isForbiddenError, isUnauthenticatedError } from "./visibility/route.js";
4
+ import { assertRouteAccess, guardRoute, isAccessError, isAlreadyAuthenticatedError, isForbiddenError, isUnauthenticatedError, toReturnPath } from "./visibility/route.js";
5
5
  import { FormattedDate, FormattingScope, formatDate, formatDateTime, formatNumber, useFormatting } from "./formatting.js";
6
6
  import { SoftrPwaHead, usePwaInstall } from "./pwa.js";
7
7
  import { AuthSettingsProvider, getAuthSettings, getSoftrContext, initialSoftrContext, softrAuthMiddleware, useAuthSettings, useCurrentUser } from "./auth/isomorphic.js";
@@ -10,4 +10,4 @@ import { useUserTimezone } from "./timezone.js";
10
10
  import { DefaultErrorOverlay } from "./default-error-overlay.js";
11
11
  import { Audience } from "./visibility/audience.js";
12
12
 
13
- export { Audience, AuthSettingsProvider, DefaultErrorOverlay, FormattedDate, FormattingScope, SoftrPwaHead, assertRouteAccess, authRedirectTarget, checkAccess, defineIntegration, defineMutation, defineQuery, defineVisibility, defineWorkflow, formatDate, formatDateTime, formatNumber, getAuthSettings, getSoftrContext, guardRoute, initialSoftrContext, isAccessError, isAlreadyAuthenticatedError, isAuthorized, isForbiddenError, isUnauthenticatedError, softrAuthMiddleware, softrMcpMiddleware, useAuthSettings, useCurrentUser, useFileUpload, useFormatting, usePwaInstall, useUserTimezone };
13
+ export { Audience, AuthSettingsProvider, DefaultErrorOverlay, FormattedDate, FormattingScope, SoftrPwaHead, assertRouteAccess, checkAccess, defineIntegration, defineMutation, defineQuery, defineVisibility, defineWorkflow, formatDate, formatDateTime, formatNumber, getAuthSettings, getSoftrContext, guardRoute, initialSoftrContext, isAccessError, isAlreadyAuthenticatedError, isAuthorized, isForbiddenError, isUnauthenticatedError, softrAuthMiddleware, softrMcpMiddleware, toReturnPath, useAuthSettings, useCurrentUser, useFileUpload, useFormatting, usePwaInstall, useUserTimezone };
package/dist/mcp.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DatasourceAction } from "./action.js";
2
- import * as _tanstack_react_start4 from "@tanstack/react-start";
2
+ import * as _tanstack_react_start0 from "@tanstack/react-start";
3
3
 
4
4
  //#region src/mcp.d.ts
5
5
 
@@ -31,6 +31,6 @@ type SoftrMcpOptions = {
31
31
  * ],
32
32
  * }))
33
33
  */
34
- declare function softrMcpMiddleware(options?: SoftrMcpOptions): _tanstack_react_start4.RequestMiddlewareAfterServer<{}, undefined, undefined>;
34
+ declare function softrMcpMiddleware(options?: SoftrMcpOptions): _tanstack_react_start0.RequestMiddlewareAfterServer<{}, undefined, undefined>;
35
35
  //#endregion
36
36
  export { softrMcpMiddleware };
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.0.64-alpha.0";
2
+ var version = "0.0.64-alpha.1";
3
3
 
4
4
  //#endregion
5
5
  export { version };
package/dist/pwa.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as react4 from "react";
1
+ import * as react3 from "react";
2
2
  import "@tanstack/react-start";
3
3
 
4
4
  //#region src/pwa.d.ts
@@ -23,7 +23,7 @@ import "@tanstack/react-start";
23
23
  * )
24
24
  * }
25
25
  */
26
- declare function SoftrPwaHead(): react4.FunctionComponentElement<react4.FragmentProps>;
26
+ declare function SoftrPwaHead(): react3.FunctionComponentElement<react3.FragmentProps>;
27
27
  type PwaPlatform = "ios" | "android" | "desktop";
28
28
  type PwaInstall = {
29
29
  available: false;
@@ -48,7 +48,7 @@ declare function isAlreadyAuthenticatedError(error: unknown): error is AccessErr
48
48
  *
49
49
  * Each reason falls back to the app's configured behaviour. Pass `overrides` to
50
50
  * change what any one reason does. A redirected signed-out visitor carries the
51
- * page they wanted, for {@link authRedirectTarget} to read back.
51
+ * page they wanted, for {@link toReturnPath} to read back.
52
52
  *
53
53
  * Throws a router redirect, so it belongs in `beforeLoad` and nowhere else.
54
54
  *
@@ -81,11 +81,19 @@ declare function assertRouteAccess<R extends VisibilityRule, O extends {
81
81
  };
82
82
  /**
83
83
  * Where to send a visitor who just signed in: the path a guard sent them away
84
- * from, or `home`. Anything but a path on this app is `home`.
84
+ * from, else where `auth.guard.alreadyAuthenticated` in softr.json points, else
85
+ * `/`. Anything but a path on this app counts as missing. It always reloads the
86
+ * page, so the router picks up the new session.
85
87
  *
86
88
  * @example
87
- * const target = authRedirectTarget(useLocation({ select: (l) => l.search }), "/")
89
+ * const location = useLocation()
90
+ * navigate(toReturnPath(location))
88
91
  */
89
- declare function authRedirectTarget(search: Record<string, unknown>, home: string): string;
92
+ declare function toReturnPath(location: {
93
+ search: Record<string, unknown>;
94
+ }): {
95
+ href: string;
96
+ reloadDocument: true;
97
+ };
90
98
  //#endregion
91
- export { assertRouteAccess, authRedirectTarget, guardRoute, isAccessError, isAlreadyAuthenticatedError, isForbiddenError, isUnauthenticatedError };
99
+ export { assertRouteAccess, guardRoute, isAccessError, isAlreadyAuthenticatedError, isForbiddenError, isUnauthenticatedError, toReturnPath };
@@ -60,7 +60,7 @@ function thrownFor(reason, overrides, location) {
60
60
  *
61
61
  * Each reason falls back to the app's configured behaviour. Pass `overrides` to
62
62
  * change what any one reason does. A redirected signed-out visitor carries the
63
- * page they wanted, for {@link authRedirectTarget} to read back.
63
+ * page they wanted, for {@link toReturnPath} to read back.
64
64
  *
65
65
  * Throws a router redirect, so it belongs in `beforeLoad` and nowhere else.
66
66
  *
@@ -91,16 +91,35 @@ function assertRouteAccess(visibility, opts) {
91
91
  }
92
92
  /**
93
93
  * Where to send a visitor who just signed in: the path a guard sent them away
94
- * from, or `home`. Anything but a path on this app is `home`.
94
+ * from, else where `auth.guard.alreadyAuthenticated` in softr.json points, else
95
+ * `/`. Anything but a path on this app counts as missing. It always reloads the
96
+ * page, so the router picks up the new session.
95
97
  *
96
98
  * @example
97
- * const target = authRedirectTarget(useLocation({ select: (l) => l.search }), "/")
99
+ * const location = useLocation()
100
+ * navigate(toReturnPath(location))
98
101
  */
99
- function authRedirectTarget(search, home) {
100
- const target = search[softrConfig().auth.routes.login.redirectParam];
101
- if (typeof target !== "string" || !target.startsWith("/")) return home;
102
- return new URL(target, "https://app.invalid").origin === "https://app.invalid" ? target : home;
102
+ function toReturnPath(location) {
103
+ const { auth } = softrConfig();
104
+ const target = location.search[auth.routes.login.redirectParam];
105
+ if (typeof target === "string" && target.startsWith("/") && new URL(target, "https://app.invalid").origin === "https://app.invalid") return {
106
+ href: target,
107
+ reloadDocument: true
108
+ };
109
+ const fallback = auth.guard.alreadyAuthenticated;
110
+ if (typeof fallback === "string") return {
111
+ href: fallback,
112
+ reloadDocument: true
113
+ };
114
+ if (fallback.strategy === "redirect") return {
115
+ href: fallback.to,
116
+ reloadDocument: true
117
+ };
118
+ return {
119
+ href: "/",
120
+ reloadDocument: true
121
+ };
103
122
  }
104
123
 
105
124
  //#endregion
106
- export { assertRouteAccess, authRedirectTarget, guardRoute, isAccessError, isAlreadyAuthenticatedError, isForbiddenError, isUnauthenticatedError, softrConfig };
125
+ export { assertRouteAccess, guardRoute, isAccessError, isAlreadyAuthenticatedError, isForbiddenError, isUnauthenticatedError, softrConfig, toReturnPath };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softr/sdk",
3
- "version": "0.0.64-alpha.0",
3
+ "version": "0.0.64-alpha.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -12,7 +12,7 @@ description: >-
12
12
  metadata:
13
13
  type: core
14
14
  library: softr-sdk
15
- library_version: 0.0.64-alpha.0
15
+ library_version: 0.0.64-alpha.1
16
16
  ---
17
17
 
18
18
  # Softr SDK core
@@ -14,12 +14,12 @@ description: >-
14
14
  with and its codes invalidCredentials, tooManyAttempts, emailTaken,
15
15
  invalidDetails, invalidCode, notAuthenticated, emailDomainNotAllowed and
16
16
  linkExpired, the two-step sign-up, why a successful sign-in does not always
17
- mean the visitor is signed in, and authRedirectTarget for where to land after
17
+ mean the visitor is signed in, and toReturnPath for where to land after
18
18
  signing in.
19
19
  metadata:
20
20
  type: sub-skill
21
21
  library: softr-sdk
22
- library_version: 0.0.64-alpha.0
22
+ library_version: 0.0.64-alpha.1
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.
@@ -202,20 +202,22 @@ An app with no authenticator app configured never answers `authenticatorRequired
202
202
 
203
203
  ## Landing after sign-in
204
204
 
205
- A guard that turns a signed-out visitor away sends them to the page `softr.json` names for that, usually the login page. The page they wanted rides along in the search param named for the login page. `authRedirectTarget` from `@softr/sdk` reads it back:
205
+ A guard that turns a signed-out visitor away sends them to the page `softr.json` names for that, usually the login page. The page they wanted rides along in the search param named for the login page. `toReturnPath` from `@softr/sdk` reads it back:
206
206
 
207
207
  ```tsx
208
208
  import { useLocation, useNavigate } from '@tanstack/react-router'
209
- import { authRedirectTarget } from '@softr/sdk'
209
+ import { toReturnPath } from '@softr/sdk'
210
210
 
211
- const target = authRedirectTarget(useLocation({ select: (l) => l.search }), '/')
211
+ const location = useLocation()
212
212
  const navigate = useNavigate()
213
213
 
214
214
  // once the visitor is signed in
215
- await navigate({ href: target })
215
+ await navigate(toReturnPath(location))
216
216
  ```
217
217
 
218
- It returns the page they wanted when it is a path on this app, and the `home` you pass otherwise. Follow its answer as is. Never read the param yourself: anyone can put a link to another site in it.
218
+ A loader that signs the visitor in throws it instead: `throw redirect(toReturnPath(location))`, with the loader's own `location`.
219
+
220
+ It goes to the page they wanted when that is a path on this app. Otherwise it goes where `auth.guard.alreadyAuthenticated` in `softr.json` points, or to `/` when that setting throws. It reloads the page, so the app sees the visitor as signed in. Pass its answer as is. Never read the param yourself: anyone can put a link to another site in it.
219
221
 
220
222
  Every page that ends a sign-in lands there: login, sign-up, the magic link, a password link and the second-factor step. A link from one auth page to another keeps the search param, or the target is lost on the way.
221
223
 
@@ -12,7 +12,7 @@ description: >-
12
12
  metadata:
13
13
  type: sub-skill
14
14
  library: softr-sdk
15
- library_version: 0.0.64-alpha.0
15
+ library_version: 0.0.64-alpha.1
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.
@@ -9,7 +9,7 @@ description: >-
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.64-alpha.0
12
+ library_version: 0.0.64-alpha.1
13
13
  ---
14
14
 
15
15
  # The current user
@@ -10,7 +10,7 @@ description: >-
10
10
  metadata:
11
11
  type: sub-skill
12
12
  library: softr-sdk
13
- library_version: 0.0.64-alpha.0
13
+ library_version: 0.0.64-alpha.1
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.64-alpha.0
13
+ library_version: 0.0.64-alpha.1
14
14
  ---
15
15
 
16
16
  # Dates, times and numbers
@@ -10,7 +10,7 @@ description: >-
10
10
  metadata:
11
11
  type: sub-skill
12
12
  library: softr-sdk
13
- library_version: 0.0.64-alpha.0
13
+ library_version: 0.0.64-alpha.1
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.
@@ -5,11 +5,11 @@ description: >-
5
5
  @softr/sdk, registered in src/start.ts ahead of softrAuthMiddleware, turns the
6
6
  actions you list into tools an assistant can call as the signed-in visitor.
7
7
  The list is explicit — an action left out is invisible over MCP. The app's own
8
- login must land on authRedirectTarget, or the approval step dead-ends.
8
+ login must land on toReturnPath, or the approval step dead-ends.
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.64-alpha.0
12
+ library_version: 0.0.64-alpha.1
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.
@@ -61,9 +61,9 @@ Reads first. Add a write only when the user asked for one, and name it back to t
61
61
 
62
62
  Connecting starts with an approval page the app serves. Most people hit it signed out, and that page cannot sign anyone in. It sends them to the login page named in `softr.json` (`auth.routes.login.path`), with the approval page in the search param named there (`auth.routes.login.redirectParam`).
63
63
 
64
- **The app's login has to send them back.** Every exit that ends a sign-in, the second-factor step included, goes to `authRedirectTarget(search, home)` instead of the home page. See [auth/SKILL.md](../auth/SKILL.md). Lose the search param anywhere along the way and the approval dies quietly: they land on the home page, the assistant never gets its answer, nothing reports an error.
64
+ **The app's login has to send them back.** Every exit that ends a sign-in, the second-factor step included, goes to `toReturnPath(location)` instead of the home page. See [auth/SKILL.md](../auth/SKILL.md). Lose the search param anywhere along the way and the approval dies quietly: they land on the home page, the assistant never gets its answer, nothing reports an error.
65
65
 
66
- `authRedirectTarget` returns only a path on this app. Never follow the param without it. The whole flow starts with a link someone was sent, so this is the one redirect an attacker can reliably aim.
66
+ `toReturnPath` only ever goes to a path on this app. Never follow the param without it. The whole flow starts with a link someone was sent, so this is the one redirect an attacker can reliably aim.
67
67
  ## Failure modes
68
68
 
69
69
  **Registering with no actions.** `softrMcpMiddleware()` is valid and serves an endpoint with nothing on it. The assistant connects, lists tools, finds only `whoami`. Pass the actions.
@@ -74,7 +74,7 @@ Connecting starts with an approval page the app serves. Most people hit it signe
74
74
 
75
75
  **A login that drops the return target.** Sign-in works, approval doesn't. The most common break, and silent — test the connect flow signed out, not only signed in.
76
76
 
77
- **Following the param without `authRedirectTarget`.** Anything that is not a path on this app is someone else's page, and `/\evil.test` is one of them. A prefix check misses it.
77
+ **Following the param without `toReturnPath`.** Anything that is not a path on this app is someone else's page, and `/\evil.test` is one of them. A prefix check misses it.
78
78
 
79
79
  ## References
80
80
 
@@ -9,7 +9,7 @@ description: >-
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.64-alpha.0
12
+ library_version: 0.0.64-alpha.1
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.64-alpha.0
14
+ library_version: 0.0.64-alpha.1
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.
@@ -13,7 +13,7 @@ description: >-
13
13
  metadata:
14
14
  type: sub-skill
15
15
  library: softr-sdk
16
- library_version: 0.0.64-alpha.0
16
+ library_version: 0.0.64-alpha.1
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.
@@ -9,7 +9,7 @@ description: >-
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.64-alpha.0
12
+ library_version: 0.0.64-alpha.1
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.64-alpha.0
16
+ library_version: 0.0.64-alpha.1
17
17
  ---
18
18
 
19
19
  **Boundary.** `defineVisibility`, the access functions and the error predicates come from `@softr/sdk` and work anywhere.
@@ -138,7 +138,7 @@ Every key is required, because nothing fills in one the file leaves out:
138
138
  | `forbidden` | Signed in, in none of the named groups | `guard.forbidden` |
139
139
  | `alreadyAuthenticated` | Signed-in visitor on a signed-out-only route | `guard.alreadyAuthenticated` |
140
140
 
141
- `guard.unauthenticated` is where signed-out visitors go. It is usually the same as `routes.login.path`, but it can be another page, such as a public landing page. Every `unauthenticated` redirect, an override's included, carries the page they wanted under `routes.login.redirectParam`. The login page reads it back with `authRedirectTarget`. See [auth/SKILL.md](../auth/SKILL.md).
141
+ `guard.unauthenticated` is where signed-out visitors go. It is usually the same as `routes.login.path`, but it can be another page, such as a public landing page. Every `unauthenticated` redirect, an override's included, carries the page they wanted under `routes.login.redirectParam`. The login page reads it back with `toReturnPath`. See [auth/SKILL.md](../auth/SKILL.md).
142
142
 
143
143
  `auth.routes` names the app's sign-in pages. To move one, rename its route file and edit its `path` in `auth.routes` to match. When `guard.unauthenticated` points at the login page, edit it too.
144
144
 
@@ -9,7 +9,7 @@ description: >-
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.64-alpha.0
12
+ library_version: 0.0.64-alpha.1
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.