@rebasepro/server 0.14.0 → 0.14.1-canary.g7e666eb

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 (31) hide show
  1. package/dist/api/rest/query-parser.d.ts +15 -1
  2. package/dist/api/rest/write-validation.d.ts +26 -0
  3. package/dist/auth/interfaces.d.ts +14 -1
  4. package/dist/auth/jwt.d.ts +30 -2
  5. package/dist/{auth-CYoPVf-E.js → auth-BQcdhMBL.js} +64 -165
  6. package/dist/auth-BQcdhMBL.js.map +1 -0
  7. package/dist/{cron-store-Dvr4Y1sZ.js → cron-store-D5dUNviq.js} +2 -2
  8. package/dist/{cron-store-Dvr4Y1sZ.js.map → cron-store-D5dUNviq.js.map} +1 -1
  9. package/dist/ddl-bootstrap-BhXbTnBl.js.map +1 -1
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.es.js +267 -279
  12. package/dist/index.es.js.map +1 -1
  13. package/dist/{jwt-_IFqfTOg.js → jwt-CYGFT0ih.js} +297 -7
  14. package/dist/jwt-CYGFT0ih.js.map +1 -0
  15. package/dist/{openapi-generator-DPKtUC9X.js → openapi-generator-BWL8F2La.js} +24 -3
  16. package/dist/openapi-generator-BWL8F2La.js.map +1 -0
  17. package/dist/{request-timeout-RivJsME0.js → request-timeout-BuFoEKwT.js} +6 -3
  18. package/dist/request-timeout-BuFoEKwT.js.map +1 -0
  19. package/dist/{src-C7rkDGxA.js → src-8XDWyDfR.js} +84 -13
  20. package/dist/src-8XDWyDfR.js.map +1 -0
  21. package/dist/src-Cz9nMgUR.js.map +1 -1
  22. package/dist/storage/keys.d.ts +17 -0
  23. package/dist/storage/routes.d.ts +1 -1
  24. package/dist/storage/storage-registry.d.ts +46 -4
  25. package/dist/storage/tus-handler.d.ts +1 -1
  26. package/package.json +5 -5
  27. package/dist/auth-CYoPVf-E.js.map +0 -1
  28. package/dist/jwt-_IFqfTOg.js.map +0 -1
  29. package/dist/openapi-generator-DPKtUC9X.js.map +0 -1
  30. package/dist/request-timeout-RivJsME0.js.map +0 -1
  31. package/dist/src-C7rkDGxA.js.map +0 -1
package/dist/index.es.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import { createRequire as __createRequire } from "module";
2
2
  import process from "process";
3
3
  __createRequire(import.meta.url);
4
- import { A as RebaseApiError, D as EntityRelation, E as EntityReference, O as GeoPoint, _ as resolveCollectionRelations, b as toSnakeCase, c as collectAllPages, d as CollectionRegistry, f as createDataSourceRegistry, g as findRelation, h as fieldKeyForColumn, j as RebaseClientError, k as Vector, l as paginateFind, m as getEffectiveSecurityRules, n as buildSdkData, o as serializeFilter, p as resolveDataSource, s as serializeLogicalCondition, t as buildRoutedRebaseData, u as resolveFindWindow, v as buildCompositeId, y as resolvePrimaryKeys } from "./src-C7rkDGxA.js";
4
+ import { A as GeoPoint, M as RebaseApiError, N as RebaseClientError, O as EntityReference, S as toSnakeCase, _ as fieldKeyForColumn, b as buildCompositeId, c as collectAllPages, d as normalizeOrderBy, f as serializeOrderBy, g as getEffectiveSecurityRules, h as resolveDataSource, j as Vector, k as EntityRelation, l as paginateFind, m as createDataSourceRegistry, n as buildSdkData, o as serializeFilter, p as CollectionRegistry, s as serializeLogicalCondition, t as buildRoutedRebaseData, u as resolveFindWindow, v as findRelation, x as resolvePrimaryKeys, y as resolveCollectionRelations } from "./src-8XDWyDfR.js";
5
5
  import { c as DEFAULT_DATA_SOURCE_KEY, i as getCollectionDataPath, m as toCanonicalOp, n as RLS_ROLES_SQL, o as isPostgresCollectionConfig, r as RLS_UID_SQL, t as RLS_JWT_SQL } from "./src-Cz9nMgUR.js";
6
6
  import { n as ADMIN_PROPERTY_KEYS, t as ADMIN_COLLECTION_KEYS } from "./admin_block-H86dCPsj.js";
7
7
  import { i as isSQLAdmin, r as revokeInternalTableSql } from "./ddl-bootstrap-BhXbTnBl.js";
8
+ import { C as DEFAULT_STORAGE_SOURCE_KEY, E as storageEnvSuffix, T as normalizeStorageSources, _ as InvalidStorageKeyError, b as canonicalStorageKey, g as InvalidStorageBucketError, i as generateDownloadToken, n as configureJwt, v as canonicalStorageBucket, w as findStorageSuffixCollision, y as canonicalStorageId } from "./jwt-CYGFT0ih.js";
8
9
  import { n as createContractRoutes } from "./contract-routes-Bet-eCNJ.js";
9
- import { $ as fileTokenAuth, A as MemoryRateLimitStore, B as getEmailVerificationTemplate, C as ZodNumber, Ct as isPublicStoragePath, D as _coercedNumber, E as string, F as resolveAuthHooks, G as RawHtml, H as getPasswordResetTemplate, I as hashPassword, J as raw, K as escapeHtml, L as validatePasswordStrength, M as createEmailService, N as assertEmailLinkBases, O as DEFAULT_FUNCTIONS_ANONYMOUS_LIMIT, P as resolveEmailLinkBase, Q as extractUserFromToken, R as verifyPassword, S as createBuiltinAuthAdapter, St as PUBLIC_STORAGE_PREFIX, T as object, U as getUserInvitationTemplate, V as getMagicLinkTemplate, W as getWelcomeEmailTemplate, X as createAuthMiddleware, Y as createAdapterAuthMiddleware, Z as createRequireAuth, _ as createLinkedinProvider, _t as scopeDataDriver, a as createSpotifyProvider, at as InvalidStorageBucketError, b as pkceTokenParams, bt as parseQueryOptions, c as createGitLabProvider, ct as canonicalStorageKey, d as createFacebookProvider, dt as createStorageApiKeyGuard, et as optionalAuth, f as createAppleProvider, ft as isApiKeyToken, g as createGitHubProvider, gt as SERVICE_IDENTITY, h as verifyOidcIdToken, ht as safeCompare, i as createApiKeyStore, it as requireAuth, j as SMTPEmailService, k as createDataRateLimiter, l as createDiscordProvider, lt as createApiKeyPreAuth, m as tryVerifyOidcIdToken, mt as extractBearerToken, n as createCustomAuthAdapter, nt as queryTokenAuth, o as createSlackProvider, ot as InvalidStorageKeyError, p as createMicrosoftProvider, pt as validateApiKey, q as html, r as createApiKeyRoutes, rt as requireAdmin, s as createBitbucketProvider, st as canonicalStorageBucket, tt as publicObjectAuth, u as createTwitterProvider, ut as createFunctionApiKeyGuard, v as createGoogleProvider, vt as httpMethodToOperation, w as _enum, x as providerVerifiedEmail, xt as resolveListLimitParam, y as oauthCodeFlowSchema, yt as isOperationAllowed, z as generateSecurePassword } from "./auth-CYoPVf-E.js";
10
+ import { $ as fileTokenAuth, A as MemoryRateLimitStore, B as getEmailVerificationTemplate, C as ZodNumber, D as _coercedNumber, E as string, F as resolveAuthHooks, G as RawHtml, H as getPasswordResetTemplate, I as hashPassword, J as raw, K as escapeHtml, L as validatePasswordStrength, M as createEmailService, N as assertEmailLinkBases, O as DEFAULT_FUNCTIONS_ANONYMOUS_LIMIT, P as resolveEmailLinkBase, Q as extractUserFromToken, R as verifyPassword, S as createBuiltinAuthAdapter, T as object, U as getUserInvitationTemplate, V as getMagicLinkTemplate, W as getWelcomeEmailTemplate, X as createAuthMiddleware, Y as createAdapterAuthMiddleware, Z as createRequireAuth, _ as createLinkedinProvider, _t as parseQueryOptions, a as createSpotifyProvider, at as createApiKeyPreAuth, b as pkceTokenParams, bt as isPublicStoragePath, c as createGitLabProvider, ct as isApiKeyToken, d as createFacebookProvider, dt as safeCompare, et as optionalAuth, f as createAppleProvider, ft as SERVICE_IDENTITY, g as createGitHubProvider, gt as orderByEntriesToTuples, h as verifyOidcIdToken, ht as isOperationAllowed, i as createApiKeyStore, it as requireAuth, j as SMTPEmailService, k as createDataRateLimiter, l as createDiscordProvider, lt as validateApiKey, m as tryVerifyOidcIdToken, mt as httpMethodToOperation, n as createCustomAuthAdapter, nt as queryTokenAuth, o as createSlackProvider, ot as createFunctionApiKeyGuard, p as createMicrosoftProvider, pt as scopeDataDriver, q as html, r as createApiKeyRoutes, rt as requireAdmin, s as createBitbucketProvider, st as createStorageApiKeyGuard, tt as publicObjectAuth, u as createTwitterProvider, ut as extractBearerToken, v as createGoogleProvider, vt as resolveListLimitParam, w as _enum, x as providerVerifiedEmail, y as oauthCodeFlowSchema, yt as PUBLIC_STORAGE_PREFIX, z as generateSecurePassword } from "./auth-BQcdhMBL.js";
10
11
  import { t as logger } from "./logger-DfvF_8r-.js";
11
12
  import { n as errorHandler, t as ApiError } from "./errors-EBYiaJ2E.js";
12
- import { i as generateDownloadToken, n as configureJwt } from "./jwt-_IFqfTOg.js";
13
13
  import { t as logMiddleware } from "./logs-routes-BYA72C_C.js";
14
14
  import { n as DOCUMENT_MIME_TYPES, r as IMAGE_MIME_TYPES, t as DEFAULT_MAX_FILE_SIZE } from "./types-DSnOC4mF.js";
15
15
  import { t as S3StorageController } from "./S3StorageController-B2EDXXMH.js";
@@ -19,7 +19,7 @@ import { t as createFunctionRoutes } from "./function-routes-Btcez1T-.js";
19
19
  import { n as loadCronJobsFromDirectory, r as loadCronJobsWithDiagnostics } from "./cron-loader-YhhQeVBM.js";
20
20
  import { r as validateCronExpression, t as CronScheduler } from "./cron-scheduler-DIpYBmZP.js";
21
21
  import { t as createCronRoutes } from "./cron-routes-maM_RlUu.js";
22
- import { t as createCronStore } from "./cron-store-Dvr4Y1sZ.js";
22
+ import { t as createCronStore } from "./cron-store-D5dUNviq.js";
23
23
  import { a as parseBackupTimestamp, i as parseBackupDestination, n as createBackupRoutes, o as readBackupBytes, r as listBackupObjects } from "./backup-C6ljYVTp.js";
24
24
  import { createHash, randomBytes, randomUUID } from "node:crypto";
25
25
  import * as fs$2 from "fs";
@@ -49,153 +49,6 @@ import { secureHeaders } from "hono/secure-headers";
49
49
  import { getRequestListener } from "@hono/node-server";
50
50
  import { execFile } from "node:child_process";
51
51
  import { promisify as promisify$1 } from "node:util";
52
- //#region ../types/src/types/storage_source.ts
53
- /**
54
- * Describes a named storage backend — a place files live.
55
- *
56
- * Declared once and shared front + back: the frontend uses it to decide
57
- * transport (HTTP proxy vs direct SDK), the backend uses the same `key`
58
- * to resolve a StorageController, and collection properties reference
59
- * a definition by its `key` via `StorageConfig.storageSource`.
60
- *
61
- * This mirrors the {@link DataSourceDefinition} pattern used for databases.
62
- *
63
- * @group Models
64
- */
65
- /**
66
- * The default storage source key, used when a property does not specify
67
- * a `storageSource`. Shared by the frontend and backend registries so
68
- * both agree on "the default storage backend".
69
- * @group Models
70
- */
71
- var DEFAULT_STORAGE_SOURCE_KEY = "(default)";
72
- /**
73
- * The environment-variable suffix for a storage or data source key.
74
- *
75
- * `""` for the default source — so a single-bucket project keeps configuring
76
- * plain `S3_BUCKET` — and `__<KEY>` for every named one, uppercased with
77
- * non-alphanumerics collapsed to underscores: `media-cdn` → `S3_BUCKET__MEDIA_CDN`.
78
- *
79
- * The rule derives the variable name from the declared key rather than
80
- * discovering keys by scanning the environment. Scanning would have to guess how
81
- * `S3_BUCKET__MEDIA_CDN` splits into a key; deriving cannot be ambiguous, and a
82
- * typo surfaces as a missing source at boot instead of a silently ignored
83
- * variable.
84
- *
85
- * It lives in this package, with no dependencies, because four things must agree
86
- * on it exactly: the CLI (validating a build), the runtime (reading its own
87
- * environment), the control plane (writing a tenant's Secret), and the docs. A
88
- * second implementation of a naming convention is a second chance to disagree.
89
- *
90
- * @group Models
91
- */
92
- function storageEnvSuffix(key, defaultKey = DEFAULT_STORAGE_SOURCE_KEY) {
93
- if (!key || key === defaultKey) return "";
94
- const normalized = key.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
95
- if (!normalized) throw new Error(`Source key "${key}" cannot be turned into an environment variable name. Use a key containing at least one letter or digit.`);
96
- return `__${normalized}`;
97
- }
98
- /**
99
- * Two distinct keys that collapse onto the same variable name, or `null`.
100
- *
101
- * `media-cdn` and `media_cdn` are different source keys but the same suffix, so
102
- * without this one of them silently reads the other's configuration. Returns the
103
- * offending pair rather than throwing, so each caller can raise it in its own
104
- * idiom — a `BundleError` at boot, a build failure in the CLI, a rejected deploy
105
- * in a control plane.
106
- *
107
- * @group Models
108
- */
109
- function findStorageSuffixCollision(keys, defaultKey = DEFAULT_STORAGE_SOURCE_KEY) {
110
- const seen = /* @__PURE__ */ new Map();
111
- for (const key of keys) {
112
- const suffix = storageEnvSuffix(key, defaultKey);
113
- const existing = seen.get(suffix);
114
- if (existing !== void 0 && existing !== key) return {
115
- a: existing,
116
- b: key,
117
- suffix
118
- };
119
- seen.set(suffix, key);
120
- }
121
- return null;
122
- }
123
- /**
124
- * Merge the two places a project may declare storage sources into one list.
125
- *
126
- * `rebase.json` is authoritative for every field it states. Config code may add
127
- * sources it does not mention and fill in fields it left out, but may not
128
- * contradict it: the manifest is what a host reads to decide which buckets need
129
- * configuring, and a runtime that quietly disagreed with it would put the
130
- * console back to describing a topology the tenant does not have — the exact
131
- * failure this whole mechanism exists to end.
132
- *
133
- * Note what is *not* here: no default source is invented when both inputs are
134
- * empty. That decision belongs to the resolver, which knows whether declaring
135
- * nothing means "one plain bucket" (it does) or "no storage at all".
136
- *
137
- * @group Models
138
- */
139
- function normalizeStorageSources(declared, exported) {
140
- const merged = /* @__PURE__ */ new Map();
141
- const declaredEntries = Array.isArray(declared) ? declared.filter((d) => d?.key).map((d) => [d.key, d]) : Object.entries(declared ?? {});
142
- for (const [key, config] of declaredEntries) merged.set(key, {
143
- key,
144
- engine: config.engine,
145
- transport: config.transport ?? "server",
146
- ...config.label !== void 0 ? { label: config.label } : {}
147
- });
148
- for (const definition of exported ?? []) {
149
- if (!definition?.key) continue;
150
- const existing = merged.get(definition.key);
151
- if (!existing) {
152
- merged.set(definition.key, {
153
- key: definition.key,
154
- engine: definition.engine,
155
- transport: definition.transport ?? "server",
156
- ...definition.label !== void 0 ? { label: definition.label } : {}
157
- });
158
- continue;
159
- }
160
- if (existing.label === void 0 && definition.label !== void 0) existing.label = definition.label;
161
- }
162
- return Array.from(merged.values());
163
- }
164
- //#endregion
165
- //#region ../common/src/data/sort-dialect.ts
166
- /**
167
- * Sort-order wire codec.
168
- *
169
- * This is the ONLY module that knows about the colon-delimited wire format
170
- * (`"field:direction"`) used in HTTP query parameters.
171
- * Everything else speaks {@link OrderByTuple} exclusively.
172
- *
173
- * Mirrors the filter architecture in `filter-dialect.ts`.
174
- *
175
- * @module
176
- */
177
- /**
178
- * Serialize an {@link OrderByTuple} to the wire format `"field:direction"`.
179
- *
180
- * **Runtime tolerance:** if the input is already a well-formed wire string
181
- * (from an untyped JS caller), it is returned unchanged.
182
- * This is undocumented tolerance, not public API — don't rely on it.
183
- *
184
- * @param orderBy - A canonical `[field, direction]` tuple, or at runtime
185
- * possibly a pre-serialized string (undocumented tolerance).
186
- * @returns The wire-format string, or `undefined` if the input is falsy.
187
- *
188
- * @remarks
189
- * Field names containing `:` are representable in the tuple form but
190
- * **not** on the wire — this is an inherent limitation of the colon-delimited
191
- * encoding and is not resolved here.
192
- */
193
- function serializeOrderBy(orderBy) {
194
- if (!orderBy) return void 0;
195
- if (typeof orderBy === "string") return orderBy;
196
- return `${orderBy[0]}:${orderBy[1]}`;
197
- }
198
- //#endregion
199
52
  //#region src/collections/BackendCollectionRegistry.ts
200
53
  /**
201
54
  * Backend-agnostic collection registry.
@@ -1083,6 +936,35 @@ function projectResponseFields(rows, fields, collection, options) {
1083
936
  return projected;
1084
937
  });
1085
938
  }
939
+ /**
940
+ * Both write checks, as one call, for a transport that is not the REST router.
941
+ *
942
+ * The REST routes run `assertKnownWriteFields` and `assertWriteValuesValid`
943
+ * back to back on the caller's body, seven times over — and they were the only
944
+ * place either ran. The WebSocket `SAVE` handler took a client payload straight
945
+ * to `driver.save`, so the same write arrived validated through one door and
946
+ * unvalidated through the other: `PATCH /api/data/users/1 { age: 999 }` was a
947
+ * 400, and the socket wrote it.
948
+ *
949
+ * Exported for the sockets in `@rebasepro/server-postgres` and
950
+ * `@rebasepro/server-mongo`, which are the other request boundaries. Not the
951
+ * route builder — `index.ts` keeps that internal, and this is a rule rather
952
+ * than wiring.
953
+ *
954
+ * It stays at the boundary rather than moving into the driver deliberately: it
955
+ * validates what a *caller sent*, before `beforeSave` gets a chance to fill in
956
+ * or rewrite anything. In-process writes through `rebase.data` are trusted
957
+ * server code and are not run through it, which is the placement the REST layer
958
+ * already chose.
959
+ *
960
+ * @param values the caller's payload, exactly as it arrived
961
+ * @param collection resolved from the registry by path — never the copy the
962
+ * client sent, or the rules would be the caller's to pick
963
+ */
964
+ function assertWriteRequestValid(values, collection) {
965
+ assertKnownWriteFields(values, collection);
966
+ assertWriteValuesValid(values, collection);
967
+ }
1086
968
  //#endregion
1087
969
  //#region src/api/rest/idempotency.ts
1088
970
  /**
@@ -1495,12 +1377,11 @@ var RestApiGenerator = class {
1495
1377
  logical: queryOptions.logical,
1496
1378
  limit: queryOptions.limit,
1497
1379
  offset: queryOptions.offset,
1498
- orderBy: queryOptions.orderBy?.[0]?.field,
1499
- order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
1380
+ orderBy: orderByEntriesToTuples(queryOptions.orderBy),
1500
1381
  searchString,
1501
1382
  searchExplain,
1502
1383
  vectorSearch: queryOptions.vectorSearch
1503
- }, queryOptions.include) : await this.fetchRawCollection(driver, resolvedCollection, queryOptions, searchString);
1384
+ }, queryOptions.include) : await this.fetchRawCollection(driver, resolvedCollection, queryOptions, searchString, searchExplain);
1504
1385
  const total = await this.countRawEntities(driver, resolvedCollection, queryOptions, searchString);
1505
1386
  return c.json({
1506
1387
  data: projectResponseFields(entities, queryOptions.fields, resolvedCollection, { include: queryOptions.include }),
@@ -1829,7 +1710,8 @@ var RestApiGenerator = class {
1829
1710
  path: parsed.collectionPath,
1830
1711
  filter: queryOptions.where,
1831
1712
  logical: queryOptions.logical,
1832
- searchString
1713
+ searchString,
1714
+ vectorSearch: queryOptions.vectorSearch
1833
1715
  }) : 0;
1834
1716
  return c.json({ count: total });
1835
1717
  } else if (parsed.id) {
@@ -1853,9 +1735,9 @@ var RestApiGenerator = class {
1853
1735
  logical: queryOptions.logical,
1854
1736
  limit: queryOptions.limit,
1855
1737
  offset: queryOptions.offset,
1856
- orderBy: queryOptions.orderBy?.[0]?.field,
1857
- order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
1858
- searchString
1738
+ orderBy: orderByEntriesToTuples(queryOptions.orderBy),
1739
+ searchString,
1740
+ vectorSearch: queryOptions.vectorSearch
1859
1741
  };
1860
1742
  const entities = fetchService ? await fetchService.fetchCollectionForRest(parsed.collectionPath, listOptions, queryOptions.include) : await driver.fetchCollection({
1861
1743
  path: parsed.collectionPath,
@@ -1865,7 +1747,8 @@ var RestApiGenerator = class {
1865
1747
  path: parsed.collectionPath,
1866
1748
  filter: queryOptions.where,
1867
1749
  logical: queryOptions.logical,
1868
- searchString
1750
+ searchString,
1751
+ vectorSearch: queryOptions.vectorSearch
1869
1752
  }) : entities.length;
1870
1753
  const listCollection = this.resolveNestedWriteCollection(parsed.collectionPath);
1871
1754
  return c.json({
@@ -1960,17 +1843,17 @@ var RestApiGenerator = class {
1960
1843
  /**
1961
1844
  * Fetch raw collection data without Entity wrapper (fallback for non-Postgres)
1962
1845
  */
1963
- async fetchRawCollection(driver, collection, queryOptions, searchString) {
1846
+ async fetchRawCollection(driver, collection, queryOptions, searchString, searchExplain) {
1964
1847
  return await driver.fetchCollection({
1965
1848
  path: getCollectionDataPath(collection),
1966
1849
  collection,
1967
1850
  filter: queryOptions.where,
1968
1851
  logical: queryOptions.logical,
1969
1852
  limit: queryOptions.limit,
1970
- orderBy: queryOptions.orderBy?.[0]?.field,
1971
- order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
1972
- startAfter: queryOptions.offset ? String(queryOptions.offset) : void 0,
1853
+ orderBy: orderByEntriesToTuples(queryOptions.orderBy),
1854
+ offset: queryOptions.offset,
1973
1855
  searchString,
1856
+ searchExplain,
1974
1857
  vectorSearch: queryOptions.vectorSearch
1975
1858
  });
1976
1859
  }
@@ -1983,7 +1866,8 @@ var RestApiGenerator = class {
1983
1866
  collection,
1984
1867
  filter: queryOptions.where,
1985
1868
  logical: queryOptions.logical,
1986
- searchString
1869
+ searchString,
1870
+ vectorSearch: queryOptions.vectorSearch
1987
1871
  }) : 0;
1988
1872
  }
1989
1873
  /**
@@ -2439,6 +2323,129 @@ var LocalStorageController = class {
2439
2323
  }
2440
2324
  };
2441
2325
  //#endregion
2326
+ //#region src/storage/storage-registry.ts
2327
+ /**
2328
+ * Storage Registry
2329
+ *
2330
+ * Manages multiple storage controllers for Rebase backend.
2331
+ * Allows different storage backends for different use cases.
2332
+ *
2333
+ * Usage:
2334
+ * - Single storage: Pass a single StorageController → maps to "(default)"
2335
+ * - Multiple storages: Pass a map of { storageId: StorageController }
2336
+ * - String properties use `storageId` in their config to specify which storage to use
2337
+ * - Properties without `storageId` fallback to "(default)"
2338
+ */
2339
+ /**
2340
+ * The default storage identifier used when:
2341
+ * - A single storage controller is provided (not a map)
2342
+ * - A property doesn't specify a storageId
2343
+ *
2344
+ * An alias rather than a second `"(default)"` literal: the same string is the
2345
+ * registry key here, the source key a project declares in `rebase.json`, and
2346
+ * the scope baked into a download token. Two literals that must stay equal are
2347
+ * a divergence waiting to happen — and this one would silently unscope tokens.
2348
+ */
2349
+ var DEFAULT_STORAGE_ID = DEFAULT_STORAGE_SOURCE_KEY;
2350
+ /**
2351
+ * A request named a storage source that is not registered.
2352
+ *
2353
+ * This used to be a `logger.warn` and a fallback to the default source, which
2354
+ * is the wrong answer in a way that is hard to notice: the caller asks for
2355
+ * `media`, the `storageAuthorize` hook is asked about `media`, and the bytes
2356
+ * are read from — or written to — `(default)`. A hook that widens access for
2357
+ * one named source therefore widens it for the default one, and the object the
2358
+ * hook approved is not the object the request touched. That is the same shape
2359
+ * as the non-canonical-key bypass: the check and the read disagree about which
2360
+ * object is in play.
2361
+ *
2362
+ * The failure is silent by construction. Both sources hold the *same key*,
2363
+ * because that is what makes a caller reach for a second source in the first
2364
+ * place, so the fallback returns plausible bytes rather than an error.
2365
+ *
2366
+ * It is also reachable without an attacker: a source declared in `rebase.json`
2367
+ * but not configured by the environment is skipped at boot and never
2368
+ * registered, while `GET /sources` still advertises it. The client asks for a
2369
+ * source it was told exists and silently gets a different bucket's contents.
2370
+ *
2371
+ * Carries `knownKeys` so the route layer can tell a typo from a source that was
2372
+ * declared but never configured — two different answers to the caller. The
2373
+ * registry itself does not know what was *declared*, only what was registered,
2374
+ * so it does not make that decision.
2375
+ */
2376
+ var UnknownStorageSourceError = class extends Error {
2377
+ storageId;
2378
+ knownKeys;
2379
+ constructor(storageId, knownKeys) {
2380
+ super(`[StorageRegistry] Storage source "${storageId}" is not registered. Registered sources: ${knownKeys.length > 0 ? knownKeys.map((k) => `"${k}"`).join(", ") : "(none)"}.`);
2381
+ this.storageId = storageId;
2382
+ this.knownKeys = knownKeys;
2383
+ this.name = "UnknownStorageSourceError";
2384
+ }
2385
+ };
2386
+ /**
2387
+ * Default implementation of StorageRegistry
2388
+ */
2389
+ var DefaultStorageRegistry = class DefaultStorageRegistry {
2390
+ controllers = /* @__PURE__ */ new Map();
2391
+ /**
2392
+ * Create a StorageRegistry from either a single controller or a map
2393
+ * @param input - Single StorageController (maps to "(default)") or Record<string, StorageController>
2394
+ */
2395
+ static create(input) {
2396
+ const registry = new DefaultStorageRegistry();
2397
+ if (isStorageController(input)) registry.register(DEFAULT_STORAGE_ID, input);
2398
+ else {
2399
+ for (const [id, controller] of Object.entries(input)) if (isStorageController(controller)) registry.register(id, controller);
2400
+ if (!registry.has(DEFAULT_STORAGE_ID) && registry.size() > 0) {
2401
+ const firstId = Object.keys(input).find((k) => isStorageController(input[k]));
2402
+ if (firstId) {
2403
+ logger.warn(`[StorageRegistry] No "${DEFAULT_STORAGE_ID}" storage provided. Using "${firstId}" as the default.`);
2404
+ registry.register(DEFAULT_STORAGE_ID, input[firstId]);
2405
+ }
2406
+ }
2407
+ }
2408
+ return registry;
2409
+ }
2410
+ register(id, controller) {
2411
+ if (this.controllers.has(id)) logger.warn(`[StorageRegistry] Overwriting storage with id "${id}"`);
2412
+ this.controllers.set(id, controller);
2413
+ }
2414
+ getDefault() {
2415
+ const controller = this.controllers.get(DEFAULT_STORAGE_ID);
2416
+ if (!controller) throw new Error(`[StorageRegistry] No default storage registered. Register one with id "${DEFAULT_STORAGE_ID}" or pass a single StorageController.`);
2417
+ return controller;
2418
+ }
2419
+ get(id) {
2420
+ return this.controllers.get(canonicalStorageId(id));
2421
+ }
2422
+ getOrDefault(id) {
2423
+ const key = canonicalStorageId(id);
2424
+ if (key === DEFAULT_STORAGE_ID) return this.getDefault();
2425
+ const controller = this.controllers.get(key);
2426
+ if (controller) return controller;
2427
+ throw new UnknownStorageSourceError(key, this.list());
2428
+ }
2429
+ has(id) {
2430
+ return this.controllers.has(canonicalStorageId(id));
2431
+ }
2432
+ list() {
2433
+ return Array.from(this.controllers.keys());
2434
+ }
2435
+ size() {
2436
+ return this.controllers.size;
2437
+ }
2438
+ };
2439
+ /**
2440
+ * Type guard to check if an object is a StorageController
2441
+ * vs a Record<string, StorageController> (multiple storages)
2442
+ */
2443
+ function isStorageController(obj) {
2444
+ if (typeof obj !== "object" || obj === null) return false;
2445
+ const controller = obj;
2446
+ return typeof controller.putObject === "function" && typeof controller.getSignedUrl === "function" && typeof controller.deleteObject === "function" && typeof controller.listObjects === "function" && typeof controller.getType === "function";
2447
+ }
2448
+ //#endregion
2442
2449
  //#region src/storage/image-transform.ts
2443
2450
  /**
2444
2451
  * Image Transformation Service
@@ -2810,6 +2817,7 @@ var TusHandler = class {
2810
2817
  throw new ApiError(400, "INVALID_STORAGE_BUCKET", err instanceof InvalidStorageBucketError ? err.message : "Invalid storage bucket");
2811
2818
  }
2812
2819
  const storageId = metadata.storageId || c.req.query("storageId") || void 0;
2820
+ if (storageId !== void 0 && this.storageRegistry && !this.storageRegistry.has(canonicalStorageId(storageId))) throw new ApiError(400, "UNKNOWN_STORAGE_SOURCE", `Unknown storage source "${storageId}". Available: ${this.storageRegistry.list().map((k) => `"${k}"`).join(", ") || "(none)"}.`, void 0, true);
2813
2821
  if (this.authorizeUpload) await this.authorizeUpload(c, key, bucket || "default", storageId);
2814
2822
  const filePath = join(this.tusDir, id);
2815
2823
  await writeFile(filePath, Buffer.alloc(0));
@@ -2912,7 +2920,12 @@ var TusHandler = class {
2912
2920
  async finalize(upload) {
2913
2921
  const storageId = upload.storageId;
2914
2922
  let targetController = this.storageController;
2915
- if (this.storageRegistry) targetController = storageId ? this.storageRegistry.getOrDefault(storageId) : this.storageRegistry.getDefault();
2923
+ if (this.storageRegistry) try {
2924
+ targetController = storageId ? this.storageRegistry.getOrDefault(storageId) : this.storageRegistry.getDefault();
2925
+ } catch (err) {
2926
+ if (!(err instanceof UnknownStorageSourceError)) throw err;
2927
+ targetController = void 0;
2928
+ }
2916
2929
  if (!targetController) {
2917
2930
  logger.error("[TUS] Upload received but no StorageController is configured — nothing can be written", {
2918
2931
  uploadId: upload.id,
@@ -3074,8 +3087,8 @@ function canonicalKeyOrBadRequest(key) {
3074
3087
  * The bucket's counterpart to {@link canonicalKeyOrBadRequest}, and it exists
3075
3088
  * for the same reason: the value routes a write, so it has to be checked where
3076
3089
  * it enters rather than where it is used. Applied at every entry point a bucket
3077
- * has — this route's multipart body, the `?bucket=` query, and the TUS
3078
- * `Upload-Metadata` header.
3090
+ * has — this route's multipart body, the folder route's JSON body, the
3091
+ * `?bucket=` query, and the TUS `Upload-Metadata` header.
3079
3092
  */
3080
3093
  function canonicalBucketOrBadRequest(bucket) {
3081
3094
  try {
@@ -3175,13 +3188,46 @@ function createStorageRoutes(config) {
3175
3188
  if (!allowed) throw ApiError.forbidden("Not authorized for this object");
3176
3189
  };
3177
3190
  /**
3178
- * Resolve the storage controller for a request.
3179
- * Looks up by `storageId` in the registry, falls back to the single
3180
- * controller, and finally to the registry default.
3191
+ * Resolve the storage controller for a request, or refuse the request.
3192
+ *
3193
+ * A `storageId` that names no registered source used to fall back to the
3194
+ * default one. That is silently wrong: the `storageAuthorize` hook is asked
3195
+ * about the source the caller named while the bytes come from `(default)`,
3196
+ * so the object the hook approved is not the object the request touched —
3197
+ * and because the two sources hold the same key, the fallback returns
3198
+ * plausible bytes instead of an error.
3199
+ *
3200
+ * The caller gets one of two answers, because they are two different
3201
+ * problems with two different fixes:
3202
+ *
3203
+ * - **501** if the source *is* declared in `rebase.json` but has no
3204
+ * credentials in this environment. It was skipped at boot rather than
3205
+ * crash-looping the backend, but `GET /sources` still advertises it, so a
3206
+ * client asking for it is not confused — the deployment is incomplete.
3207
+ * 501 (not 503) for the same reason the whole-storage stub uses it: this
3208
+ * is permanent until someone configures it, and the client's offline
3209
+ * queue retries 503 forever.
3210
+ * - **400** otherwise. The id names nothing this deployment has ever heard
3211
+ * of, which is a caller mistake. `expected`, so one client holding a
3212
+ * stale source name does not write a warning per request forever.
3181
3213
  */
3214
+ const declaredKeys = new Set((declaredSources ?? []).map((s) => s.key));
3215
+ const refuseUnknownSource = (storageId, knownKeys) => {
3216
+ if (declaredKeys.has(storageId)) throw new ApiError(501, "STORAGE_SOURCE_NOT_CONFIGURED", `Storage source "${storageId}" is declared but not configured on this deployment, so it cannot be read from or written to. Set its credentials (the ${storageId} suffixed environment variables) and redeploy.`);
3217
+ throw new ApiError(400, "UNKNOWN_STORAGE_SOURCE", `Unknown storage source "${storageId}". Available: ${knownKeys.length > 0 ? knownKeys.map((k) => `"${k}"`).join(", ") : "(none)"}.`, void 0, true);
3218
+ };
3182
3219
  const resolveController = (storageId) => {
3183
- if (registry) return registry.getOrDefault(storageId);
3184
- if (controller) return controller;
3220
+ if (registry) try {
3221
+ return registry.getOrDefault(storageId);
3222
+ } catch (err) {
3223
+ if (err instanceof UnknownStorageSourceError) return refuseUnknownSource(err.storageId, err.knownKeys);
3224
+ throw err;
3225
+ }
3226
+ if (controller) {
3227
+ const requested = canonicalStorageId(storageId);
3228
+ if (requested !== "(default)") return refuseUnknownSource(requested, [DEFAULT_STORAGE_SOURCE_KEY]);
3229
+ return controller;
3230
+ }
3185
3231
  throw new Error("No storage controller or registry available");
3186
3232
  };
3187
3233
  /** Get the default controller (used for TUS and base-path derivation). */
@@ -3263,7 +3309,7 @@ function createStorageRoutes(config) {
3263
3309
  const transformKeyPrefix = `${storageId || "(default)"}/${bucket}/${resolvedPath}`;
3264
3310
  const transformOpts = transformOptionsOrBadRequest(c.req.query());
3265
3311
  if (resolved.getType() === "local") {
3266
- const absolutePath = resolved.getAbsolutePath(resolvedPath, bucket);
3312
+ const absolutePath = resolved.getAbsolutePath(resolvedPath, bucket ?? "default");
3267
3313
  try {
3268
3314
  await fsp.access(absolutePath);
3269
3315
  } catch {
@@ -3324,7 +3370,7 @@ function createStorageRoutes(config) {
3324
3370
  const scopedPath = `${bucket}/${resolvedPath}`;
3325
3371
  if (isPublicStoragePath(scopedPath)) downloadConfig.metadata.public = true;
3326
3372
  else {
3327
- downloadConfig.metadata.token = generateDownloadToken(scopedPath, 300);
3373
+ downloadConfig.metadata.token = generateDownloadToken(scopedPath, 300, storageId);
3328
3374
  downloadConfig.metadata.tokenExpiresIn = 300;
3329
3375
  }
3330
3376
  }
@@ -3384,9 +3430,10 @@ function createStorageRoutes(config) {
3384
3430
  const storageId = typeof body.storageId === "string" ? body.storageId : c.req.query("storageId");
3385
3431
  if (!folderPath || typeof folderPath !== "string") throw ApiError.badRequest("Folder path is required");
3386
3432
  const resolved = resolveController(storageId);
3387
- const { bucket, resolvedPath } = parseBucketAndPath(folderPath);
3433
+ const { resolvedPath } = parseBucketAndPath(folderPath);
3434
+ const bucket = canonicalBucketOrBadRequest(typeof body.bucket === "string" ? body.bucket : void 0);
3388
3435
  if (!resolvedPath || resolvedPath.trim() === "") throw ApiError.badRequest("Invalid folder path");
3389
- await checkAuthorized(c, "write", resolvedPath, bucket, storageId);
3436
+ await checkAuthorized(c, "write", resolvedPath, bucket ?? "default", storageId);
3390
3437
  if (resolved.getType() === "local") {
3391
3438
  const absolutePath = resolved.getAbsolutePath(resolvedPath, bucket);
3392
3439
  fs$1.mkdirSync(absolutePath, { recursive: true });
@@ -3395,7 +3442,8 @@ function createStorageRoutes(config) {
3395
3442
  const emptyFile = new File([], key, { type: "application/x-directory" });
3396
3443
  await resolved.putObject({
3397
3444
  file: emptyFile,
3398
- key
3445
+ key,
3446
+ bucket
3399
3447
  });
3400
3448
  }
3401
3449
  return c.json({
@@ -3446,77 +3494,6 @@ function createStorageRoutes(config) {
3446
3494
  return router;
3447
3495
  }
3448
3496
  //#endregion
3449
- //#region src/storage/storage-registry.ts
3450
- /**
3451
- * The default storage identifier used when:
3452
- * - A single storage controller is provided (not a map)
3453
- * - A property doesn't specify a storageId
3454
- */
3455
- var DEFAULT_STORAGE_ID = "(default)";
3456
- /**
3457
- * Default implementation of StorageRegistry
3458
- */
3459
- var DefaultStorageRegistry = class DefaultStorageRegistry {
3460
- controllers = /* @__PURE__ */ new Map();
3461
- /**
3462
- * Create a StorageRegistry from either a single controller or a map
3463
- * @param input - Single StorageController (maps to "(default)") or Record<string, StorageController>
3464
- */
3465
- static create(input) {
3466
- const registry = new DefaultStorageRegistry();
3467
- if (isStorageController(input)) registry.register(DEFAULT_STORAGE_ID, input);
3468
- else {
3469
- for (const [id, controller] of Object.entries(input)) if (isStorageController(controller)) registry.register(id, controller);
3470
- if (!registry.has("(default)") && registry.size() > 0) {
3471
- const firstId = Object.keys(input).find((k) => isStorageController(input[k]));
3472
- if (firstId) {
3473
- logger.warn(`[StorageRegistry] No "${DEFAULT_STORAGE_ID}" storage provided. Using "${firstId}" as the default.`);
3474
- registry.register(DEFAULT_STORAGE_ID, input[firstId]);
3475
- }
3476
- }
3477
- }
3478
- return registry;
3479
- }
3480
- register(id, controller) {
3481
- if (this.controllers.has(id)) logger.warn(`[StorageRegistry] Overwriting storage with id "${id}"`);
3482
- this.controllers.set(id, controller);
3483
- }
3484
- getDefault() {
3485
- const controller = this.controllers.get(DEFAULT_STORAGE_ID);
3486
- if (!controller) throw new Error(`[StorageRegistry] No default storage registered. Register one with id "${DEFAULT_STORAGE_ID}" or pass a single StorageController.`);
3487
- return controller;
3488
- }
3489
- get(id) {
3490
- if (id === void 0 || id === null) return this.controllers.get(DEFAULT_STORAGE_ID);
3491
- return this.controllers.get(id);
3492
- }
3493
- getOrDefault(id) {
3494
- if (id === void 0 || id === null) return this.getDefault();
3495
- const controller = this.controllers.get(id);
3496
- if (controller) return controller;
3497
- logger.warn(`[StorageRegistry] Storage "${id}" not found, falling back to "${DEFAULT_STORAGE_ID}"`);
3498
- return this.getDefault();
3499
- }
3500
- has(id) {
3501
- return this.controllers.has(id);
3502
- }
3503
- list() {
3504
- return Array.from(this.controllers.keys());
3505
- }
3506
- size() {
3507
- return this.controllers.size;
3508
- }
3509
- };
3510
- /**
3511
- * Type guard to check if an object is a StorageController
3512
- * vs a Record<string, StorageController> (multiple storages)
3513
- */
3514
- function isStorageController(obj) {
3515
- if (typeof obj !== "object" || obj === null) return false;
3516
- const controller = obj;
3517
- return typeof controller.putObject === "function" && typeof controller.getSignedUrl === "function" && typeof controller.deleteObject === "function" && typeof controller.listObjects === "function" && typeof controller.getType === "function";
3518
- }
3519
- //#endregion
3520
3497
  //#region src/storage/index.ts
3521
3498
  /**
3522
3499
  * Create a storage controller from a config object.
@@ -3610,7 +3587,7 @@ function assertStorageAccessControlConfigured(state, isProduction) {
3610
3587
  */
3611
3588
  async function mountOpenApiDocs(app, basePath, enableSwagger, serverCollections, requireAuth) {
3612
3589
  if (enableSwagger === false || serverCollections.length === 0) return;
3613
- const { generateOpenApiSpec } = await import("./openapi-generator-DPKtUC9X.js");
3590
+ const { generateOpenApiSpec } = await import("./openapi-generator-BWL8F2La.js");
3614
3591
  app.get(`${basePath}/docs`, (c) => {
3615
3592
  const spec = generateOpenApiSpec(serverCollections, {
3616
3593
  basePath,
@@ -5092,9 +5069,15 @@ var SDKQueryBuilder = class {
5092
5069
  }
5093
5070
  /**
5094
5071
  * Order the results by a specific column.
5072
+ *
5073
+ * Call it again to add a tie-breaker rather than replace the sort: keys
5074
+ * apply in the order they were added, so
5075
+ * `.orderBy("roles").orderBy("created_at", "desc")` sorts by role and
5076
+ * shows the newest first within each one.
5095
5077
  */
5096
5078
  orderBy(column, direction = "asc") {
5097
- this.params.orderBy = [column, direction];
5079
+ const existing = normalizeOrderBy(this.params.orderBy) ?? [];
5080
+ this.params.orderBy = [...existing, [column, direction]];
5098
5081
  return this;
5099
5082
  }
5100
5083
  /**
@@ -5409,8 +5392,7 @@ function createCollectionClient(transport, slug, ws) {
5409
5392
  logical: params?.logical,
5410
5393
  limit: params?.limit,
5411
5394
  offset: window.driverOffset,
5412
- orderBy: params?.orderBy?.[0],
5413
- order: params?.orderBy?.[1],
5395
+ orderBy: normalizeOrderBy(params?.orderBy),
5414
5396
  searchString: params?.searchString,
5415
5397
  searchExplain: params?.searchExplain
5416
5398
  }, (incomingRows) => {
@@ -7896,23 +7878,30 @@ function matchesParams(row, params) {
7896
7878
  * not shuffle rows between pages.
7897
7879
  */
7898
7880
  function sortRows(rows, orderBy) {
7899
- if (!orderBy) return rows;
7900
- const [field, direction = "asc"] = orderBy;
7901
- const sign = direction === "desc" ? -1 : 1;
7881
+ const keys = normalizeOrderBy(orderBy);
7882
+ if (!keys) return rows;
7902
7883
  return rows.sort((a, b) => {
7903
- const av = a[field];
7904
- const bv = b[field];
7905
- const aNull = isNullish(toComparable(av));
7906
- const bNull = isNullish(toComparable(bv));
7907
- if (aNull || bNull) {
7908
- if (aNull && bNull) return tiebreak(a, b);
7909
- return (aNull ? 1 : -1) * (direction === "desc" ? -1 : 1);
7910
- }
7911
- const cmp = compareValues(av, bv);
7912
- if (cmp === void 0 || cmp === 0) return tiebreak(a, b);
7913
- return cmp * sign;
7884
+ for (const [field, direction = "asc"] of keys) {
7885
+ const cmp = compareOnKey(a, b, field, direction);
7886
+ if (cmp !== 0) return cmp;
7887
+ }
7888
+ return tiebreak(a, b);
7914
7889
  });
7915
7890
  }
7891
+ /** One key's verdict: negative, positive, or 0 for "these two are equal here". */
7892
+ function compareOnKey(a, b, field, direction) {
7893
+ const av = a[field];
7894
+ const bv = b[field];
7895
+ const aNull = isNullish(toComparable(av));
7896
+ const bNull = isNullish(toComparable(bv));
7897
+ if (aNull || bNull) {
7898
+ if (aNull && bNull) return 0;
7899
+ return (aNull ? 1 : -1) * (direction === "desc" ? -1 : 1);
7900
+ }
7901
+ const cmp = compareValues(av, bv);
7902
+ if (cmp === void 0 || cmp === 0) return 0;
7903
+ return cmp * (direction === "desc" ? -1 : 1);
7904
+ }
7916
7905
  function tiebreak(a, b) {
7917
7906
  return compareValues(a.id, b.id) ?? 0;
7918
7907
  }
@@ -8009,17 +7998,16 @@ function isExactlyEvaluable(params) {
8009
7998
  * comparator.
8010
7999
  */
8011
8000
  function isLocallySortable(rows, orderBy) {
8012
- if (!orderBy) return true;
8013
- const [field] = orderBy;
8014
- for (const row of rows) {
8001
+ const keys = normalizeOrderBy(orderBy);
8002
+ if (!keys) return true;
8003
+ return keys.every(([field]) => rows.every((row) => {
8015
8004
  const value = toComparable(row[field]);
8016
- if (isNullish(value)) continue;
8017
- if (typeof value === "number" || typeof value === "boolean") continue;
8018
- if (typeof value === "bigint") continue;
8019
- if (typeof value === "string" && value.trim() !== "" && !Number.isNaN(Number(value))) continue;
8005
+ if (isNullish(value)) return true;
8006
+ if (typeof value === "number" || typeof value === "boolean") return true;
8007
+ if (typeof value === "bigint") return true;
8008
+ if (typeof value === "string" && value.trim() !== "" && !Number.isNaN(Number(value))) return true;
8020
8009
  return false;
8021
- }
8022
- return true;
8010
+ }));
8023
8011
  }
8024
8012
  /** Run a full query — filter, sort, paginate — over a set of rows. */
8025
8013
  function runLocalQuery(rows, params) {
@@ -10185,7 +10173,7 @@ async function _initializeRebaseBackend(config) {
10185
10173
  ]) {
10186
10174
  const providerConfig = safeAuthConfig[key];
10187
10175
  if (providerConfig && requiredFields.every((f) => Boolean(providerConfig[f]))) {
10188
- const createFn = (await import("./auth-CYoPVf-E.js").then((n) => n.t))[factory];
10176
+ const createFn = (await import("./auth-BQcdhMBL.js").then((n) => n.t))[factory];
10189
10177
  oauthProviders.push(createFn(providerConfig));
10190
10178
  }
10191
10179
  }
@@ -10463,7 +10451,7 @@ async function _initializeRebaseBackend(config) {
10463
10451
  if (config.functionsDir) {
10464
10452
  const { loadFunctionsWithDiagnostics } = await import("./function-loader-DDS1v7YX.js").then((n) => n.t);
10465
10453
  const { createFunctionRoutes } = await import("./function-routes-Btcez1T-.js").then((n) => n.n);
10466
- const { createFunctionsRequestTimeout, resolveFunctionsTimeoutMs } = await import("./request-timeout-RivJsME0.js");
10454
+ const { createFunctionsRequestTimeout, resolveFunctionsTimeoutMs } = await import("./request-timeout-BuFoEKwT.js");
10467
10455
  const { functions: loadedFunctions, problems } = await loadFunctionsWithDiagnostics(config.functionsDir);
10468
10456
  const functionsRouter = new Hono();
10469
10457
  functionsRouter.onError(errorHandler);
@@ -10502,7 +10490,7 @@ async function _initializeRebaseBackend(config) {
10502
10490
  const { loadCronJobsWithDiagnostics } = await import("./cron-loader-YhhQeVBM.js").then((n) => n.t);
10503
10491
  const { CronScheduler } = await import("./cron-scheduler-DIpYBmZP.js").then((n) => n.n);
10504
10492
  const { createCronRoutes } = await import("./cron-routes-maM_RlUu.js").then((n) => n.n);
10505
- const { createCronStore } = await import("./cron-store-Dvr4Y1sZ.js").then((n) => n.n);
10493
+ const { createCronStore } = await import("./cron-store-D5dUNviq.js").then((n) => n.n);
10506
10494
  const { jobs: loadedCronJobs, problems: cronProblems } = await loadCronJobsWithDiagnostics(config.cronsDir);
10507
10495
  cronScheduler = new CronScheduler();
10508
10496
  cronScheduler.setClient(serverSingleton);
@@ -13411,6 +13399,6 @@ async function ensureCollectionPolicies(bundle, dataSources, env) {
13411
13399
  else logger.debug("RLS policies are up to date.");
13412
13400
  }
13413
13401
  //#endregion
13414
- export { ApiError, BlockedUrlError, BundleError, CronScheduler, DEFAULT_DRIVER_ID, DEFAULT_MAX_FILE_SIZE, DEFAULT_STORAGE_ID, DOCUMENT_MIME_TYPES, DefaultDriverRegistry, DefaultStorageRegistry, GCSStorageController, IMAGE_MIME_TYPES, LocalStorageController, MetricsRegistry, RawHtml, S3StorageController, SMTPEmailService, TransformCache, TusHandler, WebhookDispatcher, _resetRebaseMock, _setRebaseMock, applyCollectionDefaults, assertAllowedOutboundUrl, assertCollectionConfigs, assertDistinctSuffixes, assertEmailLinkBases, authJwt, authRoles, authUid, blockedAddressReason, bootFromBundle, classifySurface, cleanupDevPortFile, createAppleProvider, createBackupRoutes, createBitbucketProvider, createBuiltinAuthAdapter, createContractRoutes, createCronRoutes, createCronStore, createCustomAuthAdapter, createDiscordProvider, createEmailService, createFacebookProvider, createFunctionRoutes, createGitHubProvider, createGitLabProvider, createGoogleProvider, createHistoryRoutes, createLinkedinProvider, createMetricsMiddleware, createMetricsRoutes, createMicrosoftProvider, createSlackProvider, createSourceBundle, createSpotifyProvider, createStorageController, createStorageRoutes, createTwitterProvider, defineCron, defineFunction, envSuffixForKey, errorHandler, escapeHtml, extractUserFromToken, fileTokenAuth, findCollectionConfigProblems, generateSecurePassword, getEmailVerificationTemplate, getMagicLinkTemplate, getPasswordResetTemplate, getUserInvitationTemplate, getWelcomeEmailTemplate, hashPassword, html, httpMethodToOperation, initializeDataSource, initializeDataSources, initializeRebaseBackend, installShutdownHandlers, isApiKeyToken, isAuthAdapter, isDatabaseAdapter, isLocalhostOrigin, isOperationAllowed, isTransformableImage, listBackupObjects, listenWithPortRetry, loadBootEnv, loadBundle, loadBundleConfigExports, loadBundleSchema, loadCollectionsFromDirectory, loadCronJobsFromDirectory, loadCronJobsWithDiagnostics, loadDeclaredStorageSources, loadEnv, loadFunctionsFromDirectory, loadUsersCollection, logger, oauthCodeFlowSchema, optionalAuth, parseBackupDestination, parseBackupTimestamp, parseTransformOptions, pkceTokenParams, providerVerifiedEmail, queryTokenAuth, raw, readBackupBytes, readBundleManifest, rebase, requireAdmin, requireAuth, resolveAuthHooks, resolveAuthOptions, resolveCorsOrigin, resolveDataSources, resolveEmailLinkBase, resolveEmailOptions, resolveRequireAuth, resolveStorageBackend, resolveStorageSources, runFromBundle, safeCompare, serveSPA, transformImage, tryVerifyOidcIdToken, unknownKeyPolicyFromEnv, validateApiKey, validateCronExpression, validatePasswordStrength, verifyOidcIdToken, verifyPassword };
13402
+ export { ApiError, BlockedUrlError, BundleError, CronScheduler, DEFAULT_DRIVER_ID, DEFAULT_MAX_FILE_SIZE, DEFAULT_STORAGE_ID, DOCUMENT_MIME_TYPES, DefaultDriverRegistry, DefaultStorageRegistry, GCSStorageController, IMAGE_MIME_TYPES, LocalStorageController, MetricsRegistry, RawHtml, S3StorageController, SMTPEmailService, TransformCache, TusHandler, UnknownStorageSourceError, WebhookDispatcher, _resetRebaseMock, _setRebaseMock, applyCollectionDefaults, assertAllowedOutboundUrl, assertCollectionConfigs, assertDistinctSuffixes, assertEmailLinkBases, assertWriteRequestValid, authJwt, authRoles, authUid, blockedAddressReason, bootFromBundle, classifySurface, cleanupDevPortFile, createAppleProvider, createBackupRoutes, createBitbucketProvider, createBuiltinAuthAdapter, createContractRoutes, createCronRoutes, createCronStore, createCustomAuthAdapter, createDiscordProvider, createEmailService, createFacebookProvider, createFunctionRoutes, createGitHubProvider, createGitLabProvider, createGoogleProvider, createHistoryRoutes, createLinkedinProvider, createMetricsMiddleware, createMetricsRoutes, createMicrosoftProvider, createSlackProvider, createSourceBundle, createSpotifyProvider, createStorageController, createStorageRoutes, createTwitterProvider, defineCron, defineFunction, envSuffixForKey, errorHandler, escapeHtml, extractUserFromToken, fileTokenAuth, findCollectionConfigProblems, generateSecurePassword, getEmailVerificationTemplate, getMagicLinkTemplate, getPasswordResetTemplate, getUserInvitationTemplate, getWelcomeEmailTemplate, hashPassword, html, httpMethodToOperation, initializeDataSource, initializeDataSources, initializeRebaseBackend, installShutdownHandlers, isApiKeyToken, isAuthAdapter, isDatabaseAdapter, isLocalhostOrigin, isOperationAllowed, isTransformableImage, listBackupObjects, listenWithPortRetry, loadBootEnv, loadBundle, loadBundleConfigExports, loadBundleSchema, loadCollectionsFromDirectory, loadCronJobsFromDirectory, loadCronJobsWithDiagnostics, loadDeclaredStorageSources, loadEnv, loadFunctionsFromDirectory, loadUsersCollection, logger, oauthCodeFlowSchema, optionalAuth, parseBackupDestination, parseBackupTimestamp, parseTransformOptions, pkceTokenParams, providerVerifiedEmail, queryTokenAuth, raw, readBackupBytes, readBundleManifest, rebase, requireAdmin, requireAuth, resolveAuthHooks, resolveAuthOptions, resolveCorsOrigin, resolveDataSources, resolveEmailLinkBase, resolveEmailOptions, resolveRequireAuth, resolveStorageBackend, resolveStorageSources, runFromBundle, safeCompare, serveSPA, transformImage, tryVerifyOidcIdToken, unknownKeyPolicyFromEnv, validateApiKey, validateCronExpression, validatePasswordStrength, verifyOidcIdToken, verifyPassword };
13415
13403
 
13416
13404
  //# sourceMappingURL=index.es.js.map