@rebasepro/server-core 0.2.3 → 0.3.0

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.umd.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function(global2, factory) {
2
- typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("fs"), require("path"), require("url"), require("hono"), require("buffer"), require("stream"), require("util"), require("crypto"), require("hono/body-limit"), require("hono/csrf"), require("child_process"), require("https"), require("net"), require("tls"), require("assert"), require("http"), require("os"), require("node:events"), require("node:process"), require("node:util"), require("events"), require("@aws-sdk/client-s3"), require("@aws-sdk/s3-request-presigner"), require("hono/cors"), require("hono/secure-headers"), require("@hono/node-server"), require("ts-morph"), require("drizzle-orm"), require("@hono/node-server/serve-static")) : typeof define === "function" && define.amd ? define(["exports", "fs", "path", "url", "hono", "buffer", "stream", "util", "crypto", "hono/body-limit", "hono/csrf", "child_process", "https", "net", "tls", "assert", "http", "os", "node:events", "node:process", "node:util", "events", "@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner", "hono/cors", "hono/secure-headers", "@hono/node-server", "ts-morph", "drizzle-orm", "@hono/node-server/serve-static"], factory) : (global2 = typeof globalThis !== "undefined" ? globalThis : global2 || self, factory(global2["Rebase Backend"] = {}, global2.fs$4, global2.path$3, global2.require$$0$2, global2.hono, global2.require$$0$3, global2.require$$0$4, global2.require$$5, global2.require$$0$5, global2.bodyLimit, global2.csrf, global2.require$$0$a, global2.require$$1, global2.require$$0$7, global2.require$$1$1, global2.require$$2, global2.require$$0$6, global2.require$$1$2, global2.require$$0$8, global2.require$$1$3, global2.require$$2$1, global2.require$$0$9, global2.clientS3, global2.s3RequestPresigner, global2.cors, global2.secureHeaders, global2.nodeServer, global2.tsMorph, global2.drizzleOrm, global2.serveStatic));
3
- })(this, function(exports2, fs$4, path$3, require$$0$2, hono, require$$0$3, require$$0$4, require$$5, require$$0$5, bodyLimit, csrf, require$$0$a, require$$1, require$$0$7, require$$1$1, require$$2, require$$0$6, require$$1$2, require$$0$8, require$$1$3, require$$2$1, require$$0$9, clientS3, s3RequestPresigner, cors, secureHeaders, nodeServer, tsMorph, drizzleOrm, serveStatic) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("fs"), require("path"), require("url"), require("hono"), require("buffer"), require("stream"), require("util"), require("crypto"), require("hono/body-limit"), require("hono/csrf"), require("child_process"), require("https"), require("querystring"), require("net"), require("tls"), require("assert"), require("http"), require("os"), require("node:events"), require("node:process"), require("node:util"), require("events"), require("@aws-sdk/client-s3"), require("@aws-sdk/s3-request-presigner"), require("fs/promises"), require("zlib"), require("dns"), require("hono/cors"), require("hono/secure-headers"), require("@hono/node-server"), require("ts-morph"), require("drizzle-orm"), require("@hono/node-server/serve-static")) : typeof define === "function" && define.amd ? define(["exports", "fs", "path", "url", "hono", "buffer", "stream", "util", "crypto", "hono/body-limit", "hono/csrf", "child_process", "https", "querystring", "net", "tls", "assert", "http", "os", "node:events", "node:process", "node:util", "events", "@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner", "fs/promises", "zlib", "dns", "hono/cors", "hono/secure-headers", "@hono/node-server", "ts-morph", "drizzle-orm", "@hono/node-server/serve-static"], factory) : (global2 = typeof globalThis !== "undefined" ? globalThis : global2 || self, factory(global2["Rebase Backend"] = {}, global2.fs$4, global2.path$3, global2.require$$0$2, global2.hono, global2.require$$0$3, global2.require$$0$4, global2.require$$5, global2.require$$0$5, global2.bodyLimit, global2.csrf, global2.require$$0$a, global2.require$$1, global2.require$$1$2, global2.require$$0$7, global2.require$$1$1, global2.require$$2, global2.require$$0$6, global2.require$$1$3, global2.require$$0$8, global2.require$$1$4, global2.require$$2$1, global2.require$$0$9, global2.clientS3, global2.s3RequestPresigner, global2.promises, global2.require$$3, global2.require$$4$1, global2.cors, global2.secureHeaders, global2.nodeServer, global2.tsMorph, global2.drizzleOrm, global2.serveStatic));
3
+ })(this, function(exports2, fs$4, path$3, require$$0$2, hono, require$$0$3, require$$0$4, require$$5, require$$0$5, bodyLimit, csrf, require$$0$a, require$$1, require$$1$2, require$$0$7, require$$1$1, require$$2, require$$0$6, require$$1$3, require$$0$8, require$$1$4, require$$2$1, require$$0$9, clientS3, s3RequestPresigner, promises, require$$3, require$$4$1, cors, secureHeaders, nodeServer, tsMorph, drizzleOrm, serveStatic) {
4
4
  "use strict";
5
5
  function _interopNamespaceDefault(e) {
6
6
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
@@ -202,30 +202,6 @@
202
202
  function getDefaultExportFromCjs(x) {
203
203
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
204
204
  }
205
- function getAugmentedNamespace(n) {
206
- if (n.__esModule) return n;
207
- var f2 = n.default;
208
- if (typeof f2 == "function") {
209
- var a2 = function a3() {
210
- if (this instanceof a3) {
211
- return Reflect.construct(f2, arguments, this.constructor);
212
- }
213
- return f2.apply(this, arguments);
214
- };
215
- a2.prototype = f2.prototype;
216
- } else a2 = {};
217
- Object.defineProperty(a2, "__esModule", { value: true });
218
- Object.keys(n).forEach(function(k) {
219
- var d2 = Object.getOwnPropertyDescriptor(n, k);
220
- Object.defineProperty(a2, k, d2.get ? d2 : {
221
- enumerable: true,
222
- get: function() {
223
- return n[k];
224
- }
225
- });
226
- });
227
- return a2;
228
- }
229
205
  function commonjsRequire(path2) {
230
206
  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.');
231
207
  }
@@ -1057,6 +1033,9 @@
1057
1033
  return output;
1058
1034
  }
1059
1035
  for (const key in source) {
1036
+ if (key === "__proto__" || key === "constructor" || key === "prototype") {
1037
+ continue;
1038
+ }
1060
1039
  if (Object.prototype.hasOwnProperty.call(source, key)) {
1061
1040
  const sourceValue = source[key];
1062
1041
  const outputValue = output[key];
@@ -1245,8 +1224,8 @@
1245
1224
  } catch (e) {
1246
1225
  }
1247
1226
  if (!foundForeignKey) {
1248
- const keyPrefix = newRelation.inverseRelationName ? toSnakeCase(newRelation.inverseRelationName) : sourceName;
1249
- newRelation.foreignKeyOnTarget = generateForeignKeyName(keyPrefix);
1227
+ const keyPrefix2 = newRelation.inverseRelationName ? toSnakeCase(newRelation.inverseRelationName) : sourceName;
1228
+ newRelation.foreignKeyOnTarget = generateForeignKeyName(keyPrefix2);
1250
1229
  }
1251
1230
  }
1252
1231
  } else if (newRelation.cardinality === "many" && newRelation.direction === "inverse") {
@@ -2846,10 +2825,15 @@
2846
2825
  const issue = error2.code === "42703" ? "column" : "table";
2847
2826
  logMessage = `Database schema mismatch (${issue} missing): ${error2.message}. Did you forget to run migrations ('pnpm db:push' or 'pnpm db:migrate')?`;
2848
2827
  }
2849
- console.error(`❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
2850
2828
  const causePg = error2.cause && typeof error2.cause === "object" ? error2.cause : void 0;
2851
2829
  const pgErrorCode = causePg?.code || error2.code;
2852
- const suppressStack = pgErrorCode === "42703" || pgErrorCode === "42P01" || statusCode < 500 && code2 === "BAD_REQUEST";
2830
+ const isDbSchemaMismatch = pgErrorCode === "42703" || pgErrorCode === "42P01";
2831
+ if (isDbSchemaMismatch) {
2832
+ console.warn(`⚠️ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
2833
+ } else {
2834
+ console.error(`❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
2835
+ }
2836
+ const suppressStack = isDbSchemaMismatch || statusCode < 500 && code2 === "BAD_REQUEST";
2853
2837
  if (!suppressStack) {
2854
2838
  console.error(error2.stack || error2);
2855
2839
  }
@@ -2930,7 +2914,7 @@
2930
2914
  options2.offset = (page - 1) * limit;
2931
2915
  }
2932
2916
  options2.where = {};
2933
- const reservedQueryKeys = ["limit", "offset", "page", "orderBy", "include", "fields", "searchString"];
2917
+ const reservedQueryKeys = ["limit", "offset", "page", "orderBy", "include", "fields", "searchString", "vector_search", "vector", "vector_distance", "vector_threshold"];
2934
2918
  for (const [key, rawValue] of Object.entries(query)) {
2935
2919
  if (reservedQueryKeys.includes(key)) continue;
2936
2920
  const value = Array.isArray(rawValue) ? rawValue[rawValue.length - 1] : rawValue;
@@ -3002,8 +2986,63 @@
3002
2986
  const fieldsStr = String(query.fields).trim();
3003
2987
  options2.fields = fieldsStr.split(",").map((s2) => s2.trim()).filter(Boolean);
3004
2988
  }
2989
+ if (query.vector_search && query.vector) {
2990
+ const vectorStr = String(query.vector);
2991
+ let queryVector;
2992
+ try {
2993
+ queryVector = JSON.parse(vectorStr);
2994
+ if (!Array.isArray(queryVector) || !queryVector.every((v) => typeof v === "number")) {
2995
+ throw new Error("Expected array of numbers");
2996
+ }
2997
+ } catch {
2998
+ throw new Error("Invalid vector format. Expected JSON array of numbers, e.g. [0.1,0.2,0.3]");
2999
+ }
3000
+ const distanceParam = query.vector_distance ? String(query.vector_distance) : "cosine";
3001
+ if (distanceParam !== "cosine" && distanceParam !== "l2" && distanceParam !== "inner_product") {
3002
+ throw new Error(`Invalid vector_distance: ${distanceParam}. Expected: cosine, l2, or inner_product`);
3003
+ }
3004
+ const vectorSearch = {
3005
+ property: String(query.vector_search),
3006
+ vector: queryVector,
3007
+ distance: distanceParam
3008
+ };
3009
+ if (query.vector_threshold) {
3010
+ const threshold = parseFloat(String(query.vector_threshold));
3011
+ if (isNaN(threshold)) {
3012
+ throw new Error("Invalid vector_threshold. Expected a number.");
3013
+ }
3014
+ vectorSearch.threshold = threshold;
3015
+ }
3016
+ options2.vectorSearch = vectorSearch;
3017
+ }
3005
3018
  return options2;
3006
3019
  }
3020
+ function httpMethodToOperation(method) {
3021
+ const upper = method.toUpperCase();
3022
+ switch (upper) {
3023
+ case "GET":
3024
+ case "HEAD":
3025
+ case "OPTIONS":
3026
+ return "read";
3027
+ case "POST":
3028
+ case "PUT":
3029
+ case "PATCH":
3030
+ return "write";
3031
+ case "DELETE":
3032
+ return "delete";
3033
+ default:
3034
+ return "read";
3035
+ }
3036
+ }
3037
+ function isOperationAllowed(permissions, collection, operation) {
3038
+ for (const perm of permissions) {
3039
+ const collectionMatch = perm.collection === "*" || perm.collection === collection;
3040
+ if (collectionMatch && perm.operations.includes(operation)) {
3041
+ return true;
3042
+ }
3043
+ }
3044
+ return false;
3045
+ }
3007
3046
  class RestApiGenerator {
3008
3047
  collections;
3009
3048
  router;
@@ -3036,6 +3075,19 @@
3036
3075
  this.createSubcollectionRoutes();
3037
3076
  return this.router;
3038
3077
  }
3078
+ /**
3079
+ * Check API key permissions for a collection operation.
3080
+ * Throws 403 if the key doesn't have the required permission.
3081
+ * No-ops if the request is not authenticated via an API key.
3082
+ */
3083
+ enforceApiKeyPermission(c, collectionSlug) {
3084
+ const apiKey = c.get("apiKey");
3085
+ if (!apiKey) return;
3086
+ const operation = httpMethodToOperation(c.req.method);
3087
+ if (!isOperationAllowed(apiKey.permissions, collectionSlug, operation)) {
3088
+ throw ApiError.forbidden(`API key does not have "${operation}" permission for collection "${collectionSlug}"`, "API_KEY_FORBIDDEN");
3089
+ }
3090
+ }
3039
3091
  /**
3040
3092
  * Get the typed RestFetchService from a driver if it exposes one (for include support).
3041
3093
  */
@@ -3049,6 +3101,7 @@
3049
3101
  const basePath = `/${collection.slug}`;
3050
3102
  const resolvedCollection = collection;
3051
3103
  this.router.get(`${basePath}/count`, async (c) => {
3104
+ this.enforceApiKeyPermission(c, collection.slug);
3052
3105
  const queryDict = c.req.query();
3053
3106
  const queryOptions = parseQueryOptions(queryDict);
3054
3107
  const searchString = queryDict.searchString;
@@ -3059,6 +3112,7 @@
3059
3112
  });
3060
3113
  });
3061
3114
  this.router.get(basePath, async (c) => {
3115
+ this.enforceApiKeyPermission(c, collection.slug);
3062
3116
  const queryDict = c.req.query();
3063
3117
  const queryOptions = parseQueryOptions(queryDict);
3064
3118
  const searchString = queryDict.searchString;
@@ -3073,7 +3127,8 @@
3073
3127
  offset: queryOptions.offset,
3074
3128
  orderBy: queryOptions.orderBy?.[0]?.field,
3075
3129
  order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
3076
- searchString
3130
+ searchString,
3131
+ vectorSearch: queryOptions.vectorSearch
3077
3132
  }, queryOptions.include);
3078
3133
  entities2 = await this.applyAfterReadBatch(collection.slug, entities2, hookCtx);
3079
3134
  const total2 = await this.countRawEntities(driver, resolvedCollection, queryOptions, searchString);
@@ -3101,6 +3156,7 @@
3101
3156
  });
3102
3157
  });
3103
3158
  this.router.get(`${basePath}/:id`, async (c) => {
3159
+ this.enforceApiKeyPermission(c, collection.slug);
3104
3160
  const id = c.req.param("id");
3105
3161
  const queryDict = c.req.query();
3106
3162
  const queryOptions = parseQueryOptions(queryDict);
@@ -3131,6 +3187,7 @@
3131
3187
  });
3132
3188
  this.router.post(basePath, async (c) => {
3133
3189
  try {
3190
+ this.enforceApiKeyPermission(c, collection.slug);
3134
3191
  const driver = c.get("driver") || this.driver;
3135
3192
  const path2 = collection.slug;
3136
3193
  const hookCtx = this.buildHookContext(c, "POST");
@@ -3159,6 +3216,7 @@
3159
3216
  });
3160
3217
  this.router.put(`${basePath}/:id`, async (c) => {
3161
3218
  try {
3219
+ this.enforceApiKeyPermission(c, collection.slug);
3162
3220
  const id = c.req.param("id");
3163
3221
  const driver = c.get("driver") || this.driver;
3164
3222
  const hookCtx = this.buildHookContext(c, "PUT");
@@ -3195,6 +3253,7 @@
3195
3253
  }
3196
3254
  });
3197
3255
  this.router.delete(`${basePath}/:id`, async (c) => {
3256
+ this.enforceApiKeyPermission(c, collection.slug);
3198
3257
  const id = c.req.param("id");
3199
3258
  const driver = c.get("driver") || this.driver;
3200
3259
  const hookCtx = this.buildHookContext(c, "DELETE");
@@ -3266,6 +3325,7 @@
3266
3325
  const parsed = parseSubPath(rawPath);
3267
3326
  if (!parsed) return next();
3268
3327
  const driver = c.get("driver") || this.driver;
3328
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3269
3329
  if (parsed.entityId === "count") {
3270
3330
  const queryDict = c.req.query();
3271
3331
  const queryOptions = parseQueryOptions(queryDict);
@@ -3313,6 +3373,7 @@
3313
3373
  const parsed = parseSubPath(rawPath);
3314
3374
  if (!parsed || parsed.entityId) return next();
3315
3375
  const driver = c.get("driver") || this.driver;
3376
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3316
3377
  const body = await c.req.json().catch(() => ({}));
3317
3378
  const entity = await driver.saveEntity({
3318
3379
  path: parsed.collectionPath,
@@ -3328,6 +3389,7 @@
3328
3389
  const parsed = parseSubPath(rawPath);
3329
3390
  if (!parsed || !parsed.entityId) return next();
3330
3391
  const driver = c.get("driver") || this.driver;
3392
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3331
3393
  const body = await c.req.json().catch(() => ({}));
3332
3394
  const entity = await driver.saveEntity({
3333
3395
  path: parsed.collectionPath,
@@ -3344,6 +3406,7 @@
3344
3406
  const parsed = parseSubPath(rawPath);
3345
3407
  if (!parsed || !parsed.entityId) return next();
3346
3408
  const driver = c.get("driver") || this.driver;
3409
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3347
3410
  const existingEntity = await driver.fetchEntity({
3348
3411
  path: parsed.collectionPath,
3349
3412
  entityId: parsed.entityId
@@ -3409,7 +3472,8 @@
3409
3472
  orderBy: queryOptions.orderBy?.[0]?.field,
3410
3473
  order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
3411
3474
  startAfter: queryOptions.offset ? String(queryOptions.offset) : void 0,
3412
- searchString
3475
+ searchString,
3476
+ vectorSearch: queryOptions.vectorSearch
3413
3477
  });
3414
3478
  return entities.map((entity) => this.flattenEntity(entity));
3415
3479
  }
@@ -4935,7 +4999,7 @@
4935
4999
  const SemVer$6 = semver$4;
4936
5000
  const parse$6 = parse_1;
4937
5001
  const { safeRe: re, t } = reExports;
4938
- const coerce$1 = (version2, options2) => {
5002
+ const coerce$2 = (version2, options2) => {
4939
5003
  if (version2 instanceof SemVer$6) {
4940
5004
  return version2;
4941
5005
  }
@@ -4970,7 +5034,7 @@
4970
5034
  const build = options2.includePrerelease && match[6] ? `+${match[6]}` : "";
4971
5035
  return parse$6(`${major2}.${minor2}.${patch2}${prerelease2}${build}`, options2);
4972
5036
  };
4973
- var coerce_1 = coerce$1;
5037
+ var coerce_1 = coerce$2;
4974
5038
  const parse$5 = parse_1;
4975
5039
  const constants$1 = constants$2;
4976
5040
  const SemVer$5 = semver$4;
@@ -5256,19 +5320,19 @@
5256
5320
  const replaceCaret = (comp, options2) => {
5257
5321
  debug2("caret", comp, options2);
5258
5322
  const r = options2.loose ? re2[t2.CARETLOOSE] : re2[t2.CARET];
5259
- const z = options2.includePrerelease ? "-0" : "";
5323
+ const z2 = options2.includePrerelease ? "-0" : "";
5260
5324
  return comp.replace(r, (_, M, m2, p, pr) => {
5261
5325
  debug2("caret", comp, _, M, m2, p, pr);
5262
5326
  let ret;
5263
5327
  if (isX(M)) {
5264
5328
  ret = "";
5265
5329
  } else if (isX(m2)) {
5266
- ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`;
5330
+ ret = `>=${M}.0.0${z2} <${+M + 1}.0.0-0`;
5267
5331
  } else if (isX(p)) {
5268
5332
  if (M === "0") {
5269
- ret = `>=${M}.${m2}.0${z} <${M}.${+m2 + 1}.0-0`;
5333
+ ret = `>=${M}.${m2}.0${z2} <${M}.${+m2 + 1}.0-0`;
5270
5334
  } else {
5271
- ret = `>=${M}.${m2}.0${z} <${+M + 1}.0.0-0`;
5335
+ ret = `>=${M}.${m2}.0${z2} <${+M + 1}.0.0-0`;
5272
5336
  }
5273
5337
  } else if (pr) {
5274
5338
  debug2("replaceCaret pr", pr);
@@ -5285,9 +5349,9 @@
5285
5349
  debug2("no pr");
5286
5350
  if (M === "0") {
5287
5351
  if (m2 === "0") {
5288
- ret = `>=${M}.${m2}.${p}${z} <${M}.${m2}.${+p + 1}-0`;
5352
+ ret = `>=${M}.${m2}.${p}${z2} <${M}.${m2}.${+p + 1}-0`;
5289
5353
  } else {
5290
- ret = `>=${M}.${m2}.${p}${z} <${M}.${+m2 + 1}.0-0`;
5354
+ ret = `>=${M}.${m2}.${p}${z2} <${M}.${+m2 + 1}.0-0`;
5291
5355
  }
5292
5356
  } else {
5293
5357
  ret = `>=${M}.${m2}.${p} <${+M + 1}.0.0-0`;
@@ -5944,7 +6008,7 @@
5944
6008
  const gte = gte_1;
5945
6009
  const lte = lte_1;
5946
6010
  const cmp = cmp_1;
5947
- const coerce = coerce_1;
6011
+ const coerce$1 = coerce_1;
5948
6012
  const truncate = truncate_1;
5949
6013
  const Comparator = requireComparator();
5950
6014
  const Range = requireRange();
@@ -5983,7 +6047,7 @@
5983
6047
  gte,
5984
6048
  lte,
5985
6049
  cmp,
5986
- coerce,
6050
+ coerce: coerce$1,
5987
6051
  truncate,
5988
6052
  Comparator,
5989
6053
  Range,
@@ -6864,7 +6928,7 @@
6864
6928
  NotBeforeError: NotBeforeError_1,
6865
6929
  TokenExpiredError: TokenExpiredError_1
6866
6930
  };
6867
- const jwt = /* @__PURE__ */ getDefaultExportFromCjs(jsonwebtoken);
6931
+ const jwt$1 = /* @__PURE__ */ getDefaultExportFromCjs(jsonwebtoken);
6868
6932
  let jwtConfig = {
6869
6933
  secret: "",
6870
6934
  accessExpiresIn: "1h",
@@ -6883,15 +6947,17 @@
6883
6947
  ...config
6884
6948
  };
6885
6949
  }
6886
- function generateAccessToken(userId, roles) {
6950
+ function generateAccessToken(userId, roles, aal = "aal1", customClaims) {
6887
6951
  if (!jwtConfig.secret) {
6888
6952
  throw new Error("JWT secret not configured. Call configureJwt() first.");
6889
6953
  }
6890
6954
  const payload = {
6891
6955
  userId,
6892
- roles
6956
+ roles,
6957
+ aal,
6958
+ ...customClaims
6893
6959
  };
6894
- return jwt.sign(payload, jwtConfig.secret, {
6960
+ return jwt$1.sign(payload, jwtConfig.secret, {
6895
6961
  expiresIn: jwtConfig.accessExpiresIn,
6896
6962
  algorithm: "HS256"
6897
6963
  });
@@ -6925,7 +6991,7 @@
6925
6991
  throw new Error("JWT secret not configured. Call configureJwt() first.");
6926
6992
  }
6927
6993
  try {
6928
- const decoded = jwt.verify(token, jwtConfig.secret, {
6994
+ const decoded = jwt$1.verify(token, jwtConfig.secret, {
6929
6995
  algorithms: ["HS256"]
6930
6996
  });
6931
6997
  const id = decoded.userId || decoded.uid || decoded.sub;
@@ -6933,9 +6999,11 @@
6933
6999
  console.error("[JWT] Verification failed: missing id in payload", decoded);
6934
7000
  return null;
6935
7001
  }
7002
+ const aal = decoded.aal === "aal1" || decoded.aal === "aal2" ? decoded.aal : "aal1";
6936
7003
  return {
6937
7004
  userId: id,
6938
- roles: decoded.roles || []
7005
+ roles: decoded.roles || [],
7006
+ aal
6939
7007
  };
6940
7008
  } catch (error2) {
6941
7009
  console.error("[JWT] Verification failed:", error2, "Token start:", token.substring(0, 15));
@@ -6975,6 +7043,17 @@
6975
7043
  }
6976
7044
  return new Date(Date.now() + ms2);
6977
7045
  }
7046
+ const jwt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
7047
+ __proto__: null,
7048
+ configureJwt,
7049
+ generateAccessToken,
7050
+ generateRefreshToken,
7051
+ getAccessTokenExpiry,
7052
+ getAccessTokenExpiryMs,
7053
+ getRefreshTokenExpiry,
7054
+ hashRefreshToken,
7055
+ verifyAccessToken
7056
+ }, Symbol.toStringTag, { value: "Module" }));
6978
7057
  function isRLSScopedDriver(driver) {
6979
7058
  return "withAuth" in driver && typeof driver.withAuth === "function";
6980
7059
  }
@@ -6997,6 +7076,81 @@
6997
7076
  return false;
6998
7077
  }
6999
7078
  }
7079
+ function isApiKeyToken(token) {
7080
+ return token.startsWith("rk_");
7081
+ }
7082
+ function hashToken$2(token) {
7083
+ return require$$0$5.createHash("sha256").update(token).digest("hex");
7084
+ }
7085
+ async function validateApiKey(c, token, options2) {
7086
+ const {
7087
+ store,
7088
+ driver
7089
+ } = options2;
7090
+ const hash = hashToken$2(token);
7091
+ const apiKey = await store.findByKeyHash(hash);
7092
+ if (!apiKey) {
7093
+ return c.json({
7094
+ error: {
7095
+ message: "Invalid API key",
7096
+ code: "UNAUTHORIZED"
7097
+ }
7098
+ }, 401);
7099
+ }
7100
+ if (apiKey.revoked_at) {
7101
+ return c.json({
7102
+ error: {
7103
+ message: "API key has been revoked",
7104
+ code: "UNAUTHORIZED"
7105
+ }
7106
+ }, 401);
7107
+ }
7108
+ if (apiKey.expires_at && new Date(apiKey.expires_at) < /* @__PURE__ */ new Date()) {
7109
+ return c.json({
7110
+ error: {
7111
+ message: "API key has expired",
7112
+ code: "UNAUTHORIZED"
7113
+ }
7114
+ }, 401);
7115
+ }
7116
+ const userId = `api-key:${apiKey.id}`;
7117
+ c.set("user", {
7118
+ userId,
7119
+ roles: []
7120
+ });
7121
+ const masked = {
7122
+ id: apiKey.id,
7123
+ name: apiKey.name,
7124
+ key_prefix: apiKey.key_prefix,
7125
+ permissions: apiKey.permissions,
7126
+ rate_limit: apiKey.rate_limit,
7127
+ created_by: apiKey.created_by,
7128
+ created_at: apiKey.created_at,
7129
+ updated_at: apiKey.updated_at,
7130
+ last_used_at: apiKey.last_used_at,
7131
+ expires_at: apiKey.expires_at,
7132
+ revoked_at: apiKey.revoked_at
7133
+ };
7134
+ c.set("apiKey", masked);
7135
+ try {
7136
+ const scopedDriver = await scopeDataDriver(driver, {
7137
+ uid: userId,
7138
+ roles: ["service"]
7139
+ });
7140
+ c.set("driver", scopedDriver);
7141
+ } catch (error2) {
7142
+ console.error("[AUTH] RLS scoping failed for API key:", error2);
7143
+ return c.json({
7144
+ error: {
7145
+ message: "Internal authentication error",
7146
+ code: "INTERNAL_ERROR"
7147
+ }
7148
+ }, 500);
7149
+ }
7150
+ store.updateLastUsed(apiKey.id).catch(() => {
7151
+ });
7152
+ return true;
7153
+ }
7000
7154
  const requireAuth = async (c, next) => {
7001
7155
  const authHeader = c.req.header("authorization");
7002
7156
  const queryToken = c.req.query("token");
@@ -7103,7 +7257,8 @@
7103
7257
  driver,
7104
7258
  requireAuth: enforceAuth = true,
7105
7259
  validator,
7106
- serviceKey
7260
+ serviceKey,
7261
+ apiKeyStore
7107
7262
  } = options2;
7108
7263
  return async (c, next) => {
7109
7264
  if (validator) {
@@ -7178,6 +7333,14 @@
7178
7333
  }
7179
7334
  }, 500);
7180
7335
  }
7336
+ } else if (apiKeyStore && isApiKeyToken(token)) {
7337
+ const result = await validateApiKey(c, token, {
7338
+ store: apiKeyStore,
7339
+ driver
7340
+ });
7341
+ if (result !== true) {
7342
+ return result;
7343
+ }
7181
7344
  } else {
7182
7345
  const payload = extractUserFromToken(token);
7183
7346
  if (payload) {
@@ -7238,9 +7401,22 @@
7238
7401
  const {
7239
7402
  adapter,
7240
7403
  driver,
7241
- requireAuth: enforceAuth = true
7404
+ requireAuth: enforceAuth = true,
7405
+ apiKeyStore
7242
7406
  } = options2;
7243
7407
  return async (c, next) => {
7408
+ if (apiKeyStore) {
7409
+ const authHeader = c.req.header("authorization") || "";
7410
+ const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
7411
+ if (token.startsWith("rk_")) {
7412
+ const result = await validateApiKey(c, token, {
7413
+ store: apiKeyStore,
7414
+ driver
7415
+ });
7416
+ if (result === true) return next();
7417
+ return result;
7418
+ }
7419
+ }
7244
7420
  let authenticatedUser = null;
7245
7421
  try {
7246
7422
  authenticatedUser = await adapter.verifyRequest(c.req.raw);
@@ -7336,11 +7512,11 @@
7336
7512
  const derivedKey = await scryptAsync(password, salt, KEY_LENGTH);
7337
7513
  return require$$0$5.timingSafeEqual(derivedKey, storedKey);
7338
7514
  }
7339
- function resolveAuthOverrides(overrides) {
7515
+ function resolveAuthHooks(hooks) {
7340
7516
  return {
7341
- hashPassword: overrides?.hashPassword ?? hashPassword,
7342
- verifyPassword: overrides?.verifyPassword ?? verifyPassword,
7343
- validatePasswordStrength: overrides?.validatePasswordStrength ?? validatePasswordStrength
7517
+ hashPassword: hooks?.hashPassword ?? hashPassword,
7518
+ verifyPassword: hooks?.verifyPassword ?? verifyPassword,
7519
+ validatePasswordStrength: hooks?.validatePasswordStrength ?? validatePasswordStrength
7344
7520
  };
7345
7521
  }
7346
7522
  function getGreeting(user) {
@@ -7742,6 +7918,63 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
7742
7918
  limit: 50,
7743
7919
  message: "Too many requests to this sensitive endpoint, please try again later."
7744
7920
  });
7921
+ function apiKeyKeyGenerator(c) {
7922
+ const apiKey = c.get("apiKey");
7923
+ if (apiKey) {
7924
+ return `api-key:${apiKey.id}`;
7925
+ }
7926
+ return defaultKeyGenerator(c);
7927
+ }
7928
+ function createApiKeyRateLimiter(defaultLimit = 1e3, windowMs = 15 * 60 * 1e3) {
7929
+ const store = /* @__PURE__ */ new Map();
7930
+ const cleanupInterval = setInterval(() => {
7931
+ const now = Date.now();
7932
+ for (const [key, entry] of store.entries()) {
7933
+ entry.timestamps = entry.timestamps.filter((t2) => now - t2 < windowMs);
7934
+ if (entry.timestamps.length === 0) {
7935
+ store.delete(key);
7936
+ }
7937
+ }
7938
+ }, windowMs);
7939
+ if (cleanupInterval.unref) {
7940
+ cleanupInterval.unref();
7941
+ }
7942
+ return async (c, next) => {
7943
+ const apiKey = c.get("apiKey");
7944
+ if (!apiKey) {
7945
+ return next();
7946
+ }
7947
+ const limit = apiKey.rate_limit ?? defaultLimit;
7948
+ const key = `api-key:${apiKey.id}`;
7949
+ const now = Date.now();
7950
+ let entry = store.get(key);
7951
+ if (!entry) {
7952
+ entry = {
7953
+ timestamps: []
7954
+ };
7955
+ store.set(key, entry);
7956
+ }
7957
+ entry.timestamps = entry.timestamps.filter((t2) => now - t2 < windowMs);
7958
+ if (entry.timestamps.length >= limit) {
7959
+ const retryAfterMs = entry.timestamps[0] + windowMs - now;
7960
+ const retryAfterSec = Math.ceil(retryAfterMs / 1e3);
7961
+ c.header("Retry-After", String(retryAfterSec));
7962
+ c.header("X-RateLimit-Limit", String(limit));
7963
+ c.header("X-RateLimit-Remaining", "0");
7964
+ c.header("X-RateLimit-Reset", String(Math.ceil((now + retryAfterMs) / 1e3)));
7965
+ return c.json({
7966
+ error: {
7967
+ message: "API key rate limit exceeded, please try again later.",
7968
+ code: "RATE_LIMITED"
7969
+ }
7970
+ }, 429);
7971
+ }
7972
+ entry.timestamps.push(now);
7973
+ c.header("X-RateLimit-Limit", String(limit));
7974
+ c.header("X-RateLimit-Remaining", String(limit - entry.timestamps.length));
7975
+ return next();
7976
+ };
7977
+ }
7745
7978
  var util$3;
7746
7979
  (function(util2) {
7747
7980
  util2.assertEqual = (_) => {
@@ -7892,6 +8125,10 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
7892
8125
  "not_multiple_of",
7893
8126
  "not_finite"
7894
8127
  ]);
8128
+ const quotelessJson = (obj) => {
8129
+ const json = JSON.stringify(obj, null, 2);
8130
+ return json.replace(/"([^"]+)":/g, "$1:");
8131
+ };
7895
8132
  class ZodError extends Error {
7896
8133
  get errors() {
7897
8134
  return this.issues;
@@ -8087,6 +8324,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
8087
8324
  return { message };
8088
8325
  };
8089
8326
  let overrideErrorMap = errorMap;
8327
+ function setErrorMap(map2) {
8328
+ overrideErrorMap = map2;
8329
+ }
8090
8330
  function getErrorMap() {
8091
8331
  return overrideErrorMap;
8092
8332
  }
@@ -8115,6 +8355,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
8115
8355
  message: errorMessage
8116
8356
  };
8117
8357
  };
8358
+ const EMPTY_PATH = [];
8118
8359
  function addIssueToContext(ctx, issueData) {
8119
8360
  const overrideMap = getErrorMap();
8120
8361
  const issue = makeIssue({
@@ -10405,6 +10646,113 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
10405
10646
  ...processCreateParams(params)
10406
10647
  });
10407
10648
  };
10649
+ const getDiscriminator = (type) => {
10650
+ if (type instanceof ZodLazy) {
10651
+ return getDiscriminator(type.schema);
10652
+ } else if (type instanceof ZodEffects) {
10653
+ return getDiscriminator(type.innerType());
10654
+ } else if (type instanceof ZodLiteral) {
10655
+ return [type.value];
10656
+ } else if (type instanceof ZodEnum) {
10657
+ return type.options;
10658
+ } else if (type instanceof ZodNativeEnum) {
10659
+ return util$3.objectValues(type.enum);
10660
+ } else if (type instanceof ZodDefault) {
10661
+ return getDiscriminator(type._def.innerType);
10662
+ } else if (type instanceof ZodUndefined) {
10663
+ return [void 0];
10664
+ } else if (type instanceof ZodNull) {
10665
+ return [null];
10666
+ } else if (type instanceof ZodOptional) {
10667
+ return [void 0, ...getDiscriminator(type.unwrap())];
10668
+ } else if (type instanceof ZodNullable) {
10669
+ return [null, ...getDiscriminator(type.unwrap())];
10670
+ } else if (type instanceof ZodBranded) {
10671
+ return getDiscriminator(type.unwrap());
10672
+ } else if (type instanceof ZodReadonly) {
10673
+ return getDiscriminator(type.unwrap());
10674
+ } else if (type instanceof ZodCatch) {
10675
+ return getDiscriminator(type._def.innerType);
10676
+ } else {
10677
+ return [];
10678
+ }
10679
+ };
10680
+ class ZodDiscriminatedUnion extends ZodType {
10681
+ _parse(input) {
10682
+ const { ctx } = this._processInputParams(input);
10683
+ if (ctx.parsedType !== ZodParsedType.object) {
10684
+ addIssueToContext(ctx, {
10685
+ code: ZodIssueCode.invalid_type,
10686
+ expected: ZodParsedType.object,
10687
+ received: ctx.parsedType
10688
+ });
10689
+ return INVALID;
10690
+ }
10691
+ const discriminator = this.discriminator;
10692
+ const discriminatorValue = ctx.data[discriminator];
10693
+ const option = this.optionsMap.get(discriminatorValue);
10694
+ if (!option) {
10695
+ addIssueToContext(ctx, {
10696
+ code: ZodIssueCode.invalid_union_discriminator,
10697
+ options: Array.from(this.optionsMap.keys()),
10698
+ path: [discriminator]
10699
+ });
10700
+ return INVALID;
10701
+ }
10702
+ if (ctx.common.async) {
10703
+ return option._parseAsync({
10704
+ data: ctx.data,
10705
+ path: ctx.path,
10706
+ parent: ctx
10707
+ });
10708
+ } else {
10709
+ return option._parseSync({
10710
+ data: ctx.data,
10711
+ path: ctx.path,
10712
+ parent: ctx
10713
+ });
10714
+ }
10715
+ }
10716
+ get discriminator() {
10717
+ return this._def.discriminator;
10718
+ }
10719
+ get options() {
10720
+ return this._def.options;
10721
+ }
10722
+ get optionsMap() {
10723
+ return this._def.optionsMap;
10724
+ }
10725
+ /**
10726
+ * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor.
10727
+ * However, it only allows a union of objects, all of which need to share a discriminator property. This property must
10728
+ * have a different value for each object in the union.
10729
+ * @param discriminator the name of the discriminator property
10730
+ * @param types an array of object schemas
10731
+ * @param params
10732
+ */
10733
+ static create(discriminator, options2, params) {
10734
+ const optionsMap = /* @__PURE__ */ new Map();
10735
+ for (const type of options2) {
10736
+ const discriminatorValues = getDiscriminator(type.shape[discriminator]);
10737
+ if (!discriminatorValues.length) {
10738
+ throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`);
10739
+ }
10740
+ for (const value of discriminatorValues) {
10741
+ if (optionsMap.has(value)) {
10742
+ throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`);
10743
+ }
10744
+ optionsMap.set(value, type);
10745
+ }
10746
+ }
10747
+ return new ZodDiscriminatedUnion({
10748
+ typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion,
10749
+ discriminator,
10750
+ options: options2,
10751
+ optionsMap,
10752
+ ...processCreateParams(params)
10753
+ });
10754
+ }
10755
+ }
10408
10756
  function mergeValues(a2, b) {
10409
10757
  const aType = getParsedType(a2);
10410
10758
  const bType = getParsedType(b);
@@ -10563,6 +10911,59 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
10563
10911
  ...processCreateParams(params)
10564
10912
  });
10565
10913
  };
10914
+ class ZodRecord extends ZodType {
10915
+ get keySchema() {
10916
+ return this._def.keyType;
10917
+ }
10918
+ get valueSchema() {
10919
+ return this._def.valueType;
10920
+ }
10921
+ _parse(input) {
10922
+ const { status, ctx } = this._processInputParams(input);
10923
+ if (ctx.parsedType !== ZodParsedType.object) {
10924
+ addIssueToContext(ctx, {
10925
+ code: ZodIssueCode.invalid_type,
10926
+ expected: ZodParsedType.object,
10927
+ received: ctx.parsedType
10928
+ });
10929
+ return INVALID;
10930
+ }
10931
+ const pairs = [];
10932
+ const keyType = this._def.keyType;
10933
+ const valueType = this._def.valueType;
10934
+ for (const key in ctx.data) {
10935
+ pairs.push({
10936
+ key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)),
10937
+ value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)),
10938
+ alwaysSet: key in ctx.data
10939
+ });
10940
+ }
10941
+ if (ctx.common.async) {
10942
+ return ParseStatus.mergeObjectAsync(status, pairs);
10943
+ } else {
10944
+ return ParseStatus.mergeObjectSync(status, pairs);
10945
+ }
10946
+ }
10947
+ get element() {
10948
+ return this._def.valueType;
10949
+ }
10950
+ static create(first, second, third) {
10951
+ if (second instanceof ZodType) {
10952
+ return new ZodRecord({
10953
+ keyType: first,
10954
+ valueType: second,
10955
+ typeName: ZodFirstPartyTypeKind.ZodRecord,
10956
+ ...processCreateParams(third)
10957
+ });
10958
+ }
10959
+ return new ZodRecord({
10960
+ keyType: ZodString.create(),
10961
+ valueType: first,
10962
+ typeName: ZodFirstPartyTypeKind.ZodRecord,
10963
+ ...processCreateParams(second)
10964
+ });
10965
+ }
10966
+ }
10566
10967
  class ZodMap extends ZodType {
10567
10968
  get keySchema() {
10568
10969
  return this._def.keyType;
@@ -10714,6 +11115,111 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
10714
11115
  ...processCreateParams(params)
10715
11116
  });
10716
11117
  };
11118
+ class ZodFunction extends ZodType {
11119
+ constructor() {
11120
+ super(...arguments);
11121
+ this.validate = this.implement;
11122
+ }
11123
+ _parse(input) {
11124
+ const { ctx } = this._processInputParams(input);
11125
+ if (ctx.parsedType !== ZodParsedType.function) {
11126
+ addIssueToContext(ctx, {
11127
+ code: ZodIssueCode.invalid_type,
11128
+ expected: ZodParsedType.function,
11129
+ received: ctx.parsedType
11130
+ });
11131
+ return INVALID;
11132
+ }
11133
+ function makeArgsIssue(args, error2) {
11134
+ return makeIssue({
11135
+ data: args,
11136
+ path: ctx.path,
11137
+ errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), errorMap].filter((x) => !!x),
11138
+ issueData: {
11139
+ code: ZodIssueCode.invalid_arguments,
11140
+ argumentsError: error2
11141
+ }
11142
+ });
11143
+ }
11144
+ function makeReturnsIssue(returns, error2) {
11145
+ return makeIssue({
11146
+ data: returns,
11147
+ path: ctx.path,
11148
+ errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), errorMap].filter((x) => !!x),
11149
+ issueData: {
11150
+ code: ZodIssueCode.invalid_return_type,
11151
+ returnTypeError: error2
11152
+ }
11153
+ });
11154
+ }
11155
+ const params = { errorMap: ctx.common.contextualErrorMap };
11156
+ const fn = ctx.data;
11157
+ if (this._def.returns instanceof ZodPromise) {
11158
+ const me = this;
11159
+ return OK(async function(...args) {
11160
+ const error2 = new ZodError([]);
11161
+ const parsedArgs = await me._def.args.parseAsync(args, params).catch((e) => {
11162
+ error2.addIssue(makeArgsIssue(args, e));
11163
+ throw error2;
11164
+ });
11165
+ const result = await Reflect.apply(fn, this, parsedArgs);
11166
+ const parsedReturns = await me._def.returns._def.type.parseAsync(result, params).catch((e) => {
11167
+ error2.addIssue(makeReturnsIssue(result, e));
11168
+ throw error2;
11169
+ });
11170
+ return parsedReturns;
11171
+ });
11172
+ } else {
11173
+ const me = this;
11174
+ return OK(function(...args) {
11175
+ const parsedArgs = me._def.args.safeParse(args, params);
11176
+ if (!parsedArgs.success) {
11177
+ throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
11178
+ }
11179
+ const result = Reflect.apply(fn, this, parsedArgs.data);
11180
+ const parsedReturns = me._def.returns.safeParse(result, params);
11181
+ if (!parsedReturns.success) {
11182
+ throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
11183
+ }
11184
+ return parsedReturns.data;
11185
+ });
11186
+ }
11187
+ }
11188
+ parameters() {
11189
+ return this._def.args;
11190
+ }
11191
+ returnType() {
11192
+ return this._def.returns;
11193
+ }
11194
+ args(...items) {
11195
+ return new ZodFunction({
11196
+ ...this._def,
11197
+ args: ZodTuple.create(items).rest(ZodUnknown.create())
11198
+ });
11199
+ }
11200
+ returns(returnType) {
11201
+ return new ZodFunction({
11202
+ ...this._def,
11203
+ returns: returnType
11204
+ });
11205
+ }
11206
+ implement(func) {
11207
+ const validatedFunc = this.parse(func);
11208
+ return validatedFunc;
11209
+ }
11210
+ strictImplement(func) {
11211
+ const validatedFunc = this.parse(func);
11212
+ return validatedFunc;
11213
+ }
11214
+ static create(args, returns, params) {
11215
+ return new ZodFunction({
11216
+ args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()),
11217
+ returns: returns || ZodUnknown.create(),
11218
+ typeName: ZodFirstPartyTypeKind.ZodFunction,
11219
+ ...processCreateParams(params)
11220
+ });
11221
+ }
11222
+ }
10717
11223
  class ZodLazy extends ZodType {
10718
11224
  get schema() {
10719
11225
  return this._def.getter();
@@ -11171,6 +11677,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11171
11677
  ...processCreateParams(params)
11172
11678
  });
11173
11679
  };
11680
+ const BRAND = Symbol("zod_brand");
11174
11681
  class ZodBranded extends ZodType {
11175
11682
  _parse(input) {
11176
11683
  const { ctx } = this._processInputParams(input);
@@ -11262,6 +11769,36 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11262
11769
  ...processCreateParams(params)
11263
11770
  });
11264
11771
  };
11772
+ function cleanParams(params, data) {
11773
+ const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
11774
+ const p2 = typeof p === "string" ? { message: p } : p;
11775
+ return p2;
11776
+ }
11777
+ function custom(check, _params = {}, fatal) {
11778
+ if (check)
11779
+ return ZodAny.create().superRefine((data, ctx) => {
11780
+ const r = check(data);
11781
+ if (r instanceof Promise) {
11782
+ return r.then((r2) => {
11783
+ if (!r2) {
11784
+ const params = cleanParams(_params, data);
11785
+ const _fatal = params.fatal ?? fatal ?? true;
11786
+ ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
11787
+ }
11788
+ });
11789
+ }
11790
+ if (!r) {
11791
+ const params = cleanParams(_params, data);
11792
+ const _fatal = params.fatal ?? fatal ?? true;
11793
+ ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
11794
+ }
11795
+ return;
11796
+ });
11797
+ return ZodAny.create();
11798
+ }
11799
+ const late = {
11800
+ object: ZodObject.lazycreate
11801
+ };
11265
11802
  var ZodFirstPartyTypeKind;
11266
11803
  (function(ZodFirstPartyTypeKind2) {
11267
11804
  ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
@@ -11301,17 +11838,251 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11301
11838
  ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
11302
11839
  ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
11303
11840
  })(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
11841
+ const instanceOfType = (cls, params = {
11842
+ message: `Input not instance of ${cls.name}`
11843
+ }) => custom((data) => data instanceof cls, params);
11304
11844
  const stringType = ZodString.create;
11305
- ZodNever.create;
11306
- ZodArray.create;
11845
+ const numberType = ZodNumber.create;
11846
+ const nanType = ZodNaN.create;
11847
+ const bigIntType = ZodBigInt.create;
11848
+ const booleanType = ZodBoolean.create;
11849
+ const dateType = ZodDate.create;
11850
+ const symbolType = ZodSymbol.create;
11851
+ const undefinedType = ZodUndefined.create;
11852
+ const nullType = ZodNull.create;
11853
+ const anyType = ZodAny.create;
11854
+ const unknownType = ZodUnknown.create;
11855
+ const neverType = ZodNever.create;
11856
+ const voidType = ZodVoid.create;
11857
+ const arrayType = ZodArray.create;
11307
11858
  const objectType = ZodObject.create;
11308
- ZodUnion.create;
11309
- ZodIntersection.create;
11310
- ZodTuple.create;
11859
+ const strictObjectType = ZodObject.strictCreate;
11860
+ const unionType = ZodUnion.create;
11861
+ const discriminatedUnionType = ZodDiscriminatedUnion.create;
11862
+ const intersectionType = ZodIntersection.create;
11863
+ const tupleType = ZodTuple.create;
11864
+ const recordType = ZodRecord.create;
11865
+ const mapType = ZodMap.create;
11866
+ const setType = ZodSet.create;
11867
+ const functionType = ZodFunction.create;
11868
+ const lazyType = ZodLazy.create;
11869
+ const literalType = ZodLiteral.create;
11311
11870
  const enumType = ZodEnum.create;
11312
- ZodPromise.create;
11313
- ZodOptional.create;
11314
- ZodNullable.create;
11871
+ const nativeEnumType = ZodNativeEnum.create;
11872
+ const promiseType = ZodPromise.create;
11873
+ const effectsType = ZodEffects.create;
11874
+ const optionalType = ZodOptional.create;
11875
+ const nullableType = ZodNullable.create;
11876
+ const preprocessType = ZodEffects.createWithPreprocess;
11877
+ const pipelineType = ZodPipeline.create;
11878
+ const ostring = () => stringType().optional();
11879
+ const onumber = () => numberType().optional();
11880
+ const oboolean = () => booleanType().optional();
11881
+ const coerce = {
11882
+ string: (arg) => ZodString.create({ ...arg, coerce: true }),
11883
+ number: (arg) => ZodNumber.create({ ...arg, coerce: true }),
11884
+ boolean: (arg) => ZodBoolean.create({
11885
+ ...arg,
11886
+ coerce: true
11887
+ }),
11888
+ bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }),
11889
+ date: (arg) => ZodDate.create({ ...arg, coerce: true })
11890
+ };
11891
+ const NEVER = INVALID;
11892
+ const z = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
11893
+ __proto__: null,
11894
+ BRAND,
11895
+ DIRTY,
11896
+ EMPTY_PATH,
11897
+ INVALID,
11898
+ NEVER,
11899
+ OK,
11900
+ ParseStatus,
11901
+ Schema: ZodType,
11902
+ ZodAny,
11903
+ ZodArray,
11904
+ ZodBigInt,
11905
+ ZodBoolean,
11906
+ ZodBranded,
11907
+ ZodCatch,
11908
+ ZodDate,
11909
+ ZodDefault,
11910
+ ZodDiscriminatedUnion,
11911
+ ZodEffects,
11912
+ ZodEnum,
11913
+ ZodError,
11914
+ get ZodFirstPartyTypeKind() {
11915
+ return ZodFirstPartyTypeKind;
11916
+ },
11917
+ ZodFunction,
11918
+ ZodIntersection,
11919
+ ZodIssueCode,
11920
+ ZodLazy,
11921
+ ZodLiteral,
11922
+ ZodMap,
11923
+ ZodNaN,
11924
+ ZodNativeEnum,
11925
+ ZodNever,
11926
+ ZodNull,
11927
+ ZodNullable,
11928
+ ZodNumber,
11929
+ ZodObject,
11930
+ ZodOptional,
11931
+ ZodParsedType,
11932
+ ZodPipeline,
11933
+ ZodPromise,
11934
+ ZodReadonly,
11935
+ ZodRecord,
11936
+ ZodSchema: ZodType,
11937
+ ZodSet,
11938
+ ZodString,
11939
+ ZodSymbol,
11940
+ ZodTransformer: ZodEffects,
11941
+ ZodTuple,
11942
+ ZodType,
11943
+ ZodUndefined,
11944
+ ZodUnion,
11945
+ ZodUnknown,
11946
+ ZodVoid,
11947
+ addIssueToContext,
11948
+ any: anyType,
11949
+ array: arrayType,
11950
+ bigint: bigIntType,
11951
+ boolean: booleanType,
11952
+ coerce,
11953
+ custom,
11954
+ date: dateType,
11955
+ datetimeRegex,
11956
+ defaultErrorMap: errorMap,
11957
+ discriminatedUnion: discriminatedUnionType,
11958
+ effect: effectsType,
11959
+ enum: enumType,
11960
+ function: functionType,
11961
+ getErrorMap,
11962
+ getParsedType,
11963
+ instanceof: instanceOfType,
11964
+ intersection: intersectionType,
11965
+ isAborted,
11966
+ isAsync,
11967
+ isDirty,
11968
+ isValid,
11969
+ late,
11970
+ lazy: lazyType,
11971
+ literal: literalType,
11972
+ makeIssue,
11973
+ map: mapType,
11974
+ nan: nanType,
11975
+ nativeEnum: nativeEnumType,
11976
+ never: neverType,
11977
+ null: nullType,
11978
+ nullable: nullableType,
11979
+ number: numberType,
11980
+ object: objectType,
11981
+ get objectUtil() {
11982
+ return objectUtil;
11983
+ },
11984
+ oboolean,
11985
+ onumber,
11986
+ optional: optionalType,
11987
+ ostring,
11988
+ pipeline: pipelineType,
11989
+ preprocess: preprocessType,
11990
+ promise: promiseType,
11991
+ quotelessJson,
11992
+ record: recordType,
11993
+ set: setType,
11994
+ setErrorMap,
11995
+ strictObject: strictObjectType,
11996
+ string: stringType,
11997
+ symbol: symbolType,
11998
+ transformer: effectsType,
11999
+ tuple: tupleType,
12000
+ undefined: undefinedType,
12001
+ union: unionType,
12002
+ unknown: unknownType,
12003
+ get util() {
12004
+ return util$3;
12005
+ },
12006
+ void: voidType
12007
+ }, Symbol.toStringTag, { value: "Module" }));
12008
+ const BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
12009
+ function base32Encode(buffer) {
12010
+ let bits = 0;
12011
+ let value = 0;
12012
+ let output = "";
12013
+ for (let i2 = 0; i2 < buffer.length; i2++) {
12014
+ value = value << 8 | buffer[i2];
12015
+ bits += 8;
12016
+ while (bits >= 5) {
12017
+ output += BASE32_ALPHABET[value >>> bits - 5 & 31];
12018
+ bits -= 5;
12019
+ }
12020
+ }
12021
+ if (bits > 0) {
12022
+ output += BASE32_ALPHABET[value << 5 - bits & 31];
12023
+ }
12024
+ return output;
12025
+ }
12026
+ function base32Decode(encoded) {
12027
+ const cleanInput = encoded.replace(/=+$/, "").toUpperCase();
12028
+ const bytes = [];
12029
+ let bits = 0;
12030
+ let value = 0;
12031
+ for (let i2 = 0; i2 < cleanInput.length; i2++) {
12032
+ const index2 = BASE32_ALPHABET.indexOf(cleanInput[i2]);
12033
+ if (index2 === -1) continue;
12034
+ value = value << 5 | index2;
12035
+ bits += 5;
12036
+ if (bits >= 8) {
12037
+ bytes.push(value >>> bits - 8 & 255);
12038
+ bits -= 8;
12039
+ }
12040
+ }
12041
+ return Buffer.from(bytes);
12042
+ }
12043
+ function generateHotp(secret, counter) {
12044
+ const hmac = require$$0$5.createHmac("sha1", secret);
12045
+ const counterBuffer = Buffer.alloc(8);
12046
+ counterBuffer.writeBigInt64BE(counter);
12047
+ hmac.update(counterBuffer);
12048
+ const hash = hmac.digest();
12049
+ const offset = hash[hash.length - 1] & 15;
12050
+ const code2 = ((hash[offset] & 127) << 24 | hash[offset + 1] << 16 | hash[offset + 2] << 8 | hash[offset + 3]) % 1e6;
12051
+ return code2.toString().padStart(6, "0");
12052
+ }
12053
+ function verifyTotp(secret, token, window2 = 1) {
12054
+ const timeStep = 30;
12055
+ const counter = BigInt(Math.floor(Date.now() / 1e3 / timeStep));
12056
+ for (let i2 = -window2; i2 <= window2; i2++) {
12057
+ if (generateHotp(secret, counter + BigInt(i2)) === token) {
12058
+ return true;
12059
+ }
12060
+ }
12061
+ return false;
12062
+ }
12063
+ function generateTotpSecret(issuer, accountName) {
12064
+ const secretBuffer = require$$0$5.randomBytes(20);
12065
+ const secret = base32Encode(secretBuffer);
12066
+ const encodedIssuer = encodeURIComponent(issuer);
12067
+ const encodedAccount = encodeURIComponent(accountName);
12068
+ const uri = `otpauth://totp/${encodedIssuer}:${encodedAccount}?secret=${secret}&issuer=${encodedIssuer}&algorithm=SHA1&digits=6&period=30`;
12069
+ return {
12070
+ secret,
12071
+ uri
12072
+ };
12073
+ }
12074
+ function generateRecoveryCodes(count = 10) {
12075
+ return Array.from({
12076
+ length: count
12077
+ }, () => {
12078
+ const raw = require$$0$5.randomBytes(5).toString("hex").toUpperCase();
12079
+ const parts = raw.match(/.{1,5}/g);
12080
+ return parts ? parts.join("-") : raw;
12081
+ });
12082
+ }
12083
+ function hashRecoveryCode(code2) {
12084
+ return require$$0$5.createHash("sha256").update(code2.replace(/-/g, "").toUpperCase()).digest("hex");
12085
+ }
11315
12086
  function buildAuthResponse(user, roleIds, accessToken, refreshToken) {
11316
12087
  return {
11317
12088
  user: {
@@ -11349,9 +12120,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11349
12120
  emailService,
11350
12121
  emailConfig,
11351
12122
  allowRegistration = false,
11352
- overrides
12123
+ authHooks
11353
12124
  } = config;
11354
- const ops = resolveAuthOverrides(overrides);
12125
+ const ops = resolveAuthHooks(authHooks);
11355
12126
  const registerSchema = objectType({
11356
12127
  email: stringType().email("Invalid email address").max(255),
11357
12128
  password: stringType().min(1, "Password is required").max(128),
@@ -11415,7 +12186,19 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11415
12186
  async function createSessionAndTokens(userId, userAgent, ipAddress) {
11416
12187
  const roles = await authRepo.getUserRoles(userId);
11417
12188
  const roleIds = roles.map((r) => r.id);
11418
- const accessToken = generateAccessToken(userId, roleIds);
12189
+ let customClaims;
12190
+ if (authHooks?.customizeAccessToken) {
12191
+ const user = await authRepo.getUserById(userId);
12192
+ if (user) {
12193
+ const defaultClaims = {
12194
+ userId,
12195
+ roles: roleIds,
12196
+ aal: "aal1"
12197
+ };
12198
+ customClaims = await authHooks.customizeAccessToken(defaultClaims, user);
12199
+ }
12200
+ }
12201
+ const accessToken = generateAccessToken(userId, roleIds, "aal1", customClaims);
11419
12202
  const refreshToken = generateRefreshToken();
11420
12203
  await authRepo.createRefreshToken(userId, hashRefreshToken(refreshToken), getRefreshTokenExpiry(), userAgent, ipAddress);
11421
12204
  return {
@@ -11450,8 +12233,8 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11450
12233
  passwordHash,
11451
12234
  displayName: displayName || void 0
11452
12235
  };
11453
- if (overrides?.beforeUserCreate) {
11454
- createData = await overrides.beforeUserCreate(createData);
12236
+ if (authHooks?.beforeUserCreate) {
12237
+ createData = await authHooks.beforeUserCreate(createData);
11455
12238
  }
11456
12239
  const user = await authRepo.createUser(createData);
11457
12240
  const existingUsers = await authRepo.listUsers();
@@ -11470,14 +12253,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11470
12253
  email: user.email,
11471
12254
  displayName: user.displayName
11472
12255
  });
11473
- if (overrides?.afterUserCreate) {
11474
- overrides.afterUserCreate(user).catch((err) => {
11475
- console.error("[AuthOverrides] afterUserCreate error:", err instanceof Error ? err.message : err);
11476
- });
12256
+ if (authHooks?.afterUserCreate) {
12257
+ try {
12258
+ await authHooks.afterUserCreate(user);
12259
+ } catch (err) {
12260
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
12261
+ }
11477
12262
  }
11478
- if (overrides?.onAuthenticated) {
11479
- overrides.onAuthenticated(user, "register").catch((err) => {
11480
- console.error("[AuthOverrides] onAuthenticated error:", err instanceof Error ? err.message : err);
12263
+ if (authHooks?.onAuthenticated) {
12264
+ authHooks.onAuthenticated(user, "register").catch((err) => {
12265
+ console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
11481
12266
  });
11482
12267
  }
11483
12268
  return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken), 201);
@@ -11487,9 +12272,12 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11487
12272
  email,
11488
12273
  password
11489
12274
  } = parseBody2(loginSchema, await c.req.json());
12275
+ if (authHooks?.beforeLogin) {
12276
+ await authHooks.beforeLogin(email, "login");
12277
+ }
11490
12278
  let user;
11491
- if (overrides?.verifyCredentials) {
11492
- user = await overrides.verifyCredentials(email, password, authRepo);
12279
+ if (authHooks?.verifyCredentials) {
12280
+ user = await authHooks.verifyCredentials(email, password, authRepo);
11493
12281
  if (!user) {
11494
12282
  throw ApiError.unauthorized("Invalid email or password", "INVALID_CREDENTIALS");
11495
12283
  }
@@ -11511,9 +12299,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11511
12299
  accessToken,
11512
12300
  refreshToken
11513
12301
  } = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
11514
- if (overrides?.onAuthenticated) {
11515
- overrides.onAuthenticated(user, "login").catch((err) => {
11516
- console.error("[AuthOverrides] onAuthenticated error:", err instanceof Error ? err.message : err);
12302
+ if (authHooks?.onAuthenticated) {
12303
+ authHooks.onAuthenticated(user, "login").catch((err) => {
12304
+ console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
11517
12305
  });
11518
12306
  }
11519
12307
  return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken));
@@ -11552,6 +12340,13 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11552
12340
  await authRepo.linkUserIdentity(user.id, provider.id, externalUser.providerId, {
11553
12341
  email: externalUser.email
11554
12342
  });
12343
+ if (authHooks?.afterUserCreate) {
12344
+ try {
12345
+ await authHooks.afterUserCreate(user);
12346
+ } catch (err) {
12347
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
12348
+ }
12349
+ }
11555
12350
  const allUsers = await authRepo.listUsers();
11556
12351
  const isFirstUser = allUsers.length === 1 && allUsers[0].id === user.id;
11557
12352
  if (isFirstUser) {
@@ -11637,6 +12432,11 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11637
12432
  await authRepo.updatePassword(storedToken.userId, passwordHash);
11638
12433
  await authRepo.markPasswordResetTokenUsed(tokenHash);
11639
12434
  await authRepo.deleteAllRefreshTokensForUser(storedToken.userId);
12435
+ if (authHooks?.onPasswordReset) {
12436
+ authHooks.onPasswordReset(storedToken.userId).catch((err) => {
12437
+ console.error("[AuthHooks] onPasswordReset error:", err instanceof Error ? err.message : err);
12438
+ });
12439
+ }
11640
12440
  return c.json({
11641
12441
  success: true,
11642
12442
  message: "Password has been reset successfully"
@@ -11740,7 +12540,19 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11740
12540
  }
11741
12541
  const roles = await authRepo.getUserRoles(storedToken.userId);
11742
12542
  const roleIds = roles.map((r) => r.id);
11743
- const newAccessToken = generateAccessToken(storedToken.userId, roleIds);
12543
+ let customClaims;
12544
+ if (authHooks?.customizeAccessToken) {
12545
+ const user = await authRepo.getUserById(storedToken.userId);
12546
+ if (user) {
12547
+ const defaultClaims = {
12548
+ userId: storedToken.userId,
12549
+ roles: roleIds,
12550
+ aal: "aal1"
12551
+ };
12552
+ customClaims = await authHooks.customizeAccessToken(defaultClaims, user);
12553
+ }
12554
+ }
12555
+ const newAccessToken = generateAccessToken(storedToken.userId, roleIds, "aal1", customClaims);
11744
12556
  const newRefreshToken = generateRefreshToken();
11745
12557
  const userAgent = c.req.header("user-agent") || "unknown";
11746
12558
  const ipAddress = c.req.header("x-forwarded-for") || "unknown";
@@ -11762,6 +12574,18 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11762
12574
  const tokenHash = hashRefreshToken(refreshToken);
11763
12575
  await authRepo.deleteRefreshToken(tokenHash);
11764
12576
  }
12577
+ const authHeader = c.req.header("authorization");
12578
+ if (authHooks?.afterLogout && authHeader?.startsWith("Bearer ")) {
12579
+ const {
12580
+ verifyAccessToken: verifyAccessToken2
12581
+ } = await Promise.resolve().then(() => jwt);
12582
+ const payload = verifyAccessToken2(authHeader.substring(7));
12583
+ if (payload) {
12584
+ authHooks.afterLogout(payload.userId).catch((err) => {
12585
+ console.error("[AuthHooks] afterLogout error:", err instanceof Error ? err.message : err);
12586
+ });
12587
+ }
12588
+ }
11765
12589
  return c.json({
11766
12590
  success: true
11767
12591
  });
@@ -11881,6 +12705,270 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11881
12705
  enabledProviders
11882
12706
  });
11883
12707
  });
12708
+ router.post("/anonymous", strictAuthLimiter, async (c) => {
12709
+ const anonId = require$$0$5.randomBytes(16).toString("hex");
12710
+ const anonEmail = `anon_${anonId.slice(0, 8)}@anonymous.local`;
12711
+ let createData = {
12712
+ email: anonEmail,
12713
+ emailVerified: false,
12714
+ isAnonymous: true
12715
+ };
12716
+ if (authHooks?.beforeUserCreate) {
12717
+ createData = await authHooks.beforeUserCreate(createData);
12718
+ }
12719
+ const user = await authRepo.createUser(createData);
12720
+ if (config.defaultRole) {
12721
+ await authRepo.assignDefaultRole(user.id, config.defaultRole);
12722
+ }
12723
+ const {
12724
+ roleIds,
12725
+ accessToken,
12726
+ refreshToken
12727
+ } = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
12728
+ if (authHooks?.afterUserCreate) {
12729
+ authHooks.afterUserCreate(user).catch((err) => {
12730
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
12731
+ });
12732
+ }
12733
+ if (authHooks?.onAuthenticated) {
12734
+ authHooks.onAuthenticated(user, "anonymous").catch((err) => {
12735
+ console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
12736
+ });
12737
+ }
12738
+ return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken), 201);
12739
+ });
12740
+ router.post("/anonymous/link", requireAuth, async (c) => {
12741
+ const userCtx = c.get("user");
12742
+ if (!userCtx) {
12743
+ throw ApiError.unauthorized("Not authenticated");
12744
+ }
12745
+ const user = await authRepo.getUserById(userCtx.userId);
12746
+ if (!user?.isAnonymous) {
12747
+ throw ApiError.badRequest("User is not anonymous", "NOT_ANONYMOUS");
12748
+ }
12749
+ const linkSchema = objectType({
12750
+ email: stringType().email("Invalid email address").max(255),
12751
+ password: stringType().min(1, "Password is required").max(128)
12752
+ });
12753
+ const {
12754
+ email,
12755
+ password
12756
+ } = parseBody2(linkSchema, await c.req.json());
12757
+ const passwordValidation = ops.validatePasswordStrength(password);
12758
+ if (!passwordValidation.valid) {
12759
+ throw ApiError.badRequest(passwordValidation.errors.join(". "), "WEAK_PASSWORD");
12760
+ }
12761
+ const existingUser = await authRepo.getUserByEmail(email.toLowerCase());
12762
+ if (existingUser) {
12763
+ throw ApiError.conflict("Email already registered", "EMAIL_EXISTS");
12764
+ }
12765
+ const passwordHash = await ops.hashPassword(password);
12766
+ const updatedUser = await authRepo.updateUser(user.id, {
12767
+ email: email.toLowerCase(),
12768
+ passwordHash,
12769
+ isAnonymous: false
12770
+ });
12771
+ if (!updatedUser) {
12772
+ throw ApiError.notFound("User not found");
12773
+ }
12774
+ const {
12775
+ roleIds,
12776
+ accessToken,
12777
+ refreshToken
12778
+ } = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
12779
+ return c.json(buildAuthResponse(updatedUser, roleIds, accessToken, refreshToken));
12780
+ });
12781
+ router.post("/mfa/enroll", requireAuth, async (c) => {
12782
+ const userCtx = c.get("user");
12783
+ if (!userCtx) {
12784
+ throw ApiError.unauthorized("Not authenticated");
12785
+ }
12786
+ const body = await c.req.json().catch(() => ({}));
12787
+ const friendlyName = typeof body.friendlyName === "string" ? body.friendlyName : void 0;
12788
+ const issuer = typeof body.issuer === "string" ? body.issuer : emailConfig?.appName || "Rebase";
12789
+ const user = await authRepo.getUserById(userCtx.userId);
12790
+ if (!user) {
12791
+ throw ApiError.notFound("User not found");
12792
+ }
12793
+ const {
12794
+ secret,
12795
+ uri
12796
+ } = generateTotpSecret(issuer, user.email);
12797
+ const factor = await authRepo.createMfaFactor(
12798
+ user.id,
12799
+ "totp",
12800
+ secret,
12801
+ // In production, encrypt this before storage
12802
+ friendlyName
12803
+ );
12804
+ const codes = generateRecoveryCodes(10);
12805
+ const codeHashes = codes.map(hashRecoveryCode);
12806
+ await authRepo.createRecoveryCodes(user.id, codeHashes);
12807
+ return c.json({
12808
+ factor: {
12809
+ id: factor.id,
12810
+ factorType: factor.factorType,
12811
+ friendlyName: factor.friendlyName
12812
+ },
12813
+ totp: {
12814
+ secret,
12815
+ uri,
12816
+ qrUri: uri
12817
+ // Client can use a QR library to render this
12818
+ },
12819
+ recoveryCodes: codes
12820
+ }, 201);
12821
+ });
12822
+ router.post("/mfa/verify", requireAuth, async (c) => {
12823
+ const userCtx = c.get("user");
12824
+ if (!userCtx) {
12825
+ throw ApiError.unauthorized("Not authenticated");
12826
+ }
12827
+ const verifySchema = objectType({
12828
+ factorId: stringType().min(1, "Factor ID is required"),
12829
+ code: stringType().length(6, "Code must be 6 digits")
12830
+ });
12831
+ const {
12832
+ factorId,
12833
+ code: code2
12834
+ } = parseBody2(verifySchema, await c.req.json());
12835
+ const factor = await authRepo.getMfaFactorById(factorId);
12836
+ if (!factor || factor.userId !== userCtx.userId) {
12837
+ throw ApiError.notFound("MFA factor not found");
12838
+ }
12839
+ if (factor.verified) {
12840
+ throw ApiError.badRequest("Factor is already verified", "ALREADY_VERIFIED");
12841
+ }
12842
+ const secretBuffer = base32Decode(factor.secretEncrypted);
12843
+ const isValid2 = verifyTotp(secretBuffer, code2);
12844
+ if (!isValid2) {
12845
+ throw ApiError.unauthorized("Invalid TOTP code", "INVALID_CODE");
12846
+ }
12847
+ await authRepo.verifyMfaFactor(factorId);
12848
+ return c.json({
12849
+ success: true,
12850
+ message: "MFA factor verified and enrolled"
12851
+ });
12852
+ });
12853
+ router.post("/mfa/challenge", requireAuth, async (c) => {
12854
+ const userCtx = c.get("user");
12855
+ if (!userCtx) {
12856
+ throw ApiError.unauthorized("Not authenticated");
12857
+ }
12858
+ const challengeSchema = objectType({
12859
+ factorId: stringType().min(1, "Factor ID is required")
12860
+ });
12861
+ const {
12862
+ factorId
12863
+ } = parseBody2(challengeSchema, await c.req.json());
12864
+ const factor = await authRepo.getMfaFactorById(factorId);
12865
+ if (!factor || factor.userId !== userCtx.userId) {
12866
+ throw ApiError.notFound("MFA factor not found");
12867
+ }
12868
+ if (!factor.verified) {
12869
+ throw ApiError.badRequest("MFA factor is not yet verified", "FACTOR_NOT_VERIFIED");
12870
+ }
12871
+ const ipAddress = c.req.header("x-forwarded-for") || "unknown";
12872
+ const challenge = await authRepo.createMfaChallenge(factorId, ipAddress);
12873
+ return c.json({
12874
+ challengeId: challenge.id,
12875
+ factorId: challenge.factorId,
12876
+ expiresAt: new Date(Date.now() + 5 * 60 * 1e3).toISOString()
12877
+ });
12878
+ });
12879
+ router.post("/mfa/challenge/verify", requireAuth, async (c) => {
12880
+ const userCtx = c.get("user");
12881
+ if (!userCtx) {
12882
+ throw ApiError.unauthorized("Not authenticated");
12883
+ }
12884
+ const challengeVerifySchema = objectType({
12885
+ challengeId: stringType().min(1, "Challenge ID is required"),
12886
+ code: stringType().min(1, "Code is required")
12887
+ });
12888
+ const {
12889
+ challengeId,
12890
+ code: code2
12891
+ } = parseBody2(challengeVerifySchema, await c.req.json());
12892
+ const challenge = await authRepo.getMfaChallengeById(challengeId);
12893
+ if (!challenge) {
12894
+ throw ApiError.badRequest("Invalid or expired challenge", "INVALID_CHALLENGE");
12895
+ }
12896
+ const factor = await authRepo.getMfaFactorById(challenge.factorId);
12897
+ if (!factor || factor.userId !== userCtx.userId) {
12898
+ throw ApiError.notFound("MFA factor not found");
12899
+ }
12900
+ const isRecoveryCode = code2.length > 6;
12901
+ let isValid2 = false;
12902
+ if (isRecoveryCode) {
12903
+ const codeHash = hashRecoveryCode(code2);
12904
+ isValid2 = await authRepo.useRecoveryCode(userCtx.userId, codeHash);
12905
+ } else {
12906
+ const secretBuffer = base32Decode(factor.secretEncrypted);
12907
+ isValid2 = verifyTotp(secretBuffer, code2);
12908
+ }
12909
+ if (!isValid2) {
12910
+ throw ApiError.unauthorized("Invalid verification code", "INVALID_CODE");
12911
+ }
12912
+ await authRepo.verifyMfaChallenge(challengeId);
12913
+ const roles = await authRepo.getUserRoles(userCtx.userId);
12914
+ const roleIds = roles.map((r) => r.id);
12915
+ const accessToken = generateAccessToken(userCtx.userId, roleIds, "aal2");
12916
+ const refreshToken = generateRefreshToken();
12917
+ await authRepo.createRefreshToken(userCtx.userId, hashRefreshToken(refreshToken), getRefreshTokenExpiry(), c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
12918
+ if (authHooks?.onMfaVerified) {
12919
+ authHooks.onMfaVerified(userCtx.userId, factor.id).catch((err) => {
12920
+ console.error("[AuthHooks] onMfaVerified error:", err instanceof Error ? err.message : err);
12921
+ });
12922
+ }
12923
+ return c.json({
12924
+ tokens: {
12925
+ accessToken,
12926
+ refreshToken,
12927
+ accessTokenExpiresAt: getAccessTokenExpiry()
12928
+ }
12929
+ });
12930
+ });
12931
+ router.get("/mfa/factors", requireAuth, async (c) => {
12932
+ const userCtx = c.get("user");
12933
+ if (!userCtx) {
12934
+ throw ApiError.unauthorized("Not authenticated");
12935
+ }
12936
+ const factors = await authRepo.getMfaFactors(userCtx.userId);
12937
+ return c.json({
12938
+ factors: factors.map((f2) => ({
12939
+ id: f2.id,
12940
+ factorType: f2.factorType,
12941
+ friendlyName: f2.friendlyName,
12942
+ verified: f2.verified,
12943
+ createdAt: f2.createdAt
12944
+ }))
12945
+ });
12946
+ });
12947
+ router.delete("/mfa/unenroll", requireAuth, async (c) => {
12948
+ const userCtx = c.get("user");
12949
+ if (!userCtx) {
12950
+ throw ApiError.unauthorized("Not authenticated");
12951
+ }
12952
+ const unenrollSchema = objectType({
12953
+ factorId: stringType().min(1, "Factor ID is required")
12954
+ });
12955
+ const {
12956
+ factorId
12957
+ } = parseBody2(unenrollSchema, await c.req.json());
12958
+ const factor = await authRepo.getMfaFactorById(factorId);
12959
+ if (!factor || factor.userId !== userCtx.userId) {
12960
+ throw ApiError.notFound("MFA factor not found");
12961
+ }
12962
+ await authRepo.deleteMfaFactor(factorId, userCtx.userId);
12963
+ const hasFactors = await authRepo.hasVerifiedMfaFactors(userCtx.userId);
12964
+ if (!hasFactors) {
12965
+ await authRepo.deleteAllRecoveryCodes(userCtx.userId);
12966
+ }
12967
+ return c.json({
12968
+ success: true,
12969
+ message: "MFA factor removed"
12970
+ });
12971
+ });
11884
12972
  return router;
11885
12973
  }
11886
12974
  function generateSecurePassword() {
@@ -11912,9 +13000,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11912
13000
  emailService,
11913
13001
  emailConfig,
11914
13002
  hooks,
11915
- overrides
13003
+ authHooks
11916
13004
  } = config;
11917
- const ops = resolveAuthOverrides(overrides);
13005
+ const ops = resolveAuthHooks(authHooks);
11918
13006
  function buildHookContext(c, method) {
11919
13007
  const user = c.get("user");
11920
13008
  return {
@@ -11982,6 +13070,10 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11982
13070
  if (!userId) {
11983
13071
  throw ApiError.unauthorized("User ID not found in auth context");
11984
13072
  }
13073
+ const caller = await authRepo.getUserById(userId);
13074
+ if (!caller) {
13075
+ throw ApiError.notFound("Authenticated user does not exist in the database. Please sign out and sign in/register again.", "USER_NOT_FOUND");
13076
+ }
11985
13077
  await authRepo.setUserRoles(userId, ["admin"]);
11986
13078
  if (config.setBootstrapCompleted) {
11987
13079
  await config.setBootstrapCompleted();
@@ -12233,6 +13325,18 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12233
13325
  await authRepo.updateUser(userId, updates);
12234
13326
  }
12235
13327
  if (roles !== void 0 && Array.isArray(roles)) {
13328
+ const currentRoles = await authRepo.getUserRoleIds(userId);
13329
+ const wasAdmin = currentRoles.includes("admin");
13330
+ const willBeAdmin = roles.includes("admin");
13331
+ if (wasAdmin && !willBeAdmin) {
13332
+ const adminUsers = await authRepo.listUsersPaginated({
13333
+ roleId: "admin",
13334
+ limit: 1
13335
+ });
13336
+ if (adminUsers.total <= 1) {
13337
+ throw ApiError.forbidden("Cannot demote the last administrator", "LAST_ADMIN");
13338
+ }
13339
+ }
12236
13340
  await authRepo.setUserRoles(userId, roles);
12237
13341
  }
12238
13342
  const result = await authRepo.getUserWithRoles(userId);
@@ -12257,6 +13361,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12257
13361
  if (!existing) {
12258
13362
  throw ApiError.notFound("User not found");
12259
13363
  }
13364
+ const roles = await authRepo.getUserRoleIds(userId);
13365
+ if (roles.includes("admin")) {
13366
+ const adminUsers = await authRepo.listUsersPaginated({
13367
+ roleId: "admin",
13368
+ limit: 1
13369
+ });
13370
+ if (adminUsers.total <= 1) {
13371
+ throw ApiError.forbidden("Cannot delete the last administrator", "LAST_ADMIN");
13372
+ }
13373
+ }
12260
13374
  const hookCtx = buildHookContext(c, "DELETE");
12261
13375
  if (hooks?.users?.beforeDelete) {
12262
13376
  await hooks.users.beforeDelete(userId, hookCtx);
@@ -12278,8 +13392,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12278
13392
  id: r.id,
12279
13393
  name: r.name,
12280
13394
  isAdmin: r.isAdmin,
12281
- defaultPermissions: r.defaultPermissions,
12282
- config: r.config
13395
+ defaultPermissions: r.defaultPermissions
12283
13396
  }));
12284
13397
  adminRoles = await applyRoleAfterReadBatch(adminRoles, hookCtx);
12285
13398
  return c.json({
@@ -12302,8 +13415,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12302
13415
  id,
12303
13416
  name: name2,
12304
13417
  isAdmin,
12305
- defaultPermissions,
12306
- config: config2
13418
+ defaultPermissions
12307
13419
  } = body;
12308
13420
  if (!id || !name2) {
12309
13421
  throw ApiError.badRequest("Role ID and name are required", "INVALID_INPUT");
@@ -12316,8 +13428,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12316
13428
  id,
12317
13429
  name: name2,
12318
13430
  isAdmin: isAdmin ?? false,
12319
- defaultPermissions: defaultPermissions ?? null,
12320
- config: config2 ?? null
13431
+ defaultPermissions: defaultPermissions ?? null
12321
13432
  });
12322
13433
  return c.json({
12323
13434
  role
@@ -12329,8 +13440,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12329
13440
  const {
12330
13441
  name: name2,
12331
13442
  isAdmin,
12332
- defaultPermissions,
12333
- config: config2
13443
+ defaultPermissions
12334
13444
  } = body;
12335
13445
  const existing = await authRepo.getRoleById(roleId);
12336
13446
  if (!existing) {
@@ -12339,8 +13449,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12339
13449
  const role = await authRepo.updateRole(roleId, {
12340
13450
  name: name2,
12341
13451
  isAdmin,
12342
- defaultPermissions,
12343
- config: config2
13452
+ defaultPermissions
12344
13453
  });
12345
13454
  return c.json({
12346
13455
  role
@@ -12372,9 +13481,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12372
13481
  oauthProviders = [],
12373
13482
  serviceKey,
12374
13483
  hooks,
12375
- overrides
13484
+ authHooks
12376
13485
  } = config;
12377
- const resolvedOps = resolveAuthOverrides(overrides);
13486
+ const resolvedOps = resolveAuthHooks(authHooks);
12378
13487
  const adapter = {
12379
13488
  id: "rebase-builtin",
12380
13489
  serviceKey,
@@ -12448,7 +13557,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12448
13557
  rawToken: token
12449
13558
  };
12450
13559
  },
12451
- userManagement: createUserManagementFromRepo(authRepository, resolvedOps, overrides),
13560
+ userManagement: createUserManagementFromRepo(authRepository, resolvedOps, authHooks),
12452
13561
  roleManagement: createRoleManagementFromRepo(authRepository),
12453
13562
  createAuthRoutes() {
12454
13563
  return createAuthRoutes({
@@ -12458,7 +13567,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12458
13567
  allowRegistration,
12459
13568
  defaultRole,
12460
13569
  oauthProviders,
12461
- overrides
13570
+ authHooks
12462
13571
  });
12463
13572
  },
12464
13573
  createAdminRoutes() {
@@ -12468,7 +13577,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12468
13577
  emailConfig,
12469
13578
  serviceKey,
12470
13579
  hooks,
12471
- overrides
13580
+ authHooks
12472
13581
  });
12473
13582
  },
12474
13583
  async getCapabilities() {
@@ -12497,7 +13606,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12497
13606
  };
12498
13607
  return adapter;
12499
13608
  }
12500
- function createUserManagementFromRepo(repo, resolvedOps, overrides) {
13609
+ function createUserManagementFromRepo(repo, resolvedOps, authHooks) {
12501
13610
  return {
12502
13611
  async listUsers(options2) {
12503
13612
  const result = await repo.listUsersPaginated({
@@ -12528,14 +13637,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12528
13637
  photoUrl: data.photoUrl,
12529
13638
  metadata: data.metadata
12530
13639
  };
12531
- if (overrides?.beforeUserCreate) {
12532
- createData = await overrides.beforeUserCreate(createData);
13640
+ if (authHooks?.beforeUserCreate) {
13641
+ createData = await authHooks.beforeUserCreate(createData);
12533
13642
  }
12534
13643
  const user = await repo.createUser(createData);
12535
- if (overrides?.afterUserCreate) {
12536
- overrides.afterUserCreate(user).catch((err) => {
12537
- console.error("[AuthOverrides] afterUserCreate error:", err instanceof Error ? err.message : err);
12538
- });
13644
+ if (authHooks?.afterUserCreate) {
13645
+ try {
13646
+ await authHooks.afterUserCreate(user);
13647
+ } catch (err) {
13648
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
13649
+ }
12539
13650
  }
12540
13651
  return toAuthUserData(user);
12541
13652
  },
@@ -12552,7 +13663,15 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12552
13663
  return user ? toAuthUserData(user) : null;
12553
13664
  },
12554
13665
  async deleteUser(id) {
13666
+ if (authHooks?.beforeUserDelete) {
13667
+ await authHooks.beforeUserDelete(id);
13668
+ }
12555
13669
  await repo.deleteUser(id);
13670
+ if (authHooks?.afterUserDelete) {
13671
+ authHooks.afterUserDelete(id).catch((err) => {
13672
+ console.error("[AuthHooks] afterUserDelete error:", err instanceof Error ? err.message : err);
13673
+ });
13674
+ }
12556
13675
  },
12557
13676
  async getUserRoles(userId) {
12558
13677
  const roles = await repo.getUserRoles(userId);
@@ -12579,8 +13698,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12579
13698
  name: data.name,
12580
13699
  isAdmin: data.isAdmin,
12581
13700
  defaultPermissions: data.defaultPermissions,
12582
- collectionPermissions: data.collectionPermissions,
12583
- config: data.config
13701
+ collectionPermissions: data.collectionPermissions
12584
13702
  });
12585
13703
  return toAuthRoleData(role);
12586
13704
  },
@@ -12611,8 +13729,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12611
13729
  name: role.name,
12612
13730
  isAdmin: role.isAdmin,
12613
13731
  defaultPermissions: role.defaultPermissions,
12614
- collectionPermissions: role.collectionPermissions,
12615
- config: role.config
13732
+ collectionPermissions: role.collectionPermissions
12616
13733
  };
12617
13734
  }
12618
13735
  function configureLogLevel(logLevel) {
@@ -12633,20 +13750,20 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12633
13750
  if (currentLevel < 0) console.error = () => {
12634
13751
  };
12635
13752
  }
13753
+ let originalConsole;
12636
13754
  function resetConsole() {
12637
- if (!global.__originalConsole) {
12638
- global.__originalConsole = {
13755
+ if (!originalConsole) {
13756
+ originalConsole = {
12639
13757
  log: console.log,
12640
13758
  warn: console.warn,
12641
13759
  error: console.error,
12642
13760
  debug: console.debug
12643
13761
  };
12644
13762
  }
12645
- const original = global.__originalConsole;
12646
- console.log = original.log;
12647
- console.warn = original.warn;
12648
- console.error = original.error;
12649
- console.debug = original.debug;
13763
+ console.log = originalConsole.log;
13764
+ console.warn = originalConsole.warn;
13765
+ console.error = originalConsole.error;
13766
+ console.debug = originalConsole.debug;
12650
13767
  }
12651
13768
  const GCP_SEVERITY = {
12652
13769
  debug: "DEBUG",
@@ -12888,12 +14005,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12888
14005
  exports3.Response = globalObject.Response;
12889
14006
  })(browser$1, browser$1.exports);
12890
14007
  var browserExports = browser$1.exports;
12891
- const __viteBrowserExternal = {};
12892
- const __viteBrowserExternal$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12893
- __proto__: null,
12894
- default: __viteBrowserExternal
12895
- }, Symbol.toStringTag, { value: "Module" }));
12896
- const require$$11 = /* @__PURE__ */ getAugmentedNamespace(__viteBrowserExternal$1);
12897
14008
  const isStream = (stream2) => stream2 !== null && typeof stream2 === "object" && typeof stream2.pipe === "function";
12898
14009
  isStream.writable = (stream2) => isStream(stream2) && stream2.writable !== false && typeof stream2._write === "function" && typeof stream2._writableState === "object";
12899
14010
  isStream.readable = (stream2) => isStream(stream2) && stream2.readable !== false && typeof stream2._read === "function" && typeof stream2._readableState === "object";
@@ -13678,16 +14789,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
13678
14789
  value: true
13679
14790
  });
13680
14791
  sha1$1.default = void 0;
13681
- function f(s2, x, y2, z) {
14792
+ function f(s2, x, y2, z2) {
13682
14793
  switch (s2) {
13683
14794
  case 0:
13684
- return x & y2 ^ ~x & z;
14795
+ return x & y2 ^ ~x & z2;
13685
14796
  case 1:
13686
- return x ^ y2 ^ z;
14797
+ return x ^ y2 ^ z2;
13687
14798
  case 2:
13688
- return x & y2 ^ x & z ^ y2 & z;
14799
+ return x & y2 ^ x & z2 ^ y2 & z2;
13689
14800
  case 3:
13690
- return x ^ y2 ^ z;
14801
+ return x ^ y2 ^ z2;
13691
14802
  }
13692
14803
  }
13693
14804
  function ROTL(x, n) {
@@ -14736,7 +15847,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
14736
15847
  const extend_1 = __importDefault(extend);
14737
15848
  const https_1 = require$$1;
14738
15849
  const node_fetch_1 = __importDefault(browserExports);
14739
- const querystring_1$1 = __importDefault(require$$11);
15850
+ const querystring_1$1 = __importDefault(require$$1$2);
14740
15851
  const is_stream_1 = __importDefault(isStream_1);
14741
15852
  const url_1 = require$$0$2;
14742
15853
  const common_1 = common$1;
@@ -16414,11 +17525,11 @@ Content-Type: ${partContentType}\r
16414
17525
  return n > 0 || n === i2 ? i2 : i2 - 1;
16415
17526
  }
16416
17527
  function coeffToString(a2) {
16417
- var s2, z, i2 = 1, j = a2.length, r = a2[0] + "";
17528
+ var s2, z2, i2 = 1, j = a2.length, r = a2[0] + "";
16418
17529
  for (; i2 < j; ) {
16419
17530
  s2 = a2[i2++] + "";
16420
- z = LOG_BASE - s2.length;
16421
- for (; z--; s2 = "0" + s2) ;
17531
+ z2 = LOG_BASE - s2.length;
17532
+ for (; z2--; s2 = "0" + s2) ;
16422
17533
  r += s2;
16423
17534
  }
16424
17535
  for (j = r.length; r.charCodeAt(--j) === 48; ) ;
@@ -16451,15 +17562,15 @@ Content-Type: ${partContentType}\r
16451
17562
  function toExponential(str, e) {
16452
17563
  return (str.length > 1 ? str.charAt(0) + "." + str.slice(1) : str) + (e < 0 ? "e" : "e+") + e;
16453
17564
  }
16454
- function toFixedPoint(str, e, z) {
17565
+ function toFixedPoint(str, e, z2) {
16455
17566
  var len2, zs;
16456
17567
  if (e < 0) {
16457
- for (zs = z + "."; ++e; zs += z) ;
17568
+ for (zs = z2 + "."; ++e; zs += z2) ;
16458
17569
  str = zs + str;
16459
17570
  } else {
16460
17571
  len2 = str.length;
16461
17572
  if (++e > len2) {
16462
- for (zs = z, e -= len2; --e; zs += z) ;
17573
+ for (zs = z2, e -= len2; --e; zs += z2) ;
16463
17574
  str += zs;
16464
17575
  } else if (e < len2) {
16465
17576
  str = str.slice(0, e) + "." + str.slice(e);
@@ -16878,7 +17989,7 @@ Content-Type: ${partContentType}\r
16878
17989
  exports3.isGoogleComputeEngine = isGoogleComputeEngine;
16879
17990
  exports3.detectGCPResidency = detectGCPResidency;
16880
17991
  const fs_1 = fs$4;
16881
- const os_1 = require$$1$2;
17992
+ const os_1 = require$$1$3;
16882
17993
  exports3.GCE_LINUX_BIOS_PATHS = {
16883
17994
  BIOS_DATE: "/sys/class/dmi/id/bios_date",
16884
17995
  BIOS_VENDOR: "/sys/class/dmi/id/bios_vendor"
@@ -17011,7 +18122,7 @@ Content-Type: ${partContentType}\r
17011
18122
  exports3.setBackend = setBackend;
17012
18123
  exports3.log = log;
17013
18124
  const node_events_1 = require$$0$8;
17014
- const process2 = __importStar2(require$$1$3);
18125
+ const process2 = __importStar2(require$$1$4);
17015
18126
  const util2 = __importStar2(require$$2$1);
17016
18127
  const colours_1 = colours;
17017
18128
  var LogSeverity;
@@ -18083,7 +19194,7 @@ Content-Type: ${partContentType}\r
18083
19194
  Object.defineProperty(oauth2client, "__esModule", { value: true });
18084
19195
  oauth2client.OAuth2Client = oauth2client.ClientAuthentication = oauth2client.CertificateFormat = oauth2client.CodeChallengeMethod = void 0;
18085
19196
  const gaxios_1$5 = src$2;
18086
- const querystring$2 = require$$11;
19197
+ const querystring$2 = require$$1$2;
18087
19198
  const stream$4 = require$$0$4;
18088
19199
  const formatEcdsa = ecdsaSigFormatter;
18089
19200
  const crypto_1$2 = requireCrypto();
@@ -19571,7 +20682,7 @@ Content-Type: ${partContentType}\r
19571
20682
  Object.defineProperty(refreshclient, "__esModule", { value: true });
19572
20683
  refreshclient.UserRefreshClient = refreshclient.USER_REFRESH_ACCOUNT_TYPE = void 0;
19573
20684
  const oauth2client_1$1 = oauth2client;
19574
- const querystring_1 = require$$11;
20685
+ const querystring_1 = require$$1$2;
19575
20686
  refreshclient.USER_REFRESH_ACCOUNT_TYPE = "authorized_user";
19576
20687
  class UserRefreshClient extends oauth2client_1$1.OAuth2Client {
19577
20688
  constructor(optionsOrClientId, clientSecret, refreshToken, eagerRefreshThresholdMillis, forceRefreshOnFailure) {
@@ -19846,7 +20957,7 @@ Content-Type: ${partContentType}\r
19846
20957
  Object.defineProperty(oauth2common, "__esModule", { value: true });
19847
20958
  oauth2common.OAuthClientAuthHandler = void 0;
19848
20959
  oauth2common.getErrorFromOAuthErrorResponse = getErrorFromOAuthErrorResponse;
19849
- const querystring$1 = require$$11;
20960
+ const querystring$1 = require$$1$2;
19850
20961
  const crypto_1$1 = requireCrypto();
19851
20962
  const METHODS_SUPPORTING_REQUEST_BODY = ["PUT", "POST", "PATCH"];
19852
20963
  class OAuthClientAuthHandler {
@@ -19992,7 +21103,7 @@ Content-Type: ${partContentType}\r
19992
21103
  Object.defineProperty(stscredentials, "__esModule", { value: true });
19993
21104
  stscredentials.StsCredentials = void 0;
19994
21105
  const gaxios_1$1 = src$2;
19995
- const querystring = require$$11;
21106
+ const querystring = require$$1$2;
19996
21107
  const transporters_1 = transporters;
19997
21108
  const oauth2common_1$1 = oauth2common;
19998
21109
  class StsCredentials extends oauth2common_1$1.OAuthClientAuthHandler {
@@ -21598,7 +22709,7 @@ ${credentialScope}
21598
22709
  const child_process_1 = require$$0$a;
21599
22710
  const fs2 = fs$4;
21600
22711
  const gcpMetadata2 = src$3;
21601
- const os2 = require$$1$2;
22712
+ const os2 = require$$1$3;
21602
22713
  const path2 = path$3;
21603
22714
  const crypto_12 = requireCrypto();
21604
22715
  const transporters_12 = transporters;
@@ -22962,7 +24073,7 @@ ${credentialScope}
22962
24073
  }
22963
24074
  function createAppleProvider(config) {
22964
24075
  async function generateClientSecret() {
22965
- return jwt.sign({}, config.privateKey, {
24076
+ return jwt$1.sign({}, config.privateKey, {
22966
24077
  algorithm: "ES256",
22967
24078
  keyid: config.keyId,
22968
24079
  issuer: config.teamId,
@@ -23442,6 +24553,341 @@ ${credentialScope}
23442
24553
  }
23443
24554
  };
23444
24555
  }
24556
+ const TABLE$1 = "rebase.api_keys";
24557
+ function generateApiKey() {
24558
+ const random = require$$0$5.randomBytes(16).toString("hex");
24559
+ return `rk_live_${random}`;
24560
+ }
24561
+ function hashKey(plaintext) {
24562
+ return require$$0$5.createHash("sha256").update(plaintext).digest("hex");
24563
+ }
24564
+ function keyPrefix(plaintext) {
24565
+ return plaintext.substring(0, 12);
24566
+ }
24567
+ function toMasked(row) {
24568
+ return {
24569
+ id: row.id,
24570
+ name: row.name,
24571
+ key_prefix: row.key_prefix,
24572
+ permissions: row.permissions,
24573
+ rate_limit: row.rate_limit,
24574
+ created_by: row.created_by,
24575
+ created_at: row.created_at,
24576
+ updated_at: row.updated_at,
24577
+ last_used_at: row.last_used_at,
24578
+ expires_at: row.expires_at,
24579
+ revoked_at: row.revoked_at
24580
+ };
24581
+ }
24582
+ function rowToApiKey(row) {
24583
+ const permissions = typeof row.permissions === "string" ? JSON.parse(row.permissions) : row.permissions ?? [];
24584
+ return {
24585
+ id: row.id,
24586
+ name: row.name,
24587
+ key_prefix: row.key_prefix,
24588
+ key_hash: row.key_hash,
24589
+ permissions,
24590
+ rate_limit: row.rate_limit !== null && row.rate_limit !== void 0 ? Number(row.rate_limit) : null,
24591
+ created_by: row.created_by,
24592
+ created_at: new Date(row.created_at).toISOString(),
24593
+ updated_at: new Date(row.updated_at).toISOString(),
24594
+ last_used_at: row.last_used_at ? new Date(row.last_used_at).toISOString() : null,
24595
+ expires_at: row.expires_at ? new Date(row.expires_at).toISOString() : null,
24596
+ revoked_at: row.revoked_at ? new Date(row.revoked_at).toISOString() : null
24597
+ };
24598
+ }
24599
+ function esc(value) {
24600
+ return value.replace(/'/g, "''");
24601
+ }
24602
+ function createApiKeyStore(driver) {
24603
+ const admin = driver.admin;
24604
+ if (!isSQLAdmin(admin)) {
24605
+ logger.warn("⚠️ [api-key-store] DataDriver does not support SQL admin — API keys will not be available.");
24606
+ return void 0;
24607
+ }
24608
+ const exec = admin.executeSql.bind(admin);
24609
+ return {
24610
+ // ── Schema bootstrap ────────────────────────────────────────
24611
+ async ensureTable() {
24612
+ try {
24613
+ await exec("CREATE SCHEMA IF NOT EXISTS rebase");
24614
+ await exec(`
24615
+ CREATE TABLE IF NOT EXISTS ${TABLE$1} (
24616
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
24617
+ name TEXT NOT NULL,
24618
+ key_prefix TEXT NOT NULL,
24619
+ key_hash TEXT NOT NULL UNIQUE,
24620
+ permissions JSONB NOT NULL DEFAULT '[]'::jsonb,
24621
+ rate_limit INTEGER,
24622
+ created_by TEXT NOT NULL,
24623
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
24624
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
24625
+ last_used_at TIMESTAMPTZ,
24626
+ expires_at TIMESTAMPTZ,
24627
+ revoked_at TIMESTAMPTZ
24628
+ )
24629
+ `);
24630
+ await exec(`
24631
+ CREATE INDEX IF NOT EXISTS idx_api_keys_hash
24632
+ ON ${TABLE$1}(key_hash)
24633
+ `);
24634
+ await exec(`
24635
+ CREATE INDEX IF NOT EXISTS idx_api_keys_prefix
24636
+ ON ${TABLE$1}(key_prefix)
24637
+ `);
24638
+ logger.info("✅ API keys table ready");
24639
+ } catch (err) {
24640
+ logger.error("❌ Failed to create API keys table", {
24641
+ error: err
24642
+ });
24643
+ logger.warn("⚠️ Continuing without API keys support.");
24644
+ }
24645
+ },
24646
+ // ── Create ──────────────────────────────────────────────────
24647
+ async createApiKey(request, createdBy) {
24648
+ const plaintext = generateApiKey();
24649
+ const hash = hashKey(plaintext);
24650
+ const prefix = keyPrefix(plaintext);
24651
+ const permissionsJson = JSON.stringify(request.permissions);
24652
+ const rateLimitSql = request.rate_limit !== null && request.rate_limit !== void 0 ? String(request.rate_limit) : "NULL";
24653
+ const expiresAtSql = request.expires_at ? `'${esc(request.expires_at)}'` : "NULL";
24654
+ const rows = await exec(`
24655
+ INSERT INTO ${TABLE$1} (name, key_prefix, key_hash, permissions, rate_limit, created_by, expires_at)
24656
+ VALUES (
24657
+ '${esc(request.name)}',
24658
+ '${esc(prefix)}',
24659
+ '${esc(hash)}',
24660
+ '${esc(permissionsJson)}'::jsonb,
24661
+ ${rateLimitSql},
24662
+ '${esc(createdBy)}',
24663
+ ${expiresAtSql}
24664
+ )
24665
+ RETURNING *
24666
+ `);
24667
+ const apiKey = rowToApiKey(rows[0]);
24668
+ return {
24669
+ ...toMasked(apiKey),
24670
+ key: plaintext
24671
+ };
24672
+ },
24673
+ // ── Lookup by hash ──────────────────────────────────────────
24674
+ async findByKeyHash(hash) {
24675
+ const rows = await exec(`
24676
+ SELECT * FROM ${TABLE$1}
24677
+ WHERE key_hash = '${esc(hash)}'
24678
+ LIMIT 1
24679
+ `);
24680
+ if (rows.length === 0) return null;
24681
+ return rowToApiKey(rows[0]);
24682
+ },
24683
+ // ── List all (masked) ───────────────────────────────────────
24684
+ async listApiKeys() {
24685
+ const rows = await exec(`
24686
+ SELECT * FROM ${TABLE$1}
24687
+ ORDER BY created_at DESC
24688
+ `);
24689
+ return rows.map((r) => toMasked(rowToApiKey(r)));
24690
+ },
24691
+ // ── Get by ID (masked) ──────────────────────────────────────
24692
+ async getApiKeyById(id) {
24693
+ const rows = await exec(`
24694
+ SELECT * FROM ${TABLE$1}
24695
+ WHERE id = '${esc(id)}'
24696
+ LIMIT 1
24697
+ `);
24698
+ if (rows.length === 0) return null;
24699
+ return toMasked(rowToApiKey(rows[0]));
24700
+ },
24701
+ // ── Update ──────────────────────────────────────────────────
24702
+ async updateApiKey(id, updates) {
24703
+ const setClauses = [];
24704
+ if (updates.name !== void 0) {
24705
+ setClauses.push(`name = '${esc(updates.name)}'`);
24706
+ }
24707
+ if (updates.permissions !== void 0) {
24708
+ setClauses.push(`permissions = '${esc(JSON.stringify(updates.permissions))}'::jsonb`);
24709
+ }
24710
+ if (updates.rate_limit !== void 0) {
24711
+ setClauses.push(updates.rate_limit !== null ? `rate_limit = ${updates.rate_limit}` : "rate_limit = NULL");
24712
+ }
24713
+ if (updates.expires_at !== void 0) {
24714
+ setClauses.push(updates.expires_at !== null ? `expires_at = '${esc(updates.expires_at)}'` : "expires_at = NULL");
24715
+ }
24716
+ if (setClauses.length === 0) {
24717
+ return this.getApiKeyById(id);
24718
+ }
24719
+ setClauses.push("updated_at = NOW()");
24720
+ const rows = await exec(`
24721
+ UPDATE ${TABLE$1}
24722
+ SET ${setClauses.join(", ")}
24723
+ WHERE id = '${esc(id)}'
24724
+ RETURNING *
24725
+ `);
24726
+ if (rows.length === 0) return null;
24727
+ return toMasked(rowToApiKey(rows[0]));
24728
+ },
24729
+ // ── Revoke (soft-delete) ────────────────────────────────────
24730
+ async revokeApiKey(id) {
24731
+ const rows = await exec(`
24732
+ UPDATE ${TABLE$1}
24733
+ SET revoked_at = NOW(), updated_at = NOW()
24734
+ WHERE id = '${esc(id)}' AND revoked_at IS NULL
24735
+ RETURNING id
24736
+ `);
24737
+ return rows.length > 0;
24738
+ },
24739
+ // ── Touch last_used_at ──────────────────────────────────────
24740
+ async updateLastUsed(id) {
24741
+ try {
24742
+ await exec(`
24743
+ UPDATE ${TABLE$1}
24744
+ SET last_used_at = NOW()
24745
+ WHERE id = '${esc(id)}'
24746
+ `);
24747
+ } catch (err) {
24748
+ logger.error("[api-key-store] Failed to update last_used_at", {
24749
+ error: err
24750
+ });
24751
+ }
24752
+ }
24753
+ };
24754
+ }
24755
+ function validatePermissions(permissions) {
24756
+ if (!Array.isArray(permissions)) return false;
24757
+ for (const perm of permissions) {
24758
+ if (typeof perm !== "object" || perm === null) return false;
24759
+ if (typeof perm.collection !== "string" || perm.collection.length === 0) return false;
24760
+ if (!Array.isArray(perm.operations) || perm.operations.length === 0) return false;
24761
+ const validOps = /* @__PURE__ */ new Set(["read", "write", "delete"]);
24762
+ for (const op of perm.operations) {
24763
+ if (!validOps.has(op)) return false;
24764
+ }
24765
+ }
24766
+ return true;
24767
+ }
24768
+ function createApiKeyRoutes(options2) {
24769
+ const {
24770
+ store,
24771
+ serviceKey
24772
+ } = options2;
24773
+ const router = new hono.Hono();
24774
+ router.onError(errorHandler);
24775
+ router.use("/*", createRequireAuth({
24776
+ serviceKey
24777
+ }));
24778
+ router.use("/*", requireAdmin);
24779
+ router.get("/", async (c) => {
24780
+ const keys2 = await store.listApiKeys();
24781
+ return c.json({
24782
+ keys: keys2
24783
+ });
24784
+ });
24785
+ router.post("/", async (c) => {
24786
+ const body = await c.req.json();
24787
+ const {
24788
+ name: name2,
24789
+ permissions,
24790
+ rate_limit,
24791
+ expires_at
24792
+ } = body;
24793
+ if (!name2 || typeof name2 !== "string" || name2.trim().length === 0) {
24794
+ throw ApiError.badRequest("Name is required", "INVALID_INPUT");
24795
+ }
24796
+ if (!validatePermissions(permissions)) {
24797
+ throw ApiError.badRequest("Permissions must be an array of { collection: string, operations: ('read' | 'write' | 'delete')[] }", "INVALID_INPUT");
24798
+ }
24799
+ if (rate_limit !== void 0 && rate_limit !== null) {
24800
+ if (typeof rate_limit !== "number" || rate_limit < 1 || !Number.isInteger(rate_limit)) {
24801
+ throw ApiError.badRequest("rate_limit must be a positive integer or null", "INVALID_INPUT");
24802
+ }
24803
+ }
24804
+ if (expires_at !== void 0 && expires_at !== null) {
24805
+ const parsed = new Date(expires_at);
24806
+ if (isNaN(parsed.getTime())) {
24807
+ throw ApiError.badRequest("expires_at must be a valid ISO-8601 date", "INVALID_INPUT");
24808
+ }
24809
+ if (parsed <= /* @__PURE__ */ new Date()) {
24810
+ throw ApiError.badRequest("expires_at must be in the future", "INVALID_INPUT");
24811
+ }
24812
+ }
24813
+ const user = c.get("user");
24814
+ const createdBy = user && typeof user === "object" && "userId" in user ? user.userId : "unknown";
24815
+ const request = {
24816
+ name: name2.trim(),
24817
+ permissions,
24818
+ rate_limit: rate_limit ?? null,
24819
+ expires_at: expires_at ?? null
24820
+ };
24821
+ const keyWithSecret = await store.createApiKey(request, createdBy);
24822
+ return c.json({
24823
+ key: keyWithSecret
24824
+ }, 201);
24825
+ });
24826
+ router.get("/:id", async (c) => {
24827
+ const id = c.req.param("id");
24828
+ const key = await store.getApiKeyById(id);
24829
+ if (!key) {
24830
+ throw ApiError.notFound("API key not found");
24831
+ }
24832
+ return c.json({
24833
+ key
24834
+ });
24835
+ });
24836
+ router.put("/:id", async (c) => {
24837
+ const id = c.req.param("id");
24838
+ const body = await c.req.json();
24839
+ const {
24840
+ name: name2,
24841
+ permissions,
24842
+ rate_limit,
24843
+ expires_at
24844
+ } = body;
24845
+ if (name2 !== void 0) {
24846
+ if (typeof name2 !== "string" || name2.trim().length === 0) {
24847
+ throw ApiError.badRequest("Name must be a non-empty string", "INVALID_INPUT");
24848
+ }
24849
+ }
24850
+ if (permissions !== void 0) {
24851
+ if (!validatePermissions(permissions)) {
24852
+ throw ApiError.badRequest("Permissions must be an array of { collection: string, operations: ('read' | 'write' | 'delete')[] }", "INVALID_INPUT");
24853
+ }
24854
+ }
24855
+ if (rate_limit !== void 0 && rate_limit !== null) {
24856
+ if (typeof rate_limit !== "number" || rate_limit < 1 || !Number.isInteger(rate_limit)) {
24857
+ throw ApiError.badRequest("rate_limit must be a positive integer or null", "INVALID_INPUT");
24858
+ }
24859
+ }
24860
+ if (expires_at !== void 0 && expires_at !== null) {
24861
+ const parsed = new Date(expires_at);
24862
+ if (isNaN(parsed.getTime())) {
24863
+ throw ApiError.badRequest("expires_at must be a valid ISO-8601 date", "INVALID_INPUT");
24864
+ }
24865
+ }
24866
+ const updates = {};
24867
+ if (name2 !== void 0) updates.name = name2.trim();
24868
+ if (permissions !== void 0) updates.permissions = permissions;
24869
+ if (rate_limit !== void 0) updates.rate_limit = rate_limit;
24870
+ if (expires_at !== void 0) updates.expires_at = expires_at;
24871
+ const key = await store.updateApiKey(id, updates);
24872
+ if (!key) {
24873
+ throw ApiError.notFound("API key not found");
24874
+ }
24875
+ return c.json({
24876
+ key
24877
+ });
24878
+ });
24879
+ router.delete("/:id", async (c) => {
24880
+ const id = c.req.param("id");
24881
+ const revoked = await store.revokeApiKey(id);
24882
+ if (!revoked) {
24883
+ throw ApiError.notFound("API key not found or already revoked");
24884
+ }
24885
+ return c.json({
24886
+ success: true
24887
+ });
24888
+ });
24889
+ return router;
24890
+ }
23445
24891
  function createCustomAuthAdapter(options2) {
23446
24892
  const defaultCapabilities = {
23447
24893
  hasBuiltInAuthRoutes: false,
@@ -23476,9 +24922,13 @@ ${credentialScope}
23476
24922
  }
23477
24923
  const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
23478
24924
  __proto__: null,
24925
+ apiKeyKeyGenerator,
23479
24926
  configureJwt,
23480
24927
  createAdapterAuthMiddleware,
23481
24928
  createAdminRoutes,
24929
+ createApiKeyRateLimiter,
24930
+ createApiKeyRoutes,
24931
+ createApiKeyStore,
23482
24932
  createAppleProvider,
23483
24933
  createAuthMiddleware,
23484
24934
  createAuthRoutes,
@@ -23504,11 +24954,15 @@ ${credentialScope}
23504
24954
  getRefreshTokenExpiry,
23505
24955
  hashPassword,
23506
24956
  hashRefreshToken,
24957
+ httpMethodToOperation,
24958
+ isApiKeyToken,
24959
+ isOperationAllowed,
23507
24960
  optionalAuth,
23508
24961
  requireAdmin,
23509
24962
  requireAuth,
23510
- resolveAuthOverrides,
24963
+ resolveAuthHooks,
23511
24964
  strictAuthLimiter,
24965
+ validateApiKey,
23512
24966
  validatePasswordStrength,
23513
24967
  verifyAccessToken,
23514
24968
  verifyPassword
@@ -24033,6 +25487,409 @@ ${credentialScope}
24033
25487
  };
24034
25488
  }
24035
25489
  }
25490
+ let sharpFactory;
25491
+ async function getSharp() {
25492
+ if (!sharpFactory) {
25493
+ try {
25494
+ const mod = await import("sharp");
25495
+ sharpFactory = mod.default;
25496
+ } catch (err) {
25497
+ throw new Error("Failed to load optional 'sharp' dependency for image transformation.");
25498
+ }
25499
+ }
25500
+ if (!sharpFactory) {
25501
+ throw new Error("Failed to load optional 'sharp' dependency for image transformation.");
25502
+ }
25503
+ return sharpFactory;
25504
+ }
25505
+ const MAX_DIMENSION = 4096;
25506
+ const MAX_QUALITY = 100;
25507
+ const MIN_QUALITY = 1;
25508
+ const VALID_FORMATS = /* @__PURE__ */ new Set(["webp", "avif", "jpeg", "png"]);
25509
+ const VALID_FITS = /* @__PURE__ */ new Set(["cover", "contain", "fill", "inside", "outside"]);
25510
+ function parseTransformOptions(query) {
25511
+ const opts = {};
25512
+ let hasTransform = false;
25513
+ if (query.width) {
25514
+ const w2 = parseInt(query.width, 10);
25515
+ if (!Number.isNaN(w2) && w2 > 0) {
25516
+ opts.width = Math.min(w2, MAX_DIMENSION);
25517
+ hasTransform = true;
25518
+ }
25519
+ }
25520
+ if (query.height) {
25521
+ const h2 = parseInt(query.height, 10);
25522
+ if (!Number.isNaN(h2) && h2 > 0) {
25523
+ opts.height = Math.min(h2, MAX_DIMENSION);
25524
+ hasTransform = true;
25525
+ }
25526
+ }
25527
+ if (query.quality) {
25528
+ const q = parseInt(query.quality, 10);
25529
+ if (!Number.isNaN(q)) {
25530
+ opts.quality = Math.min(Math.max(q, MIN_QUALITY), MAX_QUALITY);
25531
+ hasTransform = true;
25532
+ }
25533
+ }
25534
+ if (query.format && VALID_FORMATS.has(query.format)) {
25535
+ opts.format = query.format;
25536
+ hasTransform = true;
25537
+ }
25538
+ if (query.fit && VALID_FITS.has(query.fit)) {
25539
+ opts.fit = query.fit;
25540
+ hasTransform = true;
25541
+ }
25542
+ return hasTransform ? opts : null;
25543
+ }
25544
+ const FORMAT_CONTENT_TYPES = {
25545
+ webp: "image/webp",
25546
+ avif: "image/avif",
25547
+ jpeg: "image/jpeg",
25548
+ png: "image/png"
25549
+ };
25550
+ function isTransformableImage(contentType) {
25551
+ return contentType.startsWith("image/") && !contentType.includes("svg") && !contentType.includes("gif");
25552
+ }
25553
+ async function transformImage(buffer, options2) {
25554
+ const sharp = await getSharp();
25555
+ let pipeline = sharp(buffer);
25556
+ if (options2.width || options2.height) {
25557
+ pipeline = pipeline.resize({
25558
+ width: options2.width,
25559
+ height: options2.height,
25560
+ fit: options2.fit || "cover",
25561
+ withoutEnlargement: true
25562
+ });
25563
+ }
25564
+ const format = options2.format || "webp";
25565
+ const quality = options2.quality || 80;
25566
+ switch (format) {
25567
+ case "webp":
25568
+ pipeline = pipeline.webp({
25569
+ quality
25570
+ });
25571
+ break;
25572
+ case "avif":
25573
+ pipeline = pipeline.avif({
25574
+ quality
25575
+ });
25576
+ break;
25577
+ case "jpeg":
25578
+ pipeline = pipeline.jpeg({
25579
+ quality
25580
+ });
25581
+ break;
25582
+ case "png":
25583
+ pipeline = pipeline.png({
25584
+ quality
25585
+ });
25586
+ break;
25587
+ }
25588
+ const data = await pipeline.toBuffer();
25589
+ return {
25590
+ data,
25591
+ contentType: FORMAT_CONTENT_TYPES[format]
25592
+ };
25593
+ }
25594
+ class TransformCache {
25595
+ cache = /* @__PURE__ */ new Map();
25596
+ maxEntries;
25597
+ maxAgeMs;
25598
+ constructor(maxEntries = 500, maxAgeMs = 36e5) {
25599
+ this.maxEntries = maxEntries;
25600
+ this.maxAgeMs = maxAgeMs;
25601
+ }
25602
+ /** Build a deterministic cache key from file key + transform options. */
25603
+ buildKey(fileKey, options2) {
25604
+ return `${fileKey}::${JSON.stringify(options2)}`;
25605
+ }
25606
+ get(cacheKey) {
25607
+ const entry = this.cache.get(cacheKey);
25608
+ if (!entry) return null;
25609
+ if (Date.now() - entry.timestamp > this.maxAgeMs) {
25610
+ this.cache.delete(cacheKey);
25611
+ return null;
25612
+ }
25613
+ this.cache.delete(cacheKey);
25614
+ this.cache.set(cacheKey, entry);
25615
+ return {
25616
+ data: entry.data,
25617
+ contentType: entry.contentType
25618
+ };
25619
+ }
25620
+ set(cacheKey, data, contentType) {
25621
+ if (this.cache.size >= this.maxEntries) {
25622
+ const oldest = this.cache.keys().next().value;
25623
+ if (oldest !== void 0) this.cache.delete(oldest);
25624
+ }
25625
+ this.cache.set(cacheKey, {
25626
+ data,
25627
+ contentType,
25628
+ timestamp: Date.now()
25629
+ });
25630
+ }
25631
+ }
25632
+ const MAX_UPLOAD_SIZE = 5 * 1024 * 1024 * 1024;
25633
+ const UPLOAD_EXPIRY_MS = 24 * 60 * 60 * 1e3;
25634
+ class TusHandler {
25635
+ constructor(storageBaseDir, storageController) {
25636
+ this.storageController = storageController;
25637
+ this.tusDir = path$3.join(storageBaseDir, ".tus-uploads");
25638
+ }
25639
+ uploads = /* @__PURE__ */ new Map();
25640
+ tusDir;
25641
+ cleanupTimer;
25642
+ /** Ensure the temp directory exists. */
25643
+ async ensureDir() {
25644
+ if (!fs$4.existsSync(this.tusDir)) {
25645
+ await promises.mkdir(this.tusDir, {
25646
+ recursive: true
25647
+ });
25648
+ }
25649
+ }
25650
+ /** Start periodic cleanup of stale uploads. */
25651
+ startCleanup() {
25652
+ if (this.cleanupTimer) return;
25653
+ this.cleanupTimer = setInterval(() => {
25654
+ void this.cleanupStale();
25655
+ }, 6e4);
25656
+ }
25657
+ /** Remove uploads that have been idle for longer than UPLOAD_EXPIRY_MS. */
25658
+ async cleanupStale() {
25659
+ const now = Date.now();
25660
+ for (const [id, upload] of this.uploads) {
25661
+ if (now - upload.createdAt > UPLOAD_EXPIRY_MS && !upload.completed) {
25662
+ try {
25663
+ await promises.unlink(upload.filePath);
25664
+ } catch {
25665
+ }
25666
+ this.uploads.delete(id);
25667
+ }
25668
+ }
25669
+ }
25670
+ // -----------------------------------------------------------------------
25671
+ // TUS Metadata Parsing
25672
+ // -----------------------------------------------------------------------
25673
+ /**
25674
+ * Parse the `Upload-Metadata` header.
25675
+ *
25676
+ * Format: `key base64value,key2 base64value2`
25677
+ */
25678
+ parseMetadata(header) {
25679
+ const metadata = {};
25680
+ if (!header) return metadata;
25681
+ for (const pair of header.split(",")) {
25682
+ const trimmed = pair.trim();
25683
+ const spaceIdx = trimmed.indexOf(" ");
25684
+ if (spaceIdx === -1) {
25685
+ metadata[trimmed] = "";
25686
+ } else {
25687
+ const key = trimmed.substring(0, spaceIdx);
25688
+ const value = Buffer.from(trimmed.substring(spaceIdx + 1), "base64").toString("utf-8");
25689
+ metadata[key] = value;
25690
+ }
25691
+ }
25692
+ return metadata;
25693
+ }
25694
+ // -----------------------------------------------------------------------
25695
+ // Protocol Endpoints
25696
+ // -----------------------------------------------------------------------
25697
+ /** `OPTIONS /tus` — TUS capability discovery. */
25698
+ options() {
25699
+ return new Response(null, {
25700
+ status: 204,
25701
+ headers: {
25702
+ "Tus-Resumable": "1.0.0",
25703
+ "Tus-Version": "1.0.0",
25704
+ "Tus-Extension": "creation,termination",
25705
+ "Tus-Max-Size": String(MAX_UPLOAD_SIZE)
25706
+ }
25707
+ });
25708
+ }
25709
+ /** `POST /tus` — Create a new upload. */
25710
+ async create(c) {
25711
+ await this.ensureDir();
25712
+ const uploadLengthHeader = c.req.header("Upload-Length");
25713
+ if (!uploadLengthHeader) {
25714
+ return c.json({
25715
+ error: "Upload-Length header is required"
25716
+ }, 400);
25717
+ }
25718
+ const uploadLength = parseInt(uploadLengthHeader, 10);
25719
+ if (Number.isNaN(uploadLength) || uploadLength <= 0) {
25720
+ return c.json({
25721
+ error: "Invalid Upload-Length"
25722
+ }, 400);
25723
+ }
25724
+ if (uploadLength > MAX_UPLOAD_SIZE) {
25725
+ return c.json({
25726
+ error: `Upload-Length exceeds maximum of ${MAX_UPLOAD_SIZE} bytes`
25727
+ }, 413);
25728
+ }
25729
+ const metadata = this.parseMetadata(c.req.header("Upload-Metadata") || "");
25730
+ const id = require$$0$5.randomUUID();
25731
+ const filePath = path$3.join(this.tusDir, id);
25732
+ await promises.writeFile(filePath, Buffer.alloc(0));
25733
+ const upload = {
25734
+ id,
25735
+ size: uploadLength,
25736
+ offset: 0,
25737
+ metadata,
25738
+ createdAt: Date.now(),
25739
+ filePath,
25740
+ bucket: metadata.bucket || void 0,
25741
+ key: metadata.key || metadata.filename || void 0,
25742
+ completed: false
25743
+ };
25744
+ this.uploads.set(id, upload);
25745
+ const reqUrl = new URL(c.req.url);
25746
+ const location = `${reqUrl.origin}${reqUrl.pathname}/${id}`;
25747
+ return new Response(null, {
25748
+ status: 201,
25749
+ headers: {
25750
+ Location: location,
25751
+ "Tus-Resumable": "1.0.0",
25752
+ "Upload-Offset": "0"
25753
+ }
25754
+ });
25755
+ }
25756
+ /** `HEAD /tus/:id` — Query upload progress. */
25757
+ head(c, id) {
25758
+ const upload = this.uploads.get(id);
25759
+ if (!upload) {
25760
+ return c.json({
25761
+ error: "Upload not found"
25762
+ }, 404);
25763
+ }
25764
+ return new Response(null, {
25765
+ status: 200,
25766
+ headers: {
25767
+ "Tus-Resumable": "1.0.0",
25768
+ "Upload-Offset": String(upload.offset),
25769
+ "Upload-Length": String(upload.size),
25770
+ "Cache-Control": "no-store"
25771
+ }
25772
+ });
25773
+ }
25774
+ /** `PATCH /tus/:id` — Append data to an upload. */
25775
+ async patch(c, id) {
25776
+ const upload = this.uploads.get(id);
25777
+ if (!upload) {
25778
+ return c.json({
25779
+ error: "Upload not found"
25780
+ }, 404);
25781
+ }
25782
+ if (upload.completed) {
25783
+ return c.json({
25784
+ error: "Upload already completed"
25785
+ }, 400);
25786
+ }
25787
+ const offsetHeader = c.req.header("Upload-Offset");
25788
+ if (!offsetHeader) {
25789
+ return c.json({
25790
+ error: "Upload-Offset header is required"
25791
+ }, 400);
25792
+ }
25793
+ const offset = parseInt(offsetHeader, 10);
25794
+ if (offset !== upload.offset) {
25795
+ return c.json({
25796
+ error: "Offset mismatch"
25797
+ }, 409);
25798
+ }
25799
+ const contentType = c.req.header("Content-Type");
25800
+ if (contentType !== "application/offset+octet-stream") {
25801
+ return c.json({
25802
+ error: "Content-Type must be application/offset+octet-stream"
25803
+ }, 415);
25804
+ }
25805
+ const body = await c.req.arrayBuffer();
25806
+ const chunk = Buffer.from(body);
25807
+ if (upload.offset + chunk.length > upload.size) {
25808
+ return c.json({
25809
+ error: "Chunk exceeds declared Upload-Length"
25810
+ }, 413);
25811
+ }
25812
+ const fh = await promises.open(upload.filePath, "a");
25813
+ try {
25814
+ await fh.write(chunk);
25815
+ } finally {
25816
+ await fh.close();
25817
+ }
25818
+ upload.offset += chunk.length;
25819
+ if (upload.offset >= upload.size) {
25820
+ await this.finalize(upload);
25821
+ }
25822
+ return new Response(null, {
25823
+ status: 204,
25824
+ headers: {
25825
+ "Tus-Resumable": "1.0.0",
25826
+ "Upload-Offset": String(upload.offset)
25827
+ }
25828
+ });
25829
+ }
25830
+ /** `DELETE /tus/:id` — Cancel and remove an upload. */
25831
+ async delete(c, id) {
25832
+ const upload = this.uploads.get(id);
25833
+ if (!upload) {
25834
+ return c.json({
25835
+ error: "Upload not found"
25836
+ }, 404);
25837
+ }
25838
+ try {
25839
+ await promises.unlink(upload.filePath);
25840
+ } catch {
25841
+ }
25842
+ this.uploads.delete(id);
25843
+ return new Response(null, {
25844
+ status: 204,
25845
+ headers: {
25846
+ "Tus-Resumable": "1.0.0"
25847
+ }
25848
+ });
25849
+ }
25850
+ // -----------------------------------------------------------------------
25851
+ // Finalization
25852
+ // -----------------------------------------------------------------------
25853
+ /**
25854
+ * Move a completed upload into the storage controller.
25855
+ */
25856
+ async finalize(upload) {
25857
+ upload.completed = true;
25858
+ if (!this.storageController) {
25859
+ logger.warn("[TUS] Upload completed but no StorageController configured. Temp file remains:", {
25860
+ filePath: upload.filePath
25861
+ });
25862
+ return;
25863
+ }
25864
+ try {
25865
+ const {
25866
+ readFile: readFile2
25867
+ } = await import("fs/promises");
25868
+ const data = await readFile2(upload.filePath);
25869
+ const fileName = upload.key || upload.metadata.filename || upload.id;
25870
+ const mimeType = upload.metadata.contentType || upload.metadata.filetype || "application/octet-stream";
25871
+ const file = new File([data], fileName, {
25872
+ type: mimeType
25873
+ });
25874
+ await this.storageController.putObject({
25875
+ file,
25876
+ key: fileName,
25877
+ bucket: upload.bucket
25878
+ });
25879
+ try {
25880
+ await promises.unlink(upload.filePath);
25881
+ } catch {
25882
+ }
25883
+ this.uploads.delete(upload.id);
25884
+ logger.info(`[TUS] Upload ${upload.id} finalized → ${fileName}`);
25885
+ } catch (err) {
25886
+ logger.error(`[TUS] Failed to finalize upload ${upload.id}`, {
25887
+ error: err
25888
+ });
25889
+ }
25890
+ }
25891
+ }
25892
+ const transformCache = new TransformCache();
24036
25893
  function extractWildcardPath(c) {
24037
25894
  const routePath = c.req.routePath;
24038
25895
  const prefix = routePath.replace("/*", "");
@@ -24096,6 +25953,7 @@ ${credentialScope}
24096
25953
  throw ApiError.notFound("File not found");
24097
25954
  }
24098
25955
  const filePath = decodeURIComponent(rawPath);
25956
+ const transformOpts = parseTransformOptions(c.req.query());
24099
25957
  if (controller.getType() === "local") {
24100
25958
  const localController = controller;
24101
25959
  const {
@@ -24115,8 +25973,19 @@ ${credentialScope}
24115
25973
  } catch {
24116
25974
  }
24117
25975
  }
24118
- c.header("Content-Type", contentType);
24119
25976
  const fileContent = fs__namespace.readFileSync(absolutePath);
25977
+ if (transformOpts && isTransformableImage(contentType)) {
25978
+ const cacheKey = transformCache.buildKey(filePath, transformOpts);
25979
+ let cached = transformCache.get(cacheKey);
25980
+ if (!cached) {
25981
+ cached = await transformImage(Buffer.from(fileContent), transformOpts);
25982
+ transformCache.set(cacheKey, cached.data, cached.contentType);
25983
+ }
25984
+ c.header("Content-Type", cached.contentType);
25985
+ c.header("Cache-Control", "public, max-age=31536000, immutable");
25986
+ return c.body(new Uint8Array(cached.data));
25987
+ }
25988
+ c.header("Content-Type", contentType);
24120
25989
  return c.body(new Uint8Array(fileContent));
24121
25990
  }
24122
25991
  const {
@@ -24127,7 +25996,20 @@ ${credentialScope}
24127
25996
  if (!fileObject) {
24128
25997
  throw ApiError.notFound("File not found");
24129
25998
  }
24130
- c.header("Content-Type", fileObject.type || "application/octet-stream");
25999
+ const remoteContentType = fileObject.type || "application/octet-stream";
26000
+ if (transformOpts && isTransformableImage(remoteContentType)) {
26001
+ const cacheKey = transformCache.buildKey(filePath, transformOpts);
26002
+ let cached = transformCache.get(cacheKey);
26003
+ if (!cached) {
26004
+ const buf2 = Buffer.from(await fileObject.arrayBuffer());
26005
+ cached = await transformImage(buf2, transformOpts);
26006
+ transformCache.set(cacheKey, cached.data, cached.contentType);
26007
+ }
26008
+ c.header("Content-Type", cached.contentType);
26009
+ c.header("Cache-Control", "public, max-age=31536000, immutable");
26010
+ return c.body(new Uint8Array(cached.data));
26011
+ }
26012
+ c.header("Content-Type", remoteContentType);
24131
26013
  c.header("Cache-Control", "public, max-age=3600, immutable");
24132
26014
  const buf = await fileObject.arrayBuffer();
24133
26015
  return c.body(new Uint8Array(buf));
@@ -24223,6 +26105,14 @@ ${credentialScope}
24223
26105
  message: "Folder created"
24224
26106
  }, 201);
24225
26107
  });
26108
+ const tusBaseDir = controller.getType() === "local" ? controller.getBasePath() : process.env.STORAGE_PATH || "./uploads";
26109
+ const tusHandler = new TusHandler(tusBaseDir, controller);
26110
+ tusHandler.startCleanup();
26111
+ router.options("/tus", (_c2) => tusHandler.options());
26112
+ router.post("/tus", writeAuthMiddleware, async (c) => tusHandler.create(c));
26113
+ router.get("/tus/:id", readAuthMiddleware, (c) => tusHandler.head(c, c.req.param("id")));
26114
+ router.patch("/tus/:id", writeAuthMiddleware, async (c) => tusHandler.patch(c, c.req.param("id")));
26115
+ router.delete("/tus/:id", writeAuthMiddleware, async (c) => tusHandler.delete(c, c.req.param("id")));
24226
26116
  return router;
24227
26117
  }
24228
26118
  const DEFAULT_STORAGE_ID = "(default)";
@@ -24446,6 +26336,13 @@ ${credentialScope}
24446
26336
  } catch (e) {
24447
26337
  }
24448
26338
  }
26339
+ const getErrorField = (obj, field) => {
26340
+ const err = obj?.error;
26341
+ if (err && typeof err === "object" && err !== null && field in err) {
26342
+ return err[field];
26343
+ }
26344
+ return obj?.[field];
26345
+ };
24449
26346
  if (res.status === 401 && onUnauthorizedHandler) {
24450
26347
  const retried = await onUnauthorizedHandler();
24451
26348
  if (retried) {
@@ -24479,7 +26376,7 @@ ${credentialScope}
24479
26376
  const method = init?.method || "GET";
24480
26377
  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.`;
24481
26378
  }
24482
- 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);
26379
+ throw new RebaseApiError(retryRes.status, String(getErrorField(retryBody, "message") || fallbackMessage || `Request failed with status ${retryRes.status}`), getErrorField(retryBody, "code"), getErrorField(retryBody, "details"));
24483
26380
  }
24484
26381
  return retryBody;
24485
26382
  }
@@ -24490,7 +26387,7 @@ ${credentialScope}
24490
26387
  const method = init?.method || "GET";
24491
26388
  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.`;
24492
26389
  }
24493
- 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);
26390
+ throw new RebaseApiError(res.status, String(getErrorField(body, "message") || fallbackMessage || `Request failed with status ${res.status}`), getErrorField(body, "code"), getErrorField(body, "details"));
24494
26391
  }
24495
26392
  return body;
24496
26393
  }
@@ -25746,7 +27643,7 @@ ${credentialScope}
25746
27643
  const http = require$$0$6;
25747
27644
  const https = require$$1;
25748
27645
  const urllib$2 = require$$0$2;
25749
- const zlib = require$$11;
27646
+ const zlib = require$$3;
25750
27647
  const PassThrough$3 = require$$0$4.PassThrough;
25751
27648
  const Cookies = cookies;
25752
27649
  const packageData$7 = require$$9;
@@ -25981,9 +27878,9 @@ ${credentialScope}
25981
27878
  const util2 = require$$5;
25982
27879
  const fs2 = fs$4;
25983
27880
  const nmfetch2 = fetchExports;
25984
- const dns2 = require$$11;
27881
+ const dns2 = require$$4$1;
25985
27882
  const net2 = require$$0$7;
25986
- const os2 = require$$1$2;
27883
+ const os2 = require$$1$3;
25987
27884
  const DNS_TTL = 5 * 60 * 1e3;
25988
27885
  let networkInterfaces;
25989
27886
  try {
@@ -32129,7 +34026,7 @@ ${credentialScope}
32129
34026
  const packageData$6 = require$$9;
32130
34027
  const MailMessage = mailMessage;
32131
34028
  const net$1 = require$$0$7;
32132
- const dns = require$$11;
34029
+ const dns = require$$4$1;
32133
34030
  const crypto$3 = require$$0$5;
32134
34031
  class Mail extends EventEmitter$5 {
32135
34032
  constructor(transporter, options2, defaults) {
@@ -32565,7 +34462,7 @@ ${credentialScope}
32565
34462
  const EventEmitter$4 = require$$0$9.EventEmitter;
32566
34463
  const net = require$$0$7;
32567
34464
  const tls = require$$1$1;
32568
- const os = require$$1$2;
34465
+ const os = require$$1$3;
32569
34466
  const crypto$2 = require$$0$5;
32570
34467
  const DataStream = dataStream;
32571
34468
  const PassThrough = require$$0$4.PassThrough;
@@ -36753,7 +38650,7 @@ ${credentialScope}
36753
38650
  oauthProviders,
36754
38651
  serviceKey,
36755
38652
  hooks: config.hooks,
36756
- overrides: safeAuthConfig.overrides
38653
+ authHooks: safeAuthConfig.hooks
36757
38654
  });
36758
38655
  }
36759
38656
  logger.info("Authentication initialized");
@@ -36898,7 +38795,7 @@ ${credentialScope}
36898
38795
  oauthProviders,
36899
38796
  serviceKey,
36900
38797
  hooks: config.hooks,
36901
- overrides: safeAuthConfig.overrides
38798
+ authHooks: safeAuthConfig.hooks
36902
38799
  });
36903
38800
  }
36904
38801
  if (authAdapter.createAuthRoutes) {
@@ -36920,6 +38817,21 @@ ${credentialScope}
36920
38817
  }
36921
38818
  }
36922
38819
  }
38820
+ let apiKeyStore;
38821
+ const apiKeyStoreResult = createApiKeyStore(defaultDriver);
38822
+ if (apiKeyStoreResult) {
38823
+ apiKeyStore = apiKeyStoreResult;
38824
+ await apiKeyStore.ensureTable();
38825
+ logger.info("Service API Keys initialized");
38826
+ const apiKeyRoutes = createApiKeyRoutes({
38827
+ store: apiKeyStore,
38828
+ serviceKey
38829
+ });
38830
+ config.app.route(`${basePath}/admin/api-keys`, apiKeyRoutes);
38831
+ logger.info("API key admin routes mounted", {
38832
+ path: `${basePath}/admin/api-keys`
38833
+ });
38834
+ }
36923
38835
  if (config.collectionsDir) {
36924
38836
  if (process.env.NODE_ENV !== "production") {
36925
38837
  const {
@@ -36970,15 +38882,20 @@ ${credentialScope}
36970
38882
  dataRouter.use("/*", createAdapterAuthMiddleware({
36971
38883
  adapter: authAdapter,
36972
38884
  driver: defaultDriver,
36973
- requireAuth: dataRequireAuth
38885
+ requireAuth: dataRequireAuth,
38886
+ apiKeyStore
36974
38887
  }));
36975
38888
  } else {
36976
38889
  dataRouter.use("/*", createAuthMiddleware({
36977
38890
  driver: defaultDriver,
36978
38891
  requireAuth: dataRequireAuth,
36979
- serviceKey
38892
+ serviceKey,
38893
+ apiKeyStore
36980
38894
  }));
36981
38895
  }
38896
+ if (apiKeyStore) {
38897
+ dataRouter.use("/*", createApiKeyRateLimiter());
38898
+ }
36982
38899
  if (historyConfigResult && historyConfigResult.historyService) {
36983
38900
  const historyRoutes = createHistoryRoutes({
36984
38901
  historyService: historyConfigResult.historyService,
@@ -37072,13 +38989,15 @@ ${credentialScope}
37072
38989
  functionsRouter.use("/*", createAdapterAuthMiddleware({
37073
38990
  adapter: authAdapter,
37074
38991
  driver: defaultDriver,
37075
- requireAuth: functionsRequireAuth
38992
+ requireAuth: functionsRequireAuth,
38993
+ apiKeyStore
37076
38994
  }));
37077
38995
  } else {
37078
38996
  functionsRouter.use("/*", createAuthMiddleware({
37079
38997
  driver: defaultDriver,
37080
38998
  requireAuth: functionsRequireAuth,
37081
- serviceKey
38999
+ serviceKey,
39000
+ apiKeyStore
37082
39001
  }));
37083
39002
  }
37084
39003
  const fnRoutes = createFunctionRoutes2(loadedFunctions);
@@ -47077,7 +48996,7 @@ spurious results.`);
47077
48996
  return "undefined";
47078
48997
  }
47079
48998
  if (typeof obj === "string") {
47080
- return `"${obj.replace(/"/g, '\\"')}"`;
48999
+ return JSON.stringify(obj);
47081
49000
  }
47082
49001
  if (typeof obj === "number" || typeof obj === "boolean") {
47083
49002
  return String(obj);
@@ -47108,7 +49027,7 @@ ${indent2}]`;
47108
49027
  const kind = init.getKind();
47109
49028
  const isCode = kind === tsMorph.SyntaxKind.ArrowFunction || kind === tsMorph.SyntaxKind.FunctionExpression || kind === tsMorph.SyntaxKind.Identifier || kind === tsMorph.SyntaxKind.CallExpression || kind === tsMorph.SyntaxKind.JsxElement;
47110
49029
  if (isCode || name2 === "target" || name2 === "callbacks" || name2 === "permissions" || name2 === "securityRules") {
47111
- const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name2) ? name2 : `"${name2}"`;
49030
+ const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name2) ? name2 : JSON.stringify(name2);
47112
49031
  preservedProps.push(`${keyStr}: ${init.getText()}`);
47113
49032
  }
47114
49033
  }
@@ -47118,7 +49037,7 @@ ${indent2}]`;
47118
49037
  }
47119
49038
  if (keys2.length === 0 && preservedProps.length === 0) return "{}";
47120
49039
  const props = keys2.map((key) => {
47121
- const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : `"${key}"`;
49040
+ const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
47122
49041
  let childAstNode;
47123
49042
  if (oldAstNode && typeof record[key] === "object" && record[key] !== null && !Array.isArray(record[key])) {
47124
49043
  const oldProp = oldAstNode.getProperty((p) => "getName" in p && typeof p.getName === "function" && (p.getName() === key || p.getName() === `"${key}"` || p.getName() === `'${key}'`));
@@ -47160,7 +49079,7 @@ ${indent2}}`;
47160
49079
  }
47161
49080
  } else {
47162
49081
  propsObj.addPropertyAssignment({
47163
- name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey : `"${propertyKey}"`,
49082
+ name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey : JSON.stringify(propertyKey),
47164
49083
  initializer: newInitializer
47165
49084
  });
47166
49085
  }
@@ -48231,7 +50150,7 @@ export default ${safeId}Collection;
48231
50150
  const mod = await dynamicImport(fileUrl);
48232
50151
  const exported = mod.default;
48233
50152
  if (!exported) {
48234
- console.warn(`[functions] ${file}: no default export. Skipping.`);
50153
+ logger.warn(`[functions] ${file}: no default export. Skipping.`);
48235
50154
  continue;
48236
50155
  }
48237
50156
  if (isHonoLike(exported)) {
@@ -48240,7 +50159,7 @@ export default ${safeId}Collection;
48240
50159
  name: name2,
48241
50160
  app: exported
48242
50161
  });
48243
- console.log(`⚡ Loaded function route: ${name2}`);
50162
+ logger.info(`⚡ Loaded function route: ${name2}`);
48244
50163
  continue;
48245
50164
  }
48246
50165
  if (typeof exported === "function") {
@@ -48251,20 +50170,20 @@ export default ${safeId}Collection;
48251
50170
  name: name2,
48252
50171
  app: result
48253
50172
  });
48254
- console.log(`⚡ Loaded function route: ${name2}`);
50173
+ logger.info(`⚡ Loaded function route: ${name2}`);
48255
50174
  continue;
48256
50175
  }
48257
50176
  }
48258
50177
  const exportType = typeof exported;
48259
50178
  const keys2 = exported && typeof exported === "object" ? Object.getOwnPropertyNames(Object.getPrototypeOf(exported)).slice(0, 10).join(", ") : "N/A";
48260
- console.warn(`[functions] ${file}: default export is not a Hono app or factory. Skipping.
50179
+ logger.warn(`[functions] ${file}: default export is not a Hono app or factory. Skipping.
48261
50180
  export type: ${exportType}${exported?.constructor?.name ? ` (${exported.constructor.name})` : ""}
48262
50181
  prototype methods: ${keys2}
48263
50182
  Hint: ensure the function exports a Hono app created with the same hono version as the server.
48264
50183
  The loader checks for .fetch() and .routes — any Hono-compatible app will work.`);
48265
50184
  } catch (err) {
48266
50185
  const message = err instanceof Error ? err.message : String(err);
48267
- console.error(`[functions] Failed to load ${file}: ${message}`);
50186
+ logger.error(`[functions] Failed to load ${file}: ${message}`);
48268
50187
  }
48269
50188
  }
48270
50189
  }
@@ -48313,12 +50232,12 @@ export default ${safeId}Collection;
48313
50232
  const mod = await dynamicImport(fileUrl);
48314
50233
  const exported = mod.default;
48315
50234
  if (!exported || typeof exported !== "object") {
48316
- console.warn(`[cron] ${file}: no valid default export. Skipping.`);
50235
+ logger.warn(`[cron] ${file}: no valid default export. Skipping.`);
48317
50236
  continue;
48318
50237
  }
48319
50238
  const def = exported;
48320
50239
  if (typeof def.schedule !== "string" || typeof def.handler !== "function") {
48321
- console.warn(`[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`);
50240
+ logger.warn(`[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`);
48322
50241
  continue;
48323
50242
  }
48324
50243
  const id = path__namespace.basename(file, path__namespace.extname(file));
@@ -48334,10 +50253,10 @@ export default ${safeId}Collection;
48334
50253
  id,
48335
50254
  definition
48336
50255
  });
48337
- console.log(`⏰ Loaded cron job: ${id} (${definition.schedule})`);
50256
+ logger.info(`⏰ Loaded cron job: ${id} (${definition.schedule})`);
48338
50257
  } catch (err) {
48339
50258
  const message = err instanceof Error ? err.message : String(err);
48340
- console.error(`[cron] Failed to load ${file}: ${message}`);
50259
+ logger.error(`[cron] Failed to load ${file}: ${message}`);
48341
50260
  }
48342
50261
  }
48343
50262
  }
@@ -48492,12 +50411,12 @@ export default ${safeId}Collection;
48492
50411
  for (const loaded of loadedJobs) {
48493
50412
  const validation = validateCronExpression(loaded.definition.schedule);
48494
50413
  if (!validation.valid) {
48495
- console.error(`[cron] Rejecting job "${loaded.id}": invalid schedule "${loaded.definition.schedule}" — ${validation.reason}`);
50414
+ logger.error(`[cron] Rejecting job "${loaded.id}": invalid schedule "${loaded.definition.schedule}" — ${validation.reason}`);
48496
50415
  continue;
48497
50416
  }
48498
50417
  const existing = this.jobs.get(loaded.id);
48499
50418
  if (existing) {
48500
- console.warn(`[cron] Duplicate cron job id: "${loaded.id}". Overwriting.`);
50419
+ logger.warn(`[cron] Duplicate cron job id: "${loaded.id}". Overwriting.`);
48501
50420
  this.stopJob(loaded.id);
48502
50421
  }
48503
50422
  const enabled = loaded.definition.enabled !== false;
@@ -48535,7 +50454,9 @@ export default ${safeId}Collection;
48535
50454
  }
48536
50455
  }
48537
50456
  }).catch((err) => {
48538
- console.warn("[cron] Failed to seed job stats from database:", err);
50457
+ logger.warn("[cron] Failed to seed job stats from database", {
50458
+ error: err
50459
+ });
48539
50460
  });
48540
50461
  }
48541
50462
  for (const [id, job] of this.jobs) {
@@ -48543,7 +50464,7 @@ export default ${safeId}Collection;
48543
50464
  this.scheduleNext(id);
48544
50465
  }
48545
50466
  }
48546
- console.log(`⏰ Cron scheduler started with ${this.jobs.size} job(s)`);
50467
+ logger.info(`⏰ Cron scheduler started with ${this.jobs.size} job(s)`);
48547
50468
  }
48548
50469
  /**
48549
50470
  * Stop the scheduler and clear all timers.
@@ -48617,7 +50538,7 @@ export default ${safeId}Collection;
48617
50538
  const job = this.jobs.get(id);
48618
50539
  if (!job) return void 0;
48619
50540
  if (job.executing) {
48620
- console.warn(`[cron] Skipping manual trigger of "${id}" — already executing`);
50541
+ logger.warn(`[cron] Skipping manual trigger of "${id}" — already executing`);
48621
50542
  const logEntry = {
48622
50543
  jobId: id,
48623
50544
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -48661,7 +50582,7 @@ export default ${safeId}Collection;
48661
50582
  const timer = setTimeout(async () => {
48662
50583
  if (!job.enabled || !this.started) return;
48663
50584
  if (job.executing) {
48664
- console.warn(`[cron] Skipping scheduled run of "${id}" — still executing from previous run`);
50585
+ logger.warn(`[cron] Skipping scheduled run of "${id}" — still executing from previous run`);
48665
50586
  this.scheduleNext(id);
48666
50587
  return;
48667
50588
  }
@@ -48675,7 +50596,9 @@ export default ${safeId}Collection;
48675
50596
  }
48676
50597
  job.timerId = timer;
48677
50598
  } catch (err) {
48678
- console.error(`[cron] Failed to schedule "${id}":`, err);
50599
+ logger.error(`[cron] Failed to schedule "${id}"`, {
50600
+ error: err
50601
+ });
48679
50602
  job.state = "error";
48680
50603
  job.lastError = err instanceof Error ? err.message : String(err);
48681
50604
  }
@@ -48760,13 +50683,15 @@ export default ${safeId}Collection;
48760
50683
  }
48761
50684
  if (this.store) {
48762
50685
  this.store.insertLog(logEntry).catch((persistErr) => {
48763
- console.error(`[cron] Failed to persist log for "${job.id}":`, persistErr);
50686
+ logger.error(`[cron] Failed to persist log for "${job.id}"`, {
50687
+ error: persistErr
50688
+ });
48764
50689
  });
48765
50690
  }
48766
50691
  if (success) {
48767
- console.log(`✅ [cron] "${job.id}" completed in ${durationMs}ms`);
50692
+ logger.info(`✅ [cron] "${job.id}" completed in ${durationMs}ms`);
48768
50693
  } else {
48769
- console.error(`❌ [cron] "${job.id}" failed in ${durationMs}ms: ${error2}`);
50694
+ logger.error(`❌ [cron] "${job.id}" failed in ${durationMs}ms: ${error2}`);
48770
50695
  }
48771
50696
  return logEntry;
48772
50697
  }
@@ -48884,7 +50809,7 @@ export default ${safeId}Collection;
48884
50809
  function createCronStore(driver) {
48885
50810
  const admin = driver.admin;
48886
50811
  if (!isSQLAdmin(admin)) {
48887
- console.warn("⚠️ [cron-store] DataDriver does not support SQL admin — cron logs will not be persisted.");
50812
+ logger.warn("⚠️ [cron-store] DataDriver does not support SQL admin — cron logs will not be persisted.");
48888
50813
  return void 0;
48889
50814
  }
48890
50815
  const exec = admin.executeSql.bind(admin);
@@ -48910,10 +50835,12 @@ export default ${safeId}Collection;
48910
50835
  CREATE INDEX IF NOT EXISTS idx_cron_logs_job
48911
50836
  ON ${TABLE}(job_id, started_at DESC)
48912
50837
  `);
48913
- console.log("✅ Cron logs table ready");
50838
+ logger.info("✅ Cron logs table ready");
48914
50839
  } catch (err) {
48915
- console.error("❌ Failed to create cron logs table:", err);
48916
- console.warn("⚠️ Continuing without cron log persistence.");
50840
+ logger.error("❌ Failed to create cron logs table", {
50841
+ error: err
50842
+ });
50843
+ logger.warn("⚠️ Continuing without cron log persistence.");
48917
50844
  }
48918
50845
  },
48919
50846
  async insertLog(entry) {
@@ -48936,7 +50863,9 @@ export default ${safeId}Collection;
48936
50863
  )
48937
50864
  `);
48938
50865
  } catch (err) {
48939
- console.error(`[cron-store] Failed to persist log for "${entry.jobId}":`, err);
50866
+ logger.error(`[cron-store] Failed to persist log for "${entry.jobId}"`, {
50867
+ error: err
50868
+ });
48940
50869
  }
48941
50870
  },
48942
50871
  async fetchLogs(jobId, limit = 50) {
@@ -48950,7 +50879,9 @@ export default ${safeId}Collection;
48950
50879
  `);
48951
50880
  return rows.map(rowToLogEntry);
48952
50881
  } catch (err) {
48953
- console.error(`[cron-store] Failed to fetch logs for "${jobId}":`, err);
50882
+ logger.error(`[cron-store] Failed to fetch logs for "${jobId}"`, {
50883
+ error: err
50884
+ });
48954
50885
  return [];
48955
50886
  }
48956
50887
  },
@@ -48974,7 +50905,9 @@ export default ${safeId}Collection;
48974
50905
  });
48975
50906
  }
48976
50907
  } catch (err) {
48977
- console.error("[cron-store] Failed to fetch job stats:", err);
50908
+ logger.error("[cron-store] Failed to fetch job stats", {
50909
+ error: err
50910
+ });
48978
50911
  }
48979
50912
  return stats;
48980
50913
  }
@@ -49005,8 +50938,8 @@ export default ${safeId}Collection;
49005
50938
  indexFile = "index.html"
49006
50939
  } = config;
49007
50940
  if (!fs__namespace.existsSync(frontendPath)) {
49008
- console.warn(`⚠️ Frontend build path does not exist: ${frontendPath}`);
49009
- console.warn(" SPA serving is disabled. Build your frontend first.");
50941
+ logger.warn(`⚠️ Frontend build path does not exist: ${frontendPath}`);
50942
+ logger.warn(" SPA serving is disabled. Build your frontend first.");
49010
50943
  return;
49011
50944
  }
49012
50945
  app.use("/*", serveStatic.serveStatic({
@@ -49019,13 +50952,13 @@ export default ${safeId}Collection;
49019
50952
  }
49020
50953
  const indexPath = path__namespace.join(frontendPath, indexFile);
49021
50954
  if (!fs__namespace.existsSync(indexPath)) {
49022
- console.warn(`⚠️ Index file not found: ${indexPath}`);
50955
+ logger.warn(`⚠️ Index file not found: ${indexPath}`);
49023
50956
  return next();
49024
50957
  }
49025
50958
  const html = fs__namespace.readFileSync(indexPath, "utf-8");
49026
50959
  return c.html(html);
49027
50960
  });
49028
- console.log(`✅ SPA serving enabled from: ${frontendPath}`);
50961
+ logger.info(`✅ SPA serving enabled from: ${frontendPath}`);
49029
50962
  }
49030
50963
  const MAX_PORT_ATTEMPTS = 20;
49031
50964
  const DEV_PORT_FILENAME = ".rebase-dev-port";
@@ -49033,6 +50966,19 @@ export default ${safeId}Collection;
49033
50966
  const host = options2?.host ?? "0.0.0.0";
49034
50967
  const maxAttempts = options2?.maxAttempts ?? MAX_PORT_ATTEMPTS;
49035
50968
  const portFileDir = options2?.portFileDir;
50969
+ const isProd = process.env.NODE_ENV === "production";
50970
+ if (isProd) {
50971
+ return new Promise((resolve, reject) => {
50972
+ const onError = (err) => {
50973
+ reject(err);
50974
+ };
50975
+ server.once("error", onError);
50976
+ server.listen(startPort, host, () => {
50977
+ server.removeListener("error", onError);
50978
+ resolve(startPort);
50979
+ });
50980
+ });
50981
+ }
49036
50982
  let affinityPort = null;
49037
50983
  if (portFileDir) {
49038
50984
  try {
@@ -49175,6 +51121,8 @@ export default ${safeId}Collection;
49175
51121
  DB_POOL_MAX: stringType().default("20").transform(Number),
49176
51122
  DB_POOL_IDLE_TIMEOUT: stringType().default("30000").transform(Number),
49177
51123
  DB_POOL_CONNECT_TIMEOUT: stringType().default("10000").transform(Number),
51124
+ DATABASE_DIRECT_URL: stringType().url().optional(),
51125
+ DATABASE_READ_URL: stringType().url().optional(),
49178
51126
  FORCE_LOCAL_STORAGE: optionalBoolString,
49179
51127
  STORAGE_TYPE: enumType(["local", "s3"]).default("local"),
49180
51128
  STORAGE_PATH: stringType().optional(),
@@ -49250,8 +51198,11 @@ export default ${safeId}Collection;
49250
51198
  exports2.RestApiGenerator = RestApiGenerator;
49251
51199
  exports2.S3StorageController = S3StorageController;
49252
51200
  exports2.SMTPEmailService = SMTPEmailService;
51201
+ exports2.TransformCache = TransformCache;
51202
+ exports2.TusHandler = TusHandler;
49253
51203
  exports2._resetRebaseMock = _resetRebaseMock;
49254
51204
  exports2._setRebaseMock = _setRebaseMock;
51205
+ exports2.apiKeyKeyGenerator = apiKeyKeyGenerator;
49255
51206
  exports2.authJwt = authJwt;
49256
51207
  exports2.authRoles = authRoles;
49257
51208
  exports2.authUid = authUid;
@@ -49260,6 +51211,9 @@ export default ${safeId}Collection;
49260
51211
  exports2.configureLogLevel = configureLogLevel;
49261
51212
  exports2.createAdapterAuthMiddleware = createAdapterAuthMiddleware;
49262
51213
  exports2.createAdminRoutes = createAdminRoutes;
51214
+ exports2.createApiKeyRateLimiter = createApiKeyRateLimiter;
51215
+ exports2.createApiKeyRoutes = createApiKeyRoutes;
51216
+ exports2.createApiKeyStore = createApiKeyStore;
49263
51217
  exports2.createAppleProvider = createAppleProvider;
49264
51218
  exports2.createAuthMiddleware = createAuthMiddleware;
49265
51219
  exports2.createAuthRoutes = createAuthRoutes;
@@ -49297,27 +51251,35 @@ export default ${safeId}Collection;
49297
51251
  exports2.getWelcomeEmailTemplate = getWelcomeEmailTemplate;
49298
51252
  exports2.hashPassword = hashPassword;
49299
51253
  exports2.hashRefreshToken = hashRefreshToken;
51254
+ exports2.httpMethodToOperation = httpMethodToOperation;
49300
51255
  exports2.initializeRebaseBackend = initializeRebaseBackend;
51256
+ exports2.isApiKeyToken = isApiKeyToken;
49301
51257
  exports2.isAuthAdapter = isAuthAdapter;
49302
51258
  exports2.isDatabaseAdapter = isDatabaseAdapter;
51259
+ exports2.isOperationAllowed = isOperationAllowed;
51260
+ exports2.isTransformableImage = isTransformableImage;
49303
51261
  exports2.listenWithPortRetry = listenWithPortRetry;
49304
51262
  exports2.loadCronJobsFromDirectory = loadCronJobsFromDirectory;
49305
51263
  exports2.loadEnv = loadEnv;
49306
51264
  exports2.loadFunctionsFromDirectory = loadFunctionsFromDirectory;
49307
51265
  exports2.logger = logger;
49308
51266
  exports2.optionalAuth = optionalAuth;
51267
+ exports2.parseTransformOptions = parseTransformOptions;
49309
51268
  exports2.rebase = rebase;
49310
51269
  exports2.requestLogger = requestLogger;
49311
51270
  exports2.requireAdmin = requireAdmin;
49312
51271
  exports2.requireAuth = requireAuth;
49313
51272
  exports2.resetConsole = resetConsole;
49314
- exports2.resolveAuthOverrides = resolveAuthOverrides;
51273
+ exports2.resolveAuthHooks = resolveAuthHooks;
49315
51274
  exports2.serveSPA = serveSPA;
49316
51275
  exports2.strictAuthLimiter = strictAuthLimiter;
51276
+ exports2.transformImage = transformImage;
51277
+ exports2.validateApiKey = validateApiKey;
49317
51278
  exports2.validateCronExpression = validateCronExpression;
49318
51279
  exports2.validatePasswordStrength = validatePasswordStrength;
49319
51280
  exports2.verifyAccessToken = verifyAccessToken;
49320
51281
  exports2.verifyPassword = verifyPassword;
51282
+ exports2.z = z;
49321
51283
  Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
49322
51284
  });
49323
51285
  //# sourceMappingURL=index.umd.js.map