@protobase/server 0.7.0 → 0.8.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.
Files changed (114) hide show
  1. package/dist/assistant/conversations.d.ts +1 -1
  2. package/dist/assistant/tools.d.ts +1 -1
  3. package/dist/assistant/turn.d.ts +2 -2
  4. package/dist/audit/origin.js +12 -0
  5. package/dist/auth/jwt.d.ts +2 -0
  6. package/dist/auth/jwt.js +5 -3
  7. package/dist/batch/reorder.js +1 -1
  8. package/dist/batch/run-batch.js +2 -1
  9. package/dist/better-auth/authenticator.d.ts +7 -3
  10. package/dist/better-auth/authenticator.js +24 -3
  11. package/dist/better-auth/create-auth.d.ts +619 -81
  12. package/dist/better-auth/create-auth.js +64 -27
  13. package/dist/better-auth/organizations/accept-invitation.js +80 -0
  14. package/dist/better-auth/organizations/active-organization.js +12 -0
  15. package/dist/better-auth/organizations/admin-routes.js +83 -0
  16. package/dist/better-auth/organizations/app-roles.js +43 -0
  17. package/dist/better-auth/organizations/host.d.ts +53 -0
  18. package/dist/better-auth/organizations/host.js +251 -0
  19. package/dist/better-auth/organizations/invitation-endpoints.js +133 -0
  20. package/dist/better-auth/organizations/invitation-mail.js +17 -0
  21. package/dist/better-auth/organizations/invitation-token.js +27 -0
  22. package/dist/better-auth/organizations/member-endpoints.js +182 -0
  23. package/dist/better-auth/organizations/options.d.ts +48 -0
  24. package/dist/better-auth/organizations/options.js +23 -0
  25. package/dist/better-auth/organizations/organization-plugin.js +128 -0
  26. package/dist/better-auth/organizations/protobase-plugin.js +21 -0
  27. package/dist/better-auth/organizations/role-definitions.d.ts +15 -0
  28. package/dist/better-auth/organizations/role-definitions.js +56 -0
  29. package/dist/better-auth/organizations/session-organization.js +68 -0
  30. package/dist/better-auth/organizations/store.d.ts +19 -0
  31. package/dist/better-auth/organizations/store.js +73 -0
  32. package/dist/better-auth/staff-checks.js +5 -1
  33. package/dist/better-auth/staff-sign-in-endpoints.js +1 -1
  34. package/dist/better-auth/status-route.js +14 -3
  35. package/dist/better-auth/users.d.ts +2 -1
  36. package/dist/better-auth/users.js +4 -1
  37. package/dist/changes/actor.js +12 -0
  38. package/dist/changes/capture-plan.js +62 -0
  39. package/dist/changes/capture-schema.d.ts +18 -0
  40. package/dist/changes/capture-schema.js +65 -0
  41. package/dist/changes/capture-sql.js +108 -0
  42. package/dist/changes/coalesce.d.ts +38 -0
  43. package/dist/changes/coalesce.js +69 -0
  44. package/dist/changes/events-options.d.ts +10 -0
  45. package/dist/changes/events-options.js +10 -0
  46. package/dist/changes/match-events.d.ts +9 -0
  47. package/dist/changes/match-events.js +55 -0
  48. package/dist/changes/resource-change.d.ts +27 -0
  49. package/dist/changes/resource-change.js +43 -0
  50. package/dist/create-admin.d.ts +11 -2
  51. package/dist/create-admin.js +33 -2
  52. package/dist/deps.d.ts +1 -0
  53. package/dist/files/attach.js +80 -0
  54. package/dist/files/cleanup-runner.d.ts +8 -0
  55. package/dist/files/cleanup-runner.js +58 -0
  56. package/dist/files/cleanup-schedule.d.ts +28 -0
  57. package/dist/files/cleanup-schedule.js +48 -0
  58. package/dist/files/detect.js +51 -0
  59. package/dist/files/download-url.js +43 -0
  60. package/dist/files/download.js +90 -0
  61. package/dist/files/duration.js +35 -0
  62. package/dist/files/file-cleanup.d.ts +18 -0
  63. package/dist/files/file-cleanup.js +17 -0
  64. package/dist/files/local-files.d.ts +17 -0
  65. package/dist/files/local-files.js +101 -0
  66. package/dist/files/media-types.js +99 -0
  67. package/dist/files/options.d.ts +15 -0
  68. package/dist/files/present.d.ts +15 -0
  69. package/dist/files/present.js +59 -0
  70. package/dist/files/processors.d.ts +15 -0
  71. package/dist/files/processors.js +39 -0
  72. package/dist/files/providers.js +70 -0
  73. package/dist/files/routes.js +26 -0
  74. package/dist/files/runtime.d.ts +3 -0
  75. package/dist/files/runtime.js +37 -0
  76. package/dist/files/signer.js +32 -0
  77. package/dist/files/store.d.ts +37 -0
  78. package/dist/files/ticket.js +87 -0
  79. package/dist/files/upload-body.js +85 -0
  80. package/dist/files/upload.js +124 -0
  81. package/dist/files/uri.js +47 -0
  82. package/dist/functions/define-function.d.ts +71 -0
  83. package/dist/functions/define-function.js +36 -0
  84. package/dist/functions/function-app.d.ts +17 -0
  85. package/dist/functions/function-app.js +19 -0
  86. package/dist/functions/read-json.d.ts +35 -0
  87. package/dist/functions/read-json.js +24 -0
  88. package/dist/functions/records.d.ts +43 -0
  89. package/dist/functions/records.js +31 -0
  90. package/dist/functions/require-env.d.ts +7 -0
  91. package/dist/functions/require-env.js +12 -0
  92. package/dist/functions/routes.js +60 -0
  93. package/dist/functions/verify-signature.d.ts +32 -0
  94. package/dist/functions/verify-signature.js +49 -0
  95. package/dist/index.d.ts +26 -3
  96. package/dist/index.js +17 -3
  97. package/dist/list-service.js +4 -2
  98. package/dist/openapi/resource-paths.js +78 -1
  99. package/dist/openapi/schemas.js +28 -3
  100. package/dist/project-config.d.ts +10 -1
  101. package/dist/project-config.js +41 -3
  102. package/dist/read-service.js +5 -1
  103. package/dist/request-access.d.ts +5 -0
  104. package/dist/request-access.js +9 -4
  105. package/dist/resource-source.d.ts +4 -1
  106. package/dist/routes/batch.js +1 -1
  107. package/dist/routes/collection.js +3 -1
  108. package/dist/routes/record.js +2 -5
  109. package/dist/transactions.js +10 -3
  110. package/dist/types.d.ts +25 -5
  111. package/dist/write-pipeline.js +4 -2
  112. package/dist/write-shared.js +6 -3
  113. package/dist/write-update.js +2 -1
  114. package/package.json +6 -4
@@ -0,0 +1,36 @@
1
+ //#region src/functions/define-function.ts
2
+ function defineFunction(first, handler) {
3
+ if (handler === void 0) return {
4
+ kind: "function",
5
+ options: {},
6
+ handler: first
7
+ };
8
+ return {
9
+ kind: "function",
10
+ options: first,
11
+ handler
12
+ };
13
+ }
14
+ const isApp = (value) => typeof value === "object" && value !== null && typeof value.fetch === "function";
15
+ const isApiFunction = (value) => typeof value === "object" && value !== null && value.kind === "function";
16
+ /** A function's name is its URL segment: letters, digits, `-` and `_`, starting with a letter or digit. */
17
+ const functionNamePattern = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
18
+ /** The function a source stands for; anything else is an error naming it. */
19
+ const toApiFunction = (name, source) => {
20
+ if (!functionNamePattern.test(name)) throw new Error(`The function name "${name}" is not a URL segment: use letters, digits, - and _`);
21
+ if (isApiFunction(source)) {
22
+ if (typeof source.handler !== "function" && !isApp(source.handler)) throw new Error(`The function "${name}" has no handler: pass a (request, context) function or a Hono app`);
23
+ return source;
24
+ }
25
+ if (typeof source === "function" || isApp(source)) return defineFunction(source);
26
+ throw new Error(`The function "${name}" must default-export defineFunction(...), a (request, context) handler or a Hono app`);
27
+ };
28
+ /** Calls a function's handler or app with its request and context; anything but a Response is an error. */
29
+ const invokeFunction = async (name, fn, request, context) => {
30
+ const { handler } = fn;
31
+ const response = isApp(handler) ? await handler.fetch(request, context) : await handler(request, context);
32
+ if (!(response instanceof Response)) throw new Error(`The function "${name}" returned no Response`);
33
+ return response;
34
+ };
35
+ //#endregion
36
+ export { defineFunction, functionNamePattern, invokeFunction, toApiFunction };
@@ -0,0 +1,17 @@
1
+ import { FunctionContext, PublicFunctionContext } from "./define-function.js";
2
+ import { Env, Hono } from "hono";
3
+ //#region src/functions/function-app.d.ts
4
+ /** A Hono app's env for a protected function: `c.env` is its context, `c.env.records` the caller's records. */
5
+ export type FunctionEnv = {
6
+ Bindings: FunctionContext;
7
+ };
8
+ /** A Hono app's env for a public function. */
9
+ export type PublicFunctionEnv = {
10
+ Bindings: PublicFunctionContext;
11
+ };
12
+ /**
13
+ * `new Hono()` for a function, whose errors and unknown paths reach the app's own error handling: problems as
14
+ * problem+json, anything else a 500 the app reports. A plain Hono app answers them itself.
15
+ */
16
+ export declare const functionApp: <E extends Env = FunctionEnv>() => Hono<E, import("hono/types").BlankSchema, "/">;
17
+ //#endregion
@@ -0,0 +1,19 @@
1
+ import { notFound } from "../problem.js";
2
+ import { Hono } from "hono";
3
+ //#region src/functions/function-app.ts
4
+ /**
5
+ * `new Hono()` for a function, whose errors and unknown paths reach the app's own error handling: problems as
6
+ * problem+json, anything else a 500 the app reports. A plain Hono app answers them itself.
7
+ */
8
+ const functionApp = () => {
9
+ const app = new Hono();
10
+ app.onError((error) => {
11
+ throw error;
12
+ });
13
+ app.notFound((c) => {
14
+ throw notFound(`There is no route at ${c.req.path}`);
15
+ });
16
+ return app;
17
+ };
18
+ //#endregion
19
+ export { functionApp };
@@ -0,0 +1,35 @@
1
+ //#region src/functions/read-json.d.ts
2
+ type Issue = {
3
+ message: string;
4
+ path?: ReadonlyArray<PropertyKey | {
5
+ key: PropertyKey;
6
+ }>;
7
+ };
8
+ /** A Standard Schema (https://standardschema.dev), such as a zod 4, Valibot or ArkType schema. */
9
+ export type StandardSchema<Output = unknown> = {
10
+ readonly '~standard': {
11
+ readonly version: 1;
12
+ readonly validate: (value: unknown) => {
13
+ value: Output;
14
+ issues?: undefined;
15
+ } | {
16
+ issues: ReadonlyArray<Issue>;
17
+ } | Promise<{
18
+ value: Output;
19
+ issues?: undefined;
20
+ } | {
21
+ issues: ReadonlyArray<Issue>;
22
+ }>;
23
+ readonly types?: {
24
+ readonly input: unknown;
25
+ readonly output: Output;
26
+ };
27
+ };
28
+ };
29
+ /**
30
+ * The request's JSON body, checked against `schema` when one is given. A body that is no JSON is a 400 `malformed-json`
31
+ * problem, one the schema refuses a 400 `invalid-body` problem listing each issue by field.
32
+ */
33
+ export declare function readJson(request: Request): Promise<unknown>;
34
+ export declare function readJson<Output>(request: Request, schema: StandardSchema<Output>): Promise<Output>;
35
+ //#endregion
@@ -0,0 +1,24 @@
1
+ import { badRequest } from "../problem.js";
2
+ //#region src/functions/read-json.ts
3
+ const fieldOf = (issue) => (issue.path ?? []).map((part) => String(typeof part === "object" ? part.key : part)).join(".");
4
+ async function readJson(request, schema) {
5
+ const text = await request.text();
6
+ let body;
7
+ try {
8
+ body = JSON.parse(text);
9
+ } catch (error) {
10
+ if (error instanceof SyntaxError) throw badRequest("malformed-json", "The request body is not valid JSON");
11
+ throw error;
12
+ }
13
+ if (!schema) return body;
14
+ const result = await schema["~standard"].validate(body);
15
+ if (!result.issues) return result.value;
16
+ const errors = result.issues.map((issue) => ({
17
+ field: fieldOf(issue),
18
+ message: issue.message
19
+ }));
20
+ const first = errors[0];
21
+ throw badRequest("invalid-body", `The request body is invalid: ${first.field ? `${first.field}: ` : ""}${first.message}`, { errors });
22
+ }
23
+ //#endregion
24
+ export { readJson };
@@ -0,0 +1,43 @@
1
+ import { Row } from "../types.js";
2
+ import "../deps.js";
3
+ import { RecordKeyInput, ToolRecord, ToolRecords } from "../assistant/records.js";
4
+ //#region src/functions/records.d.ts
5
+ /** The list parameters of the REST API (AIP-132), as values rather than query text. */
6
+ export type ListParams = {
7
+ filter?: string;
8
+ order_by?: string;
9
+ page_size?: number;
10
+ page_token?: string;
11
+ fields?: string[];
12
+ count?: 'exact';
13
+ show_deleted?: boolean;
14
+ };
15
+ /**
16
+ * A page of records as the REST API lists it: each item with its `etag` and, unless the resource opts out, whether the
17
+ * caller may update and delete it; and the token of the next page, empty on the last.
18
+ */
19
+ export type RecordPage = {
20
+ items: Array<Row & {
21
+ etag?: string;
22
+ permissions?: {
23
+ update: boolean;
24
+ delete: boolean;
25
+ };
26
+ }>;
27
+ next_page_token: string;
28
+ total_size_estimate: number;
29
+ total_size?: number;
30
+ };
31
+ /**
32
+ * The app's resources as the caller: their access rules, row filters, tenant scope, validation and write hooks apply as
33
+ * in the REST API. A refusal or a missing record rejects with an `HttpProblem`, which becomes the function's response
34
+ * when it is not caught.
35
+ */
36
+ export type FunctionRecords = ToolRecords & {
37
+ list: (resource: string, params?: ListParams) => Promise<RecordPage>;
38
+ /** With `etag`, fails with 412 when the record changed since it was read. Resolves to the record as it was. */
39
+ delete: (resource: string, key: RecordKeyInput, options?: {
40
+ etag?: string;
41
+ }) => Promise<ToolRecord>;
42
+ };
43
+ //#endregion
@@ -0,0 +1,31 @@
1
+ import { notFound } from "../problem.js";
2
+ import { decodeKeyText } from "../key.js";
3
+ import { runWrite } from "../write-pipeline.js";
4
+ import { assistantRecords } from "../assistant/records.js";
5
+ import { listRecords } from "../list-service.js";
6
+ import { encodeKey } from "@protobase/schema";
7
+ //#region src/functions/records.ts
8
+ const filterLength = 2e5;
9
+ /** `FunctionRecords` over the admin's resources for `session`. */
10
+ const callerRecords = (deps, session) => {
11
+ const entryOf = (resource) => {
12
+ const entry = deps.registry.find(resource);
13
+ if (!entry) throw notFound(`There is no resource "${resource}"`);
14
+ return entry;
15
+ };
16
+ return {
17
+ ...assistantRecords(deps, session),
18
+ list: async (resource, params = {}) => (await listRecords(deps, entryOf(resource), session, params, filterLength)).body,
19
+ delete: async (resource, input, { etag } = {}) => {
20
+ const entry = entryOf(resource);
21
+ const key = decodeKeyText(entry, Array.isArray(input) ? encodeKey(input) : String(input));
22
+ return runWrite(deps, entry, session, {
23
+ operation: "delete",
24
+ key,
25
+ ifMatch: etag ?? "*"
26
+ });
27
+ }
28
+ };
29
+ };
30
+ //#endregion
31
+ export { callerRecords };
@@ -0,0 +1,7 @@
1
+ //#region src/functions/require-env.d.ts
2
+ /**
3
+ * An environment variable the function cannot work without, such as an API key: `protobase dev` reads it from `.env`,
4
+ * `protobase serve` from its environment. A missing one is an error, so the call is a 500 the app reports.
5
+ */
6
+ export declare const requireEnv: (name: string) => any;
7
+ //#endregion
@@ -0,0 +1,12 @@
1
+ //#region src/functions/require-env.ts
2
+ /**
3
+ * An environment variable the function cannot work without, such as an API key: `protobase dev` reads it from `.env`,
4
+ * `protobase serve` from its environment. A missing one is an error, so the call is a 500 the app reports.
5
+ */
6
+ const requireEnv = (name) => {
7
+ const value = globalThis.process?.env?.[name];
8
+ if (!value) throw new Error(`The environment variable ${name} is not set`);
9
+ return value;
10
+ };
11
+ //#endregion
12
+ export { requireEnv };
@@ -0,0 +1,60 @@
1
+ import { forbidden, notFound } from "../problem.js";
2
+ import { invokeFunction } from "./define-function.js";
3
+ import { Hono } from "hono";
4
+ import { cors } from "hono/cors";
5
+ //#region src/functions/routes.ts
6
+ const relativeRequest = (request, prefix) => {
7
+ const url = new URL(request.url);
8
+ url.pathname = url.pathname.slice(prefix.length) || "/";
9
+ return new Request(url, request);
10
+ };
11
+ const requireRoles = (name, session, roles) => {
12
+ if (!roles || roles.some((role) => session.user.roles.includes(role))) return;
13
+ throw forbidden("function-forbidden", `You may not call the function "${name}"`);
14
+ };
15
+ /**
16
+ * Every function at `<basePath>/<name>` and below, any method. A protected function authenticates the request first, so
17
+ * a call without a valid token is the authenticator's 401 problem; a public one gets the caller only when it asks.
18
+ */
19
+ const functionRoutes = ({ basePath, functions, authenticate, db, records, onError }) => {
20
+ const app = new Hono().basePath(basePath);
21
+ app.onError(onError);
22
+ const callerOf = async (request) => {
23
+ const session = await authenticate(request);
24
+ return {
25
+ session,
26
+ records: records(session)
27
+ };
28
+ };
29
+ for (const [name, fn] of Object.entries(functions)) {
30
+ const prefix = `${basePath}/${name}`;
31
+ const { options } = fn;
32
+ if (options.cors) app.use(`/${name}/*`, cors(options.cors));
33
+ app.all(`/${name}/*`, async (c) => {
34
+ const request = relativeRequest(c.req.raw, prefix);
35
+ if (options.public) {
36
+ let caller;
37
+ return invokeFunction(name, fn, request, {
38
+ name,
39
+ db,
40
+ caller: () => caller ??= callerOf(c.req.raw)
41
+ });
42
+ }
43
+ const caller = await callerOf(c.req.raw);
44
+ requireRoles(name, caller.session, options.roles);
45
+ const context = {
46
+ name,
47
+ db,
48
+ ...caller,
49
+ caller: async () => caller
50
+ };
51
+ return invokeFunction(name, fn, request, context);
52
+ });
53
+ }
54
+ app.all("/*", (c) => {
55
+ throw notFound(`There is no function at ${c.req.path}`);
56
+ });
57
+ return app;
58
+ };
59
+ //#endregion
60
+ export { functionRoutes };
@@ -0,0 +1,32 @@
1
+ //#region src/functions/verify-signature.d.ts
2
+ export type SignatureOptions = {
3
+ /** The shared secret, as the sender shows it. */
4
+ secret: string;
5
+ /** The header that carries the signature, such as `x-hub-signature-256`. */
6
+ header: string;
7
+ /** Text before the signature in that header, such as `sha256=`. */
8
+ prefix?: string;
9
+ /** How the signature is written. Default `hex`. */
10
+ encoding?: 'hex' | 'base64';
11
+ /** Default `SHA-256`. */
12
+ hash?: 'SHA-1' | 'SHA-256' | 'SHA-512';
13
+ };
14
+ /**
15
+ * The request's body as text, once its HMAC signature checks out against `secret`, as GitHub, Shopify and many others
16
+ * sign their webhooks. A missing or wrong signature is a 401 `invalid-signature` problem.
17
+ */
18
+ export declare const verifySignature: (request: Request, { secret, header, prefix, encoding, hash }: SignatureOptions) => Promise<string>;
19
+ export type StandardWebhookOptions = {
20
+ /** The endpoint's secret, `whsec_` and base64. */
21
+ secret: string;
22
+ /** How old, or how far ahead, a message may be. Default 300. */
23
+ toleranceSeconds?: number;
24
+ /** The current time in milliseconds; for tests. */
25
+ now?: () => number;
26
+ };
27
+ /**
28
+ * The request's body as text, once its Standard Webhooks signature checks out (https://www.standardwebhooks.com), as
29
+ * Supabase Auth hooks, Svix, Resend and others sign theirs. A missing, stale or wrong signature is a 401 problem.
30
+ */
31
+ export declare const verifyStandardWebhook: (request: Request, { secret, toleranceSeconds, now }: StandardWebhookOptions) => Promise<string>;
32
+ //#endregion
@@ -0,0 +1,49 @@
1
+ import { HttpProblem } from "../problem.js";
2
+ //#region src/functions/verify-signature.ts
3
+ const invalid = (detail) => new HttpProblem(401, "invalid-signature", "Unauthorized", detail);
4
+ const decodeHex = (text) => /^(?:[0-9a-f]{2})+$/i.test(text) ? new Uint8Array(text.match(/../g).map((pair) => parseInt(pair, 16))) : void 0;
5
+ const decodeBase64 = (text) => /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(text) && text.length > 0 ? Uint8Array.from(atob(text), (char) => char.charCodeAt(0)) : void 0;
6
+ const verifyHmac = async (key, hash, signature, content) => {
7
+ const cryptoKey = await crypto.subtle.importKey("raw", key, {
8
+ name: "HMAC",
9
+ hash
10
+ }, false, ["verify"]);
11
+ return crypto.subtle.verify("HMAC", cryptoKey, signature, new TextEncoder().encode(content));
12
+ };
13
+ /**
14
+ * The request's body as text, once its HMAC signature checks out against `secret`, as GitHub, Shopify and many others
15
+ * sign their webhooks. A missing or wrong signature is a 401 `invalid-signature` problem.
16
+ */
17
+ const verifySignature = async (request, { secret, header, prefix = "", encoding = "hex", hash = "SHA-256" }) => {
18
+ const body = await request.text();
19
+ const value = request.headers.get(header);
20
+ if (!value?.startsWith(prefix)) throw invalid(`The request has no ${header} signature`);
21
+ const text = value.slice(prefix.length).trim();
22
+ const signature = encoding === "hex" ? decodeHex(text) : decodeBase64(text);
23
+ if (!signature || !await verifyHmac(new TextEncoder().encode(secret), hash, signature, body)) throw invalid(`The ${header} signature does not match the body`);
24
+ return body;
25
+ };
26
+ /**
27
+ * The request's body as text, once its Standard Webhooks signature checks out (https://www.standardwebhooks.com), as
28
+ * Supabase Auth hooks, Svix, Resend and others sign theirs. A missing, stale or wrong signature is a 401 problem.
29
+ */
30
+ const verifyStandardWebhook = async (request, { secret, toleranceSeconds = 300, now = Date.now }) => {
31
+ const body = await request.text();
32
+ const id = request.headers.get("webhook-id");
33
+ const timestamp = request.headers.get("webhook-timestamp");
34
+ const signatures = request.headers.get("webhook-signature");
35
+ if (!id || !timestamp || !signatures) throw invalid("The request has no webhook-id, webhook-timestamp and webhook-signature headers");
36
+ const seconds = Number(timestamp);
37
+ if (!Number.isInteger(seconds) || Math.abs(now() / 1e3 - seconds) > toleranceSeconds) throw invalid("The webhook-timestamp is too old or too far ahead");
38
+ const key = decodeBase64(secret.replace(/^whsec_/, ""));
39
+ if (!key) throw new Error("The Standard Webhooks secret is not base64 (whsec_...)");
40
+ const content = `${id}.${timestamp}.${body}`;
41
+ for (const entry of signatures.split(" ")) {
42
+ const [version, text = ""] = entry.split(",");
43
+ const signature = decodeBase64(text);
44
+ if (version === "v1" && signature && await verifyHmac(key, "SHA-256", signature, content)) return body;
45
+ }
46
+ throw invalid("No webhook-signature matches the body");
47
+ };
48
+ //#endregion
49
+ export { verifySignature, verifyStandardWebhook };
package/dist/index.d.ts CHANGED
@@ -1,15 +1,25 @@
1
1
  import { SignInPolicy, SignInRule, StaffAccessRule, defaultSignInPolicy, signInPolicyMethods } from "./better-auth/sign-in-policy.js";
2
2
  import { StaffSignIn } from "./better-auth/staff-log.js";
3
+ import { RoleDefinition, RolesInput } from "./better-auth/organizations/role-definitions.js";
3
4
  import { OperatorProvider, readOperatorSettings } from "./better-auth/operator-provider.js";
5
+ import { OrganizationRole, StoredMember, StoredOrganization } from "./better-auth/organizations/store.js";
6
+ import { OrganizationsOptions } from "./better-auth/organizations/options.js";
4
7
  import { PlatformSignIn, readPlatformSignIn } from "./better-auth/platform-sign-in.js";
5
8
  import { AdminAuth, CreateAuthOptions, UserCreatedHook, createAuth, normalizeRoles } from "./better-auth/create-auth.js";
9
+ import { EventsOptions } from "./changes/events-options.js";
10
+ import { FileProvider, FileStore, StoredObject } from "./files/store.js";
11
+ import { FilesOptions } from "./files/options.js";
12
+ import { Admin, AuditEvent, AuditQueue, Authenticator, PipelineHook, ScanGuardMode, Session, SessionOrganization, TenantValue, User, ViewSource, WriteEvent, WriteOperation } from "./types.js";
13
+ import { CleanupEntry, CleanupSchedule, ScheduledDelete, storageCleanupSchedule } from "./files/cleanup-schedule.js";
14
+ import { CleanupReport } from "./files/cleanup-runner.js";
6
15
  import { ResourceSource, configExports } from "./resource-source.js";
16
+ import { RecordKeyInput, ToolRecord, ToolRecords } from "./assistant/records.js";
17
+ import { FunctionRecords, ListParams, RecordPage } from "./functions/records.js";
18
+ import { ApiFunction, CorsOptions, FunctionApp, FunctionCaller, FunctionContext, FunctionHandler, FunctionOptions, FunctionSource, ProtectedFunctionOptions, PublicFunctionContext, PublicFunctionHandler, PublicFunctionOptions, defineFunction } from "./functions/define-function.js";
7
19
  import { ChatMessage, ChatTextPart, Completion, CompletionOptions, ModelError, ModelSettings, ReasoningDetail, ReasoningEffort, ToolCall, ToolSpec, streamChatCompletion } from "./assistant/chat-completions.js";
8
- import { Admin, AuditEvent, AuditQueue, Authenticator, PipelineHook, ScanGuardMode, Session, TenantValue, User, ViewSource, WriteEvent, WriteOperation } from "./types.js";
9
20
  import { ConversationRecord, ConversationStore } from "./assistant/conversation-store.js";
10
21
  import { Conversation, ConversationsOptions, conversationKey, createConversations } from "./assistant/conversations.js";
11
22
  import { ApprovalAnswer, ApprovalRequest, requestApproval } from "./assistant/approval.js";
12
- import { RecordKeyInput, ToolRecord, ToolRecords } from "./assistant/records.js";
13
23
  import { AssistantTool, ToolContext, defineTool } from "./assistant/tools.js";
14
24
  import { AssistantOptions, AssistantSettings, assistantRoles, assistantSettings, assistantVariables, seesAssistant } from "./assistant/assistant-settings.js";
15
25
  import { RuntimeOptions, runtimeUrl, runtimeVariable, seesRuntime } from "./runtime/runtime-settings.js";
@@ -24,6 +34,11 @@ import { parseRoles } from "./better-auth/parse-roles.js";
24
34
  import { LinkedAccount, NewUser, StoredUser, createUser, deleteUser, hasUsers, listUsers, roleChoices, setUserBanned, setUserRole } from "./better-auth/users.js";
25
35
  import { IssueTokenOptions, issueToken, maxTokenTtlSeconds } from "./better-auth/tokens.js";
26
36
  import { authSchemaMigration, checkAuthSchema } from "./better-auth/auth-schema.js";
37
+ import { changesMigration, checkChanges, migrationCommand } from "./changes/capture-schema.js";
38
+ import { CapturedRow, CoalescedChange, coalesce } from "./changes/coalesce.js";
39
+ import { ResourceChange, recordOf, resourceChange } from "./changes/resource-change.js";
40
+ import { eventMatcher } from "./changes/match-events.js";
41
+ import { MemberInput, NewOrganization, addAppRoleToOwners, addMember, createOrganization, listOrganizations, removeMember, setMemberRoles } from "./better-auth/organizations/host.js";
27
42
  import { AssistantBackend, assistantProtocolRoutes } from "./assistant/protocol-routes.js";
28
43
  import { fileConversationStore } from "./assistant/file-conversation-store.js";
29
44
  import { withCacheBreakpoints } from "./assistant/cache-breakpoints.js";
@@ -31,4 +46,12 @@ import { TurnOptions, runTurn } from "./assistant/turn.js";
31
46
  import { WidgetOptions, maxWidgetPropsBytes, widget } from "./assistant/widget.js";
32
47
  import { QueryOptions, QueryResult, runReadOnlyQuery, runReadWriteQuery } from "./assistant/run-query.js";
33
48
  import { readOnlyQueryTool, readWriteQueryTool } from "./assistant/query-tools.js";
34
- export { type Admin, type AdminAuth, type AdminOptions, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BetterAuthAuthenticatorOptions, type ChatMessage, type ChatTextPart, type Completion, type CompletionOptions, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CreateAdminInput, type CreateAuthOptions, HttpProblem, type IssueTokenOptions, type JwtOptions, type LinkedAccount, ModelError, type ModelSettings, type NewUser, type OperatorProvider, type PipelineHook, type PlatformSignIn, type ProjectConfig, type QueryOptions, type QueryResult, type ReasoningDetail, type ReasoningEffort, type RecordKeyInput, type ResourceSource, type RuntimeOptions, type ScanGuardMode, type Session, type SignInPolicy, type SignInRule, type StaffAccessRule, type StaffSignIn, type StoredUser, type TenantValue, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TurnOptions, type User, type UserCreatedHook, type ViewSource, type WidgetOptions, type WriteEvent, type WriteOperation, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, betterAuthAuthenticator, checkAuthSchema, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createUser, defaultSignInPolicy, defineConfig, defineTool, deleteUser, fileConversationStore, forbidden, hasUsers, issueToken, jwtAuthenticator, listUsers, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, normalizeRoles, notFound, parseRoles, readOnlyQueryTool, readOperatorSettings, readPlatformSignIn, readWriteQueryTool, requestApproval, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, seesAssistant, seesRuntime, setUserBanned, setUserRole, signInPolicyMethods, streamChatCompletion, unauthorized, widget, withCacheBreakpoints };
49
+ import { FunctionEnv, PublicFunctionEnv, functionApp } from "./functions/function-app.js";
50
+ import { StandardSchema, readJson } from "./functions/read-json.js";
51
+ import { SignatureOptions, StandardWebhookOptions, verifySignature, verifyStandardWebhook } from "./functions/verify-signature.js";
52
+ import { requireEnv } from "./functions/require-env.js";
53
+ import { LocalFilesOptions, localFileStore, localFiles } from "./files/local-files.js";
54
+ import { FileCleanupInput, createFileCleanup } from "./files/file-cleanup.js";
55
+ import { FileObject } from "./files/present.js";
56
+ import { aspectRatio, fileSize, fileType, imageSize, processor } from "./files/processors.js";
57
+ export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BetterAuthAuthenticatorOptions, type CapturedRow, type ChatMessage, type ChatTextPart, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type Completion, type CompletionOptions, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CreateAdminInput, type CreateAuthOptions, type EventsOptions, type FileCleanupInput, type FileObject, type FileProvider, type FileStore, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSource, HttpProblem, type IssueTokenOptions, type JwtOptions, type LinkedAccount, type ListParams, type LocalFilesOptions, type MemberInput, ModelError, type ModelSettings, type NewOrganization, type NewUser, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PipelineHook, type PlatformSignIn, type ProjectConfig, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type QueryOptions, type QueryResult, type ReasoningDetail, type ReasoningEffort, type RecordKeyInput, type RecordPage, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RuntimeOptions, type ScanGuardMode, type ScheduledDelete, type Session, type SessionOrganization, type SignInPolicy, type SignInRule, type SignatureOptions, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StoredMember, type StoredObject, type StoredOrganization, type StoredUser, type TenantValue, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TurnOptions, type User, type UserCreatedHook, type ViewSource, type WidgetOptions, type WriteEvent, type WriteOperation, addAppRoleToOwners, addMember, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, betterAuthAuthenticator, changesMigration, checkAuthSchema, checkChanges, coalesce, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineFunction, defineTool, deleteUser, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, functionApp, hasUsers, imageSize, issueToken, jwtAuthenticator, listOrganizations, listUsers, localFileStore, localFiles, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, normalizeRoles, notFound, parseRoles, processor, readJson, readOnlyQueryTool, readOperatorSettings, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, requestApproval, requireEnv, resourceChange, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, storageCleanupSchedule, streamChatCompletion, unauthorized, verifySignature, verifyStandardWebhook, widget, withCacheBreakpoints };
package/dist/index.js CHANGED
@@ -12,11 +12,15 @@ import { ModelError, streamChatCompletion } from "./assistant/chat-completions.j
12
12
  import { runTurn } from "./assistant/turn.js";
13
13
  import { consoleAuditQueue } from "./audit/console-queue.js";
14
14
  import { readOperatorSettings } from "./better-auth/operator-provider.js";
15
- import { readPlatformSignIn } from "./better-auth/platform-sign-in.js";
16
- import { defaultSignInPolicy, signInPolicyMethods } from "./better-auth/sign-in-policy.js";
17
15
  import { parseRoles } from "./better-auth/parse-roles.js";
16
+ import { defaultSignInPolicy, signInPolicyMethods } from "./better-auth/sign-in-policy.js";
17
+ import { readPlatformSignIn } from "./better-auth/platform-sign-in.js";
18
18
  import { createAuth, normalizeRoles } from "./better-auth/create-auth.js";
19
+ import { addAppRoleToOwners, addMember, createOrganization, listOrganizations, removeMember, setMemberRoles } from "./better-auth/organizations/host.js";
19
20
  import { createUser, deleteUser, hasUsers, listUsers, roleChoices, setUserBanned, setUserRole } from "./better-auth/users.js";
21
+ import { storageCleanupSchedule } from "./files/cleanup-schedule.js";
22
+ import { localFileStore, localFiles } from "./files/local-files.js";
23
+ import { defineFunction } from "./functions/define-function.js";
20
24
  import { runtimeUrl, runtimeVariable, seesRuntime } from "./runtime/runtime-settings.js";
21
25
  import { createAdmin } from "./create-admin.js";
22
26
  import { configExports } from "./resource-source.js";
@@ -25,5 +29,15 @@ import { jwtAuthenticator } from "./auth/jwt.js";
25
29
  import { betterAuthAuthenticator } from "./better-auth/authenticator.js";
26
30
  import { issueToken, maxTokenTtlSeconds } from "./better-auth/tokens.js";
27
31
  import { authSchemaMigration, checkAuthSchema } from "./better-auth/auth-schema.js";
32
+ import { changesMigration, checkChanges, migrationCommand } from "./changes/capture-schema.js";
33
+ import { coalesce } from "./changes/coalesce.js";
34
+ import { recordOf, resourceChange } from "./changes/resource-change.js";
35
+ import { eventMatcher } from "./changes/match-events.js";
28
36
  import { maxWidgetPropsBytes, widget } from "./assistant/widget.js";
29
- export { HttpProblem, ModelError, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, betterAuthAuthenticator, checkAuthSchema, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createUser, defaultSignInPolicy, defineConfig, defineTool, deleteUser, fileConversationStore, forbidden, hasUsers, issueToken, jwtAuthenticator, listUsers, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, normalizeRoles, notFound, parseRoles, readOnlyQueryTool, readOperatorSettings, readPlatformSignIn, readWriteQueryTool, requestApproval, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, seesAssistant, seesRuntime, setUserBanned, setUserRole, signInPolicyMethods, streamChatCompletion, unauthorized, widget, withCacheBreakpoints };
37
+ import { functionApp } from "./functions/function-app.js";
38
+ import { readJson } from "./functions/read-json.js";
39
+ import { verifySignature, verifyStandardWebhook } from "./functions/verify-signature.js";
40
+ import { requireEnv } from "./functions/require-env.js";
41
+ import { createFileCleanup } from "./files/file-cleanup.js";
42
+ import { aspectRatio, fileSize, fileType, imageSize, processor } from "./files/processors.js";
43
+ export { HttpProblem, ModelError, addAppRoleToOwners, addMember, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, betterAuthAuthenticator, changesMigration, checkAuthSchema, checkChanges, coalesce, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineFunction, defineTool, deleteUser, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, functionApp, hasUsers, imageSize, issueToken, jwtAuthenticator, listOrganizations, listUsers, localFileStore, localFiles, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, normalizeRoles, notFound, parseRoles, processor, readJson, readOnlyQueryTool, readOperatorSettings, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, requestApproval, requireEnv, resourceChange, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, storageCleanupSchedule, streamChatCompletion, unauthorized, verifySignature, verifyStandardWebhook, widget, withCacheBreakpoints };
@@ -1,3 +1,4 @@
1
+ import { presentFiles } from "./files/present.js";
1
2
  import { etagAlias } from "./sql-parts.js";
2
3
  import { etagOf } from "./etag.js";
3
4
  import { exposeRow, pickFields } from "./rows.js";
@@ -39,7 +40,8 @@ const listRecords = async (deps, entry, session, input, maxFilterLength, jump =
39
40
  [etagField.name]: etagField
40
41
  }
41
42
  } : model;
42
- const read = entry.etag && columns ? columns : Object.keys(model.fields);
43
+ const withKey = columns?.some((name) => model.fields[name].type === "file") ? [.../* @__PURE__ */ new Set([...columns, ...entry.model.primaryKey.filter((name) => model.fields[name])])] : columns;
44
+ const read = entry.etag && withKey ? withKey : Object.keys(model.fields);
43
45
  const shape = {
44
46
  ...filter && { filter },
45
47
  ...sort && { sort },
@@ -80,7 +82,7 @@ const listRecords = async (deps, entry, session, input, maxFilterLength, jump =
80
82
  return {
81
83
  warning,
82
84
  body: {
83
- items: result.rows.map((row, i) => ({
85
+ items: (await presentFiles(access, result.rows)).map((row, i) => ({
84
86
  ...pickFields(row, columns),
85
87
  etag: result.etags[i],
86
88
  ...result.permissions && { permissions: result.permissions[i] }
@@ -1,3 +1,4 @@
1
+ import { writableFields } from "../request-access.js";
1
2
  import { pascal } from "./naming.js";
2
3
  //#region src/openapi/resource-paths.ts
3
4
  const param = (name) => ({ $ref: `#/components/parameters/${name}` });
@@ -411,7 +412,83 @@ const resourcePaths = (access) => {
411
412
  }
412
413
  } }
413
414
  };
414
- return withoutDenied(paths, access);
415
+ return {
416
+ ...withoutDenied(paths, access),
417
+ ...uploadPath(access)
418
+ };
419
+ };
420
+ const uploadPath = (access) => {
421
+ const { entry } = access;
422
+ const writable = /* @__PURE__ */ new Set([...writableFields(access, "create"), ...writableFields(access, "update")]);
423
+ const fields = Object.values(access.readable.fields).filter((model) => model.file && writable.has(model.name)).map((model) => model.name);
424
+ if (fields.length === 0) return {};
425
+ const binary = {
426
+ type: "string",
427
+ format: "binary"
428
+ };
429
+ return { [`/${entry.name}:upload`]: { post: {
430
+ operationId: `upload_${entry.name}`,
431
+ tags: [entry.name],
432
+ summary: `Upload a file for a ${entry.name} field`,
433
+ description: "Streams one file: the raw body with `name`, or `multipart/form-data` with one file part. Its type is detected from the content, corrected when the name or header says otherwise, and checked against the field's allowed types. The answer's `value` is a ticket: send it as the field's value in a create, update or batch write within 24 hours.",
434
+ parameters: [{
435
+ ...field("The file field"),
436
+ schema: {
437
+ type: "string",
438
+ enum: fields
439
+ }
440
+ }, query("name", "The file name, for a raw body")],
441
+ requestBody: {
442
+ required: true,
443
+ content: {
444
+ "application/octet-stream": { schema: binary },
445
+ "multipart/form-data": { schema: {
446
+ type: "object",
447
+ properties: { file: binary }
448
+ } }
449
+ }
450
+ },
451
+ responses: {
452
+ 201: {
453
+ description: "Stored; use `value` in a write",
454
+ content: json({
455
+ type: "object",
456
+ required: [
457
+ "value",
458
+ "file",
459
+ "derived"
460
+ ],
461
+ properties: {
462
+ value: { type: "string" },
463
+ file: {
464
+ type: "object",
465
+ properties: {
466
+ name: { type: "string" },
467
+ type: { type: "string" },
468
+ size: { type: "integer" }
469
+ }
470
+ },
471
+ derived: {
472
+ type: "object",
473
+ description: "Values the field's processors computed for its derived fields"
474
+ },
475
+ corrected: {
476
+ type: "object",
477
+ properties: {
478
+ from: { type: "string" },
479
+ to: { type: "string" }
480
+ }
481
+ }
482
+ }
483
+ })
484
+ },
485
+ 400: error("BadRequest"),
486
+ ...auth,
487
+ 403: error("Forbidden"),
488
+ 413: { description: "Larger than the field allows" },
489
+ 415: { description: "A type the field does not allow" }
490
+ }
491
+ } } };
415
492
  };
416
493
  //#endregion
417
494
  export { resourcePaths };