@rebasepro/server-core 0.2.3 → 0.2.4

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 (109) hide show
  1. package/dist/common/src/collections/default-collections.d.ts +12 -0
  2. package/dist/common/src/collections/index.d.ts +1 -0
  3. package/dist/common/src/util/permissions.d.ts +1 -0
  4. package/dist/{index-BZoAtuqi.js → index-Cr1D21av.js} +11 -3
  5. package/dist/index-Cr1D21av.js.map +1 -0
  6. package/dist/index.es.js +2166 -208
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/index.umd.js +2155 -193
  9. package/dist/index.umd.js.map +1 -1
  10. package/dist/server-core/src/api/logs-routes.d.ts +37 -0
  11. package/dist/server-core/src/api/rest/api-generator.d.ts +6 -0
  12. package/dist/server-core/src/api/types.d.ts +6 -1
  13. package/dist/server-core/src/auth/adapter-middleware.d.ts +7 -3
  14. package/dist/server-core/src/auth/admin-routes.d.ts +3 -3
  15. package/dist/server-core/src/auth/api-keys/api-key-middleware.d.ts +39 -0
  16. package/dist/server-core/src/auth/api-keys/api-key-permission-guard.d.ts +32 -0
  17. package/dist/server-core/src/auth/api-keys/api-key-routes.d.ts +20 -0
  18. package/dist/server-core/src/auth/api-keys/api-key-store.d.ts +35 -0
  19. package/dist/server-core/src/auth/api-keys/api-key-types.d.ts +88 -0
  20. package/dist/server-core/src/auth/api-keys/index.d.ts +17 -0
  21. package/dist/server-core/src/auth/{auth-overrides.d.ts → auth-hooks.d.ts} +69 -11
  22. package/dist/server-core/src/auth/builtin-auth-adapter.d.ts +3 -3
  23. package/dist/server-core/src/auth/index.d.ts +5 -3
  24. package/dist/server-core/src/auth/interfaces.d.ts +93 -3
  25. package/dist/server-core/src/auth/jwt.d.ts +3 -1
  26. package/dist/server-core/src/auth/mfa.d.ts +49 -0
  27. package/dist/server-core/src/auth/middleware.d.ts +7 -0
  28. package/dist/server-core/src/auth/rate-limiter.d.ts +19 -0
  29. package/dist/server-core/src/auth/routes.d.ts +3 -3
  30. package/dist/server-core/src/env.d.ts +6 -0
  31. package/dist/server-core/src/index.d.ts +1 -0
  32. package/dist/server-core/src/init.d.ts +3 -3
  33. package/dist/server-core/src/services/webhook-service.d.ts +29 -0
  34. package/dist/server-core/src/storage/image-transform.d.ts +48 -0
  35. package/dist/server-core/src/storage/index.d.ts +3 -0
  36. package/dist/server-core/src/storage/tus-handler.d.ts +51 -0
  37. package/dist/types/src/controllers/auth.d.ts +2 -24
  38. package/dist/types/src/controllers/client.d.ts +0 -3
  39. package/dist/types/src/controllers/collection_registry.d.ts +1 -1
  40. package/dist/types/src/controllers/data_driver.d.ts +18 -0
  41. package/dist/types/src/controllers/registry.d.ts +5 -4
  42. package/dist/types/src/rebase_context.d.ts +1 -1
  43. package/dist/types/src/types/auth_adapter.d.ts +2 -4
  44. package/dist/types/src/types/collections.d.ts +0 -4
  45. package/dist/types/src/types/component_ref.d.ts +1 -1
  46. package/dist/types/src/types/cron.d.ts +1 -1
  47. package/dist/types/src/types/entity_views.d.ts +1 -0
  48. package/dist/types/src/types/export_import.d.ts +1 -1
  49. package/dist/types/src/types/formex.d.ts +2 -2
  50. package/dist/types/src/types/properties.d.ts +2 -2
  51. package/dist/types/src/types/translations.d.ts +28 -12
  52. package/dist/types/src/types/user_management_delegate.d.ts +6 -4
  53. package/dist/types/src/users/roles.d.ts +0 -8
  54. package/package.json +8 -6
  55. package/src/api/ast-schema-editor.ts +4 -4
  56. package/src/api/errors.ts +16 -7
  57. package/src/api/logs-routes.ts +129 -0
  58. package/src/api/rest/api-generator.ts +42 -2
  59. package/src/api/rest/query-parser.ts +37 -1
  60. package/src/api/types.ts +6 -1
  61. package/src/auth/adapter-middleware.ts +20 -4
  62. package/src/auth/admin-routes.ts +39 -14
  63. package/src/auth/api-keys/api-key-middleware.ts +126 -0
  64. package/src/auth/api-keys/api-key-permission-guard.ts +64 -0
  65. package/src/auth/api-keys/api-key-routes.ts +183 -0
  66. package/src/auth/api-keys/api-key-store.ts +317 -0
  67. package/src/auth/api-keys/api-key-types.ts +94 -0
  68. package/src/auth/api-keys/index.ts +37 -0
  69. package/src/auth/{auth-overrides.ts → auth-hooks.ts} +81 -14
  70. package/src/auth/builtin-auth-adapter.ts +31 -19
  71. package/src/auth/index.ts +7 -3
  72. package/src/auth/interfaces.ts +111 -3
  73. package/src/auth/jwt.ts +19 -5
  74. package/src/auth/mfa.ts +160 -0
  75. package/src/auth/middleware.ts +20 -1
  76. package/src/auth/rate-limiter.ts +92 -0
  77. package/src/auth/routes.ts +455 -24
  78. package/src/cron/cron-loader.ts +5 -10
  79. package/src/cron/cron-scheduler.ts +11 -12
  80. package/src/cron/cron-store.ts +8 -7
  81. package/src/env.ts +2 -0
  82. package/src/functions/function-loader.ts +6 -9
  83. package/src/index.ts +1 -2
  84. package/src/init.ts +37 -7
  85. package/src/serve-spa.ts +5 -4
  86. package/src/services/webhook-service.ts +155 -0
  87. package/src/storage/image-transform.ts +202 -0
  88. package/src/storage/index.ts +3 -0
  89. package/src/storage/routes.ts +56 -3
  90. package/src/storage/tus-handler.ts +315 -0
  91. package/src/utils/dev-port.ts +14 -0
  92. package/src/utils/logging.ts +9 -7
  93. package/test/admin-routes.test.ts +74 -7
  94. package/test/api-generator.test.ts +0 -1
  95. package/test/api-key-permission-guard.test.ts +132 -0
  96. package/test/ast-schema-editor.test.ts +26 -0
  97. package/test/auth-routes.test.ts +1 -2
  98. package/test/backend-hooks-admin.test.ts +3 -4
  99. package/test/email-templates.test.ts +169 -0
  100. package/test/function-loader.test.ts +124 -0
  101. package/test/jwt.test.ts +4 -2
  102. package/test/mfa.test.ts +197 -0
  103. package/test/middleware.test.ts +10 -5
  104. package/test/webhook-service.test.ts +249 -0
  105. package/vite.config.ts +3 -2
  106. package/dist/index-BZoAtuqi.js.map +0 -1
  107. package/dist/server-core/src/bootstrappers/index.d.ts +0 -0
  108. package/src/bootstrappers/index.ts +0 -1
  109. package/src/singleton.test.ts +0 -28
package/dist/index.es.js CHANGED
@@ -1,29 +1,33 @@
1
1
  import * as fs$4 from "fs";
2
- import fs__default from "fs";
2
+ import fs__default, { existsSync } from "fs";
3
3
  import * as path$3 from "path";
4
- import path__default from "path";
4
+ import path__default, { join as join$1 } from "path";
5
5
  import require$$0$5, { pathToFileURL } from "url";
6
6
  import { Hono } from "hono";
7
7
  import require$$0$3 from "buffer";
8
8
  import require$$0$4 from "stream";
9
9
  import require$$5, { promisify } from "util";
10
10
  import * as require$$0$2 from "crypto";
11
- import require$$0__default, { randomBytes, createHash, timingSafeEqual as timingSafeEqual$1, scrypt } from "crypto";
11
+ import require$$0__default, { randomBytes, createHash, timingSafeEqual as timingSafeEqual$1, scrypt, createHmac, randomUUID as randomUUID$1 } from "crypto";
12
12
  import { bodyLimit } from "hono/body-limit";
13
13
  import { csrf } from "hono/csrf";
14
14
  import require$$0$a from "child_process";
15
15
  import require$$1 from "https";
16
+ import require$$1$2 from "querystring";
16
17
  import require$$0$7 from "net";
17
18
  import require$$1$1 from "tls";
18
19
  import require$$2 from "assert";
19
20
  import require$$0$6 from "http";
20
- import require$$1$2 from "os";
21
+ import require$$1$3 from "os";
21
22
  import require$$0$8 from "node:events";
22
- import require$$1$3 from "node:process";
23
+ import require$$1$4 from "node:process";
23
24
  import require$$2$1 from "node:util";
24
25
  import require$$0$9 from "events";
25
26
  import { S3Client, PutObjectCommand, HeadObjectCommand, GetObjectCommand, DeleteObjectCommand, ListObjectsV2Command } from "@aws-sdk/client-s3";
26
27
  import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
28
+ import { mkdir as mkdir$1, unlink as unlink$1, writeFile as writeFile$1, open } from "fs/promises";
29
+ import require$$3 from "zlib";
30
+ import require$$4$1 from "dns";
27
31
  import { cors } from "hono/cors";
28
32
  import { secureHeaders } from "hono/secure-headers";
29
33
  import { serve } from "@hono/node-server";
@@ -211,30 +215,6 @@ var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof win
211
215
  function getDefaultExportFromCjs(x) {
212
216
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
213
217
  }
214
- function getAugmentedNamespace(n) {
215
- if (n.__esModule) return n;
216
- var f2 = n.default;
217
- if (typeof f2 == "function") {
218
- var a2 = function a3() {
219
- if (this instanceof a3) {
220
- return Reflect.construct(f2, arguments, this.constructor);
221
- }
222
- return f2.apply(this, arguments);
223
- };
224
- a2.prototype = f2.prototype;
225
- } else a2 = {};
226
- Object.defineProperty(a2, "__esModule", { value: true });
227
- Object.keys(n).forEach(function(k) {
228
- var d2 = Object.getOwnPropertyDescriptor(n, k);
229
- Object.defineProperty(a2, k, d2.get ? d2 : {
230
- enumerable: true,
231
- get: function() {
232
- return n[k];
233
- }
234
- });
235
- });
236
- return a2;
237
- }
238
218
  function commonjsRequire(path2) {
239
219
  throw new Error('Could not dynamically require "' + path2 + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
240
220
  }
@@ -1066,6 +1046,9 @@ function mergeDeep(target, source, ignoreUndefined = false) {
1066
1046
  return output;
1067
1047
  }
1068
1048
  for (const key in source) {
1049
+ if (key === "__proto__" || key === "constructor" || key === "prototype") {
1050
+ continue;
1051
+ }
1069
1052
  if (Object.prototype.hasOwnProperty.call(source, key)) {
1070
1053
  const sourceValue = source[key];
1071
1054
  const outputValue = output[key];
@@ -1254,8 +1237,8 @@ function sanitizeRelation(relation, sourceCollection, resolveCollection) {
1254
1237
  } catch (e) {
1255
1238
  }
1256
1239
  if (!foundForeignKey) {
1257
- const keyPrefix = newRelation.inverseRelationName ? toSnakeCase(newRelation.inverseRelationName) : sourceName;
1258
- newRelation.foreignKeyOnTarget = generateForeignKeyName(keyPrefix);
1240
+ const keyPrefix2 = newRelation.inverseRelationName ? toSnakeCase(newRelation.inverseRelationName) : sourceName;
1241
+ newRelation.foreignKeyOnTarget = generateForeignKeyName(keyPrefix2);
1259
1242
  }
1260
1243
  }
1261
1244
  } else if (newRelation.cardinality === "many" && newRelation.direction === "inverse") {
@@ -2855,10 +2838,15 @@ const errorHandler = (err, c) => {
2855
2838
  const issue = error2.code === "42703" ? "column" : "table";
2856
2839
  logMessage = `Database schema mismatch (${issue} missing): ${error2.message}. Did you forget to run migrations ('pnpm db:push' or 'pnpm db:migrate')?`;
2857
2840
  }
2858
- console.error(`❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
2859
2841
  const causePg = error2.cause && typeof error2.cause === "object" ? error2.cause : void 0;
2860
2842
  const pgErrorCode = causePg?.code || error2.code;
2861
- const suppressStack = pgErrorCode === "42703" || pgErrorCode === "42P01" || statusCode < 500 && code2 === "BAD_REQUEST";
2843
+ const isDbSchemaMismatch = pgErrorCode === "42703" || pgErrorCode === "42P01";
2844
+ if (isDbSchemaMismatch) {
2845
+ console.warn(`⚠️ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
2846
+ } else {
2847
+ console.error(`❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
2848
+ }
2849
+ const suppressStack = isDbSchemaMismatch || statusCode < 500 && code2 === "BAD_REQUEST";
2862
2850
  if (!suppressStack) {
2863
2851
  console.error(error2.stack || error2);
2864
2852
  }
@@ -2939,7 +2927,7 @@ function parseQueryOptions(query) {
2939
2927
  options2.offset = (page - 1) * limit;
2940
2928
  }
2941
2929
  options2.where = {};
2942
- const reservedQueryKeys = ["limit", "offset", "page", "orderBy", "include", "fields", "searchString"];
2930
+ const reservedQueryKeys = ["limit", "offset", "page", "orderBy", "include", "fields", "searchString", "vector_search", "vector", "vector_distance", "vector_threshold"];
2943
2931
  for (const [key, rawValue] of Object.entries(query)) {
2944
2932
  if (reservedQueryKeys.includes(key)) continue;
2945
2933
  const value = Array.isArray(rawValue) ? rawValue[rawValue.length - 1] : rawValue;
@@ -3011,8 +2999,63 @@ function parseQueryOptions(query) {
3011
2999
  const fieldsStr = String(query.fields).trim();
3012
3000
  options2.fields = fieldsStr.split(",").map((s2) => s2.trim()).filter(Boolean);
3013
3001
  }
3002
+ if (query.vector_search && query.vector) {
3003
+ const vectorStr = String(query.vector);
3004
+ let queryVector;
3005
+ try {
3006
+ queryVector = JSON.parse(vectorStr);
3007
+ if (!Array.isArray(queryVector) || !queryVector.every((v) => typeof v === "number")) {
3008
+ throw new Error("Expected array of numbers");
3009
+ }
3010
+ } catch {
3011
+ throw new Error("Invalid vector format. Expected JSON array of numbers, e.g. [0.1,0.2,0.3]");
3012
+ }
3013
+ const distanceParam = query.vector_distance ? String(query.vector_distance) : "cosine";
3014
+ if (distanceParam !== "cosine" && distanceParam !== "l2" && distanceParam !== "inner_product") {
3015
+ throw new Error(`Invalid vector_distance: ${distanceParam}. Expected: cosine, l2, or inner_product`);
3016
+ }
3017
+ const vectorSearch = {
3018
+ property: String(query.vector_search),
3019
+ vector: queryVector,
3020
+ distance: distanceParam
3021
+ };
3022
+ if (query.vector_threshold) {
3023
+ const threshold = parseFloat(String(query.vector_threshold));
3024
+ if (isNaN(threshold)) {
3025
+ throw new Error("Invalid vector_threshold. Expected a number.");
3026
+ }
3027
+ vectorSearch.threshold = threshold;
3028
+ }
3029
+ options2.vectorSearch = vectorSearch;
3030
+ }
3014
3031
  return options2;
3015
3032
  }
3033
+ function httpMethodToOperation(method) {
3034
+ const upper = method.toUpperCase();
3035
+ switch (upper) {
3036
+ case "GET":
3037
+ case "HEAD":
3038
+ case "OPTIONS":
3039
+ return "read";
3040
+ case "POST":
3041
+ case "PUT":
3042
+ case "PATCH":
3043
+ return "write";
3044
+ case "DELETE":
3045
+ return "delete";
3046
+ default:
3047
+ return "read";
3048
+ }
3049
+ }
3050
+ function isOperationAllowed(permissions, collection, operation) {
3051
+ for (const perm of permissions) {
3052
+ const collectionMatch = perm.collection === "*" || perm.collection === collection;
3053
+ if (collectionMatch && perm.operations.includes(operation)) {
3054
+ return true;
3055
+ }
3056
+ }
3057
+ return false;
3058
+ }
3016
3059
  class RestApiGenerator {
3017
3060
  collections;
3018
3061
  router;
@@ -3045,6 +3088,19 @@ class RestApiGenerator {
3045
3088
  this.createSubcollectionRoutes();
3046
3089
  return this.router;
3047
3090
  }
3091
+ /**
3092
+ * Check API key permissions for a collection operation.
3093
+ * Throws 403 if the key doesn't have the required permission.
3094
+ * No-ops if the request is not authenticated via an API key.
3095
+ */
3096
+ enforceApiKeyPermission(c, collectionSlug) {
3097
+ const apiKey = c.get("apiKey");
3098
+ if (!apiKey) return;
3099
+ const operation = httpMethodToOperation(c.req.method);
3100
+ if (!isOperationAllowed(apiKey.permissions, collectionSlug, operation)) {
3101
+ throw ApiError.forbidden(`API key does not have "${operation}" permission for collection "${collectionSlug}"`, "API_KEY_FORBIDDEN");
3102
+ }
3103
+ }
3048
3104
  /**
3049
3105
  * Get the typed RestFetchService from a driver if it exposes one (for include support).
3050
3106
  */
@@ -3058,6 +3114,7 @@ class RestApiGenerator {
3058
3114
  const basePath = `/${collection.slug}`;
3059
3115
  const resolvedCollection = collection;
3060
3116
  this.router.get(`${basePath}/count`, async (c) => {
3117
+ this.enforceApiKeyPermission(c, collection.slug);
3061
3118
  const queryDict = c.req.query();
3062
3119
  const queryOptions = parseQueryOptions(queryDict);
3063
3120
  const searchString = queryDict.searchString;
@@ -3068,6 +3125,7 @@ class RestApiGenerator {
3068
3125
  });
3069
3126
  });
3070
3127
  this.router.get(basePath, async (c) => {
3128
+ this.enforceApiKeyPermission(c, collection.slug);
3071
3129
  const queryDict = c.req.query();
3072
3130
  const queryOptions = parseQueryOptions(queryDict);
3073
3131
  const searchString = queryDict.searchString;
@@ -3082,7 +3140,8 @@ class RestApiGenerator {
3082
3140
  offset: queryOptions.offset,
3083
3141
  orderBy: queryOptions.orderBy?.[0]?.field,
3084
3142
  order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
3085
- searchString
3143
+ searchString,
3144
+ vectorSearch: queryOptions.vectorSearch
3086
3145
  }, queryOptions.include);
3087
3146
  entities2 = await this.applyAfterReadBatch(collection.slug, entities2, hookCtx);
3088
3147
  const total2 = await this.countRawEntities(driver, resolvedCollection, queryOptions, searchString);
@@ -3110,6 +3169,7 @@ class RestApiGenerator {
3110
3169
  });
3111
3170
  });
3112
3171
  this.router.get(`${basePath}/:id`, async (c) => {
3172
+ this.enforceApiKeyPermission(c, collection.slug);
3113
3173
  const id = c.req.param("id");
3114
3174
  const queryDict = c.req.query();
3115
3175
  const queryOptions = parseQueryOptions(queryDict);
@@ -3140,6 +3200,7 @@ class RestApiGenerator {
3140
3200
  });
3141
3201
  this.router.post(basePath, async (c) => {
3142
3202
  try {
3203
+ this.enforceApiKeyPermission(c, collection.slug);
3143
3204
  const driver = c.get("driver") || this.driver;
3144
3205
  const path2 = collection.slug;
3145
3206
  const hookCtx = this.buildHookContext(c, "POST");
@@ -3168,6 +3229,7 @@ class RestApiGenerator {
3168
3229
  });
3169
3230
  this.router.put(`${basePath}/:id`, async (c) => {
3170
3231
  try {
3232
+ this.enforceApiKeyPermission(c, collection.slug);
3171
3233
  const id = c.req.param("id");
3172
3234
  const driver = c.get("driver") || this.driver;
3173
3235
  const hookCtx = this.buildHookContext(c, "PUT");
@@ -3204,6 +3266,7 @@ class RestApiGenerator {
3204
3266
  }
3205
3267
  });
3206
3268
  this.router.delete(`${basePath}/:id`, async (c) => {
3269
+ this.enforceApiKeyPermission(c, collection.slug);
3207
3270
  const id = c.req.param("id");
3208
3271
  const driver = c.get("driver") || this.driver;
3209
3272
  const hookCtx = this.buildHookContext(c, "DELETE");
@@ -3275,6 +3338,7 @@ class RestApiGenerator {
3275
3338
  const parsed = parseSubPath(rawPath);
3276
3339
  if (!parsed) return next();
3277
3340
  const driver = c.get("driver") || this.driver;
3341
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3278
3342
  if (parsed.entityId === "count") {
3279
3343
  const queryDict = c.req.query();
3280
3344
  const queryOptions = parseQueryOptions(queryDict);
@@ -3322,6 +3386,7 @@ class RestApiGenerator {
3322
3386
  const parsed = parseSubPath(rawPath);
3323
3387
  if (!parsed || parsed.entityId) return next();
3324
3388
  const driver = c.get("driver") || this.driver;
3389
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3325
3390
  const body = await c.req.json().catch(() => ({}));
3326
3391
  const entity = await driver.saveEntity({
3327
3392
  path: parsed.collectionPath,
@@ -3337,6 +3402,7 @@ class RestApiGenerator {
3337
3402
  const parsed = parseSubPath(rawPath);
3338
3403
  if (!parsed || !parsed.entityId) return next();
3339
3404
  const driver = c.get("driver") || this.driver;
3405
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3340
3406
  const body = await c.req.json().catch(() => ({}));
3341
3407
  const entity = await driver.saveEntity({
3342
3408
  path: parsed.collectionPath,
@@ -3353,6 +3419,7 @@ class RestApiGenerator {
3353
3419
  const parsed = parseSubPath(rawPath);
3354
3420
  if (!parsed || !parsed.entityId) return next();
3355
3421
  const driver = c.get("driver") || this.driver;
3422
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3356
3423
  const existingEntity = await driver.fetchEntity({
3357
3424
  path: parsed.collectionPath,
3358
3425
  entityId: parsed.entityId
@@ -3418,7 +3485,8 @@ class RestApiGenerator {
3418
3485
  orderBy: queryOptions.orderBy?.[0]?.field,
3419
3486
  order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
3420
3487
  startAfter: queryOptions.offset ? String(queryOptions.offset) : void 0,
3421
- searchString
3488
+ searchString,
3489
+ vectorSearch: queryOptions.vectorSearch
3422
3490
  });
3423
3491
  return entities.map((entity) => this.flattenEntity(entity));
3424
3492
  }
@@ -4944,7 +5012,7 @@ var cmp_1 = cmp$1;
4944
5012
  const SemVer$6 = semver$4;
4945
5013
  const parse$6 = parse_1;
4946
5014
  const { safeRe: re, t } = reExports;
4947
- const coerce$1 = (version2, options2) => {
5015
+ const coerce$2 = (version2, options2) => {
4948
5016
  if (version2 instanceof SemVer$6) {
4949
5017
  return version2;
4950
5018
  }
@@ -4979,7 +5047,7 @@ const coerce$1 = (version2, options2) => {
4979
5047
  const build = options2.includePrerelease && match[6] ? `+${match[6]}` : "";
4980
5048
  return parse$6(`${major2}.${minor2}.${patch2}${prerelease2}${build}`, options2);
4981
5049
  };
4982
- var coerce_1 = coerce$1;
5050
+ var coerce_1 = coerce$2;
4983
5051
  const parse$5 = parse_1;
4984
5052
  const constants$1 = constants$2;
4985
5053
  const SemVer$5 = semver$4;
@@ -5265,19 +5333,19 @@ function requireRange() {
5265
5333
  const replaceCaret = (comp, options2) => {
5266
5334
  debug2("caret", comp, options2);
5267
5335
  const r = options2.loose ? re2[t2.CARETLOOSE] : re2[t2.CARET];
5268
- const z = options2.includePrerelease ? "-0" : "";
5336
+ const z2 = options2.includePrerelease ? "-0" : "";
5269
5337
  return comp.replace(r, (_, M, m2, p, pr) => {
5270
5338
  debug2("caret", comp, _, M, m2, p, pr);
5271
5339
  let ret;
5272
5340
  if (isX(M)) {
5273
5341
  ret = "";
5274
5342
  } else if (isX(m2)) {
5275
- ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`;
5343
+ ret = `>=${M}.0.0${z2} <${+M + 1}.0.0-0`;
5276
5344
  } else if (isX(p)) {
5277
5345
  if (M === "0") {
5278
- ret = `>=${M}.${m2}.0${z} <${M}.${+m2 + 1}.0-0`;
5346
+ ret = `>=${M}.${m2}.0${z2} <${M}.${+m2 + 1}.0-0`;
5279
5347
  } else {
5280
- ret = `>=${M}.${m2}.0${z} <${+M + 1}.0.0-0`;
5348
+ ret = `>=${M}.${m2}.0${z2} <${+M + 1}.0.0-0`;
5281
5349
  }
5282
5350
  } else if (pr) {
5283
5351
  debug2("replaceCaret pr", pr);
@@ -5294,9 +5362,9 @@ function requireRange() {
5294
5362
  debug2("no pr");
5295
5363
  if (M === "0") {
5296
5364
  if (m2 === "0") {
5297
- ret = `>=${M}.${m2}.${p}${z} <${M}.${m2}.${+p + 1}-0`;
5365
+ ret = `>=${M}.${m2}.${p}${z2} <${M}.${m2}.${+p + 1}-0`;
5298
5366
  } else {
5299
- ret = `>=${M}.${m2}.${p}${z} <${M}.${+m2 + 1}.0-0`;
5367
+ ret = `>=${M}.${m2}.${p}${z2} <${M}.${+m2 + 1}.0-0`;
5300
5368
  }
5301
5369
  } else {
5302
5370
  ret = `>=${M}.${m2}.${p} <${+M + 1}.0.0-0`;
@@ -5953,7 +6021,7 @@ const neq = neq_1;
5953
6021
  const gte = gte_1;
5954
6022
  const lte = lte_1;
5955
6023
  const cmp = cmp_1;
5956
- const coerce = coerce_1;
6024
+ const coerce$1 = coerce_1;
5957
6025
  const truncate = truncate_1;
5958
6026
  const Comparator = requireComparator();
5959
6027
  const Range = requireRange();
@@ -5992,7 +6060,7 @@ var semver$3 = {
5992
6060
  gte,
5993
6061
  lte,
5994
6062
  cmp,
5995
- coerce,
6063
+ coerce: coerce$1,
5996
6064
  truncate,
5997
6065
  Comparator,
5998
6066
  Range,
@@ -6873,7 +6941,7 @@ var jsonwebtoken = {
6873
6941
  NotBeforeError: NotBeforeError_1,
6874
6942
  TokenExpiredError: TokenExpiredError_1
6875
6943
  };
6876
- const jwt = /* @__PURE__ */ getDefaultExportFromCjs(jsonwebtoken);
6944
+ const jwt$1 = /* @__PURE__ */ getDefaultExportFromCjs(jsonwebtoken);
6877
6945
  let jwtConfig = {
6878
6946
  secret: "",
6879
6947
  accessExpiresIn: "1h",
@@ -6892,15 +6960,17 @@ function configureJwt(config) {
6892
6960
  ...config
6893
6961
  };
6894
6962
  }
6895
- function generateAccessToken(userId, roles) {
6963
+ function generateAccessToken(userId, roles, aal = "aal1", customClaims) {
6896
6964
  if (!jwtConfig.secret) {
6897
6965
  throw new Error("JWT secret not configured. Call configureJwt() first.");
6898
6966
  }
6899
6967
  const payload = {
6900
6968
  userId,
6901
- roles
6969
+ roles,
6970
+ aal,
6971
+ ...customClaims
6902
6972
  };
6903
- return jwt.sign(payload, jwtConfig.secret, {
6973
+ return jwt$1.sign(payload, jwtConfig.secret, {
6904
6974
  expiresIn: jwtConfig.accessExpiresIn,
6905
6975
  algorithm: "HS256"
6906
6976
  });
@@ -6934,7 +7004,7 @@ function verifyAccessToken(token) {
6934
7004
  throw new Error("JWT secret not configured. Call configureJwt() first.");
6935
7005
  }
6936
7006
  try {
6937
- const decoded = jwt.verify(token, jwtConfig.secret, {
7007
+ const decoded = jwt$1.verify(token, jwtConfig.secret, {
6938
7008
  algorithms: ["HS256"]
6939
7009
  });
6940
7010
  const id = decoded.userId || decoded.uid || decoded.sub;
@@ -6942,9 +7012,11 @@ function verifyAccessToken(token) {
6942
7012
  console.error("[JWT] Verification failed: missing id in payload", decoded);
6943
7013
  return null;
6944
7014
  }
7015
+ const aal = decoded.aal === "aal1" || decoded.aal === "aal2" ? decoded.aal : "aal1";
6945
7016
  return {
6946
7017
  userId: id,
6947
- roles: decoded.roles || []
7018
+ roles: decoded.roles || [],
7019
+ aal
6948
7020
  };
6949
7021
  } catch (error2) {
6950
7022
  console.error("[JWT] Verification failed:", error2, "Token start:", token.substring(0, 15));
@@ -6984,6 +7056,17 @@ function getRefreshTokenExpiry() {
6984
7056
  }
6985
7057
  return new Date(Date.now() + ms2);
6986
7058
  }
7059
+ const jwt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
7060
+ __proto__: null,
7061
+ configureJwt,
7062
+ generateAccessToken,
7063
+ generateRefreshToken,
7064
+ getAccessTokenExpiry,
7065
+ getAccessTokenExpiryMs,
7066
+ getRefreshTokenExpiry,
7067
+ hashRefreshToken,
7068
+ verifyAccessToken
7069
+ }, Symbol.toStringTag, { value: "Module" }));
6987
7070
  function isRLSScopedDriver(driver) {
6988
7071
  return "withAuth" in driver && typeof driver.withAuth === "function";
6989
7072
  }
@@ -7006,6 +7089,81 @@ function safeCompare(a2, b) {
7006
7089
  return false;
7007
7090
  }
7008
7091
  }
7092
+ function isApiKeyToken(token) {
7093
+ return token.startsWith("rk_");
7094
+ }
7095
+ function hashToken$2(token) {
7096
+ return createHash("sha256").update(token).digest("hex");
7097
+ }
7098
+ async function validateApiKey(c, token, options2) {
7099
+ const {
7100
+ store,
7101
+ driver
7102
+ } = options2;
7103
+ const hash = hashToken$2(token);
7104
+ const apiKey = await store.findByKeyHash(hash);
7105
+ if (!apiKey) {
7106
+ return c.json({
7107
+ error: {
7108
+ message: "Invalid API key",
7109
+ code: "UNAUTHORIZED"
7110
+ }
7111
+ }, 401);
7112
+ }
7113
+ if (apiKey.revoked_at) {
7114
+ return c.json({
7115
+ error: {
7116
+ message: "API key has been revoked",
7117
+ code: "UNAUTHORIZED"
7118
+ }
7119
+ }, 401);
7120
+ }
7121
+ if (apiKey.expires_at && new Date(apiKey.expires_at) < /* @__PURE__ */ new Date()) {
7122
+ return c.json({
7123
+ error: {
7124
+ message: "API key has expired",
7125
+ code: "UNAUTHORIZED"
7126
+ }
7127
+ }, 401);
7128
+ }
7129
+ const userId = `api-key:${apiKey.id}`;
7130
+ c.set("user", {
7131
+ userId,
7132
+ roles: []
7133
+ });
7134
+ const masked = {
7135
+ id: apiKey.id,
7136
+ name: apiKey.name,
7137
+ key_prefix: apiKey.key_prefix,
7138
+ permissions: apiKey.permissions,
7139
+ rate_limit: apiKey.rate_limit,
7140
+ created_by: apiKey.created_by,
7141
+ created_at: apiKey.created_at,
7142
+ updated_at: apiKey.updated_at,
7143
+ last_used_at: apiKey.last_used_at,
7144
+ expires_at: apiKey.expires_at,
7145
+ revoked_at: apiKey.revoked_at
7146
+ };
7147
+ c.set("apiKey", masked);
7148
+ try {
7149
+ const scopedDriver = await scopeDataDriver(driver, {
7150
+ uid: userId,
7151
+ roles: ["service"]
7152
+ });
7153
+ c.set("driver", scopedDriver);
7154
+ } catch (error2) {
7155
+ console.error("[AUTH] RLS scoping failed for API key:", error2);
7156
+ return c.json({
7157
+ error: {
7158
+ message: "Internal authentication error",
7159
+ code: "INTERNAL_ERROR"
7160
+ }
7161
+ }, 500);
7162
+ }
7163
+ store.updateLastUsed(apiKey.id).catch(() => {
7164
+ });
7165
+ return true;
7166
+ }
7009
7167
  const requireAuth = async (c, next) => {
7010
7168
  const authHeader = c.req.header("authorization");
7011
7169
  const queryToken = c.req.query("token");
@@ -7112,7 +7270,8 @@ function createAuthMiddleware(options2) {
7112
7270
  driver,
7113
7271
  requireAuth: enforceAuth = true,
7114
7272
  validator,
7115
- serviceKey
7273
+ serviceKey,
7274
+ apiKeyStore
7116
7275
  } = options2;
7117
7276
  return async (c, next) => {
7118
7277
  if (validator) {
@@ -7187,6 +7346,14 @@ function createAuthMiddleware(options2) {
7187
7346
  }
7188
7347
  }, 500);
7189
7348
  }
7349
+ } else if (apiKeyStore && isApiKeyToken(token)) {
7350
+ const result = await validateApiKey(c, token, {
7351
+ store: apiKeyStore,
7352
+ driver
7353
+ });
7354
+ if (result !== true) {
7355
+ return result;
7356
+ }
7190
7357
  } else {
7191
7358
  const payload = extractUserFromToken(token);
7192
7359
  if (payload) {
@@ -7247,9 +7414,22 @@ function createAdapterAuthMiddleware(options2) {
7247
7414
  const {
7248
7415
  adapter,
7249
7416
  driver,
7250
- requireAuth: enforceAuth = true
7417
+ requireAuth: enforceAuth = true,
7418
+ apiKeyStore
7251
7419
  } = options2;
7252
7420
  return async (c, next) => {
7421
+ if (apiKeyStore) {
7422
+ const authHeader = c.req.header("authorization") || "";
7423
+ const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
7424
+ if (token.startsWith("rk_")) {
7425
+ const result = await validateApiKey(c, token, {
7426
+ store: apiKeyStore,
7427
+ driver
7428
+ });
7429
+ if (result === true) return next();
7430
+ return result;
7431
+ }
7432
+ }
7253
7433
  let authenticatedUser = null;
7254
7434
  try {
7255
7435
  authenticatedUser = await adapter.verifyRequest(c.req.raw);
@@ -7345,11 +7525,11 @@ async function verifyPassword(password, storedHash) {
7345
7525
  const derivedKey = await scryptAsync(password, salt, KEY_LENGTH);
7346
7526
  return timingSafeEqual$1(derivedKey, storedKey);
7347
7527
  }
7348
- function resolveAuthOverrides(overrides) {
7528
+ function resolveAuthHooks(hooks) {
7349
7529
  return {
7350
- hashPassword: overrides?.hashPassword ?? hashPassword,
7351
- verifyPassword: overrides?.verifyPassword ?? verifyPassword,
7352
- validatePasswordStrength: overrides?.validatePasswordStrength ?? validatePasswordStrength
7530
+ hashPassword: hooks?.hashPassword ?? hashPassword,
7531
+ verifyPassword: hooks?.verifyPassword ?? verifyPassword,
7532
+ validatePasswordStrength: hooks?.validatePasswordStrength ?? validatePasswordStrength
7353
7533
  };
7354
7534
  }
7355
7535
  function getGreeting(user) {
@@ -7751,6 +7931,63 @@ const strictAuthLimiter = createRateLimiter({
7751
7931
  limit: 50,
7752
7932
  message: "Too many requests to this sensitive endpoint, please try again later."
7753
7933
  });
7934
+ function apiKeyKeyGenerator(c) {
7935
+ const apiKey = c.get("apiKey");
7936
+ if (apiKey) {
7937
+ return `api-key:${apiKey.id}`;
7938
+ }
7939
+ return defaultKeyGenerator(c);
7940
+ }
7941
+ function createApiKeyRateLimiter(defaultLimit = 1e3, windowMs = 15 * 60 * 1e3) {
7942
+ const store = /* @__PURE__ */ new Map();
7943
+ const cleanupInterval = setInterval(() => {
7944
+ const now = Date.now();
7945
+ for (const [key, entry] of store.entries()) {
7946
+ entry.timestamps = entry.timestamps.filter((t2) => now - t2 < windowMs);
7947
+ if (entry.timestamps.length === 0) {
7948
+ store.delete(key);
7949
+ }
7950
+ }
7951
+ }, windowMs);
7952
+ if (cleanupInterval.unref) {
7953
+ cleanupInterval.unref();
7954
+ }
7955
+ return async (c, next) => {
7956
+ const apiKey = c.get("apiKey");
7957
+ if (!apiKey) {
7958
+ return next();
7959
+ }
7960
+ const limit = apiKey.rate_limit ?? defaultLimit;
7961
+ const key = `api-key:${apiKey.id}`;
7962
+ const now = Date.now();
7963
+ let entry = store.get(key);
7964
+ if (!entry) {
7965
+ entry = {
7966
+ timestamps: []
7967
+ };
7968
+ store.set(key, entry);
7969
+ }
7970
+ entry.timestamps = entry.timestamps.filter((t2) => now - t2 < windowMs);
7971
+ if (entry.timestamps.length >= limit) {
7972
+ const retryAfterMs = entry.timestamps[0] + windowMs - now;
7973
+ const retryAfterSec = Math.ceil(retryAfterMs / 1e3);
7974
+ c.header("Retry-After", String(retryAfterSec));
7975
+ c.header("X-RateLimit-Limit", String(limit));
7976
+ c.header("X-RateLimit-Remaining", "0");
7977
+ c.header("X-RateLimit-Reset", String(Math.ceil((now + retryAfterMs) / 1e3)));
7978
+ return c.json({
7979
+ error: {
7980
+ message: "API key rate limit exceeded, please try again later.",
7981
+ code: "RATE_LIMITED"
7982
+ }
7983
+ }, 429);
7984
+ }
7985
+ entry.timestamps.push(now);
7986
+ c.header("X-RateLimit-Limit", String(limit));
7987
+ c.header("X-RateLimit-Remaining", String(limit - entry.timestamps.length));
7988
+ return next();
7989
+ };
7990
+ }
7754
7991
  var util$3;
7755
7992
  (function(util2) {
7756
7993
  util2.assertEqual = (_) => {
@@ -7901,6 +8138,10 @@ const ZodIssueCode = util$3.arrayToEnum([
7901
8138
  "not_multiple_of",
7902
8139
  "not_finite"
7903
8140
  ]);
8141
+ const quotelessJson = (obj) => {
8142
+ const json = JSON.stringify(obj, null, 2);
8143
+ return json.replace(/"([^"]+)":/g, "$1:");
8144
+ };
7904
8145
  class ZodError extends Error {
7905
8146
  get errors() {
7906
8147
  return this.issues;
@@ -8096,6 +8337,9 @@ const errorMap = (issue, _ctx) => {
8096
8337
  return { message };
8097
8338
  };
8098
8339
  let overrideErrorMap = errorMap;
8340
+ function setErrorMap(map2) {
8341
+ overrideErrorMap = map2;
8342
+ }
8099
8343
  function getErrorMap() {
8100
8344
  return overrideErrorMap;
8101
8345
  }
@@ -8124,6 +8368,7 @@ const makeIssue = (params) => {
8124
8368
  message: errorMessage
8125
8369
  };
8126
8370
  };
8371
+ const EMPTY_PATH = [];
8127
8372
  function addIssueToContext(ctx, issueData) {
8128
8373
  const overrideMap = getErrorMap();
8129
8374
  const issue = makeIssue({
@@ -10414,6 +10659,113 @@ ZodUnion.create = (types2, params) => {
10414
10659
  ...processCreateParams(params)
10415
10660
  });
10416
10661
  };
10662
+ const getDiscriminator = (type) => {
10663
+ if (type instanceof ZodLazy) {
10664
+ return getDiscriminator(type.schema);
10665
+ } else if (type instanceof ZodEffects) {
10666
+ return getDiscriminator(type.innerType());
10667
+ } else if (type instanceof ZodLiteral) {
10668
+ return [type.value];
10669
+ } else if (type instanceof ZodEnum) {
10670
+ return type.options;
10671
+ } else if (type instanceof ZodNativeEnum) {
10672
+ return util$3.objectValues(type.enum);
10673
+ } else if (type instanceof ZodDefault) {
10674
+ return getDiscriminator(type._def.innerType);
10675
+ } else if (type instanceof ZodUndefined) {
10676
+ return [void 0];
10677
+ } else if (type instanceof ZodNull) {
10678
+ return [null];
10679
+ } else if (type instanceof ZodOptional) {
10680
+ return [void 0, ...getDiscriminator(type.unwrap())];
10681
+ } else if (type instanceof ZodNullable) {
10682
+ return [null, ...getDiscriminator(type.unwrap())];
10683
+ } else if (type instanceof ZodBranded) {
10684
+ return getDiscriminator(type.unwrap());
10685
+ } else if (type instanceof ZodReadonly) {
10686
+ return getDiscriminator(type.unwrap());
10687
+ } else if (type instanceof ZodCatch) {
10688
+ return getDiscriminator(type._def.innerType);
10689
+ } else {
10690
+ return [];
10691
+ }
10692
+ };
10693
+ class ZodDiscriminatedUnion extends ZodType {
10694
+ _parse(input) {
10695
+ const { ctx } = this._processInputParams(input);
10696
+ if (ctx.parsedType !== ZodParsedType.object) {
10697
+ addIssueToContext(ctx, {
10698
+ code: ZodIssueCode.invalid_type,
10699
+ expected: ZodParsedType.object,
10700
+ received: ctx.parsedType
10701
+ });
10702
+ return INVALID;
10703
+ }
10704
+ const discriminator = this.discriminator;
10705
+ const discriminatorValue = ctx.data[discriminator];
10706
+ const option = this.optionsMap.get(discriminatorValue);
10707
+ if (!option) {
10708
+ addIssueToContext(ctx, {
10709
+ code: ZodIssueCode.invalid_union_discriminator,
10710
+ options: Array.from(this.optionsMap.keys()),
10711
+ path: [discriminator]
10712
+ });
10713
+ return INVALID;
10714
+ }
10715
+ if (ctx.common.async) {
10716
+ return option._parseAsync({
10717
+ data: ctx.data,
10718
+ path: ctx.path,
10719
+ parent: ctx
10720
+ });
10721
+ } else {
10722
+ return option._parseSync({
10723
+ data: ctx.data,
10724
+ path: ctx.path,
10725
+ parent: ctx
10726
+ });
10727
+ }
10728
+ }
10729
+ get discriminator() {
10730
+ return this._def.discriminator;
10731
+ }
10732
+ get options() {
10733
+ return this._def.options;
10734
+ }
10735
+ get optionsMap() {
10736
+ return this._def.optionsMap;
10737
+ }
10738
+ /**
10739
+ * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor.
10740
+ * However, it only allows a union of objects, all of which need to share a discriminator property. This property must
10741
+ * have a different value for each object in the union.
10742
+ * @param discriminator the name of the discriminator property
10743
+ * @param types an array of object schemas
10744
+ * @param params
10745
+ */
10746
+ static create(discriminator, options2, params) {
10747
+ const optionsMap = /* @__PURE__ */ new Map();
10748
+ for (const type of options2) {
10749
+ const discriminatorValues = getDiscriminator(type.shape[discriminator]);
10750
+ if (!discriminatorValues.length) {
10751
+ throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`);
10752
+ }
10753
+ for (const value of discriminatorValues) {
10754
+ if (optionsMap.has(value)) {
10755
+ throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`);
10756
+ }
10757
+ optionsMap.set(value, type);
10758
+ }
10759
+ }
10760
+ return new ZodDiscriminatedUnion({
10761
+ typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion,
10762
+ discriminator,
10763
+ options: options2,
10764
+ optionsMap,
10765
+ ...processCreateParams(params)
10766
+ });
10767
+ }
10768
+ }
10417
10769
  function mergeValues(a2, b) {
10418
10770
  const aType = getParsedType(a2);
10419
10771
  const bType = getParsedType(b);
@@ -10572,6 +10924,59 @@ ZodTuple.create = (schemas, params) => {
10572
10924
  ...processCreateParams(params)
10573
10925
  });
10574
10926
  };
10927
+ class ZodRecord extends ZodType {
10928
+ get keySchema() {
10929
+ return this._def.keyType;
10930
+ }
10931
+ get valueSchema() {
10932
+ return this._def.valueType;
10933
+ }
10934
+ _parse(input) {
10935
+ const { status, ctx } = this._processInputParams(input);
10936
+ if (ctx.parsedType !== ZodParsedType.object) {
10937
+ addIssueToContext(ctx, {
10938
+ code: ZodIssueCode.invalid_type,
10939
+ expected: ZodParsedType.object,
10940
+ received: ctx.parsedType
10941
+ });
10942
+ return INVALID;
10943
+ }
10944
+ const pairs = [];
10945
+ const keyType = this._def.keyType;
10946
+ const valueType = this._def.valueType;
10947
+ for (const key in ctx.data) {
10948
+ pairs.push({
10949
+ key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)),
10950
+ value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)),
10951
+ alwaysSet: key in ctx.data
10952
+ });
10953
+ }
10954
+ if (ctx.common.async) {
10955
+ return ParseStatus.mergeObjectAsync(status, pairs);
10956
+ } else {
10957
+ return ParseStatus.mergeObjectSync(status, pairs);
10958
+ }
10959
+ }
10960
+ get element() {
10961
+ return this._def.valueType;
10962
+ }
10963
+ static create(first, second, third) {
10964
+ if (second instanceof ZodType) {
10965
+ return new ZodRecord({
10966
+ keyType: first,
10967
+ valueType: second,
10968
+ typeName: ZodFirstPartyTypeKind.ZodRecord,
10969
+ ...processCreateParams(third)
10970
+ });
10971
+ }
10972
+ return new ZodRecord({
10973
+ keyType: ZodString.create(),
10974
+ valueType: first,
10975
+ typeName: ZodFirstPartyTypeKind.ZodRecord,
10976
+ ...processCreateParams(second)
10977
+ });
10978
+ }
10979
+ }
10575
10980
  class ZodMap extends ZodType {
10576
10981
  get keySchema() {
10577
10982
  return this._def.keyType;
@@ -10723,6 +11128,111 @@ ZodSet.create = (valueType, params) => {
10723
11128
  ...processCreateParams(params)
10724
11129
  });
10725
11130
  };
11131
+ class ZodFunction extends ZodType {
11132
+ constructor() {
11133
+ super(...arguments);
11134
+ this.validate = this.implement;
11135
+ }
11136
+ _parse(input) {
11137
+ const { ctx } = this._processInputParams(input);
11138
+ if (ctx.parsedType !== ZodParsedType.function) {
11139
+ addIssueToContext(ctx, {
11140
+ code: ZodIssueCode.invalid_type,
11141
+ expected: ZodParsedType.function,
11142
+ received: ctx.parsedType
11143
+ });
11144
+ return INVALID;
11145
+ }
11146
+ function makeArgsIssue(args, error2) {
11147
+ return makeIssue({
11148
+ data: args,
11149
+ path: ctx.path,
11150
+ errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), errorMap].filter((x) => !!x),
11151
+ issueData: {
11152
+ code: ZodIssueCode.invalid_arguments,
11153
+ argumentsError: error2
11154
+ }
11155
+ });
11156
+ }
11157
+ function makeReturnsIssue(returns, error2) {
11158
+ return makeIssue({
11159
+ data: returns,
11160
+ path: ctx.path,
11161
+ errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), errorMap].filter((x) => !!x),
11162
+ issueData: {
11163
+ code: ZodIssueCode.invalid_return_type,
11164
+ returnTypeError: error2
11165
+ }
11166
+ });
11167
+ }
11168
+ const params = { errorMap: ctx.common.contextualErrorMap };
11169
+ const fn = ctx.data;
11170
+ if (this._def.returns instanceof ZodPromise) {
11171
+ const me = this;
11172
+ return OK(async function(...args) {
11173
+ const error2 = new ZodError([]);
11174
+ const parsedArgs = await me._def.args.parseAsync(args, params).catch((e) => {
11175
+ error2.addIssue(makeArgsIssue(args, e));
11176
+ throw error2;
11177
+ });
11178
+ const result = await Reflect.apply(fn, this, parsedArgs);
11179
+ const parsedReturns = await me._def.returns._def.type.parseAsync(result, params).catch((e) => {
11180
+ error2.addIssue(makeReturnsIssue(result, e));
11181
+ throw error2;
11182
+ });
11183
+ return parsedReturns;
11184
+ });
11185
+ } else {
11186
+ const me = this;
11187
+ return OK(function(...args) {
11188
+ const parsedArgs = me._def.args.safeParse(args, params);
11189
+ if (!parsedArgs.success) {
11190
+ throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
11191
+ }
11192
+ const result = Reflect.apply(fn, this, parsedArgs.data);
11193
+ const parsedReturns = me._def.returns.safeParse(result, params);
11194
+ if (!parsedReturns.success) {
11195
+ throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
11196
+ }
11197
+ return parsedReturns.data;
11198
+ });
11199
+ }
11200
+ }
11201
+ parameters() {
11202
+ return this._def.args;
11203
+ }
11204
+ returnType() {
11205
+ return this._def.returns;
11206
+ }
11207
+ args(...items) {
11208
+ return new ZodFunction({
11209
+ ...this._def,
11210
+ args: ZodTuple.create(items).rest(ZodUnknown.create())
11211
+ });
11212
+ }
11213
+ returns(returnType) {
11214
+ return new ZodFunction({
11215
+ ...this._def,
11216
+ returns: returnType
11217
+ });
11218
+ }
11219
+ implement(func) {
11220
+ const validatedFunc = this.parse(func);
11221
+ return validatedFunc;
11222
+ }
11223
+ strictImplement(func) {
11224
+ const validatedFunc = this.parse(func);
11225
+ return validatedFunc;
11226
+ }
11227
+ static create(args, returns, params) {
11228
+ return new ZodFunction({
11229
+ args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()),
11230
+ returns: returns || ZodUnknown.create(),
11231
+ typeName: ZodFirstPartyTypeKind.ZodFunction,
11232
+ ...processCreateParams(params)
11233
+ });
11234
+ }
11235
+ }
10726
11236
  class ZodLazy extends ZodType {
10727
11237
  get schema() {
10728
11238
  return this._def.getter();
@@ -11180,6 +11690,7 @@ ZodNaN.create = (params) => {
11180
11690
  ...processCreateParams(params)
11181
11691
  });
11182
11692
  };
11693
+ const BRAND = Symbol("zod_brand");
11183
11694
  class ZodBranded extends ZodType {
11184
11695
  _parse(input) {
11185
11696
  const { ctx } = this._processInputParams(input);
@@ -11271,6 +11782,36 @@ ZodReadonly.create = (type, params) => {
11271
11782
  ...processCreateParams(params)
11272
11783
  });
11273
11784
  };
11785
+ function cleanParams(params, data) {
11786
+ const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
11787
+ const p2 = typeof p === "string" ? { message: p } : p;
11788
+ return p2;
11789
+ }
11790
+ function custom(check, _params = {}, fatal) {
11791
+ if (check)
11792
+ return ZodAny.create().superRefine((data, ctx) => {
11793
+ const r = check(data);
11794
+ if (r instanceof Promise) {
11795
+ return r.then((r2) => {
11796
+ if (!r2) {
11797
+ const params = cleanParams(_params, data);
11798
+ const _fatal = params.fatal ?? fatal ?? true;
11799
+ ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
11800
+ }
11801
+ });
11802
+ }
11803
+ if (!r) {
11804
+ const params = cleanParams(_params, data);
11805
+ const _fatal = params.fatal ?? fatal ?? true;
11806
+ ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
11807
+ }
11808
+ return;
11809
+ });
11810
+ return ZodAny.create();
11811
+ }
11812
+ const late = {
11813
+ object: ZodObject.lazycreate
11814
+ };
11274
11815
  var ZodFirstPartyTypeKind;
11275
11816
  (function(ZodFirstPartyTypeKind2) {
11276
11817
  ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
@@ -11310,17 +11851,251 @@ var ZodFirstPartyTypeKind;
11310
11851
  ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
11311
11852
  ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
11312
11853
  })(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
11854
+ const instanceOfType = (cls, params = {
11855
+ message: `Input not instance of ${cls.name}`
11856
+ }) => custom((data) => data instanceof cls, params);
11313
11857
  const stringType = ZodString.create;
11314
- ZodNever.create;
11315
- ZodArray.create;
11858
+ const numberType = ZodNumber.create;
11859
+ const nanType = ZodNaN.create;
11860
+ const bigIntType = ZodBigInt.create;
11861
+ const booleanType = ZodBoolean.create;
11862
+ const dateType = ZodDate.create;
11863
+ const symbolType = ZodSymbol.create;
11864
+ const undefinedType = ZodUndefined.create;
11865
+ const nullType = ZodNull.create;
11866
+ const anyType = ZodAny.create;
11867
+ const unknownType = ZodUnknown.create;
11868
+ const neverType = ZodNever.create;
11869
+ const voidType = ZodVoid.create;
11870
+ const arrayType = ZodArray.create;
11316
11871
  const objectType = ZodObject.create;
11317
- ZodUnion.create;
11318
- ZodIntersection.create;
11319
- ZodTuple.create;
11872
+ const strictObjectType = ZodObject.strictCreate;
11873
+ const unionType = ZodUnion.create;
11874
+ const discriminatedUnionType = ZodDiscriminatedUnion.create;
11875
+ const intersectionType = ZodIntersection.create;
11876
+ const tupleType = ZodTuple.create;
11877
+ const recordType = ZodRecord.create;
11878
+ const mapType = ZodMap.create;
11879
+ const setType = ZodSet.create;
11880
+ const functionType = ZodFunction.create;
11881
+ const lazyType = ZodLazy.create;
11882
+ const literalType = ZodLiteral.create;
11320
11883
  const enumType = ZodEnum.create;
11321
- ZodPromise.create;
11322
- ZodOptional.create;
11323
- ZodNullable.create;
11884
+ const nativeEnumType = ZodNativeEnum.create;
11885
+ const promiseType = ZodPromise.create;
11886
+ const effectsType = ZodEffects.create;
11887
+ const optionalType = ZodOptional.create;
11888
+ const nullableType = ZodNullable.create;
11889
+ const preprocessType = ZodEffects.createWithPreprocess;
11890
+ const pipelineType = ZodPipeline.create;
11891
+ const ostring = () => stringType().optional();
11892
+ const onumber = () => numberType().optional();
11893
+ const oboolean = () => booleanType().optional();
11894
+ const coerce = {
11895
+ string: (arg) => ZodString.create({ ...arg, coerce: true }),
11896
+ number: (arg) => ZodNumber.create({ ...arg, coerce: true }),
11897
+ boolean: (arg) => ZodBoolean.create({
11898
+ ...arg,
11899
+ coerce: true
11900
+ }),
11901
+ bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }),
11902
+ date: (arg) => ZodDate.create({ ...arg, coerce: true })
11903
+ };
11904
+ const NEVER = INVALID;
11905
+ const z = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
11906
+ __proto__: null,
11907
+ BRAND,
11908
+ DIRTY,
11909
+ EMPTY_PATH,
11910
+ INVALID,
11911
+ NEVER,
11912
+ OK,
11913
+ ParseStatus,
11914
+ Schema: ZodType,
11915
+ ZodAny,
11916
+ ZodArray,
11917
+ ZodBigInt,
11918
+ ZodBoolean,
11919
+ ZodBranded,
11920
+ ZodCatch,
11921
+ ZodDate,
11922
+ ZodDefault,
11923
+ ZodDiscriminatedUnion,
11924
+ ZodEffects,
11925
+ ZodEnum,
11926
+ ZodError,
11927
+ get ZodFirstPartyTypeKind() {
11928
+ return ZodFirstPartyTypeKind;
11929
+ },
11930
+ ZodFunction,
11931
+ ZodIntersection,
11932
+ ZodIssueCode,
11933
+ ZodLazy,
11934
+ ZodLiteral,
11935
+ ZodMap,
11936
+ ZodNaN,
11937
+ ZodNativeEnum,
11938
+ ZodNever,
11939
+ ZodNull,
11940
+ ZodNullable,
11941
+ ZodNumber,
11942
+ ZodObject,
11943
+ ZodOptional,
11944
+ ZodParsedType,
11945
+ ZodPipeline,
11946
+ ZodPromise,
11947
+ ZodReadonly,
11948
+ ZodRecord,
11949
+ ZodSchema: ZodType,
11950
+ ZodSet,
11951
+ ZodString,
11952
+ ZodSymbol,
11953
+ ZodTransformer: ZodEffects,
11954
+ ZodTuple,
11955
+ ZodType,
11956
+ ZodUndefined,
11957
+ ZodUnion,
11958
+ ZodUnknown,
11959
+ ZodVoid,
11960
+ addIssueToContext,
11961
+ any: anyType,
11962
+ array: arrayType,
11963
+ bigint: bigIntType,
11964
+ boolean: booleanType,
11965
+ coerce,
11966
+ custom,
11967
+ date: dateType,
11968
+ datetimeRegex,
11969
+ defaultErrorMap: errorMap,
11970
+ discriminatedUnion: discriminatedUnionType,
11971
+ effect: effectsType,
11972
+ enum: enumType,
11973
+ function: functionType,
11974
+ getErrorMap,
11975
+ getParsedType,
11976
+ instanceof: instanceOfType,
11977
+ intersection: intersectionType,
11978
+ isAborted,
11979
+ isAsync,
11980
+ isDirty,
11981
+ isValid,
11982
+ late,
11983
+ lazy: lazyType,
11984
+ literal: literalType,
11985
+ makeIssue,
11986
+ map: mapType,
11987
+ nan: nanType,
11988
+ nativeEnum: nativeEnumType,
11989
+ never: neverType,
11990
+ null: nullType,
11991
+ nullable: nullableType,
11992
+ number: numberType,
11993
+ object: objectType,
11994
+ get objectUtil() {
11995
+ return objectUtil;
11996
+ },
11997
+ oboolean,
11998
+ onumber,
11999
+ optional: optionalType,
12000
+ ostring,
12001
+ pipeline: pipelineType,
12002
+ preprocess: preprocessType,
12003
+ promise: promiseType,
12004
+ quotelessJson,
12005
+ record: recordType,
12006
+ set: setType,
12007
+ setErrorMap,
12008
+ strictObject: strictObjectType,
12009
+ string: stringType,
12010
+ symbol: symbolType,
12011
+ transformer: effectsType,
12012
+ tuple: tupleType,
12013
+ undefined: undefinedType,
12014
+ union: unionType,
12015
+ unknown: unknownType,
12016
+ get util() {
12017
+ return util$3;
12018
+ },
12019
+ void: voidType
12020
+ }, Symbol.toStringTag, { value: "Module" }));
12021
+ const BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
12022
+ function base32Encode(buffer) {
12023
+ let bits = 0;
12024
+ let value = 0;
12025
+ let output = "";
12026
+ for (let i = 0; i < buffer.length; i++) {
12027
+ value = value << 8 | buffer[i];
12028
+ bits += 8;
12029
+ while (bits >= 5) {
12030
+ output += BASE32_ALPHABET[value >>> bits - 5 & 31];
12031
+ bits -= 5;
12032
+ }
12033
+ }
12034
+ if (bits > 0) {
12035
+ output += BASE32_ALPHABET[value << 5 - bits & 31];
12036
+ }
12037
+ return output;
12038
+ }
12039
+ function base32Decode(encoded) {
12040
+ const cleanInput = encoded.replace(/=+$/, "").toUpperCase();
12041
+ const bytes = [];
12042
+ let bits = 0;
12043
+ let value = 0;
12044
+ for (let i = 0; i < cleanInput.length; i++) {
12045
+ const index = BASE32_ALPHABET.indexOf(cleanInput[i]);
12046
+ if (index === -1) continue;
12047
+ value = value << 5 | index;
12048
+ bits += 5;
12049
+ if (bits >= 8) {
12050
+ bytes.push(value >>> bits - 8 & 255);
12051
+ bits -= 8;
12052
+ }
12053
+ }
12054
+ return Buffer.from(bytes);
12055
+ }
12056
+ function generateHotp(secret, counter) {
12057
+ const hmac = createHmac("sha1", secret);
12058
+ const counterBuffer = Buffer.alloc(8);
12059
+ counterBuffer.writeBigInt64BE(counter);
12060
+ hmac.update(counterBuffer);
12061
+ const hash = hmac.digest();
12062
+ const offset = hash[hash.length - 1] & 15;
12063
+ const code2 = ((hash[offset] & 127) << 24 | hash[offset + 1] << 16 | hash[offset + 2] << 8 | hash[offset + 3]) % 1e6;
12064
+ return code2.toString().padStart(6, "0");
12065
+ }
12066
+ function verifyTotp(secret, token, window2 = 1) {
12067
+ const timeStep = 30;
12068
+ const counter = BigInt(Math.floor(Date.now() / 1e3 / timeStep));
12069
+ for (let i = -window2; i <= window2; i++) {
12070
+ if (generateHotp(secret, counter + BigInt(i)) === token) {
12071
+ return true;
12072
+ }
12073
+ }
12074
+ return false;
12075
+ }
12076
+ function generateTotpSecret(issuer, accountName) {
12077
+ const secretBuffer = randomBytes(20);
12078
+ const secret = base32Encode(secretBuffer);
12079
+ const encodedIssuer = encodeURIComponent(issuer);
12080
+ const encodedAccount = encodeURIComponent(accountName);
12081
+ const uri = `otpauth://totp/${encodedIssuer}:${encodedAccount}?secret=${secret}&issuer=${encodedIssuer}&algorithm=SHA1&digits=6&period=30`;
12082
+ return {
12083
+ secret,
12084
+ uri
12085
+ };
12086
+ }
12087
+ function generateRecoveryCodes(count = 10) {
12088
+ return Array.from({
12089
+ length: count
12090
+ }, () => {
12091
+ const raw = randomBytes(5).toString("hex").toUpperCase();
12092
+ const parts = raw.match(/.{1,5}/g);
12093
+ return parts ? parts.join("-") : raw;
12094
+ });
12095
+ }
12096
+ function hashRecoveryCode(code2) {
12097
+ return createHash("sha256").update(code2.replace(/-/g, "").toUpperCase()).digest("hex");
12098
+ }
11324
12099
  function buildAuthResponse(user, roleIds, accessToken, refreshToken) {
11325
12100
  return {
11326
12101
  user: {
@@ -11358,9 +12133,9 @@ function createAuthRoutes(config) {
11358
12133
  emailService,
11359
12134
  emailConfig,
11360
12135
  allowRegistration = false,
11361
- overrides
12136
+ authHooks
11362
12137
  } = config;
11363
- const ops = resolveAuthOverrides(overrides);
12138
+ const ops = resolveAuthHooks(authHooks);
11364
12139
  const registerSchema = objectType({
11365
12140
  email: stringType().email("Invalid email address").max(255),
11366
12141
  password: stringType().min(1, "Password is required").max(128),
@@ -11424,7 +12199,19 @@ function createAuthRoutes(config) {
11424
12199
  async function createSessionAndTokens(userId, userAgent, ipAddress) {
11425
12200
  const roles = await authRepo.getUserRoles(userId);
11426
12201
  const roleIds = roles.map((r) => r.id);
11427
- const accessToken = generateAccessToken(userId, roleIds);
12202
+ let customClaims;
12203
+ if (authHooks?.customizeAccessToken) {
12204
+ const user = await authRepo.getUserById(userId);
12205
+ if (user) {
12206
+ const defaultClaims = {
12207
+ userId,
12208
+ roles: roleIds,
12209
+ aal: "aal1"
12210
+ };
12211
+ customClaims = await authHooks.customizeAccessToken(defaultClaims, user);
12212
+ }
12213
+ }
12214
+ const accessToken = generateAccessToken(userId, roleIds, "aal1", customClaims);
11428
12215
  const refreshToken = generateRefreshToken();
11429
12216
  await authRepo.createRefreshToken(userId, hashRefreshToken(refreshToken), getRefreshTokenExpiry(), userAgent, ipAddress);
11430
12217
  return {
@@ -11459,8 +12246,8 @@ function createAuthRoutes(config) {
11459
12246
  passwordHash,
11460
12247
  displayName: displayName || void 0
11461
12248
  };
11462
- if (overrides?.beforeUserCreate) {
11463
- createData = await overrides.beforeUserCreate(createData);
12249
+ if (authHooks?.beforeUserCreate) {
12250
+ createData = await authHooks.beforeUserCreate(createData);
11464
12251
  }
11465
12252
  const user = await authRepo.createUser(createData);
11466
12253
  const existingUsers = await authRepo.listUsers();
@@ -11479,14 +12266,16 @@ function createAuthRoutes(config) {
11479
12266
  email: user.email,
11480
12267
  displayName: user.displayName
11481
12268
  });
11482
- if (overrides?.afterUserCreate) {
11483
- overrides.afterUserCreate(user).catch((err) => {
11484
- console.error("[AuthOverrides] afterUserCreate error:", err instanceof Error ? err.message : err);
11485
- });
12269
+ if (authHooks?.afterUserCreate) {
12270
+ try {
12271
+ await authHooks.afterUserCreate(user);
12272
+ } catch (err) {
12273
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
12274
+ }
11486
12275
  }
11487
- if (overrides?.onAuthenticated) {
11488
- overrides.onAuthenticated(user, "register").catch((err) => {
11489
- console.error("[AuthOverrides] onAuthenticated error:", err instanceof Error ? err.message : err);
12276
+ if (authHooks?.onAuthenticated) {
12277
+ authHooks.onAuthenticated(user, "register").catch((err) => {
12278
+ console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
11490
12279
  });
11491
12280
  }
11492
12281
  return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken), 201);
@@ -11496,9 +12285,12 @@ function createAuthRoutes(config) {
11496
12285
  email,
11497
12286
  password
11498
12287
  } = parseBody2(loginSchema, await c.req.json());
12288
+ if (authHooks?.beforeLogin) {
12289
+ await authHooks.beforeLogin(email, "login");
12290
+ }
11499
12291
  let user;
11500
- if (overrides?.verifyCredentials) {
11501
- user = await overrides.verifyCredentials(email, password, authRepo);
12292
+ if (authHooks?.verifyCredentials) {
12293
+ user = await authHooks.verifyCredentials(email, password, authRepo);
11502
12294
  if (!user) {
11503
12295
  throw ApiError.unauthorized("Invalid email or password", "INVALID_CREDENTIALS");
11504
12296
  }
@@ -11520,9 +12312,9 @@ function createAuthRoutes(config) {
11520
12312
  accessToken,
11521
12313
  refreshToken
11522
12314
  } = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
11523
- if (overrides?.onAuthenticated) {
11524
- overrides.onAuthenticated(user, "login").catch((err) => {
11525
- console.error("[AuthOverrides] onAuthenticated error:", err instanceof Error ? err.message : err);
12315
+ if (authHooks?.onAuthenticated) {
12316
+ authHooks.onAuthenticated(user, "login").catch((err) => {
12317
+ console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
11526
12318
  });
11527
12319
  }
11528
12320
  return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken));
@@ -11561,6 +12353,13 @@ function createAuthRoutes(config) {
11561
12353
  await authRepo.linkUserIdentity(user.id, provider.id, externalUser.providerId, {
11562
12354
  email: externalUser.email
11563
12355
  });
12356
+ if (authHooks?.afterUserCreate) {
12357
+ try {
12358
+ await authHooks.afterUserCreate(user);
12359
+ } catch (err) {
12360
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
12361
+ }
12362
+ }
11564
12363
  const allUsers = await authRepo.listUsers();
11565
12364
  const isFirstUser = allUsers.length === 1 && allUsers[0].id === user.id;
11566
12365
  if (isFirstUser) {
@@ -11646,6 +12445,11 @@ function createAuthRoutes(config) {
11646
12445
  await authRepo.updatePassword(storedToken.userId, passwordHash);
11647
12446
  await authRepo.markPasswordResetTokenUsed(tokenHash);
11648
12447
  await authRepo.deleteAllRefreshTokensForUser(storedToken.userId);
12448
+ if (authHooks?.onPasswordReset) {
12449
+ authHooks.onPasswordReset(storedToken.userId).catch((err) => {
12450
+ console.error("[AuthHooks] onPasswordReset error:", err instanceof Error ? err.message : err);
12451
+ });
12452
+ }
11649
12453
  return c.json({
11650
12454
  success: true,
11651
12455
  message: "Password has been reset successfully"
@@ -11749,7 +12553,19 @@ function createAuthRoutes(config) {
11749
12553
  }
11750
12554
  const roles = await authRepo.getUserRoles(storedToken.userId);
11751
12555
  const roleIds = roles.map((r) => r.id);
11752
- const newAccessToken = generateAccessToken(storedToken.userId, roleIds);
12556
+ let customClaims;
12557
+ if (authHooks?.customizeAccessToken) {
12558
+ const user = await authRepo.getUserById(storedToken.userId);
12559
+ if (user) {
12560
+ const defaultClaims = {
12561
+ userId: storedToken.userId,
12562
+ roles: roleIds,
12563
+ aal: "aal1"
12564
+ };
12565
+ customClaims = await authHooks.customizeAccessToken(defaultClaims, user);
12566
+ }
12567
+ }
12568
+ const newAccessToken = generateAccessToken(storedToken.userId, roleIds, "aal1", customClaims);
11753
12569
  const newRefreshToken = generateRefreshToken();
11754
12570
  const userAgent = c.req.header("user-agent") || "unknown";
11755
12571
  const ipAddress = c.req.header("x-forwarded-for") || "unknown";
@@ -11771,6 +12587,18 @@ function createAuthRoutes(config) {
11771
12587
  const tokenHash = hashRefreshToken(refreshToken);
11772
12588
  await authRepo.deleteRefreshToken(tokenHash);
11773
12589
  }
12590
+ const authHeader = c.req.header("authorization");
12591
+ if (authHooks?.afterLogout && authHeader?.startsWith("Bearer ")) {
12592
+ const {
12593
+ verifyAccessToken: verifyAccessToken2
12594
+ } = await Promise.resolve().then(() => jwt);
12595
+ const payload = verifyAccessToken2(authHeader.substring(7));
12596
+ if (payload) {
12597
+ authHooks.afterLogout(payload.userId).catch((err) => {
12598
+ console.error("[AuthHooks] afterLogout error:", err instanceof Error ? err.message : err);
12599
+ });
12600
+ }
12601
+ }
11774
12602
  return c.json({
11775
12603
  success: true
11776
12604
  });
@@ -11890,6 +12718,270 @@ function createAuthRoutes(config) {
11890
12718
  enabledProviders
11891
12719
  });
11892
12720
  });
12721
+ router.post("/anonymous", strictAuthLimiter, async (c) => {
12722
+ const anonId = randomBytes(16).toString("hex");
12723
+ const anonEmail = `anon_${anonId.slice(0, 8)}@anonymous.local`;
12724
+ let createData = {
12725
+ email: anonEmail,
12726
+ emailVerified: false,
12727
+ isAnonymous: true
12728
+ };
12729
+ if (authHooks?.beforeUserCreate) {
12730
+ createData = await authHooks.beforeUserCreate(createData);
12731
+ }
12732
+ const user = await authRepo.createUser(createData);
12733
+ if (config.defaultRole) {
12734
+ await authRepo.assignDefaultRole(user.id, config.defaultRole);
12735
+ }
12736
+ const {
12737
+ roleIds,
12738
+ accessToken,
12739
+ refreshToken
12740
+ } = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
12741
+ if (authHooks?.afterUserCreate) {
12742
+ authHooks.afterUserCreate(user).catch((err) => {
12743
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
12744
+ });
12745
+ }
12746
+ if (authHooks?.onAuthenticated) {
12747
+ authHooks.onAuthenticated(user, "anonymous").catch((err) => {
12748
+ console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
12749
+ });
12750
+ }
12751
+ return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken), 201);
12752
+ });
12753
+ router.post("/anonymous/link", requireAuth, async (c) => {
12754
+ const userCtx = c.get("user");
12755
+ if (!userCtx) {
12756
+ throw ApiError.unauthorized("Not authenticated");
12757
+ }
12758
+ const user = await authRepo.getUserById(userCtx.userId);
12759
+ if (!user?.isAnonymous) {
12760
+ throw ApiError.badRequest("User is not anonymous", "NOT_ANONYMOUS");
12761
+ }
12762
+ const linkSchema = objectType({
12763
+ email: stringType().email("Invalid email address").max(255),
12764
+ password: stringType().min(1, "Password is required").max(128)
12765
+ });
12766
+ const {
12767
+ email,
12768
+ password
12769
+ } = parseBody2(linkSchema, await c.req.json());
12770
+ const passwordValidation = ops.validatePasswordStrength(password);
12771
+ if (!passwordValidation.valid) {
12772
+ throw ApiError.badRequest(passwordValidation.errors.join(". "), "WEAK_PASSWORD");
12773
+ }
12774
+ const existingUser = await authRepo.getUserByEmail(email.toLowerCase());
12775
+ if (existingUser) {
12776
+ throw ApiError.conflict("Email already registered", "EMAIL_EXISTS");
12777
+ }
12778
+ const passwordHash = await ops.hashPassword(password);
12779
+ const updatedUser = await authRepo.updateUser(user.id, {
12780
+ email: email.toLowerCase(),
12781
+ passwordHash,
12782
+ isAnonymous: false
12783
+ });
12784
+ if (!updatedUser) {
12785
+ throw ApiError.notFound("User not found");
12786
+ }
12787
+ const {
12788
+ roleIds,
12789
+ accessToken,
12790
+ refreshToken
12791
+ } = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
12792
+ return c.json(buildAuthResponse(updatedUser, roleIds, accessToken, refreshToken));
12793
+ });
12794
+ router.post("/mfa/enroll", requireAuth, async (c) => {
12795
+ const userCtx = c.get("user");
12796
+ if (!userCtx) {
12797
+ throw ApiError.unauthorized("Not authenticated");
12798
+ }
12799
+ const body = await c.req.json().catch(() => ({}));
12800
+ const friendlyName = typeof body.friendlyName === "string" ? body.friendlyName : void 0;
12801
+ const issuer = typeof body.issuer === "string" ? body.issuer : emailConfig?.appName || "Rebase";
12802
+ const user = await authRepo.getUserById(userCtx.userId);
12803
+ if (!user) {
12804
+ throw ApiError.notFound("User not found");
12805
+ }
12806
+ const {
12807
+ secret,
12808
+ uri
12809
+ } = generateTotpSecret(issuer, user.email);
12810
+ const factor = await authRepo.createMfaFactor(
12811
+ user.id,
12812
+ "totp",
12813
+ secret,
12814
+ // In production, encrypt this before storage
12815
+ friendlyName
12816
+ );
12817
+ const codes = generateRecoveryCodes(10);
12818
+ const codeHashes = codes.map(hashRecoveryCode);
12819
+ await authRepo.createRecoveryCodes(user.id, codeHashes);
12820
+ return c.json({
12821
+ factor: {
12822
+ id: factor.id,
12823
+ factorType: factor.factorType,
12824
+ friendlyName: factor.friendlyName
12825
+ },
12826
+ totp: {
12827
+ secret,
12828
+ uri,
12829
+ qrUri: uri
12830
+ // Client can use a QR library to render this
12831
+ },
12832
+ recoveryCodes: codes
12833
+ }, 201);
12834
+ });
12835
+ router.post("/mfa/verify", requireAuth, async (c) => {
12836
+ const userCtx = c.get("user");
12837
+ if (!userCtx) {
12838
+ throw ApiError.unauthorized("Not authenticated");
12839
+ }
12840
+ const verifySchema = objectType({
12841
+ factorId: stringType().min(1, "Factor ID is required"),
12842
+ code: stringType().length(6, "Code must be 6 digits")
12843
+ });
12844
+ const {
12845
+ factorId,
12846
+ code: code2
12847
+ } = parseBody2(verifySchema, await c.req.json());
12848
+ const factor = await authRepo.getMfaFactorById(factorId);
12849
+ if (!factor || factor.userId !== userCtx.userId) {
12850
+ throw ApiError.notFound("MFA factor not found");
12851
+ }
12852
+ if (factor.verified) {
12853
+ throw ApiError.badRequest("Factor is already verified", "ALREADY_VERIFIED");
12854
+ }
12855
+ const secretBuffer = base32Decode(factor.secretEncrypted);
12856
+ const isValid2 = verifyTotp(secretBuffer, code2);
12857
+ if (!isValid2) {
12858
+ throw ApiError.unauthorized("Invalid TOTP code", "INVALID_CODE");
12859
+ }
12860
+ await authRepo.verifyMfaFactor(factorId);
12861
+ return c.json({
12862
+ success: true,
12863
+ message: "MFA factor verified and enrolled"
12864
+ });
12865
+ });
12866
+ router.post("/mfa/challenge", requireAuth, async (c) => {
12867
+ const userCtx = c.get("user");
12868
+ if (!userCtx) {
12869
+ throw ApiError.unauthorized("Not authenticated");
12870
+ }
12871
+ const challengeSchema = objectType({
12872
+ factorId: stringType().min(1, "Factor ID is required")
12873
+ });
12874
+ const {
12875
+ factorId
12876
+ } = parseBody2(challengeSchema, await c.req.json());
12877
+ const factor = await authRepo.getMfaFactorById(factorId);
12878
+ if (!factor || factor.userId !== userCtx.userId) {
12879
+ throw ApiError.notFound("MFA factor not found");
12880
+ }
12881
+ if (!factor.verified) {
12882
+ throw ApiError.badRequest("MFA factor is not yet verified", "FACTOR_NOT_VERIFIED");
12883
+ }
12884
+ const ipAddress = c.req.header("x-forwarded-for") || "unknown";
12885
+ const challenge = await authRepo.createMfaChallenge(factorId, ipAddress);
12886
+ return c.json({
12887
+ challengeId: challenge.id,
12888
+ factorId: challenge.factorId,
12889
+ expiresAt: new Date(Date.now() + 5 * 60 * 1e3).toISOString()
12890
+ });
12891
+ });
12892
+ router.post("/mfa/challenge/verify", requireAuth, async (c) => {
12893
+ const userCtx = c.get("user");
12894
+ if (!userCtx) {
12895
+ throw ApiError.unauthorized("Not authenticated");
12896
+ }
12897
+ const challengeVerifySchema = objectType({
12898
+ challengeId: stringType().min(1, "Challenge ID is required"),
12899
+ code: stringType().min(1, "Code is required")
12900
+ });
12901
+ const {
12902
+ challengeId,
12903
+ code: code2
12904
+ } = parseBody2(challengeVerifySchema, await c.req.json());
12905
+ const challenge = await authRepo.getMfaChallengeById(challengeId);
12906
+ if (!challenge) {
12907
+ throw ApiError.badRequest("Invalid or expired challenge", "INVALID_CHALLENGE");
12908
+ }
12909
+ const factor = await authRepo.getMfaFactorById(challenge.factorId);
12910
+ if (!factor || factor.userId !== userCtx.userId) {
12911
+ throw ApiError.notFound("MFA factor not found");
12912
+ }
12913
+ const isRecoveryCode = code2.length > 6;
12914
+ let isValid2 = false;
12915
+ if (isRecoveryCode) {
12916
+ const codeHash = hashRecoveryCode(code2);
12917
+ isValid2 = await authRepo.useRecoveryCode(userCtx.userId, codeHash);
12918
+ } else {
12919
+ const secretBuffer = base32Decode(factor.secretEncrypted);
12920
+ isValid2 = verifyTotp(secretBuffer, code2);
12921
+ }
12922
+ if (!isValid2) {
12923
+ throw ApiError.unauthorized("Invalid verification code", "INVALID_CODE");
12924
+ }
12925
+ await authRepo.verifyMfaChallenge(challengeId);
12926
+ const roles = await authRepo.getUserRoles(userCtx.userId);
12927
+ const roleIds = roles.map((r) => r.id);
12928
+ const accessToken = generateAccessToken(userCtx.userId, roleIds, "aal2");
12929
+ const refreshToken = generateRefreshToken();
12930
+ await authRepo.createRefreshToken(userCtx.userId, hashRefreshToken(refreshToken), getRefreshTokenExpiry(), c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
12931
+ if (authHooks?.onMfaVerified) {
12932
+ authHooks.onMfaVerified(userCtx.userId, factor.id).catch((err) => {
12933
+ console.error("[AuthHooks] onMfaVerified error:", err instanceof Error ? err.message : err);
12934
+ });
12935
+ }
12936
+ return c.json({
12937
+ tokens: {
12938
+ accessToken,
12939
+ refreshToken,
12940
+ accessTokenExpiresAt: getAccessTokenExpiry()
12941
+ }
12942
+ });
12943
+ });
12944
+ router.get("/mfa/factors", requireAuth, async (c) => {
12945
+ const userCtx = c.get("user");
12946
+ if (!userCtx) {
12947
+ throw ApiError.unauthorized("Not authenticated");
12948
+ }
12949
+ const factors = await authRepo.getMfaFactors(userCtx.userId);
12950
+ return c.json({
12951
+ factors: factors.map((f2) => ({
12952
+ id: f2.id,
12953
+ factorType: f2.factorType,
12954
+ friendlyName: f2.friendlyName,
12955
+ verified: f2.verified,
12956
+ createdAt: f2.createdAt
12957
+ }))
12958
+ });
12959
+ });
12960
+ router.delete("/mfa/unenroll", requireAuth, async (c) => {
12961
+ const userCtx = c.get("user");
12962
+ if (!userCtx) {
12963
+ throw ApiError.unauthorized("Not authenticated");
12964
+ }
12965
+ const unenrollSchema = objectType({
12966
+ factorId: stringType().min(1, "Factor ID is required")
12967
+ });
12968
+ const {
12969
+ factorId
12970
+ } = parseBody2(unenrollSchema, await c.req.json());
12971
+ const factor = await authRepo.getMfaFactorById(factorId);
12972
+ if (!factor || factor.userId !== userCtx.userId) {
12973
+ throw ApiError.notFound("MFA factor not found");
12974
+ }
12975
+ await authRepo.deleteMfaFactor(factorId, userCtx.userId);
12976
+ const hasFactors = await authRepo.hasVerifiedMfaFactors(userCtx.userId);
12977
+ if (!hasFactors) {
12978
+ await authRepo.deleteAllRecoveryCodes(userCtx.userId);
12979
+ }
12980
+ return c.json({
12981
+ success: true,
12982
+ message: "MFA factor removed"
12983
+ });
12984
+ });
11893
12985
  return router;
11894
12986
  }
11895
12987
  function generateSecurePassword() {
@@ -11921,9 +13013,9 @@ function createAdminRoutes(config) {
11921
13013
  emailService,
11922
13014
  emailConfig,
11923
13015
  hooks,
11924
- overrides
13016
+ authHooks
11925
13017
  } = config;
11926
- const ops = resolveAuthOverrides(overrides);
13018
+ const ops = resolveAuthHooks(authHooks);
11927
13019
  function buildHookContext(c, method) {
11928
13020
  const user = c.get("user");
11929
13021
  return {
@@ -11991,6 +13083,10 @@ function createAdminRoutes(config) {
11991
13083
  if (!userId) {
11992
13084
  throw ApiError.unauthorized("User ID not found in auth context");
11993
13085
  }
13086
+ const caller = await authRepo.getUserById(userId);
13087
+ if (!caller) {
13088
+ throw ApiError.notFound("Authenticated user does not exist in the database. Please sign out and sign in/register again.", "USER_NOT_FOUND");
13089
+ }
11994
13090
  await authRepo.setUserRoles(userId, ["admin"]);
11995
13091
  if (config.setBootstrapCompleted) {
11996
13092
  await config.setBootstrapCompleted();
@@ -12242,6 +13338,18 @@ function createAdminRoutes(config) {
12242
13338
  await authRepo.updateUser(userId, updates);
12243
13339
  }
12244
13340
  if (roles !== void 0 && Array.isArray(roles)) {
13341
+ const currentRoles = await authRepo.getUserRoleIds(userId);
13342
+ const wasAdmin = currentRoles.includes("admin");
13343
+ const willBeAdmin = roles.includes("admin");
13344
+ if (wasAdmin && !willBeAdmin) {
13345
+ const adminUsers = await authRepo.listUsersPaginated({
13346
+ roleId: "admin",
13347
+ limit: 1
13348
+ });
13349
+ if (adminUsers.total <= 1) {
13350
+ throw ApiError.forbidden("Cannot demote the last administrator", "LAST_ADMIN");
13351
+ }
13352
+ }
12245
13353
  await authRepo.setUserRoles(userId, roles);
12246
13354
  }
12247
13355
  const result = await authRepo.getUserWithRoles(userId);
@@ -12266,6 +13374,16 @@ function createAdminRoutes(config) {
12266
13374
  if (!existing) {
12267
13375
  throw ApiError.notFound("User not found");
12268
13376
  }
13377
+ const roles = await authRepo.getUserRoleIds(userId);
13378
+ if (roles.includes("admin")) {
13379
+ const adminUsers = await authRepo.listUsersPaginated({
13380
+ roleId: "admin",
13381
+ limit: 1
13382
+ });
13383
+ if (adminUsers.total <= 1) {
13384
+ throw ApiError.forbidden("Cannot delete the last administrator", "LAST_ADMIN");
13385
+ }
13386
+ }
12269
13387
  const hookCtx = buildHookContext(c, "DELETE");
12270
13388
  if (hooks?.users?.beforeDelete) {
12271
13389
  await hooks.users.beforeDelete(userId, hookCtx);
@@ -12287,8 +13405,7 @@ function createAdminRoutes(config) {
12287
13405
  id: r.id,
12288
13406
  name: r.name,
12289
13407
  isAdmin: r.isAdmin,
12290
- defaultPermissions: r.defaultPermissions,
12291
- config: r.config
13408
+ defaultPermissions: r.defaultPermissions
12292
13409
  }));
12293
13410
  adminRoles = await applyRoleAfterReadBatch(adminRoles, hookCtx);
12294
13411
  return c.json({
@@ -12311,8 +13428,7 @@ function createAdminRoutes(config) {
12311
13428
  id,
12312
13429
  name: name2,
12313
13430
  isAdmin,
12314
- defaultPermissions,
12315
- config: config2
13431
+ defaultPermissions
12316
13432
  } = body;
12317
13433
  if (!id || !name2) {
12318
13434
  throw ApiError.badRequest("Role ID and name are required", "INVALID_INPUT");
@@ -12325,8 +13441,7 @@ function createAdminRoutes(config) {
12325
13441
  id,
12326
13442
  name: name2,
12327
13443
  isAdmin: isAdmin ?? false,
12328
- defaultPermissions: defaultPermissions ?? null,
12329
- config: config2 ?? null
13444
+ defaultPermissions: defaultPermissions ?? null
12330
13445
  });
12331
13446
  return c.json({
12332
13447
  role
@@ -12338,8 +13453,7 @@ function createAdminRoutes(config) {
12338
13453
  const {
12339
13454
  name: name2,
12340
13455
  isAdmin,
12341
- defaultPermissions,
12342
- config: config2
13456
+ defaultPermissions
12343
13457
  } = body;
12344
13458
  const existing = await authRepo.getRoleById(roleId);
12345
13459
  if (!existing) {
@@ -12348,8 +13462,7 @@ function createAdminRoutes(config) {
12348
13462
  const role = await authRepo.updateRole(roleId, {
12349
13463
  name: name2,
12350
13464
  isAdmin,
12351
- defaultPermissions,
12352
- config: config2
13465
+ defaultPermissions
12353
13466
  });
12354
13467
  return c.json({
12355
13468
  role
@@ -12381,9 +13494,9 @@ function createBuiltinAuthAdapter(config) {
12381
13494
  oauthProviders = [],
12382
13495
  serviceKey,
12383
13496
  hooks,
12384
- overrides
13497
+ authHooks
12385
13498
  } = config;
12386
- const resolvedOps = resolveAuthOverrides(overrides);
13499
+ const resolvedOps = resolveAuthHooks(authHooks);
12387
13500
  const adapter = {
12388
13501
  id: "rebase-builtin",
12389
13502
  serviceKey,
@@ -12457,7 +13570,7 @@ function createBuiltinAuthAdapter(config) {
12457
13570
  rawToken: token
12458
13571
  };
12459
13572
  },
12460
- userManagement: createUserManagementFromRepo(authRepository, resolvedOps, overrides),
13573
+ userManagement: createUserManagementFromRepo(authRepository, resolvedOps, authHooks),
12461
13574
  roleManagement: createRoleManagementFromRepo(authRepository),
12462
13575
  createAuthRoutes() {
12463
13576
  return createAuthRoutes({
@@ -12467,7 +13580,7 @@ function createBuiltinAuthAdapter(config) {
12467
13580
  allowRegistration,
12468
13581
  defaultRole,
12469
13582
  oauthProviders,
12470
- overrides
13583
+ authHooks
12471
13584
  });
12472
13585
  },
12473
13586
  createAdminRoutes() {
@@ -12477,7 +13590,7 @@ function createBuiltinAuthAdapter(config) {
12477
13590
  emailConfig,
12478
13591
  serviceKey,
12479
13592
  hooks,
12480
- overrides
13593
+ authHooks
12481
13594
  });
12482
13595
  },
12483
13596
  async getCapabilities() {
@@ -12506,7 +13619,7 @@ function createBuiltinAuthAdapter(config) {
12506
13619
  };
12507
13620
  return adapter;
12508
13621
  }
12509
- function createUserManagementFromRepo(repo, resolvedOps, overrides) {
13622
+ function createUserManagementFromRepo(repo, resolvedOps, authHooks) {
12510
13623
  return {
12511
13624
  async listUsers(options2) {
12512
13625
  const result = await repo.listUsersPaginated({
@@ -12537,14 +13650,16 @@ function createUserManagementFromRepo(repo, resolvedOps, overrides) {
12537
13650
  photoUrl: data.photoUrl,
12538
13651
  metadata: data.metadata
12539
13652
  };
12540
- if (overrides?.beforeUserCreate) {
12541
- createData = await overrides.beforeUserCreate(createData);
13653
+ if (authHooks?.beforeUserCreate) {
13654
+ createData = await authHooks.beforeUserCreate(createData);
12542
13655
  }
12543
13656
  const user = await repo.createUser(createData);
12544
- if (overrides?.afterUserCreate) {
12545
- overrides.afterUserCreate(user).catch((err) => {
12546
- console.error("[AuthOverrides] afterUserCreate error:", err instanceof Error ? err.message : err);
12547
- });
13657
+ if (authHooks?.afterUserCreate) {
13658
+ try {
13659
+ await authHooks.afterUserCreate(user);
13660
+ } catch (err) {
13661
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
13662
+ }
12548
13663
  }
12549
13664
  return toAuthUserData(user);
12550
13665
  },
@@ -12561,7 +13676,15 @@ function createUserManagementFromRepo(repo, resolvedOps, overrides) {
12561
13676
  return user ? toAuthUserData(user) : null;
12562
13677
  },
12563
13678
  async deleteUser(id) {
13679
+ if (authHooks?.beforeUserDelete) {
13680
+ await authHooks.beforeUserDelete(id);
13681
+ }
12564
13682
  await repo.deleteUser(id);
13683
+ if (authHooks?.afterUserDelete) {
13684
+ authHooks.afterUserDelete(id).catch((err) => {
13685
+ console.error("[AuthHooks] afterUserDelete error:", err instanceof Error ? err.message : err);
13686
+ });
13687
+ }
12565
13688
  },
12566
13689
  async getUserRoles(userId) {
12567
13690
  const roles = await repo.getUserRoles(userId);
@@ -12588,8 +13711,7 @@ function createRoleManagementFromRepo(repo) {
12588
13711
  name: data.name,
12589
13712
  isAdmin: data.isAdmin,
12590
13713
  defaultPermissions: data.defaultPermissions,
12591
- collectionPermissions: data.collectionPermissions,
12592
- config: data.config
13714
+ collectionPermissions: data.collectionPermissions
12593
13715
  });
12594
13716
  return toAuthRoleData(role);
12595
13717
  },
@@ -12620,8 +13742,7 @@ function toAuthRoleData(role) {
12620
13742
  name: role.name,
12621
13743
  isAdmin: role.isAdmin,
12622
13744
  defaultPermissions: role.defaultPermissions,
12623
- collectionPermissions: role.collectionPermissions,
12624
- config: role.config
13745
+ collectionPermissions: role.collectionPermissions
12625
13746
  };
12626
13747
  }
12627
13748
  function configureLogLevel(logLevel) {
@@ -12642,20 +13763,20 @@ function configureLogLevel(logLevel) {
12642
13763
  if (currentLevel < 0) console.error = () => {
12643
13764
  };
12644
13765
  }
13766
+ let originalConsole;
12645
13767
  function resetConsole() {
12646
- if (!global.__originalConsole) {
12647
- global.__originalConsole = {
13768
+ if (!originalConsole) {
13769
+ originalConsole = {
12648
13770
  log: console.log,
12649
13771
  warn: console.warn,
12650
13772
  error: console.error,
12651
13773
  debug: console.debug
12652
13774
  };
12653
13775
  }
12654
- const original = global.__originalConsole;
12655
- console.log = original.log;
12656
- console.warn = original.warn;
12657
- console.error = original.error;
12658
- console.debug = original.debug;
13776
+ console.log = originalConsole.log;
13777
+ console.warn = originalConsole.warn;
13778
+ console.error = originalConsole.error;
13779
+ console.debug = originalConsole.debug;
12659
13780
  }
12660
13781
  const GCP_SEVERITY = {
12661
13782
  debug: "DEBUG",
@@ -12897,12 +14018,6 @@ var browser$1 = { exports: {} };
12897
14018
  exports.Response = globalObject.Response;
12898
14019
  })(browser$1, browser$1.exports);
12899
14020
  var browserExports = browser$1.exports;
12900
- const __viteBrowserExternal = {};
12901
- const __viteBrowserExternal$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12902
- __proto__: null,
12903
- default: __viteBrowserExternal
12904
- }, Symbol.toStringTag, { value: "Module" }));
12905
- const require$$11 = /* @__PURE__ */ getAugmentedNamespace(__viteBrowserExternal$1);
12906
14021
  const isStream = (stream2) => stream2 !== null && typeof stream2 === "object" && typeof stream2.pipe === "function";
12907
14022
  isStream.writable = (stream2) => isStream(stream2) && stream2.writable !== false && typeof stream2._write === "function" && typeof stream2._writableState === "object";
12908
14023
  isStream.readable = (stream2) => isStream(stream2) && stream2.readable !== false && typeof stream2._read === "function" && typeof stream2._readableState === "object";
@@ -13687,16 +14802,16 @@ Object.defineProperty(sha1$1, "__esModule", {
13687
14802
  value: true
13688
14803
  });
13689
14804
  sha1$1.default = void 0;
13690
- function f(s2, x, y2, z) {
14805
+ function f(s2, x, y2, z2) {
13691
14806
  switch (s2) {
13692
14807
  case 0:
13693
- return x & y2 ^ ~x & z;
14808
+ return x & y2 ^ ~x & z2;
13694
14809
  case 1:
13695
- return x ^ y2 ^ z;
14810
+ return x ^ y2 ^ z2;
13696
14811
  case 2:
13697
- return x & y2 ^ x & z ^ y2 & z;
14812
+ return x & y2 ^ x & z2 ^ y2 & z2;
13698
14813
  case 3:
13699
- return x ^ y2 ^ z;
14814
+ return x ^ y2 ^ z2;
13700
14815
  }
13701
14816
  }
13702
14817
  function ROTL(x, n) {
@@ -14745,7 +15860,7 @@ gaxios.Gaxios = void 0;
14745
15860
  const extend_1 = __importDefault(extend);
14746
15861
  const https_1 = require$$1;
14747
15862
  const node_fetch_1 = __importDefault(browserExports);
14748
- const querystring_1$1 = __importDefault(require$$11);
15863
+ const querystring_1$1 = __importDefault(require$$1$2);
14749
15864
  const is_stream_1 = __importDefault(isStream_1);
14750
15865
  const url_1 = require$$0$5;
14751
15866
  const common_1 = common$1;
@@ -16423,11 +17538,11 @@ var bignumber = { exports: {} };
16423
17538
  return n > 0 || n === i ? i : i - 1;
16424
17539
  }
16425
17540
  function coeffToString(a2) {
16426
- var s2, z, i = 1, j = a2.length, r = a2[0] + "";
17541
+ var s2, z2, i = 1, j = a2.length, r = a2[0] + "";
16427
17542
  for (; i < j; ) {
16428
17543
  s2 = a2[i++] + "";
16429
- z = LOG_BASE - s2.length;
16430
- for (; z--; s2 = "0" + s2) ;
17544
+ z2 = LOG_BASE - s2.length;
17545
+ for (; z2--; s2 = "0" + s2) ;
16431
17546
  r += s2;
16432
17547
  }
16433
17548
  for (j = r.length; r.charCodeAt(--j) === 48; ) ;
@@ -16460,15 +17575,15 @@ var bignumber = { exports: {} };
16460
17575
  function toExponential(str, e) {
16461
17576
  return (str.length > 1 ? str.charAt(0) + "." + str.slice(1) : str) + (e < 0 ? "e" : "e+") + e;
16462
17577
  }
16463
- function toFixedPoint(str, e, z) {
17578
+ function toFixedPoint(str, e, z2) {
16464
17579
  var len, zs;
16465
17580
  if (e < 0) {
16466
- for (zs = z + "."; ++e; zs += z) ;
17581
+ for (zs = z2 + "."; ++e; zs += z2) ;
16467
17582
  str = zs + str;
16468
17583
  } else {
16469
17584
  len = str.length;
16470
17585
  if (++e > len) {
16471
- for (zs = z, e -= len; --e; zs += z) ;
17586
+ for (zs = z2, e -= len; --e; zs += z2) ;
16472
17587
  str += zs;
16473
17588
  } else if (e < len) {
16474
17589
  str = str.slice(0, e) + "." + str.slice(e);
@@ -16887,7 +18002,7 @@ var gcpResidency = {};
16887
18002
  exports.isGoogleComputeEngine = isGoogleComputeEngine;
16888
18003
  exports.detectGCPResidency = detectGCPResidency;
16889
18004
  const fs_1 = fs__default;
16890
- const os_1 = require$$1$2;
18005
+ const os_1 = require$$1$3;
16891
18006
  exports.GCE_LINUX_BIOS_PATHS = {
16892
18007
  BIOS_DATE: "/sys/class/dmi/id/bios_date",
16893
18008
  BIOS_VENDOR: "/sys/class/dmi/id/bios_vendor"
@@ -17020,7 +18135,7 @@ Colours.refresh();
17020
18135
  exports.setBackend = setBackend;
17021
18136
  exports.log = log;
17022
18137
  const node_events_1 = require$$0$8;
17023
- const process2 = __importStar2(require$$1$3);
18138
+ const process2 = __importStar2(require$$1$4);
17024
18139
  const util2 = __importStar2(require$$2$1);
17025
18140
  const colours_1 = colours;
17026
18141
  var LogSeverity;
@@ -18092,7 +19207,7 @@ loginticket.LoginTicket = LoginTicket;
18092
19207
  Object.defineProperty(oauth2client, "__esModule", { value: true });
18093
19208
  oauth2client.OAuth2Client = oauth2client.ClientAuthentication = oauth2client.CertificateFormat = oauth2client.CodeChallengeMethod = void 0;
18094
19209
  const gaxios_1$5 = src$2;
18095
- const querystring$2 = require$$11;
19210
+ const querystring$2 = require$$1$2;
18096
19211
  const stream$4 = require$$0$4;
18097
19212
  const formatEcdsa = ecdsaSigFormatter;
18098
19213
  const crypto_1$2 = requireCrypto();
@@ -19580,7 +20695,7 @@ var refreshclient = {};
19580
20695
  Object.defineProperty(refreshclient, "__esModule", { value: true });
19581
20696
  refreshclient.UserRefreshClient = refreshclient.USER_REFRESH_ACCOUNT_TYPE = void 0;
19582
20697
  const oauth2client_1$1 = oauth2client;
19583
- const querystring_1 = require$$11;
20698
+ const querystring_1 = require$$1$2;
19584
20699
  refreshclient.USER_REFRESH_ACCOUNT_TYPE = "authorized_user";
19585
20700
  class UserRefreshClient extends oauth2client_1$1.OAuth2Client {
19586
20701
  constructor(optionsOrClientId, clientSecret, refreshToken, eagerRefreshThresholdMillis, forceRefreshOnFailure) {
@@ -19855,7 +20970,7 @@ var oauth2common = {};
19855
20970
  Object.defineProperty(oauth2common, "__esModule", { value: true });
19856
20971
  oauth2common.OAuthClientAuthHandler = void 0;
19857
20972
  oauth2common.getErrorFromOAuthErrorResponse = getErrorFromOAuthErrorResponse;
19858
- const querystring$1 = require$$11;
20973
+ const querystring$1 = require$$1$2;
19859
20974
  const crypto_1$1 = requireCrypto();
19860
20975
  const METHODS_SUPPORTING_REQUEST_BODY = ["PUT", "POST", "PATCH"];
19861
20976
  class OAuthClientAuthHandler {
@@ -20001,7 +21116,7 @@ function getErrorFromOAuthErrorResponse(resp, err) {
20001
21116
  Object.defineProperty(stscredentials, "__esModule", { value: true });
20002
21117
  stscredentials.StsCredentials = void 0;
20003
21118
  const gaxios_1$1 = src$2;
20004
- const querystring = require$$11;
21119
+ const querystring = require$$1$2;
20005
21120
  const transporters_1 = transporters;
20006
21121
  const oauth2common_1$1 = oauth2common;
20007
21122
  class StsCredentials extends oauth2common_1$1.OAuthClientAuthHandler {
@@ -21607,7 +22722,7 @@ externalAccountAuthorizedUserClient.ExternalAccountAuthorizedUserClient = Extern
21607
22722
  const child_process_1 = require$$0$a;
21608
22723
  const fs2 = fs__default;
21609
22724
  const gcpMetadata2 = src$3;
21610
- const os2 = require$$1$2;
22725
+ const os2 = require$$1$3;
21611
22726
  const path2 = path__default;
21612
22727
  const crypto_12 = requireCrypto();
21613
22728
  const transporters_12 = transporters;
@@ -22971,7 +24086,7 @@ function createMicrosoftProvider(config) {
22971
24086
  }
22972
24087
  function createAppleProvider(config) {
22973
24088
  async function generateClientSecret() {
22974
- return jwt.sign({}, config.privateKey, {
24089
+ return jwt$1.sign({}, config.privateKey, {
22975
24090
  algorithm: "ES256",
22976
24091
  keyid: config.keyId,
22977
24092
  issuer: config.teamId,
@@ -23451,6 +24566,341 @@ function createSpotifyProvider(config) {
23451
24566
  }
23452
24567
  };
23453
24568
  }
24569
+ const TABLE$1 = "rebase.api_keys";
24570
+ function generateApiKey() {
24571
+ const random = randomBytes(16).toString("hex");
24572
+ return `rk_live_${random}`;
24573
+ }
24574
+ function hashKey(plaintext) {
24575
+ return createHash("sha256").update(plaintext).digest("hex");
24576
+ }
24577
+ function keyPrefix(plaintext) {
24578
+ return plaintext.substring(0, 12);
24579
+ }
24580
+ function toMasked(row) {
24581
+ return {
24582
+ id: row.id,
24583
+ name: row.name,
24584
+ key_prefix: row.key_prefix,
24585
+ permissions: row.permissions,
24586
+ rate_limit: row.rate_limit,
24587
+ created_by: row.created_by,
24588
+ created_at: row.created_at,
24589
+ updated_at: row.updated_at,
24590
+ last_used_at: row.last_used_at,
24591
+ expires_at: row.expires_at,
24592
+ revoked_at: row.revoked_at
24593
+ };
24594
+ }
24595
+ function rowToApiKey(row) {
24596
+ const permissions = typeof row.permissions === "string" ? JSON.parse(row.permissions) : row.permissions ?? [];
24597
+ return {
24598
+ id: row.id,
24599
+ name: row.name,
24600
+ key_prefix: row.key_prefix,
24601
+ key_hash: row.key_hash,
24602
+ permissions,
24603
+ rate_limit: row.rate_limit !== null && row.rate_limit !== void 0 ? Number(row.rate_limit) : null,
24604
+ created_by: row.created_by,
24605
+ created_at: new Date(row.created_at).toISOString(),
24606
+ updated_at: new Date(row.updated_at).toISOString(),
24607
+ last_used_at: row.last_used_at ? new Date(row.last_used_at).toISOString() : null,
24608
+ expires_at: row.expires_at ? new Date(row.expires_at).toISOString() : null,
24609
+ revoked_at: row.revoked_at ? new Date(row.revoked_at).toISOString() : null
24610
+ };
24611
+ }
24612
+ function esc(value) {
24613
+ return value.replace(/'/g, "''");
24614
+ }
24615
+ function createApiKeyStore(driver) {
24616
+ const admin = driver.admin;
24617
+ if (!isSQLAdmin(admin)) {
24618
+ logger.warn("⚠️ [api-key-store] DataDriver does not support SQL admin — API keys will not be available.");
24619
+ return void 0;
24620
+ }
24621
+ const exec = admin.executeSql.bind(admin);
24622
+ return {
24623
+ // ── Schema bootstrap ────────────────────────────────────────
24624
+ async ensureTable() {
24625
+ try {
24626
+ await exec("CREATE SCHEMA IF NOT EXISTS rebase");
24627
+ await exec(`
24628
+ CREATE TABLE IF NOT EXISTS ${TABLE$1} (
24629
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
24630
+ name TEXT NOT NULL,
24631
+ key_prefix TEXT NOT NULL,
24632
+ key_hash TEXT NOT NULL UNIQUE,
24633
+ permissions JSONB NOT NULL DEFAULT '[]'::jsonb,
24634
+ rate_limit INTEGER,
24635
+ created_by TEXT NOT NULL,
24636
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
24637
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
24638
+ last_used_at TIMESTAMPTZ,
24639
+ expires_at TIMESTAMPTZ,
24640
+ revoked_at TIMESTAMPTZ
24641
+ )
24642
+ `);
24643
+ await exec(`
24644
+ CREATE INDEX IF NOT EXISTS idx_api_keys_hash
24645
+ ON ${TABLE$1}(key_hash)
24646
+ `);
24647
+ await exec(`
24648
+ CREATE INDEX IF NOT EXISTS idx_api_keys_prefix
24649
+ ON ${TABLE$1}(key_prefix)
24650
+ `);
24651
+ logger.info("✅ API keys table ready");
24652
+ } catch (err) {
24653
+ logger.error("❌ Failed to create API keys table", {
24654
+ error: err
24655
+ });
24656
+ logger.warn("⚠️ Continuing without API keys support.");
24657
+ }
24658
+ },
24659
+ // ── Create ──────────────────────────────────────────────────
24660
+ async createApiKey(request, createdBy) {
24661
+ const plaintext = generateApiKey();
24662
+ const hash = hashKey(plaintext);
24663
+ const prefix = keyPrefix(plaintext);
24664
+ const permissionsJson = JSON.stringify(request.permissions);
24665
+ const rateLimitSql = request.rate_limit !== null && request.rate_limit !== void 0 ? String(request.rate_limit) : "NULL";
24666
+ const expiresAtSql = request.expires_at ? `'${esc(request.expires_at)}'` : "NULL";
24667
+ const rows = await exec(`
24668
+ INSERT INTO ${TABLE$1} (name, key_prefix, key_hash, permissions, rate_limit, created_by, expires_at)
24669
+ VALUES (
24670
+ '${esc(request.name)}',
24671
+ '${esc(prefix)}',
24672
+ '${esc(hash)}',
24673
+ '${esc(permissionsJson)}'::jsonb,
24674
+ ${rateLimitSql},
24675
+ '${esc(createdBy)}',
24676
+ ${expiresAtSql}
24677
+ )
24678
+ RETURNING *
24679
+ `);
24680
+ const apiKey = rowToApiKey(rows[0]);
24681
+ return {
24682
+ ...toMasked(apiKey),
24683
+ key: plaintext
24684
+ };
24685
+ },
24686
+ // ── Lookup by hash ──────────────────────────────────────────
24687
+ async findByKeyHash(hash) {
24688
+ const rows = await exec(`
24689
+ SELECT * FROM ${TABLE$1}
24690
+ WHERE key_hash = '${esc(hash)}'
24691
+ LIMIT 1
24692
+ `);
24693
+ if (rows.length === 0) return null;
24694
+ return rowToApiKey(rows[0]);
24695
+ },
24696
+ // ── List all (masked) ───────────────────────────────────────
24697
+ async listApiKeys() {
24698
+ const rows = await exec(`
24699
+ SELECT * FROM ${TABLE$1}
24700
+ ORDER BY created_at DESC
24701
+ `);
24702
+ return rows.map((r) => toMasked(rowToApiKey(r)));
24703
+ },
24704
+ // ── Get by ID (masked) ──────────────────────────────────────
24705
+ async getApiKeyById(id) {
24706
+ const rows = await exec(`
24707
+ SELECT * FROM ${TABLE$1}
24708
+ WHERE id = '${esc(id)}'
24709
+ LIMIT 1
24710
+ `);
24711
+ if (rows.length === 0) return null;
24712
+ return toMasked(rowToApiKey(rows[0]));
24713
+ },
24714
+ // ── Update ──────────────────────────────────────────────────
24715
+ async updateApiKey(id, updates) {
24716
+ const setClauses = [];
24717
+ if (updates.name !== void 0) {
24718
+ setClauses.push(`name = '${esc(updates.name)}'`);
24719
+ }
24720
+ if (updates.permissions !== void 0) {
24721
+ setClauses.push(`permissions = '${esc(JSON.stringify(updates.permissions))}'::jsonb`);
24722
+ }
24723
+ if (updates.rate_limit !== void 0) {
24724
+ setClauses.push(updates.rate_limit !== null ? `rate_limit = ${updates.rate_limit}` : "rate_limit = NULL");
24725
+ }
24726
+ if (updates.expires_at !== void 0) {
24727
+ setClauses.push(updates.expires_at !== null ? `expires_at = '${esc(updates.expires_at)}'` : "expires_at = NULL");
24728
+ }
24729
+ if (setClauses.length === 0) {
24730
+ return this.getApiKeyById(id);
24731
+ }
24732
+ setClauses.push("updated_at = NOW()");
24733
+ const rows = await exec(`
24734
+ UPDATE ${TABLE$1}
24735
+ SET ${setClauses.join(", ")}
24736
+ WHERE id = '${esc(id)}'
24737
+ RETURNING *
24738
+ `);
24739
+ if (rows.length === 0) return null;
24740
+ return toMasked(rowToApiKey(rows[0]));
24741
+ },
24742
+ // ── Revoke (soft-delete) ────────────────────────────────────
24743
+ async revokeApiKey(id) {
24744
+ const rows = await exec(`
24745
+ UPDATE ${TABLE$1}
24746
+ SET revoked_at = NOW(), updated_at = NOW()
24747
+ WHERE id = '${esc(id)}' AND revoked_at IS NULL
24748
+ RETURNING id
24749
+ `);
24750
+ return rows.length > 0;
24751
+ },
24752
+ // ── Touch last_used_at ──────────────────────────────────────
24753
+ async updateLastUsed(id) {
24754
+ try {
24755
+ await exec(`
24756
+ UPDATE ${TABLE$1}
24757
+ SET last_used_at = NOW()
24758
+ WHERE id = '${esc(id)}'
24759
+ `);
24760
+ } catch (err) {
24761
+ logger.error("[api-key-store] Failed to update last_used_at", {
24762
+ error: err
24763
+ });
24764
+ }
24765
+ }
24766
+ };
24767
+ }
24768
+ function validatePermissions(permissions) {
24769
+ if (!Array.isArray(permissions)) return false;
24770
+ for (const perm of permissions) {
24771
+ if (typeof perm !== "object" || perm === null) return false;
24772
+ if (typeof perm.collection !== "string" || perm.collection.length === 0) return false;
24773
+ if (!Array.isArray(perm.operations) || perm.operations.length === 0) return false;
24774
+ const validOps = /* @__PURE__ */ new Set(["read", "write", "delete"]);
24775
+ for (const op of perm.operations) {
24776
+ if (!validOps.has(op)) return false;
24777
+ }
24778
+ }
24779
+ return true;
24780
+ }
24781
+ function createApiKeyRoutes(options2) {
24782
+ const {
24783
+ store,
24784
+ serviceKey
24785
+ } = options2;
24786
+ const router = new Hono();
24787
+ router.onError(errorHandler);
24788
+ router.use("/*", createRequireAuth({
24789
+ serviceKey
24790
+ }));
24791
+ router.use("/*", requireAdmin);
24792
+ router.get("/", async (c) => {
24793
+ const keys2 = await store.listApiKeys();
24794
+ return c.json({
24795
+ keys: keys2
24796
+ });
24797
+ });
24798
+ router.post("/", async (c) => {
24799
+ const body = await c.req.json();
24800
+ const {
24801
+ name: name2,
24802
+ permissions,
24803
+ rate_limit,
24804
+ expires_at
24805
+ } = body;
24806
+ if (!name2 || typeof name2 !== "string" || name2.trim().length === 0) {
24807
+ throw ApiError.badRequest("Name is required", "INVALID_INPUT");
24808
+ }
24809
+ if (!validatePermissions(permissions)) {
24810
+ throw ApiError.badRequest("Permissions must be an array of { collection: string, operations: ('read' | 'write' | 'delete')[] }", "INVALID_INPUT");
24811
+ }
24812
+ if (rate_limit !== void 0 && rate_limit !== null) {
24813
+ if (typeof rate_limit !== "number" || rate_limit < 1 || !Number.isInteger(rate_limit)) {
24814
+ throw ApiError.badRequest("rate_limit must be a positive integer or null", "INVALID_INPUT");
24815
+ }
24816
+ }
24817
+ if (expires_at !== void 0 && expires_at !== null) {
24818
+ const parsed = new Date(expires_at);
24819
+ if (isNaN(parsed.getTime())) {
24820
+ throw ApiError.badRequest("expires_at must be a valid ISO-8601 date", "INVALID_INPUT");
24821
+ }
24822
+ if (parsed <= /* @__PURE__ */ new Date()) {
24823
+ throw ApiError.badRequest("expires_at must be in the future", "INVALID_INPUT");
24824
+ }
24825
+ }
24826
+ const user = c.get("user");
24827
+ const createdBy = user && typeof user === "object" && "userId" in user ? user.userId : "unknown";
24828
+ const request = {
24829
+ name: name2.trim(),
24830
+ permissions,
24831
+ rate_limit: rate_limit ?? null,
24832
+ expires_at: expires_at ?? null
24833
+ };
24834
+ const keyWithSecret = await store.createApiKey(request, createdBy);
24835
+ return c.json({
24836
+ key: keyWithSecret
24837
+ }, 201);
24838
+ });
24839
+ router.get("/:id", async (c) => {
24840
+ const id = c.req.param("id");
24841
+ const key = await store.getApiKeyById(id);
24842
+ if (!key) {
24843
+ throw ApiError.notFound("API key not found");
24844
+ }
24845
+ return c.json({
24846
+ key
24847
+ });
24848
+ });
24849
+ router.put("/:id", async (c) => {
24850
+ const id = c.req.param("id");
24851
+ const body = await c.req.json();
24852
+ const {
24853
+ name: name2,
24854
+ permissions,
24855
+ rate_limit,
24856
+ expires_at
24857
+ } = body;
24858
+ if (name2 !== void 0) {
24859
+ if (typeof name2 !== "string" || name2.trim().length === 0) {
24860
+ throw ApiError.badRequest("Name must be a non-empty string", "INVALID_INPUT");
24861
+ }
24862
+ }
24863
+ if (permissions !== void 0) {
24864
+ if (!validatePermissions(permissions)) {
24865
+ throw ApiError.badRequest("Permissions must be an array of { collection: string, operations: ('read' | 'write' | 'delete')[] }", "INVALID_INPUT");
24866
+ }
24867
+ }
24868
+ if (rate_limit !== void 0 && rate_limit !== null) {
24869
+ if (typeof rate_limit !== "number" || rate_limit < 1 || !Number.isInteger(rate_limit)) {
24870
+ throw ApiError.badRequest("rate_limit must be a positive integer or null", "INVALID_INPUT");
24871
+ }
24872
+ }
24873
+ if (expires_at !== void 0 && expires_at !== null) {
24874
+ const parsed = new Date(expires_at);
24875
+ if (isNaN(parsed.getTime())) {
24876
+ throw ApiError.badRequest("expires_at must be a valid ISO-8601 date", "INVALID_INPUT");
24877
+ }
24878
+ }
24879
+ const updates = {};
24880
+ if (name2 !== void 0) updates.name = name2.trim();
24881
+ if (permissions !== void 0) updates.permissions = permissions;
24882
+ if (rate_limit !== void 0) updates.rate_limit = rate_limit;
24883
+ if (expires_at !== void 0) updates.expires_at = expires_at;
24884
+ const key = await store.updateApiKey(id, updates);
24885
+ if (!key) {
24886
+ throw ApiError.notFound("API key not found");
24887
+ }
24888
+ return c.json({
24889
+ key
24890
+ });
24891
+ });
24892
+ router.delete("/:id", async (c) => {
24893
+ const id = c.req.param("id");
24894
+ const revoked = await store.revokeApiKey(id);
24895
+ if (!revoked) {
24896
+ throw ApiError.notFound("API key not found or already revoked");
24897
+ }
24898
+ return c.json({
24899
+ success: true
24900
+ });
24901
+ });
24902
+ return router;
24903
+ }
23454
24904
  function createCustomAuthAdapter(options2) {
23455
24905
  const defaultCapabilities = {
23456
24906
  hasBuiltInAuthRoutes: false,
@@ -24003,6 +25453,409 @@ class S3StorageController {
24003
25453
  };
24004
25454
  }
24005
25455
  }
25456
+ let sharpFactory;
25457
+ async function getSharp() {
25458
+ if (!sharpFactory) {
25459
+ try {
25460
+ const mod = await import("sharp");
25461
+ sharpFactory = mod.default;
25462
+ } catch (err) {
25463
+ throw new Error("Failed to load optional 'sharp' dependency for image transformation.");
25464
+ }
25465
+ }
25466
+ if (!sharpFactory) {
25467
+ throw new Error("Failed to load optional 'sharp' dependency for image transformation.");
25468
+ }
25469
+ return sharpFactory;
25470
+ }
25471
+ const MAX_DIMENSION = 4096;
25472
+ const MAX_QUALITY = 100;
25473
+ const MIN_QUALITY = 1;
25474
+ const VALID_FORMATS = /* @__PURE__ */ new Set(["webp", "avif", "jpeg", "png"]);
25475
+ const VALID_FITS = /* @__PURE__ */ new Set(["cover", "contain", "fill", "inside", "outside"]);
25476
+ function parseTransformOptions(query) {
25477
+ const opts = {};
25478
+ let hasTransform = false;
25479
+ if (query.width) {
25480
+ const w2 = parseInt(query.width, 10);
25481
+ if (!Number.isNaN(w2) && w2 > 0) {
25482
+ opts.width = Math.min(w2, MAX_DIMENSION);
25483
+ hasTransform = true;
25484
+ }
25485
+ }
25486
+ if (query.height) {
25487
+ const h2 = parseInt(query.height, 10);
25488
+ if (!Number.isNaN(h2) && h2 > 0) {
25489
+ opts.height = Math.min(h2, MAX_DIMENSION);
25490
+ hasTransform = true;
25491
+ }
25492
+ }
25493
+ if (query.quality) {
25494
+ const q = parseInt(query.quality, 10);
25495
+ if (!Number.isNaN(q)) {
25496
+ opts.quality = Math.min(Math.max(q, MIN_QUALITY), MAX_QUALITY);
25497
+ hasTransform = true;
25498
+ }
25499
+ }
25500
+ if (query.format && VALID_FORMATS.has(query.format)) {
25501
+ opts.format = query.format;
25502
+ hasTransform = true;
25503
+ }
25504
+ if (query.fit && VALID_FITS.has(query.fit)) {
25505
+ opts.fit = query.fit;
25506
+ hasTransform = true;
25507
+ }
25508
+ return hasTransform ? opts : null;
25509
+ }
25510
+ const FORMAT_CONTENT_TYPES = {
25511
+ webp: "image/webp",
25512
+ avif: "image/avif",
25513
+ jpeg: "image/jpeg",
25514
+ png: "image/png"
25515
+ };
25516
+ function isTransformableImage(contentType) {
25517
+ return contentType.startsWith("image/") && !contentType.includes("svg") && !contentType.includes("gif");
25518
+ }
25519
+ async function transformImage(buffer, options2) {
25520
+ const sharp = await getSharp();
25521
+ let pipeline = sharp(buffer);
25522
+ if (options2.width || options2.height) {
25523
+ pipeline = pipeline.resize({
25524
+ width: options2.width,
25525
+ height: options2.height,
25526
+ fit: options2.fit || "cover",
25527
+ withoutEnlargement: true
25528
+ });
25529
+ }
25530
+ const format = options2.format || "webp";
25531
+ const quality = options2.quality || 80;
25532
+ switch (format) {
25533
+ case "webp":
25534
+ pipeline = pipeline.webp({
25535
+ quality
25536
+ });
25537
+ break;
25538
+ case "avif":
25539
+ pipeline = pipeline.avif({
25540
+ quality
25541
+ });
25542
+ break;
25543
+ case "jpeg":
25544
+ pipeline = pipeline.jpeg({
25545
+ quality
25546
+ });
25547
+ break;
25548
+ case "png":
25549
+ pipeline = pipeline.png({
25550
+ quality
25551
+ });
25552
+ break;
25553
+ }
25554
+ const data = await pipeline.toBuffer();
25555
+ return {
25556
+ data,
25557
+ contentType: FORMAT_CONTENT_TYPES[format]
25558
+ };
25559
+ }
25560
+ class TransformCache {
25561
+ cache = /* @__PURE__ */ new Map();
25562
+ maxEntries;
25563
+ maxAgeMs;
25564
+ constructor(maxEntries = 500, maxAgeMs = 36e5) {
25565
+ this.maxEntries = maxEntries;
25566
+ this.maxAgeMs = maxAgeMs;
25567
+ }
25568
+ /** Build a deterministic cache key from file key + transform options. */
25569
+ buildKey(fileKey, options2) {
25570
+ return `${fileKey}::${JSON.stringify(options2)}`;
25571
+ }
25572
+ get(cacheKey) {
25573
+ const entry = this.cache.get(cacheKey);
25574
+ if (!entry) return null;
25575
+ if (Date.now() - entry.timestamp > this.maxAgeMs) {
25576
+ this.cache.delete(cacheKey);
25577
+ return null;
25578
+ }
25579
+ this.cache.delete(cacheKey);
25580
+ this.cache.set(cacheKey, entry);
25581
+ return {
25582
+ data: entry.data,
25583
+ contentType: entry.contentType
25584
+ };
25585
+ }
25586
+ set(cacheKey, data, contentType) {
25587
+ if (this.cache.size >= this.maxEntries) {
25588
+ const oldest = this.cache.keys().next().value;
25589
+ if (oldest !== void 0) this.cache.delete(oldest);
25590
+ }
25591
+ this.cache.set(cacheKey, {
25592
+ data,
25593
+ contentType,
25594
+ timestamp: Date.now()
25595
+ });
25596
+ }
25597
+ }
25598
+ const MAX_UPLOAD_SIZE = 5 * 1024 * 1024 * 1024;
25599
+ const UPLOAD_EXPIRY_MS = 24 * 60 * 60 * 1e3;
25600
+ class TusHandler {
25601
+ constructor(storageBaseDir, storageController) {
25602
+ this.storageController = storageController;
25603
+ this.tusDir = join$1(storageBaseDir, ".tus-uploads");
25604
+ }
25605
+ uploads = /* @__PURE__ */ new Map();
25606
+ tusDir;
25607
+ cleanupTimer;
25608
+ /** Ensure the temp directory exists. */
25609
+ async ensureDir() {
25610
+ if (!existsSync(this.tusDir)) {
25611
+ await mkdir$1(this.tusDir, {
25612
+ recursive: true
25613
+ });
25614
+ }
25615
+ }
25616
+ /** Start periodic cleanup of stale uploads. */
25617
+ startCleanup() {
25618
+ if (this.cleanupTimer) return;
25619
+ this.cleanupTimer = setInterval(() => {
25620
+ void this.cleanupStale();
25621
+ }, 6e4);
25622
+ }
25623
+ /** Remove uploads that have been idle for longer than UPLOAD_EXPIRY_MS. */
25624
+ async cleanupStale() {
25625
+ const now = Date.now();
25626
+ for (const [id, upload] of this.uploads) {
25627
+ if (now - upload.createdAt > UPLOAD_EXPIRY_MS && !upload.completed) {
25628
+ try {
25629
+ await unlink$1(upload.filePath);
25630
+ } catch {
25631
+ }
25632
+ this.uploads.delete(id);
25633
+ }
25634
+ }
25635
+ }
25636
+ // -----------------------------------------------------------------------
25637
+ // TUS Metadata Parsing
25638
+ // -----------------------------------------------------------------------
25639
+ /**
25640
+ * Parse the `Upload-Metadata` header.
25641
+ *
25642
+ * Format: `key base64value,key2 base64value2`
25643
+ */
25644
+ parseMetadata(header) {
25645
+ const metadata = {};
25646
+ if (!header) return metadata;
25647
+ for (const pair of header.split(",")) {
25648
+ const trimmed = pair.trim();
25649
+ const spaceIdx = trimmed.indexOf(" ");
25650
+ if (spaceIdx === -1) {
25651
+ metadata[trimmed] = "";
25652
+ } else {
25653
+ const key = trimmed.substring(0, spaceIdx);
25654
+ const value = Buffer.from(trimmed.substring(spaceIdx + 1), "base64").toString("utf-8");
25655
+ metadata[key] = value;
25656
+ }
25657
+ }
25658
+ return metadata;
25659
+ }
25660
+ // -----------------------------------------------------------------------
25661
+ // Protocol Endpoints
25662
+ // -----------------------------------------------------------------------
25663
+ /** `OPTIONS /tus` — TUS capability discovery. */
25664
+ options() {
25665
+ return new Response(null, {
25666
+ status: 204,
25667
+ headers: {
25668
+ "Tus-Resumable": "1.0.0",
25669
+ "Tus-Version": "1.0.0",
25670
+ "Tus-Extension": "creation,termination",
25671
+ "Tus-Max-Size": String(MAX_UPLOAD_SIZE)
25672
+ }
25673
+ });
25674
+ }
25675
+ /** `POST /tus` — Create a new upload. */
25676
+ async create(c) {
25677
+ await this.ensureDir();
25678
+ const uploadLengthHeader = c.req.header("Upload-Length");
25679
+ if (!uploadLengthHeader) {
25680
+ return c.json({
25681
+ error: "Upload-Length header is required"
25682
+ }, 400);
25683
+ }
25684
+ const uploadLength = parseInt(uploadLengthHeader, 10);
25685
+ if (Number.isNaN(uploadLength) || uploadLength <= 0) {
25686
+ return c.json({
25687
+ error: "Invalid Upload-Length"
25688
+ }, 400);
25689
+ }
25690
+ if (uploadLength > MAX_UPLOAD_SIZE) {
25691
+ return c.json({
25692
+ error: `Upload-Length exceeds maximum of ${MAX_UPLOAD_SIZE} bytes`
25693
+ }, 413);
25694
+ }
25695
+ const metadata = this.parseMetadata(c.req.header("Upload-Metadata") || "");
25696
+ const id = randomUUID$1();
25697
+ const filePath = join$1(this.tusDir, id);
25698
+ await writeFile$1(filePath, Buffer.alloc(0));
25699
+ const upload = {
25700
+ id,
25701
+ size: uploadLength,
25702
+ offset: 0,
25703
+ metadata,
25704
+ createdAt: Date.now(),
25705
+ filePath,
25706
+ bucket: metadata.bucket || void 0,
25707
+ key: metadata.key || metadata.filename || void 0,
25708
+ completed: false
25709
+ };
25710
+ this.uploads.set(id, upload);
25711
+ const reqUrl = new URL(c.req.url);
25712
+ const location = `${reqUrl.origin}${reqUrl.pathname}/${id}`;
25713
+ return new Response(null, {
25714
+ status: 201,
25715
+ headers: {
25716
+ Location: location,
25717
+ "Tus-Resumable": "1.0.0",
25718
+ "Upload-Offset": "0"
25719
+ }
25720
+ });
25721
+ }
25722
+ /** `HEAD /tus/:id` — Query upload progress. */
25723
+ head(c, id) {
25724
+ const upload = this.uploads.get(id);
25725
+ if (!upload) {
25726
+ return c.json({
25727
+ error: "Upload not found"
25728
+ }, 404);
25729
+ }
25730
+ return new Response(null, {
25731
+ status: 200,
25732
+ headers: {
25733
+ "Tus-Resumable": "1.0.0",
25734
+ "Upload-Offset": String(upload.offset),
25735
+ "Upload-Length": String(upload.size),
25736
+ "Cache-Control": "no-store"
25737
+ }
25738
+ });
25739
+ }
25740
+ /** `PATCH /tus/:id` — Append data to an upload. */
25741
+ async patch(c, id) {
25742
+ const upload = this.uploads.get(id);
25743
+ if (!upload) {
25744
+ return c.json({
25745
+ error: "Upload not found"
25746
+ }, 404);
25747
+ }
25748
+ if (upload.completed) {
25749
+ return c.json({
25750
+ error: "Upload already completed"
25751
+ }, 400);
25752
+ }
25753
+ const offsetHeader = c.req.header("Upload-Offset");
25754
+ if (!offsetHeader) {
25755
+ return c.json({
25756
+ error: "Upload-Offset header is required"
25757
+ }, 400);
25758
+ }
25759
+ const offset = parseInt(offsetHeader, 10);
25760
+ if (offset !== upload.offset) {
25761
+ return c.json({
25762
+ error: "Offset mismatch"
25763
+ }, 409);
25764
+ }
25765
+ const contentType = c.req.header("Content-Type");
25766
+ if (contentType !== "application/offset+octet-stream") {
25767
+ return c.json({
25768
+ error: "Content-Type must be application/offset+octet-stream"
25769
+ }, 415);
25770
+ }
25771
+ const body = await c.req.arrayBuffer();
25772
+ const chunk = Buffer.from(body);
25773
+ if (upload.offset + chunk.length > upload.size) {
25774
+ return c.json({
25775
+ error: "Chunk exceeds declared Upload-Length"
25776
+ }, 413);
25777
+ }
25778
+ const fh = await open(upload.filePath, "a");
25779
+ try {
25780
+ await fh.write(chunk);
25781
+ } finally {
25782
+ await fh.close();
25783
+ }
25784
+ upload.offset += chunk.length;
25785
+ if (upload.offset >= upload.size) {
25786
+ await this.finalize(upload);
25787
+ }
25788
+ return new Response(null, {
25789
+ status: 204,
25790
+ headers: {
25791
+ "Tus-Resumable": "1.0.0",
25792
+ "Upload-Offset": String(upload.offset)
25793
+ }
25794
+ });
25795
+ }
25796
+ /** `DELETE /tus/:id` — Cancel and remove an upload. */
25797
+ async delete(c, id) {
25798
+ const upload = this.uploads.get(id);
25799
+ if (!upload) {
25800
+ return c.json({
25801
+ error: "Upload not found"
25802
+ }, 404);
25803
+ }
25804
+ try {
25805
+ await unlink$1(upload.filePath);
25806
+ } catch {
25807
+ }
25808
+ this.uploads.delete(id);
25809
+ return new Response(null, {
25810
+ status: 204,
25811
+ headers: {
25812
+ "Tus-Resumable": "1.0.0"
25813
+ }
25814
+ });
25815
+ }
25816
+ // -----------------------------------------------------------------------
25817
+ // Finalization
25818
+ // -----------------------------------------------------------------------
25819
+ /**
25820
+ * Move a completed upload into the storage controller.
25821
+ */
25822
+ async finalize(upload) {
25823
+ upload.completed = true;
25824
+ if (!this.storageController) {
25825
+ logger.warn("[TUS] Upload completed but no StorageController configured. Temp file remains:", {
25826
+ filePath: upload.filePath
25827
+ });
25828
+ return;
25829
+ }
25830
+ try {
25831
+ const {
25832
+ readFile: readFile2
25833
+ } = await import("fs/promises");
25834
+ const data = await readFile2(upload.filePath);
25835
+ const fileName = upload.key || upload.metadata.filename || upload.id;
25836
+ const mimeType = upload.metadata.contentType || upload.metadata.filetype || "application/octet-stream";
25837
+ const file = new File([data], fileName, {
25838
+ type: mimeType
25839
+ });
25840
+ await this.storageController.putObject({
25841
+ file,
25842
+ key: fileName,
25843
+ bucket: upload.bucket
25844
+ });
25845
+ try {
25846
+ await unlink$1(upload.filePath);
25847
+ } catch {
25848
+ }
25849
+ this.uploads.delete(upload.id);
25850
+ logger.info(`[TUS] Upload ${upload.id} finalized → ${fileName}`);
25851
+ } catch (err) {
25852
+ logger.error(`[TUS] Failed to finalize upload ${upload.id}`, {
25853
+ error: err
25854
+ });
25855
+ }
25856
+ }
25857
+ }
25858
+ const transformCache = new TransformCache();
24006
25859
  function extractWildcardPath(c) {
24007
25860
  const routePath = c.req.routePath;
24008
25861
  const prefix = routePath.replace("/*", "");
@@ -24066,6 +25919,7 @@ function createStorageRoutes(config) {
24066
25919
  throw ApiError.notFound("File not found");
24067
25920
  }
24068
25921
  const filePath = decodeURIComponent(rawPath);
25922
+ const transformOpts = parseTransformOptions(c.req.query());
24069
25923
  if (controller.getType() === "local") {
24070
25924
  const localController = controller;
24071
25925
  const {
@@ -24085,8 +25939,19 @@ function createStorageRoutes(config) {
24085
25939
  } catch {
24086
25940
  }
24087
25941
  }
24088
- c.header("Content-Type", contentType);
24089
25942
  const fileContent = fs$4.readFileSync(absolutePath);
25943
+ if (transformOpts && isTransformableImage(contentType)) {
25944
+ const cacheKey = transformCache.buildKey(filePath, transformOpts);
25945
+ let cached = transformCache.get(cacheKey);
25946
+ if (!cached) {
25947
+ cached = await transformImage(Buffer.from(fileContent), transformOpts);
25948
+ transformCache.set(cacheKey, cached.data, cached.contentType);
25949
+ }
25950
+ c.header("Content-Type", cached.contentType);
25951
+ c.header("Cache-Control", "public, max-age=31536000, immutable");
25952
+ return c.body(new Uint8Array(cached.data));
25953
+ }
25954
+ c.header("Content-Type", contentType);
24090
25955
  return c.body(new Uint8Array(fileContent));
24091
25956
  }
24092
25957
  const {
@@ -24097,7 +25962,20 @@ function createStorageRoutes(config) {
24097
25962
  if (!fileObject) {
24098
25963
  throw ApiError.notFound("File not found");
24099
25964
  }
24100
- c.header("Content-Type", fileObject.type || "application/octet-stream");
25965
+ const remoteContentType = fileObject.type || "application/octet-stream";
25966
+ if (transformOpts && isTransformableImage(remoteContentType)) {
25967
+ const cacheKey = transformCache.buildKey(filePath, transformOpts);
25968
+ let cached = transformCache.get(cacheKey);
25969
+ if (!cached) {
25970
+ const buf2 = Buffer.from(await fileObject.arrayBuffer());
25971
+ cached = await transformImage(buf2, transformOpts);
25972
+ transformCache.set(cacheKey, cached.data, cached.contentType);
25973
+ }
25974
+ c.header("Content-Type", cached.contentType);
25975
+ c.header("Cache-Control", "public, max-age=31536000, immutable");
25976
+ return c.body(new Uint8Array(cached.data));
25977
+ }
25978
+ c.header("Content-Type", remoteContentType);
24101
25979
  c.header("Cache-Control", "public, max-age=3600, immutable");
24102
25980
  const buf = await fileObject.arrayBuffer();
24103
25981
  return c.body(new Uint8Array(buf));
@@ -24193,6 +26071,14 @@ function createStorageRoutes(config) {
24193
26071
  message: "Folder created"
24194
26072
  }, 201);
24195
26073
  });
26074
+ const tusBaseDir = controller.getType() === "local" ? controller.getBasePath() : process.env.STORAGE_PATH || "./uploads";
26075
+ const tusHandler = new TusHandler(tusBaseDir, controller);
26076
+ tusHandler.startCleanup();
26077
+ router.options("/tus", (_c2) => tusHandler.options());
26078
+ router.post("/tus", writeAuthMiddleware, async (c) => tusHandler.create(c));
26079
+ router.get("/tus/:id", readAuthMiddleware, (c) => tusHandler.head(c, c.req.param("id")));
26080
+ router.patch("/tus/:id", writeAuthMiddleware, async (c) => tusHandler.patch(c, c.req.param("id")));
26081
+ router.delete("/tus/:id", writeAuthMiddleware, async (c) => tusHandler.delete(c, c.req.param("id")));
24196
26082
  return router;
24197
26083
  }
24198
26084
  const DEFAULT_STORAGE_ID = "(default)";
@@ -24416,6 +26302,13 @@ function createTransport$1(config) {
24416
26302
  } catch (e) {
24417
26303
  }
24418
26304
  }
26305
+ const getErrorField = (obj, field) => {
26306
+ const err = obj?.error;
26307
+ if (err && typeof err === "object" && err !== null && field in err) {
26308
+ return err[field];
26309
+ }
26310
+ return obj?.[field];
26311
+ };
24419
26312
  if (res.status === 401 && onUnauthorizedHandler) {
24420
26313
  const retried = await onUnauthorizedHandler();
24421
26314
  if (retried) {
@@ -24449,7 +26342,7 @@ function createTransport$1(config) {
24449
26342
  const method = init?.method || "GET";
24450
26343
  fallbackMessage = `Endpoint not found (${method} ${path2}). This usually means the collection is not registered on the backend, or the frontend API URL configuration (e.g. VITE_API_URL) is missing or pointing to the wrong host.`;
24451
26344
  }
24452
- throw new RebaseApiError(retryRes.status, retryBody?.error?.message || retryBody?.message || fallbackMessage || `Request failed with status ${retryRes.status}`, retryBody?.error?.code || retryBody?.code, retryBody?.error?.details || retryBody?.details);
26345
+ throw new RebaseApiError(retryRes.status, String(getErrorField(retryBody, "message") || fallbackMessage || `Request failed with status ${retryRes.status}`), getErrorField(retryBody, "code"), getErrorField(retryBody, "details"));
24453
26346
  }
24454
26347
  return retryBody;
24455
26348
  }
@@ -24460,7 +26353,7 @@ function createTransport$1(config) {
24460
26353
  const method = init?.method || "GET";
24461
26354
  fallbackMessage = `Endpoint not found (${method} ${path2}). This usually means the collection is not registered on the backend, or the frontend API URL configuration (e.g. VITE_API_URL) is missing or pointing to the wrong host.`;
24462
26355
  }
24463
- throw new RebaseApiError(res.status, body?.error?.message || body?.message || fallbackMessage || `Request failed with status ${res.status}`, body?.error?.code || body?.code, body?.error?.details || body?.details);
26356
+ throw new RebaseApiError(res.status, String(getErrorField(body, "message") || fallbackMessage || `Request failed with status ${res.status}`), getErrorField(body, "code"), getErrorField(body, "details"));
24464
26357
  }
24465
26358
  return body;
24466
26359
  }
@@ -25716,7 +27609,7 @@ const require$$9 = {
25716
27609
  const http = require$$0$6;
25717
27610
  const https = require$$1;
25718
27611
  const urllib$2 = require$$0$5;
25719
- const zlib = require$$11;
27612
+ const zlib = require$$3;
25720
27613
  const PassThrough$3 = require$$0$4.PassThrough;
25721
27614
  const Cookies2 = cookies;
25722
27615
  const packageData$7 = require$$9;
@@ -25951,9 +27844,9 @@ var fetchExports = fetch$1.exports;
25951
27844
  const util2 = require$$5;
25952
27845
  const fs2 = fs__default;
25953
27846
  const nmfetch2 = fetchExports;
25954
- const dns2 = require$$11;
27847
+ const dns2 = require$$4$1;
25955
27848
  const net2 = require$$0$7;
25956
- const os2 = require$$1$2;
27849
+ const os2 = require$$1$3;
25957
27850
  const DNS_TTL = 5 * 60 * 1e3;
25958
27851
  let networkInterfaces;
25959
27852
  try {
@@ -32099,7 +33992,7 @@ const urllib = require$$0$5;
32099
33992
  const packageData$6 = require$$9;
32100
33993
  const MailMessage2 = mailMessage;
32101
33994
  const net$1 = require$$0$7;
32102
- const dns = require$$11;
33995
+ const dns = require$$4$1;
32103
33996
  const crypto$3 = require$$0__default;
32104
33997
  class Mail extends EventEmitter$5 {
32105
33998
  constructor(transporter, options2, defaults) {
@@ -32535,7 +34428,7 @@ const packageInfo = require$$9;
32535
34428
  const EventEmitter$4 = require$$0$9.EventEmitter;
32536
34429
  const net = require$$0$7;
32537
34430
  const tls = require$$1$1;
32538
- const os = require$$1$2;
34431
+ const os = require$$1$3;
32539
34432
  const crypto$2 = require$$0__default;
32540
34433
  const DataStream2 = dataStream;
32541
34434
  const PassThrough = require$$0$4.PassThrough;
@@ -36723,7 +38616,7 @@ async function _initializeRebaseBackend(config) {
36723
38616
  oauthProviders,
36724
38617
  serviceKey,
36725
38618
  hooks: config.hooks,
36726
- overrides: safeAuthConfig.overrides
38619
+ authHooks: safeAuthConfig.hooks
36727
38620
  });
36728
38621
  }
36729
38622
  logger.info("Authentication initialized");
@@ -36790,73 +38683,73 @@ async function _initializeRebaseBackend(config) {
36790
38683
  if (safeAuthConfig.google?.clientId) {
36791
38684
  const {
36792
38685
  createGoogleProvider: createGoogleProvider2
36793
- } = await import("./index-BZoAtuqi.js");
38686
+ } = await import("./index-Cr1D21av.js");
36794
38687
  oauthProviders.push(createGoogleProvider2(safeAuthConfig.google));
36795
38688
  }
36796
38689
  if (safeAuthConfig.linkedin?.clientId && safeAuthConfig.linkedin?.clientSecret) {
36797
38690
  const {
36798
38691
  createLinkedinProvider: createLinkedinProvider2
36799
- } = await import("./index-BZoAtuqi.js");
38692
+ } = await import("./index-Cr1D21av.js");
36800
38693
  oauthProviders.push(createLinkedinProvider2(safeAuthConfig.linkedin));
36801
38694
  }
36802
38695
  if (safeAuthConfig.github?.clientId && safeAuthConfig.github?.clientSecret) {
36803
38696
  const {
36804
38697
  createGitHubProvider: createGitHubProvider2
36805
- } = await import("./index-BZoAtuqi.js");
38698
+ } = await import("./index-Cr1D21av.js");
36806
38699
  oauthProviders.push(createGitHubProvider2(safeAuthConfig.github));
36807
38700
  }
36808
38701
  if (safeAuthConfig.microsoft?.clientId && safeAuthConfig.microsoft?.clientSecret) {
36809
38702
  const {
36810
38703
  createMicrosoftProvider: createMicrosoftProvider2
36811
- } = await import("./index-BZoAtuqi.js");
38704
+ } = await import("./index-Cr1D21av.js");
36812
38705
  oauthProviders.push(createMicrosoftProvider2(safeAuthConfig.microsoft));
36813
38706
  }
36814
38707
  if (safeAuthConfig.apple?.clientId && safeAuthConfig.apple?.teamId && safeAuthConfig.apple?.keyId && safeAuthConfig.apple?.privateKey) {
36815
38708
  const {
36816
38709
  createAppleProvider: createAppleProvider2
36817
- } = await import("./index-BZoAtuqi.js");
38710
+ } = await import("./index-Cr1D21av.js");
36818
38711
  oauthProviders.push(createAppleProvider2(safeAuthConfig.apple));
36819
38712
  }
36820
38713
  if (safeAuthConfig.facebook?.clientId && safeAuthConfig.facebook?.clientSecret) {
36821
38714
  const {
36822
38715
  createFacebookProvider: createFacebookProvider2
36823
- } = await import("./index-BZoAtuqi.js");
38716
+ } = await import("./index-Cr1D21av.js");
36824
38717
  oauthProviders.push(createFacebookProvider2(safeAuthConfig.facebook));
36825
38718
  }
36826
38719
  if (safeAuthConfig.twitter?.clientId && safeAuthConfig.twitter?.clientSecret) {
36827
38720
  const {
36828
38721
  createTwitterProvider: createTwitterProvider2
36829
- } = await import("./index-BZoAtuqi.js");
38722
+ } = await import("./index-Cr1D21av.js");
36830
38723
  oauthProviders.push(createTwitterProvider2(safeAuthConfig.twitter));
36831
38724
  }
36832
38725
  if (safeAuthConfig.discord?.clientId && safeAuthConfig.discord?.clientSecret) {
36833
38726
  const {
36834
38727
  createDiscordProvider: createDiscordProvider2
36835
- } = await import("./index-BZoAtuqi.js");
38728
+ } = await import("./index-Cr1D21av.js");
36836
38729
  oauthProviders.push(createDiscordProvider2(safeAuthConfig.discord));
36837
38730
  }
36838
38731
  if (safeAuthConfig.gitlab?.clientId && safeAuthConfig.gitlab?.clientSecret) {
36839
38732
  const {
36840
38733
  createGitLabProvider: createGitLabProvider2
36841
- } = await import("./index-BZoAtuqi.js");
38734
+ } = await import("./index-Cr1D21av.js");
36842
38735
  oauthProviders.push(createGitLabProvider2(safeAuthConfig.gitlab));
36843
38736
  }
36844
38737
  if (safeAuthConfig.bitbucket?.clientId && safeAuthConfig.bitbucket?.clientSecret) {
36845
38738
  const {
36846
38739
  createBitbucketProvider: createBitbucketProvider2
36847
- } = await import("./index-BZoAtuqi.js");
38740
+ } = await import("./index-Cr1D21av.js");
36848
38741
  oauthProviders.push(createBitbucketProvider2(safeAuthConfig.bitbucket));
36849
38742
  }
36850
38743
  if (safeAuthConfig.slack?.clientId && safeAuthConfig.slack?.clientSecret) {
36851
38744
  const {
36852
38745
  createSlackProvider: createSlackProvider2
36853
- } = await import("./index-BZoAtuqi.js");
38746
+ } = await import("./index-Cr1D21av.js");
36854
38747
  oauthProviders.push(createSlackProvider2(safeAuthConfig.slack));
36855
38748
  }
36856
38749
  if (safeAuthConfig.spotify?.clientId && safeAuthConfig.spotify?.clientSecret) {
36857
38750
  const {
36858
38751
  createSpotifyProvider: createSpotifyProvider2
36859
- } = await import("./index-BZoAtuqi.js");
38752
+ } = await import("./index-Cr1D21av.js");
36860
38753
  oauthProviders.push(createSpotifyProvider2(safeAuthConfig.spotify));
36861
38754
  }
36862
38755
  authAdapter = createBuiltinAuthAdapter({
@@ -36868,7 +38761,7 @@ async function _initializeRebaseBackend(config) {
36868
38761
  oauthProviders,
36869
38762
  serviceKey,
36870
38763
  hooks: config.hooks,
36871
- overrides: safeAuthConfig.overrides
38764
+ authHooks: safeAuthConfig.hooks
36872
38765
  });
36873
38766
  }
36874
38767
  if (authAdapter.createAuthRoutes) {
@@ -36890,6 +38783,21 @@ async function _initializeRebaseBackend(config) {
36890
38783
  }
36891
38784
  }
36892
38785
  }
38786
+ let apiKeyStore;
38787
+ const apiKeyStoreResult = createApiKeyStore(defaultDriver);
38788
+ if (apiKeyStoreResult) {
38789
+ apiKeyStore = apiKeyStoreResult;
38790
+ await apiKeyStore.ensureTable();
38791
+ logger.info("Service API Keys initialized");
38792
+ const apiKeyRoutes = createApiKeyRoutes({
38793
+ store: apiKeyStore,
38794
+ serviceKey
38795
+ });
38796
+ config.app.route(`${basePath}/admin/api-keys`, apiKeyRoutes);
38797
+ logger.info("API key admin routes mounted", {
38798
+ path: `${basePath}/admin/api-keys`
38799
+ });
38800
+ }
36893
38801
  if (config.collectionsDir) {
36894
38802
  if (process.env.NODE_ENV !== "production") {
36895
38803
  const {
@@ -36940,15 +38848,20 @@ async function _initializeRebaseBackend(config) {
36940
38848
  dataRouter.use("/*", createAdapterAuthMiddleware({
36941
38849
  adapter: authAdapter,
36942
38850
  driver: defaultDriver,
36943
- requireAuth: dataRequireAuth
38851
+ requireAuth: dataRequireAuth,
38852
+ apiKeyStore
36944
38853
  }));
36945
38854
  } else {
36946
38855
  dataRouter.use("/*", createAuthMiddleware({
36947
38856
  driver: defaultDriver,
36948
38857
  requireAuth: dataRequireAuth,
36949
- serviceKey
38858
+ serviceKey,
38859
+ apiKeyStore
36950
38860
  }));
36951
38861
  }
38862
+ if (apiKeyStore) {
38863
+ dataRouter.use("/*", createApiKeyRateLimiter());
38864
+ }
36952
38865
  if (historyConfigResult && historyConfigResult.historyService) {
36953
38866
  const historyRoutes = createHistoryRoutes({
36954
38867
  historyService: historyConfigResult.historyService,
@@ -37042,13 +38955,15 @@ async function _initializeRebaseBackend(config) {
37042
38955
  functionsRouter.use("/*", createAdapterAuthMiddleware({
37043
38956
  adapter: authAdapter,
37044
38957
  driver: defaultDriver,
37045
- requireAuth: functionsRequireAuth
38958
+ requireAuth: functionsRequireAuth,
38959
+ apiKeyStore
37046
38960
  }));
37047
38961
  } else {
37048
38962
  functionsRouter.use("/*", createAuthMiddleware({
37049
38963
  driver: defaultDriver,
37050
38964
  requireAuth: functionsRequireAuth,
37051
- serviceKey
38965
+ serviceKey,
38966
+ apiKeyStore
37052
38967
  }));
37053
38968
  }
37054
38969
  const fnRoutes = createFunctionRoutes2(loadedFunctions);
@@ -47039,7 +48954,7 @@ class AstSchemaEditor {
47039
48954
  return "undefined";
47040
48955
  }
47041
48956
  if (typeof obj === "string") {
47042
- return `"${obj.replace(/"/g, '\\"')}"`;
48957
+ return JSON.stringify(obj);
47043
48958
  }
47044
48959
  if (typeof obj === "number" || typeof obj === "boolean") {
47045
48960
  return String(obj);
@@ -47070,7 +48985,7 @@ ${indent2}]`;
47070
48985
  const kind = init.getKind();
47071
48986
  const isCode = kind === SyntaxKind.ArrowFunction || kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.Identifier || kind === SyntaxKind.CallExpression || kind === SyntaxKind.JsxElement;
47072
48987
  if (isCode || name2 === "target" || name2 === "callbacks" || name2 === "permissions" || name2 === "securityRules") {
47073
- const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name2) ? name2 : `"${name2}"`;
48988
+ const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name2) ? name2 : JSON.stringify(name2);
47074
48989
  preservedProps.push(`${keyStr}: ${init.getText()}`);
47075
48990
  }
47076
48991
  }
@@ -47080,7 +48995,7 @@ ${indent2}]`;
47080
48995
  }
47081
48996
  if (keys2.length === 0 && preservedProps.length === 0) return "{}";
47082
48997
  const props = keys2.map((key) => {
47083
- const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : `"${key}"`;
48998
+ const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
47084
48999
  let childAstNode;
47085
49000
  if (oldAstNode && typeof record[key] === "object" && record[key] !== null && !Array.isArray(record[key])) {
47086
49001
  const oldProp = oldAstNode.getProperty((p) => "getName" in p && typeof p.getName === "function" && (p.getName() === key || p.getName() === `"${key}"` || p.getName() === `'${key}'`));
@@ -47122,7 +49037,7 @@ ${indent2}}`;
47122
49037
  }
47123
49038
  } else {
47124
49039
  propsObj.addPropertyAssignment({
47125
- name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey : `"${propertyKey}"`,
49040
+ name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey : JSON.stringify(propertyKey),
47126
49041
  initializer: newInitializer
47127
49042
  });
47128
49043
  }
@@ -48193,7 +50108,7 @@ async function loadFunctionsFromDirectory(directory) {
48193
50108
  const mod = await dynamicImport(fileUrl);
48194
50109
  const exported = mod.default;
48195
50110
  if (!exported) {
48196
- console.warn(`[functions] ${file}: no default export. Skipping.`);
50111
+ logger.warn(`[functions] ${file}: no default export. Skipping.`);
48197
50112
  continue;
48198
50113
  }
48199
50114
  if (isHonoLike(exported)) {
@@ -48202,7 +50117,7 @@ async function loadFunctionsFromDirectory(directory) {
48202
50117
  name: name2,
48203
50118
  app: exported
48204
50119
  });
48205
- console.log(`⚡ Loaded function route: ${name2}`);
50120
+ logger.info(`⚡ Loaded function route: ${name2}`);
48206
50121
  continue;
48207
50122
  }
48208
50123
  if (typeof exported === "function") {
@@ -48213,20 +50128,20 @@ async function loadFunctionsFromDirectory(directory) {
48213
50128
  name: name2,
48214
50129
  app: result
48215
50130
  });
48216
- console.log(`⚡ Loaded function route: ${name2}`);
50131
+ logger.info(`⚡ Loaded function route: ${name2}`);
48217
50132
  continue;
48218
50133
  }
48219
50134
  }
48220
50135
  const exportType = typeof exported;
48221
50136
  const keys2 = exported && typeof exported === "object" ? Object.getOwnPropertyNames(Object.getPrototypeOf(exported)).slice(0, 10).join(", ") : "N/A";
48222
- console.warn(`[functions] ${file}: default export is not a Hono app or factory. Skipping.
50137
+ logger.warn(`[functions] ${file}: default export is not a Hono app or factory. Skipping.
48223
50138
  export type: ${exportType}${exported?.constructor?.name ? ` (${exported.constructor.name})` : ""}
48224
50139
  prototype methods: ${keys2}
48225
50140
  Hint: ensure the function exports a Hono app created with the same hono version as the server.
48226
50141
  The loader checks for .fetch() and .routes — any Hono-compatible app will work.`);
48227
50142
  } catch (err) {
48228
50143
  const message = err instanceof Error ? err.message : String(err);
48229
- console.error(`[functions] Failed to load ${file}: ${message}`);
50144
+ logger.error(`[functions] Failed to load ${file}: ${message}`);
48230
50145
  }
48231
50146
  }
48232
50147
  }
@@ -48275,12 +50190,12 @@ async function loadCronJobsFromDirectory(directory) {
48275
50190
  const mod = await dynamicImport(fileUrl);
48276
50191
  const exported = mod.default;
48277
50192
  if (!exported || typeof exported !== "object") {
48278
- console.warn(`[cron] ${file}: no valid default export. Skipping.`);
50193
+ logger.warn(`[cron] ${file}: no valid default export. Skipping.`);
48279
50194
  continue;
48280
50195
  }
48281
50196
  const def = exported;
48282
50197
  if (typeof def.schedule !== "string" || typeof def.handler !== "function") {
48283
- console.warn(`[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`);
50198
+ logger.warn(`[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`);
48284
50199
  continue;
48285
50200
  }
48286
50201
  const id = path$3.basename(file, path$3.extname(file));
@@ -48296,10 +50211,10 @@ async function loadCronJobsFromDirectory(directory) {
48296
50211
  id,
48297
50212
  definition
48298
50213
  });
48299
- console.log(`⏰ Loaded cron job: ${id} (${definition.schedule})`);
50214
+ logger.info(`⏰ Loaded cron job: ${id} (${definition.schedule})`);
48300
50215
  } catch (err) {
48301
50216
  const message = err instanceof Error ? err.message : String(err);
48302
- console.error(`[cron] Failed to load ${file}: ${message}`);
50217
+ logger.error(`[cron] Failed to load ${file}: ${message}`);
48303
50218
  }
48304
50219
  }
48305
50220
  }
@@ -48454,12 +50369,12 @@ class CronScheduler {
48454
50369
  for (const loaded of loadedJobs) {
48455
50370
  const validation = validateCronExpression(loaded.definition.schedule);
48456
50371
  if (!validation.valid) {
48457
- console.error(`[cron] Rejecting job "${loaded.id}": invalid schedule "${loaded.definition.schedule}" — ${validation.reason}`);
50372
+ logger.error(`[cron] Rejecting job "${loaded.id}": invalid schedule "${loaded.definition.schedule}" — ${validation.reason}`);
48458
50373
  continue;
48459
50374
  }
48460
50375
  const existing = this.jobs.get(loaded.id);
48461
50376
  if (existing) {
48462
- console.warn(`[cron] Duplicate cron job id: "${loaded.id}". Overwriting.`);
50377
+ logger.warn(`[cron] Duplicate cron job id: "${loaded.id}". Overwriting.`);
48463
50378
  this.stopJob(loaded.id);
48464
50379
  }
48465
50380
  const enabled = loaded.definition.enabled !== false;
@@ -48497,7 +50412,9 @@ class CronScheduler {
48497
50412
  }
48498
50413
  }
48499
50414
  }).catch((err) => {
48500
- console.warn("[cron] Failed to seed job stats from database:", err);
50415
+ logger.warn("[cron] Failed to seed job stats from database", {
50416
+ error: err
50417
+ });
48501
50418
  });
48502
50419
  }
48503
50420
  for (const [id, job] of this.jobs) {
@@ -48505,7 +50422,7 @@ class CronScheduler {
48505
50422
  this.scheduleNext(id);
48506
50423
  }
48507
50424
  }
48508
- console.log(`⏰ Cron scheduler started with ${this.jobs.size} job(s)`);
50425
+ logger.info(`⏰ Cron scheduler started with ${this.jobs.size} job(s)`);
48509
50426
  }
48510
50427
  /**
48511
50428
  * Stop the scheduler and clear all timers.
@@ -48579,7 +50496,7 @@ class CronScheduler {
48579
50496
  const job = this.jobs.get(id);
48580
50497
  if (!job) return void 0;
48581
50498
  if (job.executing) {
48582
- console.warn(`[cron] Skipping manual trigger of "${id}" — already executing`);
50499
+ logger.warn(`[cron] Skipping manual trigger of "${id}" — already executing`);
48583
50500
  const logEntry = {
48584
50501
  jobId: id,
48585
50502
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -48623,7 +50540,7 @@ class CronScheduler {
48623
50540
  const timer = setTimeout(async () => {
48624
50541
  if (!job.enabled || !this.started) return;
48625
50542
  if (job.executing) {
48626
- console.warn(`[cron] Skipping scheduled run of "${id}" — still executing from previous run`);
50543
+ logger.warn(`[cron] Skipping scheduled run of "${id}" — still executing from previous run`);
48627
50544
  this.scheduleNext(id);
48628
50545
  return;
48629
50546
  }
@@ -48637,7 +50554,9 @@ class CronScheduler {
48637
50554
  }
48638
50555
  job.timerId = timer;
48639
50556
  } catch (err) {
48640
- console.error(`[cron] Failed to schedule "${id}":`, err);
50557
+ logger.error(`[cron] Failed to schedule "${id}"`, {
50558
+ error: err
50559
+ });
48641
50560
  job.state = "error";
48642
50561
  job.lastError = err instanceof Error ? err.message : String(err);
48643
50562
  }
@@ -48722,13 +50641,15 @@ class CronScheduler {
48722
50641
  }
48723
50642
  if (this.store) {
48724
50643
  this.store.insertLog(logEntry).catch((persistErr) => {
48725
- console.error(`[cron] Failed to persist log for "${job.id}":`, persistErr);
50644
+ logger.error(`[cron] Failed to persist log for "${job.id}"`, {
50645
+ error: persistErr
50646
+ });
48726
50647
  });
48727
50648
  }
48728
50649
  if (success) {
48729
- console.log(`✅ [cron] "${job.id}" completed in ${durationMs}ms`);
50650
+ logger.info(`✅ [cron] "${job.id}" completed in ${durationMs}ms`);
48730
50651
  } else {
48731
- console.error(`❌ [cron] "${job.id}" failed in ${durationMs}ms: ${error2}`);
50652
+ logger.error(`❌ [cron] "${job.id}" failed in ${durationMs}ms: ${error2}`);
48732
50653
  }
48733
50654
  return logEntry;
48734
50655
  }
@@ -48846,7 +50767,7 @@ const TABLE = "rebase.cron_logs";
48846
50767
  function createCronStore(driver) {
48847
50768
  const admin = driver.admin;
48848
50769
  if (!isSQLAdmin(admin)) {
48849
- console.warn("⚠️ [cron-store] DataDriver does not support SQL admin — cron logs will not be persisted.");
50770
+ logger.warn("⚠️ [cron-store] DataDriver does not support SQL admin — cron logs will not be persisted.");
48850
50771
  return void 0;
48851
50772
  }
48852
50773
  const exec = admin.executeSql.bind(admin);
@@ -48872,10 +50793,12 @@ function createCronStore(driver) {
48872
50793
  CREATE INDEX IF NOT EXISTS idx_cron_logs_job
48873
50794
  ON ${TABLE}(job_id, started_at DESC)
48874
50795
  `);
48875
- console.log("✅ Cron logs table ready");
50796
+ logger.info("✅ Cron logs table ready");
48876
50797
  } catch (err) {
48877
- console.error("❌ Failed to create cron logs table:", err);
48878
- console.warn("⚠️ Continuing without cron log persistence.");
50798
+ logger.error("❌ Failed to create cron logs table", {
50799
+ error: err
50800
+ });
50801
+ logger.warn("⚠️ Continuing without cron log persistence.");
48879
50802
  }
48880
50803
  },
48881
50804
  async insertLog(entry) {
@@ -48898,7 +50821,9 @@ function createCronStore(driver) {
48898
50821
  )
48899
50822
  `);
48900
50823
  } catch (err) {
48901
- console.error(`[cron-store] Failed to persist log for "${entry.jobId}":`, err);
50824
+ logger.error(`[cron-store] Failed to persist log for "${entry.jobId}"`, {
50825
+ error: err
50826
+ });
48902
50827
  }
48903
50828
  },
48904
50829
  async fetchLogs(jobId, limit = 50) {
@@ -48912,7 +50837,9 @@ function createCronStore(driver) {
48912
50837
  `);
48913
50838
  return rows.map(rowToLogEntry);
48914
50839
  } catch (err) {
48915
- console.error(`[cron-store] Failed to fetch logs for "${jobId}":`, err);
50840
+ logger.error(`[cron-store] Failed to fetch logs for "${jobId}"`, {
50841
+ error: err
50842
+ });
48916
50843
  return [];
48917
50844
  }
48918
50845
  },
@@ -48936,7 +50863,9 @@ function createCronStore(driver) {
48936
50863
  });
48937
50864
  }
48938
50865
  } catch (err) {
48939
- console.error("[cron-store] Failed to fetch job stats:", err);
50866
+ logger.error("[cron-store] Failed to fetch job stats", {
50867
+ error: err
50868
+ });
48940
50869
  }
48941
50870
  return stats;
48942
50871
  }
@@ -48967,8 +50896,8 @@ function serveSPA(app, config) {
48967
50896
  indexFile = "index.html"
48968
50897
  } = config;
48969
50898
  if (!fs$4.existsSync(frontendPath)) {
48970
- console.warn(`⚠️ Frontend build path does not exist: ${frontendPath}`);
48971
- console.warn(" SPA serving is disabled. Build your frontend first.");
50899
+ logger.warn(`⚠️ Frontend build path does not exist: ${frontendPath}`);
50900
+ logger.warn(" SPA serving is disabled. Build your frontend first.");
48972
50901
  return;
48973
50902
  }
48974
50903
  app.use("/*", serveStatic({
@@ -48981,13 +50910,13 @@ function serveSPA(app, config) {
48981
50910
  }
48982
50911
  const indexPath = path$3.join(frontendPath, indexFile);
48983
50912
  if (!fs$4.existsSync(indexPath)) {
48984
- console.warn(`⚠️ Index file not found: ${indexPath}`);
50913
+ logger.warn(`⚠️ Index file not found: ${indexPath}`);
48985
50914
  return next();
48986
50915
  }
48987
50916
  const html = fs$4.readFileSync(indexPath, "utf-8");
48988
50917
  return c.html(html);
48989
50918
  });
48990
- console.log(`✅ SPA serving enabled from: ${frontendPath}`);
50919
+ logger.info(`✅ SPA serving enabled from: ${frontendPath}`);
48991
50920
  }
48992
50921
  const MAX_PORT_ATTEMPTS = 20;
48993
50922
  const DEV_PORT_FILENAME = ".rebase-dev-port";
@@ -48995,6 +50924,19 @@ function listenWithPortRetry(server, startPort, options2) {
48995
50924
  const host = options2?.host ?? "0.0.0.0";
48996
50925
  const maxAttempts = options2?.maxAttempts ?? MAX_PORT_ATTEMPTS;
48997
50926
  const portFileDir = options2?.portFileDir;
50927
+ const isProd = process.env.NODE_ENV === "production";
50928
+ if (isProd) {
50929
+ return new Promise((resolve, reject) => {
50930
+ const onError = (err) => {
50931
+ reject(err);
50932
+ };
50933
+ server.once("error", onError);
50934
+ server.listen(startPort, host, () => {
50935
+ server.removeListener("error", onError);
50936
+ resolve(startPort);
50937
+ });
50938
+ });
50939
+ }
48998
50940
  let affinityPort = null;
48999
50941
  if (portFileDir) {
49000
50942
  try {
@@ -49137,6 +51079,8 @@ const rebaseEnvSchema = objectType({
49137
51079
  DB_POOL_MAX: stringType().default("20").transform(Number),
49138
51080
  DB_POOL_IDLE_TIMEOUT: stringType().default("30000").transform(Number),
49139
51081
  DB_POOL_CONNECT_TIMEOUT: stringType().default("10000").transform(Number),
51082
+ DATABASE_DIRECT_URL: stringType().url().optional(),
51083
+ DATABASE_READ_URL: stringType().url().optional(),
49140
51084
  FORCE_LOCAL_STORAGE: optionalBoolString,
49141
51085
  STORAGE_TYPE: enumType(["local", "s3"]).default("local"),
49142
51086
  STORAGE_PATH: stringType().optional(),
@@ -49213,8 +51157,11 @@ export {
49213
51157
  RestApiGenerator,
49214
51158
  S3StorageController,
49215
51159
  SMTPEmailService,
51160
+ TransformCache,
51161
+ TusHandler,
49216
51162
  _resetRebaseMock,
49217
51163
  _setRebaseMock,
51164
+ apiKeyKeyGenerator,
49218
51165
  authJwt,
49219
51166
  authRoles,
49220
51167
  authUid,
@@ -49223,6 +51170,9 @@ export {
49223
51170
  configureLogLevel,
49224
51171
  createAdapterAuthMiddleware,
49225
51172
  createAdminRoutes,
51173
+ createApiKeyRateLimiter,
51174
+ createApiKeyRoutes,
51175
+ createApiKeyStore,
49226
51176
  createAppleProvider,
49227
51177
  createAuthMiddleware,
49228
51178
  createAuthRoutes,
@@ -49260,26 +51210,34 @@ export {
49260
51210
  getWelcomeEmailTemplate,
49261
51211
  hashPassword,
49262
51212
  hashRefreshToken,
51213
+ httpMethodToOperation,
49263
51214
  initializeRebaseBackend,
51215
+ isApiKeyToken,
49264
51216
  isAuthAdapter,
49265
51217
  isDatabaseAdapter,
51218
+ isOperationAllowed,
51219
+ isTransformableImage,
49266
51220
  listenWithPortRetry,
49267
51221
  loadCronJobsFromDirectory,
49268
51222
  loadEnv,
49269
51223
  loadFunctionsFromDirectory,
49270
51224
  logger,
49271
51225
  optionalAuth,
51226
+ parseTransformOptions,
49272
51227
  rebase,
49273
51228
  requestLogger,
49274
51229
  requireAdmin,
49275
51230
  requireAuth,
49276
51231
  resetConsole,
49277
- resolveAuthOverrides,
51232
+ resolveAuthHooks,
49278
51233
  serveSPA,
49279
51234
  strictAuthLimiter,
51235
+ transformImage,
51236
+ validateApiKey,
49280
51237
  validateCronExpression,
49281
51238
  validatePasswordStrength,
49282
51239
  verifyAccessToken,
49283
- verifyPassword
51240
+ verifyPassword,
51241
+ z
49284
51242
  };
49285
51243
  //# sourceMappingURL=index.es.js.map