@rebasepro/server 0.13.0 → 0.13.1-canary.g3660bd5

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 (39) hide show
  1. package/dist/{admin_block-CcSGdH7M.js → admin_block-EmIHae6X.js} +2 -1
  2. package/dist/admin_block-EmIHae6X.js.map +1 -0
  3. package/dist/api/errors.d.ts +0 -6
  4. package/dist/api/openapi-generator.d.ts +14 -0
  5. package/dist/api/rest/idempotency.d.ts +41 -4
  6. package/dist/api/rest/write-validation.d.ts +12 -5
  7. package/dist/auth/index.d.ts +1 -1
  8. package/dist/auth/jwt.d.ts +12 -0
  9. package/dist/{auth-CuC9M2x6.js → auth-BE_FSK1l.js} +17 -37
  10. package/dist/auth-BE_FSK1l.js.map +1 -0
  11. package/dist/{backup-CVggVhR2.js → backup-BJiY_mM-.js} +2 -2
  12. package/dist/{backup-CVggVhR2.js.map → backup-BJiY_mM-.js.map} +1 -1
  13. package/dist/{cron-routes-CrQ0tK-_.js → cron-routes-D5a9v1HY.js} +2 -2
  14. package/dist/{cron-routes-CrQ0tK-_.js.map → cron-routes-D5a9v1HY.js.map} +1 -1
  15. package/dist/{cron-scheduler-B3RFt0HS.js → cron-scheduler-BhFZWR0T.js} +21 -2
  16. package/dist/cron-scheduler-BhFZWR0T.js.map +1 -0
  17. package/dist/{cron-store-BywZsyfZ.js → cron-store-Cu4sOseb.js} +11 -1
  18. package/dist/cron-store-Cu4sOseb.js.map +1 -0
  19. package/dist/{errors-CgkCzoj7.js → errors-BpudWAVU.js} +2 -10
  20. package/dist/errors-BpudWAVU.js.map +1 -0
  21. package/dist/index.es.js +220 -164
  22. package/dist/index.es.js.map +1 -1
  23. package/dist/{jwt-DD6EtpGj.js → jwt-CzeviDcB.js} +17 -2
  24. package/dist/{jwt-DD6EtpGj.js.map → jwt-CzeviDcB.js.map} +1 -1
  25. package/dist/{openapi-generator-BEwyiaAr.js → openapi-generator-C-Rqz9sp.js} +99 -102
  26. package/dist/openapi-generator-C-Rqz9sp.js.map +1 -0
  27. package/dist/{schema-editor-routes-CbF20Mf1.js → schema-editor-routes-C1DxDnDR.js} +3 -3
  28. package/dist/{schema-editor-routes-CbF20Mf1.js.map → schema-editor-routes-C1DxDnDR.js.map} +1 -1
  29. package/dist/{src-Cum9kox5.js → src-7U0xuQo3.js} +65 -15
  30. package/dist/src-7U0xuQo3.js.map +1 -0
  31. package/dist/utils/logging.d.ts +0 -4
  32. package/package.json +5 -5
  33. package/dist/admin_block-CcSGdH7M.js.map +0 -1
  34. package/dist/auth-CuC9M2x6.js.map +0 -1
  35. package/dist/cron-scheduler-B3RFt0HS.js.map +0 -1
  36. package/dist/cron-store-BywZsyfZ.js.map +0 -1
  37. package/dist/errors-CgkCzoj7.js.map +0 -1
  38. package/dist/openapi-generator-BEwyiaAr.js.map +0 -1
  39. package/dist/src-Cum9kox5.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 { S as Vector, a as serializeFilter, b as EntityRelation, c as paginateFind, d as resolveDataSource, f as getEffectiveSecurityRules, g as toSnakeCase, h as buildCompositeId, i as deserializeLogicalCondition, l as CollectionRegistry, m as resolveCollectionRelations, n as buildSdkData, o as serializeLogicalCondition, p as findRelation, r as deserializeFilter, s as collectAllPages, t as buildRoutedRebaseData, u as createDataSourceRegistry, x as GeoPoint, y as EntityReference } from "./src-Cum9kox5.js";
4
+ import { C as EntityReference, E as Vector, T as GeoPoint, _ as resolvePrimaryKeys, a as serializeFilter, b as resolveClientListLimit, c as paginateFind, d as createDataSourceRegistry, f as resolveDataSource, g as buildCompositeId, h as resolveCollectionRelations, i as deserializeLogicalCondition, l as resolveFindWindow, m as findRelation, n as buildSdkData, o as serializeLogicalCondition, p as getEffectiveSecurityRules, r as deserializeFilter, s as collectAllPages, t as buildRoutedRebaseData, u as CollectionRegistry, v as toSnakeCase, w as EntityRelation } from "./src-7U0xuQo3.js";
5
5
  import { a as DEFAULT_DATA_SOURCE_KEY, r as isPostgresCollectionConfig, t as getCollectionDataPath, u as toCanonicalOp } from "./src-_qQ3RNCK.js";
6
- import { n as ADMIN_PROPERTY_KEYS, t as ADMIN_COLLECTION_KEYS } from "./admin_block-CcSGdH7M.js";
6
+ import { n as ADMIN_PROPERTY_KEYS, t as ADMIN_COLLECTION_KEYS } from "./admin_block-EmIHae6X.js";
7
7
  import { t as isSQLAdmin } from "./backend-CIxN4FVm.js";
8
8
  import { n as createContractRoutes } from "./contract-routes-Dj8i5AiM.js";
9
- import { $ as httpMethodToOperation, A as getEmailVerificationTemplate, B as optionalAuth, C as createDataRateLimiter, D as validatePasswordStrength, E as hashPassword, F as createAdapterAuthMiddleware, G as createApiKeyPreAuth, H as queryTokenAuth, I as createAuthMiddleware, J as isApiKeyToken, K as createFunctionApiKeyGuard, L as createRequireAuth, M as getPasswordResetTemplate, N as getUserInvitationTemplate, O as verifyPassword, P as getWelcomeEmailTemplate, Q as scopeDataDriver, R as extractUserFromToken, S as _coercedNumber, T as resolveAuthHooks, U as requireAdmin, V as publicObjectAuth, W as requireAuth, X as extractBearerToken, Y as validateApiKey, Z as safeCompare, _ as createBuiltinAuthAdapter, a as createSpotifyProvider, b as object, c as createGitLabProvider, d as createFacebookProvider, et as isOperationAllowed, f as createAppleProvider, g as createGoogleProvider, h as createLinkedinProvider, i as createApiKeyStore, it as resolveClientListLimit, j as getMagicLinkTemplate, k as generateSecurePassword, l as createDiscordProvider, m as createGitHubProvider, n as createCustomAuthAdapter, nt as isPublicStoragePath, o as createSlackProvider, p as createMicrosoftProvider, q as createStorageApiKeyGuard, r as createApiKeyRoutes, s as createBitbucketProvider, tt as PUBLIC_STORAGE_PREFIX, u as createTwitterProvider, v as ZodNumber, w as MemoryRateLimitStore, x as string, y as _enum, z as fileTokenAuth } from "./auth-CuC9M2x6.js";
9
+ import { $ as httpMethodToOperation, A as getEmailVerificationTemplate, B as optionalAuth, C as createDataRateLimiter, D as validatePasswordStrength, E as hashPassword, F as createAdapterAuthMiddleware, G as createApiKeyPreAuth, H as queryTokenAuth, I as createAuthMiddleware, J as isApiKeyToken, K as createFunctionApiKeyGuard, L as createRequireAuth, M as getPasswordResetTemplate, N as getUserInvitationTemplate, O as verifyPassword, P as getWelcomeEmailTemplate, Q as scopeDataDriver, R as extractUserFromToken, S as _coercedNumber, T as resolveAuthHooks, U as requireAdmin, V as publicObjectAuth, W as requireAuth, X as extractBearerToken, Y as validateApiKey, Z as safeCompare, _ as createBuiltinAuthAdapter, a as createSpotifyProvider, b as object, c as createGitLabProvider, d as createFacebookProvider, et as isOperationAllowed, f as createAppleProvider, g as createGoogleProvider, h as createLinkedinProvider, i as createApiKeyStore, j as getMagicLinkTemplate, k as generateSecurePassword, l as createDiscordProvider, m as createGitHubProvider, n as createCustomAuthAdapter, nt as isPublicStoragePath, o as createSlackProvider, p as createMicrosoftProvider, q as createStorageApiKeyGuard, r as createApiKeyRoutes, s as createBitbucketProvider, tt as PUBLIC_STORAGE_PREFIX, u as createTwitterProvider, v as ZodNumber, w as MemoryRateLimitStore, x as string, y as _enum, z as fileTokenAuth } from "./auth-BE_FSK1l.js";
10
10
  import { t as logger } from "./logger-BYU66ENZ.js";
11
- import { n as errorHandler, r as isRebaseApiError, t as ApiError } from "./errors-CgkCzoj7.js";
12
- import { i as generateDownloadToken, n as configureJwt } from "./jwt-DD6EtpGj.js";
11
+ import { n as errorHandler, t as ApiError } from "./errors-BpudWAVU.js";
12
+ import { i as generateDownloadToken, n as configureJwt } from "./jwt-CzeviDcB.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";
@@ -17,10 +17,10 @@ import { t as GCSStorageController } from "./GCSStorageController-BEmDYFKc.js";
17
17
  import { n as loadFunctionsFromDirectory } from "./function-loader-B_1fYfUY.js";
18
18
  import { t as createFunctionRoutes } from "./function-routes-C0cLIy3N.js";
19
19
  import { n as loadCronJobsFromDirectory } from "./cron-loader-B1S2MCSl.js";
20
- import { r as validateCronExpression, t as CronScheduler } from "./cron-scheduler-B3RFt0HS.js";
21
- import { t as createCronRoutes } from "./cron-routes-CrQ0tK-_.js";
22
- import { t as createCronStore } from "./cron-store-BywZsyfZ.js";
23
- import { a as parseBackupTimestamp, i as parseBackupDestination, n as createBackupRoutes, o as readBackupBytes, r as listBackupObjects } from "./backup-CVggVhR2.js";
20
+ import { r as validateCronExpression, t as CronScheduler } from "./cron-scheduler-BhFZWR0T.js";
21
+ import { t as createCronRoutes } from "./cron-routes-D5a9v1HY.js";
22
+ import { t as createCronStore } from "./cron-store-Cu4sOseb.js";
23
+ import { a as parseBackupTimestamp, i as parseBackupDestination, n as createBackupRoutes, o as readBackupBytes, r as listBackupObjects } from "./backup-BJiY_mM-.js";
24
24
  import { randomBytes, randomUUID } from "node:crypto";
25
25
  import * as fs$2 from "fs";
26
26
  import fs, { existsSync } from "fs";
@@ -1055,11 +1055,18 @@ function assertKnownWriteFields(values, collection, options) {
1055
1055
  * This shapes the *response*, which is what the parameter says it does; it is
1056
1056
  * not a column pushdown, so it saves bandwidth rather than database work.
1057
1057
  *
1058
- * `id` always survives. Rows are addressed by it everywhere above this layer —
1059
- * the admin table, realtime reconciliation, the offline cache — and a row that
1060
- * arrives without one is not a smaller row, it is an unusable one. Asking for
1061
- * `fields=title` and being unable to open the record you clicked is a worse
1062
- * answer than one extra key.
1058
+ * The collection's key always survives. Rows are addressed by it everywhere
1059
+ * above this layer — the admin table, realtime reconciliation, the offline
1060
+ * cache — and a row that arrives without one is not a smaller row, it is an
1061
+ * unusable one. Asking for `fields=title` and being unable to open the record
1062
+ * you clicked is a worse answer than one extra key.
1063
+ *
1064
+ * That is a statement about the key, not about the name `id`: a collection
1065
+ * keyed on `slug`, or on `user_id + role_id`, was projected down to the fields
1066
+ * asked for and nothing else, which is the very outcome the paragraph above
1067
+ * describes. `assertKnownWriteFields` one function up already has a dedicated
1068
+ * error for collections with no `id` column, so this layer had no excuse for
1069
+ * assuming one.
1063
1070
  */
1064
1071
  function projectResponseFields(rows, fields, collection, options) {
1065
1072
  if (!fields || fields.length === 0) return rows;
@@ -1077,7 +1084,8 @@ function projectResponseFields(rows, fields, collection, options) {
1077
1084
  collection: collection.slug
1078
1085
  });
1079
1086
  }
1080
- const keep = /* @__PURE__ */ new Set([...fields, "id"]);
1087
+ const primaryKeys = resolvePrimaryKeys(collection).map((key) => key.fieldName);
1088
+ const keep = /* @__PURE__ */ new Set([...fields, ...primaryKeys.length > 0 ? primaryKeys : ["id"]]);
1081
1089
  return rows.map((row) => {
1082
1090
  const projected = {};
1083
1091
  for (const key of Object.keys(row)) if (keep.has(key)) projected[key] = row[key];
@@ -1161,26 +1169,43 @@ function createIdempotencyStore(driver) {
1161
1169
  return ready;
1162
1170
  };
1163
1171
  return {
1164
- async recall(key, uid) {
1165
- if (!key || !await ensure()) return void 0;
1172
+ async claim(key, uid) {
1173
+ if (!key || !await ensure()) return { status: "claimed" };
1166
1174
  try {
1167
- return (await exec(`SELECT response FROM ${TABLE}
1168
- WHERE uid = $1 AND key = $2 AND created_at > NOW() - INTERVAL '${TTL_HOURS} hours'`, [principal(uid), key]))[0]?.response;
1175
+ if ((await exec(`INSERT INTO ${TABLE} (key, uid, response, created_at)
1176
+ VALUES ($1, $2, NULL, NOW())
1177
+ ON CONFLICT (uid, key) DO UPDATE
1178
+ SET response = NULL, created_at = NOW()
1179
+ WHERE ${TABLE}.created_at < NOW() - INTERVAL '${TTL_HOURS} hours'
1180
+ RETURNING 1 AS claimed`, [key, principal(uid)])).length > 0) return { status: "claimed" };
1181
+ const row = (await exec(`SELECT response, response IS NULL AS pending FROM ${TABLE}
1182
+ WHERE uid = $1 AND key = $2`, [principal(uid), key]))[0];
1183
+ if (!row) return { status: "claimed" };
1184
+ if (row.pending) return { status: "in-flight" };
1185
+ return {
1186
+ status: "replay",
1187
+ response: row.response
1188
+ };
1169
1189
  } catch {
1170
- return;
1190
+ return { status: "claimed" };
1171
1191
  }
1172
1192
  },
1173
- async remember(key, uid, response) {
1193
+ async complete(key, uid, response) {
1174
1194
  if (!key || !await ensure()) return;
1175
1195
  try {
1176
- await exec(`INSERT INTO ${TABLE} (key, uid, response) VALUES ($1, $2, $3::jsonb)
1177
- ON CONFLICT (uid, key) DO NOTHING`, [
1178
- key,
1196
+ await exec(`UPDATE ${TABLE} SET response = $3::jsonb WHERE uid = $1 AND key = $2`, [
1179
1197
  principal(uid),
1198
+ key,
1180
1199
  JSON.stringify(response ?? null)
1181
1200
  ]);
1182
1201
  if (Math.random() < .01) await exec(`DELETE FROM ${TABLE} WHERE created_at < NOW() - INTERVAL '${TTL_HOURS} hours'`);
1183
1202
  } catch {}
1203
+ },
1204
+ async release(key, uid) {
1205
+ if (!key || !await ensure()) return;
1206
+ try {
1207
+ await exec(`DELETE FROM ${TABLE} WHERE uid = $1 AND key = $2 AND response IS NULL`, [principal(uid), key]);
1208
+ } catch {}
1184
1209
  }
1185
1210
  };
1186
1211
  }
@@ -1395,92 +1420,83 @@ var RestApiGenerator = class {
1395
1420
  });
1396
1421
  });
1397
1422
  this.router.post(basePath, async (c) => {
1398
- try {
1399
- this.enforceApiKeyPermission(c, collection.slug);
1400
- const driver = this.getScopedDriver(c);
1401
- const path = collection.slug;
1402
- const body = await parseJsonBody(c);
1403
- const isAuth = collection.auth;
1404
- const isAuthCollection = isAuth === true || isAuth && typeof isAuth === "object" && isAuth.enabled === true;
1405
- const collectionAuthConfig = typeof isAuth === "object" ? isAuth : void 0;
1406
- if (!isAuthCollection) assertKnownWriteFields(body, resolvedCollection);
1407
- else {
1408
- const contract = this.authAdapter?.describeUserCreationContract?.(collectionAuthConfig);
1409
- if (contract?.validate) assertKnownWriteFields(body, resolvedCollection, { extraKnownFields: contract.extraFields });
1410
- }
1411
- if (isAuthCollection && this.authAdapter?.prepareUserCreation) {
1412
- const prepared = await this.authAdapter.prepareUserCreation(body, collectionAuthConfig);
1413
- const entity = await driver.save({
1414
- path,
1415
- values: prepared.values,
1416
- collection: resolvedCollection,
1417
- status: "new"
1418
- });
1419
- const result = prepared.hookHandledEmail ? {
1420
- temporaryPassword: prepared.clearPassword,
1421
- invitationSent: prepared.invitationSent
1422
- } : this.authAdapter.finalizeUserCreation ? await this.authAdapter.finalizeUserCreation({
1423
- id: entity.id,
1424
- values: entity
1425
- }, prepared.clearPassword) : { invitationSent: false };
1426
- const response = this.formatResponse(entity);
1427
- return c.json({
1428
- ...response,
1429
- invitationSent: result.invitationSent,
1430
- ...result.temporaryPassword ? { temporaryPassword: result.temporaryPassword } : {},
1431
- ..."emailDeliveryFailed" in result && result.emailDeliveryFailed ? { emailDeliveryFailed: true } : {}
1432
- }, 201);
1433
- }
1434
- const idempotencyKey = c.req.header(IDEMPOTENCY_HEADER);
1435
- const uid = c.get("user")?.uid;
1436
- const store = this.idempotency();
1437
- if (idempotencyKey && store) {
1438
- const already = await store.recall(idempotencyKey, uid);
1439
- if (already !== void 0) return c.json(already, 201);
1440
- }
1423
+ this.enforceApiKeyPermission(c, collection.slug);
1424
+ const driver = this.getScopedDriver(c);
1425
+ const path = collection.slug;
1426
+ const body = await parseJsonBody(c);
1427
+ const isAuth = collection.auth;
1428
+ const isAuthCollection = isAuth === true || isAuth && typeof isAuth === "object" && isAuth.enabled === true;
1429
+ const collectionAuthConfig = typeof isAuth === "object" ? isAuth : void 0;
1430
+ if (!isAuthCollection) assertKnownWriteFields(body, resolvedCollection);
1431
+ else {
1432
+ const contract = this.authAdapter?.describeUserCreationContract?.(collectionAuthConfig);
1433
+ if (contract?.validate) assertKnownWriteFields(body, resolvedCollection, { extraKnownFields: contract.extraFields });
1434
+ }
1435
+ if (isAuthCollection && this.authAdapter?.prepareUserCreation) {
1436
+ const prepared = await this.authAdapter.prepareUserCreation(body, collectionAuthConfig);
1441
1437
  const entity = await driver.save({
1442
1438
  path,
1443
- values: body,
1439
+ values: prepared.values,
1444
1440
  collection: resolvedCollection,
1445
1441
  status: "new"
1446
1442
  });
1443
+ const result = prepared.hookHandledEmail ? {
1444
+ temporaryPassword: prepared.clearPassword,
1445
+ invitationSent: prepared.invitationSent
1446
+ } : this.authAdapter.finalizeUserCreation ? await this.authAdapter.finalizeUserCreation({
1447
+ id: entity.id,
1448
+ values: entity
1449
+ }, prepared.clearPassword) : { invitationSent: false };
1447
1450
  const response = this.formatResponse(entity);
1448
- if (idempotencyKey && store) await store.remember(idempotencyKey, uid, response);
1449
- return c.json(response, 201);
1450
- } catch (error) {
1451
- if (isRebaseApiError(error) && !error.code) {
1452
- if (!(error instanceof TypeError || error instanceof RangeError || error instanceof SyntaxError || error instanceof ReferenceError)) error.code = "BAD_REQUEST";
1453
- }
1454
- throw error;
1451
+ return c.json({
1452
+ ...response,
1453
+ invitationSent: result.invitationSent,
1454
+ ...result.temporaryPassword ? { temporaryPassword: result.temporaryPassword } : {},
1455
+ ..."emailDeliveryFailed" in result && result.emailDeliveryFailed ? { emailDeliveryFailed: true } : {}
1456
+ }, 201);
1455
1457
  }
1456
- });
1457
- this.router.put(`${basePath}/:id`, async (c) => {
1458
+ const idempotencyKey = c.req.header(IDEMPOTENCY_HEADER);
1459
+ const uid = c.get("user")?.uid;
1460
+ const store = this.idempotency();
1461
+ const claimed = idempotencyKey && store ? await store.claim(idempotencyKey, uid) : void 0;
1462
+ if (claimed?.status === "replay") return c.json(claimed.response, 201);
1463
+ if (claimed?.status === "in-flight") throw ApiError.conflict(`A request with Idempotency-Key '${idempotencyKey}' is already in progress. Retry once it has answered; its result will be replayed.`, "IDEMPOTENCY_KEY_IN_PROGRESS");
1464
+ let response;
1458
1465
  try {
1459
- this.enforceApiKeyPermission(c, collection.slug);
1460
- const id = c.req.param("id");
1461
- const driver = this.getScopedDriver(c);
1462
- if (!await driver.fetchOne({
1463
- path: getCollectionDataPath(collection),
1464
- id: String(id),
1465
- collection: resolvedCollection
1466
- })) throw ApiError.notFound("Entity not found");
1467
- const body = await parseJsonBody(c);
1468
- assertKnownWriteFields(body, resolvedCollection);
1469
1466
  const entity = await driver.save({
1470
- path: getCollectionDataPath(collection),
1471
- id: String(id),
1467
+ path,
1472
1468
  values: body,
1473
1469
  collection: resolvedCollection,
1474
- status: "existing"
1470
+ status: "new"
1475
1471
  });
1476
- const response = this.formatResponse(entity);
1477
- return c.json(response);
1472
+ response = this.formatResponse(entity);
1478
1473
  } catch (error) {
1479
- if (isRebaseApiError(error) && !error.code) {
1480
- if (!(error instanceof TypeError || error instanceof RangeError || error instanceof SyntaxError || error instanceof ReferenceError)) error.code = "BAD_REQUEST";
1481
- }
1474
+ if (claimed?.status === "claimed" && idempotencyKey && store) await store.release(idempotencyKey, uid);
1482
1475
  throw error;
1483
1476
  }
1477
+ if (claimed?.status === "claimed" && idempotencyKey && store) await store.complete(idempotencyKey, uid, response);
1478
+ return c.json(response, 201);
1479
+ });
1480
+ this.router.put(`${basePath}/:id`, async (c) => {
1481
+ this.enforceApiKeyPermission(c, collection.slug);
1482
+ const id = c.req.param("id");
1483
+ const driver = this.getScopedDriver(c);
1484
+ if (!await driver.fetchOne({
1485
+ path: getCollectionDataPath(collection),
1486
+ id: String(id),
1487
+ collection: resolvedCollection
1488
+ })) throw ApiError.notFound("Entity not found");
1489
+ const body = await parseJsonBody(c);
1490
+ assertKnownWriteFields(body, resolvedCollection);
1491
+ const entity = await driver.save({
1492
+ path: getCollectionDataPath(collection),
1493
+ id: String(id),
1494
+ values: body,
1495
+ collection: resolvedCollection,
1496
+ status: "existing"
1497
+ });
1498
+ const response = this.formatResponse(entity);
1499
+ return c.json(response);
1484
1500
  });
1485
1501
  this.router.delete(`${basePath}/:id`, async (c) => {
1486
1502
  this.enforceApiKeyPermission(c, collection.slug);
@@ -1552,6 +1568,7 @@ var RestApiGenerator = class {
1552
1568
  const total = driver.count ? await driver.count({
1553
1569
  path: parsed.collectionPath,
1554
1570
  filter: queryOptions.where,
1571
+ logical: queryOptions.logical,
1555
1572
  searchString
1556
1573
  }) : 0;
1557
1574
  return c.json({ count: total });
@@ -1563,7 +1580,9 @@ var RestApiGenerator = class {
1563
1580
  id: parsed.id
1564
1581
  });
1565
1582
  if (!entity) throw ApiError.notFound("Entity not found");
1566
- return c.json(entity);
1583
+ const nestedCollection = this.resolveNestedWriteCollection(parsed.collectionPath);
1584
+ if (!nestedCollection) return c.json(entity);
1585
+ return c.json(projectResponseFields([entity], queryOptions.fields, nestedCollection, { include: queryOptions.include })[0]);
1567
1586
  } else {
1568
1587
  const queryDict = c.req.queries();
1569
1588
  const queryOptions = this.parseQuery(queryDict);
@@ -1588,8 +1607,9 @@ var RestApiGenerator = class {
1588
1607
  logical: queryOptions.logical,
1589
1608
  searchString
1590
1609
  }) : entities.length;
1610
+ const listCollection = this.resolveNestedWriteCollection(parsed.collectionPath);
1591
1611
  return c.json({
1592
- data: entities,
1612
+ data: listCollection ? projectResponseFields(entities, queryOptions.fields, listCollection, { include: queryOptions.include }) : entities,
1593
1613
  meta: {
1594
1614
  total,
1595
1615
  limit: queryOptions.limit,
@@ -3081,7 +3101,7 @@ function assertStorageAccessControlConfigured(state, isProduction) {
3081
3101
  //#region src/init/docs.ts
3082
3102
  async function mountOpenApiDocs(app, basePath, enableSwagger, activeCollections, requireAuth) {
3083
3103
  if (enableSwagger === false || activeCollections.length === 0) return;
3084
- const { generateOpenApiSpec } = await import("./openapi-generator-BEwyiaAr.js");
3104
+ const { generateOpenApiSpec } = await import("./openapi-generator-C-Rqz9sp.js");
3085
3105
  app.get(`${basePath}/docs`, (c) => {
3086
3106
  const spec = generateOpenApiSpec(activeCollections, {
3087
3107
  basePath,
@@ -3418,6 +3438,13 @@ function resolveBaseUrl(configured) {
3418
3438
  function createTransport(config, environment) {
3419
3439
  const fetchFn = config.fetch || globalThis.fetch;
3420
3440
  const apiPath = config.apiPath || "/api";
3441
+ for (const field of ["baseUrl", "storageUrlOrigin"]) {
3442
+ const value = config[field];
3443
+ if (!value || !apiPath) continue;
3444
+ const trimmed = value.replace(/\/+$/, "");
3445
+ if (!trimmed.endsWith(apiPath)) continue;
3446
+ console.warn(`[Rebase] ${field} ${JSON.stringify(value)} already ends with the API path ${JSON.stringify(apiPath)}, which is appended to it — requests will go to ${trimmed}${apiPath}/… and 404. Pass the origin only (${JSON.stringify(trimmed.slice(0, trimmed.length - apiPath.length) || "/")}), or set \`apiPath\` if the server really does mount the API one level deeper.`);
3447
+ }
3421
3448
  let token = config.token;
3422
3449
  let tokenGetter;
3423
3450
  let onUnauthorizedHandler = config.onUnauthorized;
@@ -3628,7 +3655,9 @@ function createAuth(transport, options) {
3628
3655
  function emit(event, session) {
3629
3656
  for (const fn of listeners) try {
3630
3657
  fn(event, session);
3631
- } catch (e) {}
3658
+ } catch (e) {
3659
+ console.error("Error in auth state change listener:", e);
3660
+ }
3632
3661
  }
3633
3662
  function saveSession(session) {
3634
3663
  if (!persistSession || authFlowMode === "cookie") return;
@@ -4573,8 +4602,15 @@ function createCollectionClient(transport, slug, ws) {
4573
4602
  },
4574
4603
  observe(params, onResult, onError, options) {
4575
4604
  let closed = false;
4576
- const emit = (result) => {
4605
+ let liveDelivered = false;
4606
+ let signature;
4607
+ const deliver = (result, fromLive) => {
4577
4608
  if (closed) return;
4609
+ if (fromLive) liveDelivered = true;
4610
+ else if (liveDelivered) return;
4611
+ const next = `${result.meta?.total ?? ""}|${JSON.stringify(result.data)}`;
4612
+ if (signature !== void 0 && next === signature) return;
4613
+ signature = next;
4578
4614
  onResult({
4579
4615
  ...result,
4580
4616
  fromCache: false,
@@ -4582,10 +4618,10 @@ function createCollectionClient(transport, slug, ws) {
4582
4618
  partial: false
4583
4619
  });
4584
4620
  };
4585
- client.find(params).then(emit).catch((error) => {
4621
+ client.find(params).then((result) => deliver(result, false)).catch((error) => {
4586
4622
  if (!closed) onError?.(error);
4587
4623
  });
4588
- const live = options?.realtime !== false && client.listen ? client.listen(params, emit, onError) : void 0;
4624
+ const live = options?.realtime !== false && client.listen ? client.listen(params, (result) => deliver(result, true), onError) : void 0;
4589
4625
  return () => {
4590
4626
  closed = true;
4591
4627
  live?.();
@@ -4593,17 +4629,24 @@ function createCollectionClient(transport, slug, ws) {
4593
4629
  },
4594
4630
  observeById(id, onResult, onError, options) {
4595
4631
  let closed = false;
4596
- const emit = (row) => {
4632
+ let liveDelivered = false;
4633
+ let signature;
4634
+ const deliver = (row, fromLive) => {
4597
4635
  if (closed) return;
4636
+ if (fromLive) liveDelivered = true;
4637
+ else if (liveDelivered) return;
4638
+ const next = row === void 0 ? "\0missing" : JSON.stringify(row);
4639
+ if (signature !== void 0 && next === signature) return;
4640
+ signature = next;
4598
4641
  onResult(row, {
4599
4642
  fromCache: false,
4600
4643
  hasPendingWrites: false
4601
4644
  });
4602
4645
  };
4603
- client.findById(id).then(emit).catch((error) => {
4646
+ client.findById(id).then((row) => deliver(row, false)).catch((error) => {
4604
4647
  if (!closed) onError?.(error);
4605
4648
  });
4606
- const live = options?.realtime !== false && client.listenById ? client.listenById(id, emit, onError) : void 0;
4649
+ const live = options?.realtime !== false && client.listenById ? client.listenById(id, (row) => deliver(row, true), onError) : void 0;
4607
4650
  return () => {
4608
4651
  closed = true;
4609
4652
  live?.();
@@ -4634,51 +4677,43 @@ function createCollectionClient(transport, slug, ws) {
4634
4677
  client.listen = (params, onUpdate, onError) => {
4635
4678
  let active = true;
4636
4679
  let lastUpdateId = 0;
4680
+ let lastKnownTotal;
4681
+ const window = resolveFindWindow(params);
4637
4682
  const unsub = ws.listenCollection({
4638
4683
  path: slug,
4639
4684
  filter: params?.where,
4685
+ logical: params?.logical,
4640
4686
  limit: params?.limit,
4641
- startAfter: params?.offset ? String(params.offset) : void 0,
4687
+ offset: window.driverOffset,
4642
4688
  orderBy: params?.orderBy?.[0],
4643
4689
  order: params?.orderBy?.[1],
4644
4690
  searchString: params?.searchString
4645
4691
  }, (incomingRows) => {
4646
4692
  const currentUpdateId = ++lastUpdateId;
4647
- const requestedLimit = params?.limit || 20;
4648
- const offset = params?.offset || 0;
4693
+ const requestedLimit = window.limit;
4694
+ const offset = window.offset;
4649
4695
  const rows = incomingRows;
4650
- const heuristicTotal = rows.length;
4651
- const heuristicHasMore = rows.length >= requestedLimit;
4652
- if (client.count) client.count(params).then((total) => {
4653
- if (active && currentUpdateId === lastUpdateId) onUpdate({
4696
+ const emit = (total, hasMore) => {
4697
+ if (!active || currentUpdateId !== lastUpdateId) return;
4698
+ onUpdate({
4654
4699
  data: rows,
4655
4700
  meta: {
4656
4701
  total,
4657
4702
  limit: requestedLimit,
4658
4703
  offset,
4659
- hasMore: offset + rows.length < total
4704
+ hasMore
4660
4705
  }
4661
4706
  });
4707
+ };
4708
+ const emitWithoutCount = () => emit(offset + rows.length, rows.length >= requestedLimit);
4709
+ if (client.count) client.count(params).then((total) => {
4710
+ lastKnownTotal = total;
4711
+ emit(total, offset + rows.length < total);
4662
4712
  }).catch(() => {
4663
- if (active && currentUpdateId === lastUpdateId) onUpdate({
4664
- data: rows,
4665
- meta: {
4666
- total: heuristicTotal,
4667
- limit: requestedLimit,
4668
- offset,
4669
- hasMore: heuristicHasMore
4670
- }
4671
- });
4672
- });
4673
- else onUpdate({
4674
- data: rows,
4675
- meta: {
4676
- total: heuristicTotal,
4677
- limit: requestedLimit,
4678
- offset,
4679
- hasMore: heuristicHasMore
4680
- }
4713
+ if (lastKnownTotal !== void 0) emit(lastKnownTotal, offset + rows.length < lastKnownTotal);
4714
+ else emitWithoutCount();
4681
4715
  });
4716
+ else emitWithoutCount();
4682
4717
  }, onError);
4683
4718
  return () => {
4684
4719
  active = false;
@@ -6061,15 +6096,10 @@ var RebaseWebSocketClient = class {
6061
6096
  }
6062
6097
  }
6063
6098
  createCollectionSubscriptionKey(props) {
6099
+ const { collection, ...query } = props;
6064
6100
  const key = {
6065
- path: props.path,
6066
- filter: props.filter,
6067
- limit: props.limit,
6068
- startAfter: props.startAfter,
6069
- orderBy: props.orderBy,
6070
- order: props.order,
6071
- searchString: props.searchString,
6072
- collection: props.collection?.name
6101
+ ...query,
6102
+ collection: collection?.name
6073
6103
  };
6074
6104
  return JSON.stringify(key, (_, value) => {
6075
6105
  if (value && typeof value === "object" && !Array.isArray(value)) return Object.keys(value).sort().reduce((sorted, k) => {
@@ -7121,12 +7151,20 @@ function sortRows(rows, orderBy) {
7121
7151
  function tiebreak(a, b) {
7122
7152
  return compareValues(a.id, b.id) ?? 0;
7123
7153
  }
7124
- /** Resolve `page`/`offset`/`limit` the way the server does. */
7154
+ /**
7155
+ * Resolve `page`/`offset`/`limit` the way the server does.
7156
+ *
7157
+ * It did not: this defaulted an absent limit to 20 while `/api/data` pages by
7158
+ * 50, so the same `observe()` answered with 20 rows from the local database and
7159
+ * 50 from the network — a list that changed length depending on which side
7160
+ * answered, with `page` striding differently on each. Delegated now, so the
7161
+ * sentence above is true by construction rather than by agreement.
7162
+ */
7125
7163
  function resolvePagination(params) {
7126
- const limit = params?.limit ?? 20;
7164
+ const { limit, offset } = resolveFindWindow(params);
7127
7165
  return {
7128
7166
  limit,
7129
- offset: params?.page != null ? Math.max(0, (params.page - 1) * limit) : params?.offset ?? 0
7167
+ offset
7130
7168
  };
7131
7169
  }
7132
7170
  /**
@@ -7748,9 +7786,8 @@ var OfflineManager = class {
7748
7786
  if (!state) return {
7749
7787
  data: [],
7750
7788
  meta: {
7789
+ ...resolvePagination(params),
7751
7790
  total: 0,
7752
- limit: params?.limit ?? 20,
7753
- offset: params?.offset ?? 0,
7754
7791
  hasMore: false
7755
7792
  },
7756
7793
  fromCache: true,
@@ -7942,17 +7979,17 @@ var OfflineManager = class {
7942
7979
  return row;
7943
7980
  }
7944
7981
  recordSnapshot(slug, params, result) {
7982
+ const window = resolvePagination(params);
7945
7983
  const meta = result.meta ?? {
7946
7984
  total: result.data?.length ?? 0,
7947
- limit: 20,
7948
- offset: 0,
7985
+ ...window,
7949
7986
  hasMore: false
7950
7987
  };
7951
7988
  const snapshot = {
7952
7989
  ids: (result.data ?? []).map((row) => row.id).filter((id) => id !== void 0),
7953
7990
  total: meta.total ?? result.data?.length ?? 0,
7954
- limit: meta.limit ?? params?.limit ?? 20,
7955
- offset: meta.offset ?? params?.offset ?? 0,
7991
+ limit: meta.limit ?? window.limit,
7992
+ offset: meta.offset ?? window.offset,
7956
7993
  hasMore: meta.hasMore ?? false
7957
7994
  };
7958
7995
  const state = this.collectionState(slug);
@@ -8414,23 +8451,37 @@ var OfflineManager = class {
8414
8451
  /**
8415
8452
  * Derive a WebSocket URL from an HTTP base URL.
8416
8453
  * `http://` → `ws://`, `https://` → `wss://`.
8454
+ *
8455
+ * A backend mounted under a path is the reason `baseUrl` accepts one, so the
8456
+ * path is kept. It used to be kept for an absolute `baseUrl` and dropped for a
8457
+ * relative one — resolved through `.origin` — so one deployment dialled two
8458
+ * different sockets depending on whether its config said `"/backend"` or
8459
+ * `"https://app.example.com/backend"`.
8460
+ *
8461
+ * Returns `""` when there is nothing to resolve against: a relative `baseUrl`
8462
+ * outside a browser has no origin, and inventing one would dial somewhere
8463
+ * arbitrary. The caller warns rather than leaving that silent.
8417
8464
  */
8418
8465
  function deriveWebSocketUrl(baseUrl) {
8466
+ const toWsProtocol = (url) => {
8467
+ const secure = /^(https|wss):/i.test(url);
8468
+ return url.replace(/^https?:\/\//i, secure ? "wss://" : "ws://").replace(/^wss?:\/\//i, secure ? "wss://" : "ws://").replace(/\/$/, "");
8469
+ };
8419
8470
  if (typeof window !== "undefined") {
8420
- let absoluteUrl = "";
8471
+ let absoluteUrl;
8421
8472
  if (!baseUrl) absoluteUrl = window.location.origin;
8422
8473
  else if (/^https?:\/\//i.test(baseUrl) || /^wss?:\/\//i.test(baseUrl)) absoluteUrl = baseUrl;
8423
8474
  else try {
8424
- absoluteUrl = new URL(baseUrl, window.location.href).origin;
8475
+ const resolved = new URL(baseUrl, window.location.href);
8476
+ absoluteUrl = resolved.origin + resolved.pathname;
8425
8477
  } catch {
8426
8478
  absoluteUrl = window.location.origin;
8427
8479
  }
8428
- const protocol = absoluteUrl.startsWith("https:") || absoluteUrl.startsWith("wss:") ? "wss:" : "ws:";
8429
- return absoluteUrl.replace(/^https?:\/\//i, `${protocol}//`).replace(/^wss?:\/\//i, `${protocol}//`).replace(/\/$/, "");
8480
+ return toWsProtocol(absoluteUrl);
8430
8481
  }
8431
8482
  if (!baseUrl) return "";
8432
8483
  if (!/^https?:\/\//i.test(baseUrl) && !/^wss?:\/\//i.test(baseUrl)) return "";
8433
- return baseUrl.replace(/^https?:\/\//i, (match) => match.toLowerCase() === "https://" ? "wss://" : "ws://").replace(/\/$/, "");
8484
+ return toWsProtocol(baseUrl);
8434
8485
  }
8435
8486
  function createRebaseClient(options) {
8436
8487
  const transport = createTransport(options, { credentialOutOfBand: options.auth?.authFlowMode === "cookie" });
@@ -8458,7 +8509,11 @@ function createRebaseClient(options) {
8458
8509
  });
8459
8510
  return storageSourcesPromise;
8460
8511
  };
8461
- const resolvedWsUrl = options.realtime !== false ? options.websocketUrl ?? deriveWebSocketUrl(options.baseUrl) : void 0;
8512
+ const realtimeEnabled = options.realtime !== false;
8513
+ const resolvedWsUrl = realtimeEnabled ? options.websocketUrl ?? deriveWebSocketUrl(options.baseUrl) : void 0;
8514
+ const realtimeUnreachable = realtimeEnabled && !resolvedWsUrl;
8515
+ const unreachableReason = `no WebSocket URL could be derived from baseUrl ${JSON.stringify(options.baseUrl ?? null)} — outside a browser there is no page origin to resolve a relative URL against. Pass an absolute \`baseUrl\`, set \`websocketUrl\` explicitly, or pass \`realtime: false\` to say this was intended.`;
8516
+ if (realtimeUnreachable) console.warn(`[Rebase] Realtime is enabled but ${unreachableReason} Live queries will fall back to a single fetch and channels will throw.`);
8462
8517
  let ws;
8463
8518
  /** One channel object per name — see `realtime.channel`. */
8464
8519
  const realtimeChannels = /* @__PURE__ */ new Map();
@@ -8578,7 +8633,7 @@ function createRebaseClient(options) {
8578
8633
  * cut off the others.
8579
8634
  */
8580
8635
  channel: (name, options) => {
8581
- if (!ws) throw new RebaseClientError("Realtime is disabled on this client (realtime: false), so channels are unavailable.");
8636
+ if (!ws) throw new RebaseClientError(realtimeUnreachable ? `Realtime is enabled but ${unreachableReason}` : "Realtime is disabled on this client (realtime: false), so channels are unavailable.");
8582
8637
  let existing = realtimeChannels.get(name);
8583
8638
  if (!existing) {
8584
8639
  existing = new RebaseRealtimeChannel(name, ws, options);
@@ -9249,7 +9304,7 @@ async function _initializeRebaseBackend(config) {
9249
9304
  ]) {
9250
9305
  const providerConfig = safeAuthConfig[key];
9251
9306
  if (providerConfig && requiredFields.every((f) => Boolean(providerConfig[f]))) {
9252
- const createFn = (await import("./auth-CuC9M2x6.js").then((n) => n.t))[factory];
9307
+ const createFn = (await import("./auth-BE_FSK1l.js").then((n) => n.t))[factory];
9253
9308
  oauthProviders.push(createFn(providerConfig));
9254
9309
  }
9255
9310
  }
@@ -9343,7 +9398,7 @@ async function _initializeRebaseBackend(config) {
9343
9398
  let schemaEditorOff = schemaEditorUnavailable();
9344
9399
  let schemaEditorRoutes;
9345
9400
  if (!schemaEditorOff && config.collectionsDir) try {
9346
- schemaEditorRoutes = (await import("./schema-editor-routes-CbF20Mf1.js")).createSchemaEditorRoutes(config.collectionsDir);
9401
+ schemaEditorRoutes = (await import("./schema-editor-routes-C1DxDnDR.js")).createSchemaEditorRoutes(config.collectionsDir);
9347
9402
  } catch (err) {
9348
9403
  if (err?.code === "ERR_MODULE_NOT_FOUND") {
9349
9404
  schemaEditorOff = {
@@ -9556,9 +9611,9 @@ async function _initializeRebaseBackend(config) {
9556
9611
  let cronScheduler;
9557
9612
  if (config.cronsDir) {
9558
9613
  const { loadCronJobsFromDirectory } = await import("./cron-loader-B1S2MCSl.js").then((n) => n.t);
9559
- const { CronScheduler } = await import("./cron-scheduler-B3RFt0HS.js").then((n) => n.n);
9560
- const { createCronRoutes } = await import("./cron-routes-CrQ0tK-_.js").then((n) => n.n);
9561
- const { createCronStore } = await import("./cron-store-BywZsyfZ.js").then((n) => n.n);
9614
+ const { CronScheduler } = await import("./cron-scheduler-BhFZWR0T.js").then((n) => n.n);
9615
+ const { createCronRoutes } = await import("./cron-routes-D5a9v1HY.js").then((n) => n.n);
9616
+ const { createCronStore } = await import("./cron-store-Cu4sOseb.js").then((n) => n.n);
9562
9617
  const loadedCronJobs = await loadCronJobsFromDirectory(config.cronsDir);
9563
9618
  cronScheduler = new CronScheduler();
9564
9619
  cronScheduler.setClient(serverClient);
@@ -9583,7 +9638,7 @@ async function _initializeRebaseBackend(config) {
9583
9638
  } else logger.warn(`Cron routes mounted at ${basePath}/cron, but no jobs loaded from ${config.cronsDir}. Nothing is scheduled — check the messages above for files that failed to load.`);
9584
9639
  }
9585
9640
  {
9586
- const { createBackupRoutes, parseBackupDestination } = await import("./backup-CVggVhR2.js").then((n) => n.t);
9641
+ const { createBackupRoutes, parseBackupDestination } = await import("./backup-BJiY_mM-.js").then((n) => n.t);
9587
9642
  const backupRouter = new Hono();
9588
9643
  applyAdminGate(backupRouter, "Backup");
9589
9644
  backupRouter.route("/", createBackupRoutes({
@@ -11785,7 +11840,8 @@ async function bootFromBundle(options = {}) {
11785
11840
  });
11786
11841
  await ensureCollectionPolicies(bundle, dataSources, env);
11787
11842
  metrics?.setKnownCollections(backend.collectionRegistry.getCollections().map((collection) => collection.slug).filter((slug) => Boolean(slug)));
11788
- app.get("/health", async (c) => {
11843
+ const healthPaths = ["/health", `${(env.REBASE_BASE_PATH || "/api").replace(/\/$/, "")}/health`];
11844
+ for (const healthPath of [...new Set(healthPaths)]) app.get(healthPath, async (c) => {
11789
11845
  const result = await backend.healthCheck();
11790
11846
  const unhealthy = (await Promise.all(dataSources.filter((source) => source.key !== DEFAULT_DATA_SOURCE_KEY).map(async (source) => ({
11791
11847
  key: source.key,
@@ -11891,7 +11947,7 @@ async function bootStaticApp(bundle, devRoot, options) {
11891
11947
  if (metrics) app.use("/*", metrics.middleware);
11892
11948
  const server = createServer(getRequestListener(app.fetch));
11893
11949
  app.get("/livez", (c) => c.json({ status: "ok" }));
11894
- app.get("/health", (c) => c.json({
11950
+ for (const healthPath of [.../* @__PURE__ */ new Set(["/health", `${basePath.replace(/\/$/, "")}/health`])]) app.get(healthPath, (c) => c.json({
11895
11951
  status: "ok",
11896
11952
  latencyMs: 0
11897
11953
  }));
@@ -12109,6 +12165,6 @@ async function ensureCollectionPolicies(bundle, dataSources, env) {
12109
12165
  logger.info(applied > 0 ? `Applied ${applied} RLS policy statement(s) before serving.` : "RLS policies are up to date.");
12110
12166
  }
12111
12167
  //#endregion
12112
- export { ApiError, BundleError, CronScheduler, DEFAULT_DRIVER_ID, DEFAULT_MAX_FILE_SIZE, DEFAULT_STORAGE_ID, DOCUMENT_MIME_TYPES, DefaultDriverRegistry, DefaultStorageRegistry, GCSStorageController, IMAGE_MIME_TYPES, LocalStorageController, MetricsRegistry, S3StorageController, SMTPEmailService, TransformCache, TusHandler, WebhookDispatcher, _resetRebaseMock, _setRebaseMock, applyCollectionDefaults, assertCollectionConfigs, assertDistinctSuffixes, authJwt, authRoles, authUid, 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, extractUserFromToken, fileTokenAuth, findCollectionConfigProblems, generateSecurePassword, getEmailVerificationTemplate, getMagicLinkTemplate, getPasswordResetTemplate, getUserInvitationTemplate, getWelcomeEmailTemplate, hashPassword, httpMethodToOperation, initializeDataSource, initializeDataSources, initializeRebaseBackend, installShutdownHandlers, isApiKeyToken, isAuthAdapter, isDatabaseAdapter, isLocalhostOrigin, isOperationAllowed, isRebaseApiError, isTransformableImage, listBackupObjects, listenWithPortRetry, loadBootEnv, loadBundle, loadBundleConfigExports, loadBundleSchema, loadCollectionsFromDirectory, loadCronJobsFromDirectory, loadDeclaredStorageSources, loadEnv, loadFunctionsFromDirectory, loadUsersCollection, logger, optionalAuth, parseBackupDestination, parseBackupTimestamp, parseTransformOptions, queryTokenAuth, readBackupBytes, readBundleManifest, rebase, requireAdmin, requireAuth, resolveAuthHooks, resolveAuthOptions, resolveCorsOrigin, resolveDataSources, resolveEmailOptions, resolveRequireAuth, resolveStorageBackend, resolveStorageSources, runFromBundle, safeCompare, serveSPA, transformImage, unknownKeyPolicyFromEnv, validateApiKey, validateCronExpression, validatePasswordStrength, verifyPassword };
12168
+ export { ApiError, BundleError, CronScheduler, DEFAULT_DRIVER_ID, DEFAULT_MAX_FILE_SIZE, DEFAULT_STORAGE_ID, DOCUMENT_MIME_TYPES, DefaultDriverRegistry, DefaultStorageRegistry, GCSStorageController, IMAGE_MIME_TYPES, LocalStorageController, MetricsRegistry, S3StorageController, SMTPEmailService, TransformCache, TusHandler, WebhookDispatcher, _resetRebaseMock, _setRebaseMock, applyCollectionDefaults, assertCollectionConfigs, assertDistinctSuffixes, authJwt, authRoles, authUid, 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, extractUserFromToken, fileTokenAuth, findCollectionConfigProblems, generateSecurePassword, getEmailVerificationTemplate, getMagicLinkTemplate, getPasswordResetTemplate, getUserInvitationTemplate, getWelcomeEmailTemplate, hashPassword, httpMethodToOperation, initializeDataSource, initializeDataSources, initializeRebaseBackend, installShutdownHandlers, isApiKeyToken, isAuthAdapter, isDatabaseAdapter, isLocalhostOrigin, isOperationAllowed, isTransformableImage, listBackupObjects, listenWithPortRetry, loadBootEnv, loadBundle, loadBundleConfigExports, loadBundleSchema, loadCollectionsFromDirectory, loadCronJobsFromDirectory, loadDeclaredStorageSources, loadEnv, loadFunctionsFromDirectory, loadUsersCollection, logger, optionalAuth, parseBackupDestination, parseBackupTimestamp, parseTransformOptions, queryTokenAuth, readBackupBytes, readBundleManifest, rebase, requireAdmin, requireAuth, resolveAuthHooks, resolveAuthOptions, resolveCorsOrigin, resolveDataSources, resolveEmailOptions, resolveRequireAuth, resolveStorageBackend, resolveStorageSources, runFromBundle, safeCompare, serveSPA, transformImage, unknownKeyPolicyFromEnv, validateApiKey, validateCronExpression, validatePasswordStrength, verifyPassword };
12113
12169
 
12114
12170
  //# sourceMappingURL=index.es.js.map