@softr/sdk 0.0.78-alpha.2 → 0.0.78

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.
Files changed (49) hide show
  1. package/dist/action.d.ts +1 -34
  2. package/dist/action.js +1 -12
  3. package/dist/assertions.js +1 -1
  4. package/dist/auth/methods/profile.d.ts +5 -5
  5. package/dist/auth/methods/profile.js +2 -2
  6. package/dist/auth/platform.d.ts +0 -1
  7. package/dist/auth/platform.js +36 -14
  8. package/dist/auth/session.js +1 -1
  9. package/dist/auth/user.d.ts +8 -1
  10. package/dist/auth/user.js +2 -1
  11. package/dist/cli.js +38 -12
  12. package/dist/codegen/datasource-docs.js +92 -1
  13. package/dist/codegen/datasource-types.js +16 -1
  14. package/dist/codegen/fetch-metadata.js +27 -11
  15. package/dist/codegen/softr-env.js +2 -1
  16. package/dist/cookies.js +10 -2
  17. package/dist/formatting.d.ts +1 -1
  18. package/dist/formatting.js +1 -1
  19. package/dist/index.d.ts +3 -4
  20. package/dist/index.js +2 -2
  21. package/dist/mcp.d.ts +2 -2
  22. package/dist/mcp.js +2 -2
  23. package/dist/package.js +1 -1
  24. package/dist/pwa.js +1 -1
  25. package/dist/route/route.js +1 -1
  26. package/dist/server.d.ts +1 -2
  27. package/dist/server.js +1 -2
  28. package/dist/softr-config.schema.js +52 -24
  29. package/dist/vite.js +1 -19
  30. package/package.json +1 -1
  31. package/schema/softr.schema.json +13 -0
  32. package/skills/softr-sdk/SKILL.md +4 -12
  33. package/skills/softr-sdk/auth/SKILL.md +1 -1
  34. package/skills/softr-sdk/charts-and-metrics/SKILL.md +1 -1
  35. package/skills/softr-sdk/comments/SKILL.md +1 -1
  36. package/skills/softr-sdk/current-user/SKILL.md +1 -1
  37. package/skills/softr-sdk/file-uploads/SKILL.md +1 -1
  38. package/skills/softr-sdk/formatting/SKILL.md +1 -1
  39. package/skills/softr-sdk/integrations/SKILL.md +4 -4
  40. package/skills/softr-sdk/mcp/SKILL.md +1 -1
  41. package/skills/softr-sdk/pwa/SKILL.md +1 -1
  42. package/skills/softr-sdk/record-mutations/SKILL.md +1 -1
  43. package/skills/softr-sdk/record-queries/SKILL.md +1 -1
  44. package/skills/softr-sdk/region-visibility/SKILL.md +1 -1
  45. package/skills/softr-sdk/routes-and-visibility/SKILL.md +1 -1
  46. package/skills/softr-sdk/workflows/SKILL.md +1 -1
  47. package/dist/core/ai.d.ts +0 -76
  48. package/dist/core/ai.js +0 -64
  49. package/skills/softr-sdk/ai/SKILL.md +0 -161
package/dist/index.d.ts CHANGED
@@ -1,9 +1,8 @@
1
- import { SoftrUserGroupId, SoftrUserGroups, User } from "./auth/user.js";
2
- import { ActionPermissions, SoftrDatasources, defineAiAction, defineComments, defineIntegration, defineMutation, defineQuery, defineWorkflow } from "./action.js";
1
+ import { SoftrUserGroupId, SoftrUserGroups, SoftrUserProperties, User } from "./auth/user.js";
2
+ import { ActionPermissions, SoftrDatasources, defineComments, defineIntegration, defineMutation, defineQuery, defineWorkflow } from "./action.js";
3
3
  import { FileAttachment } from "./core/file-upload.js";
4
4
  import { FileItem, useFileUpload } from "./core/use-file-upload.js";
5
5
  import { CommentAttachmentInput, CommentMention, CommentThread, CommentView } from "./comments/comments.js";
6
- import { AiAnswer, AiFile, AiGenerateInit, AiMessage, AiUsage } from "./core/ai.js";
7
6
  import { DateOptions, DateParts, DateSeparator, DateValue, FixedDateFormat, FormattedDate, Formatting, FormattingScope, WeekDay, formatDate, formatDateTime, formatNumber, useFormatting } from "./formatting.js";
8
7
  import { SoftrContext } from "./auth/context.js";
9
8
  import { AuthSettings } from "./auth/platform.js";
@@ -19,4 +18,4 @@ import { useUserTimezone } from "./timezone.js";
19
18
  import { PwaInstall, PwaPlatform, SoftrPwaHead, usePwaInstall } from "./pwa.js";
20
19
  import { DefaultErrorOverlay } from "./default-error-overlay.js";
21
20
  import { Audience } from "./route/audience.js";
22
- export { type ActionPermissions, type AiAnswer, type AiFile, type AiGenerateInit, type AiMessage, type AiUsage, Audience, type AuthOptions, type AuthSettings, AuthSettingsProvider, type CommentAttachmentInput, type CommentMention, type CommentThread, type CommentView, type DateOptions, type DateParts, type DateSeparator, type DateValue, DefaultErrorOverlay, type FieldShape, type FileAttachment, type FileItem, type FixedDateFormat, FormattedDate, type Formatting, FormattingScope, type Page, type PageSettings, type PwaInstall, type PwaPlatform, type SoftrContext, type SoftrDatasources, type SoftrFieldRegistry, SoftrPwaHead, type SoftrUserGroupId, type SoftrUserGroups, type User, type WeekDay, assertRouteAccess, checkAccess, defineAiAction, defineComments, defineIntegration, defineMutation, definePage, defineQuery, defineVisibility, defineWorkflow, fieldShape, formatDate, formatDateTime, formatDuration, formatField, formatNumber, formatValue, getAuthSettings, getSoftrContext, guardRoute, initialSoftrContext, isAccessError, isAlreadyAuthenticatedError, isAuthorized, isForbiddenError, isUnauthenticatedError, pageHead, registerSoftrFields, softrAuthMiddleware, softrMcpMiddleware, toReturnPath, useAuthSettings, useCurrentUser, useFileUpload, useFormatting, useOverrides, usePwaInstall, useUserTimezone };
21
+ export { type ActionPermissions, Audience, type AuthOptions, type AuthSettings, AuthSettingsProvider, type CommentAttachmentInput, type CommentMention, type CommentThread, type CommentView, type DateOptions, type DateParts, type DateSeparator, type DateValue, DefaultErrorOverlay, type FieldShape, type FileAttachment, type FileItem, type FixedDateFormat, FormattedDate, type Formatting, FormattingScope, type Page, type PageSettings, type PwaInstall, type PwaPlatform, type SoftrContext, type SoftrDatasources, type SoftrFieldRegistry, SoftrPwaHead, type SoftrUserGroupId, type SoftrUserGroups, type SoftrUserProperties, type User, type WeekDay, assertRouteAccess, checkAccess, defineComments, defineIntegration, defineMutation, definePage, defineQuery, defineVisibility, defineWorkflow, fieldShape, formatDate, formatDateTime, formatDuration, formatField, formatNumber, formatValue, getAuthSettings, getSoftrContext, guardRoute, initialSoftrContext, isAccessError, isAlreadyAuthenticatedError, isAuthorized, isForbiddenError, isUnauthenticatedError, pageHead, registerSoftrFields, softrAuthMiddleware, softrMcpMiddleware, toReturnPath, useAuthSettings, useCurrentUser, useFileUpload, useFormatting, useOverrides, usePwaInstall, useUserTimezone };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useFileUpload } from "./core/use-file-upload.js";
2
- import { defineAiAction, defineComments, defineIntegration, defineMutation, defineQuery, defineWorkflow } from "./action.js";
2
+ import { defineComments, defineIntegration, defineMutation, defineQuery, defineWorkflow } from "./action.js";
3
3
  import { checkAccess, defineVisibility, isAuthorized } from "./route/visibility.js";
4
4
  import { assertRouteAccess, guardRoute, isAccessError, isAlreadyAuthenticatedError, isForbiddenError, isUnauthenticatedError } from "./route/route.js";
5
5
  import { definePage, pageHead } from "./route/page.js";
@@ -14,4 +14,4 @@ import { useUserTimezone } from "./timezone.js";
14
14
  import { DefaultErrorOverlay } from "./default-error-overlay.js";
15
15
  import { Audience } from "./route/audience.js";
16
16
 
17
- export { Audience, AuthSettingsProvider, DefaultErrorOverlay, FormattedDate, FormattingScope, SoftrPwaHead, assertRouteAccess, checkAccess, defineAiAction, defineComments, defineIntegration, defineMutation, definePage, defineQuery, defineVisibility, defineWorkflow, fieldShape, formatDate, formatDateTime, formatDuration, formatField, formatNumber, formatValue, getAuthSettings, getSoftrContext, guardRoute, initialSoftrContext, isAccessError, isAlreadyAuthenticatedError, isAuthorized, isForbiddenError, isUnauthenticatedError, pageHead, registerSoftrFields, softrAuthMiddleware, softrMcpMiddleware, toReturnPath, useAuthSettings, useCurrentUser, useFileUpload, useFormatting, useOverrides, usePwaInstall, useUserTimezone };
17
+ export { Audience, AuthSettingsProvider, DefaultErrorOverlay, FormattedDate, FormattingScope, SoftrPwaHead, assertRouteAccess, checkAccess, defineComments, defineIntegration, defineMutation, definePage, defineQuery, defineVisibility, defineWorkflow, fieldShape, formatDate, formatDateTime, formatDuration, formatField, formatNumber, formatValue, getAuthSettings, getSoftrContext, guardRoute, initialSoftrContext, isAccessError, isAlreadyAuthenticatedError, isAuthorized, isForbiddenError, isUnauthenticatedError, pageHead, registerSoftrFields, softrAuthMiddleware, softrMcpMiddleware, toReturnPath, useAuthSettings, useCurrentUser, useFileUpload, useFormatting, useOverrides, usePwaInstall, useUserTimezone };
package/dist/mcp.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DatasourceAction } from "./action.js";
2
- import * as _tanstack_react_start0 from "@tanstack/react-start";
2
+ import * as _tanstack_react_start5 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_start0.RequestMiddlewareAfterServer<{}, undefined, undefined>;
34
+ declare function softrMcpMiddleware(options?: SoftrMcpOptions): _tanstack_react_start5.RequestMiddlewareAfterServer<{}, undefined, undefined>;
35
35
  //#endregion
36
36
  export { softrMcpMiddleware };
package/dist/mcp.js CHANGED
@@ -2,12 +2,12 @@ import { assert } from "./assertions.js";
2
2
  import { fetch } from "./fetch.js";
3
3
  import { sessionConfig } from "./auth/config.js";
4
4
  import { readSessionToken, withSessionToken } from "./auth/session.js";
5
+ import { softrConfig } from "./softr-config.js";
5
6
  import { makePlatform, platformApiUrl } from "./auth/platform.js";
6
7
  import { isDatasourceAction } from "./action.js";
7
- import { softrConfig } from "./softr-config.js";
8
8
  import { ds } from "./datasource/datasource.js";
9
- import { createMiddleware } from "@tanstack/react-start";
10
9
  import { z } from "zod";
10
+ import { createMiddleware } from "@tanstack/react-start";
11
11
 
12
12
  //#region src/mcp.ts
13
13
  const MCP_PATH = "/_softr/mcp";
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.0.78-alpha.2";
2
+ var version = "0.0.78";
3
3
 
4
4
  //#endregion
5
5
  export { version };
package/dist/pwa.js CHANGED
@@ -2,9 +2,9 @@ import { logger } from "./logger.js";
2
2
  import { fetch } from "./fetch.js";
3
3
  import { sessionConfig } from "./auth/config.js";
4
4
  import { platformApiUrl } from "./auth/platform.js";
5
+ import { z } from "zod";
5
6
  import { Fragment, createElement, useEffect, useState } from "react";
6
7
  import { createMiddleware } from "@tanstack/react-start";
7
- import { z } from "zod";
8
8
 
9
9
  //#region src/pwa.ts
10
10
  const MANIFEST_PATH = "/manifest.webmanifest";
@@ -1,6 +1,6 @@
1
1
  import { panic } from "../assertions.js";
2
- import { ACCESS_REASONS, checkRule } from "./visibility.js";
3
2
  import { softrConfig } from "../softr-config.js";
3
+ import { ACCESS_REASONS, checkRule } from "./visibility.js";
4
4
  import { redirect } from "@tanstack/react-router";
5
5
 
6
6
  //#region src/route/route.ts
package/dist/server.d.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  import { upload } from "./core/file-upload.js";
2
2
  import { comments } from "./comments/comments.js";
3
- import { AiError, ai } from "./core/ai.js";
4
3
  import { auth } from "./auth/server.js";
5
4
  import { FieldChoice, RecordOption, RecordOptions } from "./core/options.js";
6
5
  import { ds } from "./datasource/datasource.js";
7
6
  import { workflow } from "./core/workflow.js";
8
7
  import { integration } from "./core/integration.js";
9
8
  import "@tanstack/react-start/server-only";
10
- export { AiError, type FieldChoice, type RecordOption, type RecordOptions, ai, auth, comments, ds, integration, upload, workflow };
9
+ export { type FieldChoice, type RecordOption, type RecordOptions, auth, comments, ds, integration, upload, workflow };
package/dist/server.js CHANGED
@@ -3,8 +3,7 @@ import { ds } from "./datasource/datasource.js";
3
3
  import { auth } from "./auth/server.js";
4
4
  import { workflow } from "./core/workflow.js";
5
5
  import { integration } from "./core/integration.js";
6
- import { AiError, ai } from "./core/ai.js";
7
6
  import { comments } from "./comments/comments.js";
8
7
  import "@tanstack/react-start/server-only";
9
8
 
10
- export { AiError, ai, auth, comments, ds, integration, upload, workflow };
9
+ export { auth, comments, ds, integration, upload, workflow };
@@ -1,6 +1,17 @@
1
+ import { throwAtCaller } from "./assertions.js";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import path from "node:path";
1
4
  import { z } from "zod";
2
5
 
3
6
  //#region src/softr-config.schema.ts
7
+ const SOFTR_CONFIG_FILE = "softr.json";
8
+ /** The cause is in softr.json, so the app's author can fix it. */
9
+ var SoftrConfigError = class extends Error {
10
+ name = "SoftrConfigError";
11
+ };
12
+ function panicConfig(message) {
13
+ throwAtCaller(panicConfig, new SoftrConfigError(message));
14
+ }
4
15
  const AccessBehaviour = z.union([
5
16
  z.string(),
6
17
  z.object({
@@ -9,32 +20,49 @@ const AccessBehaviour = z.union([
9
20
  }),
10
21
  z.object({ strategy: z.literal("throw") })
11
22
  ], { error: "must be a path like \"/login\", { \"strategy\": \"redirect\", \"to\": \"/login\" } or { \"strategy\": \"throw\" }" });
12
- const AppPath = z.string().refine((path) => path.startsWith("/") && !path.includes("?") && !path.includes("#") && new URL(path, "https://app.invalid").origin === "https://app.invalid", { error: "must be a path on this app like \"/login\", with no \"?\" or \"#\"" });
13
- const SoftrConfigSchema = z.object({ auth: z.strictObject({
14
- routes: z.strictObject({
15
- login: z.strictObject({
16
- path: AppPath,
17
- redirectParam: z.string()
23
+ const AppPath = z.string().refine((path$1) => path$1.startsWith("/") && !path$1.includes("?") && !path$1.includes("#") && new URL(path$1, "https://app.invalid").origin === "https://app.invalid", { error: "must be a path on this app like \"/login\", with no \"?\" or \"#\"" });
24
+ const SoftrConfigSchema = z.object({
25
+ auth: z.strictObject({
26
+ routes: z.strictObject({
27
+ login: z.strictObject({
28
+ path: AppPath,
29
+ redirectParam: z.string()
30
+ }),
31
+ resetPassword: z.strictObject({
32
+ path: AppPath,
33
+ tokenParam: z.string()
34
+ }),
35
+ acceptInvitation: z.strictObject({
36
+ path: AppPath,
37
+ tokenParam: z.string()
38
+ }),
39
+ magicLink: z.strictObject({
40
+ path: AppPath,
41
+ tokenParam: z.string()
42
+ })
18
43
  }),
19
- resetPassword: z.strictObject({
20
- path: AppPath,
21
- tokenParam: z.string()
22
- }),
23
- acceptInvitation: z.strictObject({
24
- path: AppPath,
25
- tokenParam: z.string()
26
- }),
27
- magicLink: z.strictObject({
28
- path: AppPath,
29
- tokenParam: z.string()
44
+ guard: z.strictObject({
45
+ unauthenticated: AccessBehaviour,
46
+ forbidden: AccessBehaviour,
47
+ alreadyAuthenticated: AccessBehaviour
30
48
  })
31
49
  }),
32
- guard: z.strictObject({
33
- unauthenticated: AccessBehaviour,
34
- forbidden: AccessBehaviour,
35
- alreadyAuthenticated: AccessBehaviour
36
- })
37
- }) });
50
+ user: z.strictObject({ properties: z.record(z.string(), z.string()) }).optional()
51
+ });
52
+ function loadSoftrConfig(root) {
53
+ const file = path.join(root, SOFTR_CONFIG_FILE);
54
+ if (!existsSync(file)) panicConfig(`${SOFTR_CONFIG_FILE} is missing from ${root}`);
55
+ const source = readFileSync(file, "utf8");
56
+ let parsed;
57
+ try {
58
+ parsed = JSON.parse(source);
59
+ } catch (error) {
60
+ panicConfig(`${SOFTR_CONFIG_FILE} is not valid JSON: ${error instanceof Error ? error.message : error}`);
61
+ }
62
+ const result = SoftrConfigSchema.safeParse(parsed);
63
+ if (!result.success) panicConfig(`${SOFTR_CONFIG_FILE} does not match the schema:\n\n${z.prettifyError(result.error)}`);
64
+ return result.data;
65
+ }
38
66
 
39
67
  //#endregion
40
- export { SoftrConfigSchema };
68
+ export { SOFTR_CONFIG_FILE, SoftrConfigError, loadSoftrConfig, panicConfig };
package/dist/vite.js CHANGED
@@ -1,12 +1,8 @@
1
- import { assert, panic } from "./assertions.js";
2
- import { SoftrConfigSchema } from "./softr-config.schema.js";
3
- import { existsSync, readFileSync } from "node:fs";
1
+ import { SOFTR_CONFIG_FILE, loadSoftrConfig } from "./softr-config.schema.js";
4
2
  import path from "node:path";
5
- import { z } from "zod";
6
3
  import { pathToFileURL } from "node:url";
7
4
 
8
5
  //#region src/vite.ts
9
- const SOFTR_CONFIG_FILE = "softr.json";
10
6
  function softr() {
11
7
  return [softrConfig(), editEngine()];
12
8
  }
@@ -27,20 +23,6 @@ function softrConfig() {
27
23
  }
28
24
  };
29
25
  }
30
- function loadSoftrConfig(root) {
31
- const file = path.join(root, SOFTR_CONFIG_FILE);
32
- assert(existsSync(file), `${SOFTR_CONFIG_FILE} is missing from ${root}`);
33
- const source = readFileSync(file, "utf8");
34
- let parsed;
35
- try {
36
- parsed = JSON.parse(source);
37
- } catch (error) {
38
- panic(`${SOFTR_CONFIG_FILE} is not valid JSON: ${error instanceof Error ? error.message : error}`);
39
- }
40
- const result = SoftrConfigSchema.safeParse(parsed);
41
- if (!result.success) panic(`${SOFTR_CONFIG_FILE} does not match the schema:\n\n${z.prettifyError(result.error)}`);
42
- return result.data;
43
- }
44
26
  /**
45
27
  * The edit engine is provided by the pod runtime rather than an app dependency: it is never in the
46
28
  * app's `package.json` and never in its `node_modules`. The pod points `SOFTR_EDIT_ENGINE_DIR` at
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softr/sdk",
3
- "version": "0.0.78-alpha.2",
3
+ "version": "0.0.78",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" },
@@ -54,6 +54,19 @@
54
54
  },
55
55
  "required": ["routes", "guard"],
56
56
  "additionalProperties": false
57
+ },
58
+ "user": {
59
+ "type": "object",
60
+ "description": "The signed-in user.",
61
+ "properties": {
62
+ "properties": {
63
+ "type": "object",
64
+ "description": "Fields of the users table the app reads as user.properties, keyed by the name the app uses. Each value is a field id. No other field leaves the server.",
65
+ "additionalProperties": { "type": "string" }
66
+ }
67
+ },
68
+ "required": ["properties"],
69
+ "additionalProperties": false
57
70
  }
58
71
  },
59
72
  "required": ["auth"],
@@ -8,7 +8,7 @@ description: >-
8
8
  metadata:
9
9
  type: core
10
10
  library: softr-sdk
11
- library_version: 0.0.77
11
+ library_version: 0.0.78
12
12
  ---
13
13
 
14
14
  # Softr SDK core
@@ -18,7 +18,6 @@ metadata:
18
18
  > **CRITICAL**: The SDK is split across four entrypoints and the split is enforced. `ds` and `auth` exist only on `@softr/sdk/server` and only run inside a `createServerFn` handler. Importing them anywhere else fails the build.
19
19
  > **CRITICAL**: Every route file must declare its audience with `defineVisibility` and guard itself with that rule in `beforeLoad`. There is no default and nothing is inherited from a sibling.
20
20
  > **CRITICAL**: The app is already wired. `src/start.ts`, `src/router.tsx` and `src/routes/__root.tsx` register `softrAuthMiddleware` and resolve the session. Do not add a second registration of it. `softrMcpMiddleware` is the one thing an app adds to `src/start.ts` itself — load the `softr-sdk/mcp` sub-skill before doing so.
21
- > **CRITICAL**: `ai.generate` and `ai.stream` live on `@softr/sdk/server` and only run inside a `createServerFn` handler. Use `defineAiAction` from `@softr/sdk` to declare the action with the model it runs on, an id copied from the `ai_list_models` tool. Softr runs the model on its own keys; never ask the user for an API key for the app's own AI feature. Load the `softr-sdk/ai` sub-skill for the full pattern.
22
21
  > **CRITICAL**: `workflow.trigger` lives on `@softr/sdk/server` and only runs inside a `createServerFn` handler. Use `defineWorkflow` from `@softr/sdk` to declare the action, then pass it to `workflow.trigger`. Load the `softr-sdk/workflows` sub-skill for the full pattern.
23
22
 
24
23
  Each sub-skill stands alone for its task and repeats the boundary above, so most work needs **one** sub-skill and not this file. Load this one when you are unsure which sub-skill applies, or when the wiring below is what you are touching. Add a second sub-skill only when the work genuinely crosses that boundary: a page that lists records behind a group restriction needs `record-queries` plus `routes-and-visibility`.
@@ -27,8 +26,8 @@ Each sub-skill stands alone for its task and repeats the boundary above, so most
27
26
 
28
27
  | Import | Runs | Holds |
29
28
  | --- | --- | --- |
30
- | `@softr/sdk` | Both | `defineQuery`, `defineMutation`, `defineComments`, `defineAiAction`, `defineVisibility`, `guardRoute`, `assertRouteAccess`, `isAuthorized`, `checkAccess`, `Audience`, `useCurrentUser`, `useUserTimezone`, `FormattedDate`, `FormattingScope`, `useFormatting`, `formatDate`, `formatDateTime`, `formatNumber`, `formatValue`, `formatField`, `fieldShape`, `registerSoftrFields`, `useOverrides`, `SoftrPwaHead`, `usePwaInstall`, `getSoftrContext`, `initialSoftrContext`, `softrAuthMiddleware`, the error predicates, and every type |
31
- | `@softr/sdk/server` | Server only | `ds`, `auth`, `workflow`, `integration`, `comments`, `ai` |
29
+ | `@softr/sdk` | Both | `defineQuery`, `defineMutation`, `defineComments`, `defineVisibility`, `guardRoute`, `assertRouteAccess`, `isAuthorized`, `checkAccess`, `Audience`, `useCurrentUser`, `useUserTimezone`, `FormattedDate`, `FormattingScope`, `useFormatting`, `formatDate`, `formatDateTime`, `formatNumber`, `formatValue`, `formatField`, `fieldShape`, `registerSoftrFields`, `useOverrides`, `SoftrPwaHead`, `usePwaInstall`, `getSoftrContext`, `initialSoftrContext`, `softrAuthMiddleware`, the error predicates, and every type |
30
+ | `@softr/sdk/server` | Server only | `ds`, `auth`, `workflow`, `integration`, `comments` |
32
31
  | `@softr/sdk/client` | Browser only | `useFileUpload`, `useCommentComposer` |
33
32
  | `@softr/sdk/vite` | Build | `softr()`, already in `vite.config.ts` |
34
33
 
@@ -44,8 +43,7 @@ Each sub-skill stands alone for its task and repeats the boundary above, so most
44
43
  | Create, update or delete a record | [record-mutations/SKILL.md](./record-mutations/SKILL.md) |
45
44
  | Trigger a Softr workflow | [workflows/SKILL.md](./workflows/SKILL.md) |
46
45
  | Sign a visitor in, out, or up | [auth/SKILL.md](./auth/SKILL.md) |
47
- | Ask an AI model: a summary, an extraction from a document, a classification, a draft, a chat | [ai/SKILL.md](./ai/SKILL.md) |
48
- | Call a connected third-party API (Slack, Stripe, Gmail, …), or an AI model on the user's own provider account | [integrations/SKILL.md](./integrations/SKILL.md) |
46
+ | Call a connected third-party API (Slack, Stripe, Gmail, …) | [integrations/SKILL.md](./integrations/SKILL.md) |
49
47
  | Open the app to AI assistants over MCP | [mcp/SKILL.md](./mcp/SKILL.md) |
50
48
  | Let visitors install the app on their home screen | [pwa/SKILL.md](./pwa/SKILL.md) |
51
49
  | Show a date, a time or a number | [formatting/SKILL.md](./formatting/SKILL.md) |
@@ -78,15 +76,9 @@ Triggering a Softr workflow from a button or form?
78
76
  Wiring a login, signup, or sign-out?
79
77
  load softr-sdk/auth
80
78
 
81
- Asking an AI model for anything: a summary, fields out of a document, a classification, a draft, a chat?
82
- load softr-sdk/ai — Softr runs the model on its own keys, nothing to connect; pick the model from ai_list_models
83
-
84
79
  Calling a connected third-party API (Slack, Stripe, Gmail, Google Calendar, …)?
85
80
  load softr-sdk/integrations
86
81
 
87
- Running a model on the user's OWN provider account, because they asked for that?
88
- load softr-sdk/integrations: its "Calling an AI model" section
89
-
90
82
  Opening the app to an AI assistant over MCP?
91
83
  load softr-sdk/mcp
92
84
 
@@ -9,7 +9,7 @@ description: >-
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.77
12
+ library_version: 0.0.78
13
13
  ---
14
14
 
15
15
  **Boundary.** `auth` lives on `@softr/sdk/server` and runs only inside a `createServerFn` handler — importing it into a component fails the build.
@@ -9,7 +9,7 @@ description: >-
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.77
12
+ library_version: 0.0.78
13
13
  ---
14
14
 
15
15
  **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.
@@ -8,7 +8,7 @@ description: >-
8
8
  metadata:
9
9
  type: sub-skill
10
10
  library: softr-sdk
11
- library_version: 0.0.77
11
+ library_version: 0.0.78
12
12
  ---
13
13
 
14
14
  **Boundary.** `comments` lives on `@softr/sdk/server` and runs only inside a `createServerFn` handler. Importing it into a component fails the build. `useCommentComposer` lives on `@softr/sdk/client` and runs in components. `defineComments` comes 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.77
12
+ library_version: 0.0.78
13
13
  ---
14
14
 
15
15
  # The current user
@@ -7,7 +7,7 @@ description: >-
7
7
  metadata:
8
8
  type: sub-skill
9
9
  library: softr-sdk
10
- library_version: 0.0.77
10
+ library_version: 0.0.78
11
11
  ---
12
12
 
13
13
  **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.
@@ -8,7 +8,7 @@ description: >-
8
8
  metadata:
9
9
  type: sub-skill
10
10
  library: softr-sdk
11
- library_version: 0.0.77
11
+ library_version: 0.0.78
12
12
  ---
13
13
 
14
14
  # Dates, times and numbers
@@ -7,7 +7,7 @@ description: >-
7
7
  metadata:
8
8
  type: sub-skill
9
9
  library: softr-sdk
10
- library_version: 0.0.77
10
+ library_version: 0.0.78
11
11
  ---
12
12
 
13
13
  **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.
@@ -126,9 +126,9 @@ Do not add an `Authorization` key to `headers` — Softr injects the integration
126
126
 
127
127
  ## Calling an AI model
128
128
 
129
- **This is the path for the user's own provider account only.** An AI feature of the app's own — a summary, an extraction, a chat — runs on Softr's keys through an AI action, with nothing to connect and no key to ask for: load the `ai` sub-skill for it. Call a model through an integration only when the user asked to use their own OpenAI, Anthropic or Gemini account, and had it connected.
130
-
131
- An AI integration is called like any other. For `ANTHROPIC_AI`, the action is `POST` on
129
+ A button on one record that hands its fields to the model and shows the answer is the `ai-action` pattern:
130
+ it carries this call and the model id, so write the call by hand only for another shape — a batch, a chat,
131
+ a field the model fills on create. An AI integration is called like any other. For `ANTHROPIC_AI`, the action is `POST` on
132
132
  `https://api.anthropic.com/v1/messages`, the request carries the `anthropic-version` header and a body of
133
133
  `model`, `max_tokens`, `system` and `messages`, and the answer's text is in `content[].text`:
134
134
 
@@ -9,7 +9,7 @@ description: >-
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.77
12
+ library_version: 0.0.78
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.
@@ -9,7 +9,7 @@ description: >-
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.77
12
+ library_version: 0.0.78
13
13
  ---
14
14
 
15
15
  # Installing the app
@@ -8,7 +8,7 @@ description: >-
8
8
  metadata:
9
9
  type: sub-skill
10
10
  library: softr-sdk
11
- library_version: 0.0.77
11
+ library_version: 0.0.78
12
12
  ---
13
13
 
14
14
  **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.
@@ -9,7 +9,7 @@ description: >-
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.77
12
+ library_version: 0.0.78
13
13
  ---
14
14
 
15
15
  **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.77
12
+ library_version: 0.0.78
13
13
  ---
14
14
 
15
15
  **Boundary.** `Audience` and `defineVisibility` come from `@softr/sdk` and work anywhere.
@@ -8,7 +8,7 @@ description: >-
8
8
  metadata:
9
9
  type: sub-skill
10
10
  library: softr-sdk
11
- library_version: 0.0.77
11
+ library_version: 0.0.78
12
12
  ---
13
13
 
14
14
  **Boundary.** `defineVisibility`, the access functions and the error predicates come from `@softr/sdk` and work anywhere.
@@ -9,7 +9,7 @@ description: >-
9
9
  metadata:
10
10
  type: sub-skill
11
11
  library: softr-sdk
12
- library_version: 0.0.77
12
+ library_version: 0.0.78
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.
package/dist/core/ai.d.ts DELETED
@@ -1,76 +0,0 @@
1
- import { AiAction } from "../action.js";
2
-
3
- //#region src/core/ai.d.ts
4
- /** A document or image the model reads, by the URL the app holds it under: one of its own uploads, as a rule. */
5
- interface AiFile {
6
- /** An https URL Softr may fetch. */
7
- url: string;
8
- /** The file's type when the URL does not say, such as `application/pdf`. */
9
- mimeType?: string;
10
- }
11
- /** One earlier turn of a conversation. */
12
- interface AiMessage {
13
- role: 'user' | 'assistant';
14
- content: string;
15
- }
16
- interface AiGenerateInit {
17
- /** What the model is and the rules it answers under. */
18
- system?: string;
19
- /** The earlier turns of a conversation, oldest first, before the prompt. Leave out for a one-shot call. */
20
- messages?: ReadonlyArray<AiMessage>;
21
- /** What it is asked, with the facts it is given. */
22
- prompt: string;
23
- /**
24
- * A JSON schema the answer has to match. With it, the model is asked for JSON only and the
25
- * answer comes back parsed in `json` as well as written in `text`.
26
- */
27
- schema?: Record<string, unknown>;
28
- /** Documents and images the model reads alongside the prompt. */
29
- files?: ReadonlyArray<AiFile>;
30
- /** A cap on the answer's length, in tokens; Softr caps it further. */
31
- maxOutputTokens?: number;
32
- /**
33
- * How hard a reasoning model thinks before it answers. A model that does not reason ignores it;
34
- * the catalog says which do. Left out, the provider's default applies.
35
- */
36
- reasoning?: 'low' | 'medium' | 'high';
37
- /** How varied the answer is, 0 to 1: 0 for an extraction, higher for a draft. A low default when left out. */
38
- temperature?: number;
39
- signal?: AbortSignal;
40
- }
41
- interface AiUsage {
42
- inputTokens: number;
43
- outputTokens: number;
44
- }
45
- interface AiAnswer {
46
- /** The answer as the model wrote it. */
47
- text: string;
48
- /** The answer parsed as JSON when `schema` was given and the model kept to it; `null` otherwise. */
49
- json: unknown;
50
- usage: AiUsage;
51
- /** The model that answered. */
52
- model: string;
53
- }
54
- /**
55
- * Thrown when the model call was refused or failed. `status` is the HTTP status Softr answered
56
- * with; 402 means the workspace has no AI credits left.
57
- */
58
- declare class AiError extends Error {
59
- readonly status: number;
60
- constructor(status: number, message: string);
61
- }
62
- /** Runs the action once and answers when the model is done. */
63
- declare function aiGenerate(action: AiAction, init: AiGenerateInit): Promise<AiAnswer>;
64
- /**
65
- * Runs the action and answers while the model writes. The returned `Response` body is a stream of
66
- * Server-Sent Events, one JSON object per `data:` line: `{ "text": "..." }` for each piece of the
67
- * answer, then `{ "done": true, "usage": {...}, "model": "..." }`, or `{ "error": "..." }` in its
68
- * place. Hand it to the browser as a `Response` with `Content-Type: text/event-stream`.
69
- */
70
- declare function aiStream(action: AiAction, init: AiGenerateInit): Promise<Response>;
71
- declare const ai: {
72
- generate: typeof aiGenerate;
73
- stream: typeof aiStream;
74
- };
75
- //#endregion
76
- export { AiAnswer, AiError, AiFile, AiGenerateInit, AiMessage, AiUsage, ai };
package/dist/core/ai.js DELETED
@@ -1,64 +0,0 @@
1
- import { fetch } from "../fetch.js";
2
- import { readSessionToken } from "../auth/session.js";
3
- import { describeFailure, dsUrl, refererHeader } from "../datasource/record.js";
4
-
5
- //#region src/core/ai.ts
6
- /**
7
- * Thrown when the model call was refused or failed. `status` is the HTTP status Softr answered
8
- * with; 402 means the workspace has no AI credits left.
9
- */
10
- var AiError = class extends Error {
11
- status;
12
- constructor(status, message) {
13
- super(message);
14
- this.name = "AiError";
15
- this.status = status;
16
- }
17
- };
18
- function body(init) {
19
- return JSON.stringify({
20
- system: init.system,
21
- messages: init.messages,
22
- prompt: init.prompt,
23
- schema: init.schema,
24
- files: init.files,
25
- maxOutputTokens: init.maxOutputTokens,
26
- reasoning: init.reasoning,
27
- temperature: init.temperature
28
- });
29
- }
30
- async function post(action, path, init) {
31
- if (init.signal?.aborted) throw init.signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
32
- const res = await fetch(`${dsUrl(action.id)}/${path}`, {
33
- method: "POST",
34
- headers: {
35
- "Content-Type": "application/json",
36
- "Authorization": `Bearer ${readSessionToken()}`,
37
- ...refererHeader()
38
- },
39
- body: body(init),
40
- signal: init.signal
41
- });
42
- if (!res.ok) throw new AiError(res.status, `AI call "${action.id}" failed: ${await describeFailure(res)}`);
43
- return res;
44
- }
45
- /** Runs the action once and answers when the model is done. */
46
- async function aiGenerate(action, init) {
47
- return await (await post(action, "softr-ai/generate", init)).json();
48
- }
49
- /**
50
- * Runs the action and answers while the model writes. The returned `Response` body is a stream of
51
- * Server-Sent Events, one JSON object per `data:` line: `{ "text": "..." }` for each piece of the
52
- * answer, then `{ "done": true, "usage": {...}, "model": "..." }`, or `{ "error": "..." }` in its
53
- * place. Hand it to the browser as a `Response` with `Content-Type: text/event-stream`.
54
- */
55
- async function aiStream(action, init) {
56
- return post(action, "softr-ai/generate/stream", init);
57
- }
58
- const ai = {
59
- generate: aiGenerate,
60
- stream: aiStream
61
- };
62
-
63
- //#endregion
64
- export { AiError, ai };