@protobase/server 0.30.2 → 0.31.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.
@@ -1,10 +1,10 @@
1
1
  import { AuditQueue, PipelineHook, ScanGuardMode } from "./types.js";
2
+ import { ApiKeysOptions } from "./api-keys/api-key-settings.js";
2
3
  import { ErrorReportingOptions } from "./error-reporting/error-reporting-settings.js";
3
4
  import { MapOptions } from "./maps/map-settings.js";
4
5
  import { PluginOption } from "./plugins/plugin-settings.js";
5
6
  import { SourceOption } from "./sources/source-settings.js";
6
7
  import { CorsOrigins } from "./api-cors.js";
7
- import { ApiKeysOptions } from "./api-keys/api-key-settings.js";
8
8
  import { AssistantOptions } from "./assistant/assistant-settings.js";
9
9
  import { LocaleOption } from "./locale/locale-option.js";
10
10
  import { RuntimeOptions } from "./runtime/runtime-settings.js";
@@ -14,8 +14,8 @@ export type AdminOptions = {
14
14
  /** Default `/api/v1`. */
15
15
  basePath?: string;
16
16
  /**
17
- * The app's publishable and secret keys, with which supabase-js calls the PostgREST-compatible `/rest/v1` and the
18
- * Realtime-compatible `/realtime/v1` and the Storage-compatible `/storage/v1` (see `ApiKeysOptions`); default `PROTOBASE_PUBLISHABLE_KEY` and
17
+ * The app's publishable and secret keys, with which supabase-js calls the PostgREST-compatible `/api/sb/rest/v1` and the
18
+ * Realtime-compatible `/api/sb/realtime/v1` and the Storage-compatible `/api/sb/storage/v1` (see `ApiKeysOptions`); default `PROTOBASE_PUBLISHABLE_KEY` and
19
19
  * `PROTOBASE_SECRET_KEY`. Without either, or with `false`, the app serves neither.
20
20
  */
21
21
  apiKeys?: ApiKeysOptions | false;
@@ -1,6 +1,6 @@
1
1
  //#region src/api-keys/caller-settings.ts
2
2
  /**
3
- * The transaction settings a request admitted by key (`/rest/v1`, `/storage/v1`) runs with, as Supabase's PostgREST
3
+ * The transaction settings a request admitted by key (`/api/sb/rest/v1`, `/api/sb/storage/v1`) runs with, as Supabase's PostgREST
4
4
  * sets them: `role`, which is `SET LOCAL ROLE`, to `anon` or `authenticated`, and the caller's claims in `request.jwt.claims` and its user in
5
5
  * `request.jwt.claim.sub`, which `auth.uid()`, `auth.role()` and `auth.jwt()` read. The `role` claim names the
6
6
  * database role, as on Supabase, whatever the token's own says. The app's role, of the secret key, sets no role: the
@@ -72,11 +72,11 @@ export type CreateAdminInput = {
72
72
  formsDifficulty?: Difficulty;
73
73
  /** @internal The users comments name, mention and mail, which tests fake; from `auth` otherwise. */
74
74
  commentUsers?: CommentUsers;
75
- /** @internal Verifies a user's token on `/rest/v1`, `/realtime/v1` and `/storage/v1` and gives its claims, which tests fake; those `/auth/v1` signs otherwise. */
75
+ /** @internal Verifies a user's token on `/api/sb/rest/v1`, `/api/sb/realtime/v1` and `/api/sb/storage/v1` and gives its claims, which tests fake; those `/api/sb/auth/v1` signs otherwise. */
76
76
  verifyToken?: (token: string) => Promise<JWTPayload>;
77
77
  /**
78
78
  * The host's WebSocket upgrade, such as `@hono/node-server`'s, with which an app with keys and live resources serves
79
- * supabase-js's Realtime at `/realtime/v1/websocket`; without it, the app serves none.
79
+ * supabase-js's Realtime at `/api/sb/realtime/v1/websocket`; without it, the app serves none.
80
80
  */
81
81
  upgradeWebSocket?: UpgradeWebSocket;
82
82
  };
@@ -19,6 +19,7 @@ import { realtimeApp } from "./realtime/realtime-app.js";
19
19
  import { restRoutes } from "./rest/rest-routes.js";
20
20
  import { storageRoutes } from "./storage/storage-routes.js";
21
21
  import { readAppName } from "./app-name.js";
22
+ import { realtimePath, restPath, storagePath, supabaseFunctionsPath, supabasePath } from "./supabase-paths.js";
22
23
  import { goTruePath } from "./gotrue/keys.js";
23
24
  import { authBasePath } from "./better-auth/auth-base-path.js";
24
25
  import { organizationRoutes } from "./better-auth/organizations/admin-routes.js";
@@ -55,6 +56,7 @@ import { fileRoutes } from "./files/routes.js";
55
56
  import { formRoutes } from "./forms/routes.js";
56
57
  import { createFormsRuntime } from "./forms/runtime.js";
57
58
  import { functionRoutes } from "./functions/routes.js";
59
+ import { supabaseFunctionRoutes } from "./functions/supabase-routes.js";
58
60
  import { localeSetting } from "./locale/locale-option.js";
59
61
  import { mapSettings } from "./maps/map-settings.js";
60
62
  import { pluginOptions, pluginsBeforeSignIn } from "./plugins/plugin-settings.js";
@@ -229,7 +231,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
229
231
  const corsOrigins = withMobileOrigins(options.cors ?? readCorsOrigins(globalThis.process?.env ?? {}), Boolean(mobile));
230
232
  const ownOrigins = auth?.origins ?? [];
231
233
  if (corsOrigins !== void 0 || ownOrigins.length > 1) {
232
- const answering = [authBasePath, ...Object.entries(apiFunctions).filter(([, fn]) => fn.options.cors).map(([name]) => `${systemPath}/functions/${name}`)];
234
+ const answering = [authBasePath, ...Object.entries(apiFunctions).filter(([, fn]) => fn.options.cors).flatMap(([name]) => [`${systemPath}/functions/${name}`, `${supabaseFunctionsPath}/${name}`])];
233
235
  const skip = (path) => answering.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));
234
236
  const cors = apiCors({
235
237
  own: ownOrigins,
@@ -237,7 +239,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
237
239
  skip
238
240
  });
239
241
  app.use(`${systemPath}/*`, cors);
240
- if (auth && !"/auth/v1".startsWith(`${systemPath}/`)) app.use(`${goTruePath}/*`, cors);
242
+ if ((servesKeys || auth) && !"/api/sb".startsWith(`${systemPath}/`)) app.use(`${supabasePath}/*`, cors);
241
243
  }
242
244
  if (auth) {
243
245
  app.route(authBasePath, statusRoute(auth, {
@@ -279,6 +281,17 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
279
281
  onError,
280
282
  wake: () => wakeDispatch(deps)
281
283
  }));
284
+ const verifyGoTrue = auth ? jwtVerifier(goTrueTokens({ auth })) : () => Promise.reject(unauthorized("This app signs no user tokens"));
285
+ app.route("/", supabaseFunctionRoutes({
286
+ functions: apiFunctions,
287
+ db,
288
+ keys: apiKeys,
289
+ caller: apiKeyCaller({
290
+ keys: apiKeys,
291
+ verify: verifyGoTrue
292
+ }),
293
+ onError
294
+ }));
282
295
  app.route("/", batchRoute(deps, authenticate, meta));
283
296
  app.route("/", fileRoutes(deps, authenticate));
284
297
  if (forms) app.route(`${systemPath}/forms`, formRoutes(deps, forms));
@@ -288,19 +301,18 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
288
301
  schema: changes,
289
302
  report
290
303
  });
291
- if (appModels.some((model) => model.live)) app.route(systemPath || "/", guarded((live) => void live.route("/", liveRoutes(feed, (session) => visibleNotices(deps, session))), withAnonymous(authenticate, options.roles)));
292
304
  if (servesKeys) {
293
305
  const verify = verifyToken ?? (auth ? jwtVerifier(goTrueTokens({ auth })) : () => Promise.reject(unauthorized("This app signs no user tokens")));
294
306
  const caller = apiKeyCaller({
295
307
  keys: apiKeys,
296
308
  verify
297
309
  });
298
- app.route("/rest/v1", restRoutes({
310
+ app.route(restPath, restRoutes({
299
311
  deps,
300
312
  caller,
301
313
  report
302
314
  }));
303
- if (upgradeWebSocket) app.route("/realtime/v1", realtimeApp({
315
+ if (upgradeWebSocket) app.route(realtimePath, realtimeApp({
304
316
  deps,
305
317
  feed,
306
318
  upgradeWebSocket,
@@ -317,12 +329,13 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
317
329
  maxBytes: files.storage.maxBytes,
318
330
  caller
319
331
  };
320
- app.route("/storage/v1", storageRoutes({
332
+ app.route(storagePath, storageRoutes({
321
333
  deps: storage,
322
334
  report
323
335
  }));
324
336
  }
325
337
  }
338
+ if (appModels.some((model) => model.live)) app.route(systemPath || "/", guarded((live) => void live.route("/", liveRoutes(feed, (session) => visibleNotices(deps, session))), withAnonymous(authenticate, options.roles)));
326
339
  app.route(basePath, api);
327
340
  app.route(`${deps.systemPath}/meta`, metaApp);
328
341
  app.route(systemPath || "/", webManifestRoute(appName, identity, icons));
@@ -23,9 +23,9 @@ export type FilesOptions = {
23
23
  */
24
24
  trustedHosts?: string[];
25
25
  /**
26
- * Where `supabase.storage` keeps the bytes of its objects, which the app serves at `/storage/v1` when it has an API key
26
+ * Where `supabase.storage` keeps the bytes of its objects, which the app serves at `/api/sb/storage/v1` when it has an API key
27
27
  * (see `apiKeys`): `provider` names one of the providers, default `private`, and `maxFileSize` caps every upload, as
28
- * `50 MB` (the default) or a number of bytes; a bucket's own limit can only be lower. `false` serves no `/storage/v1`.
28
+ * `50 MB` (the default) or a number of bytes; a bucket's own limit can only be lower. `false` serves no `/api/sb/storage/v1`.
29
29
  */
30
30
  storage?: {
31
31
  provider?: string;
@@ -10,7 +10,7 @@ import { parseSize } from "@protobase/schema/internal";
10
10
  const ticketSeconds = 86400;
11
11
  const imageFields = (models) => models.flatMap((model) => Object.values(model.fields).flatMap(({ name, file }) => file && (file.variants || file.original || file.placeholder !== void 0) ? [`${model.name}.${name}`] : []));
12
12
  /**
13
- * The runtime, when a resource has a file field, the config names providers or the app serves `/storage/v1`. It fails at startup when a field's
13
+ * The runtime, when a resource has a file field, the config names providers or the app serves `/api/sb/storage/v1`. It fails at startup when a field's
14
14
  * provider is not configured, nothing signs uploads, or a field makes image variants or placeholders without sharp.
15
15
  */
16
16
  const createFilesRuntime = ({ options = {}, env, models, systemPath, storage: servesStorage = false }) => {
@@ -23,7 +23,7 @@ const createFilesRuntime = ({ options = {}, env, models, systemPath, storage: se
23
23
  const used = [.../* @__PURE__ */ new Set([...fieldProviders, ...storage ? [storage.provider] : []])];
24
24
  if (used.length === 0 && !options.providers) return void 0;
25
25
  const secret = options.secret ?? env.PROTOBASE_FILES_SECRET ?? env.BETTER_AUTH_SECRET;
26
- if (!secret) throw new Error("File fields and /storage/v1 need a secret to sign uploads: set BETTER_AUTH_SECRET, PROTOBASE_FILES_SECRET or files.secret");
26
+ if (!secret) throw new Error("File fields and /api/sb/storage/v1 need a secret to sign uploads: set BETTER_AUTH_SECRET, PROTOBASE_FILES_SECRET or files.secret");
27
27
  const images = imageFields(models);
28
28
  if (images.length > 0 && !options.sharp) throw new Error(`${images.join(", ")} make image variants or placeholders, which need sharp: add it to the project (bun add sharp), then import sharp from 'sharp' in the config and pass files: { sharp }`);
29
29
  const providers = resolveProviders(options, env, used);
@@ -2,7 +2,7 @@ import { FileProvider } from "./store.js";
2
2
  import { FilesOptions } from "./options.js";
3
3
  //#region src/files/storage-provider.d.ts
4
4
  /**
5
- * The provider `/storage/v1` keeps object bytes in, as the app resolves it from the config and the environment, for a
5
+ * The provider `/api/sb/storage/v1` keeps object bytes in, as the app resolves it from the config and the environment, for a
6
6
  * command that writes them, such as `protobase import supabase`. Fails when `files.storage` is `false`.
7
7
  */
8
8
  export declare const storageProvider: (options: FilesOptions, env: Record<string, string | undefined>) => {
@@ -1,9 +1,9 @@
1
1
  import { resolveProviders } from "./providers.js";
2
2
  //#region src/files/storage-provider.ts
3
- /** The provider `/storage/v1` keeps object bytes in: `files.storage.provider`, default `private`. */
3
+ /** The provider `/api/sb/storage/v1` keeps object bytes in: `files.storage.provider`, default `private`. */
4
4
  const storageProviderName = (options) => options.storage === false ? void 0 : options.storage?.provider ?? "private";
5
5
  /**
6
- * The provider `/storage/v1` keeps object bytes in, as the app resolves it from the config and the environment, for a
6
+ * The provider `/api/sb/storage/v1` keeps object bytes in, as the app resolves it from the config and the environment, for a
7
7
  * command that writes them, such as `protobase import supabase`. Fails when `files.storage` is `false`.
8
8
  */
9
9
  const storageProvider = (options, env) => {
@@ -1,4 +1,5 @@
1
1
  import { Session } from "../types.js";
2
+ import { Caller } from "../api-keys/resolve-caller.js";
2
3
  import { FunctionRecords } from "./records.js";
3
4
  import { Capability } from "@protobase/schema";
4
5
  import { Db } from "@protobase/query";
@@ -20,6 +21,13 @@ export type FunctionCaller = {
20
21
  };
21
22
  /** What a function that needs a signed-in caller gets: the caller resolved before it runs. */
22
23
  export type FunctionContext = PublicFunctionContext & FunctionCaller;
24
+ /** What a function gets at `/api/sb/functions/v1/<name>`, where supabase-js calls it: the caller its keys and token make. */
25
+ export type KeyFunctionContext = {
26
+ name: string;
27
+ db: Db;
28
+ /** The caller of the `apikey` header and bearer token: rejects with a 401 problem when they admit nobody. */
29
+ caller: () => Promise<Caller>;
30
+ };
23
31
  /** A Web-standard handler. Its request's path is the part after `/api/functions/<name>`: `/` or `/more/parts`. */
24
32
  export type FunctionHandler = (request: Request, context: FunctionContext) => Response | Promise<Response>;
25
33
  export type PublicFunctionHandler = (request: Request, context: PublicFunctionContext) => Response | Promise<Response>;
@@ -59,4 +59,4 @@ const functionRoutes = ({ basePath, functions, authenticate, access, db, records
59
59
  return app;
60
60
  };
61
61
  //#endregion
62
- export { functionRoutes };
62
+ export { functionRoutes, relativeRequest };
@@ -0,0 +1,49 @@
1
+ import { notFound, unauthorized } from "../problem.js";
2
+ import { supabaseFunctionsPath } from "../supabase-paths.js";
3
+ import { invokeFunction } from "./define-function.js";
4
+ import { runInBackground } from "../background-work.js";
5
+ import { relativeRequest } from "./routes.js";
6
+ import { Hono } from "hono";
7
+ import { cors } from "hono/cors";
8
+ //#region src/functions/supabase-routes.ts
9
+ /**
10
+ * The app's functions again at `/api/sb/functions/v1/<name>`, as Supabase serves Edge Functions. A protected function lets in
11
+ * a request its app's keys admit, as Supabase's `verify_jwt` does, and a public one anyone. Either gets the key-made
12
+ * caller, not the app's session, so a function with `roles` is served at `/api/functions/<name>` only. As on Supabase,
13
+ * the function sees the path from its name on, `/<name>/more/parts`, and a CORS preflight, which carries no key,
14
+ * reaches it unchecked.
15
+ */
16
+ const supabaseFunctionRoutes = ({ functions, db, keys, caller, onError }) => {
17
+ const app = new Hono().basePath(supabaseFunctionsPath);
18
+ app.onError(onError);
19
+ const hasKeys = keys.publishable !== void 0 || keys.secret !== void 0;
20
+ for (const [name, fn] of Object.entries(functions)) {
21
+ const { options } = fn;
22
+ if (options.cors) app.use(`/${name}/*`, cors(options.cors));
23
+ app.all(`/${name}/*`, (c) => runInBackground(async () => {
24
+ if (!options.public && options.roles) throw notFound(`The function "${name}" checks roles, so it is served at /api/functions/${name} only`);
25
+ const request = relativeRequest(c.req.raw, supabaseFunctionsPath);
26
+ if (options.public || c.req.method === "OPTIONS") {
27
+ let resolved;
28
+ return invokeFunction(name, fn, request, {
29
+ name,
30
+ db,
31
+ caller: () => resolved ??= caller(c.req.raw)
32
+ });
33
+ }
34
+ if (!hasKeys) throw unauthorized("This app has no keys, so only its public functions answer here");
35
+ const resolved = await caller(c.req.raw);
36
+ return invokeFunction(name, fn, request, {
37
+ name,
38
+ db,
39
+ caller: async () => resolved
40
+ });
41
+ }));
42
+ }
43
+ app.all("/*", (c) => {
44
+ throw notFound(`There is no function at ${c.req.path}`);
45
+ });
46
+ return app;
47
+ };
48
+ //#endregion
49
+ export { supabaseFunctionRoutes };
@@ -0,0 +1,10 @@
1
+ //#region src/functions/wait-until.d.ts
2
+ /**
3
+ * Keeps the process up until `work` settles, as Supabase's `EdgeRuntime.waitUntil`: a function answers at once and
4
+ * goes on working after its response. A graceful shutdown awaits the work (`backgroundSettled`); a process killed
5
+ * before it settles loses it. A rejection is printed, as nobody awaits the work to see it.
6
+ */
7
+ export declare const waitUntil: (work: Promise<unknown>) => void;
8
+ /** Resolves once every `waitUntil` work has settled, including work started while it waits. */
9
+ export declare const backgroundSettled: () => Promise<void>;
10
+ //#endregion
@@ -0,0 +1,18 @@
1
+ //#region src/functions/wait-until.ts
2
+ const pending = /* @__PURE__ */ new Set();
3
+ /**
4
+ * Keeps the process up until `work` settles, as Supabase's `EdgeRuntime.waitUntil`: a function answers at once and
5
+ * goes on working after its response. A graceful shutdown awaits the work (`backgroundSettled`); a process killed
6
+ * before it settles loses it. A rejection is printed, as nobody awaits the work to see it.
7
+ */
8
+ const waitUntil = (work) => {
9
+ const tracked = work.then(() => void 0, (error) => console.error(error));
10
+ pending.add(tracked);
11
+ tracked.then(() => pending.delete(tracked));
12
+ };
13
+ /** Resolves once every `waitUntil` work has settled, including work started while it waits. */
14
+ const backgroundSettled = async () => {
15
+ while (pending.size > 0) await Promise.all(pending);
16
+ };
17
+ //#endregion
18
+ export { backgroundSettled, waitUntil };
@@ -0,0 +1,5 @@
1
+ import "jose";
2
+ //#region src/gotrue/keys.d.ts
3
+ /** Where the GoTrue-compatible endpoints are served, below the app's origin. */
4
+ export declare const goTruePath = "/api/sb/auth/v1";
5
+ //#endregion
@@ -1,6 +1,7 @@
1
+ import { supabasePath } from "../supabase-paths.js";
1
2
  //#region src/gotrue/keys.ts
2
3
  /** Where the GoTrue-compatible endpoints are served, below the app's origin. */
3
- const goTruePath = "/auth/v1";
4
+ const goTruePath = `${supabasePath}/auth/v1`;
4
5
  /** The key pair the JWT plugin keeps beside its EdDSA one for these tokens; it makes it on the first token. */
5
6
  const goTrueKeyPair = { alg: "ES256" };
6
7
  const goTrueAlgorithm = goTrueKeyPair.alg;
package/dist/index.d.ts CHANGED
@@ -22,6 +22,8 @@ import { EventData, RunContext, SubscriptionDefinition, SubscriptionEvent, Subsc
22
22
  import { Sharp, renderVariant, thumbHash } from "./files/images.js";
23
23
  import { FileProvider, FileStore, StoredObject } from "./files/store.js";
24
24
  import { FilesOptions } from "./files/options.js";
25
+ import { ApiKeys, ApiKeysOptions, apiKeySettings, apiKeyVariables } from "./api-keys/api-key-settings.js";
26
+ import { Caller, CallerOptions, apiKeyCaller } from "./api-keys/resolve-caller.js";
25
27
  import { ErrorReportingOptions, ErrorReportingSettings, errorReportingSettings, errorReportingVariable } from "./error-reporting/error-reporting-settings.js";
26
28
  import { ChatMessage, ChatTextPart, Completion, CompletionOptions, ModelError, ModelSettings, ReasoningDetail, ReasoningEffort, ToolCall, ToolSpec, streamChatCompletion } from "./assistant/chat-completions.js";
27
29
  import { CleanupEntry, CleanupSchedule, ScheduledDelete, storageCleanupSchedule } from "./files/cleanup-schedule.js";
@@ -33,9 +35,8 @@ import { ResourceSource, configExports } from "./resource-source.js";
33
35
  import { SourceOption, SourceRef, checkSourceNames, sourceOptions, sourcesFor, sourcesVariable } from "./sources/source-settings.js";
34
36
  import { RecordKeyInput, ToolRecord, ToolRecords } from "./assistant/records.js";
35
37
  import { FunctionRecords, ListParams, RecordPage } from "./functions/records.js";
36
- import { ApiFunction, CorsOptions, FunctionApp, FunctionCaller, FunctionContext, FunctionHandler, FunctionOptions, FunctionSource, ProtectedFunctionOptions, PublicFunctionContext, PublicFunctionHandler, PublicFunctionOptions, defineFunction } from "./functions/define-function.js";
38
+ import { ApiFunction, CorsOptions, FunctionApp, FunctionCaller, FunctionContext, FunctionHandler, FunctionOptions, FunctionSource, KeyFunctionContext, ProtectedFunctionOptions, PublicFunctionContext, PublicFunctionHandler, PublicFunctionOptions, defineFunction } from "./functions/define-function.js";
37
39
  import { CorsOrigins } from "./api-cors.js";
38
- import { ApiKeys, ApiKeysOptions, apiKeySettings, apiKeyVariables } from "./api-keys/api-key-settings.js";
39
40
  import { ConversationRecord, ConversationStore } from "./assistant/conversation-store.js";
40
41
  import { Conversation, ConversationsOptions, conversationKey, createConversations } from "./assistant/conversations.js";
41
42
  import { ApprovalAnswer, ApprovalRequest, requestApproval } from "./assistant/approval.js";
@@ -50,7 +51,6 @@ import { checkViewQueries } from "./check-view-queries.js";
50
51
  import { ProjectConfig, defineConfig, mergeConfig } from "./project-config.js";
51
52
  import { consoleAuditQueue } from "./audit/console-queue.js";
52
53
  import { JwtOptions, JwtVerifyOptions, jwtAuthenticator, jwtVerifier } from "./auth/jwt.js";
53
- import { Caller, CallerOptions, apiKeyCaller } from "./api-keys/resolve-caller.js";
54
54
  import { HttpProblem, MissingCredentials, badRequest, forbidden, notFound, unauthorized } from "./problem.js";
55
55
  import { TrustedApp, withTrustedApps } from "./better-auth/app-tokens.js";
56
56
  import { BetterAuthAuthenticatorOptions, betterAuthAuthenticator, betterAuthTokens } from "./better-auth/authenticator.js";
@@ -58,6 +58,8 @@ import { parseRoles } from "./better-auth/parse-roles.js";
58
58
  import { LinkedAccount, NewUser, StoredUser, createUser, deleteUser, hasUsers, listUsers, roleChoices, setUserBanned, setUserRole } from "./better-auth/users.js";
59
59
  import { IssueTokenOptions, issueToken, maxTokenTtlSeconds } from "./better-auth/tokens.js";
60
60
  import { webauthnPath } from "./better-auth/origins.js";
61
+ import { goTruePath } from "./gotrue/keys.js";
62
+ import { supabasePath } from "./supabase-paths.js";
61
63
  import { defaultAppName } from "./app-name.js";
62
64
  import { authSchemaMigration, checkAuthSchema } from "./better-auth/auth-schema.js";
63
65
  import { changesMigration, checkChanges, migrationCommand } from "./changes/capture-schema.js";
@@ -86,6 +88,7 @@ import { startErrorReporting } from "./error-reporting/error-reporter.js";
86
88
  import { FunctionEnv, PublicFunctionEnv, functionApp } from "./functions/function-app.js";
87
89
  import { SignatureOptions, StandardWebhookOptions, verifySignature, verifyStandardWebhook } from "./functions/verify-signature.js";
88
90
  import { requireEnv } from "./functions/require-env.js";
91
+ import { backgroundSettled, waitUntil } from "./functions/wait-until.js";
89
92
  import { FileLimits, limitedFileStore, storeUsage } from "./files/limits.js";
90
93
  import { LocalFilesOptions, localFileStore, localFiles } from "./files/local-files.js";
91
94
  import { StoreFileInput, storeFile } from "./files/store-file.js";
@@ -102,4 +105,4 @@ import { isSchedule } from "./jobs/schedule.js";
102
105
  import { DrainResult, JobsRuntime, StartJobsOptions, startJobs } from "./jobs/runtime.js";
103
106
  import { jobsDrainApp } from "./jobs/drain-route.js";
104
107
  import { checkJobsSchema, jobsSchema, jobsSchemaMigration } from "./jobs/schema.js";
105
- export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApiKeys, type ApiKeysOptions, type AppIcons, type AppIdentity, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BetterAuthAuthenticatorOptions, type Caller, type CallerOptions, type CapturedRow, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentUsers, type Completion, type CompletionOptions, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type DrainResult, type EmailComponent, type EmailOptions, type EmailTemplate, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileRef, type FileStore, type FileVariantObject, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSource, type HistoryOptions, HttpProblem, type IssueTokenOptions, type JobContext, type JobDefinition, type JobOptions, type JobsRuntime, type JwtOptions, type JwtVerifyOptions, type LinkedAccount, type ListParams, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type MemberInput, MissingCredentials, type MobileConfig, ModelError, type ModelSettings, type NewOrganization, type NewUser, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type PngIcon, 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 RenderedEmail, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RunContext, type RuntimeOptions, type S3Bucket, type S3FilesOptions, type S3ListPage, type S3Object, type ScanGuardMode, type ScheduledDelete, type Session, type SessionOrganization, type Sharp, type SignInPolicy, type SignInRule, type SignatureOptions, type SourceOption, type SourceRef, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoreFileInput, type StoredMember, type StoredObject, type StoredOrganization, type StoredUser, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type User, type UserCreatedHook, type ViewSource, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, handleDroppedConnections, hasUsers, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, objectKey, parseColumns, parseRoles, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, thumbHash, unauthorized, variantPath, verifySignature, verifyStandardWebhook, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
108
+ export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApiKeys, type ApiKeysOptions, type AppIcons, type AppIdentity, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BetterAuthAuthenticatorOptions, type Caller, type CallerOptions, type CapturedRow, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentUsers, type Completion, type CompletionOptions, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type DrainResult, type EmailComponent, type EmailOptions, type EmailTemplate, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileRef, type FileStore, type FileVariantObject, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSource, type HistoryOptions, HttpProblem, type IssueTokenOptions, type JobContext, type JobDefinition, type JobOptions, type JobsRuntime, type JwtOptions, type JwtVerifyOptions, type KeyFunctionContext, type LinkedAccount, type ListParams, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type MemberInput, MissingCredentials, type MobileConfig, ModelError, type ModelSettings, type NewOrganization, type NewUser, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type PngIcon, 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 RenderedEmail, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RunContext, type RuntimeOptions, type S3Bucket, type S3FilesOptions, type S3ListPage, type S3Object, type ScanGuardMode, type ScheduledDelete, type Session, type SessionOrganization, type Sharp, type SignInPolicy, type SignInRule, type SignatureOptions, type SourceOption, type SourceRef, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoreFileInput, type StoredMember, type StoredObject, type StoredOrganization, type StoredUser, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type User, type UserCreatedHook, type ViewSource, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, backgroundSettled, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, goTruePath, handleDroppedConnections, hasUsers, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, objectKey, parseColumns, parseRoles, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, variantPath, verifySignature, verifyStandardWebhook, waitUntil, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
package/dist/index.js CHANGED
@@ -23,6 +23,8 @@ import { apiKeyCaller } from "./api-keys/resolve-caller.js";
23
23
  import { jwtAuthenticator, jwtVerifier } from "./auth/jwt.js";
24
24
  import { objectKey } from "./storage/object-key.js";
25
25
  import { defaultAppName } from "./app-name.js";
26
+ import { supabasePath } from "./supabase-paths.js";
27
+ import { goTruePath } from "./gotrue/keys.js";
26
28
  import { defaultSignInPolicy, signInPolicyMethods } from "./better-auth/sign-in-policy.js";
27
29
  import { parseRoles } from "./better-auth/parse-roles.js";
28
30
  import { readOperatorSettings } from "./better-auth/operator-provider.js";
@@ -71,10 +73,11 @@ import { functionApp } from "./functions/function-app.js";
71
73
  import { readJson } from "./functions/read-json.js";
72
74
  import { verifySignature, verifyStandardWebhook } from "./functions/verify-signature.js";
73
75
  import { requireEnv } from "./functions/require-env.js";
76
+ import { backgroundSettled, waitUntil } from "./functions/wait-until.js";
74
77
  import { storeFile } from "./files/store-file.js";
75
78
  import { createFileCleanup } from "./files/file-cleanup.js";
76
79
  import { aspectRatio, fileSize, fileType, imageSize, processor } from "./files/processors.js";
77
80
  import { defineEmail } from "./mail/define-email.js";
78
81
  import { startJobs } from "./jobs/runtime.js";
79
82
  import { jobsDrainApp } from "./jobs/drain-route.js";
80
- export { FatalJobError, HttpProblem, MissingCredentials, ModelError, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, handleDroppedConnections, hasUsers, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, objectKey, parseColumns, parseRoles, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, thumbHash, unauthorized, variantPath, verifySignature, verifyStandardWebhook, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
83
+ export { FatalJobError, HttpProblem, MissingCredentials, ModelError, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, backgroundSettled, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, goTruePath, handleDroppedConnections, hasUsers, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, objectKey, parseColumns, parseRoles, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, variantPath, verifySignature, verifyStandardWebhook, waitUntil, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
@@ -24,7 +24,7 @@ const seen = (entry, filters, rows) => new Map(rows.map((row) => [encodeKey(keyC
24
24
  }]));
25
25
  /**
26
26
  * Checks which changes callers may see, for every group in one read-only transaction: per group it sets the role and
27
- * claims as `/rest/v1` does, then reads the current rows by key, so the table's RLS policies decide, and evaluates
27
+ * claims as `/api/sb/rest/v1` does, then reads the current rows by key, so the table's RLS policies decide, and evaluates
28
28
  * each filter in Postgres, as the column's type compares. The transaction holds one pooled connection only while it
29
29
  * runs. A group whose check fails is checked again in a transaction of its own, so it costs only its own callers the
30
30
  * batch.
@@ -3,7 +3,7 @@ import { changeHub } from "./change-hub.js";
3
3
  import { realtimeRoutes } from "./realtime-routes.js";
4
4
  //#region src/realtime/realtime-app.ts
5
5
  /**
6
- * `/realtime/v1`: the `postgres_changes` of the live resources, each checked under the subscriber's role, claims and
6
+ * `/api/sb/realtime/v1`: the `postgres_changes` of the live resources, each checked under the subscriber's role, claims and
7
7
  * the tables' RLS policies before it is sent, and broadcasts and presence between the channels of the process.
8
8
  */
9
9
  const realtimeApp = ({ deps, feed, upgradeWebSocket, caller, report }) => {
@@ -10,7 +10,7 @@ const keyRequest = (apikey, token) => new Request("http://realtime.invalid/", {
10
10
  ...token !== void 0 && { authorization: `Bearer ${token}` }
11
11
  } });
12
12
  /**
13
- * `GET /realtime/v1/websocket?apikey=…&vsn=…`: Supabase Realtime's socket, for `postgres_changes` of the live
13
+ * `GET /api/sb/realtime/v1/websocket?apikey=…&vsn=…`: Supabase Realtime's socket, for `postgres_changes` of the live
14
14
  * resources, and broadcasts and presence between the sockets of this process (see `realtimeSocket`). A missing or wrong
15
15
  * key is refused before the upgrade, with 401.
16
16
  */
@@ -11,7 +11,7 @@ const refusal = (error) => {
11
11
  throw error;
12
12
  };
13
13
  /**
14
- * One WebSocket of `/realtime/v1`, speaking Phoenix channels as realtime-js expects: heartbeats, channel joins with
14
+ * One WebSocket of `/api/sb/realtime/v1`, speaking Phoenix channels as realtime-js expects: heartbeats, channel joins with
15
15
  * `postgres_changes` bindings and an access token, token refreshes on an open channel, broadcasts with their `self`
16
16
  * and `ack` options, presence tracking, and leaves. A channel's token ends it when it expires, unless a newer one came
17
17
  * first. Messages are handled one at a time, in order. Private channels are refused for now.
@@ -1,6 +1,6 @@
1
1
  //#region src/rest/caller-settings.ts
2
2
  /**
3
- * The transaction settings a `/rest/v1` request runs with, as Supabase's PostgREST sets them: `role`, which is
3
+ * The transaction settings a `/api/sb/rest/v1` request runs with, as Supabase's PostgREST sets them: `role`, which is
4
4
  * `SET LOCAL ROLE`, to `anon` or `authenticated`, and the caller's claims in `request.jwt.claims` and its user in
5
5
  * `request.jwt.claim.sub`, which `auth.uid()`, `auth.role()` and `auth.jwt()` read. The `role` claim names the
6
6
  * database role, as on Supabase, whatever the token's own says. The app's role, of the secret key, sets no role: the
@@ -15,7 +15,7 @@ const readForeignKeys = async (db) => {
15
15
  return rows;
16
16
  };
17
17
  /**
18
- * The foreign keys of the `public` schema, which `/rest/v1` embeds through, read from the catalog once and kept for the
18
+ * The foreign keys of the `public` schema, which `/api/sb/rest/v1` embeds through, read from the catalog once and kept for the
19
19
  * process lifetime, as the scan guard keeps indexes: migrations run before a new version serves.
20
20
  */
21
21
  const foreignKeyCache = (db) => {
@@ -7,7 +7,7 @@ const embedPattern = /^(?:([^:(!]+):(?!:))?([^!(]+)((?:![^!(]+)*)\(([\s\S]*)\)$/
7
7
  const parseEmbed = (item) => {
8
8
  const [, alias, nameText = "", modifiers = "", inner = ""] = embedPattern.exec(item) ?? [];
9
9
  const name = nameText.trim();
10
- if (!name || name.startsWith("...") || name.includes(".") || name === "count") throw restError(400, "PGRST100", `"${item}" spreads a resource or calls an aggregate, which /rest/v1 does not support yet`);
10
+ if (!name || name.startsWith("...") || name.includes(".") || name === "count") throw restError(400, "PGRST100", `"${item}" spreads a resource or calls an aggregate, which /api/sb/rest/v1 does not support yet`);
11
11
  const flags = modifiers.split("!").slice(1).map((flag) => flag.trim());
12
12
  const hint = flags.find((flag) => flag !== "inner" && flag !== "left");
13
13
  return {
@@ -1,6 +1,6 @@
1
1
  import { isDatabaseError } from "../error-handler.js";
2
2
  //#region src/rest/rest-error.ts
3
- /** A `/rest/v1` error: its status, the body, and headers such as `Content-Range`. Thrown anywhere below the route. */
3
+ /** A `/api/sb/rest/v1` error: its status, the body, and headers such as `Content-Range`. Thrown anywhere below the route. */
4
4
  var RestError = class RestError extends Error {
5
5
  status;
6
6
  body;
@@ -66,7 +66,7 @@ const databaseStatus = (code, message, anonymous) => {
66
66
  return codeStatus[code] ?? prefixStatus.find(([prefix]) => code.startsWith(prefix))?.[1] ?? 400;
67
67
  };
68
68
  /**
69
- * The `/rest/v1` error an error stands for: its own, or a database error with PostgREST's status and the error's
69
+ * The `/api/sb/rest/v1` error an error stands for: its own, or a database error with PostgREST's status and the error's
70
70
  * message, detail and hint, which PostgREST passes on as they are. Other errors give `undefined`. Refusing a write by
71
71
  * RLS is 401 for an anonymous caller and 403 for others, as on Supabase.
72
72
  */
@@ -7,7 +7,7 @@ import { tableRoutes } from "./table-routes.js";
7
7
  import { Hono } from "hono";
8
8
  //#region src/rest/rest-routes.ts
9
9
  /**
10
- * The PostgREST-compatible API supabase-js's `from()` and `rpc()` call, mounted at `/rest/v1`. Each request runs in one
10
+ * The PostgREST-compatible API supabase-js's `from()` and `rpc()` call, mounted at `/api/sb/rest/v1`. Each request runs in one
11
11
  * transaction as `anon`, `authenticated` or the app's own role, so the tables' RLS policies decide what it may read and
12
12
  * write. Errors come back as PostgREST's `{ code, details, hint, message }`; an unexpected one as a 500 that names
13
13
  * nothing of it.
@@ -38,7 +38,7 @@ const call = async (deps, c) => {
38
38
  const source = callSource(chosen, args);
39
39
  const query = parseQuery(params, c.req.header("range") ?? null, new Set(given));
40
40
  const shape = query.select.map((item) => {
41
- if (item.kind === "embed") throw restError(400, "PGRST100", `"${item.name}" embeds a resource in a function's rows, which /rest/v1 does not support yet`);
41
+ if (item.kind === "embed") throw restError(400, "PGRST100", `"${item.name}" embeds a resource in a function's rows, which /api/sb/rest/v1 does not support yet`);
42
42
  return item;
43
43
  });
44
44
  if (chosen.info.returns === "void") {
@@ -13,7 +13,7 @@ import { Hono } from "hono";
13
13
  import { sql } from "kysely";
14
14
  //#region src/rest/table-routes.ts
15
15
  /**
16
- * The resource whose table `name` is, in the `public` schema or the search path's: `/rest/v1` serves the tables the app
16
+ * The resource whose table `name` is, in the `public` schema or the search path's: `/api/sb/rest/v1` serves the tables the app
17
17
  * declares as resources, as PostgREST serves those of its exposed schema. Any other is 404 (PGRST205).
18
18
  */
19
19
  const servedEntry = (deps, name) => deps.registry.entries.find((one) => one.model.table.name === name && (one.model.table.schema ?? "public") === "public");
@@ -181,7 +181,7 @@ const remove = async (deps, foreignKeys, c) => {
181
181
  return withHeaders(writeResponse(written, object, 200, 204, `*/${counted(prefer, written.total) ?? "*"}`), warning);
182
182
  };
183
183
  /**
184
- * `/rest/v1/<table>`: read with GET and HEAD, insert and upsert with POST, update with PATCH, delete with DELETE. Reads
184
+ * `/api/sb/rest/v1/<table>`: read with GET and HEAD, insert and upsert with POST, update with PATCH, delete with DELETE. Reads
185
185
  * and the rows a write returns embed related rows through `foreignKeys`.
186
186
  */
187
187
  const tableRoutes = (deps, foreignKeys) => {
@@ -3,7 +3,7 @@ import { callerSettings } from "../api-keys/caller-settings.js";
3
3
  import { sql } from "kysely";
4
4
  //#region src/storage/storage-db.ts
5
5
  /**
6
- * Makes the rest of the transaction run as `caller`: its role and claims, as `/rest/v1` sets them (see
6
+ * Makes the rest of the transaction run as `caller`: its role and claims, as `/api/sb/rest/v1` sets them (see
7
7
  * `callerSettings`), so the restored `storage.objects` and `storage.buckets` policies decide what it reads and writes.
8
8
  * What runs before it runs as the app's own role, which owns the tables.
9
9
  */
@@ -5,7 +5,7 @@ import { signedRoutes } from "./signed-routes.js";
5
5
  import { Hono } from "hono";
6
6
  //#region src/storage/storage-routes.ts
7
7
  /**
8
- * The Storage-compatible API `supabase.storage` calls, mounted at `/storage/v1`, over the app's storage provider. Every
8
+ * The Storage-compatible API `supabase.storage` calls, mounted at `/api/sb/storage/v1`, over the app's storage provider. Every
9
9
  * read and write of `storage.objects` and `storage.buckets` runs as the request's caller (`anon`, `authenticated` or
10
10
  * the app's own role), so the policies restored with them decide; public buckets and signed URLs need no key. Errors
11
11
  * come back as Supabase Storage's `{ statusCode, error, message }`; an unexpected one as a 500 that names nothing of it.
@@ -0,0 +1,4 @@
1
+ //#region src/supabase-paths.d.ts
2
+ /** Where the Supabase-compatible endpoints live, below the app's origin: supabase-js takes `<origin>/api/sb` as its URL. */
3
+ export declare const supabasePath = "/api/sb";
4
+ //#endregion
@@ -0,0 +1,13 @@
1
+ //#region src/supabase-paths.ts
2
+ /** Where the Supabase-compatible endpoints live, below the app's origin: supabase-js takes `<origin>/api/sb` as its URL. */
3
+ const supabasePath = "/api/sb";
4
+ /** The PostgREST-compatible API of `from()` and `rpc()`. */
5
+ const restPath = `${supabasePath}/rest/v1`;
6
+ /** The Realtime-compatible socket of `channel()`. */
7
+ const realtimePath = `${supabasePath}/realtime/v1`;
8
+ /** The Storage-compatible API of `storage`. */
9
+ const storagePath = `${supabasePath}/storage/v1`;
10
+ /** Where `functions.invoke` calls a function: `<origin>/api/sb/functions/v1/<name>`. */
11
+ const supabaseFunctionsPath = `${supabasePath}/functions/v1`;
12
+ //#endregion
13
+ export { realtimePath, restPath, storagePath, supabaseFunctionsPath, supabasePath };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/server",
3
- "version": "0.30.2",
3
+ "version": "0.31.1",
4
4
  "description": "The Protobase REST API as a Hono app, with Better Auth sign-in.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -32,9 +32,9 @@
32
32
  "dependencies": {
33
33
  "@better-auth/passkey": "^1.7.7",
34
34
  "@better-auth/sso": "^1.7.7",
35
- "@protobase/layout": "0.30.2",
36
- "@protobase/query": "0.30.2",
37
- "@protobase/schema": "0.30.2",
35
+ "@protobase/layout": "0.31.1",
36
+ "@protobase/query": "0.31.1",
37
+ "@protobase/schema": "0.31.1",
38
38
  "@react-email/render": "^2.1.0",
39
39
  "@scalar/hono-api-reference": "^0.12.10",
40
40
  "@sentry/core": "^11.7.0",