@rebasepro/server-core 0.2.3 → 0.2.5

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 (116) hide show
  1. package/dist/common/src/collections/default-collections.d.ts +9 -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 +2309 -370
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/index.umd.js +2298 -355
  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 +4 -26
  38. package/dist/types/src/controllers/client.d.ts +25 -43
  39. package/dist/types/src/controllers/collection_registry.d.ts +1 -1
  40. package/dist/types/src/controllers/data.d.ts +4 -0
  41. package/dist/types/src/controllers/data_driver.d.ts +23 -0
  42. package/dist/types/src/controllers/registry.d.ts +5 -4
  43. package/dist/types/src/rebase_context.d.ts +1 -1
  44. package/dist/types/src/types/auth_adapter.d.ts +5 -60
  45. package/dist/types/src/types/backend.d.ts +2 -2
  46. package/dist/types/src/types/backend_hooks.d.ts +2 -17
  47. package/dist/types/src/types/collections.d.ts +0 -4
  48. package/dist/types/src/types/component_ref.d.ts +1 -1
  49. package/dist/types/src/types/cron.d.ts +1 -1
  50. package/dist/types/src/types/entity_views.d.ts +1 -0
  51. package/dist/types/src/types/export_import.d.ts +1 -1
  52. package/dist/types/src/types/formex.d.ts +2 -2
  53. package/dist/types/src/types/properties.d.ts +9 -7
  54. package/dist/types/src/types/translations.d.ts +28 -12
  55. package/dist/types/src/types/user_management_delegate.d.ts +22 -57
  56. package/dist/types/src/users/index.d.ts +0 -1
  57. package/dist/types/src/users/user.d.ts +0 -1
  58. package/package.json +8 -6
  59. package/src/api/ast-schema-editor.ts +4 -4
  60. package/src/api/errors.ts +16 -7
  61. package/src/api/logs-routes.ts +129 -0
  62. package/src/api/rest/api-generator.ts +42 -2
  63. package/src/api/rest/query-parser.ts +37 -1
  64. package/src/api/types.ts +6 -1
  65. package/src/auth/adapter-middleware.ts +20 -4
  66. package/src/auth/admin-routes.ts +36 -100
  67. package/src/auth/api-keys/api-key-middleware.ts +126 -0
  68. package/src/auth/api-keys/api-key-permission-guard.ts +64 -0
  69. package/src/auth/api-keys/api-key-routes.ts +183 -0
  70. package/src/auth/api-keys/api-key-store.ts +317 -0
  71. package/src/auth/api-keys/api-key-types.ts +94 -0
  72. package/src/auth/api-keys/index.ts +37 -0
  73. package/src/auth/{auth-overrides.ts → auth-hooks.ts} +81 -14
  74. package/src/auth/builtin-auth-adapter.ts +35 -73
  75. package/src/auth/custom-auth-adapter.ts +1 -1
  76. package/src/auth/index.ts +7 -3
  77. package/src/auth/interfaces.ts +111 -3
  78. package/src/auth/jwt.ts +19 -5
  79. package/src/auth/mfa.ts +160 -0
  80. package/src/auth/middleware.ts +20 -1
  81. package/src/auth/rate-limiter.ts +92 -0
  82. package/src/auth/routes.ts +455 -24
  83. package/src/cron/cron-loader.ts +5 -10
  84. package/src/cron/cron-scheduler.ts +11 -12
  85. package/src/cron/cron-store.ts +8 -7
  86. package/src/env.ts +2 -0
  87. package/src/functions/function-loader.ts +6 -9
  88. package/src/index.ts +1 -2
  89. package/src/init.ts +56 -8
  90. package/src/serve-spa.ts +5 -4
  91. package/src/services/webhook-service.ts +155 -0
  92. package/src/storage/image-transform.ts +202 -0
  93. package/src/storage/index.ts +3 -0
  94. package/src/storage/routes.ts +56 -3
  95. package/src/storage/tus-handler.ts +315 -0
  96. package/src/utils/dev-port.ts +14 -0
  97. package/src/utils/logging.ts +9 -7
  98. package/test/admin-routes.test.ts +62 -164
  99. package/test/api-generator.test.ts +0 -1
  100. package/test/api-key-permission-guard.test.ts +132 -0
  101. package/test/ast-schema-editor.test.ts +26 -0
  102. package/test/auth-routes.test.ts +1 -2
  103. package/test/backend-hooks-admin.test.ts +3 -29
  104. package/test/custom-auth-adapter.test.ts +2 -10
  105. package/test/email-templates.test.ts +169 -0
  106. package/test/function-loader.test.ts +124 -0
  107. package/test/jwt.test.ts +4 -2
  108. package/test/mfa.test.ts +197 -0
  109. package/test/middleware.test.ts +10 -5
  110. package/test/webhook-service.test.ts +249 -0
  111. package/vite.config.ts +3 -2
  112. package/dist/index-BZoAtuqi.js.map +0 -1
  113. package/dist/server-core/src/bootstrappers/index.d.ts +0 -0
  114. package/dist/types/src/users/roles.d.ts +0 -22
  115. package/src/bootstrappers/index.ts +0 -1
  116. 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") {
@@ -2555,6 +2534,142 @@
2555
2534
  };
2556
2535
  }
2557
2536
  }
2537
+ const defaultUsersCollection = {
2538
+ name: "Users",
2539
+ singularName: "User",
2540
+ slug: "users",
2541
+ table: "users",
2542
+ schema: "rebase",
2543
+ icon: "Users",
2544
+ group: "Settings",
2545
+ openEntityMode: "dialog",
2546
+ disableDefaultActions: ["copy"],
2547
+ sort: ["createdAt", "desc"],
2548
+ properties: {
2549
+ id: {
2550
+ name: "ID",
2551
+ type: "string",
2552
+ isId: "uuid",
2553
+ ui: {
2554
+ readOnly: true
2555
+ }
2556
+ },
2557
+ email: {
2558
+ name: "Email",
2559
+ type: "string",
2560
+ validation: {
2561
+ required: true,
2562
+ unique: true
2563
+ }
2564
+ },
2565
+ displayName: {
2566
+ name: "Name",
2567
+ type: "string",
2568
+ columnName: "display_name",
2569
+ validation: {
2570
+ required: true
2571
+ }
2572
+ },
2573
+ photoURL: {
2574
+ name: "Photo URL",
2575
+ type: "string",
2576
+ columnName: "photo_url",
2577
+ url: "image"
2578
+ },
2579
+ roles: {
2580
+ name: "Roles",
2581
+ type: "array",
2582
+ columnType: "text[]",
2583
+ of: {
2584
+ name: "Role",
2585
+ type: "string",
2586
+ enum: {
2587
+ admin: "Admin",
2588
+ editor: "Editor",
2589
+ viewer: "Viewer"
2590
+ }
2591
+ }
2592
+ },
2593
+ passwordHash: {
2594
+ name: "Password Hash",
2595
+ type: "string",
2596
+ columnName: "password_hash",
2597
+ ui: {
2598
+ hideFromCollection: true,
2599
+ disabled: {
2600
+ hidden: true
2601
+ }
2602
+ }
2603
+ },
2604
+ emailVerified: {
2605
+ name: "Email Verified",
2606
+ type: "boolean",
2607
+ columnName: "email_verified",
2608
+ defaultValue: false,
2609
+ ui: {
2610
+ hideFromCollection: true,
2611
+ disabled: {
2612
+ hidden: true
2613
+ }
2614
+ }
2615
+ },
2616
+ emailVerificationToken: {
2617
+ name: "Email Verification Token",
2618
+ type: "string",
2619
+ columnName: "email_verification_token",
2620
+ ui: {
2621
+ hideFromCollection: true,
2622
+ disabled: {
2623
+ hidden: true
2624
+ }
2625
+ }
2626
+ },
2627
+ emailVerificationSentAt: {
2628
+ name: "Email Verification Sent At",
2629
+ type: "date",
2630
+ columnName: "email_verification_sent_at",
2631
+ ui: {
2632
+ hideFromCollection: true,
2633
+ disabled: {
2634
+ hidden: true
2635
+ }
2636
+ }
2637
+ },
2638
+ metadata: {
2639
+ name: "Metadata",
2640
+ type: "map",
2641
+ defaultValue: {},
2642
+ ui: {
2643
+ hideFromCollection: true,
2644
+ disabled: {
2645
+ hidden: true
2646
+ }
2647
+ }
2648
+ },
2649
+ createdAt: {
2650
+ name: "Created At",
2651
+ type: "date",
2652
+ columnName: "created_at",
2653
+ ui: {
2654
+ readOnly: true
2655
+ }
2656
+ },
2657
+ updatedAt: {
2658
+ name: "Updated At",
2659
+ type: "date",
2660
+ columnName: "updated_at",
2661
+ autoValue: "on_update",
2662
+ ui: {
2663
+ hideFromCollection: true,
2664
+ disabled: {
2665
+ hidden: true
2666
+ }
2667
+ }
2668
+ }
2669
+ },
2670
+ listProperties: ["displayName", "email", "roles", "createdAt"],
2671
+ propertiesOrder: ["id", "email", "displayName", "roles", "createdAt"]
2672
+ };
2558
2673
  function mapOperator$1(op) {
2559
2674
  switch (op) {
2560
2675
  case "==":
@@ -2846,10 +2961,15 @@
2846
2961
  const issue = error2.code === "42703" ? "column" : "table";
2847
2962
  logMessage = `Database schema mismatch (${issue} missing): ${error2.message}. Did you forget to run migrations ('pnpm db:push' or 'pnpm db:migrate')?`;
2848
2963
  }
2849
- console.error(`❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
2850
2964
  const causePg = error2.cause && typeof error2.cause === "object" ? error2.cause : void 0;
2851
2965
  const pgErrorCode = causePg?.code || error2.code;
2852
- const suppressStack = pgErrorCode === "42703" || pgErrorCode === "42P01" || statusCode < 500 && code2 === "BAD_REQUEST";
2966
+ const isDbSchemaMismatch = pgErrorCode === "42703" || pgErrorCode === "42P01";
2967
+ if (isDbSchemaMismatch) {
2968
+ console.warn(`⚠️ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
2969
+ } else {
2970
+ console.error(`❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
2971
+ }
2972
+ const suppressStack = isDbSchemaMismatch || statusCode < 500 && code2 === "BAD_REQUEST";
2853
2973
  if (!suppressStack) {
2854
2974
  console.error(error2.stack || error2);
2855
2975
  }
@@ -2930,7 +3050,7 @@
2930
3050
  options2.offset = (page - 1) * limit;
2931
3051
  }
2932
3052
  options2.where = {};
2933
- const reservedQueryKeys = ["limit", "offset", "page", "orderBy", "include", "fields", "searchString"];
3053
+ const reservedQueryKeys = ["limit", "offset", "page", "orderBy", "include", "fields", "searchString", "vector_search", "vector", "vector_distance", "vector_threshold"];
2934
3054
  for (const [key, rawValue] of Object.entries(query)) {
2935
3055
  if (reservedQueryKeys.includes(key)) continue;
2936
3056
  const value = Array.isArray(rawValue) ? rawValue[rawValue.length - 1] : rawValue;
@@ -3002,8 +3122,63 @@
3002
3122
  const fieldsStr = String(query.fields).trim();
3003
3123
  options2.fields = fieldsStr.split(",").map((s2) => s2.trim()).filter(Boolean);
3004
3124
  }
3125
+ if (query.vector_search && query.vector) {
3126
+ const vectorStr = String(query.vector);
3127
+ let queryVector;
3128
+ try {
3129
+ queryVector = JSON.parse(vectorStr);
3130
+ if (!Array.isArray(queryVector) || !queryVector.every((v) => typeof v === "number")) {
3131
+ throw new Error("Expected array of numbers");
3132
+ }
3133
+ } catch {
3134
+ throw new Error("Invalid vector format. Expected JSON array of numbers, e.g. [0.1,0.2,0.3]");
3135
+ }
3136
+ const distanceParam = query.vector_distance ? String(query.vector_distance) : "cosine";
3137
+ if (distanceParam !== "cosine" && distanceParam !== "l2" && distanceParam !== "inner_product") {
3138
+ throw new Error(`Invalid vector_distance: ${distanceParam}. Expected: cosine, l2, or inner_product`);
3139
+ }
3140
+ const vectorSearch = {
3141
+ property: String(query.vector_search),
3142
+ vector: queryVector,
3143
+ distance: distanceParam
3144
+ };
3145
+ if (query.vector_threshold) {
3146
+ const threshold = parseFloat(String(query.vector_threshold));
3147
+ if (isNaN(threshold)) {
3148
+ throw new Error("Invalid vector_threshold. Expected a number.");
3149
+ }
3150
+ vectorSearch.threshold = threshold;
3151
+ }
3152
+ options2.vectorSearch = vectorSearch;
3153
+ }
3005
3154
  return options2;
3006
3155
  }
3156
+ function httpMethodToOperation(method) {
3157
+ const upper = method.toUpperCase();
3158
+ switch (upper) {
3159
+ case "GET":
3160
+ case "HEAD":
3161
+ case "OPTIONS":
3162
+ return "read";
3163
+ case "POST":
3164
+ case "PUT":
3165
+ case "PATCH":
3166
+ return "write";
3167
+ case "DELETE":
3168
+ return "delete";
3169
+ default:
3170
+ return "read";
3171
+ }
3172
+ }
3173
+ function isOperationAllowed(permissions, collection, operation) {
3174
+ for (const perm of permissions) {
3175
+ const collectionMatch = perm.collection === "*" || perm.collection === collection;
3176
+ if (collectionMatch && perm.operations.includes(operation)) {
3177
+ return true;
3178
+ }
3179
+ }
3180
+ return false;
3181
+ }
3007
3182
  class RestApiGenerator {
3008
3183
  collections;
3009
3184
  router;
@@ -3036,6 +3211,19 @@
3036
3211
  this.createSubcollectionRoutes();
3037
3212
  return this.router;
3038
3213
  }
3214
+ /**
3215
+ * Check API key permissions for a collection operation.
3216
+ * Throws 403 if the key doesn't have the required permission.
3217
+ * No-ops if the request is not authenticated via an API key.
3218
+ */
3219
+ enforceApiKeyPermission(c, collectionSlug) {
3220
+ const apiKey = c.get("apiKey");
3221
+ if (!apiKey) return;
3222
+ const operation = httpMethodToOperation(c.req.method);
3223
+ if (!isOperationAllowed(apiKey.permissions, collectionSlug, operation)) {
3224
+ throw ApiError.forbidden(`API key does not have "${operation}" permission for collection "${collectionSlug}"`, "API_KEY_FORBIDDEN");
3225
+ }
3226
+ }
3039
3227
  /**
3040
3228
  * Get the typed RestFetchService from a driver if it exposes one (for include support).
3041
3229
  */
@@ -3049,6 +3237,7 @@
3049
3237
  const basePath = `/${collection.slug}`;
3050
3238
  const resolvedCollection = collection;
3051
3239
  this.router.get(`${basePath}/count`, async (c) => {
3240
+ this.enforceApiKeyPermission(c, collection.slug);
3052
3241
  const queryDict = c.req.query();
3053
3242
  const queryOptions = parseQueryOptions(queryDict);
3054
3243
  const searchString = queryDict.searchString;
@@ -3059,6 +3248,7 @@
3059
3248
  });
3060
3249
  });
3061
3250
  this.router.get(basePath, async (c) => {
3251
+ this.enforceApiKeyPermission(c, collection.slug);
3062
3252
  const queryDict = c.req.query();
3063
3253
  const queryOptions = parseQueryOptions(queryDict);
3064
3254
  const searchString = queryDict.searchString;
@@ -3073,7 +3263,8 @@
3073
3263
  offset: queryOptions.offset,
3074
3264
  orderBy: queryOptions.orderBy?.[0]?.field,
3075
3265
  order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
3076
- searchString
3266
+ searchString,
3267
+ vectorSearch: queryOptions.vectorSearch
3077
3268
  }, queryOptions.include);
3078
3269
  entities2 = await this.applyAfterReadBatch(collection.slug, entities2, hookCtx);
3079
3270
  const total2 = await this.countRawEntities(driver, resolvedCollection, queryOptions, searchString);
@@ -3101,6 +3292,7 @@
3101
3292
  });
3102
3293
  });
3103
3294
  this.router.get(`${basePath}/:id`, async (c) => {
3295
+ this.enforceApiKeyPermission(c, collection.slug);
3104
3296
  const id = c.req.param("id");
3105
3297
  const queryDict = c.req.query();
3106
3298
  const queryOptions = parseQueryOptions(queryDict);
@@ -3131,6 +3323,7 @@
3131
3323
  });
3132
3324
  this.router.post(basePath, async (c) => {
3133
3325
  try {
3326
+ this.enforceApiKeyPermission(c, collection.slug);
3134
3327
  const driver = c.get("driver") || this.driver;
3135
3328
  const path2 = collection.slug;
3136
3329
  const hookCtx = this.buildHookContext(c, "POST");
@@ -3159,6 +3352,7 @@
3159
3352
  });
3160
3353
  this.router.put(`${basePath}/:id`, async (c) => {
3161
3354
  try {
3355
+ this.enforceApiKeyPermission(c, collection.slug);
3162
3356
  const id = c.req.param("id");
3163
3357
  const driver = c.get("driver") || this.driver;
3164
3358
  const hookCtx = this.buildHookContext(c, "PUT");
@@ -3195,6 +3389,7 @@
3195
3389
  }
3196
3390
  });
3197
3391
  this.router.delete(`${basePath}/:id`, async (c) => {
3392
+ this.enforceApiKeyPermission(c, collection.slug);
3198
3393
  const id = c.req.param("id");
3199
3394
  const driver = c.get("driver") || this.driver;
3200
3395
  const hookCtx = this.buildHookContext(c, "DELETE");
@@ -3266,6 +3461,7 @@
3266
3461
  const parsed = parseSubPath(rawPath);
3267
3462
  if (!parsed) return next();
3268
3463
  const driver = c.get("driver") || this.driver;
3464
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3269
3465
  if (parsed.entityId === "count") {
3270
3466
  const queryDict = c.req.query();
3271
3467
  const queryOptions = parseQueryOptions(queryDict);
@@ -3313,6 +3509,7 @@
3313
3509
  const parsed = parseSubPath(rawPath);
3314
3510
  if (!parsed || parsed.entityId) return next();
3315
3511
  const driver = c.get("driver") || this.driver;
3512
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3316
3513
  const body = await c.req.json().catch(() => ({}));
3317
3514
  const entity = await driver.saveEntity({
3318
3515
  path: parsed.collectionPath,
@@ -3328,6 +3525,7 @@
3328
3525
  const parsed = parseSubPath(rawPath);
3329
3526
  if (!parsed || !parsed.entityId) return next();
3330
3527
  const driver = c.get("driver") || this.driver;
3528
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3331
3529
  const body = await c.req.json().catch(() => ({}));
3332
3530
  const entity = await driver.saveEntity({
3333
3531
  path: parsed.collectionPath,
@@ -3344,6 +3542,7 @@
3344
3542
  const parsed = parseSubPath(rawPath);
3345
3543
  if (!parsed || !parsed.entityId) return next();
3346
3544
  const driver = c.get("driver") || this.driver;
3545
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3347
3546
  const existingEntity = await driver.fetchEntity({
3348
3547
  path: parsed.collectionPath,
3349
3548
  entityId: parsed.entityId
@@ -3409,7 +3608,8 @@
3409
3608
  orderBy: queryOptions.orderBy?.[0]?.field,
3410
3609
  order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
3411
3610
  startAfter: queryOptions.offset ? String(queryOptions.offset) : void 0,
3412
- searchString
3611
+ searchString,
3612
+ vectorSearch: queryOptions.vectorSearch
3413
3613
  });
3414
3614
  return entities.map((entity) => this.flattenEntity(entity));
3415
3615
  }
@@ -4935,7 +5135,7 @@
4935
5135
  const SemVer$6 = semver$4;
4936
5136
  const parse$6 = parse_1;
4937
5137
  const { safeRe: re, t } = reExports;
4938
- const coerce$1 = (version2, options2) => {
5138
+ const coerce$2 = (version2, options2) => {
4939
5139
  if (version2 instanceof SemVer$6) {
4940
5140
  return version2;
4941
5141
  }
@@ -4970,7 +5170,7 @@
4970
5170
  const build = options2.includePrerelease && match[6] ? `+${match[6]}` : "";
4971
5171
  return parse$6(`${major2}.${minor2}.${patch2}${prerelease2}${build}`, options2);
4972
5172
  };
4973
- var coerce_1 = coerce$1;
5173
+ var coerce_1 = coerce$2;
4974
5174
  const parse$5 = parse_1;
4975
5175
  const constants$1 = constants$2;
4976
5176
  const SemVer$5 = semver$4;
@@ -5256,19 +5456,19 @@
5256
5456
  const replaceCaret = (comp, options2) => {
5257
5457
  debug2("caret", comp, options2);
5258
5458
  const r = options2.loose ? re2[t2.CARETLOOSE] : re2[t2.CARET];
5259
- const z = options2.includePrerelease ? "-0" : "";
5459
+ const z2 = options2.includePrerelease ? "-0" : "";
5260
5460
  return comp.replace(r, (_, M, m2, p, pr) => {
5261
5461
  debug2("caret", comp, _, M, m2, p, pr);
5262
5462
  let ret;
5263
5463
  if (isX(M)) {
5264
5464
  ret = "";
5265
5465
  } else if (isX(m2)) {
5266
- ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`;
5466
+ ret = `>=${M}.0.0${z2} <${+M + 1}.0.0-0`;
5267
5467
  } else if (isX(p)) {
5268
5468
  if (M === "0") {
5269
- ret = `>=${M}.${m2}.0${z} <${M}.${+m2 + 1}.0-0`;
5469
+ ret = `>=${M}.${m2}.0${z2} <${M}.${+m2 + 1}.0-0`;
5270
5470
  } else {
5271
- ret = `>=${M}.${m2}.0${z} <${+M + 1}.0.0-0`;
5471
+ ret = `>=${M}.${m2}.0${z2} <${+M + 1}.0.0-0`;
5272
5472
  }
5273
5473
  } else if (pr) {
5274
5474
  debug2("replaceCaret pr", pr);
@@ -5285,9 +5485,9 @@
5285
5485
  debug2("no pr");
5286
5486
  if (M === "0") {
5287
5487
  if (m2 === "0") {
5288
- ret = `>=${M}.${m2}.${p}${z} <${M}.${m2}.${+p + 1}-0`;
5488
+ ret = `>=${M}.${m2}.${p}${z2} <${M}.${m2}.${+p + 1}-0`;
5289
5489
  } else {
5290
- ret = `>=${M}.${m2}.${p}${z} <${M}.${+m2 + 1}.0-0`;
5490
+ ret = `>=${M}.${m2}.${p}${z2} <${M}.${+m2 + 1}.0-0`;
5291
5491
  }
5292
5492
  } else {
5293
5493
  ret = `>=${M}.${m2}.${p} <${+M + 1}.0.0-0`;
@@ -5944,7 +6144,7 @@
5944
6144
  const gte = gte_1;
5945
6145
  const lte = lte_1;
5946
6146
  const cmp = cmp_1;
5947
- const coerce = coerce_1;
6147
+ const coerce$1 = coerce_1;
5948
6148
  const truncate = truncate_1;
5949
6149
  const Comparator = requireComparator();
5950
6150
  const Range = requireRange();
@@ -5983,7 +6183,7 @@
5983
6183
  gte,
5984
6184
  lte,
5985
6185
  cmp,
5986
- coerce,
6186
+ coerce: coerce$1,
5987
6187
  truncate,
5988
6188
  Comparator,
5989
6189
  Range,
@@ -6864,7 +7064,7 @@
6864
7064
  NotBeforeError: NotBeforeError_1,
6865
7065
  TokenExpiredError: TokenExpiredError_1
6866
7066
  };
6867
- const jwt = /* @__PURE__ */ getDefaultExportFromCjs(jsonwebtoken);
7067
+ const jwt$1 = /* @__PURE__ */ getDefaultExportFromCjs(jsonwebtoken);
6868
7068
  let jwtConfig = {
6869
7069
  secret: "",
6870
7070
  accessExpiresIn: "1h",
@@ -6883,15 +7083,17 @@
6883
7083
  ...config
6884
7084
  };
6885
7085
  }
6886
- function generateAccessToken(userId, roles) {
7086
+ function generateAccessToken(userId, roles, aal = "aal1", customClaims) {
6887
7087
  if (!jwtConfig.secret) {
6888
7088
  throw new Error("JWT secret not configured. Call configureJwt() first.");
6889
7089
  }
6890
7090
  const payload = {
6891
7091
  userId,
6892
- roles
7092
+ roles,
7093
+ aal,
7094
+ ...customClaims
6893
7095
  };
6894
- return jwt.sign(payload, jwtConfig.secret, {
7096
+ return jwt$1.sign(payload, jwtConfig.secret, {
6895
7097
  expiresIn: jwtConfig.accessExpiresIn,
6896
7098
  algorithm: "HS256"
6897
7099
  });
@@ -6925,7 +7127,7 @@
6925
7127
  throw new Error("JWT secret not configured. Call configureJwt() first.");
6926
7128
  }
6927
7129
  try {
6928
- const decoded = jwt.verify(token, jwtConfig.secret, {
7130
+ const decoded = jwt$1.verify(token, jwtConfig.secret, {
6929
7131
  algorithms: ["HS256"]
6930
7132
  });
6931
7133
  const id = decoded.userId || decoded.uid || decoded.sub;
@@ -6933,9 +7135,11 @@
6933
7135
  console.error("[JWT] Verification failed: missing id in payload", decoded);
6934
7136
  return null;
6935
7137
  }
7138
+ const aal = decoded.aal === "aal1" || decoded.aal === "aal2" ? decoded.aal : "aal1";
6936
7139
  return {
6937
7140
  userId: id,
6938
- roles: decoded.roles || []
7141
+ roles: decoded.roles || [],
7142
+ aal
6939
7143
  };
6940
7144
  } catch (error2) {
6941
7145
  console.error("[JWT] Verification failed:", error2, "Token start:", token.substring(0, 15));
@@ -6975,6 +7179,17 @@
6975
7179
  }
6976
7180
  return new Date(Date.now() + ms2);
6977
7181
  }
7182
+ const jwt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
7183
+ __proto__: null,
7184
+ configureJwt,
7185
+ generateAccessToken,
7186
+ generateRefreshToken,
7187
+ getAccessTokenExpiry,
7188
+ getAccessTokenExpiryMs,
7189
+ getRefreshTokenExpiry,
7190
+ hashRefreshToken,
7191
+ verifyAccessToken
7192
+ }, Symbol.toStringTag, { value: "Module" }));
6978
7193
  function isRLSScopedDriver(driver) {
6979
7194
  return "withAuth" in driver && typeof driver.withAuth === "function";
6980
7195
  }
@@ -6997,6 +7212,81 @@
6997
7212
  return false;
6998
7213
  }
6999
7214
  }
7215
+ function isApiKeyToken(token) {
7216
+ return token.startsWith("rk_");
7217
+ }
7218
+ function hashToken$2(token) {
7219
+ return require$$0$5.createHash("sha256").update(token).digest("hex");
7220
+ }
7221
+ async function validateApiKey(c, token, options2) {
7222
+ const {
7223
+ store,
7224
+ driver
7225
+ } = options2;
7226
+ const hash = hashToken$2(token);
7227
+ const apiKey = await store.findByKeyHash(hash);
7228
+ if (!apiKey) {
7229
+ return c.json({
7230
+ error: {
7231
+ message: "Invalid API key",
7232
+ code: "UNAUTHORIZED"
7233
+ }
7234
+ }, 401);
7235
+ }
7236
+ if (apiKey.revoked_at) {
7237
+ return c.json({
7238
+ error: {
7239
+ message: "API key has been revoked",
7240
+ code: "UNAUTHORIZED"
7241
+ }
7242
+ }, 401);
7243
+ }
7244
+ if (apiKey.expires_at && new Date(apiKey.expires_at) < /* @__PURE__ */ new Date()) {
7245
+ return c.json({
7246
+ error: {
7247
+ message: "API key has expired",
7248
+ code: "UNAUTHORIZED"
7249
+ }
7250
+ }, 401);
7251
+ }
7252
+ const userId = `api-key:${apiKey.id}`;
7253
+ c.set("user", {
7254
+ userId,
7255
+ roles: []
7256
+ });
7257
+ const masked = {
7258
+ id: apiKey.id,
7259
+ name: apiKey.name,
7260
+ key_prefix: apiKey.key_prefix,
7261
+ permissions: apiKey.permissions,
7262
+ rate_limit: apiKey.rate_limit,
7263
+ created_by: apiKey.created_by,
7264
+ created_at: apiKey.created_at,
7265
+ updated_at: apiKey.updated_at,
7266
+ last_used_at: apiKey.last_used_at,
7267
+ expires_at: apiKey.expires_at,
7268
+ revoked_at: apiKey.revoked_at
7269
+ };
7270
+ c.set("apiKey", masked);
7271
+ try {
7272
+ const scopedDriver = await scopeDataDriver(driver, {
7273
+ uid: userId,
7274
+ roles: ["service"]
7275
+ });
7276
+ c.set("driver", scopedDriver);
7277
+ } catch (error2) {
7278
+ console.error("[AUTH] RLS scoping failed for API key:", error2);
7279
+ return c.json({
7280
+ error: {
7281
+ message: "Internal authentication error",
7282
+ code: "INTERNAL_ERROR"
7283
+ }
7284
+ }, 500);
7285
+ }
7286
+ store.updateLastUsed(apiKey.id).catch(() => {
7287
+ });
7288
+ return true;
7289
+ }
7000
7290
  const requireAuth = async (c, next) => {
7001
7291
  const authHeader = c.req.header("authorization");
7002
7292
  const queryToken = c.req.query("token");
@@ -7103,7 +7393,8 @@
7103
7393
  driver,
7104
7394
  requireAuth: enforceAuth = true,
7105
7395
  validator,
7106
- serviceKey
7396
+ serviceKey,
7397
+ apiKeyStore
7107
7398
  } = options2;
7108
7399
  return async (c, next) => {
7109
7400
  if (validator) {
@@ -7178,6 +7469,14 @@
7178
7469
  }
7179
7470
  }, 500);
7180
7471
  }
7472
+ } else if (apiKeyStore && isApiKeyToken(token)) {
7473
+ const result = await validateApiKey(c, token, {
7474
+ store: apiKeyStore,
7475
+ driver
7476
+ });
7477
+ if (result !== true) {
7478
+ return result;
7479
+ }
7181
7480
  } else {
7182
7481
  const payload = extractUserFromToken(token);
7183
7482
  if (payload) {
@@ -7238,9 +7537,22 @@
7238
7537
  const {
7239
7538
  adapter,
7240
7539
  driver,
7241
- requireAuth: enforceAuth = true
7540
+ requireAuth: enforceAuth = true,
7541
+ apiKeyStore
7242
7542
  } = options2;
7243
7543
  return async (c, next) => {
7544
+ if (apiKeyStore) {
7545
+ const authHeader = c.req.header("authorization") || "";
7546
+ const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
7547
+ if (token.startsWith("rk_")) {
7548
+ const result = await validateApiKey(c, token, {
7549
+ store: apiKeyStore,
7550
+ driver
7551
+ });
7552
+ if (result === true) return next();
7553
+ return result;
7554
+ }
7555
+ }
7244
7556
  let authenticatedUser = null;
7245
7557
  try {
7246
7558
  authenticatedUser = await adapter.verifyRequest(c.req.raw);
@@ -7336,11 +7648,11 @@
7336
7648
  const derivedKey = await scryptAsync(password, salt, KEY_LENGTH);
7337
7649
  return require$$0$5.timingSafeEqual(derivedKey, storedKey);
7338
7650
  }
7339
- function resolveAuthOverrides(overrides) {
7651
+ function resolveAuthHooks(hooks) {
7340
7652
  return {
7341
- hashPassword: overrides?.hashPassword ?? hashPassword,
7342
- verifyPassword: overrides?.verifyPassword ?? verifyPassword,
7343
- validatePasswordStrength: overrides?.validatePasswordStrength ?? validatePasswordStrength
7653
+ hashPassword: hooks?.hashPassword ?? hashPassword,
7654
+ verifyPassword: hooks?.verifyPassword ?? verifyPassword,
7655
+ validatePasswordStrength: hooks?.validatePasswordStrength ?? validatePasswordStrength
7344
7656
  };
7345
7657
  }
7346
7658
  function getGreeting(user) {
@@ -7742,6 +8054,63 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
7742
8054
  limit: 50,
7743
8055
  message: "Too many requests to this sensitive endpoint, please try again later."
7744
8056
  });
8057
+ function apiKeyKeyGenerator(c) {
8058
+ const apiKey = c.get("apiKey");
8059
+ if (apiKey) {
8060
+ return `api-key:${apiKey.id}`;
8061
+ }
8062
+ return defaultKeyGenerator(c);
8063
+ }
8064
+ function createApiKeyRateLimiter(defaultLimit = 1e3, windowMs = 15 * 60 * 1e3) {
8065
+ const store = /* @__PURE__ */ new Map();
8066
+ const cleanupInterval = setInterval(() => {
8067
+ const now = Date.now();
8068
+ for (const [key, entry] of store.entries()) {
8069
+ entry.timestamps = entry.timestamps.filter((t2) => now - t2 < windowMs);
8070
+ if (entry.timestamps.length === 0) {
8071
+ store.delete(key);
8072
+ }
8073
+ }
8074
+ }, windowMs);
8075
+ if (cleanupInterval.unref) {
8076
+ cleanupInterval.unref();
8077
+ }
8078
+ return async (c, next) => {
8079
+ const apiKey = c.get("apiKey");
8080
+ if (!apiKey) {
8081
+ return next();
8082
+ }
8083
+ const limit = apiKey.rate_limit ?? defaultLimit;
8084
+ const key = `api-key:${apiKey.id}`;
8085
+ const now = Date.now();
8086
+ let entry = store.get(key);
8087
+ if (!entry) {
8088
+ entry = {
8089
+ timestamps: []
8090
+ };
8091
+ store.set(key, entry);
8092
+ }
8093
+ entry.timestamps = entry.timestamps.filter((t2) => now - t2 < windowMs);
8094
+ if (entry.timestamps.length >= limit) {
8095
+ const retryAfterMs = entry.timestamps[0] + windowMs - now;
8096
+ const retryAfterSec = Math.ceil(retryAfterMs / 1e3);
8097
+ c.header("Retry-After", String(retryAfterSec));
8098
+ c.header("X-RateLimit-Limit", String(limit));
8099
+ c.header("X-RateLimit-Remaining", "0");
8100
+ c.header("X-RateLimit-Reset", String(Math.ceil((now + retryAfterMs) / 1e3)));
8101
+ return c.json({
8102
+ error: {
8103
+ message: "API key rate limit exceeded, please try again later.",
8104
+ code: "RATE_LIMITED"
8105
+ }
8106
+ }, 429);
8107
+ }
8108
+ entry.timestamps.push(now);
8109
+ c.header("X-RateLimit-Limit", String(limit));
8110
+ c.header("X-RateLimit-Remaining", String(limit - entry.timestamps.length));
8111
+ return next();
8112
+ };
8113
+ }
7745
8114
  var util$3;
7746
8115
  (function(util2) {
7747
8116
  util2.assertEqual = (_) => {
@@ -7892,6 +8261,10 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
7892
8261
  "not_multiple_of",
7893
8262
  "not_finite"
7894
8263
  ]);
8264
+ const quotelessJson = (obj) => {
8265
+ const json = JSON.stringify(obj, null, 2);
8266
+ return json.replace(/"([^"]+)":/g, "$1:");
8267
+ };
7895
8268
  class ZodError extends Error {
7896
8269
  get errors() {
7897
8270
  return this.issues;
@@ -8087,6 +8460,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
8087
8460
  return { message };
8088
8461
  };
8089
8462
  let overrideErrorMap = errorMap;
8463
+ function setErrorMap(map2) {
8464
+ overrideErrorMap = map2;
8465
+ }
8090
8466
  function getErrorMap() {
8091
8467
  return overrideErrorMap;
8092
8468
  }
@@ -8115,6 +8491,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
8115
8491
  message: errorMessage
8116
8492
  };
8117
8493
  };
8494
+ const EMPTY_PATH = [];
8118
8495
  function addIssueToContext(ctx, issueData) {
8119
8496
  const overrideMap = getErrorMap();
8120
8497
  const issue = makeIssue({
@@ -10405,6 +10782,113 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
10405
10782
  ...processCreateParams(params)
10406
10783
  });
10407
10784
  };
10785
+ const getDiscriminator = (type) => {
10786
+ if (type instanceof ZodLazy) {
10787
+ return getDiscriminator(type.schema);
10788
+ } else if (type instanceof ZodEffects) {
10789
+ return getDiscriminator(type.innerType());
10790
+ } else if (type instanceof ZodLiteral) {
10791
+ return [type.value];
10792
+ } else if (type instanceof ZodEnum) {
10793
+ return type.options;
10794
+ } else if (type instanceof ZodNativeEnum) {
10795
+ return util$3.objectValues(type.enum);
10796
+ } else if (type instanceof ZodDefault) {
10797
+ return getDiscriminator(type._def.innerType);
10798
+ } else if (type instanceof ZodUndefined) {
10799
+ return [void 0];
10800
+ } else if (type instanceof ZodNull) {
10801
+ return [null];
10802
+ } else if (type instanceof ZodOptional) {
10803
+ return [void 0, ...getDiscriminator(type.unwrap())];
10804
+ } else if (type instanceof ZodNullable) {
10805
+ return [null, ...getDiscriminator(type.unwrap())];
10806
+ } else if (type instanceof ZodBranded) {
10807
+ return getDiscriminator(type.unwrap());
10808
+ } else if (type instanceof ZodReadonly) {
10809
+ return getDiscriminator(type.unwrap());
10810
+ } else if (type instanceof ZodCatch) {
10811
+ return getDiscriminator(type._def.innerType);
10812
+ } else {
10813
+ return [];
10814
+ }
10815
+ };
10816
+ class ZodDiscriminatedUnion extends ZodType {
10817
+ _parse(input) {
10818
+ const { ctx } = this._processInputParams(input);
10819
+ if (ctx.parsedType !== ZodParsedType.object) {
10820
+ addIssueToContext(ctx, {
10821
+ code: ZodIssueCode.invalid_type,
10822
+ expected: ZodParsedType.object,
10823
+ received: ctx.parsedType
10824
+ });
10825
+ return INVALID;
10826
+ }
10827
+ const discriminator = this.discriminator;
10828
+ const discriminatorValue = ctx.data[discriminator];
10829
+ const option = this.optionsMap.get(discriminatorValue);
10830
+ if (!option) {
10831
+ addIssueToContext(ctx, {
10832
+ code: ZodIssueCode.invalid_union_discriminator,
10833
+ options: Array.from(this.optionsMap.keys()),
10834
+ path: [discriminator]
10835
+ });
10836
+ return INVALID;
10837
+ }
10838
+ if (ctx.common.async) {
10839
+ return option._parseAsync({
10840
+ data: ctx.data,
10841
+ path: ctx.path,
10842
+ parent: ctx
10843
+ });
10844
+ } else {
10845
+ return option._parseSync({
10846
+ data: ctx.data,
10847
+ path: ctx.path,
10848
+ parent: ctx
10849
+ });
10850
+ }
10851
+ }
10852
+ get discriminator() {
10853
+ return this._def.discriminator;
10854
+ }
10855
+ get options() {
10856
+ return this._def.options;
10857
+ }
10858
+ get optionsMap() {
10859
+ return this._def.optionsMap;
10860
+ }
10861
+ /**
10862
+ * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor.
10863
+ * However, it only allows a union of objects, all of which need to share a discriminator property. This property must
10864
+ * have a different value for each object in the union.
10865
+ * @param discriminator the name of the discriminator property
10866
+ * @param types an array of object schemas
10867
+ * @param params
10868
+ */
10869
+ static create(discriminator, options2, params) {
10870
+ const optionsMap = /* @__PURE__ */ new Map();
10871
+ for (const type of options2) {
10872
+ const discriminatorValues = getDiscriminator(type.shape[discriminator]);
10873
+ if (!discriminatorValues.length) {
10874
+ throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`);
10875
+ }
10876
+ for (const value of discriminatorValues) {
10877
+ if (optionsMap.has(value)) {
10878
+ throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`);
10879
+ }
10880
+ optionsMap.set(value, type);
10881
+ }
10882
+ }
10883
+ return new ZodDiscriminatedUnion({
10884
+ typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion,
10885
+ discriminator,
10886
+ options: options2,
10887
+ optionsMap,
10888
+ ...processCreateParams(params)
10889
+ });
10890
+ }
10891
+ }
10408
10892
  function mergeValues(a2, b) {
10409
10893
  const aType = getParsedType(a2);
10410
10894
  const bType = getParsedType(b);
@@ -10563,6 +11047,59 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
10563
11047
  ...processCreateParams(params)
10564
11048
  });
10565
11049
  };
11050
+ class ZodRecord extends ZodType {
11051
+ get keySchema() {
11052
+ return this._def.keyType;
11053
+ }
11054
+ get valueSchema() {
11055
+ return this._def.valueType;
11056
+ }
11057
+ _parse(input) {
11058
+ const { status, ctx } = this._processInputParams(input);
11059
+ if (ctx.parsedType !== ZodParsedType.object) {
11060
+ addIssueToContext(ctx, {
11061
+ code: ZodIssueCode.invalid_type,
11062
+ expected: ZodParsedType.object,
11063
+ received: ctx.parsedType
11064
+ });
11065
+ return INVALID;
11066
+ }
11067
+ const pairs = [];
11068
+ const keyType = this._def.keyType;
11069
+ const valueType = this._def.valueType;
11070
+ for (const key in ctx.data) {
11071
+ pairs.push({
11072
+ key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)),
11073
+ value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)),
11074
+ alwaysSet: key in ctx.data
11075
+ });
11076
+ }
11077
+ if (ctx.common.async) {
11078
+ return ParseStatus.mergeObjectAsync(status, pairs);
11079
+ } else {
11080
+ return ParseStatus.mergeObjectSync(status, pairs);
11081
+ }
11082
+ }
11083
+ get element() {
11084
+ return this._def.valueType;
11085
+ }
11086
+ static create(first, second, third) {
11087
+ if (second instanceof ZodType) {
11088
+ return new ZodRecord({
11089
+ keyType: first,
11090
+ valueType: second,
11091
+ typeName: ZodFirstPartyTypeKind.ZodRecord,
11092
+ ...processCreateParams(third)
11093
+ });
11094
+ }
11095
+ return new ZodRecord({
11096
+ keyType: ZodString.create(),
11097
+ valueType: first,
11098
+ typeName: ZodFirstPartyTypeKind.ZodRecord,
11099
+ ...processCreateParams(second)
11100
+ });
11101
+ }
11102
+ }
10566
11103
  class ZodMap extends ZodType {
10567
11104
  get keySchema() {
10568
11105
  return this._def.keyType;
@@ -10714,6 +11251,111 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
10714
11251
  ...processCreateParams(params)
10715
11252
  });
10716
11253
  };
11254
+ class ZodFunction extends ZodType {
11255
+ constructor() {
11256
+ super(...arguments);
11257
+ this.validate = this.implement;
11258
+ }
11259
+ _parse(input) {
11260
+ const { ctx } = this._processInputParams(input);
11261
+ if (ctx.parsedType !== ZodParsedType.function) {
11262
+ addIssueToContext(ctx, {
11263
+ code: ZodIssueCode.invalid_type,
11264
+ expected: ZodParsedType.function,
11265
+ received: ctx.parsedType
11266
+ });
11267
+ return INVALID;
11268
+ }
11269
+ function makeArgsIssue(args, error2) {
11270
+ return makeIssue({
11271
+ data: args,
11272
+ path: ctx.path,
11273
+ errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), errorMap].filter((x) => !!x),
11274
+ issueData: {
11275
+ code: ZodIssueCode.invalid_arguments,
11276
+ argumentsError: error2
11277
+ }
11278
+ });
11279
+ }
11280
+ function makeReturnsIssue(returns, error2) {
11281
+ return makeIssue({
11282
+ data: returns,
11283
+ path: ctx.path,
11284
+ errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), errorMap].filter((x) => !!x),
11285
+ issueData: {
11286
+ code: ZodIssueCode.invalid_return_type,
11287
+ returnTypeError: error2
11288
+ }
11289
+ });
11290
+ }
11291
+ const params = { errorMap: ctx.common.contextualErrorMap };
11292
+ const fn = ctx.data;
11293
+ if (this._def.returns instanceof ZodPromise) {
11294
+ const me = this;
11295
+ return OK(async function(...args) {
11296
+ const error2 = new ZodError([]);
11297
+ const parsedArgs = await me._def.args.parseAsync(args, params).catch((e) => {
11298
+ error2.addIssue(makeArgsIssue(args, e));
11299
+ throw error2;
11300
+ });
11301
+ const result = await Reflect.apply(fn, this, parsedArgs);
11302
+ const parsedReturns = await me._def.returns._def.type.parseAsync(result, params).catch((e) => {
11303
+ error2.addIssue(makeReturnsIssue(result, e));
11304
+ throw error2;
11305
+ });
11306
+ return parsedReturns;
11307
+ });
11308
+ } else {
11309
+ const me = this;
11310
+ return OK(function(...args) {
11311
+ const parsedArgs = me._def.args.safeParse(args, params);
11312
+ if (!parsedArgs.success) {
11313
+ throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
11314
+ }
11315
+ const result = Reflect.apply(fn, this, parsedArgs.data);
11316
+ const parsedReturns = me._def.returns.safeParse(result, params);
11317
+ if (!parsedReturns.success) {
11318
+ throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
11319
+ }
11320
+ return parsedReturns.data;
11321
+ });
11322
+ }
11323
+ }
11324
+ parameters() {
11325
+ return this._def.args;
11326
+ }
11327
+ returnType() {
11328
+ return this._def.returns;
11329
+ }
11330
+ args(...items) {
11331
+ return new ZodFunction({
11332
+ ...this._def,
11333
+ args: ZodTuple.create(items).rest(ZodUnknown.create())
11334
+ });
11335
+ }
11336
+ returns(returnType) {
11337
+ return new ZodFunction({
11338
+ ...this._def,
11339
+ returns: returnType
11340
+ });
11341
+ }
11342
+ implement(func) {
11343
+ const validatedFunc = this.parse(func);
11344
+ return validatedFunc;
11345
+ }
11346
+ strictImplement(func) {
11347
+ const validatedFunc = this.parse(func);
11348
+ return validatedFunc;
11349
+ }
11350
+ static create(args, returns, params) {
11351
+ return new ZodFunction({
11352
+ args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()),
11353
+ returns: returns || ZodUnknown.create(),
11354
+ typeName: ZodFirstPartyTypeKind.ZodFunction,
11355
+ ...processCreateParams(params)
11356
+ });
11357
+ }
11358
+ }
10717
11359
  class ZodLazy extends ZodType {
10718
11360
  get schema() {
10719
11361
  return this._def.getter();
@@ -11171,6 +11813,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11171
11813
  ...processCreateParams(params)
11172
11814
  });
11173
11815
  };
11816
+ const BRAND = Symbol("zod_brand");
11174
11817
  class ZodBranded extends ZodType {
11175
11818
  _parse(input) {
11176
11819
  const { ctx } = this._processInputParams(input);
@@ -11262,6 +11905,36 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11262
11905
  ...processCreateParams(params)
11263
11906
  });
11264
11907
  };
11908
+ function cleanParams(params, data) {
11909
+ const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
11910
+ const p2 = typeof p === "string" ? { message: p } : p;
11911
+ return p2;
11912
+ }
11913
+ function custom(check, _params = {}, fatal) {
11914
+ if (check)
11915
+ return ZodAny.create().superRefine((data, ctx) => {
11916
+ const r = check(data);
11917
+ if (r instanceof Promise) {
11918
+ return r.then((r2) => {
11919
+ if (!r2) {
11920
+ const params = cleanParams(_params, data);
11921
+ const _fatal = params.fatal ?? fatal ?? true;
11922
+ ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
11923
+ }
11924
+ });
11925
+ }
11926
+ if (!r) {
11927
+ const params = cleanParams(_params, data);
11928
+ const _fatal = params.fatal ?? fatal ?? true;
11929
+ ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
11930
+ }
11931
+ return;
11932
+ });
11933
+ return ZodAny.create();
11934
+ }
11935
+ const late = {
11936
+ object: ZodObject.lazycreate
11937
+ };
11265
11938
  var ZodFirstPartyTypeKind;
11266
11939
  (function(ZodFirstPartyTypeKind2) {
11267
11940
  ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
@@ -11301,17 +11974,251 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11301
11974
  ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
11302
11975
  ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
11303
11976
  })(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
11977
+ const instanceOfType = (cls, params = {
11978
+ message: `Input not instance of ${cls.name}`
11979
+ }) => custom((data) => data instanceof cls, params);
11304
11980
  const stringType = ZodString.create;
11305
- ZodNever.create;
11306
- ZodArray.create;
11981
+ const numberType = ZodNumber.create;
11982
+ const nanType = ZodNaN.create;
11983
+ const bigIntType = ZodBigInt.create;
11984
+ const booleanType = ZodBoolean.create;
11985
+ const dateType = ZodDate.create;
11986
+ const symbolType = ZodSymbol.create;
11987
+ const undefinedType = ZodUndefined.create;
11988
+ const nullType = ZodNull.create;
11989
+ const anyType = ZodAny.create;
11990
+ const unknownType = ZodUnknown.create;
11991
+ const neverType = ZodNever.create;
11992
+ const voidType = ZodVoid.create;
11993
+ const arrayType = ZodArray.create;
11307
11994
  const objectType = ZodObject.create;
11308
- ZodUnion.create;
11309
- ZodIntersection.create;
11310
- ZodTuple.create;
11995
+ const strictObjectType = ZodObject.strictCreate;
11996
+ const unionType = ZodUnion.create;
11997
+ const discriminatedUnionType = ZodDiscriminatedUnion.create;
11998
+ const intersectionType = ZodIntersection.create;
11999
+ const tupleType = ZodTuple.create;
12000
+ const recordType = ZodRecord.create;
12001
+ const mapType = ZodMap.create;
12002
+ const setType = ZodSet.create;
12003
+ const functionType = ZodFunction.create;
12004
+ const lazyType = ZodLazy.create;
12005
+ const literalType = ZodLiteral.create;
11311
12006
  const enumType = ZodEnum.create;
11312
- ZodPromise.create;
11313
- ZodOptional.create;
11314
- ZodNullable.create;
12007
+ const nativeEnumType = ZodNativeEnum.create;
12008
+ const promiseType = ZodPromise.create;
12009
+ const effectsType = ZodEffects.create;
12010
+ const optionalType = ZodOptional.create;
12011
+ const nullableType = ZodNullable.create;
12012
+ const preprocessType = ZodEffects.createWithPreprocess;
12013
+ const pipelineType = ZodPipeline.create;
12014
+ const ostring = () => stringType().optional();
12015
+ const onumber = () => numberType().optional();
12016
+ const oboolean = () => booleanType().optional();
12017
+ const coerce = {
12018
+ string: (arg) => ZodString.create({ ...arg, coerce: true }),
12019
+ number: (arg) => ZodNumber.create({ ...arg, coerce: true }),
12020
+ boolean: (arg) => ZodBoolean.create({
12021
+ ...arg,
12022
+ coerce: true
12023
+ }),
12024
+ bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }),
12025
+ date: (arg) => ZodDate.create({ ...arg, coerce: true })
12026
+ };
12027
+ const NEVER = INVALID;
12028
+ const z = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12029
+ __proto__: null,
12030
+ BRAND,
12031
+ DIRTY,
12032
+ EMPTY_PATH,
12033
+ INVALID,
12034
+ NEVER,
12035
+ OK,
12036
+ ParseStatus,
12037
+ Schema: ZodType,
12038
+ ZodAny,
12039
+ ZodArray,
12040
+ ZodBigInt,
12041
+ ZodBoolean,
12042
+ ZodBranded,
12043
+ ZodCatch,
12044
+ ZodDate,
12045
+ ZodDefault,
12046
+ ZodDiscriminatedUnion,
12047
+ ZodEffects,
12048
+ ZodEnum,
12049
+ ZodError,
12050
+ get ZodFirstPartyTypeKind() {
12051
+ return ZodFirstPartyTypeKind;
12052
+ },
12053
+ ZodFunction,
12054
+ ZodIntersection,
12055
+ ZodIssueCode,
12056
+ ZodLazy,
12057
+ ZodLiteral,
12058
+ ZodMap,
12059
+ ZodNaN,
12060
+ ZodNativeEnum,
12061
+ ZodNever,
12062
+ ZodNull,
12063
+ ZodNullable,
12064
+ ZodNumber,
12065
+ ZodObject,
12066
+ ZodOptional,
12067
+ ZodParsedType,
12068
+ ZodPipeline,
12069
+ ZodPromise,
12070
+ ZodReadonly,
12071
+ ZodRecord,
12072
+ ZodSchema: ZodType,
12073
+ ZodSet,
12074
+ ZodString,
12075
+ ZodSymbol,
12076
+ ZodTransformer: ZodEffects,
12077
+ ZodTuple,
12078
+ ZodType,
12079
+ ZodUndefined,
12080
+ ZodUnion,
12081
+ ZodUnknown,
12082
+ ZodVoid,
12083
+ addIssueToContext,
12084
+ any: anyType,
12085
+ array: arrayType,
12086
+ bigint: bigIntType,
12087
+ boolean: booleanType,
12088
+ coerce,
12089
+ custom,
12090
+ date: dateType,
12091
+ datetimeRegex,
12092
+ defaultErrorMap: errorMap,
12093
+ discriminatedUnion: discriminatedUnionType,
12094
+ effect: effectsType,
12095
+ enum: enumType,
12096
+ function: functionType,
12097
+ getErrorMap,
12098
+ getParsedType,
12099
+ instanceof: instanceOfType,
12100
+ intersection: intersectionType,
12101
+ isAborted,
12102
+ isAsync,
12103
+ isDirty,
12104
+ isValid,
12105
+ late,
12106
+ lazy: lazyType,
12107
+ literal: literalType,
12108
+ makeIssue,
12109
+ map: mapType,
12110
+ nan: nanType,
12111
+ nativeEnum: nativeEnumType,
12112
+ never: neverType,
12113
+ null: nullType,
12114
+ nullable: nullableType,
12115
+ number: numberType,
12116
+ object: objectType,
12117
+ get objectUtil() {
12118
+ return objectUtil;
12119
+ },
12120
+ oboolean,
12121
+ onumber,
12122
+ optional: optionalType,
12123
+ ostring,
12124
+ pipeline: pipelineType,
12125
+ preprocess: preprocessType,
12126
+ promise: promiseType,
12127
+ quotelessJson,
12128
+ record: recordType,
12129
+ set: setType,
12130
+ setErrorMap,
12131
+ strictObject: strictObjectType,
12132
+ string: stringType,
12133
+ symbol: symbolType,
12134
+ transformer: effectsType,
12135
+ tuple: tupleType,
12136
+ undefined: undefinedType,
12137
+ union: unionType,
12138
+ unknown: unknownType,
12139
+ get util() {
12140
+ return util$3;
12141
+ },
12142
+ void: voidType
12143
+ }, Symbol.toStringTag, { value: "Module" }));
12144
+ const BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
12145
+ function base32Encode(buffer) {
12146
+ let bits = 0;
12147
+ let value = 0;
12148
+ let output = "";
12149
+ for (let i2 = 0; i2 < buffer.length; i2++) {
12150
+ value = value << 8 | buffer[i2];
12151
+ bits += 8;
12152
+ while (bits >= 5) {
12153
+ output += BASE32_ALPHABET[value >>> bits - 5 & 31];
12154
+ bits -= 5;
12155
+ }
12156
+ }
12157
+ if (bits > 0) {
12158
+ output += BASE32_ALPHABET[value << 5 - bits & 31];
12159
+ }
12160
+ return output;
12161
+ }
12162
+ function base32Decode(encoded) {
12163
+ const cleanInput = encoded.replace(/=+$/, "").toUpperCase();
12164
+ const bytes = [];
12165
+ let bits = 0;
12166
+ let value = 0;
12167
+ for (let i2 = 0; i2 < cleanInput.length; i2++) {
12168
+ const index2 = BASE32_ALPHABET.indexOf(cleanInput[i2]);
12169
+ if (index2 === -1) continue;
12170
+ value = value << 5 | index2;
12171
+ bits += 5;
12172
+ if (bits >= 8) {
12173
+ bytes.push(value >>> bits - 8 & 255);
12174
+ bits -= 8;
12175
+ }
12176
+ }
12177
+ return Buffer.from(bytes);
12178
+ }
12179
+ function generateHotp(secret, counter) {
12180
+ const hmac = require$$0$5.createHmac("sha1", secret);
12181
+ const counterBuffer = Buffer.alloc(8);
12182
+ counterBuffer.writeBigInt64BE(counter);
12183
+ hmac.update(counterBuffer);
12184
+ const hash = hmac.digest();
12185
+ const offset = hash[hash.length - 1] & 15;
12186
+ const code2 = ((hash[offset] & 127) << 24 | hash[offset + 1] << 16 | hash[offset + 2] << 8 | hash[offset + 3]) % 1e6;
12187
+ return code2.toString().padStart(6, "0");
12188
+ }
12189
+ function verifyTotp(secret, token, window2 = 1) {
12190
+ const timeStep = 30;
12191
+ const counter = BigInt(Math.floor(Date.now() / 1e3 / timeStep));
12192
+ for (let i2 = -window2; i2 <= window2; i2++) {
12193
+ if (generateHotp(secret, counter + BigInt(i2)) === token) {
12194
+ return true;
12195
+ }
12196
+ }
12197
+ return false;
12198
+ }
12199
+ function generateTotpSecret(issuer, accountName) {
12200
+ const secretBuffer = require$$0$5.randomBytes(20);
12201
+ const secret = base32Encode(secretBuffer);
12202
+ const encodedIssuer = encodeURIComponent(issuer);
12203
+ const encodedAccount = encodeURIComponent(accountName);
12204
+ const uri = `otpauth://totp/${encodedIssuer}:${encodedAccount}?secret=${secret}&issuer=${encodedIssuer}&algorithm=SHA1&digits=6&period=30`;
12205
+ return {
12206
+ secret,
12207
+ uri
12208
+ };
12209
+ }
12210
+ function generateRecoveryCodes(count = 10) {
12211
+ return Array.from({
12212
+ length: count
12213
+ }, () => {
12214
+ const raw = require$$0$5.randomBytes(5).toString("hex").toUpperCase();
12215
+ const parts = raw.match(/.{1,5}/g);
12216
+ return parts ? parts.join("-") : raw;
12217
+ });
12218
+ }
12219
+ function hashRecoveryCode(code2) {
12220
+ return require$$0$5.createHash("sha256").update(code2.replace(/-/g, "").toUpperCase()).digest("hex");
12221
+ }
11315
12222
  function buildAuthResponse(user, roleIds, accessToken, refreshToken) {
11316
12223
  return {
11317
12224
  user: {
@@ -11349,9 +12256,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11349
12256
  emailService,
11350
12257
  emailConfig,
11351
12258
  allowRegistration = false,
11352
- overrides
12259
+ authHooks
11353
12260
  } = config;
11354
- const ops = resolveAuthOverrides(overrides);
12261
+ const ops = resolveAuthHooks(authHooks);
11355
12262
  const registerSchema = objectType({
11356
12263
  email: stringType().email("Invalid email address").max(255),
11357
12264
  password: stringType().min(1, "Password is required").max(128),
@@ -11415,7 +12322,19 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11415
12322
  async function createSessionAndTokens(userId, userAgent, ipAddress) {
11416
12323
  const roles = await authRepo.getUserRoles(userId);
11417
12324
  const roleIds = roles.map((r) => r.id);
11418
- const accessToken = generateAccessToken(userId, roleIds);
12325
+ let customClaims;
12326
+ if (authHooks?.customizeAccessToken) {
12327
+ const user = await authRepo.getUserById(userId);
12328
+ if (user) {
12329
+ const defaultClaims = {
12330
+ userId,
12331
+ roles: roleIds,
12332
+ aal: "aal1"
12333
+ };
12334
+ customClaims = await authHooks.customizeAccessToken(defaultClaims, user);
12335
+ }
12336
+ }
12337
+ const accessToken = generateAccessToken(userId, roleIds, "aal1", customClaims);
11419
12338
  const refreshToken = generateRefreshToken();
11420
12339
  await authRepo.createRefreshToken(userId, hashRefreshToken(refreshToken), getRefreshTokenExpiry(), userAgent, ipAddress);
11421
12340
  return {
@@ -11450,8 +12369,8 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11450
12369
  passwordHash,
11451
12370
  displayName: displayName || void 0
11452
12371
  };
11453
- if (overrides?.beforeUserCreate) {
11454
- createData = await overrides.beforeUserCreate(createData);
12372
+ if (authHooks?.beforeUserCreate) {
12373
+ createData = await authHooks.beforeUserCreate(createData);
11455
12374
  }
11456
12375
  const user = await authRepo.createUser(createData);
11457
12376
  const existingUsers = await authRepo.listUsers();
@@ -11470,14 +12389,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11470
12389
  email: user.email,
11471
12390
  displayName: user.displayName
11472
12391
  });
11473
- if (overrides?.afterUserCreate) {
11474
- overrides.afterUserCreate(user).catch((err) => {
11475
- console.error("[AuthOverrides] afterUserCreate error:", err instanceof Error ? err.message : err);
11476
- });
12392
+ if (authHooks?.afterUserCreate) {
12393
+ try {
12394
+ await authHooks.afterUserCreate(user);
12395
+ } catch (err) {
12396
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
12397
+ }
11477
12398
  }
11478
- if (overrides?.onAuthenticated) {
11479
- overrides.onAuthenticated(user, "register").catch((err) => {
11480
- console.error("[AuthOverrides] onAuthenticated error:", err instanceof Error ? err.message : err);
12399
+ if (authHooks?.onAuthenticated) {
12400
+ authHooks.onAuthenticated(user, "register").catch((err) => {
12401
+ console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
11481
12402
  });
11482
12403
  }
11483
12404
  return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken), 201);
@@ -11487,9 +12408,12 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11487
12408
  email,
11488
12409
  password
11489
12410
  } = parseBody2(loginSchema, await c.req.json());
12411
+ if (authHooks?.beforeLogin) {
12412
+ await authHooks.beforeLogin(email, "login");
12413
+ }
11490
12414
  let user;
11491
- if (overrides?.verifyCredentials) {
11492
- user = await overrides.verifyCredentials(email, password, authRepo);
12415
+ if (authHooks?.verifyCredentials) {
12416
+ user = await authHooks.verifyCredentials(email, password, authRepo);
11493
12417
  if (!user) {
11494
12418
  throw ApiError.unauthorized("Invalid email or password", "INVALID_CREDENTIALS");
11495
12419
  }
@@ -11511,9 +12435,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11511
12435
  accessToken,
11512
12436
  refreshToken
11513
12437
  } = 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);
12438
+ if (authHooks?.onAuthenticated) {
12439
+ authHooks.onAuthenticated(user, "login").catch((err) => {
12440
+ console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
11517
12441
  });
11518
12442
  }
11519
12443
  return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken));
@@ -11552,6 +12476,13 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11552
12476
  await authRepo.linkUserIdentity(user.id, provider.id, externalUser.providerId, {
11553
12477
  email: externalUser.email
11554
12478
  });
12479
+ if (authHooks?.afterUserCreate) {
12480
+ try {
12481
+ await authHooks.afterUserCreate(user);
12482
+ } catch (err) {
12483
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
12484
+ }
12485
+ }
11555
12486
  const allUsers = await authRepo.listUsers();
11556
12487
  const isFirstUser = allUsers.length === 1 && allUsers[0].id === user.id;
11557
12488
  if (isFirstUser) {
@@ -11637,6 +12568,11 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11637
12568
  await authRepo.updatePassword(storedToken.userId, passwordHash);
11638
12569
  await authRepo.markPasswordResetTokenUsed(tokenHash);
11639
12570
  await authRepo.deleteAllRefreshTokensForUser(storedToken.userId);
12571
+ if (authHooks?.onPasswordReset) {
12572
+ authHooks.onPasswordReset(storedToken.userId).catch((err) => {
12573
+ console.error("[AuthHooks] onPasswordReset error:", err instanceof Error ? err.message : err);
12574
+ });
12575
+ }
11640
12576
  return c.json({
11641
12577
  success: true,
11642
12578
  message: "Password has been reset successfully"
@@ -11740,7 +12676,19 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11740
12676
  }
11741
12677
  const roles = await authRepo.getUserRoles(storedToken.userId);
11742
12678
  const roleIds = roles.map((r) => r.id);
11743
- const newAccessToken = generateAccessToken(storedToken.userId, roleIds);
12679
+ let customClaims;
12680
+ if (authHooks?.customizeAccessToken) {
12681
+ const user = await authRepo.getUserById(storedToken.userId);
12682
+ if (user) {
12683
+ const defaultClaims = {
12684
+ userId: storedToken.userId,
12685
+ roles: roleIds,
12686
+ aal: "aal1"
12687
+ };
12688
+ customClaims = await authHooks.customizeAccessToken(defaultClaims, user);
12689
+ }
12690
+ }
12691
+ const newAccessToken = generateAccessToken(storedToken.userId, roleIds, "aal1", customClaims);
11744
12692
  const newRefreshToken = generateRefreshToken();
11745
12693
  const userAgent = c.req.header("user-agent") || "unknown";
11746
12694
  const ipAddress = c.req.header("x-forwarded-for") || "unknown";
@@ -11762,6 +12710,18 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11762
12710
  const tokenHash = hashRefreshToken(refreshToken);
11763
12711
  await authRepo.deleteRefreshToken(tokenHash);
11764
12712
  }
12713
+ const authHeader = c.req.header("authorization");
12714
+ if (authHooks?.afterLogout && authHeader?.startsWith("Bearer ")) {
12715
+ const {
12716
+ verifyAccessToken: verifyAccessToken2
12717
+ } = await Promise.resolve().then(() => jwt);
12718
+ const payload = verifyAccessToken2(authHeader.substring(7));
12719
+ if (payload) {
12720
+ authHooks.afterLogout(payload.userId).catch((err) => {
12721
+ console.error("[AuthHooks] afterLogout error:", err instanceof Error ? err.message : err);
12722
+ });
12723
+ }
12724
+ }
11765
12725
  return c.json({
11766
12726
  success: true
11767
12727
  });
@@ -11881,6 +12841,270 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11881
12841
  enabledProviders
11882
12842
  });
11883
12843
  });
12844
+ router.post("/anonymous", strictAuthLimiter, async (c) => {
12845
+ const anonId = require$$0$5.randomBytes(16).toString("hex");
12846
+ const anonEmail = `anon_${anonId.slice(0, 8)}@anonymous.local`;
12847
+ let createData = {
12848
+ email: anonEmail,
12849
+ emailVerified: false,
12850
+ isAnonymous: true
12851
+ };
12852
+ if (authHooks?.beforeUserCreate) {
12853
+ createData = await authHooks.beforeUserCreate(createData);
12854
+ }
12855
+ const user = await authRepo.createUser(createData);
12856
+ if (config.defaultRole) {
12857
+ await authRepo.assignDefaultRole(user.id, config.defaultRole);
12858
+ }
12859
+ const {
12860
+ roleIds,
12861
+ accessToken,
12862
+ refreshToken
12863
+ } = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
12864
+ if (authHooks?.afterUserCreate) {
12865
+ authHooks.afterUserCreate(user).catch((err) => {
12866
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
12867
+ });
12868
+ }
12869
+ if (authHooks?.onAuthenticated) {
12870
+ authHooks.onAuthenticated(user, "anonymous").catch((err) => {
12871
+ console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
12872
+ });
12873
+ }
12874
+ return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken), 201);
12875
+ });
12876
+ router.post("/anonymous/link", requireAuth, async (c) => {
12877
+ const userCtx = c.get("user");
12878
+ if (!userCtx) {
12879
+ throw ApiError.unauthorized("Not authenticated");
12880
+ }
12881
+ const user = await authRepo.getUserById(userCtx.userId);
12882
+ if (!user?.isAnonymous) {
12883
+ throw ApiError.badRequest("User is not anonymous", "NOT_ANONYMOUS");
12884
+ }
12885
+ const linkSchema = objectType({
12886
+ email: stringType().email("Invalid email address").max(255),
12887
+ password: stringType().min(1, "Password is required").max(128)
12888
+ });
12889
+ const {
12890
+ email,
12891
+ password
12892
+ } = parseBody2(linkSchema, await c.req.json());
12893
+ const passwordValidation = ops.validatePasswordStrength(password);
12894
+ if (!passwordValidation.valid) {
12895
+ throw ApiError.badRequest(passwordValidation.errors.join(". "), "WEAK_PASSWORD");
12896
+ }
12897
+ const existingUser = await authRepo.getUserByEmail(email.toLowerCase());
12898
+ if (existingUser) {
12899
+ throw ApiError.conflict("Email already registered", "EMAIL_EXISTS");
12900
+ }
12901
+ const passwordHash = await ops.hashPassword(password);
12902
+ const updatedUser = await authRepo.updateUser(user.id, {
12903
+ email: email.toLowerCase(),
12904
+ passwordHash,
12905
+ isAnonymous: false
12906
+ });
12907
+ if (!updatedUser) {
12908
+ throw ApiError.notFound("User not found");
12909
+ }
12910
+ const {
12911
+ roleIds,
12912
+ accessToken,
12913
+ refreshToken
12914
+ } = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
12915
+ return c.json(buildAuthResponse(updatedUser, roleIds, accessToken, refreshToken));
12916
+ });
12917
+ router.post("/mfa/enroll", requireAuth, async (c) => {
12918
+ const userCtx = c.get("user");
12919
+ if (!userCtx) {
12920
+ throw ApiError.unauthorized("Not authenticated");
12921
+ }
12922
+ const body = await c.req.json().catch(() => ({}));
12923
+ const friendlyName = typeof body.friendlyName === "string" ? body.friendlyName : void 0;
12924
+ const issuer = typeof body.issuer === "string" ? body.issuer : emailConfig?.appName || "Rebase";
12925
+ const user = await authRepo.getUserById(userCtx.userId);
12926
+ if (!user) {
12927
+ throw ApiError.notFound("User not found");
12928
+ }
12929
+ const {
12930
+ secret,
12931
+ uri
12932
+ } = generateTotpSecret(issuer, user.email);
12933
+ const factor = await authRepo.createMfaFactor(
12934
+ user.id,
12935
+ "totp",
12936
+ secret,
12937
+ // In production, encrypt this before storage
12938
+ friendlyName
12939
+ );
12940
+ const codes = generateRecoveryCodes(10);
12941
+ const codeHashes = codes.map(hashRecoveryCode);
12942
+ await authRepo.createRecoveryCodes(user.id, codeHashes);
12943
+ return c.json({
12944
+ factor: {
12945
+ id: factor.id,
12946
+ factorType: factor.factorType,
12947
+ friendlyName: factor.friendlyName
12948
+ },
12949
+ totp: {
12950
+ secret,
12951
+ uri,
12952
+ qrUri: uri
12953
+ // Client can use a QR library to render this
12954
+ },
12955
+ recoveryCodes: codes
12956
+ }, 201);
12957
+ });
12958
+ router.post("/mfa/verify", requireAuth, async (c) => {
12959
+ const userCtx = c.get("user");
12960
+ if (!userCtx) {
12961
+ throw ApiError.unauthorized("Not authenticated");
12962
+ }
12963
+ const verifySchema = objectType({
12964
+ factorId: stringType().min(1, "Factor ID is required"),
12965
+ code: stringType().length(6, "Code must be 6 digits")
12966
+ });
12967
+ const {
12968
+ factorId,
12969
+ code: code2
12970
+ } = parseBody2(verifySchema, await c.req.json());
12971
+ const factor = await authRepo.getMfaFactorById(factorId);
12972
+ if (!factor || factor.userId !== userCtx.userId) {
12973
+ throw ApiError.notFound("MFA factor not found");
12974
+ }
12975
+ if (factor.verified) {
12976
+ throw ApiError.badRequest("Factor is already verified", "ALREADY_VERIFIED");
12977
+ }
12978
+ const secretBuffer = base32Decode(factor.secretEncrypted);
12979
+ const isValid2 = verifyTotp(secretBuffer, code2);
12980
+ if (!isValid2) {
12981
+ throw ApiError.unauthorized("Invalid TOTP code", "INVALID_CODE");
12982
+ }
12983
+ await authRepo.verifyMfaFactor(factorId);
12984
+ return c.json({
12985
+ success: true,
12986
+ message: "MFA factor verified and enrolled"
12987
+ });
12988
+ });
12989
+ router.post("/mfa/challenge", requireAuth, async (c) => {
12990
+ const userCtx = c.get("user");
12991
+ if (!userCtx) {
12992
+ throw ApiError.unauthorized("Not authenticated");
12993
+ }
12994
+ const challengeSchema = objectType({
12995
+ factorId: stringType().min(1, "Factor ID is required")
12996
+ });
12997
+ const {
12998
+ factorId
12999
+ } = parseBody2(challengeSchema, await c.req.json());
13000
+ const factor = await authRepo.getMfaFactorById(factorId);
13001
+ if (!factor || factor.userId !== userCtx.userId) {
13002
+ throw ApiError.notFound("MFA factor not found");
13003
+ }
13004
+ if (!factor.verified) {
13005
+ throw ApiError.badRequest("MFA factor is not yet verified", "FACTOR_NOT_VERIFIED");
13006
+ }
13007
+ const ipAddress = c.req.header("x-forwarded-for") || "unknown";
13008
+ const challenge = await authRepo.createMfaChallenge(factorId, ipAddress);
13009
+ return c.json({
13010
+ challengeId: challenge.id,
13011
+ factorId: challenge.factorId,
13012
+ expiresAt: new Date(Date.now() + 5 * 60 * 1e3).toISOString()
13013
+ });
13014
+ });
13015
+ router.post("/mfa/challenge/verify", requireAuth, async (c) => {
13016
+ const userCtx = c.get("user");
13017
+ if (!userCtx) {
13018
+ throw ApiError.unauthorized("Not authenticated");
13019
+ }
13020
+ const challengeVerifySchema = objectType({
13021
+ challengeId: stringType().min(1, "Challenge ID is required"),
13022
+ code: stringType().min(1, "Code is required")
13023
+ });
13024
+ const {
13025
+ challengeId,
13026
+ code: code2
13027
+ } = parseBody2(challengeVerifySchema, await c.req.json());
13028
+ const challenge = await authRepo.getMfaChallengeById(challengeId);
13029
+ if (!challenge) {
13030
+ throw ApiError.badRequest("Invalid or expired challenge", "INVALID_CHALLENGE");
13031
+ }
13032
+ const factor = await authRepo.getMfaFactorById(challenge.factorId);
13033
+ if (!factor || factor.userId !== userCtx.userId) {
13034
+ throw ApiError.notFound("MFA factor not found");
13035
+ }
13036
+ const isRecoveryCode = code2.length > 6;
13037
+ let isValid2 = false;
13038
+ if (isRecoveryCode) {
13039
+ const codeHash = hashRecoveryCode(code2);
13040
+ isValid2 = await authRepo.useRecoveryCode(userCtx.userId, codeHash);
13041
+ } else {
13042
+ const secretBuffer = base32Decode(factor.secretEncrypted);
13043
+ isValid2 = verifyTotp(secretBuffer, code2);
13044
+ }
13045
+ if (!isValid2) {
13046
+ throw ApiError.unauthorized("Invalid verification code", "INVALID_CODE");
13047
+ }
13048
+ await authRepo.verifyMfaChallenge(challengeId);
13049
+ const roles = await authRepo.getUserRoles(userCtx.userId);
13050
+ const roleIds = roles.map((r) => r.id);
13051
+ const accessToken = generateAccessToken(userCtx.userId, roleIds, "aal2");
13052
+ const refreshToken = generateRefreshToken();
13053
+ await authRepo.createRefreshToken(userCtx.userId, hashRefreshToken(refreshToken), getRefreshTokenExpiry(), c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
13054
+ if (authHooks?.onMfaVerified) {
13055
+ authHooks.onMfaVerified(userCtx.userId, factor.id).catch((err) => {
13056
+ console.error("[AuthHooks] onMfaVerified error:", err instanceof Error ? err.message : err);
13057
+ });
13058
+ }
13059
+ return c.json({
13060
+ tokens: {
13061
+ accessToken,
13062
+ refreshToken,
13063
+ accessTokenExpiresAt: getAccessTokenExpiry()
13064
+ }
13065
+ });
13066
+ });
13067
+ router.get("/mfa/factors", requireAuth, async (c) => {
13068
+ const userCtx = c.get("user");
13069
+ if (!userCtx) {
13070
+ throw ApiError.unauthorized("Not authenticated");
13071
+ }
13072
+ const factors = await authRepo.getMfaFactors(userCtx.userId);
13073
+ return c.json({
13074
+ factors: factors.map((f2) => ({
13075
+ id: f2.id,
13076
+ factorType: f2.factorType,
13077
+ friendlyName: f2.friendlyName,
13078
+ verified: f2.verified,
13079
+ createdAt: f2.createdAt
13080
+ }))
13081
+ });
13082
+ });
13083
+ router.delete("/mfa/unenroll", requireAuth, async (c) => {
13084
+ const userCtx = c.get("user");
13085
+ if (!userCtx) {
13086
+ throw ApiError.unauthorized("Not authenticated");
13087
+ }
13088
+ const unenrollSchema = objectType({
13089
+ factorId: stringType().min(1, "Factor ID is required")
13090
+ });
13091
+ const {
13092
+ factorId
13093
+ } = parseBody2(unenrollSchema, await c.req.json());
13094
+ const factor = await authRepo.getMfaFactorById(factorId);
13095
+ if (!factor || factor.userId !== userCtx.userId) {
13096
+ throw ApiError.notFound("MFA factor not found");
13097
+ }
13098
+ await authRepo.deleteMfaFactor(factorId, userCtx.userId);
13099
+ const hasFactors = await authRepo.hasVerifiedMfaFactors(userCtx.userId);
13100
+ if (!hasFactors) {
13101
+ await authRepo.deleteAllRecoveryCodes(userCtx.userId);
13102
+ }
13103
+ return c.json({
13104
+ success: true,
13105
+ message: "MFA factor removed"
13106
+ });
13107
+ });
11884
13108
  return router;
11885
13109
  }
11886
13110
  function generateSecurePassword() {
@@ -11912,9 +13136,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11912
13136
  emailService,
11913
13137
  emailConfig,
11914
13138
  hooks,
11915
- overrides
13139
+ authHooks
11916
13140
  } = config;
11917
- const ops = resolveAuthOverrides(overrides);
13141
+ const ops = resolveAuthHooks(authHooks);
11918
13142
  function buildHookContext(c, method) {
11919
13143
  const user = c.get("user");
11920
13144
  return {
@@ -11934,11 +13158,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11934
13158
  const results = await Promise.all(users.map((u) => applyUserAfterRead(u, ctx)));
11935
13159
  return results.filter((u) => u !== null);
11936
13160
  }
11937
- async function applyRoleAfterReadBatch(roles, ctx) {
11938
- if (!hooks?.roles?.afterRead) return roles;
11939
- const results = await Promise.all(roles.map((r) => hooks.roles.afterRead(r, ctx)));
11940
- return results.filter((r) => r !== null);
11941
- }
11942
13161
  function toAdminUser(u, roles) {
11943
13162
  return {
11944
13163
  uid: u.id,
@@ -11982,6 +13201,10 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11982
13201
  if (!userId) {
11983
13202
  throw ApiError.unauthorized("User ID not found in auth context");
11984
13203
  }
13204
+ const caller = await authRepo.getUserById(userId);
13205
+ if (!caller) {
13206
+ throw ApiError.notFound("Authenticated user does not exist in the database. Please sign out and sign in/register again.", "USER_NOT_FOUND");
13207
+ }
11985
13208
  await authRepo.setUserRoles(userId, ["admin"]);
11986
13209
  if (config.setBootstrapCompleted) {
11987
13210
  await config.setBootstrapCompleted();
@@ -12233,6 +13456,18 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12233
13456
  await authRepo.updateUser(userId, updates);
12234
13457
  }
12235
13458
  if (roles !== void 0 && Array.isArray(roles)) {
13459
+ const currentRoles = await authRepo.getUserRoleIds(userId);
13460
+ const wasAdmin = currentRoles.includes("admin");
13461
+ const willBeAdmin = roles.includes("admin");
13462
+ if (wasAdmin && !willBeAdmin) {
13463
+ const adminUsers = await authRepo.listUsersPaginated({
13464
+ roleId: "admin",
13465
+ limit: 1
13466
+ });
13467
+ if (adminUsers.total <= 1) {
13468
+ throw ApiError.forbidden("Cannot demote the last administrator", "LAST_ADMIN");
13469
+ }
13470
+ }
12236
13471
  await authRepo.setUserRoles(userId, roles);
12237
13472
  }
12238
13473
  const result = await authRepo.getUserWithRoles(userId);
@@ -12257,6 +13492,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12257
13492
  if (!existing) {
12258
13493
  throw ApiError.notFound("User not found");
12259
13494
  }
13495
+ const roles = await authRepo.getUserRoleIds(userId);
13496
+ if (roles.includes("admin")) {
13497
+ const adminUsers = await authRepo.listUsersPaginated({
13498
+ roleId: "admin",
13499
+ limit: 1
13500
+ });
13501
+ if (adminUsers.total <= 1) {
13502
+ throw ApiError.forbidden("Cannot delete the last administrator", "LAST_ADMIN");
13503
+ }
13504
+ }
12260
13505
  const hookCtx = buildHookContext(c, "DELETE");
12261
13506
  if (hooks?.users?.beforeDelete) {
12262
13507
  await hooks.users.beforeDelete(userId, hookCtx);
@@ -12271,95 +13516,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12271
13516
  success: true
12272
13517
  });
12273
13518
  });
12274
- router.get("/roles", requireAdmin, async (c) => {
12275
- const roles = await authRepo.listRoles();
12276
- const hookCtx = buildHookContext(c, "GET");
12277
- let adminRoles = roles.map((r) => ({
12278
- id: r.id,
12279
- name: r.name,
12280
- isAdmin: r.isAdmin,
12281
- defaultPermissions: r.defaultPermissions,
12282
- config: r.config
12283
- }));
12284
- adminRoles = await applyRoleAfterReadBatch(adminRoles, hookCtx);
12285
- return c.json({
12286
- roles: adminRoles
12287
- });
12288
- });
12289
- router.get("/roles/:roleId", requireAdmin, async (c) => {
12290
- const roleId = c.req.param("roleId");
12291
- const role = await authRepo.getRoleById(roleId);
12292
- if (!role) {
12293
- throw ApiError.notFound("Role not found");
12294
- }
12295
- return c.json({
12296
- role
12297
- });
12298
- });
12299
- router.post("/roles", requireAdmin, async (c) => {
12300
- const body = await c.req.json();
12301
- const {
12302
- id,
12303
- name: name2,
12304
- isAdmin,
12305
- defaultPermissions,
12306
- config: config2
12307
- } = body;
12308
- if (!id || !name2) {
12309
- throw ApiError.badRequest("Role ID and name are required", "INVALID_INPUT");
12310
- }
12311
- const existing = await authRepo.getRoleById(id);
12312
- if (existing) {
12313
- throw ApiError.conflict("Role already exists", "ROLE_EXISTS");
12314
- }
12315
- const role = await authRepo.createRole({
12316
- id,
12317
- name: name2,
12318
- isAdmin: isAdmin ?? false,
12319
- defaultPermissions: defaultPermissions ?? null,
12320
- config: config2 ?? null
12321
- });
12322
- return c.json({
12323
- role
12324
- }, 201);
12325
- });
12326
- router.put("/roles/:roleId", requireAdmin, async (c) => {
12327
- const roleId = c.req.param("roleId");
12328
- const body = await c.req.json();
12329
- const {
12330
- name: name2,
12331
- isAdmin,
12332
- defaultPermissions,
12333
- config: config2
12334
- } = body;
12335
- const existing = await authRepo.getRoleById(roleId);
12336
- if (!existing) {
12337
- throw ApiError.notFound("Role not found");
12338
- }
12339
- const role = await authRepo.updateRole(roleId, {
12340
- name: name2,
12341
- isAdmin,
12342
- defaultPermissions,
12343
- config: config2
12344
- });
12345
- return c.json({
12346
- role
12347
- });
12348
- });
12349
- router.delete("/roles/:roleId", requireAdmin, async (c) => {
12350
- const roleId = c.req.param("roleId");
12351
- if (["admin", "editor", "viewer"].includes(roleId)) {
12352
- throw ApiError.badRequest("Cannot delete built-in roles", "BUILTIN_ROLE");
12353
- }
12354
- const existing = await authRepo.getRoleById(roleId);
12355
- if (!existing) {
12356
- throw ApiError.notFound("Role not found");
12357
- }
12358
- await authRepo.deleteRole(roleId);
12359
- return c.json({
12360
- success: true
12361
- });
12362
- });
12363
13519
  return router;
12364
13520
  }
12365
13521
  function createBuiltinAuthAdapter(config) {
@@ -12372,9 +13528,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12372
13528
  oauthProviders = [],
12373
13529
  serviceKey,
12374
13530
  hooks,
12375
- overrides
13531
+ authHooks
12376
13532
  } = config;
12377
- const resolvedOps = resolveAuthOverrides(overrides);
13533
+ const resolvedOps = resolveAuthHooks(authHooks);
12378
13534
  const adapter = {
12379
13535
  id: "rebase-builtin",
12380
13536
  serviceKey,
@@ -12403,8 +13559,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12403
13559
  const extendedPayload = payload;
12404
13560
  let roles = payload.roles || [];
12405
13561
  try {
12406
- const userRoles = await authRepository.getUserRoles(payload.userId);
12407
- roles = userRoles.map((r) => r.id);
13562
+ roles = await authRepository.getUserRoleIds(payload.userId);
12408
13563
  } catch {
12409
13564
  }
12410
13565
  const isAdmin = roles.some((r) => r === "admin" || r === "schema-admin");
@@ -12434,8 +13589,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12434
13589
  const extendedPayload = payload;
12435
13590
  let roles = payload.roles || [];
12436
13591
  try {
12437
- const userRoles = await authRepository.getUserRoles(payload.userId);
12438
- roles = userRoles.map((r) => r.id);
13592
+ roles = await authRepository.getUserRoleIds(payload.userId);
12439
13593
  } catch {
12440
13594
  }
12441
13595
  const isAdmin = roles.some((r) => r === "admin" || r === "schema-admin");
@@ -12448,8 +13602,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12448
13602
  rawToken: token
12449
13603
  };
12450
13604
  },
12451
- userManagement: createUserManagementFromRepo(authRepository, resolvedOps, overrides),
12452
- roleManagement: createRoleManagementFromRepo(authRepository),
13605
+ userManagement: createUserManagementFromRepo(authRepository, resolvedOps, authHooks),
12453
13606
  createAuthRoutes() {
12454
13607
  return createAuthRoutes({
12455
13608
  authRepo: authRepository,
@@ -12458,7 +13611,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12458
13611
  allowRegistration,
12459
13612
  defaultRole,
12460
13613
  oauthProviders,
12461
- overrides
13614
+ authHooks
12462
13615
  });
12463
13616
  },
12464
13617
  createAdminRoutes() {
@@ -12468,7 +13621,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12468
13621
  emailConfig,
12469
13622
  serviceKey,
12470
13623
  hooks,
12471
- overrides
13624
+ authHooks
12472
13625
  });
12473
13626
  },
12474
13627
  async getCapabilities() {
@@ -12497,7 +13650,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12497
13650
  };
12498
13651
  return adapter;
12499
13652
  }
12500
- function createUserManagementFromRepo(repo, resolvedOps, overrides) {
13653
+ function createUserManagementFromRepo(repo, resolvedOps, authHooks) {
12501
13654
  return {
12502
13655
  async listUsers(options2) {
12503
13656
  const result = await repo.listUsersPaginated({
@@ -12528,14 +13681,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12528
13681
  photoUrl: data.photoUrl,
12529
13682
  metadata: data.metadata
12530
13683
  };
12531
- if (overrides?.beforeUserCreate) {
12532
- createData = await overrides.beforeUserCreate(createData);
13684
+ if (authHooks?.beforeUserCreate) {
13685
+ createData = await authHooks.beforeUserCreate(createData);
12533
13686
  }
12534
13687
  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
- });
13688
+ if (authHooks?.afterUserCreate) {
13689
+ try {
13690
+ await authHooks.afterUserCreate(user);
13691
+ } catch (err) {
13692
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
13693
+ }
12539
13694
  }
12540
13695
  return toAuthUserData(user);
12541
13696
  },
@@ -12552,47 +13707,24 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12552
13707
  return user ? toAuthUserData(user) : null;
12553
13708
  },
12554
13709
  async deleteUser(id) {
13710
+ if (authHooks?.beforeUserDelete) {
13711
+ await authHooks.beforeUserDelete(id);
13712
+ }
12555
13713
  await repo.deleteUser(id);
13714
+ if (authHooks?.afterUserDelete) {
13715
+ authHooks.afterUserDelete(id).catch((err) => {
13716
+ console.error("[AuthHooks] afterUserDelete error:", err instanceof Error ? err.message : err);
13717
+ });
13718
+ }
12556
13719
  },
12557
13720
  async getUserRoles(userId) {
12558
- const roles = await repo.getUserRoles(userId);
12559
- return roles.map(toAuthRoleData);
13721
+ return repo.getUserRoleIds(userId);
12560
13722
  },
12561
13723
  async setUserRoles(userId, roleIds) {
12562
13724
  await repo.setUserRoles(userId, roleIds);
12563
13725
  }
12564
13726
  };
12565
13727
  }
12566
- function createRoleManagementFromRepo(repo) {
12567
- return {
12568
- async listRoles() {
12569
- const roles = await repo.listRoles();
12570
- return roles.map(toAuthRoleData);
12571
- },
12572
- async getRoleById(id) {
12573
- const role = await repo.getRoleById(id);
12574
- return role ? toAuthRoleData(role) : null;
12575
- },
12576
- async createRole(data) {
12577
- const role = await repo.createRole({
12578
- id: data.id,
12579
- name: data.name,
12580
- isAdmin: data.isAdmin,
12581
- defaultPermissions: data.defaultPermissions,
12582
- collectionPermissions: data.collectionPermissions,
12583
- config: data.config
12584
- });
12585
- return toAuthRoleData(role);
12586
- },
12587
- async updateRole(id, data) {
12588
- const role = await repo.updateRole(id, data);
12589
- return role ? toAuthRoleData(role) : null;
12590
- },
12591
- async deleteRole(id) {
12592
- await repo.deleteRole(id);
12593
- }
12594
- };
12595
- }
12596
13728
  function toAuthUserData(user) {
12597
13729
  return {
12598
13730
  id: user.id,
@@ -12605,16 +13737,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12605
13737
  updatedAt: user.updatedAt
12606
13738
  };
12607
13739
  }
12608
- function toAuthRoleData(role) {
12609
- return {
12610
- id: role.id,
12611
- name: role.name,
12612
- isAdmin: role.isAdmin,
12613
- defaultPermissions: role.defaultPermissions,
12614
- collectionPermissions: role.collectionPermissions,
12615
- config: role.config
12616
- };
12617
- }
12618
13740
  function configureLogLevel(logLevel) {
12619
13741
  const LOG_LEVEL = logLevel || process.env.LOG_LEVEL || "info";
12620
13742
  const logLevels = {
@@ -12633,20 +13755,20 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12633
13755
  if (currentLevel < 0) console.error = () => {
12634
13756
  };
12635
13757
  }
13758
+ let originalConsole;
12636
13759
  function resetConsole() {
12637
- if (!global.__originalConsole) {
12638
- global.__originalConsole = {
13760
+ if (!originalConsole) {
13761
+ originalConsole = {
12639
13762
  log: console.log,
12640
13763
  warn: console.warn,
12641
13764
  error: console.error,
12642
13765
  debug: console.debug
12643
13766
  };
12644
13767
  }
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;
13768
+ console.log = originalConsole.log;
13769
+ console.warn = originalConsole.warn;
13770
+ console.error = originalConsole.error;
13771
+ console.debug = originalConsole.debug;
12650
13772
  }
12651
13773
  const GCP_SEVERITY = {
12652
13774
  debug: "DEBUG",
@@ -12888,12 +14010,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12888
14010
  exports3.Response = globalObject.Response;
12889
14011
  })(browser$1, browser$1.exports);
12890
14012
  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
14013
  const isStream = (stream2) => stream2 !== null && typeof stream2 === "object" && typeof stream2.pipe === "function";
12898
14014
  isStream.writable = (stream2) => isStream(stream2) && stream2.writable !== false && typeof stream2._write === "function" && typeof stream2._writableState === "object";
12899
14015
  isStream.readable = (stream2) => isStream(stream2) && stream2.readable !== false && typeof stream2._read === "function" && typeof stream2._readableState === "object";
@@ -13678,16 +14794,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
13678
14794
  value: true
13679
14795
  });
13680
14796
  sha1$1.default = void 0;
13681
- function f(s2, x, y2, z) {
14797
+ function f(s2, x, y2, z2) {
13682
14798
  switch (s2) {
13683
14799
  case 0:
13684
- return x & y2 ^ ~x & z;
14800
+ return x & y2 ^ ~x & z2;
13685
14801
  case 1:
13686
- return x ^ y2 ^ z;
14802
+ return x ^ y2 ^ z2;
13687
14803
  case 2:
13688
- return x & y2 ^ x & z ^ y2 & z;
14804
+ return x & y2 ^ x & z2 ^ y2 & z2;
13689
14805
  case 3:
13690
- return x ^ y2 ^ z;
14806
+ return x ^ y2 ^ z2;
13691
14807
  }
13692
14808
  }
13693
14809
  function ROTL(x, n) {
@@ -14736,7 +15852,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
14736
15852
  const extend_1 = __importDefault(extend);
14737
15853
  const https_1 = require$$1;
14738
15854
  const node_fetch_1 = __importDefault(browserExports);
14739
- const querystring_1$1 = __importDefault(require$$11);
15855
+ const querystring_1$1 = __importDefault(require$$1$2);
14740
15856
  const is_stream_1 = __importDefault(isStream_1);
14741
15857
  const url_1 = require$$0$2;
14742
15858
  const common_1 = common$1;
@@ -16414,11 +17530,11 @@ Content-Type: ${partContentType}\r
16414
17530
  return n > 0 || n === i2 ? i2 : i2 - 1;
16415
17531
  }
16416
17532
  function coeffToString(a2) {
16417
- var s2, z, i2 = 1, j = a2.length, r = a2[0] + "";
17533
+ var s2, z2, i2 = 1, j = a2.length, r = a2[0] + "";
16418
17534
  for (; i2 < j; ) {
16419
17535
  s2 = a2[i2++] + "";
16420
- z = LOG_BASE - s2.length;
16421
- for (; z--; s2 = "0" + s2) ;
17536
+ z2 = LOG_BASE - s2.length;
17537
+ for (; z2--; s2 = "0" + s2) ;
16422
17538
  r += s2;
16423
17539
  }
16424
17540
  for (j = r.length; r.charCodeAt(--j) === 48; ) ;
@@ -16451,15 +17567,15 @@ Content-Type: ${partContentType}\r
16451
17567
  function toExponential(str, e) {
16452
17568
  return (str.length > 1 ? str.charAt(0) + "." + str.slice(1) : str) + (e < 0 ? "e" : "e+") + e;
16453
17569
  }
16454
- function toFixedPoint(str, e, z) {
17570
+ function toFixedPoint(str, e, z2) {
16455
17571
  var len2, zs;
16456
17572
  if (e < 0) {
16457
- for (zs = z + "."; ++e; zs += z) ;
17573
+ for (zs = z2 + "."; ++e; zs += z2) ;
16458
17574
  str = zs + str;
16459
17575
  } else {
16460
17576
  len2 = str.length;
16461
17577
  if (++e > len2) {
16462
- for (zs = z, e -= len2; --e; zs += z) ;
17578
+ for (zs = z2, e -= len2; --e; zs += z2) ;
16463
17579
  str += zs;
16464
17580
  } else if (e < len2) {
16465
17581
  str = str.slice(0, e) + "." + str.slice(e);
@@ -16878,7 +17994,7 @@ Content-Type: ${partContentType}\r
16878
17994
  exports3.isGoogleComputeEngine = isGoogleComputeEngine;
16879
17995
  exports3.detectGCPResidency = detectGCPResidency;
16880
17996
  const fs_1 = fs$4;
16881
- const os_1 = require$$1$2;
17997
+ const os_1 = require$$1$3;
16882
17998
  exports3.GCE_LINUX_BIOS_PATHS = {
16883
17999
  BIOS_DATE: "/sys/class/dmi/id/bios_date",
16884
18000
  BIOS_VENDOR: "/sys/class/dmi/id/bios_vendor"
@@ -17011,7 +18127,7 @@ Content-Type: ${partContentType}\r
17011
18127
  exports3.setBackend = setBackend;
17012
18128
  exports3.log = log;
17013
18129
  const node_events_1 = require$$0$8;
17014
- const process2 = __importStar2(require$$1$3);
18130
+ const process2 = __importStar2(require$$1$4);
17015
18131
  const util2 = __importStar2(require$$2$1);
17016
18132
  const colours_1 = colours;
17017
18133
  var LogSeverity;
@@ -18083,7 +19199,7 @@ Content-Type: ${partContentType}\r
18083
19199
  Object.defineProperty(oauth2client, "__esModule", { value: true });
18084
19200
  oauth2client.OAuth2Client = oauth2client.ClientAuthentication = oauth2client.CertificateFormat = oauth2client.CodeChallengeMethod = void 0;
18085
19201
  const gaxios_1$5 = src$2;
18086
- const querystring$2 = require$$11;
19202
+ const querystring$2 = require$$1$2;
18087
19203
  const stream$4 = require$$0$4;
18088
19204
  const formatEcdsa = ecdsaSigFormatter;
18089
19205
  const crypto_1$2 = requireCrypto();
@@ -19571,7 +20687,7 @@ Content-Type: ${partContentType}\r
19571
20687
  Object.defineProperty(refreshclient, "__esModule", { value: true });
19572
20688
  refreshclient.UserRefreshClient = refreshclient.USER_REFRESH_ACCOUNT_TYPE = void 0;
19573
20689
  const oauth2client_1$1 = oauth2client;
19574
- const querystring_1 = require$$11;
20690
+ const querystring_1 = require$$1$2;
19575
20691
  refreshclient.USER_REFRESH_ACCOUNT_TYPE = "authorized_user";
19576
20692
  class UserRefreshClient extends oauth2client_1$1.OAuth2Client {
19577
20693
  constructor(optionsOrClientId, clientSecret, refreshToken, eagerRefreshThresholdMillis, forceRefreshOnFailure) {
@@ -19846,7 +20962,7 @@ Content-Type: ${partContentType}\r
19846
20962
  Object.defineProperty(oauth2common, "__esModule", { value: true });
19847
20963
  oauth2common.OAuthClientAuthHandler = void 0;
19848
20964
  oauth2common.getErrorFromOAuthErrorResponse = getErrorFromOAuthErrorResponse;
19849
- const querystring$1 = require$$11;
20965
+ const querystring$1 = require$$1$2;
19850
20966
  const crypto_1$1 = requireCrypto();
19851
20967
  const METHODS_SUPPORTING_REQUEST_BODY = ["PUT", "POST", "PATCH"];
19852
20968
  class OAuthClientAuthHandler {
@@ -19992,7 +21108,7 @@ Content-Type: ${partContentType}\r
19992
21108
  Object.defineProperty(stscredentials, "__esModule", { value: true });
19993
21109
  stscredentials.StsCredentials = void 0;
19994
21110
  const gaxios_1$1 = src$2;
19995
- const querystring = require$$11;
21111
+ const querystring = require$$1$2;
19996
21112
  const transporters_1 = transporters;
19997
21113
  const oauth2common_1$1 = oauth2common;
19998
21114
  class StsCredentials extends oauth2common_1$1.OAuthClientAuthHandler {
@@ -21598,7 +22714,7 @@ ${credentialScope}
21598
22714
  const child_process_1 = require$$0$a;
21599
22715
  const fs2 = fs$4;
21600
22716
  const gcpMetadata2 = src$3;
21601
- const os2 = require$$1$2;
22717
+ const os2 = require$$1$3;
21602
22718
  const path2 = path$3;
21603
22719
  const crypto_12 = requireCrypto();
21604
22720
  const transporters_12 = transporters;
@@ -22962,7 +24078,7 @@ ${credentialScope}
22962
24078
  }
22963
24079
  function createAppleProvider(config) {
22964
24080
  async function generateClientSecret() {
22965
- return jwt.sign({}, config.privateKey, {
24081
+ return jwt$1.sign({}, config.privateKey, {
22966
24082
  algorithm: "ES256",
22967
24083
  keyid: config.keyId,
22968
24084
  issuer: config.teamId,
@@ -23442,6 +24558,341 @@ ${credentialScope}
23442
24558
  }
23443
24559
  };
23444
24560
  }
24561
+ const TABLE$1 = "rebase.api_keys";
24562
+ function generateApiKey() {
24563
+ const random = require$$0$5.randomBytes(16).toString("hex");
24564
+ return `rk_live_${random}`;
24565
+ }
24566
+ function hashKey(plaintext) {
24567
+ return require$$0$5.createHash("sha256").update(plaintext).digest("hex");
24568
+ }
24569
+ function keyPrefix(plaintext) {
24570
+ return plaintext.substring(0, 12);
24571
+ }
24572
+ function toMasked(row) {
24573
+ return {
24574
+ id: row.id,
24575
+ name: row.name,
24576
+ key_prefix: row.key_prefix,
24577
+ permissions: row.permissions,
24578
+ rate_limit: row.rate_limit,
24579
+ created_by: row.created_by,
24580
+ created_at: row.created_at,
24581
+ updated_at: row.updated_at,
24582
+ last_used_at: row.last_used_at,
24583
+ expires_at: row.expires_at,
24584
+ revoked_at: row.revoked_at
24585
+ };
24586
+ }
24587
+ function rowToApiKey(row) {
24588
+ const permissions = typeof row.permissions === "string" ? JSON.parse(row.permissions) : row.permissions ?? [];
24589
+ return {
24590
+ id: row.id,
24591
+ name: row.name,
24592
+ key_prefix: row.key_prefix,
24593
+ key_hash: row.key_hash,
24594
+ permissions,
24595
+ rate_limit: row.rate_limit !== null && row.rate_limit !== void 0 ? Number(row.rate_limit) : null,
24596
+ created_by: row.created_by,
24597
+ created_at: new Date(row.created_at).toISOString(),
24598
+ updated_at: new Date(row.updated_at).toISOString(),
24599
+ last_used_at: row.last_used_at ? new Date(row.last_used_at).toISOString() : null,
24600
+ expires_at: row.expires_at ? new Date(row.expires_at).toISOString() : null,
24601
+ revoked_at: row.revoked_at ? new Date(row.revoked_at).toISOString() : null
24602
+ };
24603
+ }
24604
+ function esc(value) {
24605
+ return value.replace(/'/g, "''");
24606
+ }
24607
+ function createApiKeyStore(driver) {
24608
+ const admin = driver.admin;
24609
+ if (!isSQLAdmin(admin)) {
24610
+ logger.warn("⚠️ [api-key-store] DataDriver does not support SQL admin — API keys will not be available.");
24611
+ return void 0;
24612
+ }
24613
+ const exec = admin.executeSql.bind(admin);
24614
+ return {
24615
+ // ── Schema bootstrap ────────────────────────────────────────
24616
+ async ensureTable() {
24617
+ try {
24618
+ await exec("CREATE SCHEMA IF NOT EXISTS rebase");
24619
+ await exec(`
24620
+ CREATE TABLE IF NOT EXISTS ${TABLE$1} (
24621
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
24622
+ name TEXT NOT NULL,
24623
+ key_prefix TEXT NOT NULL,
24624
+ key_hash TEXT NOT NULL UNIQUE,
24625
+ permissions JSONB NOT NULL DEFAULT '[]'::jsonb,
24626
+ rate_limit INTEGER,
24627
+ created_by TEXT NOT NULL,
24628
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
24629
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
24630
+ last_used_at TIMESTAMPTZ,
24631
+ expires_at TIMESTAMPTZ,
24632
+ revoked_at TIMESTAMPTZ
24633
+ )
24634
+ `);
24635
+ await exec(`
24636
+ CREATE INDEX IF NOT EXISTS idx_api_keys_hash
24637
+ ON ${TABLE$1}(key_hash)
24638
+ `);
24639
+ await exec(`
24640
+ CREATE INDEX IF NOT EXISTS idx_api_keys_prefix
24641
+ ON ${TABLE$1}(key_prefix)
24642
+ `);
24643
+ logger.info("✅ API keys table ready");
24644
+ } catch (err) {
24645
+ logger.error("❌ Failed to create API keys table", {
24646
+ error: err
24647
+ });
24648
+ logger.warn("⚠️ Continuing without API keys support.");
24649
+ }
24650
+ },
24651
+ // ── Create ──────────────────────────────────────────────────
24652
+ async createApiKey(request, createdBy) {
24653
+ const plaintext = generateApiKey();
24654
+ const hash = hashKey(plaintext);
24655
+ const prefix = keyPrefix(plaintext);
24656
+ const permissionsJson = JSON.stringify(request.permissions);
24657
+ const rateLimitSql = request.rate_limit !== null && request.rate_limit !== void 0 ? String(request.rate_limit) : "NULL";
24658
+ const expiresAtSql = request.expires_at ? `'${esc(request.expires_at)}'` : "NULL";
24659
+ const rows = await exec(`
24660
+ INSERT INTO ${TABLE$1} (name, key_prefix, key_hash, permissions, rate_limit, created_by, expires_at)
24661
+ VALUES (
24662
+ '${esc(request.name)}',
24663
+ '${esc(prefix)}',
24664
+ '${esc(hash)}',
24665
+ '${esc(permissionsJson)}'::jsonb,
24666
+ ${rateLimitSql},
24667
+ '${esc(createdBy)}',
24668
+ ${expiresAtSql}
24669
+ )
24670
+ RETURNING *
24671
+ `);
24672
+ const apiKey = rowToApiKey(rows[0]);
24673
+ return {
24674
+ ...toMasked(apiKey),
24675
+ key: plaintext
24676
+ };
24677
+ },
24678
+ // ── Lookup by hash ──────────────────────────────────────────
24679
+ async findByKeyHash(hash) {
24680
+ const rows = await exec(`
24681
+ SELECT * FROM ${TABLE$1}
24682
+ WHERE key_hash = '${esc(hash)}'
24683
+ LIMIT 1
24684
+ `);
24685
+ if (rows.length === 0) return null;
24686
+ return rowToApiKey(rows[0]);
24687
+ },
24688
+ // ── List all (masked) ───────────────────────────────────────
24689
+ async listApiKeys() {
24690
+ const rows = await exec(`
24691
+ SELECT * FROM ${TABLE$1}
24692
+ ORDER BY created_at DESC
24693
+ `);
24694
+ return rows.map((r) => toMasked(rowToApiKey(r)));
24695
+ },
24696
+ // ── Get by ID (masked) ──────────────────────────────────────
24697
+ async getApiKeyById(id) {
24698
+ const rows = await exec(`
24699
+ SELECT * FROM ${TABLE$1}
24700
+ WHERE id = '${esc(id)}'
24701
+ LIMIT 1
24702
+ `);
24703
+ if (rows.length === 0) return null;
24704
+ return toMasked(rowToApiKey(rows[0]));
24705
+ },
24706
+ // ── Update ──────────────────────────────────────────────────
24707
+ async updateApiKey(id, updates) {
24708
+ const setClauses = [];
24709
+ if (updates.name !== void 0) {
24710
+ setClauses.push(`name = '${esc(updates.name)}'`);
24711
+ }
24712
+ if (updates.permissions !== void 0) {
24713
+ setClauses.push(`permissions = '${esc(JSON.stringify(updates.permissions))}'::jsonb`);
24714
+ }
24715
+ if (updates.rate_limit !== void 0) {
24716
+ setClauses.push(updates.rate_limit !== null ? `rate_limit = ${updates.rate_limit}` : "rate_limit = NULL");
24717
+ }
24718
+ if (updates.expires_at !== void 0) {
24719
+ setClauses.push(updates.expires_at !== null ? `expires_at = '${esc(updates.expires_at)}'` : "expires_at = NULL");
24720
+ }
24721
+ if (setClauses.length === 0) {
24722
+ return this.getApiKeyById(id);
24723
+ }
24724
+ setClauses.push("updated_at = NOW()");
24725
+ const rows = await exec(`
24726
+ UPDATE ${TABLE$1}
24727
+ SET ${setClauses.join(", ")}
24728
+ WHERE id = '${esc(id)}'
24729
+ RETURNING *
24730
+ `);
24731
+ if (rows.length === 0) return null;
24732
+ return toMasked(rowToApiKey(rows[0]));
24733
+ },
24734
+ // ── Revoke (soft-delete) ────────────────────────────────────
24735
+ async revokeApiKey(id) {
24736
+ const rows = await exec(`
24737
+ UPDATE ${TABLE$1}
24738
+ SET revoked_at = NOW(), updated_at = NOW()
24739
+ WHERE id = '${esc(id)}' AND revoked_at IS NULL
24740
+ RETURNING id
24741
+ `);
24742
+ return rows.length > 0;
24743
+ },
24744
+ // ── Touch last_used_at ──────────────────────────────────────
24745
+ async updateLastUsed(id) {
24746
+ try {
24747
+ await exec(`
24748
+ UPDATE ${TABLE$1}
24749
+ SET last_used_at = NOW()
24750
+ WHERE id = '${esc(id)}'
24751
+ `);
24752
+ } catch (err) {
24753
+ logger.error("[api-key-store] Failed to update last_used_at", {
24754
+ error: err
24755
+ });
24756
+ }
24757
+ }
24758
+ };
24759
+ }
24760
+ function validatePermissions(permissions) {
24761
+ if (!Array.isArray(permissions)) return false;
24762
+ for (const perm of permissions) {
24763
+ if (typeof perm !== "object" || perm === null) return false;
24764
+ if (typeof perm.collection !== "string" || perm.collection.length === 0) return false;
24765
+ if (!Array.isArray(perm.operations) || perm.operations.length === 0) return false;
24766
+ const validOps = /* @__PURE__ */ new Set(["read", "write", "delete"]);
24767
+ for (const op of perm.operations) {
24768
+ if (!validOps.has(op)) return false;
24769
+ }
24770
+ }
24771
+ return true;
24772
+ }
24773
+ function createApiKeyRoutes(options2) {
24774
+ const {
24775
+ store,
24776
+ serviceKey
24777
+ } = options2;
24778
+ const router = new hono.Hono();
24779
+ router.onError(errorHandler);
24780
+ router.use("/*", createRequireAuth({
24781
+ serviceKey
24782
+ }));
24783
+ router.use("/*", requireAdmin);
24784
+ router.get("/", async (c) => {
24785
+ const keys2 = await store.listApiKeys();
24786
+ return c.json({
24787
+ keys: keys2
24788
+ });
24789
+ });
24790
+ router.post("/", async (c) => {
24791
+ const body = await c.req.json();
24792
+ const {
24793
+ name: name2,
24794
+ permissions,
24795
+ rate_limit,
24796
+ expires_at
24797
+ } = body;
24798
+ if (!name2 || typeof name2 !== "string" || name2.trim().length === 0) {
24799
+ throw ApiError.badRequest("Name is required", "INVALID_INPUT");
24800
+ }
24801
+ if (!validatePermissions(permissions)) {
24802
+ throw ApiError.badRequest("Permissions must be an array of { collection: string, operations: ('read' | 'write' | 'delete')[] }", "INVALID_INPUT");
24803
+ }
24804
+ if (rate_limit !== void 0 && rate_limit !== null) {
24805
+ if (typeof rate_limit !== "number" || rate_limit < 1 || !Number.isInteger(rate_limit)) {
24806
+ throw ApiError.badRequest("rate_limit must be a positive integer or null", "INVALID_INPUT");
24807
+ }
24808
+ }
24809
+ if (expires_at !== void 0 && expires_at !== null) {
24810
+ const parsed = new Date(expires_at);
24811
+ if (isNaN(parsed.getTime())) {
24812
+ throw ApiError.badRequest("expires_at must be a valid ISO-8601 date", "INVALID_INPUT");
24813
+ }
24814
+ if (parsed <= /* @__PURE__ */ new Date()) {
24815
+ throw ApiError.badRequest("expires_at must be in the future", "INVALID_INPUT");
24816
+ }
24817
+ }
24818
+ const user = c.get("user");
24819
+ const createdBy = user && typeof user === "object" && "userId" in user ? user.userId : "unknown";
24820
+ const request = {
24821
+ name: name2.trim(),
24822
+ permissions,
24823
+ rate_limit: rate_limit ?? null,
24824
+ expires_at: expires_at ?? null
24825
+ };
24826
+ const keyWithSecret = await store.createApiKey(request, createdBy);
24827
+ return c.json({
24828
+ key: keyWithSecret
24829
+ }, 201);
24830
+ });
24831
+ router.get("/:id", async (c) => {
24832
+ const id = c.req.param("id");
24833
+ const key = await store.getApiKeyById(id);
24834
+ if (!key) {
24835
+ throw ApiError.notFound("API key not found");
24836
+ }
24837
+ return c.json({
24838
+ key
24839
+ });
24840
+ });
24841
+ router.put("/:id", async (c) => {
24842
+ const id = c.req.param("id");
24843
+ const body = await c.req.json();
24844
+ const {
24845
+ name: name2,
24846
+ permissions,
24847
+ rate_limit,
24848
+ expires_at
24849
+ } = body;
24850
+ if (name2 !== void 0) {
24851
+ if (typeof name2 !== "string" || name2.trim().length === 0) {
24852
+ throw ApiError.badRequest("Name must be a non-empty string", "INVALID_INPUT");
24853
+ }
24854
+ }
24855
+ if (permissions !== void 0) {
24856
+ if (!validatePermissions(permissions)) {
24857
+ throw ApiError.badRequest("Permissions must be an array of { collection: string, operations: ('read' | 'write' | 'delete')[] }", "INVALID_INPUT");
24858
+ }
24859
+ }
24860
+ if (rate_limit !== void 0 && rate_limit !== null) {
24861
+ if (typeof rate_limit !== "number" || rate_limit < 1 || !Number.isInteger(rate_limit)) {
24862
+ throw ApiError.badRequest("rate_limit must be a positive integer or null", "INVALID_INPUT");
24863
+ }
24864
+ }
24865
+ if (expires_at !== void 0 && expires_at !== null) {
24866
+ const parsed = new Date(expires_at);
24867
+ if (isNaN(parsed.getTime())) {
24868
+ throw ApiError.badRequest("expires_at must be a valid ISO-8601 date", "INVALID_INPUT");
24869
+ }
24870
+ }
24871
+ const updates = {};
24872
+ if (name2 !== void 0) updates.name = name2.trim();
24873
+ if (permissions !== void 0) updates.permissions = permissions;
24874
+ if (rate_limit !== void 0) updates.rate_limit = rate_limit;
24875
+ if (expires_at !== void 0) updates.expires_at = expires_at;
24876
+ const key = await store.updateApiKey(id, updates);
24877
+ if (!key) {
24878
+ throw ApiError.notFound("API key not found");
24879
+ }
24880
+ return c.json({
24881
+ key
24882
+ });
24883
+ });
24884
+ router.delete("/:id", async (c) => {
24885
+ const id = c.req.param("id");
24886
+ const revoked = await store.revokeApiKey(id);
24887
+ if (!revoked) {
24888
+ throw ApiError.notFound("API key not found or already revoked");
24889
+ }
24890
+ return c.json({
24891
+ success: true
24892
+ });
24893
+ });
24894
+ return router;
24895
+ }
23445
24896
  function createCustomAuthAdapter(options2) {
23446
24897
  const defaultCapabilities = {
23447
24898
  hasBuiltInAuthRoutes: false,
@@ -23468,7 +24919,6 @@ ${credentialScope}
23468
24919
  verifyRequest: options2.verifyRequest,
23469
24920
  verifyToken: resolvedVerifyToken,
23470
24921
  userManagement: options2.userManagement,
23471
- roleManagement: options2.roleManagement,
23472
24922
  getCapabilities() {
23473
24923
  return defaultCapabilities;
23474
24924
  }
@@ -23476,9 +24926,13 @@ ${credentialScope}
23476
24926
  }
23477
24927
  const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
23478
24928
  __proto__: null,
24929
+ apiKeyKeyGenerator,
23479
24930
  configureJwt,
23480
24931
  createAdapterAuthMiddleware,
23481
24932
  createAdminRoutes,
24933
+ createApiKeyRateLimiter,
24934
+ createApiKeyRoutes,
24935
+ createApiKeyStore,
23482
24936
  createAppleProvider,
23483
24937
  createAuthMiddleware,
23484
24938
  createAuthRoutes,
@@ -23504,11 +24958,15 @@ ${credentialScope}
23504
24958
  getRefreshTokenExpiry,
23505
24959
  hashPassword,
23506
24960
  hashRefreshToken,
24961
+ httpMethodToOperation,
24962
+ isApiKeyToken,
24963
+ isOperationAllowed,
23507
24964
  optionalAuth,
23508
24965
  requireAdmin,
23509
24966
  requireAuth,
23510
- resolveAuthOverrides,
24967
+ resolveAuthHooks,
23511
24968
  strictAuthLimiter,
24969
+ validateApiKey,
23512
24970
  validatePasswordStrength,
23513
24971
  verifyAccessToken,
23514
24972
  verifyPassword
@@ -24033,6 +25491,409 @@ ${credentialScope}
24033
25491
  };
24034
25492
  }
24035
25493
  }
25494
+ let sharpFactory;
25495
+ async function getSharp() {
25496
+ if (!sharpFactory) {
25497
+ try {
25498
+ const mod = await import("sharp");
25499
+ sharpFactory = mod.default;
25500
+ } catch (err) {
25501
+ throw new Error("Failed to load optional 'sharp' dependency for image transformation.");
25502
+ }
25503
+ }
25504
+ if (!sharpFactory) {
25505
+ throw new Error("Failed to load optional 'sharp' dependency for image transformation.");
25506
+ }
25507
+ return sharpFactory;
25508
+ }
25509
+ const MAX_DIMENSION = 4096;
25510
+ const MAX_QUALITY = 100;
25511
+ const MIN_QUALITY = 1;
25512
+ const VALID_FORMATS = /* @__PURE__ */ new Set(["webp", "avif", "jpeg", "png"]);
25513
+ const VALID_FITS = /* @__PURE__ */ new Set(["cover", "contain", "fill", "inside", "outside"]);
25514
+ function parseTransformOptions(query) {
25515
+ const opts = {};
25516
+ let hasTransform = false;
25517
+ if (query.width) {
25518
+ const w2 = parseInt(query.width, 10);
25519
+ if (!Number.isNaN(w2) && w2 > 0) {
25520
+ opts.width = Math.min(w2, MAX_DIMENSION);
25521
+ hasTransform = true;
25522
+ }
25523
+ }
25524
+ if (query.height) {
25525
+ const h2 = parseInt(query.height, 10);
25526
+ if (!Number.isNaN(h2) && h2 > 0) {
25527
+ opts.height = Math.min(h2, MAX_DIMENSION);
25528
+ hasTransform = true;
25529
+ }
25530
+ }
25531
+ if (query.quality) {
25532
+ const q = parseInt(query.quality, 10);
25533
+ if (!Number.isNaN(q)) {
25534
+ opts.quality = Math.min(Math.max(q, MIN_QUALITY), MAX_QUALITY);
25535
+ hasTransform = true;
25536
+ }
25537
+ }
25538
+ if (query.format && VALID_FORMATS.has(query.format)) {
25539
+ opts.format = query.format;
25540
+ hasTransform = true;
25541
+ }
25542
+ if (query.fit && VALID_FITS.has(query.fit)) {
25543
+ opts.fit = query.fit;
25544
+ hasTransform = true;
25545
+ }
25546
+ return hasTransform ? opts : null;
25547
+ }
25548
+ const FORMAT_CONTENT_TYPES = {
25549
+ webp: "image/webp",
25550
+ avif: "image/avif",
25551
+ jpeg: "image/jpeg",
25552
+ png: "image/png"
25553
+ };
25554
+ function isTransformableImage(contentType) {
25555
+ return contentType.startsWith("image/") && !contentType.includes("svg") && !contentType.includes("gif");
25556
+ }
25557
+ async function transformImage(buffer, options2) {
25558
+ const sharp = await getSharp();
25559
+ let pipeline = sharp(buffer);
25560
+ if (options2.width || options2.height) {
25561
+ pipeline = pipeline.resize({
25562
+ width: options2.width,
25563
+ height: options2.height,
25564
+ fit: options2.fit || "cover",
25565
+ withoutEnlargement: true
25566
+ });
25567
+ }
25568
+ const format = options2.format || "webp";
25569
+ const quality = options2.quality || 80;
25570
+ switch (format) {
25571
+ case "webp":
25572
+ pipeline = pipeline.webp({
25573
+ quality
25574
+ });
25575
+ break;
25576
+ case "avif":
25577
+ pipeline = pipeline.avif({
25578
+ quality
25579
+ });
25580
+ break;
25581
+ case "jpeg":
25582
+ pipeline = pipeline.jpeg({
25583
+ quality
25584
+ });
25585
+ break;
25586
+ case "png":
25587
+ pipeline = pipeline.png({
25588
+ quality
25589
+ });
25590
+ break;
25591
+ }
25592
+ const data = await pipeline.toBuffer();
25593
+ return {
25594
+ data,
25595
+ contentType: FORMAT_CONTENT_TYPES[format]
25596
+ };
25597
+ }
25598
+ class TransformCache {
25599
+ cache = /* @__PURE__ */ new Map();
25600
+ maxEntries;
25601
+ maxAgeMs;
25602
+ constructor(maxEntries = 500, maxAgeMs = 36e5) {
25603
+ this.maxEntries = maxEntries;
25604
+ this.maxAgeMs = maxAgeMs;
25605
+ }
25606
+ /** Build a deterministic cache key from file key + transform options. */
25607
+ buildKey(fileKey, options2) {
25608
+ return `${fileKey}::${JSON.stringify(options2)}`;
25609
+ }
25610
+ get(cacheKey) {
25611
+ const entry = this.cache.get(cacheKey);
25612
+ if (!entry) return null;
25613
+ if (Date.now() - entry.timestamp > this.maxAgeMs) {
25614
+ this.cache.delete(cacheKey);
25615
+ return null;
25616
+ }
25617
+ this.cache.delete(cacheKey);
25618
+ this.cache.set(cacheKey, entry);
25619
+ return {
25620
+ data: entry.data,
25621
+ contentType: entry.contentType
25622
+ };
25623
+ }
25624
+ set(cacheKey, data, contentType) {
25625
+ if (this.cache.size >= this.maxEntries) {
25626
+ const oldest = this.cache.keys().next().value;
25627
+ if (oldest !== void 0) this.cache.delete(oldest);
25628
+ }
25629
+ this.cache.set(cacheKey, {
25630
+ data,
25631
+ contentType,
25632
+ timestamp: Date.now()
25633
+ });
25634
+ }
25635
+ }
25636
+ const MAX_UPLOAD_SIZE = 5 * 1024 * 1024 * 1024;
25637
+ const UPLOAD_EXPIRY_MS = 24 * 60 * 60 * 1e3;
25638
+ class TusHandler {
25639
+ constructor(storageBaseDir, storageController) {
25640
+ this.storageController = storageController;
25641
+ this.tusDir = path$3.join(storageBaseDir, ".tus-uploads");
25642
+ }
25643
+ uploads = /* @__PURE__ */ new Map();
25644
+ tusDir;
25645
+ cleanupTimer;
25646
+ /** Ensure the temp directory exists. */
25647
+ async ensureDir() {
25648
+ if (!fs$4.existsSync(this.tusDir)) {
25649
+ await promises.mkdir(this.tusDir, {
25650
+ recursive: true
25651
+ });
25652
+ }
25653
+ }
25654
+ /** Start periodic cleanup of stale uploads. */
25655
+ startCleanup() {
25656
+ if (this.cleanupTimer) return;
25657
+ this.cleanupTimer = setInterval(() => {
25658
+ void this.cleanupStale();
25659
+ }, 6e4);
25660
+ }
25661
+ /** Remove uploads that have been idle for longer than UPLOAD_EXPIRY_MS. */
25662
+ async cleanupStale() {
25663
+ const now = Date.now();
25664
+ for (const [id, upload] of this.uploads) {
25665
+ if (now - upload.createdAt > UPLOAD_EXPIRY_MS && !upload.completed) {
25666
+ try {
25667
+ await promises.unlink(upload.filePath);
25668
+ } catch {
25669
+ }
25670
+ this.uploads.delete(id);
25671
+ }
25672
+ }
25673
+ }
25674
+ // -----------------------------------------------------------------------
25675
+ // TUS Metadata Parsing
25676
+ // -----------------------------------------------------------------------
25677
+ /**
25678
+ * Parse the `Upload-Metadata` header.
25679
+ *
25680
+ * Format: `key base64value,key2 base64value2`
25681
+ */
25682
+ parseMetadata(header) {
25683
+ const metadata = {};
25684
+ if (!header) return metadata;
25685
+ for (const pair of header.split(",")) {
25686
+ const trimmed = pair.trim();
25687
+ const spaceIdx = trimmed.indexOf(" ");
25688
+ if (spaceIdx === -1) {
25689
+ metadata[trimmed] = "";
25690
+ } else {
25691
+ const key = trimmed.substring(0, spaceIdx);
25692
+ const value = Buffer.from(trimmed.substring(spaceIdx + 1), "base64").toString("utf-8");
25693
+ metadata[key] = value;
25694
+ }
25695
+ }
25696
+ return metadata;
25697
+ }
25698
+ // -----------------------------------------------------------------------
25699
+ // Protocol Endpoints
25700
+ // -----------------------------------------------------------------------
25701
+ /** `OPTIONS /tus` — TUS capability discovery. */
25702
+ options() {
25703
+ return new Response(null, {
25704
+ status: 204,
25705
+ headers: {
25706
+ "Tus-Resumable": "1.0.0",
25707
+ "Tus-Version": "1.0.0",
25708
+ "Tus-Extension": "creation,termination",
25709
+ "Tus-Max-Size": String(MAX_UPLOAD_SIZE)
25710
+ }
25711
+ });
25712
+ }
25713
+ /** `POST /tus` — Create a new upload. */
25714
+ async create(c) {
25715
+ await this.ensureDir();
25716
+ const uploadLengthHeader = c.req.header("Upload-Length");
25717
+ if (!uploadLengthHeader) {
25718
+ return c.json({
25719
+ error: "Upload-Length header is required"
25720
+ }, 400);
25721
+ }
25722
+ const uploadLength = parseInt(uploadLengthHeader, 10);
25723
+ if (Number.isNaN(uploadLength) || uploadLength <= 0) {
25724
+ return c.json({
25725
+ error: "Invalid Upload-Length"
25726
+ }, 400);
25727
+ }
25728
+ if (uploadLength > MAX_UPLOAD_SIZE) {
25729
+ return c.json({
25730
+ error: `Upload-Length exceeds maximum of ${MAX_UPLOAD_SIZE} bytes`
25731
+ }, 413);
25732
+ }
25733
+ const metadata = this.parseMetadata(c.req.header("Upload-Metadata") || "");
25734
+ const id = require$$0$5.randomUUID();
25735
+ const filePath = path$3.join(this.tusDir, id);
25736
+ await promises.writeFile(filePath, Buffer.alloc(0));
25737
+ const upload = {
25738
+ id,
25739
+ size: uploadLength,
25740
+ offset: 0,
25741
+ metadata,
25742
+ createdAt: Date.now(),
25743
+ filePath,
25744
+ bucket: metadata.bucket || void 0,
25745
+ key: metadata.key || metadata.filename || void 0,
25746
+ completed: false
25747
+ };
25748
+ this.uploads.set(id, upload);
25749
+ const reqUrl = new URL(c.req.url);
25750
+ const location = `${reqUrl.origin}${reqUrl.pathname}/${id}`;
25751
+ return new Response(null, {
25752
+ status: 201,
25753
+ headers: {
25754
+ Location: location,
25755
+ "Tus-Resumable": "1.0.0",
25756
+ "Upload-Offset": "0"
25757
+ }
25758
+ });
25759
+ }
25760
+ /** `HEAD /tus/:id` — Query upload progress. */
25761
+ head(c, id) {
25762
+ const upload = this.uploads.get(id);
25763
+ if (!upload) {
25764
+ return c.json({
25765
+ error: "Upload not found"
25766
+ }, 404);
25767
+ }
25768
+ return new Response(null, {
25769
+ status: 200,
25770
+ headers: {
25771
+ "Tus-Resumable": "1.0.0",
25772
+ "Upload-Offset": String(upload.offset),
25773
+ "Upload-Length": String(upload.size),
25774
+ "Cache-Control": "no-store"
25775
+ }
25776
+ });
25777
+ }
25778
+ /** `PATCH /tus/:id` — Append data to an upload. */
25779
+ async patch(c, id) {
25780
+ const upload = this.uploads.get(id);
25781
+ if (!upload) {
25782
+ return c.json({
25783
+ error: "Upload not found"
25784
+ }, 404);
25785
+ }
25786
+ if (upload.completed) {
25787
+ return c.json({
25788
+ error: "Upload already completed"
25789
+ }, 400);
25790
+ }
25791
+ const offsetHeader = c.req.header("Upload-Offset");
25792
+ if (!offsetHeader) {
25793
+ return c.json({
25794
+ error: "Upload-Offset header is required"
25795
+ }, 400);
25796
+ }
25797
+ const offset = parseInt(offsetHeader, 10);
25798
+ if (offset !== upload.offset) {
25799
+ return c.json({
25800
+ error: "Offset mismatch"
25801
+ }, 409);
25802
+ }
25803
+ const contentType = c.req.header("Content-Type");
25804
+ if (contentType !== "application/offset+octet-stream") {
25805
+ return c.json({
25806
+ error: "Content-Type must be application/offset+octet-stream"
25807
+ }, 415);
25808
+ }
25809
+ const body = await c.req.arrayBuffer();
25810
+ const chunk = Buffer.from(body);
25811
+ if (upload.offset + chunk.length > upload.size) {
25812
+ return c.json({
25813
+ error: "Chunk exceeds declared Upload-Length"
25814
+ }, 413);
25815
+ }
25816
+ const fh = await promises.open(upload.filePath, "a");
25817
+ try {
25818
+ await fh.write(chunk);
25819
+ } finally {
25820
+ await fh.close();
25821
+ }
25822
+ upload.offset += chunk.length;
25823
+ if (upload.offset >= upload.size) {
25824
+ await this.finalize(upload);
25825
+ }
25826
+ return new Response(null, {
25827
+ status: 204,
25828
+ headers: {
25829
+ "Tus-Resumable": "1.0.0",
25830
+ "Upload-Offset": String(upload.offset)
25831
+ }
25832
+ });
25833
+ }
25834
+ /** `DELETE /tus/:id` — Cancel and remove an upload. */
25835
+ async delete(c, id) {
25836
+ const upload = this.uploads.get(id);
25837
+ if (!upload) {
25838
+ return c.json({
25839
+ error: "Upload not found"
25840
+ }, 404);
25841
+ }
25842
+ try {
25843
+ await promises.unlink(upload.filePath);
25844
+ } catch {
25845
+ }
25846
+ this.uploads.delete(id);
25847
+ return new Response(null, {
25848
+ status: 204,
25849
+ headers: {
25850
+ "Tus-Resumable": "1.0.0"
25851
+ }
25852
+ });
25853
+ }
25854
+ // -----------------------------------------------------------------------
25855
+ // Finalization
25856
+ // -----------------------------------------------------------------------
25857
+ /**
25858
+ * Move a completed upload into the storage controller.
25859
+ */
25860
+ async finalize(upload) {
25861
+ upload.completed = true;
25862
+ if (!this.storageController) {
25863
+ logger.warn("[TUS] Upload completed but no StorageController configured. Temp file remains:", {
25864
+ filePath: upload.filePath
25865
+ });
25866
+ return;
25867
+ }
25868
+ try {
25869
+ const {
25870
+ readFile: readFile2
25871
+ } = await import("fs/promises");
25872
+ const data = await readFile2(upload.filePath);
25873
+ const fileName = upload.key || upload.metadata.filename || upload.id;
25874
+ const mimeType = upload.metadata.contentType || upload.metadata.filetype || "application/octet-stream";
25875
+ const file = new File([data], fileName, {
25876
+ type: mimeType
25877
+ });
25878
+ await this.storageController.putObject({
25879
+ file,
25880
+ key: fileName,
25881
+ bucket: upload.bucket
25882
+ });
25883
+ try {
25884
+ await promises.unlink(upload.filePath);
25885
+ } catch {
25886
+ }
25887
+ this.uploads.delete(upload.id);
25888
+ logger.info(`[TUS] Upload ${upload.id} finalized → ${fileName}`);
25889
+ } catch (err) {
25890
+ logger.error(`[TUS] Failed to finalize upload ${upload.id}`, {
25891
+ error: err
25892
+ });
25893
+ }
25894
+ }
25895
+ }
25896
+ const transformCache = new TransformCache();
24036
25897
  function extractWildcardPath(c) {
24037
25898
  const routePath = c.req.routePath;
24038
25899
  const prefix = routePath.replace("/*", "");
@@ -24096,6 +25957,7 @@ ${credentialScope}
24096
25957
  throw ApiError.notFound("File not found");
24097
25958
  }
24098
25959
  const filePath = decodeURIComponent(rawPath);
25960
+ const transformOpts = parseTransformOptions(c.req.query());
24099
25961
  if (controller.getType() === "local") {
24100
25962
  const localController = controller;
24101
25963
  const {
@@ -24115,8 +25977,19 @@ ${credentialScope}
24115
25977
  } catch {
24116
25978
  }
24117
25979
  }
24118
- c.header("Content-Type", contentType);
24119
25980
  const fileContent = fs__namespace.readFileSync(absolutePath);
25981
+ if (transformOpts && isTransformableImage(contentType)) {
25982
+ const cacheKey = transformCache.buildKey(filePath, transformOpts);
25983
+ let cached = transformCache.get(cacheKey);
25984
+ if (!cached) {
25985
+ cached = await transformImage(Buffer.from(fileContent), transformOpts);
25986
+ transformCache.set(cacheKey, cached.data, cached.contentType);
25987
+ }
25988
+ c.header("Content-Type", cached.contentType);
25989
+ c.header("Cache-Control", "public, max-age=31536000, immutable");
25990
+ return c.body(new Uint8Array(cached.data));
25991
+ }
25992
+ c.header("Content-Type", contentType);
24120
25993
  return c.body(new Uint8Array(fileContent));
24121
25994
  }
24122
25995
  const {
@@ -24127,7 +26000,20 @@ ${credentialScope}
24127
26000
  if (!fileObject) {
24128
26001
  throw ApiError.notFound("File not found");
24129
26002
  }
24130
- c.header("Content-Type", fileObject.type || "application/octet-stream");
26003
+ const remoteContentType = fileObject.type || "application/octet-stream";
26004
+ if (transformOpts && isTransformableImage(remoteContentType)) {
26005
+ const cacheKey = transformCache.buildKey(filePath, transformOpts);
26006
+ let cached = transformCache.get(cacheKey);
26007
+ if (!cached) {
26008
+ const buf2 = Buffer.from(await fileObject.arrayBuffer());
26009
+ cached = await transformImage(buf2, transformOpts);
26010
+ transformCache.set(cacheKey, cached.data, cached.contentType);
26011
+ }
26012
+ c.header("Content-Type", cached.contentType);
26013
+ c.header("Cache-Control", "public, max-age=31536000, immutable");
26014
+ return c.body(new Uint8Array(cached.data));
26015
+ }
26016
+ c.header("Content-Type", remoteContentType);
24131
26017
  c.header("Cache-Control", "public, max-age=3600, immutable");
24132
26018
  const buf = await fileObject.arrayBuffer();
24133
26019
  return c.body(new Uint8Array(buf));
@@ -24223,6 +26109,14 @@ ${credentialScope}
24223
26109
  message: "Folder created"
24224
26110
  }, 201);
24225
26111
  });
26112
+ const tusBaseDir = controller.getType() === "local" ? controller.getBasePath() : process.env.STORAGE_PATH || "./uploads";
26113
+ const tusHandler = new TusHandler(tusBaseDir, controller);
26114
+ tusHandler.startCleanup();
26115
+ router.options("/tus", (_c2) => tusHandler.options());
26116
+ router.post("/tus", writeAuthMiddleware, async (c) => tusHandler.create(c));
26117
+ router.get("/tus/:id", readAuthMiddleware, (c) => tusHandler.head(c, c.req.param("id")));
26118
+ router.patch("/tus/:id", writeAuthMiddleware, async (c) => tusHandler.patch(c, c.req.param("id")));
26119
+ router.delete("/tus/:id", writeAuthMiddleware, async (c) => tusHandler.delete(c, c.req.param("id")));
24226
26120
  return router;
24227
26121
  }
24228
26122
  const DEFAULT_STORAGE_ID = "(default)";
@@ -24446,6 +26340,13 @@ ${credentialScope}
24446
26340
  } catch (e) {
24447
26341
  }
24448
26342
  }
26343
+ const getErrorField = (obj, field) => {
26344
+ const err = obj?.error;
26345
+ if (err && typeof err === "object" && err !== null && field in err) {
26346
+ return err[field];
26347
+ }
26348
+ return obj?.[field];
26349
+ };
24449
26350
  if (res.status === 401 && onUnauthorizedHandler) {
24450
26351
  const retried = await onUnauthorizedHandler();
24451
26352
  if (retried) {
@@ -24479,7 +26380,7 @@ ${credentialScope}
24479
26380
  const method = init?.method || "GET";
24480
26381
  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
26382
  }
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);
26383
+ throw new RebaseApiError(retryRes.status, String(getErrorField(retryBody, "message") || fallbackMessage || `Request failed with status ${retryRes.status}`), getErrorField(retryBody, "code"), getErrorField(retryBody, "details"));
24483
26384
  }
24484
26385
  return retryBody;
24485
26386
  }
@@ -24490,7 +26391,7 @@ ${credentialScope}
24490
26391
  const method = init?.method || "GET";
24491
26392
  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
26393
  }
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);
26394
+ throw new RebaseApiError(res.status, String(getErrorField(body, "message") || fallbackMessage || `Request failed with status ${res.status}`), getErrorField(body, "code"), getErrorField(body, "details"));
24494
26395
  }
24495
26396
  return body;
24496
26397
  }
@@ -25064,33 +26965,6 @@ ${credentialScope}
25064
26965
  method: "DELETE"
25065
26966
  });
25066
26967
  }
25067
- async function listRoles() {
25068
- return transport.request(adminPath + "/roles", {
25069
- method: "GET"
25070
- });
25071
- }
25072
- async function getRole(roleId) {
25073
- return transport.request(adminPath + "/roles/" + encodeURIComponent(roleId), {
25074
- method: "GET"
25075
- });
25076
- }
25077
- async function createRole(data) {
25078
- return transport.request(adminPath + "/roles", {
25079
- method: "POST",
25080
- body: JSON.stringify(data)
25081
- });
25082
- }
25083
- async function updateRole(roleId, data) {
25084
- return transport.request(adminPath + "/roles/" + encodeURIComponent(roleId), {
25085
- method: "PUT",
25086
- body: JSON.stringify(data)
25087
- });
25088
- }
25089
- async function deleteRole(roleId) {
25090
- return transport.request(adminPath + "/roles/" + encodeURIComponent(roleId), {
25091
- method: "DELETE"
25092
- });
25093
- }
25094
26968
  async function bootstrap() {
25095
26969
  return transport.request(adminPath + "/bootstrap", {
25096
26970
  method: "POST"
@@ -25103,11 +26977,6 @@ ${credentialScope}
25103
26977
  createUser,
25104
26978
  updateUser,
25105
26979
  deleteUser,
25106
- listRoles,
25107
- getRole,
25108
- createRole,
25109
- updateRole,
25110
- deleteRole,
25111
26980
  bootstrap
25112
26981
  };
25113
26982
  }
@@ -25746,7 +27615,7 @@ ${credentialScope}
25746
27615
  const http = require$$0$6;
25747
27616
  const https = require$$1;
25748
27617
  const urllib$2 = require$$0$2;
25749
- const zlib = require$$11;
27618
+ const zlib = require$$3;
25750
27619
  const PassThrough$3 = require$$0$4.PassThrough;
25751
27620
  const Cookies = cookies;
25752
27621
  const packageData$7 = require$$9;
@@ -25981,9 +27850,9 @@ ${credentialScope}
25981
27850
  const util2 = require$$5;
25982
27851
  const fs2 = fs$4;
25983
27852
  const nmfetch2 = fetchExports;
25984
- const dns2 = require$$11;
27853
+ const dns2 = require$$4$1;
25985
27854
  const net2 = require$$0$7;
25986
- const os2 = require$$1$2;
27855
+ const os2 = require$$1$3;
25987
27856
  const DNS_TTL = 5 * 60 * 1e3;
25988
27857
  let networkInterfaces;
25989
27858
  try {
@@ -32129,7 +33998,7 @@ ${credentialScope}
32129
33998
  const packageData$6 = require$$9;
32130
33999
  const MailMessage = mailMessage;
32131
34000
  const net$1 = require$$0$7;
32132
- const dns = require$$11;
34001
+ const dns = require$$4$1;
32133
34002
  const crypto$3 = require$$0$5;
32134
34003
  class Mail extends EventEmitter$5 {
32135
34004
  constructor(transporter, options2, defaults) {
@@ -32565,7 +34434,7 @@ ${credentialScope}
32565
34434
  const EventEmitter$4 = require$$0$9.EventEmitter;
32566
34435
  const net = require$$0$7;
32567
34436
  const tls = require$$1$1;
32568
- const os = require$$1$2;
34437
+ const os = require$$1$3;
32569
34438
  const crypto$2 = require$$0$5;
32570
34439
  const DataStream = dataStream;
32571
34440
  const PassThrough = require$$0$4.PassThrough;
@@ -36652,6 +38521,9 @@ ${credentialScope}
36652
38521
  dir: config.collectionsDir
36653
38522
  });
36654
38523
  }
38524
+ if (config.auth) {
38525
+ activeCollections = Array.from(new Map([defaultUsersCollection, ...activeCollections].map((c) => [c.slug, c])).values());
38526
+ }
36655
38527
  const realtimeServices = {};
36656
38528
  const delegates = {};
36657
38529
  let bootstrappers = config.bootstrappers || [];
@@ -36720,8 +38592,7 @@ ${credentialScope}
36720
38592
  id: authAdapter.id
36721
38593
  });
36722
38594
  authConfigResult = {
36723
- userService: authAdapter.userManagement ?? {},
36724
- roleService: authAdapter.roleManagement ?? {}
38595
+ userService: authAdapter.userManagement ?? {}
36725
38596
  };
36726
38597
  } else {
36727
38598
  const safeAuthConfig = config.auth;
@@ -36753,7 +38624,7 @@ ${credentialScope}
36753
38624
  oauthProviders,
36754
38625
  serviceKey,
36755
38626
  hooks: config.hooks,
36756
- overrides: safeAuthConfig.overrides
38627
+ authHooks: safeAuthConfig.hooks
36757
38628
  });
36758
38629
  }
36759
38630
  logger.info("Authentication initialized");
@@ -36898,7 +38769,7 @@ ${credentialScope}
36898
38769
  oauthProviders,
36899
38770
  serviceKey,
36900
38771
  hooks: config.hooks,
36901
- overrides: safeAuthConfig.overrides
38772
+ authHooks: safeAuthConfig.hooks
36902
38773
  });
36903
38774
  }
36904
38775
  if (authAdapter.createAuthRoutes) {
@@ -36920,6 +38791,21 @@ ${credentialScope}
36920
38791
  }
36921
38792
  }
36922
38793
  }
38794
+ let apiKeyStore;
38795
+ const apiKeyStoreResult = createApiKeyStore(defaultDriver);
38796
+ if (apiKeyStoreResult) {
38797
+ apiKeyStore = apiKeyStoreResult;
38798
+ await apiKeyStore.ensureTable();
38799
+ logger.info("Service API Keys initialized");
38800
+ const apiKeyRoutes = createApiKeyRoutes({
38801
+ store: apiKeyStore,
38802
+ serviceKey
38803
+ });
38804
+ config.app.route(`${basePath}/admin/api-keys`, apiKeyRoutes);
38805
+ logger.info("API key admin routes mounted", {
38806
+ path: `${basePath}/admin/api-keys`
38807
+ });
38808
+ }
36923
38809
  if (config.collectionsDir) {
36924
38810
  if (process.env.NODE_ENV !== "production") {
36925
38811
  const {
@@ -36970,15 +38856,20 @@ ${credentialScope}
36970
38856
  dataRouter.use("/*", createAdapterAuthMiddleware({
36971
38857
  adapter: authAdapter,
36972
38858
  driver: defaultDriver,
36973
- requireAuth: dataRequireAuth
38859
+ requireAuth: dataRequireAuth,
38860
+ apiKeyStore
36974
38861
  }));
36975
38862
  } else {
36976
38863
  dataRouter.use("/*", createAuthMiddleware({
36977
38864
  driver: defaultDriver,
36978
38865
  requireAuth: dataRequireAuth,
36979
- serviceKey
38866
+ serviceKey,
38867
+ apiKeyStore
36980
38868
  }));
36981
38869
  }
38870
+ if (apiKeyStore) {
38871
+ dataRouter.use("/*", createApiKeyRateLimiter());
38872
+ }
36982
38873
  if (historyConfigResult && historyConfigResult.historyService) {
36983
38874
  const historyRoutes = createHistoryRoutes({
36984
38875
  historyService: historyConfigResult.historyService,
@@ -37047,6 +38938,13 @@ ${credentialScope}
37047
38938
  configured: emailService.isConfigured()
37048
38939
  });
37049
38940
  }
38941
+ const driverAdmin = defaultBootstrapper.getAdmin?.(defaultDriverResult);
38942
+ if (isSQLAdmin(driverAdmin)) {
38943
+ Object.assign(serverClient, {
38944
+ sql: (query, options2) => driverAdmin.executeSql(query, options2)
38945
+ });
38946
+ logger.info("SQL capability attached to singleton");
38947
+ }
37050
38948
  _initRebase(serverClient);
37051
38949
  logger.info("Rebase singleton initialized");
37052
38950
  if (defaultDriverResult.internals) {
@@ -37072,13 +38970,15 @@ ${credentialScope}
37072
38970
  functionsRouter.use("/*", createAdapterAuthMiddleware({
37073
38971
  adapter: authAdapter,
37074
38972
  driver: defaultDriver,
37075
- requireAuth: functionsRequireAuth
38973
+ requireAuth: functionsRequireAuth,
38974
+ apiKeyStore
37076
38975
  }));
37077
38976
  } else {
37078
38977
  functionsRouter.use("/*", createAuthMiddleware({
37079
38978
  driver: defaultDriver,
37080
38979
  requireAuth: functionsRequireAuth,
37081
- serviceKey
38980
+ serviceKey,
38981
+ apiKeyStore
37082
38982
  }));
37083
38983
  }
37084
38984
  const fnRoutes = createFunctionRoutes2(loadedFunctions);
@@ -47077,7 +48977,7 @@ spurious results.`);
47077
48977
  return "undefined";
47078
48978
  }
47079
48979
  if (typeof obj === "string") {
47080
- return `"${obj.replace(/"/g, '\\"')}"`;
48980
+ return JSON.stringify(obj);
47081
48981
  }
47082
48982
  if (typeof obj === "number" || typeof obj === "boolean") {
47083
48983
  return String(obj);
@@ -47108,7 +49008,7 @@ ${indent2}]`;
47108
49008
  const kind = init.getKind();
47109
49009
  const isCode = kind === tsMorph.SyntaxKind.ArrowFunction || kind === tsMorph.SyntaxKind.FunctionExpression || kind === tsMorph.SyntaxKind.Identifier || kind === tsMorph.SyntaxKind.CallExpression || kind === tsMorph.SyntaxKind.JsxElement;
47110
49010
  if (isCode || name2 === "target" || name2 === "callbacks" || name2 === "permissions" || name2 === "securityRules") {
47111
- const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name2) ? name2 : `"${name2}"`;
49011
+ const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name2) ? name2 : JSON.stringify(name2);
47112
49012
  preservedProps.push(`${keyStr}: ${init.getText()}`);
47113
49013
  }
47114
49014
  }
@@ -47118,7 +49018,7 @@ ${indent2}]`;
47118
49018
  }
47119
49019
  if (keys2.length === 0 && preservedProps.length === 0) return "{}";
47120
49020
  const props = keys2.map((key) => {
47121
- const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : `"${key}"`;
49021
+ const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
47122
49022
  let childAstNode;
47123
49023
  if (oldAstNode && typeof record[key] === "object" && record[key] !== null && !Array.isArray(record[key])) {
47124
49024
  const oldProp = oldAstNode.getProperty((p) => "getName" in p && typeof p.getName === "function" && (p.getName() === key || p.getName() === `"${key}"` || p.getName() === `'${key}'`));
@@ -47160,7 +49060,7 @@ ${indent2}}`;
47160
49060
  }
47161
49061
  } else {
47162
49062
  propsObj.addPropertyAssignment({
47163
- name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey : `"${propertyKey}"`,
49063
+ name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey : JSON.stringify(propertyKey),
47164
49064
  initializer: newInitializer
47165
49065
  });
47166
49066
  }
@@ -48231,7 +50131,7 @@ export default ${safeId}Collection;
48231
50131
  const mod = await dynamicImport(fileUrl);
48232
50132
  const exported = mod.default;
48233
50133
  if (!exported) {
48234
- console.warn(`[functions] ${file}: no default export. Skipping.`);
50134
+ logger.warn(`[functions] ${file}: no default export. Skipping.`);
48235
50135
  continue;
48236
50136
  }
48237
50137
  if (isHonoLike(exported)) {
@@ -48240,7 +50140,7 @@ export default ${safeId}Collection;
48240
50140
  name: name2,
48241
50141
  app: exported
48242
50142
  });
48243
- console.log(`⚡ Loaded function route: ${name2}`);
50143
+ logger.info(`⚡ Loaded function route: ${name2}`);
48244
50144
  continue;
48245
50145
  }
48246
50146
  if (typeof exported === "function") {
@@ -48251,20 +50151,20 @@ export default ${safeId}Collection;
48251
50151
  name: name2,
48252
50152
  app: result
48253
50153
  });
48254
- console.log(`⚡ Loaded function route: ${name2}`);
50154
+ logger.info(`⚡ Loaded function route: ${name2}`);
48255
50155
  continue;
48256
50156
  }
48257
50157
  }
48258
50158
  const exportType = typeof exported;
48259
50159
  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.
50160
+ logger.warn(`[functions] ${file}: default export is not a Hono app or factory. Skipping.
48261
50161
  export type: ${exportType}${exported?.constructor?.name ? ` (${exported.constructor.name})` : ""}
48262
50162
  prototype methods: ${keys2}
48263
50163
  Hint: ensure the function exports a Hono app created with the same hono version as the server.
48264
50164
  The loader checks for .fetch() and .routes — any Hono-compatible app will work.`);
48265
50165
  } catch (err) {
48266
50166
  const message = err instanceof Error ? err.message : String(err);
48267
- console.error(`[functions] Failed to load ${file}: ${message}`);
50167
+ logger.error(`[functions] Failed to load ${file}: ${message}`);
48268
50168
  }
48269
50169
  }
48270
50170
  }
@@ -48313,12 +50213,12 @@ export default ${safeId}Collection;
48313
50213
  const mod = await dynamicImport(fileUrl);
48314
50214
  const exported = mod.default;
48315
50215
  if (!exported || typeof exported !== "object") {
48316
- console.warn(`[cron] ${file}: no valid default export. Skipping.`);
50216
+ logger.warn(`[cron] ${file}: no valid default export. Skipping.`);
48317
50217
  continue;
48318
50218
  }
48319
50219
  const def = exported;
48320
50220
  if (typeof def.schedule !== "string" || typeof def.handler !== "function") {
48321
- console.warn(`[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`);
50221
+ logger.warn(`[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`);
48322
50222
  continue;
48323
50223
  }
48324
50224
  const id = path__namespace.basename(file, path__namespace.extname(file));
@@ -48334,10 +50234,10 @@ export default ${safeId}Collection;
48334
50234
  id,
48335
50235
  definition
48336
50236
  });
48337
- console.log(`⏰ Loaded cron job: ${id} (${definition.schedule})`);
50237
+ logger.info(`⏰ Loaded cron job: ${id} (${definition.schedule})`);
48338
50238
  } catch (err) {
48339
50239
  const message = err instanceof Error ? err.message : String(err);
48340
- console.error(`[cron] Failed to load ${file}: ${message}`);
50240
+ logger.error(`[cron] Failed to load ${file}: ${message}`);
48341
50241
  }
48342
50242
  }
48343
50243
  }
@@ -48492,12 +50392,12 @@ export default ${safeId}Collection;
48492
50392
  for (const loaded of loadedJobs) {
48493
50393
  const validation = validateCronExpression(loaded.definition.schedule);
48494
50394
  if (!validation.valid) {
48495
- console.error(`[cron] Rejecting job "${loaded.id}": invalid schedule "${loaded.definition.schedule}" — ${validation.reason}`);
50395
+ logger.error(`[cron] Rejecting job "${loaded.id}": invalid schedule "${loaded.definition.schedule}" — ${validation.reason}`);
48496
50396
  continue;
48497
50397
  }
48498
50398
  const existing = this.jobs.get(loaded.id);
48499
50399
  if (existing) {
48500
- console.warn(`[cron] Duplicate cron job id: "${loaded.id}". Overwriting.`);
50400
+ logger.warn(`[cron] Duplicate cron job id: "${loaded.id}". Overwriting.`);
48501
50401
  this.stopJob(loaded.id);
48502
50402
  }
48503
50403
  const enabled = loaded.definition.enabled !== false;
@@ -48535,7 +50435,9 @@ export default ${safeId}Collection;
48535
50435
  }
48536
50436
  }
48537
50437
  }).catch((err) => {
48538
- console.warn("[cron] Failed to seed job stats from database:", err);
50438
+ logger.warn("[cron] Failed to seed job stats from database", {
50439
+ error: err
50440
+ });
48539
50441
  });
48540
50442
  }
48541
50443
  for (const [id, job] of this.jobs) {
@@ -48543,7 +50445,7 @@ export default ${safeId}Collection;
48543
50445
  this.scheduleNext(id);
48544
50446
  }
48545
50447
  }
48546
- console.log(`⏰ Cron scheduler started with ${this.jobs.size} job(s)`);
50448
+ logger.info(`⏰ Cron scheduler started with ${this.jobs.size} job(s)`);
48547
50449
  }
48548
50450
  /**
48549
50451
  * Stop the scheduler and clear all timers.
@@ -48617,7 +50519,7 @@ export default ${safeId}Collection;
48617
50519
  const job = this.jobs.get(id);
48618
50520
  if (!job) return void 0;
48619
50521
  if (job.executing) {
48620
- console.warn(`[cron] Skipping manual trigger of "${id}" — already executing`);
50522
+ logger.warn(`[cron] Skipping manual trigger of "${id}" — already executing`);
48621
50523
  const logEntry = {
48622
50524
  jobId: id,
48623
50525
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -48661,7 +50563,7 @@ export default ${safeId}Collection;
48661
50563
  const timer = setTimeout(async () => {
48662
50564
  if (!job.enabled || !this.started) return;
48663
50565
  if (job.executing) {
48664
- console.warn(`[cron] Skipping scheduled run of "${id}" — still executing from previous run`);
50566
+ logger.warn(`[cron] Skipping scheduled run of "${id}" — still executing from previous run`);
48665
50567
  this.scheduleNext(id);
48666
50568
  return;
48667
50569
  }
@@ -48675,7 +50577,9 @@ export default ${safeId}Collection;
48675
50577
  }
48676
50578
  job.timerId = timer;
48677
50579
  } catch (err) {
48678
- console.error(`[cron] Failed to schedule "${id}":`, err);
50580
+ logger.error(`[cron] Failed to schedule "${id}"`, {
50581
+ error: err
50582
+ });
48679
50583
  job.state = "error";
48680
50584
  job.lastError = err instanceof Error ? err.message : String(err);
48681
50585
  }
@@ -48760,13 +50664,15 @@ export default ${safeId}Collection;
48760
50664
  }
48761
50665
  if (this.store) {
48762
50666
  this.store.insertLog(logEntry).catch((persistErr) => {
48763
- console.error(`[cron] Failed to persist log for "${job.id}":`, persistErr);
50667
+ logger.error(`[cron] Failed to persist log for "${job.id}"`, {
50668
+ error: persistErr
50669
+ });
48764
50670
  });
48765
50671
  }
48766
50672
  if (success) {
48767
- console.log(`✅ [cron] "${job.id}" completed in ${durationMs}ms`);
50673
+ logger.info(`✅ [cron] "${job.id}" completed in ${durationMs}ms`);
48768
50674
  } else {
48769
- console.error(`❌ [cron] "${job.id}" failed in ${durationMs}ms: ${error2}`);
50675
+ logger.error(`❌ [cron] "${job.id}" failed in ${durationMs}ms: ${error2}`);
48770
50676
  }
48771
50677
  return logEntry;
48772
50678
  }
@@ -48884,7 +50790,7 @@ export default ${safeId}Collection;
48884
50790
  function createCronStore(driver) {
48885
50791
  const admin = driver.admin;
48886
50792
  if (!isSQLAdmin(admin)) {
48887
- console.warn("⚠️ [cron-store] DataDriver does not support SQL admin — cron logs will not be persisted.");
50793
+ logger.warn("⚠️ [cron-store] DataDriver does not support SQL admin — cron logs will not be persisted.");
48888
50794
  return void 0;
48889
50795
  }
48890
50796
  const exec = admin.executeSql.bind(admin);
@@ -48910,10 +50816,12 @@ export default ${safeId}Collection;
48910
50816
  CREATE INDEX IF NOT EXISTS idx_cron_logs_job
48911
50817
  ON ${TABLE}(job_id, started_at DESC)
48912
50818
  `);
48913
- console.log("✅ Cron logs table ready");
50819
+ logger.info("✅ Cron logs table ready");
48914
50820
  } catch (err) {
48915
- console.error("❌ Failed to create cron logs table:", err);
48916
- console.warn("⚠️ Continuing without cron log persistence.");
50821
+ logger.error("❌ Failed to create cron logs table", {
50822
+ error: err
50823
+ });
50824
+ logger.warn("⚠️ Continuing without cron log persistence.");
48917
50825
  }
48918
50826
  },
48919
50827
  async insertLog(entry) {
@@ -48936,7 +50844,9 @@ export default ${safeId}Collection;
48936
50844
  )
48937
50845
  `);
48938
50846
  } catch (err) {
48939
- console.error(`[cron-store] Failed to persist log for "${entry.jobId}":`, err);
50847
+ logger.error(`[cron-store] Failed to persist log for "${entry.jobId}"`, {
50848
+ error: err
50849
+ });
48940
50850
  }
48941
50851
  },
48942
50852
  async fetchLogs(jobId, limit = 50) {
@@ -48950,7 +50860,9 @@ export default ${safeId}Collection;
48950
50860
  `);
48951
50861
  return rows.map(rowToLogEntry);
48952
50862
  } catch (err) {
48953
- console.error(`[cron-store] Failed to fetch logs for "${jobId}":`, err);
50863
+ logger.error(`[cron-store] Failed to fetch logs for "${jobId}"`, {
50864
+ error: err
50865
+ });
48954
50866
  return [];
48955
50867
  }
48956
50868
  },
@@ -48974,7 +50886,9 @@ export default ${safeId}Collection;
48974
50886
  });
48975
50887
  }
48976
50888
  } catch (err) {
48977
- console.error("[cron-store] Failed to fetch job stats:", err);
50889
+ logger.error("[cron-store] Failed to fetch job stats", {
50890
+ error: err
50891
+ });
48978
50892
  }
48979
50893
  return stats;
48980
50894
  }
@@ -49005,8 +50919,8 @@ export default ${safeId}Collection;
49005
50919
  indexFile = "index.html"
49006
50920
  } = config;
49007
50921
  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.");
50922
+ logger.warn(`⚠️ Frontend build path does not exist: ${frontendPath}`);
50923
+ logger.warn(" SPA serving is disabled. Build your frontend first.");
49010
50924
  return;
49011
50925
  }
49012
50926
  app.use("/*", serveStatic.serveStatic({
@@ -49019,13 +50933,13 @@ export default ${safeId}Collection;
49019
50933
  }
49020
50934
  const indexPath = path__namespace.join(frontendPath, indexFile);
49021
50935
  if (!fs__namespace.existsSync(indexPath)) {
49022
- console.warn(`⚠️ Index file not found: ${indexPath}`);
50936
+ logger.warn(`⚠️ Index file not found: ${indexPath}`);
49023
50937
  return next();
49024
50938
  }
49025
50939
  const html = fs__namespace.readFileSync(indexPath, "utf-8");
49026
50940
  return c.html(html);
49027
50941
  });
49028
- console.log(`✅ SPA serving enabled from: ${frontendPath}`);
50942
+ logger.info(`✅ SPA serving enabled from: ${frontendPath}`);
49029
50943
  }
49030
50944
  const MAX_PORT_ATTEMPTS = 20;
49031
50945
  const DEV_PORT_FILENAME = ".rebase-dev-port";
@@ -49033,6 +50947,19 @@ export default ${safeId}Collection;
49033
50947
  const host = options2?.host ?? "0.0.0.0";
49034
50948
  const maxAttempts = options2?.maxAttempts ?? MAX_PORT_ATTEMPTS;
49035
50949
  const portFileDir = options2?.portFileDir;
50950
+ const isProd = process.env.NODE_ENV === "production";
50951
+ if (isProd) {
50952
+ return new Promise((resolve, reject) => {
50953
+ const onError = (err) => {
50954
+ reject(err);
50955
+ };
50956
+ server.once("error", onError);
50957
+ server.listen(startPort, host, () => {
50958
+ server.removeListener("error", onError);
50959
+ resolve(startPort);
50960
+ });
50961
+ });
50962
+ }
49036
50963
  let affinityPort = null;
49037
50964
  if (portFileDir) {
49038
50965
  try {
@@ -49175,6 +51102,8 @@ export default ${safeId}Collection;
49175
51102
  DB_POOL_MAX: stringType().default("20").transform(Number),
49176
51103
  DB_POOL_IDLE_TIMEOUT: stringType().default("30000").transform(Number),
49177
51104
  DB_POOL_CONNECT_TIMEOUT: stringType().default("10000").transform(Number),
51105
+ DATABASE_DIRECT_URL: stringType().url().optional(),
51106
+ DATABASE_READ_URL: stringType().url().optional(),
49178
51107
  FORCE_LOCAL_STORAGE: optionalBoolString,
49179
51108
  STORAGE_TYPE: enumType(["local", "s3"]).default("local"),
49180
51109
  STORAGE_PATH: stringType().optional(),
@@ -49250,8 +51179,11 @@ export default ${safeId}Collection;
49250
51179
  exports2.RestApiGenerator = RestApiGenerator;
49251
51180
  exports2.S3StorageController = S3StorageController;
49252
51181
  exports2.SMTPEmailService = SMTPEmailService;
51182
+ exports2.TransformCache = TransformCache;
51183
+ exports2.TusHandler = TusHandler;
49253
51184
  exports2._resetRebaseMock = _resetRebaseMock;
49254
51185
  exports2._setRebaseMock = _setRebaseMock;
51186
+ exports2.apiKeyKeyGenerator = apiKeyKeyGenerator;
49255
51187
  exports2.authJwt = authJwt;
49256
51188
  exports2.authRoles = authRoles;
49257
51189
  exports2.authUid = authUid;
@@ -49260,6 +51192,9 @@ export default ${safeId}Collection;
49260
51192
  exports2.configureLogLevel = configureLogLevel;
49261
51193
  exports2.createAdapterAuthMiddleware = createAdapterAuthMiddleware;
49262
51194
  exports2.createAdminRoutes = createAdminRoutes;
51195
+ exports2.createApiKeyRateLimiter = createApiKeyRateLimiter;
51196
+ exports2.createApiKeyRoutes = createApiKeyRoutes;
51197
+ exports2.createApiKeyStore = createApiKeyStore;
49263
51198
  exports2.createAppleProvider = createAppleProvider;
49264
51199
  exports2.createAuthMiddleware = createAuthMiddleware;
49265
51200
  exports2.createAuthRoutes = createAuthRoutes;
@@ -49297,27 +51232,35 @@ export default ${safeId}Collection;
49297
51232
  exports2.getWelcomeEmailTemplate = getWelcomeEmailTemplate;
49298
51233
  exports2.hashPassword = hashPassword;
49299
51234
  exports2.hashRefreshToken = hashRefreshToken;
51235
+ exports2.httpMethodToOperation = httpMethodToOperation;
49300
51236
  exports2.initializeRebaseBackend = initializeRebaseBackend;
51237
+ exports2.isApiKeyToken = isApiKeyToken;
49301
51238
  exports2.isAuthAdapter = isAuthAdapter;
49302
51239
  exports2.isDatabaseAdapter = isDatabaseAdapter;
51240
+ exports2.isOperationAllowed = isOperationAllowed;
51241
+ exports2.isTransformableImage = isTransformableImage;
49303
51242
  exports2.listenWithPortRetry = listenWithPortRetry;
49304
51243
  exports2.loadCronJobsFromDirectory = loadCronJobsFromDirectory;
49305
51244
  exports2.loadEnv = loadEnv;
49306
51245
  exports2.loadFunctionsFromDirectory = loadFunctionsFromDirectory;
49307
51246
  exports2.logger = logger;
49308
51247
  exports2.optionalAuth = optionalAuth;
51248
+ exports2.parseTransformOptions = parseTransformOptions;
49309
51249
  exports2.rebase = rebase;
49310
51250
  exports2.requestLogger = requestLogger;
49311
51251
  exports2.requireAdmin = requireAdmin;
49312
51252
  exports2.requireAuth = requireAuth;
49313
51253
  exports2.resetConsole = resetConsole;
49314
- exports2.resolveAuthOverrides = resolveAuthOverrides;
51254
+ exports2.resolveAuthHooks = resolveAuthHooks;
49315
51255
  exports2.serveSPA = serveSPA;
49316
51256
  exports2.strictAuthLimiter = strictAuthLimiter;
51257
+ exports2.transformImage = transformImage;
51258
+ exports2.validateApiKey = validateApiKey;
49317
51259
  exports2.validateCronExpression = validateCronExpression;
49318
51260
  exports2.validatePasswordStrength = validatePasswordStrength;
49319
51261
  exports2.verifyAccessToken = verifyAccessToken;
49320
51262
  exports2.verifyPassword = verifyPassword;
51263
+ exports2.z = z;
49321
51264
  Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
49322
51265
  });
49323
51266
  //# sourceMappingURL=index.umd.js.map