@rebasepro/server-core 0.2.1 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/dist/common/src/collections/default-collections.d.ts +12 -0
  2. package/dist/common/src/collections/index.d.ts +1 -0
  3. package/dist/common/src/data/query_builder.d.ts +51 -0
  4. package/dist/common/src/index.d.ts +1 -0
  5. package/dist/common/src/util/permissions.d.ts +1 -0
  6. package/dist/{index-BZoAtuqi.js → index-Cr1D21av.js} +11 -3
  7. package/dist/index-Cr1D21av.js.map +1 -0
  8. package/dist/index.es.js +2340 -370
  9. package/dist/index.es.js.map +1 -1
  10. package/dist/index.umd.js +2329 -355
  11. package/dist/index.umd.js.map +1 -1
  12. package/dist/server-core/src/api/logs-routes.d.ts +37 -0
  13. package/dist/server-core/src/api/rest/api-generator.d.ts +6 -0
  14. package/dist/server-core/src/api/types.d.ts +6 -1
  15. package/dist/server-core/src/auth/adapter-middleware.d.ts +7 -3
  16. package/dist/server-core/src/auth/admin-routes.d.ts +3 -3
  17. package/dist/server-core/src/auth/api-keys/api-key-middleware.d.ts +39 -0
  18. package/dist/server-core/src/auth/api-keys/api-key-permission-guard.d.ts +32 -0
  19. package/dist/server-core/src/auth/api-keys/api-key-routes.d.ts +20 -0
  20. package/dist/server-core/src/auth/api-keys/api-key-store.d.ts +35 -0
  21. package/dist/server-core/src/auth/api-keys/api-key-types.d.ts +88 -0
  22. package/dist/server-core/src/auth/api-keys/index.d.ts +17 -0
  23. package/dist/server-core/src/auth/{auth-overrides.d.ts → auth-hooks.d.ts} +69 -11
  24. package/dist/server-core/src/auth/builtin-auth-adapter.d.ts +3 -3
  25. package/dist/server-core/src/auth/index.d.ts +5 -3
  26. package/dist/server-core/src/auth/interfaces.d.ts +93 -3
  27. package/dist/server-core/src/auth/jwt.d.ts +3 -1
  28. package/dist/server-core/src/auth/mfa.d.ts +49 -0
  29. package/dist/server-core/src/auth/middleware.d.ts +7 -0
  30. package/dist/server-core/src/auth/rate-limiter.d.ts +19 -0
  31. package/dist/server-core/src/auth/routes.d.ts +3 -3
  32. package/dist/server-core/src/env.d.ts +6 -0
  33. package/dist/server-core/src/index.d.ts +1 -0
  34. package/dist/server-core/src/init.d.ts +3 -3
  35. package/dist/server-core/src/services/webhook-service.d.ts +29 -0
  36. package/dist/server-core/src/storage/image-transform.d.ts +48 -0
  37. package/dist/server-core/src/storage/index.d.ts +3 -0
  38. package/dist/server-core/src/storage/tus-handler.d.ts +51 -0
  39. package/dist/types/src/controllers/auth.d.ts +2 -24
  40. package/dist/types/src/controllers/client.d.ts +0 -3
  41. package/dist/types/src/controllers/collection_registry.d.ts +1 -1
  42. package/dist/types/src/controllers/data.d.ts +21 -0
  43. package/dist/types/src/controllers/data_driver.d.ts +18 -0
  44. package/dist/types/src/controllers/registry.d.ts +5 -4
  45. package/dist/types/src/rebase_context.d.ts +1 -1
  46. package/dist/types/src/types/auth_adapter.d.ts +2 -4
  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 +2 -2
  54. package/dist/types/src/types/translations.d.ts +28 -12
  55. package/dist/types/src/types/user_management_delegate.d.ts +6 -4
  56. package/dist/types/src/users/roles.d.ts +0 -8
  57. package/jest.config.cjs +4 -1
  58. package/package.json +9 -7
  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 +39 -14
  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 +31 -19
  75. package/src/auth/index.ts +7 -3
  76. package/src/auth/interfaces.ts +111 -3
  77. package/src/auth/jwt.ts +19 -5
  78. package/src/auth/mfa.ts +160 -0
  79. package/src/auth/middleware.ts +20 -1
  80. package/src/auth/rate-limiter.ts +92 -0
  81. package/src/auth/routes.ts +455 -24
  82. package/src/cron/cron-loader.ts +5 -10
  83. package/src/cron/cron-scheduler.ts +11 -12
  84. package/src/cron/cron-store.ts +8 -7
  85. package/src/env.ts +2 -0
  86. package/src/functions/function-loader.ts +6 -9
  87. package/src/index.ts +1 -2
  88. package/src/init.ts +37 -7
  89. package/src/serve-spa.ts +5 -4
  90. package/src/services/webhook-service.ts +155 -0
  91. package/src/storage/image-transform.ts +202 -0
  92. package/src/storage/index.ts +3 -0
  93. package/src/storage/routes.ts +56 -3
  94. package/src/storage/tus-handler.ts +315 -0
  95. package/src/utils/dev-port.ts +14 -0
  96. package/src/utils/logging.ts +9 -7
  97. package/test/admin-routes.test.ts +74 -7
  98. package/test/api-generator.test.ts +0 -1
  99. package/test/api-key-permission-guard.test.ts +132 -0
  100. package/test/ast-schema-editor.test.ts +26 -0
  101. package/test/auth-routes.test.ts +1 -2
  102. package/test/backend-hooks-admin.test.ts +3 -4
  103. package/test/email-templates.test.ts +169 -0
  104. package/test/function-loader.test.ts +124 -0
  105. package/test/jwt.test.ts +4 -2
  106. package/test/mfa.test.ts +197 -0
  107. package/test/middleware.test.ts +10 -5
  108. package/test/webhook-service.test.ts +249 -0
  109. package/vite.config.ts +3 -2
  110. package/dist/index-BZoAtuqi.js.map +0 -1
  111. package/dist/server-core/src/bootstrappers/index.d.ts +0 -0
  112. package/src/bootstrappers/index.ts +0 -1
  113. 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];
@@ -1156,42 +1135,6 @@
1156
1135
  });
1157
1136
  }
1158
1137
  }
1159
- function getSubcollections(collection) {
1160
- if (collection.childCollections) {
1161
- return collection.childCollections() ?? [];
1162
- }
1163
- if (getDataSourceCapabilities(collection.driver).supportsSubcollections && collection.subcollections) {
1164
- return collection.subcollections() ?? [];
1165
- }
1166
- if (getDataSourceCapabilities(collection.driver).supportsRelations && collection.relations) {
1167
- const manyRelations = collection.relations.filter((r) => r.cardinality === "many");
1168
- return manyRelations.map((r) => {
1169
- const target = r.target();
1170
- if (!target) return void 0;
1171
- const relationKey = r.relationName || target.slug;
1172
- let customName;
1173
- if (collection.properties) {
1174
- const prop = Object.entries(collection.properties).find(([_, p]) => p.type === "relation" && p.relationName === relationKey);
1175
- if (prop && prop[1].name) {
1176
- customName = prop[1].name;
1177
- }
1178
- }
1179
- const baseOverrides = {
1180
- slug: relationKey
1181
- };
1182
- if (customName) {
1183
- baseOverrides.name = customName;
1184
- baseOverrides.singularName = customName;
1185
- }
1186
- const targetWithOverrides = {
1187
- ...target,
1188
- ...baseOverrides
1189
- };
1190
- return r.overrides ? mergeDeep(targetWithOverrides, r.overrides) : targetWithOverrides;
1191
- }).filter((c) => Boolean(c));
1192
- }
1193
- return [];
1194
- }
1195
1138
  function sanitizeRelation(relation, sourceCollection, resolveCollection) {
1196
1139
  if (!relation.target) {
1197
1140
  throw new Error("Relation is missing a `target` collection.");
@@ -1223,6 +1166,8 @@
1223
1166
  } else {
1224
1167
  targetCollection = evaluated;
1225
1168
  }
1169
+ } else if (rawTarget && typeof rawTarget === "object") {
1170
+ targetCollection = rawTarget;
1226
1171
  }
1227
1172
  if (!targetCollection) {
1228
1173
  throw new Error("Relation is missing a valid `target` collection.");
@@ -1279,8 +1224,8 @@
1279
1224
  } catch (e) {
1280
1225
  }
1281
1226
  if (!foundForeignKey) {
1282
- const keyPrefix = newRelation.inverseRelationName ? toSnakeCase(newRelation.inverseRelationName) : sourceName;
1283
- newRelation.foreignKeyOnTarget = generateForeignKeyName(keyPrefix);
1227
+ const keyPrefix2 = newRelation.inverseRelationName ? toSnakeCase(newRelation.inverseRelationName) : sourceName;
1228
+ newRelation.foreignKeyOnTarget = generateForeignKeyName(keyPrefix2);
1284
1229
  }
1285
1230
  }
1286
1231
  } else if (newRelation.cardinality === "many" && newRelation.direction === "inverse") {
@@ -1342,11 +1287,14 @@
1342
1287
  const registeredRelationNames = /* @__PURE__ */ new Set();
1343
1288
  if (relCollection.relations) {
1344
1289
  relCollection.relations.forEach((relation) => {
1345
- const normalizedRelation = sanitizeRelation(relation, collection);
1346
- const relationKey = normalizedRelation.relationName;
1347
- if (relationKey) {
1348
- relations[relationKey] = normalizedRelation;
1349
- registeredRelationNames.add(relationKey);
1290
+ try {
1291
+ const normalizedRelation = sanitizeRelation(relation, collection);
1292
+ const relationKey = normalizedRelation.relationName;
1293
+ if (relationKey) {
1294
+ relations[relationKey] = normalizedRelation;
1295
+ registeredRelationNames.add(relationKey);
1296
+ }
1297
+ } catch (e) {
1350
1298
  }
1351
1299
  });
1352
1300
  }
@@ -1394,12 +1342,8 @@
1394
1342
  overrides: relProp.overrides
1395
1343
  };
1396
1344
  }
1397
- const relation = (sourceCollection.relations ?? []).find((rel) => rel.relationName === relProp.relationName);
1398
- if (!relation) {
1399
- console.warn(`Unrecognized relation format for property '${propertyKey}' in collection '${sourceCollection.slug}'`);
1400
- return void 0;
1401
- }
1402
- return relation;
1345
+ console.warn(`Unrecognized or missing relation target for property '${propertyKey}' in collection '${sourceCollection.slug}'`);
1346
+ return void 0;
1403
1347
  }
1404
1348
  function getTableName(collection) {
1405
1349
  if (getDataSourceCapabilities(collection.driver).supportsRelations) {
@@ -1415,6 +1359,43 @@
1415
1359
  if (snakeKey !== key && resolvedRelations[snakeKey]) return resolvedRelations[snakeKey];
1416
1360
  return void 0;
1417
1361
  }
1362
+ function getSubcollections(collection) {
1363
+ if (collection.childCollections) {
1364
+ return collection.childCollections() ?? [];
1365
+ }
1366
+ if (getDataSourceCapabilities(collection.driver).supportsSubcollections && collection.subcollections) {
1367
+ return collection.subcollections() ?? [];
1368
+ }
1369
+ if (getDataSourceCapabilities(collection.driver).supportsRelations) {
1370
+ const resolvedRelations = resolveCollectionRelations(collection);
1371
+ const manyRelations = Object.values(resolvedRelations).filter((r) => r.cardinality === "many");
1372
+ return manyRelations.map((r) => {
1373
+ const target = r.target();
1374
+ if (!target) return void 0;
1375
+ const relationKey = r.relationName || target.slug;
1376
+ let customName;
1377
+ if (collection.properties) {
1378
+ const prop = Object.entries(collection.properties).find(([_, p]) => p.type === "relation" && p.relationName === relationKey);
1379
+ if (prop && prop[1].name) {
1380
+ customName = prop[1].name;
1381
+ }
1382
+ }
1383
+ const baseOverrides = {
1384
+ slug: relationKey
1385
+ };
1386
+ if (customName) {
1387
+ baseOverrides.name = customName;
1388
+ baseOverrides.singularName = customName;
1389
+ }
1390
+ const targetWithOverrides = {
1391
+ ...target,
1392
+ ...baseOverrides
1393
+ };
1394
+ return r.overrides ? mergeDeep(targetWithOverrides, r.overrides) : targetWithOverrides;
1395
+ }).filter((c) => Boolean(c));
1396
+ }
1397
+ return [];
1398
+ }
1418
1399
  var logic = { exports: {} };
1419
1400
  (function(module2, exports3) {
1420
1401
  (function(root, factory) {
@@ -2322,8 +2303,18 @@
2322
2303
  const mergedRelationsRaw = [...extractedRelations];
2323
2304
  for (const manual of manualRelations) {
2324
2305
  const name2 = manual.relationName;
2325
- if (!name2 || !mergedRelationsRaw.find((r) => r.relationName === name2)) {
2306
+ if (!name2) {
2326
2307
  mergedRelationsRaw.push(manual);
2308
+ } else {
2309
+ const existingIndex = mergedRelationsRaw.findIndex((r) => r.relationName === name2);
2310
+ if (existingIndex === -1) {
2311
+ mergedRelationsRaw.push(manual);
2312
+ } else {
2313
+ mergedRelationsRaw[existingIndex] = {
2314
+ ...manual,
2315
+ ...mergedRelationsRaw[existingIndex]
2316
+ };
2317
+ }
2327
2318
  }
2328
2319
  }
2329
2320
  let mergedRelations = mergedRelationsRaw;
@@ -2543,6 +2534,118 @@
2543
2534
  };
2544
2535
  }
2545
2536
  }
2537
+ function mapOperator$1(op) {
2538
+ switch (op) {
2539
+ case "==":
2540
+ return "eq";
2541
+ case "!=":
2542
+ return "neq";
2543
+ case ">":
2544
+ return "gt";
2545
+ case ">=":
2546
+ return "gte";
2547
+ case "<":
2548
+ return "lt";
2549
+ case "<=":
2550
+ return "lte";
2551
+ case "array-contains":
2552
+ return "cs";
2553
+ case "array-contains-any":
2554
+ return "csa";
2555
+ case "not-in":
2556
+ return "nin";
2557
+ default:
2558
+ return op;
2559
+ }
2560
+ }
2561
+ class QueryBuilder {
2562
+ constructor(collection) {
2563
+ this.collection = collection;
2564
+ }
2565
+ params = {
2566
+ where: {}
2567
+ };
2568
+ /**
2569
+ * Add a filter condition to your query.
2570
+ * @example
2571
+ * client.collection('users').where('age', '>=', 18).find()
2572
+ */
2573
+ where(column, operator, value) {
2574
+ if (!this.params.where) {
2575
+ this.params.where = {};
2576
+ }
2577
+ const mappedOp = mapOperator$1(operator);
2578
+ let formattedValue = value;
2579
+ if (Array.isArray(value) && ["in", "nin", "cs", "csa"].includes(mappedOp)) {
2580
+ formattedValue = `(${value.join(",")})`;
2581
+ } else if (value === null) {
2582
+ formattedValue = "null";
2583
+ }
2584
+ this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
2585
+ return this;
2586
+ }
2587
+ /**
2588
+ * Order the results by a specific column.
2589
+ * @example
2590
+ * client.collection('users').orderBy('createdAt', 'desc').find()
2591
+ */
2592
+ orderBy(column, ascending = "asc") {
2593
+ this.params.orderBy = `${column}:${ascending}`;
2594
+ return this;
2595
+ }
2596
+ /**
2597
+ * Limit the number of results returned.
2598
+ */
2599
+ limit(count) {
2600
+ this.params.limit = count;
2601
+ return this;
2602
+ }
2603
+ /**
2604
+ * Skip the first N results.
2605
+ */
2606
+ offset(count) {
2607
+ this.params.offset = count;
2608
+ return this;
2609
+ }
2610
+ /**
2611
+ * Set a free-text search string if supported by the backend.
2612
+ */
2613
+ search(searchString) {
2614
+ this.params.searchString = searchString;
2615
+ return this;
2616
+ }
2617
+ /**
2618
+ * Include related entities in the response.
2619
+ * Relations will be populated with full entity data instead of just IDs.
2620
+ *
2621
+ * @param relations - Relation names to include, or "*" for all.
2622
+ * @example
2623
+ * // Include specific relations
2624
+ * client.data.posts.include("tags", "author").find()
2625
+ *
2626
+ * // Include all relations
2627
+ * client.data.posts.include("*").find()
2628
+ */
2629
+ include(...relations) {
2630
+ this.params.include = relations;
2631
+ return this;
2632
+ }
2633
+ /**
2634
+ * Execute the find query and return the results.
2635
+ */
2636
+ async find() {
2637
+ return this.collection.find(this.params);
2638
+ }
2639
+ /**
2640
+ * Listen to realtime updates matching this query.
2641
+ */
2642
+ listen(onUpdate, onError) {
2643
+ if (!this.collection.listen) {
2644
+ throw new Error("Listen is only available when RebaseClient is configured with a websocketUrl.");
2645
+ }
2646
+ return this.collection.listen(this.params, onUpdate, onError);
2647
+ }
2648
+ }
2546
2649
  class BackendCollectionRegistry extends CollectionRegistry {
2547
2650
  /**
2548
2651
  * Get the available relation keys for a given collection path.
@@ -2722,10 +2825,15 @@
2722
2825
  const issue = error2.code === "42703" ? "column" : "table";
2723
2826
  logMessage = `Database schema mismatch (${issue} missing): ${error2.message}. Did you forget to run migrations ('pnpm db:push' or 'pnpm db:migrate')?`;
2724
2827
  }
2725
- console.error(`❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
2726
2828
  const causePg = error2.cause && typeof error2.cause === "object" ? error2.cause : void 0;
2727
2829
  const pgErrorCode = causePg?.code || error2.code;
2728
- const suppressStack = pgErrorCode === "42703" || pgErrorCode === "42P01" || statusCode < 500 && code2 === "BAD_REQUEST";
2830
+ const isDbSchemaMismatch = pgErrorCode === "42703" || pgErrorCode === "42P01";
2831
+ if (isDbSchemaMismatch) {
2832
+ console.warn(`⚠️ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
2833
+ } else {
2834
+ console.error(`❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
2835
+ }
2836
+ const suppressStack = isDbSchemaMismatch || statusCode < 500 && code2 === "BAD_REQUEST";
2729
2837
  if (!suppressStack) {
2730
2838
  console.error(error2.stack || error2);
2731
2839
  }
@@ -2770,7 +2878,7 @@
2770
2878
  };
2771
2879
  return map2[code2];
2772
2880
  }
2773
- function mapOperator$1(op) {
2881
+ function mapOperator(op) {
2774
2882
  switch (op) {
2775
2883
  case "eq":
2776
2884
  return "==";
@@ -2806,7 +2914,7 @@
2806
2914
  options2.offset = (page - 1) * limit;
2807
2915
  }
2808
2916
  options2.where = {};
2809
- const reservedQueryKeys = ["limit", "offset", "page", "orderBy", "include", "fields", "searchString"];
2917
+ const reservedQueryKeys = ["limit", "offset", "page", "orderBy", "include", "fields", "searchString", "vector_search", "vector", "vector_distance", "vector_threshold"];
2810
2918
  for (const [key, rawValue] of Object.entries(query)) {
2811
2919
  if (reservedQueryKeys.includes(key)) continue;
2812
2920
  const value = Array.isArray(rawValue) ? rawValue[rawValue.length - 1] : rawValue;
@@ -2815,7 +2923,7 @@
2815
2923
  if (parts.length >= 2) {
2816
2924
  const op = parts[0];
2817
2925
  const val = parts.slice(1).join(".");
2818
- const rebaseOp = mapOperator$1(op);
2926
+ const rebaseOp = mapOperator(op);
2819
2927
  if (rebaseOp) {
2820
2928
  let parsedVal = val;
2821
2929
  if (val === "true") parsedVal = true;
@@ -2878,8 +2986,63 @@
2878
2986
  const fieldsStr = String(query.fields).trim();
2879
2987
  options2.fields = fieldsStr.split(",").map((s2) => s2.trim()).filter(Boolean);
2880
2988
  }
2989
+ if (query.vector_search && query.vector) {
2990
+ const vectorStr = String(query.vector);
2991
+ let queryVector;
2992
+ try {
2993
+ queryVector = JSON.parse(vectorStr);
2994
+ if (!Array.isArray(queryVector) || !queryVector.every((v) => typeof v === "number")) {
2995
+ throw new Error("Expected array of numbers");
2996
+ }
2997
+ } catch {
2998
+ throw new Error("Invalid vector format. Expected JSON array of numbers, e.g. [0.1,0.2,0.3]");
2999
+ }
3000
+ const distanceParam = query.vector_distance ? String(query.vector_distance) : "cosine";
3001
+ if (distanceParam !== "cosine" && distanceParam !== "l2" && distanceParam !== "inner_product") {
3002
+ throw new Error(`Invalid vector_distance: ${distanceParam}. Expected: cosine, l2, or inner_product`);
3003
+ }
3004
+ const vectorSearch = {
3005
+ property: String(query.vector_search),
3006
+ vector: queryVector,
3007
+ distance: distanceParam
3008
+ };
3009
+ if (query.vector_threshold) {
3010
+ const threshold = parseFloat(String(query.vector_threshold));
3011
+ if (isNaN(threshold)) {
3012
+ throw new Error("Invalid vector_threshold. Expected a number.");
3013
+ }
3014
+ vectorSearch.threshold = threshold;
3015
+ }
3016
+ options2.vectorSearch = vectorSearch;
3017
+ }
2881
3018
  return options2;
2882
3019
  }
3020
+ function httpMethodToOperation(method) {
3021
+ const upper = method.toUpperCase();
3022
+ switch (upper) {
3023
+ case "GET":
3024
+ case "HEAD":
3025
+ case "OPTIONS":
3026
+ return "read";
3027
+ case "POST":
3028
+ case "PUT":
3029
+ case "PATCH":
3030
+ return "write";
3031
+ case "DELETE":
3032
+ return "delete";
3033
+ default:
3034
+ return "read";
3035
+ }
3036
+ }
3037
+ function isOperationAllowed(permissions, collection, operation) {
3038
+ for (const perm of permissions) {
3039
+ const collectionMatch = perm.collection === "*" || perm.collection === collection;
3040
+ if (collectionMatch && perm.operations.includes(operation)) {
3041
+ return true;
3042
+ }
3043
+ }
3044
+ return false;
3045
+ }
2883
3046
  class RestApiGenerator {
2884
3047
  collections;
2885
3048
  router;
@@ -2912,6 +3075,19 @@
2912
3075
  this.createSubcollectionRoutes();
2913
3076
  return this.router;
2914
3077
  }
3078
+ /**
3079
+ * Check API key permissions for a collection operation.
3080
+ * Throws 403 if the key doesn't have the required permission.
3081
+ * No-ops if the request is not authenticated via an API key.
3082
+ */
3083
+ enforceApiKeyPermission(c, collectionSlug) {
3084
+ const apiKey = c.get("apiKey");
3085
+ if (!apiKey) return;
3086
+ const operation = httpMethodToOperation(c.req.method);
3087
+ if (!isOperationAllowed(apiKey.permissions, collectionSlug, operation)) {
3088
+ throw ApiError.forbidden(`API key does not have "${operation}" permission for collection "${collectionSlug}"`, "API_KEY_FORBIDDEN");
3089
+ }
3090
+ }
2915
3091
  /**
2916
3092
  * Get the typed RestFetchService from a driver if it exposes one (for include support).
2917
3093
  */
@@ -2925,6 +3101,7 @@
2925
3101
  const basePath = `/${collection.slug}`;
2926
3102
  const resolvedCollection = collection;
2927
3103
  this.router.get(`${basePath}/count`, async (c) => {
3104
+ this.enforceApiKeyPermission(c, collection.slug);
2928
3105
  const queryDict = c.req.query();
2929
3106
  const queryOptions = parseQueryOptions(queryDict);
2930
3107
  const searchString = queryDict.searchString;
@@ -2935,6 +3112,7 @@
2935
3112
  });
2936
3113
  });
2937
3114
  this.router.get(basePath, async (c) => {
3115
+ this.enforceApiKeyPermission(c, collection.slug);
2938
3116
  const queryDict = c.req.query();
2939
3117
  const queryOptions = parseQueryOptions(queryDict);
2940
3118
  const searchString = queryDict.searchString;
@@ -2949,7 +3127,8 @@
2949
3127
  offset: queryOptions.offset,
2950
3128
  orderBy: queryOptions.orderBy?.[0]?.field,
2951
3129
  order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
2952
- searchString
3130
+ searchString,
3131
+ vectorSearch: queryOptions.vectorSearch
2953
3132
  }, queryOptions.include);
2954
3133
  entities2 = await this.applyAfterReadBatch(collection.slug, entities2, hookCtx);
2955
3134
  const total2 = await this.countRawEntities(driver, resolvedCollection, queryOptions, searchString);
@@ -2977,6 +3156,7 @@
2977
3156
  });
2978
3157
  });
2979
3158
  this.router.get(`${basePath}/:id`, async (c) => {
3159
+ this.enforceApiKeyPermission(c, collection.slug);
2980
3160
  const id = c.req.param("id");
2981
3161
  const queryDict = c.req.query();
2982
3162
  const queryOptions = parseQueryOptions(queryDict);
@@ -3007,6 +3187,7 @@
3007
3187
  });
3008
3188
  this.router.post(basePath, async (c) => {
3009
3189
  try {
3190
+ this.enforceApiKeyPermission(c, collection.slug);
3010
3191
  const driver = c.get("driver") || this.driver;
3011
3192
  const path2 = collection.slug;
3012
3193
  const hookCtx = this.buildHookContext(c, "POST");
@@ -3035,6 +3216,7 @@
3035
3216
  });
3036
3217
  this.router.put(`${basePath}/:id`, async (c) => {
3037
3218
  try {
3219
+ this.enforceApiKeyPermission(c, collection.slug);
3038
3220
  const id = c.req.param("id");
3039
3221
  const driver = c.get("driver") || this.driver;
3040
3222
  const hookCtx = this.buildHookContext(c, "PUT");
@@ -3071,6 +3253,7 @@
3071
3253
  }
3072
3254
  });
3073
3255
  this.router.delete(`${basePath}/:id`, async (c) => {
3256
+ this.enforceApiKeyPermission(c, collection.slug);
3074
3257
  const id = c.req.param("id");
3075
3258
  const driver = c.get("driver") || this.driver;
3076
3259
  const hookCtx = this.buildHookContext(c, "DELETE");
@@ -3142,6 +3325,7 @@
3142
3325
  const parsed = parseSubPath(rawPath);
3143
3326
  if (!parsed) return next();
3144
3327
  const driver = c.get("driver") || this.driver;
3328
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3145
3329
  if (parsed.entityId === "count") {
3146
3330
  const queryDict = c.req.query();
3147
3331
  const queryOptions = parseQueryOptions(queryDict);
@@ -3189,6 +3373,7 @@
3189
3373
  const parsed = parseSubPath(rawPath);
3190
3374
  if (!parsed || parsed.entityId) return next();
3191
3375
  const driver = c.get("driver") || this.driver;
3376
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3192
3377
  const body = await c.req.json().catch(() => ({}));
3193
3378
  const entity = await driver.saveEntity({
3194
3379
  path: parsed.collectionPath,
@@ -3204,6 +3389,7 @@
3204
3389
  const parsed = parseSubPath(rawPath);
3205
3390
  if (!parsed || !parsed.entityId) return next();
3206
3391
  const driver = c.get("driver") || this.driver;
3392
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3207
3393
  const body = await c.req.json().catch(() => ({}));
3208
3394
  const entity = await driver.saveEntity({
3209
3395
  path: parsed.collectionPath,
@@ -3220,6 +3406,7 @@
3220
3406
  const parsed = parseSubPath(rawPath);
3221
3407
  if (!parsed || !parsed.entityId) return next();
3222
3408
  const driver = c.get("driver") || this.driver;
3409
+ this.enforceApiKeyPermission(c, c.req.param("parent"));
3223
3410
  const existingEntity = await driver.fetchEntity({
3224
3411
  path: parsed.collectionPath,
3225
3412
  entityId: parsed.entityId
@@ -3285,7 +3472,8 @@
3285
3472
  orderBy: queryOptions.orderBy?.[0]?.field,
3286
3473
  order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
3287
3474
  startAfter: queryOptions.offset ? String(queryOptions.offset) : void 0,
3288
- searchString
3475
+ searchString,
3476
+ vectorSearch: queryOptions.vectorSearch
3289
3477
  });
3290
3478
  return entities.map((entity) => this.flattenEntity(entity));
3291
3479
  }
@@ -4811,7 +4999,7 @@
4811
4999
  const SemVer$6 = semver$4;
4812
5000
  const parse$6 = parse_1;
4813
5001
  const { safeRe: re, t } = reExports;
4814
- const coerce$1 = (version2, options2) => {
5002
+ const coerce$2 = (version2, options2) => {
4815
5003
  if (version2 instanceof SemVer$6) {
4816
5004
  return version2;
4817
5005
  }
@@ -4846,7 +5034,7 @@
4846
5034
  const build = options2.includePrerelease && match[6] ? `+${match[6]}` : "";
4847
5035
  return parse$6(`${major2}.${minor2}.${patch2}${prerelease2}${build}`, options2);
4848
5036
  };
4849
- var coerce_1 = coerce$1;
5037
+ var coerce_1 = coerce$2;
4850
5038
  const parse$5 = parse_1;
4851
5039
  const constants$1 = constants$2;
4852
5040
  const SemVer$5 = semver$4;
@@ -5132,19 +5320,19 @@
5132
5320
  const replaceCaret = (comp, options2) => {
5133
5321
  debug2("caret", comp, options2);
5134
5322
  const r = options2.loose ? re2[t2.CARETLOOSE] : re2[t2.CARET];
5135
- const z = options2.includePrerelease ? "-0" : "";
5323
+ const z2 = options2.includePrerelease ? "-0" : "";
5136
5324
  return comp.replace(r, (_, M, m2, p, pr) => {
5137
5325
  debug2("caret", comp, _, M, m2, p, pr);
5138
5326
  let ret;
5139
5327
  if (isX(M)) {
5140
5328
  ret = "";
5141
5329
  } else if (isX(m2)) {
5142
- ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`;
5330
+ ret = `>=${M}.0.0${z2} <${+M + 1}.0.0-0`;
5143
5331
  } else if (isX(p)) {
5144
5332
  if (M === "0") {
5145
- ret = `>=${M}.${m2}.0${z} <${M}.${+m2 + 1}.0-0`;
5333
+ ret = `>=${M}.${m2}.0${z2} <${M}.${+m2 + 1}.0-0`;
5146
5334
  } else {
5147
- ret = `>=${M}.${m2}.0${z} <${+M + 1}.0.0-0`;
5335
+ ret = `>=${M}.${m2}.0${z2} <${+M + 1}.0.0-0`;
5148
5336
  }
5149
5337
  } else if (pr) {
5150
5338
  debug2("replaceCaret pr", pr);
@@ -5161,9 +5349,9 @@
5161
5349
  debug2("no pr");
5162
5350
  if (M === "0") {
5163
5351
  if (m2 === "0") {
5164
- ret = `>=${M}.${m2}.${p}${z} <${M}.${m2}.${+p + 1}-0`;
5352
+ ret = `>=${M}.${m2}.${p}${z2} <${M}.${m2}.${+p + 1}-0`;
5165
5353
  } else {
5166
- ret = `>=${M}.${m2}.${p}${z} <${M}.${+m2 + 1}.0-0`;
5354
+ ret = `>=${M}.${m2}.${p}${z2} <${M}.${+m2 + 1}.0-0`;
5167
5355
  }
5168
5356
  } else {
5169
5357
  ret = `>=${M}.${m2}.${p} <${+M + 1}.0.0-0`;
@@ -5820,7 +6008,7 @@
5820
6008
  const gte = gte_1;
5821
6009
  const lte = lte_1;
5822
6010
  const cmp = cmp_1;
5823
- const coerce = coerce_1;
6011
+ const coerce$1 = coerce_1;
5824
6012
  const truncate = truncate_1;
5825
6013
  const Comparator = requireComparator();
5826
6014
  const Range = requireRange();
@@ -5859,7 +6047,7 @@
5859
6047
  gte,
5860
6048
  lte,
5861
6049
  cmp,
5862
- coerce,
6050
+ coerce: coerce$1,
5863
6051
  truncate,
5864
6052
  Comparator,
5865
6053
  Range,
@@ -6740,7 +6928,7 @@
6740
6928
  NotBeforeError: NotBeforeError_1,
6741
6929
  TokenExpiredError: TokenExpiredError_1
6742
6930
  };
6743
- const jwt = /* @__PURE__ */ getDefaultExportFromCjs(jsonwebtoken);
6931
+ const jwt$1 = /* @__PURE__ */ getDefaultExportFromCjs(jsonwebtoken);
6744
6932
  let jwtConfig = {
6745
6933
  secret: "",
6746
6934
  accessExpiresIn: "1h",
@@ -6759,15 +6947,17 @@
6759
6947
  ...config
6760
6948
  };
6761
6949
  }
6762
- function generateAccessToken(userId, roles) {
6950
+ function generateAccessToken(userId, roles, aal = "aal1", customClaims) {
6763
6951
  if (!jwtConfig.secret) {
6764
6952
  throw new Error("JWT secret not configured. Call configureJwt() first.");
6765
6953
  }
6766
6954
  const payload = {
6767
6955
  userId,
6768
- roles
6956
+ roles,
6957
+ aal,
6958
+ ...customClaims
6769
6959
  };
6770
- return jwt.sign(payload, jwtConfig.secret, {
6960
+ return jwt$1.sign(payload, jwtConfig.secret, {
6771
6961
  expiresIn: jwtConfig.accessExpiresIn,
6772
6962
  algorithm: "HS256"
6773
6963
  });
@@ -6801,7 +6991,7 @@
6801
6991
  throw new Error("JWT secret not configured. Call configureJwt() first.");
6802
6992
  }
6803
6993
  try {
6804
- const decoded = jwt.verify(token, jwtConfig.secret, {
6994
+ const decoded = jwt$1.verify(token, jwtConfig.secret, {
6805
6995
  algorithms: ["HS256"]
6806
6996
  });
6807
6997
  const id = decoded.userId || decoded.uid || decoded.sub;
@@ -6809,9 +6999,11 @@
6809
6999
  console.error("[JWT] Verification failed: missing id in payload", decoded);
6810
7000
  return null;
6811
7001
  }
7002
+ const aal = decoded.aal === "aal1" || decoded.aal === "aal2" ? decoded.aal : "aal1";
6812
7003
  return {
6813
7004
  userId: id,
6814
- roles: decoded.roles || []
7005
+ roles: decoded.roles || [],
7006
+ aal
6815
7007
  };
6816
7008
  } catch (error2) {
6817
7009
  console.error("[JWT] Verification failed:", error2, "Token start:", token.substring(0, 15));
@@ -6851,6 +7043,17 @@
6851
7043
  }
6852
7044
  return new Date(Date.now() + ms2);
6853
7045
  }
7046
+ const jwt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
7047
+ __proto__: null,
7048
+ configureJwt,
7049
+ generateAccessToken,
7050
+ generateRefreshToken,
7051
+ getAccessTokenExpiry,
7052
+ getAccessTokenExpiryMs,
7053
+ getRefreshTokenExpiry,
7054
+ hashRefreshToken,
7055
+ verifyAccessToken
7056
+ }, Symbol.toStringTag, { value: "Module" }));
6854
7057
  function isRLSScopedDriver(driver) {
6855
7058
  return "withAuth" in driver && typeof driver.withAuth === "function";
6856
7059
  }
@@ -6873,6 +7076,81 @@
6873
7076
  return false;
6874
7077
  }
6875
7078
  }
7079
+ function isApiKeyToken(token) {
7080
+ return token.startsWith("rk_");
7081
+ }
7082
+ function hashToken$2(token) {
7083
+ return require$$0$5.createHash("sha256").update(token).digest("hex");
7084
+ }
7085
+ async function validateApiKey(c, token, options2) {
7086
+ const {
7087
+ store,
7088
+ driver
7089
+ } = options2;
7090
+ const hash = hashToken$2(token);
7091
+ const apiKey = await store.findByKeyHash(hash);
7092
+ if (!apiKey) {
7093
+ return c.json({
7094
+ error: {
7095
+ message: "Invalid API key",
7096
+ code: "UNAUTHORIZED"
7097
+ }
7098
+ }, 401);
7099
+ }
7100
+ if (apiKey.revoked_at) {
7101
+ return c.json({
7102
+ error: {
7103
+ message: "API key has been revoked",
7104
+ code: "UNAUTHORIZED"
7105
+ }
7106
+ }, 401);
7107
+ }
7108
+ if (apiKey.expires_at && new Date(apiKey.expires_at) < /* @__PURE__ */ new Date()) {
7109
+ return c.json({
7110
+ error: {
7111
+ message: "API key has expired",
7112
+ code: "UNAUTHORIZED"
7113
+ }
7114
+ }, 401);
7115
+ }
7116
+ const userId = `api-key:${apiKey.id}`;
7117
+ c.set("user", {
7118
+ userId,
7119
+ roles: []
7120
+ });
7121
+ const masked = {
7122
+ id: apiKey.id,
7123
+ name: apiKey.name,
7124
+ key_prefix: apiKey.key_prefix,
7125
+ permissions: apiKey.permissions,
7126
+ rate_limit: apiKey.rate_limit,
7127
+ created_by: apiKey.created_by,
7128
+ created_at: apiKey.created_at,
7129
+ updated_at: apiKey.updated_at,
7130
+ last_used_at: apiKey.last_used_at,
7131
+ expires_at: apiKey.expires_at,
7132
+ revoked_at: apiKey.revoked_at
7133
+ };
7134
+ c.set("apiKey", masked);
7135
+ try {
7136
+ const scopedDriver = await scopeDataDriver(driver, {
7137
+ uid: userId,
7138
+ roles: ["service"]
7139
+ });
7140
+ c.set("driver", scopedDriver);
7141
+ } catch (error2) {
7142
+ console.error("[AUTH] RLS scoping failed for API key:", error2);
7143
+ return c.json({
7144
+ error: {
7145
+ message: "Internal authentication error",
7146
+ code: "INTERNAL_ERROR"
7147
+ }
7148
+ }, 500);
7149
+ }
7150
+ store.updateLastUsed(apiKey.id).catch(() => {
7151
+ });
7152
+ return true;
7153
+ }
6876
7154
  const requireAuth = async (c, next) => {
6877
7155
  const authHeader = c.req.header("authorization");
6878
7156
  const queryToken = c.req.query("token");
@@ -6979,7 +7257,8 @@
6979
7257
  driver,
6980
7258
  requireAuth: enforceAuth = true,
6981
7259
  validator,
6982
- serviceKey
7260
+ serviceKey,
7261
+ apiKeyStore
6983
7262
  } = options2;
6984
7263
  return async (c, next) => {
6985
7264
  if (validator) {
@@ -7054,6 +7333,14 @@
7054
7333
  }
7055
7334
  }, 500);
7056
7335
  }
7336
+ } else if (apiKeyStore && isApiKeyToken(token)) {
7337
+ const result = await validateApiKey(c, token, {
7338
+ store: apiKeyStore,
7339
+ driver
7340
+ });
7341
+ if (result !== true) {
7342
+ return result;
7343
+ }
7057
7344
  } else {
7058
7345
  const payload = extractUserFromToken(token);
7059
7346
  if (payload) {
@@ -7114,9 +7401,22 @@
7114
7401
  const {
7115
7402
  adapter,
7116
7403
  driver,
7117
- requireAuth: enforceAuth = true
7404
+ requireAuth: enforceAuth = true,
7405
+ apiKeyStore
7118
7406
  } = options2;
7119
7407
  return async (c, next) => {
7408
+ if (apiKeyStore) {
7409
+ const authHeader = c.req.header("authorization") || "";
7410
+ const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
7411
+ if (token.startsWith("rk_")) {
7412
+ const result = await validateApiKey(c, token, {
7413
+ store: apiKeyStore,
7414
+ driver
7415
+ });
7416
+ if (result === true) return next();
7417
+ return result;
7418
+ }
7419
+ }
7120
7420
  let authenticatedUser = null;
7121
7421
  try {
7122
7422
  authenticatedUser = await adapter.verifyRequest(c.req.raw);
@@ -7212,11 +7512,11 @@
7212
7512
  const derivedKey = await scryptAsync(password, salt, KEY_LENGTH);
7213
7513
  return require$$0$5.timingSafeEqual(derivedKey, storedKey);
7214
7514
  }
7215
- function resolveAuthOverrides(overrides) {
7515
+ function resolveAuthHooks(hooks) {
7216
7516
  return {
7217
- hashPassword: overrides?.hashPassword ?? hashPassword,
7218
- verifyPassword: overrides?.verifyPassword ?? verifyPassword,
7219
- validatePasswordStrength: overrides?.validatePasswordStrength ?? validatePasswordStrength
7517
+ hashPassword: hooks?.hashPassword ?? hashPassword,
7518
+ verifyPassword: hooks?.verifyPassword ?? verifyPassword,
7519
+ validatePasswordStrength: hooks?.validatePasswordStrength ?? validatePasswordStrength
7220
7520
  };
7221
7521
  }
7222
7522
  function getGreeting(user) {
@@ -7618,6 +7918,63 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
7618
7918
  limit: 50,
7619
7919
  message: "Too many requests to this sensitive endpoint, please try again later."
7620
7920
  });
7921
+ function apiKeyKeyGenerator(c) {
7922
+ const apiKey = c.get("apiKey");
7923
+ if (apiKey) {
7924
+ return `api-key:${apiKey.id}`;
7925
+ }
7926
+ return defaultKeyGenerator(c);
7927
+ }
7928
+ function createApiKeyRateLimiter(defaultLimit = 1e3, windowMs = 15 * 60 * 1e3) {
7929
+ const store = /* @__PURE__ */ new Map();
7930
+ const cleanupInterval = setInterval(() => {
7931
+ const now = Date.now();
7932
+ for (const [key, entry] of store.entries()) {
7933
+ entry.timestamps = entry.timestamps.filter((t2) => now - t2 < windowMs);
7934
+ if (entry.timestamps.length === 0) {
7935
+ store.delete(key);
7936
+ }
7937
+ }
7938
+ }, windowMs);
7939
+ if (cleanupInterval.unref) {
7940
+ cleanupInterval.unref();
7941
+ }
7942
+ return async (c, next) => {
7943
+ const apiKey = c.get("apiKey");
7944
+ if (!apiKey) {
7945
+ return next();
7946
+ }
7947
+ const limit = apiKey.rate_limit ?? defaultLimit;
7948
+ const key = `api-key:${apiKey.id}`;
7949
+ const now = Date.now();
7950
+ let entry = store.get(key);
7951
+ if (!entry) {
7952
+ entry = {
7953
+ timestamps: []
7954
+ };
7955
+ store.set(key, entry);
7956
+ }
7957
+ entry.timestamps = entry.timestamps.filter((t2) => now - t2 < windowMs);
7958
+ if (entry.timestamps.length >= limit) {
7959
+ const retryAfterMs = entry.timestamps[0] + windowMs - now;
7960
+ const retryAfterSec = Math.ceil(retryAfterMs / 1e3);
7961
+ c.header("Retry-After", String(retryAfterSec));
7962
+ c.header("X-RateLimit-Limit", String(limit));
7963
+ c.header("X-RateLimit-Remaining", "0");
7964
+ c.header("X-RateLimit-Reset", String(Math.ceil((now + retryAfterMs) / 1e3)));
7965
+ return c.json({
7966
+ error: {
7967
+ message: "API key rate limit exceeded, please try again later.",
7968
+ code: "RATE_LIMITED"
7969
+ }
7970
+ }, 429);
7971
+ }
7972
+ entry.timestamps.push(now);
7973
+ c.header("X-RateLimit-Limit", String(limit));
7974
+ c.header("X-RateLimit-Remaining", String(limit - entry.timestamps.length));
7975
+ return next();
7976
+ };
7977
+ }
7621
7978
  var util$3;
7622
7979
  (function(util2) {
7623
7980
  util2.assertEqual = (_) => {
@@ -7768,6 +8125,10 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
7768
8125
  "not_multiple_of",
7769
8126
  "not_finite"
7770
8127
  ]);
8128
+ const quotelessJson = (obj) => {
8129
+ const json = JSON.stringify(obj, null, 2);
8130
+ return json.replace(/"([^"]+)":/g, "$1:");
8131
+ };
7771
8132
  class ZodError extends Error {
7772
8133
  get errors() {
7773
8134
  return this.issues;
@@ -7963,6 +8324,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
7963
8324
  return { message };
7964
8325
  };
7965
8326
  let overrideErrorMap = errorMap;
8327
+ function setErrorMap(map2) {
8328
+ overrideErrorMap = map2;
8329
+ }
7966
8330
  function getErrorMap() {
7967
8331
  return overrideErrorMap;
7968
8332
  }
@@ -7991,6 +8355,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
7991
8355
  message: errorMessage
7992
8356
  };
7993
8357
  };
8358
+ const EMPTY_PATH = [];
7994
8359
  function addIssueToContext(ctx, issueData) {
7995
8360
  const overrideMap = getErrorMap();
7996
8361
  const issue = makeIssue({
@@ -10281,6 +10646,113 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
10281
10646
  ...processCreateParams(params)
10282
10647
  });
10283
10648
  };
10649
+ const getDiscriminator = (type) => {
10650
+ if (type instanceof ZodLazy) {
10651
+ return getDiscriminator(type.schema);
10652
+ } else if (type instanceof ZodEffects) {
10653
+ return getDiscriminator(type.innerType());
10654
+ } else if (type instanceof ZodLiteral) {
10655
+ return [type.value];
10656
+ } else if (type instanceof ZodEnum) {
10657
+ return type.options;
10658
+ } else if (type instanceof ZodNativeEnum) {
10659
+ return util$3.objectValues(type.enum);
10660
+ } else if (type instanceof ZodDefault) {
10661
+ return getDiscriminator(type._def.innerType);
10662
+ } else if (type instanceof ZodUndefined) {
10663
+ return [void 0];
10664
+ } else if (type instanceof ZodNull) {
10665
+ return [null];
10666
+ } else if (type instanceof ZodOptional) {
10667
+ return [void 0, ...getDiscriminator(type.unwrap())];
10668
+ } else if (type instanceof ZodNullable) {
10669
+ return [null, ...getDiscriminator(type.unwrap())];
10670
+ } else if (type instanceof ZodBranded) {
10671
+ return getDiscriminator(type.unwrap());
10672
+ } else if (type instanceof ZodReadonly) {
10673
+ return getDiscriminator(type.unwrap());
10674
+ } else if (type instanceof ZodCatch) {
10675
+ return getDiscriminator(type._def.innerType);
10676
+ } else {
10677
+ return [];
10678
+ }
10679
+ };
10680
+ class ZodDiscriminatedUnion extends ZodType {
10681
+ _parse(input) {
10682
+ const { ctx } = this._processInputParams(input);
10683
+ if (ctx.parsedType !== ZodParsedType.object) {
10684
+ addIssueToContext(ctx, {
10685
+ code: ZodIssueCode.invalid_type,
10686
+ expected: ZodParsedType.object,
10687
+ received: ctx.parsedType
10688
+ });
10689
+ return INVALID;
10690
+ }
10691
+ const discriminator = this.discriminator;
10692
+ const discriminatorValue = ctx.data[discriminator];
10693
+ const option = this.optionsMap.get(discriminatorValue);
10694
+ if (!option) {
10695
+ addIssueToContext(ctx, {
10696
+ code: ZodIssueCode.invalid_union_discriminator,
10697
+ options: Array.from(this.optionsMap.keys()),
10698
+ path: [discriminator]
10699
+ });
10700
+ return INVALID;
10701
+ }
10702
+ if (ctx.common.async) {
10703
+ return option._parseAsync({
10704
+ data: ctx.data,
10705
+ path: ctx.path,
10706
+ parent: ctx
10707
+ });
10708
+ } else {
10709
+ return option._parseSync({
10710
+ data: ctx.data,
10711
+ path: ctx.path,
10712
+ parent: ctx
10713
+ });
10714
+ }
10715
+ }
10716
+ get discriminator() {
10717
+ return this._def.discriminator;
10718
+ }
10719
+ get options() {
10720
+ return this._def.options;
10721
+ }
10722
+ get optionsMap() {
10723
+ return this._def.optionsMap;
10724
+ }
10725
+ /**
10726
+ * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor.
10727
+ * However, it only allows a union of objects, all of which need to share a discriminator property. This property must
10728
+ * have a different value for each object in the union.
10729
+ * @param discriminator the name of the discriminator property
10730
+ * @param types an array of object schemas
10731
+ * @param params
10732
+ */
10733
+ static create(discriminator, options2, params) {
10734
+ const optionsMap = /* @__PURE__ */ new Map();
10735
+ for (const type of options2) {
10736
+ const discriminatorValues = getDiscriminator(type.shape[discriminator]);
10737
+ if (!discriminatorValues.length) {
10738
+ throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`);
10739
+ }
10740
+ for (const value of discriminatorValues) {
10741
+ if (optionsMap.has(value)) {
10742
+ throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`);
10743
+ }
10744
+ optionsMap.set(value, type);
10745
+ }
10746
+ }
10747
+ return new ZodDiscriminatedUnion({
10748
+ typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion,
10749
+ discriminator,
10750
+ options: options2,
10751
+ optionsMap,
10752
+ ...processCreateParams(params)
10753
+ });
10754
+ }
10755
+ }
10284
10756
  function mergeValues(a2, b) {
10285
10757
  const aType = getParsedType(a2);
10286
10758
  const bType = getParsedType(b);
@@ -10439,6 +10911,59 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
10439
10911
  ...processCreateParams(params)
10440
10912
  });
10441
10913
  };
10914
+ class ZodRecord extends ZodType {
10915
+ get keySchema() {
10916
+ return this._def.keyType;
10917
+ }
10918
+ get valueSchema() {
10919
+ return this._def.valueType;
10920
+ }
10921
+ _parse(input) {
10922
+ const { status, ctx } = this._processInputParams(input);
10923
+ if (ctx.parsedType !== ZodParsedType.object) {
10924
+ addIssueToContext(ctx, {
10925
+ code: ZodIssueCode.invalid_type,
10926
+ expected: ZodParsedType.object,
10927
+ received: ctx.parsedType
10928
+ });
10929
+ return INVALID;
10930
+ }
10931
+ const pairs = [];
10932
+ const keyType = this._def.keyType;
10933
+ const valueType = this._def.valueType;
10934
+ for (const key in ctx.data) {
10935
+ pairs.push({
10936
+ key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)),
10937
+ value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)),
10938
+ alwaysSet: key in ctx.data
10939
+ });
10940
+ }
10941
+ if (ctx.common.async) {
10942
+ return ParseStatus.mergeObjectAsync(status, pairs);
10943
+ } else {
10944
+ return ParseStatus.mergeObjectSync(status, pairs);
10945
+ }
10946
+ }
10947
+ get element() {
10948
+ return this._def.valueType;
10949
+ }
10950
+ static create(first, second, third) {
10951
+ if (second instanceof ZodType) {
10952
+ return new ZodRecord({
10953
+ keyType: first,
10954
+ valueType: second,
10955
+ typeName: ZodFirstPartyTypeKind.ZodRecord,
10956
+ ...processCreateParams(third)
10957
+ });
10958
+ }
10959
+ return new ZodRecord({
10960
+ keyType: ZodString.create(),
10961
+ valueType: first,
10962
+ typeName: ZodFirstPartyTypeKind.ZodRecord,
10963
+ ...processCreateParams(second)
10964
+ });
10965
+ }
10966
+ }
10442
10967
  class ZodMap extends ZodType {
10443
10968
  get keySchema() {
10444
10969
  return this._def.keyType;
@@ -10590,6 +11115,111 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
10590
11115
  ...processCreateParams(params)
10591
11116
  });
10592
11117
  };
11118
+ class ZodFunction extends ZodType {
11119
+ constructor() {
11120
+ super(...arguments);
11121
+ this.validate = this.implement;
11122
+ }
11123
+ _parse(input) {
11124
+ const { ctx } = this._processInputParams(input);
11125
+ if (ctx.parsedType !== ZodParsedType.function) {
11126
+ addIssueToContext(ctx, {
11127
+ code: ZodIssueCode.invalid_type,
11128
+ expected: ZodParsedType.function,
11129
+ received: ctx.parsedType
11130
+ });
11131
+ return INVALID;
11132
+ }
11133
+ function makeArgsIssue(args, error2) {
11134
+ return makeIssue({
11135
+ data: args,
11136
+ path: ctx.path,
11137
+ errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), errorMap].filter((x) => !!x),
11138
+ issueData: {
11139
+ code: ZodIssueCode.invalid_arguments,
11140
+ argumentsError: error2
11141
+ }
11142
+ });
11143
+ }
11144
+ function makeReturnsIssue(returns, error2) {
11145
+ return makeIssue({
11146
+ data: returns,
11147
+ path: ctx.path,
11148
+ errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), errorMap].filter((x) => !!x),
11149
+ issueData: {
11150
+ code: ZodIssueCode.invalid_return_type,
11151
+ returnTypeError: error2
11152
+ }
11153
+ });
11154
+ }
11155
+ const params = { errorMap: ctx.common.contextualErrorMap };
11156
+ const fn = ctx.data;
11157
+ if (this._def.returns instanceof ZodPromise) {
11158
+ const me = this;
11159
+ return OK(async function(...args) {
11160
+ const error2 = new ZodError([]);
11161
+ const parsedArgs = await me._def.args.parseAsync(args, params).catch((e) => {
11162
+ error2.addIssue(makeArgsIssue(args, e));
11163
+ throw error2;
11164
+ });
11165
+ const result = await Reflect.apply(fn, this, parsedArgs);
11166
+ const parsedReturns = await me._def.returns._def.type.parseAsync(result, params).catch((e) => {
11167
+ error2.addIssue(makeReturnsIssue(result, e));
11168
+ throw error2;
11169
+ });
11170
+ return parsedReturns;
11171
+ });
11172
+ } else {
11173
+ const me = this;
11174
+ return OK(function(...args) {
11175
+ const parsedArgs = me._def.args.safeParse(args, params);
11176
+ if (!parsedArgs.success) {
11177
+ throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
11178
+ }
11179
+ const result = Reflect.apply(fn, this, parsedArgs.data);
11180
+ const parsedReturns = me._def.returns.safeParse(result, params);
11181
+ if (!parsedReturns.success) {
11182
+ throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
11183
+ }
11184
+ return parsedReturns.data;
11185
+ });
11186
+ }
11187
+ }
11188
+ parameters() {
11189
+ return this._def.args;
11190
+ }
11191
+ returnType() {
11192
+ return this._def.returns;
11193
+ }
11194
+ args(...items) {
11195
+ return new ZodFunction({
11196
+ ...this._def,
11197
+ args: ZodTuple.create(items).rest(ZodUnknown.create())
11198
+ });
11199
+ }
11200
+ returns(returnType) {
11201
+ return new ZodFunction({
11202
+ ...this._def,
11203
+ returns: returnType
11204
+ });
11205
+ }
11206
+ implement(func) {
11207
+ const validatedFunc = this.parse(func);
11208
+ return validatedFunc;
11209
+ }
11210
+ strictImplement(func) {
11211
+ const validatedFunc = this.parse(func);
11212
+ return validatedFunc;
11213
+ }
11214
+ static create(args, returns, params) {
11215
+ return new ZodFunction({
11216
+ args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()),
11217
+ returns: returns || ZodUnknown.create(),
11218
+ typeName: ZodFirstPartyTypeKind.ZodFunction,
11219
+ ...processCreateParams(params)
11220
+ });
11221
+ }
11222
+ }
10593
11223
  class ZodLazy extends ZodType {
10594
11224
  get schema() {
10595
11225
  return this._def.getter();
@@ -11047,6 +11677,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11047
11677
  ...processCreateParams(params)
11048
11678
  });
11049
11679
  };
11680
+ const BRAND = Symbol("zod_brand");
11050
11681
  class ZodBranded extends ZodType {
11051
11682
  _parse(input) {
11052
11683
  const { ctx } = this._processInputParams(input);
@@ -11138,6 +11769,36 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11138
11769
  ...processCreateParams(params)
11139
11770
  });
11140
11771
  };
11772
+ function cleanParams(params, data) {
11773
+ const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
11774
+ const p2 = typeof p === "string" ? { message: p } : p;
11775
+ return p2;
11776
+ }
11777
+ function custom(check, _params = {}, fatal) {
11778
+ if (check)
11779
+ return ZodAny.create().superRefine((data, ctx) => {
11780
+ const r = check(data);
11781
+ if (r instanceof Promise) {
11782
+ return r.then((r2) => {
11783
+ if (!r2) {
11784
+ const params = cleanParams(_params, data);
11785
+ const _fatal = params.fatal ?? fatal ?? true;
11786
+ ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
11787
+ }
11788
+ });
11789
+ }
11790
+ if (!r) {
11791
+ const params = cleanParams(_params, data);
11792
+ const _fatal = params.fatal ?? fatal ?? true;
11793
+ ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
11794
+ }
11795
+ return;
11796
+ });
11797
+ return ZodAny.create();
11798
+ }
11799
+ const late = {
11800
+ object: ZodObject.lazycreate
11801
+ };
11141
11802
  var ZodFirstPartyTypeKind;
11142
11803
  (function(ZodFirstPartyTypeKind2) {
11143
11804
  ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
@@ -11177,17 +11838,251 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11177
11838
  ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
11178
11839
  ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
11179
11840
  })(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
11841
+ const instanceOfType = (cls, params = {
11842
+ message: `Input not instance of ${cls.name}`
11843
+ }) => custom((data) => data instanceof cls, params);
11180
11844
  const stringType = ZodString.create;
11181
- ZodNever.create;
11182
- ZodArray.create;
11845
+ const numberType = ZodNumber.create;
11846
+ const nanType = ZodNaN.create;
11847
+ const bigIntType = ZodBigInt.create;
11848
+ const booleanType = ZodBoolean.create;
11849
+ const dateType = ZodDate.create;
11850
+ const symbolType = ZodSymbol.create;
11851
+ const undefinedType = ZodUndefined.create;
11852
+ const nullType = ZodNull.create;
11853
+ const anyType = ZodAny.create;
11854
+ const unknownType = ZodUnknown.create;
11855
+ const neverType = ZodNever.create;
11856
+ const voidType = ZodVoid.create;
11857
+ const arrayType = ZodArray.create;
11183
11858
  const objectType = ZodObject.create;
11184
- ZodUnion.create;
11185
- ZodIntersection.create;
11186
- ZodTuple.create;
11859
+ const strictObjectType = ZodObject.strictCreate;
11860
+ const unionType = ZodUnion.create;
11861
+ const discriminatedUnionType = ZodDiscriminatedUnion.create;
11862
+ const intersectionType = ZodIntersection.create;
11863
+ const tupleType = ZodTuple.create;
11864
+ const recordType = ZodRecord.create;
11865
+ const mapType = ZodMap.create;
11866
+ const setType = ZodSet.create;
11867
+ const functionType = ZodFunction.create;
11868
+ const lazyType = ZodLazy.create;
11869
+ const literalType = ZodLiteral.create;
11187
11870
  const enumType = ZodEnum.create;
11188
- ZodPromise.create;
11189
- ZodOptional.create;
11190
- ZodNullable.create;
11871
+ const nativeEnumType = ZodNativeEnum.create;
11872
+ const promiseType = ZodPromise.create;
11873
+ const effectsType = ZodEffects.create;
11874
+ const optionalType = ZodOptional.create;
11875
+ const nullableType = ZodNullable.create;
11876
+ const preprocessType = ZodEffects.createWithPreprocess;
11877
+ const pipelineType = ZodPipeline.create;
11878
+ const ostring = () => stringType().optional();
11879
+ const onumber = () => numberType().optional();
11880
+ const oboolean = () => booleanType().optional();
11881
+ const coerce = {
11882
+ string: (arg) => ZodString.create({ ...arg, coerce: true }),
11883
+ number: (arg) => ZodNumber.create({ ...arg, coerce: true }),
11884
+ boolean: (arg) => ZodBoolean.create({
11885
+ ...arg,
11886
+ coerce: true
11887
+ }),
11888
+ bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }),
11889
+ date: (arg) => ZodDate.create({ ...arg, coerce: true })
11890
+ };
11891
+ const NEVER = INVALID;
11892
+ const z = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
11893
+ __proto__: null,
11894
+ BRAND,
11895
+ DIRTY,
11896
+ EMPTY_PATH,
11897
+ INVALID,
11898
+ NEVER,
11899
+ OK,
11900
+ ParseStatus,
11901
+ Schema: ZodType,
11902
+ ZodAny,
11903
+ ZodArray,
11904
+ ZodBigInt,
11905
+ ZodBoolean,
11906
+ ZodBranded,
11907
+ ZodCatch,
11908
+ ZodDate,
11909
+ ZodDefault,
11910
+ ZodDiscriminatedUnion,
11911
+ ZodEffects,
11912
+ ZodEnum,
11913
+ ZodError,
11914
+ get ZodFirstPartyTypeKind() {
11915
+ return ZodFirstPartyTypeKind;
11916
+ },
11917
+ ZodFunction,
11918
+ ZodIntersection,
11919
+ ZodIssueCode,
11920
+ ZodLazy,
11921
+ ZodLiteral,
11922
+ ZodMap,
11923
+ ZodNaN,
11924
+ ZodNativeEnum,
11925
+ ZodNever,
11926
+ ZodNull,
11927
+ ZodNullable,
11928
+ ZodNumber,
11929
+ ZodObject,
11930
+ ZodOptional,
11931
+ ZodParsedType,
11932
+ ZodPipeline,
11933
+ ZodPromise,
11934
+ ZodReadonly,
11935
+ ZodRecord,
11936
+ ZodSchema: ZodType,
11937
+ ZodSet,
11938
+ ZodString,
11939
+ ZodSymbol,
11940
+ ZodTransformer: ZodEffects,
11941
+ ZodTuple,
11942
+ ZodType,
11943
+ ZodUndefined,
11944
+ ZodUnion,
11945
+ ZodUnknown,
11946
+ ZodVoid,
11947
+ addIssueToContext,
11948
+ any: anyType,
11949
+ array: arrayType,
11950
+ bigint: bigIntType,
11951
+ boolean: booleanType,
11952
+ coerce,
11953
+ custom,
11954
+ date: dateType,
11955
+ datetimeRegex,
11956
+ defaultErrorMap: errorMap,
11957
+ discriminatedUnion: discriminatedUnionType,
11958
+ effect: effectsType,
11959
+ enum: enumType,
11960
+ function: functionType,
11961
+ getErrorMap,
11962
+ getParsedType,
11963
+ instanceof: instanceOfType,
11964
+ intersection: intersectionType,
11965
+ isAborted,
11966
+ isAsync,
11967
+ isDirty,
11968
+ isValid,
11969
+ late,
11970
+ lazy: lazyType,
11971
+ literal: literalType,
11972
+ makeIssue,
11973
+ map: mapType,
11974
+ nan: nanType,
11975
+ nativeEnum: nativeEnumType,
11976
+ never: neverType,
11977
+ null: nullType,
11978
+ nullable: nullableType,
11979
+ number: numberType,
11980
+ object: objectType,
11981
+ get objectUtil() {
11982
+ return objectUtil;
11983
+ },
11984
+ oboolean,
11985
+ onumber,
11986
+ optional: optionalType,
11987
+ ostring,
11988
+ pipeline: pipelineType,
11989
+ preprocess: preprocessType,
11990
+ promise: promiseType,
11991
+ quotelessJson,
11992
+ record: recordType,
11993
+ set: setType,
11994
+ setErrorMap,
11995
+ strictObject: strictObjectType,
11996
+ string: stringType,
11997
+ symbol: symbolType,
11998
+ transformer: effectsType,
11999
+ tuple: tupleType,
12000
+ undefined: undefinedType,
12001
+ union: unionType,
12002
+ unknown: unknownType,
12003
+ get util() {
12004
+ return util$3;
12005
+ },
12006
+ void: voidType
12007
+ }, Symbol.toStringTag, { value: "Module" }));
12008
+ const BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
12009
+ function base32Encode(buffer) {
12010
+ let bits = 0;
12011
+ let value = 0;
12012
+ let output = "";
12013
+ for (let i2 = 0; i2 < buffer.length; i2++) {
12014
+ value = value << 8 | buffer[i2];
12015
+ bits += 8;
12016
+ while (bits >= 5) {
12017
+ output += BASE32_ALPHABET[value >>> bits - 5 & 31];
12018
+ bits -= 5;
12019
+ }
12020
+ }
12021
+ if (bits > 0) {
12022
+ output += BASE32_ALPHABET[value << 5 - bits & 31];
12023
+ }
12024
+ return output;
12025
+ }
12026
+ function base32Decode(encoded) {
12027
+ const cleanInput = encoded.replace(/=+$/, "").toUpperCase();
12028
+ const bytes = [];
12029
+ let bits = 0;
12030
+ let value = 0;
12031
+ for (let i2 = 0; i2 < cleanInput.length; i2++) {
12032
+ const index2 = BASE32_ALPHABET.indexOf(cleanInput[i2]);
12033
+ if (index2 === -1) continue;
12034
+ value = value << 5 | index2;
12035
+ bits += 5;
12036
+ if (bits >= 8) {
12037
+ bytes.push(value >>> bits - 8 & 255);
12038
+ bits -= 8;
12039
+ }
12040
+ }
12041
+ return Buffer.from(bytes);
12042
+ }
12043
+ function generateHotp(secret, counter) {
12044
+ const hmac = require$$0$5.createHmac("sha1", secret);
12045
+ const counterBuffer = Buffer.alloc(8);
12046
+ counterBuffer.writeBigInt64BE(counter);
12047
+ hmac.update(counterBuffer);
12048
+ const hash = hmac.digest();
12049
+ const offset = hash[hash.length - 1] & 15;
12050
+ const code2 = ((hash[offset] & 127) << 24 | hash[offset + 1] << 16 | hash[offset + 2] << 8 | hash[offset + 3]) % 1e6;
12051
+ return code2.toString().padStart(6, "0");
12052
+ }
12053
+ function verifyTotp(secret, token, window2 = 1) {
12054
+ const timeStep = 30;
12055
+ const counter = BigInt(Math.floor(Date.now() / 1e3 / timeStep));
12056
+ for (let i2 = -window2; i2 <= window2; i2++) {
12057
+ if (generateHotp(secret, counter + BigInt(i2)) === token) {
12058
+ return true;
12059
+ }
12060
+ }
12061
+ return false;
12062
+ }
12063
+ function generateTotpSecret(issuer, accountName) {
12064
+ const secretBuffer = require$$0$5.randomBytes(20);
12065
+ const secret = base32Encode(secretBuffer);
12066
+ const encodedIssuer = encodeURIComponent(issuer);
12067
+ const encodedAccount = encodeURIComponent(accountName);
12068
+ const uri = `otpauth://totp/${encodedIssuer}:${encodedAccount}?secret=${secret}&issuer=${encodedIssuer}&algorithm=SHA1&digits=6&period=30`;
12069
+ return {
12070
+ secret,
12071
+ uri
12072
+ };
12073
+ }
12074
+ function generateRecoveryCodes(count = 10) {
12075
+ return Array.from({
12076
+ length: count
12077
+ }, () => {
12078
+ const raw = require$$0$5.randomBytes(5).toString("hex").toUpperCase();
12079
+ const parts = raw.match(/.{1,5}/g);
12080
+ return parts ? parts.join("-") : raw;
12081
+ });
12082
+ }
12083
+ function hashRecoveryCode(code2) {
12084
+ return require$$0$5.createHash("sha256").update(code2.replace(/-/g, "").toUpperCase()).digest("hex");
12085
+ }
11191
12086
  function buildAuthResponse(user, roleIds, accessToken, refreshToken) {
11192
12087
  return {
11193
12088
  user: {
@@ -11225,9 +12120,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11225
12120
  emailService,
11226
12121
  emailConfig,
11227
12122
  allowRegistration = false,
11228
- overrides
12123
+ authHooks
11229
12124
  } = config;
11230
- const ops = resolveAuthOverrides(overrides);
12125
+ const ops = resolveAuthHooks(authHooks);
11231
12126
  const registerSchema = objectType({
11232
12127
  email: stringType().email("Invalid email address").max(255),
11233
12128
  password: stringType().min(1, "Password is required").max(128),
@@ -11291,7 +12186,19 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11291
12186
  async function createSessionAndTokens(userId, userAgent, ipAddress) {
11292
12187
  const roles = await authRepo.getUserRoles(userId);
11293
12188
  const roleIds = roles.map((r) => r.id);
11294
- const accessToken = generateAccessToken(userId, roleIds);
12189
+ let customClaims;
12190
+ if (authHooks?.customizeAccessToken) {
12191
+ const user = await authRepo.getUserById(userId);
12192
+ if (user) {
12193
+ const defaultClaims = {
12194
+ userId,
12195
+ roles: roleIds,
12196
+ aal: "aal1"
12197
+ };
12198
+ customClaims = await authHooks.customizeAccessToken(defaultClaims, user);
12199
+ }
12200
+ }
12201
+ const accessToken = generateAccessToken(userId, roleIds, "aal1", customClaims);
11295
12202
  const refreshToken = generateRefreshToken();
11296
12203
  await authRepo.createRefreshToken(userId, hashRefreshToken(refreshToken), getRefreshTokenExpiry(), userAgent, ipAddress);
11297
12204
  return {
@@ -11326,8 +12233,8 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11326
12233
  passwordHash,
11327
12234
  displayName: displayName || void 0
11328
12235
  };
11329
- if (overrides?.beforeUserCreate) {
11330
- createData = await overrides.beforeUserCreate(createData);
12236
+ if (authHooks?.beforeUserCreate) {
12237
+ createData = await authHooks.beforeUserCreate(createData);
11331
12238
  }
11332
12239
  const user = await authRepo.createUser(createData);
11333
12240
  const existingUsers = await authRepo.listUsers();
@@ -11346,14 +12253,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11346
12253
  email: user.email,
11347
12254
  displayName: user.displayName
11348
12255
  });
11349
- if (overrides?.afterUserCreate) {
11350
- overrides.afterUserCreate(user).catch((err) => {
11351
- console.error("[AuthOverrides] afterUserCreate error:", err instanceof Error ? err.message : err);
11352
- });
12256
+ if (authHooks?.afterUserCreate) {
12257
+ try {
12258
+ await authHooks.afterUserCreate(user);
12259
+ } catch (err) {
12260
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
12261
+ }
11353
12262
  }
11354
- if (overrides?.onAuthenticated) {
11355
- overrides.onAuthenticated(user, "register").catch((err) => {
11356
- console.error("[AuthOverrides] onAuthenticated error:", err instanceof Error ? err.message : err);
12263
+ if (authHooks?.onAuthenticated) {
12264
+ authHooks.onAuthenticated(user, "register").catch((err) => {
12265
+ console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
11357
12266
  });
11358
12267
  }
11359
12268
  return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken), 201);
@@ -11363,9 +12272,12 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11363
12272
  email,
11364
12273
  password
11365
12274
  } = parseBody2(loginSchema, await c.req.json());
12275
+ if (authHooks?.beforeLogin) {
12276
+ await authHooks.beforeLogin(email, "login");
12277
+ }
11366
12278
  let user;
11367
- if (overrides?.verifyCredentials) {
11368
- user = await overrides.verifyCredentials(email, password, authRepo);
12279
+ if (authHooks?.verifyCredentials) {
12280
+ user = await authHooks.verifyCredentials(email, password, authRepo);
11369
12281
  if (!user) {
11370
12282
  throw ApiError.unauthorized("Invalid email or password", "INVALID_CREDENTIALS");
11371
12283
  }
@@ -11387,9 +12299,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11387
12299
  accessToken,
11388
12300
  refreshToken
11389
12301
  } = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
11390
- if (overrides?.onAuthenticated) {
11391
- overrides.onAuthenticated(user, "login").catch((err) => {
11392
- console.error("[AuthOverrides] onAuthenticated error:", err instanceof Error ? err.message : err);
12302
+ if (authHooks?.onAuthenticated) {
12303
+ authHooks.onAuthenticated(user, "login").catch((err) => {
12304
+ console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
11393
12305
  });
11394
12306
  }
11395
12307
  return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken));
@@ -11428,6 +12340,13 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11428
12340
  await authRepo.linkUserIdentity(user.id, provider.id, externalUser.providerId, {
11429
12341
  email: externalUser.email
11430
12342
  });
12343
+ if (authHooks?.afterUserCreate) {
12344
+ try {
12345
+ await authHooks.afterUserCreate(user);
12346
+ } catch (err) {
12347
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
12348
+ }
12349
+ }
11431
12350
  const allUsers = await authRepo.listUsers();
11432
12351
  const isFirstUser = allUsers.length === 1 && allUsers[0].id === user.id;
11433
12352
  if (isFirstUser) {
@@ -11513,6 +12432,11 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11513
12432
  await authRepo.updatePassword(storedToken.userId, passwordHash);
11514
12433
  await authRepo.markPasswordResetTokenUsed(tokenHash);
11515
12434
  await authRepo.deleteAllRefreshTokensForUser(storedToken.userId);
12435
+ if (authHooks?.onPasswordReset) {
12436
+ authHooks.onPasswordReset(storedToken.userId).catch((err) => {
12437
+ console.error("[AuthHooks] onPasswordReset error:", err instanceof Error ? err.message : err);
12438
+ });
12439
+ }
11516
12440
  return c.json({
11517
12441
  success: true,
11518
12442
  message: "Password has been reset successfully"
@@ -11616,7 +12540,19 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11616
12540
  }
11617
12541
  const roles = await authRepo.getUserRoles(storedToken.userId);
11618
12542
  const roleIds = roles.map((r) => r.id);
11619
- const newAccessToken = generateAccessToken(storedToken.userId, roleIds);
12543
+ let customClaims;
12544
+ if (authHooks?.customizeAccessToken) {
12545
+ const user = await authRepo.getUserById(storedToken.userId);
12546
+ if (user) {
12547
+ const defaultClaims = {
12548
+ userId: storedToken.userId,
12549
+ roles: roleIds,
12550
+ aal: "aal1"
12551
+ };
12552
+ customClaims = await authHooks.customizeAccessToken(defaultClaims, user);
12553
+ }
12554
+ }
12555
+ const newAccessToken = generateAccessToken(storedToken.userId, roleIds, "aal1", customClaims);
11620
12556
  const newRefreshToken = generateRefreshToken();
11621
12557
  const userAgent = c.req.header("user-agent") || "unknown";
11622
12558
  const ipAddress = c.req.header("x-forwarded-for") || "unknown";
@@ -11638,6 +12574,18 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11638
12574
  const tokenHash = hashRefreshToken(refreshToken);
11639
12575
  await authRepo.deleteRefreshToken(tokenHash);
11640
12576
  }
12577
+ const authHeader = c.req.header("authorization");
12578
+ if (authHooks?.afterLogout && authHeader?.startsWith("Bearer ")) {
12579
+ const {
12580
+ verifyAccessToken: verifyAccessToken2
12581
+ } = await Promise.resolve().then(() => jwt);
12582
+ const payload = verifyAccessToken2(authHeader.substring(7));
12583
+ if (payload) {
12584
+ authHooks.afterLogout(payload.userId).catch((err) => {
12585
+ console.error("[AuthHooks] afterLogout error:", err instanceof Error ? err.message : err);
12586
+ });
12587
+ }
12588
+ }
11641
12589
  return c.json({
11642
12590
  success: true
11643
12591
  });
@@ -11757,6 +12705,270 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11757
12705
  enabledProviders
11758
12706
  });
11759
12707
  });
12708
+ router.post("/anonymous", strictAuthLimiter, async (c) => {
12709
+ const anonId = require$$0$5.randomBytes(16).toString("hex");
12710
+ const anonEmail = `anon_${anonId.slice(0, 8)}@anonymous.local`;
12711
+ let createData = {
12712
+ email: anonEmail,
12713
+ emailVerified: false,
12714
+ isAnonymous: true
12715
+ };
12716
+ if (authHooks?.beforeUserCreate) {
12717
+ createData = await authHooks.beforeUserCreate(createData);
12718
+ }
12719
+ const user = await authRepo.createUser(createData);
12720
+ if (config.defaultRole) {
12721
+ await authRepo.assignDefaultRole(user.id, config.defaultRole);
12722
+ }
12723
+ const {
12724
+ roleIds,
12725
+ accessToken,
12726
+ refreshToken
12727
+ } = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
12728
+ if (authHooks?.afterUserCreate) {
12729
+ authHooks.afterUserCreate(user).catch((err) => {
12730
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
12731
+ });
12732
+ }
12733
+ if (authHooks?.onAuthenticated) {
12734
+ authHooks.onAuthenticated(user, "anonymous").catch((err) => {
12735
+ console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
12736
+ });
12737
+ }
12738
+ return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken), 201);
12739
+ });
12740
+ router.post("/anonymous/link", requireAuth, async (c) => {
12741
+ const userCtx = c.get("user");
12742
+ if (!userCtx) {
12743
+ throw ApiError.unauthorized("Not authenticated");
12744
+ }
12745
+ const user = await authRepo.getUserById(userCtx.userId);
12746
+ if (!user?.isAnonymous) {
12747
+ throw ApiError.badRequest("User is not anonymous", "NOT_ANONYMOUS");
12748
+ }
12749
+ const linkSchema = objectType({
12750
+ email: stringType().email("Invalid email address").max(255),
12751
+ password: stringType().min(1, "Password is required").max(128)
12752
+ });
12753
+ const {
12754
+ email,
12755
+ password
12756
+ } = parseBody2(linkSchema, await c.req.json());
12757
+ const passwordValidation = ops.validatePasswordStrength(password);
12758
+ if (!passwordValidation.valid) {
12759
+ throw ApiError.badRequest(passwordValidation.errors.join(". "), "WEAK_PASSWORD");
12760
+ }
12761
+ const existingUser = await authRepo.getUserByEmail(email.toLowerCase());
12762
+ if (existingUser) {
12763
+ throw ApiError.conflict("Email already registered", "EMAIL_EXISTS");
12764
+ }
12765
+ const passwordHash = await ops.hashPassword(password);
12766
+ const updatedUser = await authRepo.updateUser(user.id, {
12767
+ email: email.toLowerCase(),
12768
+ passwordHash,
12769
+ isAnonymous: false
12770
+ });
12771
+ if (!updatedUser) {
12772
+ throw ApiError.notFound("User not found");
12773
+ }
12774
+ const {
12775
+ roleIds,
12776
+ accessToken,
12777
+ refreshToken
12778
+ } = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
12779
+ return c.json(buildAuthResponse(updatedUser, roleIds, accessToken, refreshToken));
12780
+ });
12781
+ router.post("/mfa/enroll", requireAuth, async (c) => {
12782
+ const userCtx = c.get("user");
12783
+ if (!userCtx) {
12784
+ throw ApiError.unauthorized("Not authenticated");
12785
+ }
12786
+ const body = await c.req.json().catch(() => ({}));
12787
+ const friendlyName = typeof body.friendlyName === "string" ? body.friendlyName : void 0;
12788
+ const issuer = typeof body.issuer === "string" ? body.issuer : emailConfig?.appName || "Rebase";
12789
+ const user = await authRepo.getUserById(userCtx.userId);
12790
+ if (!user) {
12791
+ throw ApiError.notFound("User not found");
12792
+ }
12793
+ const {
12794
+ secret,
12795
+ uri
12796
+ } = generateTotpSecret(issuer, user.email);
12797
+ const factor = await authRepo.createMfaFactor(
12798
+ user.id,
12799
+ "totp",
12800
+ secret,
12801
+ // In production, encrypt this before storage
12802
+ friendlyName
12803
+ );
12804
+ const codes = generateRecoveryCodes(10);
12805
+ const codeHashes = codes.map(hashRecoveryCode);
12806
+ await authRepo.createRecoveryCodes(user.id, codeHashes);
12807
+ return c.json({
12808
+ factor: {
12809
+ id: factor.id,
12810
+ factorType: factor.factorType,
12811
+ friendlyName: factor.friendlyName
12812
+ },
12813
+ totp: {
12814
+ secret,
12815
+ uri,
12816
+ qrUri: uri
12817
+ // Client can use a QR library to render this
12818
+ },
12819
+ recoveryCodes: codes
12820
+ }, 201);
12821
+ });
12822
+ router.post("/mfa/verify", requireAuth, async (c) => {
12823
+ const userCtx = c.get("user");
12824
+ if (!userCtx) {
12825
+ throw ApiError.unauthorized("Not authenticated");
12826
+ }
12827
+ const verifySchema = objectType({
12828
+ factorId: stringType().min(1, "Factor ID is required"),
12829
+ code: stringType().length(6, "Code must be 6 digits")
12830
+ });
12831
+ const {
12832
+ factorId,
12833
+ code: code2
12834
+ } = parseBody2(verifySchema, await c.req.json());
12835
+ const factor = await authRepo.getMfaFactorById(factorId);
12836
+ if (!factor || factor.userId !== userCtx.userId) {
12837
+ throw ApiError.notFound("MFA factor not found");
12838
+ }
12839
+ if (factor.verified) {
12840
+ throw ApiError.badRequest("Factor is already verified", "ALREADY_VERIFIED");
12841
+ }
12842
+ const secretBuffer = base32Decode(factor.secretEncrypted);
12843
+ const isValid2 = verifyTotp(secretBuffer, code2);
12844
+ if (!isValid2) {
12845
+ throw ApiError.unauthorized("Invalid TOTP code", "INVALID_CODE");
12846
+ }
12847
+ await authRepo.verifyMfaFactor(factorId);
12848
+ return c.json({
12849
+ success: true,
12850
+ message: "MFA factor verified and enrolled"
12851
+ });
12852
+ });
12853
+ router.post("/mfa/challenge", requireAuth, async (c) => {
12854
+ const userCtx = c.get("user");
12855
+ if (!userCtx) {
12856
+ throw ApiError.unauthorized("Not authenticated");
12857
+ }
12858
+ const challengeSchema = objectType({
12859
+ factorId: stringType().min(1, "Factor ID is required")
12860
+ });
12861
+ const {
12862
+ factorId
12863
+ } = parseBody2(challengeSchema, await c.req.json());
12864
+ const factor = await authRepo.getMfaFactorById(factorId);
12865
+ if (!factor || factor.userId !== userCtx.userId) {
12866
+ throw ApiError.notFound("MFA factor not found");
12867
+ }
12868
+ if (!factor.verified) {
12869
+ throw ApiError.badRequest("MFA factor is not yet verified", "FACTOR_NOT_VERIFIED");
12870
+ }
12871
+ const ipAddress = c.req.header("x-forwarded-for") || "unknown";
12872
+ const challenge = await authRepo.createMfaChallenge(factorId, ipAddress);
12873
+ return c.json({
12874
+ challengeId: challenge.id,
12875
+ factorId: challenge.factorId,
12876
+ expiresAt: new Date(Date.now() + 5 * 60 * 1e3).toISOString()
12877
+ });
12878
+ });
12879
+ router.post("/mfa/challenge/verify", requireAuth, async (c) => {
12880
+ const userCtx = c.get("user");
12881
+ if (!userCtx) {
12882
+ throw ApiError.unauthorized("Not authenticated");
12883
+ }
12884
+ const challengeVerifySchema = objectType({
12885
+ challengeId: stringType().min(1, "Challenge ID is required"),
12886
+ code: stringType().min(1, "Code is required")
12887
+ });
12888
+ const {
12889
+ challengeId,
12890
+ code: code2
12891
+ } = parseBody2(challengeVerifySchema, await c.req.json());
12892
+ const challenge = await authRepo.getMfaChallengeById(challengeId);
12893
+ if (!challenge) {
12894
+ throw ApiError.badRequest("Invalid or expired challenge", "INVALID_CHALLENGE");
12895
+ }
12896
+ const factor = await authRepo.getMfaFactorById(challenge.factorId);
12897
+ if (!factor || factor.userId !== userCtx.userId) {
12898
+ throw ApiError.notFound("MFA factor not found");
12899
+ }
12900
+ const isRecoveryCode = code2.length > 6;
12901
+ let isValid2 = false;
12902
+ if (isRecoveryCode) {
12903
+ const codeHash = hashRecoveryCode(code2);
12904
+ isValid2 = await authRepo.useRecoveryCode(userCtx.userId, codeHash);
12905
+ } else {
12906
+ const secretBuffer = base32Decode(factor.secretEncrypted);
12907
+ isValid2 = verifyTotp(secretBuffer, code2);
12908
+ }
12909
+ if (!isValid2) {
12910
+ throw ApiError.unauthorized("Invalid verification code", "INVALID_CODE");
12911
+ }
12912
+ await authRepo.verifyMfaChallenge(challengeId);
12913
+ const roles = await authRepo.getUserRoles(userCtx.userId);
12914
+ const roleIds = roles.map((r) => r.id);
12915
+ const accessToken = generateAccessToken(userCtx.userId, roleIds, "aal2");
12916
+ const refreshToken = generateRefreshToken();
12917
+ await authRepo.createRefreshToken(userCtx.userId, hashRefreshToken(refreshToken), getRefreshTokenExpiry(), c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
12918
+ if (authHooks?.onMfaVerified) {
12919
+ authHooks.onMfaVerified(userCtx.userId, factor.id).catch((err) => {
12920
+ console.error("[AuthHooks] onMfaVerified error:", err instanceof Error ? err.message : err);
12921
+ });
12922
+ }
12923
+ return c.json({
12924
+ tokens: {
12925
+ accessToken,
12926
+ refreshToken,
12927
+ accessTokenExpiresAt: getAccessTokenExpiry()
12928
+ }
12929
+ });
12930
+ });
12931
+ router.get("/mfa/factors", requireAuth, async (c) => {
12932
+ const userCtx = c.get("user");
12933
+ if (!userCtx) {
12934
+ throw ApiError.unauthorized("Not authenticated");
12935
+ }
12936
+ const factors = await authRepo.getMfaFactors(userCtx.userId);
12937
+ return c.json({
12938
+ factors: factors.map((f2) => ({
12939
+ id: f2.id,
12940
+ factorType: f2.factorType,
12941
+ friendlyName: f2.friendlyName,
12942
+ verified: f2.verified,
12943
+ createdAt: f2.createdAt
12944
+ }))
12945
+ });
12946
+ });
12947
+ router.delete("/mfa/unenroll", requireAuth, async (c) => {
12948
+ const userCtx = c.get("user");
12949
+ if (!userCtx) {
12950
+ throw ApiError.unauthorized("Not authenticated");
12951
+ }
12952
+ const unenrollSchema = objectType({
12953
+ factorId: stringType().min(1, "Factor ID is required")
12954
+ });
12955
+ const {
12956
+ factorId
12957
+ } = parseBody2(unenrollSchema, await c.req.json());
12958
+ const factor = await authRepo.getMfaFactorById(factorId);
12959
+ if (!factor || factor.userId !== userCtx.userId) {
12960
+ throw ApiError.notFound("MFA factor not found");
12961
+ }
12962
+ await authRepo.deleteMfaFactor(factorId, userCtx.userId);
12963
+ const hasFactors = await authRepo.hasVerifiedMfaFactors(userCtx.userId);
12964
+ if (!hasFactors) {
12965
+ await authRepo.deleteAllRecoveryCodes(userCtx.userId);
12966
+ }
12967
+ return c.json({
12968
+ success: true,
12969
+ message: "MFA factor removed"
12970
+ });
12971
+ });
11760
12972
  return router;
11761
12973
  }
11762
12974
  function generateSecurePassword() {
@@ -11788,9 +13000,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11788
13000
  emailService,
11789
13001
  emailConfig,
11790
13002
  hooks,
11791
- overrides
13003
+ authHooks
11792
13004
  } = config;
11793
- const ops = resolveAuthOverrides(overrides);
13005
+ const ops = resolveAuthHooks(authHooks);
11794
13006
  function buildHookContext(c, method) {
11795
13007
  const user = c.get("user");
11796
13008
  return {
@@ -11858,6 +13070,10 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
11858
13070
  if (!userId) {
11859
13071
  throw ApiError.unauthorized("User ID not found in auth context");
11860
13072
  }
13073
+ const caller = await authRepo.getUserById(userId);
13074
+ if (!caller) {
13075
+ throw ApiError.notFound("Authenticated user does not exist in the database. Please sign out and sign in/register again.", "USER_NOT_FOUND");
13076
+ }
11861
13077
  await authRepo.setUserRoles(userId, ["admin"]);
11862
13078
  if (config.setBootstrapCompleted) {
11863
13079
  await config.setBootstrapCompleted();
@@ -12109,6 +13325,18 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12109
13325
  await authRepo.updateUser(userId, updates);
12110
13326
  }
12111
13327
  if (roles !== void 0 && Array.isArray(roles)) {
13328
+ const currentRoles = await authRepo.getUserRoleIds(userId);
13329
+ const wasAdmin = currentRoles.includes("admin");
13330
+ const willBeAdmin = roles.includes("admin");
13331
+ if (wasAdmin && !willBeAdmin) {
13332
+ const adminUsers = await authRepo.listUsersPaginated({
13333
+ roleId: "admin",
13334
+ limit: 1
13335
+ });
13336
+ if (adminUsers.total <= 1) {
13337
+ throw ApiError.forbidden("Cannot demote the last administrator", "LAST_ADMIN");
13338
+ }
13339
+ }
12112
13340
  await authRepo.setUserRoles(userId, roles);
12113
13341
  }
12114
13342
  const result = await authRepo.getUserWithRoles(userId);
@@ -12133,6 +13361,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12133
13361
  if (!existing) {
12134
13362
  throw ApiError.notFound("User not found");
12135
13363
  }
13364
+ const roles = await authRepo.getUserRoleIds(userId);
13365
+ if (roles.includes("admin")) {
13366
+ const adminUsers = await authRepo.listUsersPaginated({
13367
+ roleId: "admin",
13368
+ limit: 1
13369
+ });
13370
+ if (adminUsers.total <= 1) {
13371
+ throw ApiError.forbidden("Cannot delete the last administrator", "LAST_ADMIN");
13372
+ }
13373
+ }
12136
13374
  const hookCtx = buildHookContext(c, "DELETE");
12137
13375
  if (hooks?.users?.beforeDelete) {
12138
13376
  await hooks.users.beforeDelete(userId, hookCtx);
@@ -12154,8 +13392,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12154
13392
  id: r.id,
12155
13393
  name: r.name,
12156
13394
  isAdmin: r.isAdmin,
12157
- defaultPermissions: r.defaultPermissions,
12158
- config: r.config
13395
+ defaultPermissions: r.defaultPermissions
12159
13396
  }));
12160
13397
  adminRoles = await applyRoleAfterReadBatch(adminRoles, hookCtx);
12161
13398
  return c.json({
@@ -12178,8 +13415,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12178
13415
  id,
12179
13416
  name: name2,
12180
13417
  isAdmin,
12181
- defaultPermissions,
12182
- config: config2
13418
+ defaultPermissions
12183
13419
  } = body;
12184
13420
  if (!id || !name2) {
12185
13421
  throw ApiError.badRequest("Role ID and name are required", "INVALID_INPUT");
@@ -12192,8 +13428,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12192
13428
  id,
12193
13429
  name: name2,
12194
13430
  isAdmin: isAdmin ?? false,
12195
- defaultPermissions: defaultPermissions ?? null,
12196
- config: config2 ?? null
13431
+ defaultPermissions: defaultPermissions ?? null
12197
13432
  });
12198
13433
  return c.json({
12199
13434
  role
@@ -12205,8 +13440,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12205
13440
  const {
12206
13441
  name: name2,
12207
13442
  isAdmin,
12208
- defaultPermissions,
12209
- config: config2
13443
+ defaultPermissions
12210
13444
  } = body;
12211
13445
  const existing = await authRepo.getRoleById(roleId);
12212
13446
  if (!existing) {
@@ -12215,8 +13449,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12215
13449
  const role = await authRepo.updateRole(roleId, {
12216
13450
  name: name2,
12217
13451
  isAdmin,
12218
- defaultPermissions,
12219
- config: config2
13452
+ defaultPermissions
12220
13453
  });
12221
13454
  return c.json({
12222
13455
  role
@@ -12248,9 +13481,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12248
13481
  oauthProviders = [],
12249
13482
  serviceKey,
12250
13483
  hooks,
12251
- overrides
13484
+ authHooks
12252
13485
  } = config;
12253
- const resolvedOps = resolveAuthOverrides(overrides);
13486
+ const resolvedOps = resolveAuthHooks(authHooks);
12254
13487
  const adapter = {
12255
13488
  id: "rebase-builtin",
12256
13489
  serviceKey,
@@ -12324,7 +13557,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12324
13557
  rawToken: token
12325
13558
  };
12326
13559
  },
12327
- userManagement: createUserManagementFromRepo(authRepository, resolvedOps, overrides),
13560
+ userManagement: createUserManagementFromRepo(authRepository, resolvedOps, authHooks),
12328
13561
  roleManagement: createRoleManagementFromRepo(authRepository),
12329
13562
  createAuthRoutes() {
12330
13563
  return createAuthRoutes({
@@ -12334,7 +13567,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12334
13567
  allowRegistration,
12335
13568
  defaultRole,
12336
13569
  oauthProviders,
12337
- overrides
13570
+ authHooks
12338
13571
  });
12339
13572
  },
12340
13573
  createAdminRoutes() {
@@ -12344,7 +13577,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12344
13577
  emailConfig,
12345
13578
  serviceKey,
12346
13579
  hooks,
12347
- overrides
13580
+ authHooks
12348
13581
  });
12349
13582
  },
12350
13583
  async getCapabilities() {
@@ -12373,7 +13606,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12373
13606
  };
12374
13607
  return adapter;
12375
13608
  }
12376
- function createUserManagementFromRepo(repo, resolvedOps, overrides) {
13609
+ function createUserManagementFromRepo(repo, resolvedOps, authHooks) {
12377
13610
  return {
12378
13611
  async listUsers(options2) {
12379
13612
  const result = await repo.listUsersPaginated({
@@ -12404,14 +13637,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12404
13637
  photoUrl: data.photoUrl,
12405
13638
  metadata: data.metadata
12406
13639
  };
12407
- if (overrides?.beforeUserCreate) {
12408
- createData = await overrides.beforeUserCreate(createData);
13640
+ if (authHooks?.beforeUserCreate) {
13641
+ createData = await authHooks.beforeUserCreate(createData);
12409
13642
  }
12410
13643
  const user = await repo.createUser(createData);
12411
- if (overrides?.afterUserCreate) {
12412
- overrides.afterUserCreate(user).catch((err) => {
12413
- console.error("[AuthOverrides] afterUserCreate error:", err instanceof Error ? err.message : err);
12414
- });
13644
+ if (authHooks?.afterUserCreate) {
13645
+ try {
13646
+ await authHooks.afterUserCreate(user);
13647
+ } catch (err) {
13648
+ console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
13649
+ }
12415
13650
  }
12416
13651
  return toAuthUserData(user);
12417
13652
  },
@@ -12428,7 +13663,15 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12428
13663
  return user ? toAuthUserData(user) : null;
12429
13664
  },
12430
13665
  async deleteUser(id) {
13666
+ if (authHooks?.beforeUserDelete) {
13667
+ await authHooks.beforeUserDelete(id);
13668
+ }
12431
13669
  await repo.deleteUser(id);
13670
+ if (authHooks?.afterUserDelete) {
13671
+ authHooks.afterUserDelete(id).catch((err) => {
13672
+ console.error("[AuthHooks] afterUserDelete error:", err instanceof Error ? err.message : err);
13673
+ });
13674
+ }
12432
13675
  },
12433
13676
  async getUserRoles(userId) {
12434
13677
  const roles = await repo.getUserRoles(userId);
@@ -12455,8 +13698,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12455
13698
  name: data.name,
12456
13699
  isAdmin: data.isAdmin,
12457
13700
  defaultPermissions: data.defaultPermissions,
12458
- collectionPermissions: data.collectionPermissions,
12459
- config: data.config
13701
+ collectionPermissions: data.collectionPermissions
12460
13702
  });
12461
13703
  return toAuthRoleData(role);
12462
13704
  },
@@ -12487,8 +13729,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12487
13729
  name: role.name,
12488
13730
  isAdmin: role.isAdmin,
12489
13731
  defaultPermissions: role.defaultPermissions,
12490
- collectionPermissions: role.collectionPermissions,
12491
- config: role.config
13732
+ collectionPermissions: role.collectionPermissions
12492
13733
  };
12493
13734
  }
12494
13735
  function configureLogLevel(logLevel) {
@@ -12509,20 +13750,20 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12509
13750
  if (currentLevel < 0) console.error = () => {
12510
13751
  };
12511
13752
  }
13753
+ let originalConsole;
12512
13754
  function resetConsole() {
12513
- if (!global.__originalConsole) {
12514
- global.__originalConsole = {
13755
+ if (!originalConsole) {
13756
+ originalConsole = {
12515
13757
  log: console.log,
12516
13758
  warn: console.warn,
12517
13759
  error: console.error,
12518
13760
  debug: console.debug
12519
13761
  };
12520
13762
  }
12521
- const original = global.__originalConsole;
12522
- console.log = original.log;
12523
- console.warn = original.warn;
12524
- console.error = original.error;
12525
- console.debug = original.debug;
13763
+ console.log = originalConsole.log;
13764
+ console.warn = originalConsole.warn;
13765
+ console.error = originalConsole.error;
13766
+ console.debug = originalConsole.debug;
12526
13767
  }
12527
13768
  const GCP_SEVERITY = {
12528
13769
  debug: "DEBUG",
@@ -12764,12 +14005,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
12764
14005
  exports3.Response = globalObject.Response;
12765
14006
  })(browser$1, browser$1.exports);
12766
14007
  var browserExports = browser$1.exports;
12767
- const __viteBrowserExternal = {};
12768
- const __viteBrowserExternal$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12769
- __proto__: null,
12770
- default: __viteBrowserExternal
12771
- }, Symbol.toStringTag, { value: "Module" }));
12772
- const require$$11 = /* @__PURE__ */ getAugmentedNamespace(__viteBrowserExternal$1);
12773
14008
  const isStream = (stream2) => stream2 !== null && typeof stream2 === "object" && typeof stream2.pipe === "function";
12774
14009
  isStream.writable = (stream2) => isStream(stream2) && stream2.writable !== false && typeof stream2._write === "function" && typeof stream2._writableState === "object";
12775
14010
  isStream.readable = (stream2) => isStream(stream2) && stream2.readable !== false && typeof stream2._read === "function" && typeof stream2._readableState === "object";
@@ -13554,16 +14789,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
13554
14789
  value: true
13555
14790
  });
13556
14791
  sha1$1.default = void 0;
13557
- function f(s2, x, y2, z) {
14792
+ function f(s2, x, y2, z2) {
13558
14793
  switch (s2) {
13559
14794
  case 0:
13560
- return x & y2 ^ ~x & z;
14795
+ return x & y2 ^ ~x & z2;
13561
14796
  case 1:
13562
- return x ^ y2 ^ z;
14797
+ return x ^ y2 ^ z2;
13563
14798
  case 2:
13564
- return x & y2 ^ x & z ^ y2 & z;
14799
+ return x & y2 ^ x & z2 ^ y2 & z2;
13565
14800
  case 3:
13566
- return x ^ y2 ^ z;
14801
+ return x ^ y2 ^ z2;
13567
14802
  }
13568
14803
  }
13569
14804
  function ROTL(x, n) {
@@ -14612,7 +15847,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
14612
15847
  const extend_1 = __importDefault(extend);
14613
15848
  const https_1 = require$$1;
14614
15849
  const node_fetch_1 = __importDefault(browserExports);
14615
- const querystring_1$1 = __importDefault(require$$11);
15850
+ const querystring_1$1 = __importDefault(require$$1$2);
14616
15851
  const is_stream_1 = __importDefault(isStream_1);
14617
15852
  const url_1 = require$$0$2;
14618
15853
  const common_1 = common$1;
@@ -16290,11 +17525,11 @@ Content-Type: ${partContentType}\r
16290
17525
  return n > 0 || n === i2 ? i2 : i2 - 1;
16291
17526
  }
16292
17527
  function coeffToString(a2) {
16293
- var s2, z, i2 = 1, j = a2.length, r = a2[0] + "";
17528
+ var s2, z2, i2 = 1, j = a2.length, r = a2[0] + "";
16294
17529
  for (; i2 < j; ) {
16295
17530
  s2 = a2[i2++] + "";
16296
- z = LOG_BASE - s2.length;
16297
- for (; z--; s2 = "0" + s2) ;
17531
+ z2 = LOG_BASE - s2.length;
17532
+ for (; z2--; s2 = "0" + s2) ;
16298
17533
  r += s2;
16299
17534
  }
16300
17535
  for (j = r.length; r.charCodeAt(--j) === 48; ) ;
@@ -16327,15 +17562,15 @@ Content-Type: ${partContentType}\r
16327
17562
  function toExponential(str, e) {
16328
17563
  return (str.length > 1 ? str.charAt(0) + "." + str.slice(1) : str) + (e < 0 ? "e" : "e+") + e;
16329
17564
  }
16330
- function toFixedPoint(str, e, z) {
17565
+ function toFixedPoint(str, e, z2) {
16331
17566
  var len2, zs;
16332
17567
  if (e < 0) {
16333
- for (zs = z + "."; ++e; zs += z) ;
17568
+ for (zs = z2 + "."; ++e; zs += z2) ;
16334
17569
  str = zs + str;
16335
17570
  } else {
16336
17571
  len2 = str.length;
16337
17572
  if (++e > len2) {
16338
- for (zs = z, e -= len2; --e; zs += z) ;
17573
+ for (zs = z2, e -= len2; --e; zs += z2) ;
16339
17574
  str += zs;
16340
17575
  } else if (e < len2) {
16341
17576
  str = str.slice(0, e) + "." + str.slice(e);
@@ -16754,7 +17989,7 @@ Content-Type: ${partContentType}\r
16754
17989
  exports3.isGoogleComputeEngine = isGoogleComputeEngine;
16755
17990
  exports3.detectGCPResidency = detectGCPResidency;
16756
17991
  const fs_1 = fs$4;
16757
- const os_1 = require$$1$2;
17992
+ const os_1 = require$$1$3;
16758
17993
  exports3.GCE_LINUX_BIOS_PATHS = {
16759
17994
  BIOS_DATE: "/sys/class/dmi/id/bios_date",
16760
17995
  BIOS_VENDOR: "/sys/class/dmi/id/bios_vendor"
@@ -16887,7 +18122,7 @@ Content-Type: ${partContentType}\r
16887
18122
  exports3.setBackend = setBackend;
16888
18123
  exports3.log = log;
16889
18124
  const node_events_1 = require$$0$8;
16890
- const process2 = __importStar2(require$$1$3);
18125
+ const process2 = __importStar2(require$$1$4);
16891
18126
  const util2 = __importStar2(require$$2$1);
16892
18127
  const colours_1 = colours;
16893
18128
  var LogSeverity;
@@ -17959,7 +19194,7 @@ Content-Type: ${partContentType}\r
17959
19194
  Object.defineProperty(oauth2client, "__esModule", { value: true });
17960
19195
  oauth2client.OAuth2Client = oauth2client.ClientAuthentication = oauth2client.CertificateFormat = oauth2client.CodeChallengeMethod = void 0;
17961
19196
  const gaxios_1$5 = src$2;
17962
- const querystring$2 = require$$11;
19197
+ const querystring$2 = require$$1$2;
17963
19198
  const stream$4 = require$$0$4;
17964
19199
  const formatEcdsa = ecdsaSigFormatter;
17965
19200
  const crypto_1$2 = requireCrypto();
@@ -19447,7 +20682,7 @@ Content-Type: ${partContentType}\r
19447
20682
  Object.defineProperty(refreshclient, "__esModule", { value: true });
19448
20683
  refreshclient.UserRefreshClient = refreshclient.USER_REFRESH_ACCOUNT_TYPE = void 0;
19449
20684
  const oauth2client_1$1 = oauth2client;
19450
- const querystring_1 = require$$11;
20685
+ const querystring_1 = require$$1$2;
19451
20686
  refreshclient.USER_REFRESH_ACCOUNT_TYPE = "authorized_user";
19452
20687
  class UserRefreshClient extends oauth2client_1$1.OAuth2Client {
19453
20688
  constructor(optionsOrClientId, clientSecret, refreshToken, eagerRefreshThresholdMillis, forceRefreshOnFailure) {
@@ -19722,7 +20957,7 @@ Content-Type: ${partContentType}\r
19722
20957
  Object.defineProperty(oauth2common, "__esModule", { value: true });
19723
20958
  oauth2common.OAuthClientAuthHandler = void 0;
19724
20959
  oauth2common.getErrorFromOAuthErrorResponse = getErrorFromOAuthErrorResponse;
19725
- const querystring$1 = require$$11;
20960
+ const querystring$1 = require$$1$2;
19726
20961
  const crypto_1$1 = requireCrypto();
19727
20962
  const METHODS_SUPPORTING_REQUEST_BODY = ["PUT", "POST", "PATCH"];
19728
20963
  class OAuthClientAuthHandler {
@@ -19868,7 +21103,7 @@ Content-Type: ${partContentType}\r
19868
21103
  Object.defineProperty(stscredentials, "__esModule", { value: true });
19869
21104
  stscredentials.StsCredentials = void 0;
19870
21105
  const gaxios_1$1 = src$2;
19871
- const querystring = require$$11;
21106
+ const querystring = require$$1$2;
19872
21107
  const transporters_1 = transporters;
19873
21108
  const oauth2common_1$1 = oauth2common;
19874
21109
  class StsCredentials extends oauth2common_1$1.OAuthClientAuthHandler {
@@ -21474,7 +22709,7 @@ ${credentialScope}
21474
22709
  const child_process_1 = require$$0$a;
21475
22710
  const fs2 = fs$4;
21476
22711
  const gcpMetadata2 = src$3;
21477
- const os2 = require$$1$2;
22712
+ const os2 = require$$1$3;
21478
22713
  const path2 = path$3;
21479
22714
  const crypto_12 = requireCrypto();
21480
22715
  const transporters_12 = transporters;
@@ -22838,7 +24073,7 @@ ${credentialScope}
22838
24073
  }
22839
24074
  function createAppleProvider(config) {
22840
24075
  async function generateClientSecret() {
22841
- return jwt.sign({}, config.privateKey, {
24076
+ return jwt$1.sign({}, config.privateKey, {
22842
24077
  algorithm: "ES256",
22843
24078
  keyid: config.keyId,
22844
24079
  issuer: config.teamId,
@@ -23318,6 +24553,341 @@ ${credentialScope}
23318
24553
  }
23319
24554
  };
23320
24555
  }
24556
+ const TABLE$1 = "rebase.api_keys";
24557
+ function generateApiKey() {
24558
+ const random = require$$0$5.randomBytes(16).toString("hex");
24559
+ return `rk_live_${random}`;
24560
+ }
24561
+ function hashKey(plaintext) {
24562
+ return require$$0$5.createHash("sha256").update(plaintext).digest("hex");
24563
+ }
24564
+ function keyPrefix(plaintext) {
24565
+ return plaintext.substring(0, 12);
24566
+ }
24567
+ function toMasked(row) {
24568
+ return {
24569
+ id: row.id,
24570
+ name: row.name,
24571
+ key_prefix: row.key_prefix,
24572
+ permissions: row.permissions,
24573
+ rate_limit: row.rate_limit,
24574
+ created_by: row.created_by,
24575
+ created_at: row.created_at,
24576
+ updated_at: row.updated_at,
24577
+ last_used_at: row.last_used_at,
24578
+ expires_at: row.expires_at,
24579
+ revoked_at: row.revoked_at
24580
+ };
24581
+ }
24582
+ function rowToApiKey(row) {
24583
+ const permissions = typeof row.permissions === "string" ? JSON.parse(row.permissions) : row.permissions ?? [];
24584
+ return {
24585
+ id: row.id,
24586
+ name: row.name,
24587
+ key_prefix: row.key_prefix,
24588
+ key_hash: row.key_hash,
24589
+ permissions,
24590
+ rate_limit: row.rate_limit !== null && row.rate_limit !== void 0 ? Number(row.rate_limit) : null,
24591
+ created_by: row.created_by,
24592
+ created_at: new Date(row.created_at).toISOString(),
24593
+ updated_at: new Date(row.updated_at).toISOString(),
24594
+ last_used_at: row.last_used_at ? new Date(row.last_used_at).toISOString() : null,
24595
+ expires_at: row.expires_at ? new Date(row.expires_at).toISOString() : null,
24596
+ revoked_at: row.revoked_at ? new Date(row.revoked_at).toISOString() : null
24597
+ };
24598
+ }
24599
+ function esc(value) {
24600
+ return value.replace(/'/g, "''");
24601
+ }
24602
+ function createApiKeyStore(driver) {
24603
+ const admin = driver.admin;
24604
+ if (!isSQLAdmin(admin)) {
24605
+ logger.warn("⚠️ [api-key-store] DataDriver does not support SQL admin — API keys will not be available.");
24606
+ return void 0;
24607
+ }
24608
+ const exec = admin.executeSql.bind(admin);
24609
+ return {
24610
+ // ── Schema bootstrap ────────────────────────────────────────
24611
+ async ensureTable() {
24612
+ try {
24613
+ await exec("CREATE SCHEMA IF NOT EXISTS rebase");
24614
+ await exec(`
24615
+ CREATE TABLE IF NOT EXISTS ${TABLE$1} (
24616
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
24617
+ name TEXT NOT NULL,
24618
+ key_prefix TEXT NOT NULL,
24619
+ key_hash TEXT NOT NULL UNIQUE,
24620
+ permissions JSONB NOT NULL DEFAULT '[]'::jsonb,
24621
+ rate_limit INTEGER,
24622
+ created_by TEXT NOT NULL,
24623
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
24624
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
24625
+ last_used_at TIMESTAMPTZ,
24626
+ expires_at TIMESTAMPTZ,
24627
+ revoked_at TIMESTAMPTZ
24628
+ )
24629
+ `);
24630
+ await exec(`
24631
+ CREATE INDEX IF NOT EXISTS idx_api_keys_hash
24632
+ ON ${TABLE$1}(key_hash)
24633
+ `);
24634
+ await exec(`
24635
+ CREATE INDEX IF NOT EXISTS idx_api_keys_prefix
24636
+ ON ${TABLE$1}(key_prefix)
24637
+ `);
24638
+ logger.info("✅ API keys table ready");
24639
+ } catch (err) {
24640
+ logger.error("❌ Failed to create API keys table", {
24641
+ error: err
24642
+ });
24643
+ logger.warn("⚠️ Continuing without API keys support.");
24644
+ }
24645
+ },
24646
+ // ── Create ──────────────────────────────────────────────────
24647
+ async createApiKey(request, createdBy) {
24648
+ const plaintext = generateApiKey();
24649
+ const hash = hashKey(plaintext);
24650
+ const prefix = keyPrefix(plaintext);
24651
+ const permissionsJson = JSON.stringify(request.permissions);
24652
+ const rateLimitSql = request.rate_limit !== null && request.rate_limit !== void 0 ? String(request.rate_limit) : "NULL";
24653
+ const expiresAtSql = request.expires_at ? `'${esc(request.expires_at)}'` : "NULL";
24654
+ const rows = await exec(`
24655
+ INSERT INTO ${TABLE$1} (name, key_prefix, key_hash, permissions, rate_limit, created_by, expires_at)
24656
+ VALUES (
24657
+ '${esc(request.name)}',
24658
+ '${esc(prefix)}',
24659
+ '${esc(hash)}',
24660
+ '${esc(permissionsJson)}'::jsonb,
24661
+ ${rateLimitSql},
24662
+ '${esc(createdBy)}',
24663
+ ${expiresAtSql}
24664
+ )
24665
+ RETURNING *
24666
+ `);
24667
+ const apiKey = rowToApiKey(rows[0]);
24668
+ return {
24669
+ ...toMasked(apiKey),
24670
+ key: plaintext
24671
+ };
24672
+ },
24673
+ // ── Lookup by hash ──────────────────────────────────────────
24674
+ async findByKeyHash(hash) {
24675
+ const rows = await exec(`
24676
+ SELECT * FROM ${TABLE$1}
24677
+ WHERE key_hash = '${esc(hash)}'
24678
+ LIMIT 1
24679
+ `);
24680
+ if (rows.length === 0) return null;
24681
+ return rowToApiKey(rows[0]);
24682
+ },
24683
+ // ── List all (masked) ───────────────────────────────────────
24684
+ async listApiKeys() {
24685
+ const rows = await exec(`
24686
+ SELECT * FROM ${TABLE$1}
24687
+ ORDER BY created_at DESC
24688
+ `);
24689
+ return rows.map((r) => toMasked(rowToApiKey(r)));
24690
+ },
24691
+ // ── Get by ID (masked) ──────────────────────────────────────
24692
+ async getApiKeyById(id) {
24693
+ const rows = await exec(`
24694
+ SELECT * FROM ${TABLE$1}
24695
+ WHERE id = '${esc(id)}'
24696
+ LIMIT 1
24697
+ `);
24698
+ if (rows.length === 0) return null;
24699
+ return toMasked(rowToApiKey(rows[0]));
24700
+ },
24701
+ // ── Update ──────────────────────────────────────────────────
24702
+ async updateApiKey(id, updates) {
24703
+ const setClauses = [];
24704
+ if (updates.name !== void 0) {
24705
+ setClauses.push(`name = '${esc(updates.name)}'`);
24706
+ }
24707
+ if (updates.permissions !== void 0) {
24708
+ setClauses.push(`permissions = '${esc(JSON.stringify(updates.permissions))}'::jsonb`);
24709
+ }
24710
+ if (updates.rate_limit !== void 0) {
24711
+ setClauses.push(updates.rate_limit !== null ? `rate_limit = ${updates.rate_limit}` : "rate_limit = NULL");
24712
+ }
24713
+ if (updates.expires_at !== void 0) {
24714
+ setClauses.push(updates.expires_at !== null ? `expires_at = '${esc(updates.expires_at)}'` : "expires_at = NULL");
24715
+ }
24716
+ if (setClauses.length === 0) {
24717
+ return this.getApiKeyById(id);
24718
+ }
24719
+ setClauses.push("updated_at = NOW()");
24720
+ const rows = await exec(`
24721
+ UPDATE ${TABLE$1}
24722
+ SET ${setClauses.join(", ")}
24723
+ WHERE id = '${esc(id)}'
24724
+ RETURNING *
24725
+ `);
24726
+ if (rows.length === 0) return null;
24727
+ return toMasked(rowToApiKey(rows[0]));
24728
+ },
24729
+ // ── Revoke (soft-delete) ────────────────────────────────────
24730
+ async revokeApiKey(id) {
24731
+ const rows = await exec(`
24732
+ UPDATE ${TABLE$1}
24733
+ SET revoked_at = NOW(), updated_at = NOW()
24734
+ WHERE id = '${esc(id)}' AND revoked_at IS NULL
24735
+ RETURNING id
24736
+ `);
24737
+ return rows.length > 0;
24738
+ },
24739
+ // ── Touch last_used_at ──────────────────────────────────────
24740
+ async updateLastUsed(id) {
24741
+ try {
24742
+ await exec(`
24743
+ UPDATE ${TABLE$1}
24744
+ SET last_used_at = NOW()
24745
+ WHERE id = '${esc(id)}'
24746
+ `);
24747
+ } catch (err) {
24748
+ logger.error("[api-key-store] Failed to update last_used_at", {
24749
+ error: err
24750
+ });
24751
+ }
24752
+ }
24753
+ };
24754
+ }
24755
+ function validatePermissions(permissions) {
24756
+ if (!Array.isArray(permissions)) return false;
24757
+ for (const perm of permissions) {
24758
+ if (typeof perm !== "object" || perm === null) return false;
24759
+ if (typeof perm.collection !== "string" || perm.collection.length === 0) return false;
24760
+ if (!Array.isArray(perm.operations) || perm.operations.length === 0) return false;
24761
+ const validOps = /* @__PURE__ */ new Set(["read", "write", "delete"]);
24762
+ for (const op of perm.operations) {
24763
+ if (!validOps.has(op)) return false;
24764
+ }
24765
+ }
24766
+ return true;
24767
+ }
24768
+ function createApiKeyRoutes(options2) {
24769
+ const {
24770
+ store,
24771
+ serviceKey
24772
+ } = options2;
24773
+ const router = new hono.Hono();
24774
+ router.onError(errorHandler);
24775
+ router.use("/*", createRequireAuth({
24776
+ serviceKey
24777
+ }));
24778
+ router.use("/*", requireAdmin);
24779
+ router.get("/", async (c) => {
24780
+ const keys2 = await store.listApiKeys();
24781
+ return c.json({
24782
+ keys: keys2
24783
+ });
24784
+ });
24785
+ router.post("/", async (c) => {
24786
+ const body = await c.req.json();
24787
+ const {
24788
+ name: name2,
24789
+ permissions,
24790
+ rate_limit,
24791
+ expires_at
24792
+ } = body;
24793
+ if (!name2 || typeof name2 !== "string" || name2.trim().length === 0) {
24794
+ throw ApiError.badRequest("Name is required", "INVALID_INPUT");
24795
+ }
24796
+ if (!validatePermissions(permissions)) {
24797
+ throw ApiError.badRequest("Permissions must be an array of { collection: string, operations: ('read' | 'write' | 'delete')[] }", "INVALID_INPUT");
24798
+ }
24799
+ if (rate_limit !== void 0 && rate_limit !== null) {
24800
+ if (typeof rate_limit !== "number" || rate_limit < 1 || !Number.isInteger(rate_limit)) {
24801
+ throw ApiError.badRequest("rate_limit must be a positive integer or null", "INVALID_INPUT");
24802
+ }
24803
+ }
24804
+ if (expires_at !== void 0 && expires_at !== null) {
24805
+ const parsed = new Date(expires_at);
24806
+ if (isNaN(parsed.getTime())) {
24807
+ throw ApiError.badRequest("expires_at must be a valid ISO-8601 date", "INVALID_INPUT");
24808
+ }
24809
+ if (parsed <= /* @__PURE__ */ new Date()) {
24810
+ throw ApiError.badRequest("expires_at must be in the future", "INVALID_INPUT");
24811
+ }
24812
+ }
24813
+ const user = c.get("user");
24814
+ const createdBy = user && typeof user === "object" && "userId" in user ? user.userId : "unknown";
24815
+ const request = {
24816
+ name: name2.trim(),
24817
+ permissions,
24818
+ rate_limit: rate_limit ?? null,
24819
+ expires_at: expires_at ?? null
24820
+ };
24821
+ const keyWithSecret = await store.createApiKey(request, createdBy);
24822
+ return c.json({
24823
+ key: keyWithSecret
24824
+ }, 201);
24825
+ });
24826
+ router.get("/:id", async (c) => {
24827
+ const id = c.req.param("id");
24828
+ const key = await store.getApiKeyById(id);
24829
+ if (!key) {
24830
+ throw ApiError.notFound("API key not found");
24831
+ }
24832
+ return c.json({
24833
+ key
24834
+ });
24835
+ });
24836
+ router.put("/:id", async (c) => {
24837
+ const id = c.req.param("id");
24838
+ const body = await c.req.json();
24839
+ const {
24840
+ name: name2,
24841
+ permissions,
24842
+ rate_limit,
24843
+ expires_at
24844
+ } = body;
24845
+ if (name2 !== void 0) {
24846
+ if (typeof name2 !== "string" || name2.trim().length === 0) {
24847
+ throw ApiError.badRequest("Name must be a non-empty string", "INVALID_INPUT");
24848
+ }
24849
+ }
24850
+ if (permissions !== void 0) {
24851
+ if (!validatePermissions(permissions)) {
24852
+ throw ApiError.badRequest("Permissions must be an array of { collection: string, operations: ('read' | 'write' | 'delete')[] }", "INVALID_INPUT");
24853
+ }
24854
+ }
24855
+ if (rate_limit !== void 0 && rate_limit !== null) {
24856
+ if (typeof rate_limit !== "number" || rate_limit < 1 || !Number.isInteger(rate_limit)) {
24857
+ throw ApiError.badRequest("rate_limit must be a positive integer or null", "INVALID_INPUT");
24858
+ }
24859
+ }
24860
+ if (expires_at !== void 0 && expires_at !== null) {
24861
+ const parsed = new Date(expires_at);
24862
+ if (isNaN(parsed.getTime())) {
24863
+ throw ApiError.badRequest("expires_at must be a valid ISO-8601 date", "INVALID_INPUT");
24864
+ }
24865
+ }
24866
+ const updates = {};
24867
+ if (name2 !== void 0) updates.name = name2.trim();
24868
+ if (permissions !== void 0) updates.permissions = permissions;
24869
+ if (rate_limit !== void 0) updates.rate_limit = rate_limit;
24870
+ if (expires_at !== void 0) updates.expires_at = expires_at;
24871
+ const key = await store.updateApiKey(id, updates);
24872
+ if (!key) {
24873
+ throw ApiError.notFound("API key not found");
24874
+ }
24875
+ return c.json({
24876
+ key
24877
+ });
24878
+ });
24879
+ router.delete("/:id", async (c) => {
24880
+ const id = c.req.param("id");
24881
+ const revoked = await store.revokeApiKey(id);
24882
+ if (!revoked) {
24883
+ throw ApiError.notFound("API key not found or already revoked");
24884
+ }
24885
+ return c.json({
24886
+ success: true
24887
+ });
24888
+ });
24889
+ return router;
24890
+ }
23321
24891
  function createCustomAuthAdapter(options2) {
23322
24892
  const defaultCapabilities = {
23323
24893
  hasBuiltInAuthRoutes: false,
@@ -23352,9 +24922,13 @@ ${credentialScope}
23352
24922
  }
23353
24923
  const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
23354
24924
  __proto__: null,
24925
+ apiKeyKeyGenerator,
23355
24926
  configureJwt,
23356
24927
  createAdapterAuthMiddleware,
23357
24928
  createAdminRoutes,
24929
+ createApiKeyRateLimiter,
24930
+ createApiKeyRoutes,
24931
+ createApiKeyStore,
23358
24932
  createAppleProvider,
23359
24933
  createAuthMiddleware,
23360
24934
  createAuthRoutes,
@@ -23380,11 +24954,15 @@ ${credentialScope}
23380
24954
  getRefreshTokenExpiry,
23381
24955
  hashPassword,
23382
24956
  hashRefreshToken,
24957
+ httpMethodToOperation,
24958
+ isApiKeyToken,
24959
+ isOperationAllowed,
23383
24960
  optionalAuth,
23384
24961
  requireAdmin,
23385
24962
  requireAuth,
23386
- resolveAuthOverrides,
24963
+ resolveAuthHooks,
23387
24964
  strictAuthLimiter,
24965
+ validateApiKey,
23388
24966
  validatePasswordStrength,
23389
24967
  verifyAccessToken,
23390
24968
  verifyPassword
@@ -23909,6 +25487,409 @@ ${credentialScope}
23909
25487
  };
23910
25488
  }
23911
25489
  }
25490
+ let sharpFactory;
25491
+ async function getSharp() {
25492
+ if (!sharpFactory) {
25493
+ try {
25494
+ const mod = await import("sharp");
25495
+ sharpFactory = mod.default;
25496
+ } catch (err) {
25497
+ throw new Error("Failed to load optional 'sharp' dependency for image transformation.");
25498
+ }
25499
+ }
25500
+ if (!sharpFactory) {
25501
+ throw new Error("Failed to load optional 'sharp' dependency for image transformation.");
25502
+ }
25503
+ return sharpFactory;
25504
+ }
25505
+ const MAX_DIMENSION = 4096;
25506
+ const MAX_QUALITY = 100;
25507
+ const MIN_QUALITY = 1;
25508
+ const VALID_FORMATS = /* @__PURE__ */ new Set(["webp", "avif", "jpeg", "png"]);
25509
+ const VALID_FITS = /* @__PURE__ */ new Set(["cover", "contain", "fill", "inside", "outside"]);
25510
+ function parseTransformOptions(query) {
25511
+ const opts = {};
25512
+ let hasTransform = false;
25513
+ if (query.width) {
25514
+ const w2 = parseInt(query.width, 10);
25515
+ if (!Number.isNaN(w2) && w2 > 0) {
25516
+ opts.width = Math.min(w2, MAX_DIMENSION);
25517
+ hasTransform = true;
25518
+ }
25519
+ }
25520
+ if (query.height) {
25521
+ const h2 = parseInt(query.height, 10);
25522
+ if (!Number.isNaN(h2) && h2 > 0) {
25523
+ opts.height = Math.min(h2, MAX_DIMENSION);
25524
+ hasTransform = true;
25525
+ }
25526
+ }
25527
+ if (query.quality) {
25528
+ const q = parseInt(query.quality, 10);
25529
+ if (!Number.isNaN(q)) {
25530
+ opts.quality = Math.min(Math.max(q, MIN_QUALITY), MAX_QUALITY);
25531
+ hasTransform = true;
25532
+ }
25533
+ }
25534
+ if (query.format && VALID_FORMATS.has(query.format)) {
25535
+ opts.format = query.format;
25536
+ hasTransform = true;
25537
+ }
25538
+ if (query.fit && VALID_FITS.has(query.fit)) {
25539
+ opts.fit = query.fit;
25540
+ hasTransform = true;
25541
+ }
25542
+ return hasTransform ? opts : null;
25543
+ }
25544
+ const FORMAT_CONTENT_TYPES = {
25545
+ webp: "image/webp",
25546
+ avif: "image/avif",
25547
+ jpeg: "image/jpeg",
25548
+ png: "image/png"
25549
+ };
25550
+ function isTransformableImage(contentType) {
25551
+ return contentType.startsWith("image/") && !contentType.includes("svg") && !contentType.includes("gif");
25552
+ }
25553
+ async function transformImage(buffer, options2) {
25554
+ const sharp = await getSharp();
25555
+ let pipeline = sharp(buffer);
25556
+ if (options2.width || options2.height) {
25557
+ pipeline = pipeline.resize({
25558
+ width: options2.width,
25559
+ height: options2.height,
25560
+ fit: options2.fit || "cover",
25561
+ withoutEnlargement: true
25562
+ });
25563
+ }
25564
+ const format = options2.format || "webp";
25565
+ const quality = options2.quality || 80;
25566
+ switch (format) {
25567
+ case "webp":
25568
+ pipeline = pipeline.webp({
25569
+ quality
25570
+ });
25571
+ break;
25572
+ case "avif":
25573
+ pipeline = pipeline.avif({
25574
+ quality
25575
+ });
25576
+ break;
25577
+ case "jpeg":
25578
+ pipeline = pipeline.jpeg({
25579
+ quality
25580
+ });
25581
+ break;
25582
+ case "png":
25583
+ pipeline = pipeline.png({
25584
+ quality
25585
+ });
25586
+ break;
25587
+ }
25588
+ const data = await pipeline.toBuffer();
25589
+ return {
25590
+ data,
25591
+ contentType: FORMAT_CONTENT_TYPES[format]
25592
+ };
25593
+ }
25594
+ class TransformCache {
25595
+ cache = /* @__PURE__ */ new Map();
25596
+ maxEntries;
25597
+ maxAgeMs;
25598
+ constructor(maxEntries = 500, maxAgeMs = 36e5) {
25599
+ this.maxEntries = maxEntries;
25600
+ this.maxAgeMs = maxAgeMs;
25601
+ }
25602
+ /** Build a deterministic cache key from file key + transform options. */
25603
+ buildKey(fileKey, options2) {
25604
+ return `${fileKey}::${JSON.stringify(options2)}`;
25605
+ }
25606
+ get(cacheKey) {
25607
+ const entry = this.cache.get(cacheKey);
25608
+ if (!entry) return null;
25609
+ if (Date.now() - entry.timestamp > this.maxAgeMs) {
25610
+ this.cache.delete(cacheKey);
25611
+ return null;
25612
+ }
25613
+ this.cache.delete(cacheKey);
25614
+ this.cache.set(cacheKey, entry);
25615
+ return {
25616
+ data: entry.data,
25617
+ contentType: entry.contentType
25618
+ };
25619
+ }
25620
+ set(cacheKey, data, contentType) {
25621
+ if (this.cache.size >= this.maxEntries) {
25622
+ const oldest = this.cache.keys().next().value;
25623
+ if (oldest !== void 0) this.cache.delete(oldest);
25624
+ }
25625
+ this.cache.set(cacheKey, {
25626
+ data,
25627
+ contentType,
25628
+ timestamp: Date.now()
25629
+ });
25630
+ }
25631
+ }
25632
+ const MAX_UPLOAD_SIZE = 5 * 1024 * 1024 * 1024;
25633
+ const UPLOAD_EXPIRY_MS = 24 * 60 * 60 * 1e3;
25634
+ class TusHandler {
25635
+ constructor(storageBaseDir, storageController) {
25636
+ this.storageController = storageController;
25637
+ this.tusDir = path$3.join(storageBaseDir, ".tus-uploads");
25638
+ }
25639
+ uploads = /* @__PURE__ */ new Map();
25640
+ tusDir;
25641
+ cleanupTimer;
25642
+ /** Ensure the temp directory exists. */
25643
+ async ensureDir() {
25644
+ if (!fs$4.existsSync(this.tusDir)) {
25645
+ await promises.mkdir(this.tusDir, {
25646
+ recursive: true
25647
+ });
25648
+ }
25649
+ }
25650
+ /** Start periodic cleanup of stale uploads. */
25651
+ startCleanup() {
25652
+ if (this.cleanupTimer) return;
25653
+ this.cleanupTimer = setInterval(() => {
25654
+ void this.cleanupStale();
25655
+ }, 6e4);
25656
+ }
25657
+ /** Remove uploads that have been idle for longer than UPLOAD_EXPIRY_MS. */
25658
+ async cleanupStale() {
25659
+ const now = Date.now();
25660
+ for (const [id, upload] of this.uploads) {
25661
+ if (now - upload.createdAt > UPLOAD_EXPIRY_MS && !upload.completed) {
25662
+ try {
25663
+ await promises.unlink(upload.filePath);
25664
+ } catch {
25665
+ }
25666
+ this.uploads.delete(id);
25667
+ }
25668
+ }
25669
+ }
25670
+ // -----------------------------------------------------------------------
25671
+ // TUS Metadata Parsing
25672
+ // -----------------------------------------------------------------------
25673
+ /**
25674
+ * Parse the `Upload-Metadata` header.
25675
+ *
25676
+ * Format: `key base64value,key2 base64value2`
25677
+ */
25678
+ parseMetadata(header) {
25679
+ const metadata = {};
25680
+ if (!header) return metadata;
25681
+ for (const pair of header.split(",")) {
25682
+ const trimmed = pair.trim();
25683
+ const spaceIdx = trimmed.indexOf(" ");
25684
+ if (spaceIdx === -1) {
25685
+ metadata[trimmed] = "";
25686
+ } else {
25687
+ const key = trimmed.substring(0, spaceIdx);
25688
+ const value = Buffer.from(trimmed.substring(spaceIdx + 1), "base64").toString("utf-8");
25689
+ metadata[key] = value;
25690
+ }
25691
+ }
25692
+ return metadata;
25693
+ }
25694
+ // -----------------------------------------------------------------------
25695
+ // Protocol Endpoints
25696
+ // -----------------------------------------------------------------------
25697
+ /** `OPTIONS /tus` — TUS capability discovery. */
25698
+ options() {
25699
+ return new Response(null, {
25700
+ status: 204,
25701
+ headers: {
25702
+ "Tus-Resumable": "1.0.0",
25703
+ "Tus-Version": "1.0.0",
25704
+ "Tus-Extension": "creation,termination",
25705
+ "Tus-Max-Size": String(MAX_UPLOAD_SIZE)
25706
+ }
25707
+ });
25708
+ }
25709
+ /** `POST /tus` — Create a new upload. */
25710
+ async create(c) {
25711
+ await this.ensureDir();
25712
+ const uploadLengthHeader = c.req.header("Upload-Length");
25713
+ if (!uploadLengthHeader) {
25714
+ return c.json({
25715
+ error: "Upload-Length header is required"
25716
+ }, 400);
25717
+ }
25718
+ const uploadLength = parseInt(uploadLengthHeader, 10);
25719
+ if (Number.isNaN(uploadLength) || uploadLength <= 0) {
25720
+ return c.json({
25721
+ error: "Invalid Upload-Length"
25722
+ }, 400);
25723
+ }
25724
+ if (uploadLength > MAX_UPLOAD_SIZE) {
25725
+ return c.json({
25726
+ error: `Upload-Length exceeds maximum of ${MAX_UPLOAD_SIZE} bytes`
25727
+ }, 413);
25728
+ }
25729
+ const metadata = this.parseMetadata(c.req.header("Upload-Metadata") || "");
25730
+ const id = require$$0$5.randomUUID();
25731
+ const filePath = path$3.join(this.tusDir, id);
25732
+ await promises.writeFile(filePath, Buffer.alloc(0));
25733
+ const upload = {
25734
+ id,
25735
+ size: uploadLength,
25736
+ offset: 0,
25737
+ metadata,
25738
+ createdAt: Date.now(),
25739
+ filePath,
25740
+ bucket: metadata.bucket || void 0,
25741
+ key: metadata.key || metadata.filename || void 0,
25742
+ completed: false
25743
+ };
25744
+ this.uploads.set(id, upload);
25745
+ const reqUrl = new URL(c.req.url);
25746
+ const location = `${reqUrl.origin}${reqUrl.pathname}/${id}`;
25747
+ return new Response(null, {
25748
+ status: 201,
25749
+ headers: {
25750
+ Location: location,
25751
+ "Tus-Resumable": "1.0.0",
25752
+ "Upload-Offset": "0"
25753
+ }
25754
+ });
25755
+ }
25756
+ /** `HEAD /tus/:id` — Query upload progress. */
25757
+ head(c, id) {
25758
+ const upload = this.uploads.get(id);
25759
+ if (!upload) {
25760
+ return c.json({
25761
+ error: "Upload not found"
25762
+ }, 404);
25763
+ }
25764
+ return new Response(null, {
25765
+ status: 200,
25766
+ headers: {
25767
+ "Tus-Resumable": "1.0.0",
25768
+ "Upload-Offset": String(upload.offset),
25769
+ "Upload-Length": String(upload.size),
25770
+ "Cache-Control": "no-store"
25771
+ }
25772
+ });
25773
+ }
25774
+ /** `PATCH /tus/:id` — Append data to an upload. */
25775
+ async patch(c, id) {
25776
+ const upload = this.uploads.get(id);
25777
+ if (!upload) {
25778
+ return c.json({
25779
+ error: "Upload not found"
25780
+ }, 404);
25781
+ }
25782
+ if (upload.completed) {
25783
+ return c.json({
25784
+ error: "Upload already completed"
25785
+ }, 400);
25786
+ }
25787
+ const offsetHeader = c.req.header("Upload-Offset");
25788
+ if (!offsetHeader) {
25789
+ return c.json({
25790
+ error: "Upload-Offset header is required"
25791
+ }, 400);
25792
+ }
25793
+ const offset = parseInt(offsetHeader, 10);
25794
+ if (offset !== upload.offset) {
25795
+ return c.json({
25796
+ error: "Offset mismatch"
25797
+ }, 409);
25798
+ }
25799
+ const contentType = c.req.header("Content-Type");
25800
+ if (contentType !== "application/offset+octet-stream") {
25801
+ return c.json({
25802
+ error: "Content-Type must be application/offset+octet-stream"
25803
+ }, 415);
25804
+ }
25805
+ const body = await c.req.arrayBuffer();
25806
+ const chunk = Buffer.from(body);
25807
+ if (upload.offset + chunk.length > upload.size) {
25808
+ return c.json({
25809
+ error: "Chunk exceeds declared Upload-Length"
25810
+ }, 413);
25811
+ }
25812
+ const fh = await promises.open(upload.filePath, "a");
25813
+ try {
25814
+ await fh.write(chunk);
25815
+ } finally {
25816
+ await fh.close();
25817
+ }
25818
+ upload.offset += chunk.length;
25819
+ if (upload.offset >= upload.size) {
25820
+ await this.finalize(upload);
25821
+ }
25822
+ return new Response(null, {
25823
+ status: 204,
25824
+ headers: {
25825
+ "Tus-Resumable": "1.0.0",
25826
+ "Upload-Offset": String(upload.offset)
25827
+ }
25828
+ });
25829
+ }
25830
+ /** `DELETE /tus/:id` — Cancel and remove an upload. */
25831
+ async delete(c, id) {
25832
+ const upload = this.uploads.get(id);
25833
+ if (!upload) {
25834
+ return c.json({
25835
+ error: "Upload not found"
25836
+ }, 404);
25837
+ }
25838
+ try {
25839
+ await promises.unlink(upload.filePath);
25840
+ } catch {
25841
+ }
25842
+ this.uploads.delete(id);
25843
+ return new Response(null, {
25844
+ status: 204,
25845
+ headers: {
25846
+ "Tus-Resumable": "1.0.0"
25847
+ }
25848
+ });
25849
+ }
25850
+ // -----------------------------------------------------------------------
25851
+ // Finalization
25852
+ // -----------------------------------------------------------------------
25853
+ /**
25854
+ * Move a completed upload into the storage controller.
25855
+ */
25856
+ async finalize(upload) {
25857
+ upload.completed = true;
25858
+ if (!this.storageController) {
25859
+ logger.warn("[TUS] Upload completed but no StorageController configured. Temp file remains:", {
25860
+ filePath: upload.filePath
25861
+ });
25862
+ return;
25863
+ }
25864
+ try {
25865
+ const {
25866
+ readFile: readFile2
25867
+ } = await import("fs/promises");
25868
+ const data = await readFile2(upload.filePath);
25869
+ const fileName = upload.key || upload.metadata.filename || upload.id;
25870
+ const mimeType = upload.metadata.contentType || upload.metadata.filetype || "application/octet-stream";
25871
+ const file = new File([data], fileName, {
25872
+ type: mimeType
25873
+ });
25874
+ await this.storageController.putObject({
25875
+ file,
25876
+ key: fileName,
25877
+ bucket: upload.bucket
25878
+ });
25879
+ try {
25880
+ await promises.unlink(upload.filePath);
25881
+ } catch {
25882
+ }
25883
+ this.uploads.delete(upload.id);
25884
+ logger.info(`[TUS] Upload ${upload.id} finalized → ${fileName}`);
25885
+ } catch (err) {
25886
+ logger.error(`[TUS] Failed to finalize upload ${upload.id}`, {
25887
+ error: err
25888
+ });
25889
+ }
25890
+ }
25891
+ }
25892
+ const transformCache = new TransformCache();
23912
25893
  function extractWildcardPath(c) {
23913
25894
  const routePath = c.req.routePath;
23914
25895
  const prefix = routePath.replace("/*", "");
@@ -23972,6 +25953,7 @@ ${credentialScope}
23972
25953
  throw ApiError.notFound("File not found");
23973
25954
  }
23974
25955
  const filePath = decodeURIComponent(rawPath);
25956
+ const transformOpts = parseTransformOptions(c.req.query());
23975
25957
  if (controller.getType() === "local") {
23976
25958
  const localController = controller;
23977
25959
  const {
@@ -23991,8 +25973,19 @@ ${credentialScope}
23991
25973
  } catch {
23992
25974
  }
23993
25975
  }
23994
- c.header("Content-Type", contentType);
23995
25976
  const fileContent = fs__namespace.readFileSync(absolutePath);
25977
+ if (transformOpts && isTransformableImage(contentType)) {
25978
+ const cacheKey = transformCache.buildKey(filePath, transformOpts);
25979
+ let cached = transformCache.get(cacheKey);
25980
+ if (!cached) {
25981
+ cached = await transformImage(Buffer.from(fileContent), transformOpts);
25982
+ transformCache.set(cacheKey, cached.data, cached.contentType);
25983
+ }
25984
+ c.header("Content-Type", cached.contentType);
25985
+ c.header("Cache-Control", "public, max-age=31536000, immutable");
25986
+ return c.body(new Uint8Array(cached.data));
25987
+ }
25988
+ c.header("Content-Type", contentType);
23996
25989
  return c.body(new Uint8Array(fileContent));
23997
25990
  }
23998
25991
  const {
@@ -24003,7 +25996,20 @@ ${credentialScope}
24003
25996
  if (!fileObject) {
24004
25997
  throw ApiError.notFound("File not found");
24005
25998
  }
24006
- c.header("Content-Type", fileObject.type || "application/octet-stream");
25999
+ const remoteContentType = fileObject.type || "application/octet-stream";
26000
+ if (transformOpts && isTransformableImage(remoteContentType)) {
26001
+ const cacheKey = transformCache.buildKey(filePath, transformOpts);
26002
+ let cached = transformCache.get(cacheKey);
26003
+ if (!cached) {
26004
+ const buf2 = Buffer.from(await fileObject.arrayBuffer());
26005
+ cached = await transformImage(buf2, transformOpts);
26006
+ transformCache.set(cacheKey, cached.data, cached.contentType);
26007
+ }
26008
+ c.header("Content-Type", cached.contentType);
26009
+ c.header("Cache-Control", "public, max-age=31536000, immutable");
26010
+ return c.body(new Uint8Array(cached.data));
26011
+ }
26012
+ c.header("Content-Type", remoteContentType);
24007
26013
  c.header("Cache-Control", "public, max-age=3600, immutable");
24008
26014
  const buf = await fileObject.arrayBuffer();
24009
26015
  return c.body(new Uint8Array(buf));
@@ -24099,6 +26105,14 @@ ${credentialScope}
24099
26105
  message: "Folder created"
24100
26106
  }, 201);
24101
26107
  });
26108
+ const tusBaseDir = controller.getType() === "local" ? controller.getBasePath() : process.env.STORAGE_PATH || "./uploads";
26109
+ const tusHandler = new TusHandler(tusBaseDir, controller);
26110
+ tusHandler.startCleanup();
26111
+ router.options("/tus", (_c2) => tusHandler.options());
26112
+ router.post("/tus", writeAuthMiddleware, async (c) => tusHandler.create(c));
26113
+ router.get("/tus/:id", readAuthMiddleware, (c) => tusHandler.head(c, c.req.param("id")));
26114
+ router.patch("/tus/:id", writeAuthMiddleware, async (c) => tusHandler.patch(c, c.req.param("id")));
26115
+ router.delete("/tus/:id", writeAuthMiddleware, async (c) => tusHandler.delete(c, c.req.param("id")));
24102
26116
  return router;
24103
26117
  }
24104
26118
  const DEFAULT_STORAGE_ID = "(default)";
@@ -24322,6 +26336,13 @@ ${credentialScope}
24322
26336
  } catch (e) {
24323
26337
  }
24324
26338
  }
26339
+ const getErrorField = (obj, field) => {
26340
+ const err = obj?.error;
26341
+ if (err && typeof err === "object" && err !== null && field in err) {
26342
+ return err[field];
26343
+ }
26344
+ return obj?.[field];
26345
+ };
24325
26346
  if (res.status === 401 && onUnauthorizedHandler) {
24326
26347
  const retried = await onUnauthorizedHandler();
24327
26348
  if (retried) {
@@ -24355,7 +26376,7 @@ ${credentialScope}
24355
26376
  const method = init?.method || "GET";
24356
26377
  fallbackMessage = `Endpoint not found (${method} ${path2}). This usually means the collection is not registered on the backend, or the frontend API URL configuration (e.g. VITE_API_URL) is missing or pointing to the wrong host.`;
24357
26378
  }
24358
- throw new RebaseApiError(retryRes.status, retryBody?.error?.message || retryBody?.message || fallbackMessage || `Request failed with status ${retryRes.status}`, retryBody?.error?.code || retryBody?.code, retryBody?.error?.details || retryBody?.details);
26379
+ throw new RebaseApiError(retryRes.status, String(getErrorField(retryBody, "message") || fallbackMessage || `Request failed with status ${retryRes.status}`), getErrorField(retryBody, "code"), getErrorField(retryBody, "details"));
24359
26380
  }
24360
26381
  return retryBody;
24361
26382
  }
@@ -24366,7 +26387,7 @@ ${credentialScope}
24366
26387
  const method = init?.method || "GET";
24367
26388
  fallbackMessage = `Endpoint not found (${method} ${path2}). This usually means the collection is not registered on the backend, or the frontend API URL configuration (e.g. VITE_API_URL) is missing or pointing to the wrong host.`;
24368
26389
  }
24369
- throw new RebaseApiError(res.status, body?.error?.message || body?.message || fallbackMessage || `Request failed with status ${res.status}`, body?.error?.code || body?.code, body?.error?.details || body?.details);
26390
+ throw new RebaseApiError(res.status, String(getErrorField(body, "message") || fallbackMessage || `Request failed with status ${res.status}`), getErrorField(body, "code"), getErrorField(body, "details"));
24370
26391
  }
24371
26392
  return body;
24372
26393
  }
@@ -25028,118 +27049,6 @@ ${credentialScope}
25028
27049
  toggleJob
25029
27050
  };
25030
27051
  }
25031
- function mapOperator(op) {
25032
- switch (op) {
25033
- case "==":
25034
- return "eq";
25035
- case "!=":
25036
- return "neq";
25037
- case ">":
25038
- return "gt";
25039
- case ">=":
25040
- return "gte";
25041
- case "<":
25042
- return "lt";
25043
- case "<=":
25044
- return "lte";
25045
- case "array-contains":
25046
- return "cs";
25047
- case "array-contains-any":
25048
- return "csa";
25049
- case "not-in":
25050
- return "nin";
25051
- default:
25052
- return op;
25053
- }
25054
- }
25055
- class QueryBuilder {
25056
- constructor(collection) {
25057
- this.collection = collection;
25058
- }
25059
- params = {
25060
- where: {}
25061
- };
25062
- /**
25063
- * Add a filter condition to your query.
25064
- * @example
25065
- * client.collection('users').where('age', '>=', 18).find()
25066
- */
25067
- where(column, operator, value) {
25068
- if (!this.params.where) {
25069
- this.params.where = {};
25070
- }
25071
- const mappedOp = mapOperator(operator);
25072
- let formattedValue = value;
25073
- if (Array.isArray(value) && ["in", "nin", "cs", "csa"].includes(mappedOp)) {
25074
- formattedValue = `(${value.join(",")})`;
25075
- } else if (value === null) {
25076
- formattedValue = "null";
25077
- }
25078
- this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
25079
- return this;
25080
- }
25081
- /**
25082
- * Order the results by a specific column.
25083
- * @example
25084
- * client.collection('users').orderBy('createdAt', 'desc').find()
25085
- */
25086
- orderBy(column, ascending = "asc") {
25087
- this.params.orderBy = `${column}:${ascending}`;
25088
- return this;
25089
- }
25090
- /**
25091
- * Limit the number of results returned.
25092
- */
25093
- limit(count) {
25094
- this.params.limit = count;
25095
- return this;
25096
- }
25097
- /**
25098
- * Skip the first N results.
25099
- */
25100
- offset(count) {
25101
- this.params.offset = count;
25102
- return this;
25103
- }
25104
- /**
25105
- * Set a free-text search string if supported by the backend.
25106
- */
25107
- search(searchString) {
25108
- this.params.searchString = searchString;
25109
- return this;
25110
- }
25111
- /**
25112
- * Include related entities in the response.
25113
- * Relations will be populated with full entity data instead of just IDs.
25114
- *
25115
- * @param relations - Relation names to include, or "*" for all.
25116
- * @example
25117
- * // Include specific relations
25118
- * client.data.posts.include("tags", "author").find()
25119
- *
25120
- * // Include all relations
25121
- * client.data.posts.include("*").find()
25122
- */
25123
- include(...relations) {
25124
- this.params.include = relations;
25125
- return this;
25126
- }
25127
- /**
25128
- * Execute the find query and return the results.
25129
- */
25130
- async find() {
25131
- return this.collection.find(this.params);
25132
- }
25133
- /**
25134
- * Listen to realtime updates matching this query.
25135
- */
25136
- listen(onUpdate, onError) {
25137
- if (!this.collection.listen) {
25138
- throw new Error("Listen is only available when RebaseClient is configured with a websocketUrl.");
25139
- }
25140
- return this.collection.listen(this.params, onUpdate, onError);
25141
- }
25142
- }
25143
27052
  function rowToEntity(row, slug) {
25144
27053
  return {
25145
27054
  id: row.id,
@@ -25734,7 +27643,7 @@ ${credentialScope}
25734
27643
  const http = require$$0$6;
25735
27644
  const https = require$$1;
25736
27645
  const urllib$2 = require$$0$2;
25737
- const zlib = require$$11;
27646
+ const zlib = require$$3;
25738
27647
  const PassThrough$3 = require$$0$4.PassThrough;
25739
27648
  const Cookies = cookies;
25740
27649
  const packageData$7 = require$$9;
@@ -25969,9 +27878,9 @@ ${credentialScope}
25969
27878
  const util2 = require$$5;
25970
27879
  const fs2 = fs$4;
25971
27880
  const nmfetch2 = fetchExports;
25972
- const dns2 = require$$11;
27881
+ const dns2 = require$$4$1;
25973
27882
  const net2 = require$$0$7;
25974
- const os2 = require$$1$2;
27883
+ const os2 = require$$1$3;
25975
27884
  const DNS_TTL = 5 * 60 * 1e3;
25976
27885
  let networkInterfaces;
25977
27886
  try {
@@ -32117,7 +34026,7 @@ ${credentialScope}
32117
34026
  const packageData$6 = require$$9;
32118
34027
  const MailMessage = mailMessage;
32119
34028
  const net$1 = require$$0$7;
32120
- const dns = require$$11;
34029
+ const dns = require$$4$1;
32121
34030
  const crypto$3 = require$$0$5;
32122
34031
  class Mail extends EventEmitter$5 {
32123
34032
  constructor(transporter, options2, defaults) {
@@ -32553,7 +34462,7 @@ ${credentialScope}
32553
34462
  const EventEmitter$4 = require$$0$9.EventEmitter;
32554
34463
  const net = require$$0$7;
32555
34464
  const tls = require$$1$1;
32556
- const os = require$$1$2;
34465
+ const os = require$$1$3;
32557
34466
  const crypto$2 = require$$0$5;
32558
34467
  const DataStream = dataStream;
32559
34468
  const PassThrough = require$$0$4.PassThrough;
@@ -36741,7 +38650,7 @@ ${credentialScope}
36741
38650
  oauthProviders,
36742
38651
  serviceKey,
36743
38652
  hooks: config.hooks,
36744
- overrides: safeAuthConfig.overrides
38653
+ authHooks: safeAuthConfig.hooks
36745
38654
  });
36746
38655
  }
36747
38656
  logger.info("Authentication initialized");
@@ -36886,7 +38795,7 @@ ${credentialScope}
36886
38795
  oauthProviders,
36887
38796
  serviceKey,
36888
38797
  hooks: config.hooks,
36889
- overrides: safeAuthConfig.overrides
38798
+ authHooks: safeAuthConfig.hooks
36890
38799
  });
36891
38800
  }
36892
38801
  if (authAdapter.createAuthRoutes) {
@@ -36908,6 +38817,21 @@ ${credentialScope}
36908
38817
  }
36909
38818
  }
36910
38819
  }
38820
+ let apiKeyStore;
38821
+ const apiKeyStoreResult = createApiKeyStore(defaultDriver);
38822
+ if (apiKeyStoreResult) {
38823
+ apiKeyStore = apiKeyStoreResult;
38824
+ await apiKeyStore.ensureTable();
38825
+ logger.info("Service API Keys initialized");
38826
+ const apiKeyRoutes = createApiKeyRoutes({
38827
+ store: apiKeyStore,
38828
+ serviceKey
38829
+ });
38830
+ config.app.route(`${basePath}/admin/api-keys`, apiKeyRoutes);
38831
+ logger.info("API key admin routes mounted", {
38832
+ path: `${basePath}/admin/api-keys`
38833
+ });
38834
+ }
36911
38835
  if (config.collectionsDir) {
36912
38836
  if (process.env.NODE_ENV !== "production") {
36913
38837
  const {
@@ -36958,15 +38882,20 @@ ${credentialScope}
36958
38882
  dataRouter.use("/*", createAdapterAuthMiddleware({
36959
38883
  adapter: authAdapter,
36960
38884
  driver: defaultDriver,
36961
- requireAuth: dataRequireAuth
38885
+ requireAuth: dataRequireAuth,
38886
+ apiKeyStore
36962
38887
  }));
36963
38888
  } else {
36964
38889
  dataRouter.use("/*", createAuthMiddleware({
36965
38890
  driver: defaultDriver,
36966
38891
  requireAuth: dataRequireAuth,
36967
- serviceKey
38892
+ serviceKey,
38893
+ apiKeyStore
36968
38894
  }));
36969
38895
  }
38896
+ if (apiKeyStore) {
38897
+ dataRouter.use("/*", createApiKeyRateLimiter());
38898
+ }
36970
38899
  if (historyConfigResult && historyConfigResult.historyService) {
36971
38900
  const historyRoutes = createHistoryRoutes({
36972
38901
  historyService: historyConfigResult.historyService,
@@ -37060,13 +38989,15 @@ ${credentialScope}
37060
38989
  functionsRouter.use("/*", createAdapterAuthMiddleware({
37061
38990
  adapter: authAdapter,
37062
38991
  driver: defaultDriver,
37063
- requireAuth: functionsRequireAuth
38992
+ requireAuth: functionsRequireAuth,
38993
+ apiKeyStore
37064
38994
  }));
37065
38995
  } else {
37066
38996
  functionsRouter.use("/*", createAuthMiddleware({
37067
38997
  driver: defaultDriver,
37068
38998
  requireAuth: functionsRequireAuth,
37069
- serviceKey
38999
+ serviceKey,
39000
+ apiKeyStore
37070
39001
  }));
37071
39002
  }
37072
39003
  const fnRoutes = createFunctionRoutes2(loadedFunctions);
@@ -47065,7 +48996,7 @@ spurious results.`);
47065
48996
  return "undefined";
47066
48997
  }
47067
48998
  if (typeof obj === "string") {
47068
- return `"${obj.replace(/"/g, '\\"')}"`;
48999
+ return JSON.stringify(obj);
47069
49000
  }
47070
49001
  if (typeof obj === "number" || typeof obj === "boolean") {
47071
49002
  return String(obj);
@@ -47096,7 +49027,7 @@ ${indent2}]`;
47096
49027
  const kind = init.getKind();
47097
49028
  const isCode = kind === tsMorph.SyntaxKind.ArrowFunction || kind === tsMorph.SyntaxKind.FunctionExpression || kind === tsMorph.SyntaxKind.Identifier || kind === tsMorph.SyntaxKind.CallExpression || kind === tsMorph.SyntaxKind.JsxElement;
47098
49029
  if (isCode || name2 === "target" || name2 === "callbacks" || name2 === "permissions" || name2 === "securityRules") {
47099
- const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name2) ? name2 : `"${name2}"`;
49030
+ const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name2) ? name2 : JSON.stringify(name2);
47100
49031
  preservedProps.push(`${keyStr}: ${init.getText()}`);
47101
49032
  }
47102
49033
  }
@@ -47106,7 +49037,7 @@ ${indent2}]`;
47106
49037
  }
47107
49038
  if (keys2.length === 0 && preservedProps.length === 0) return "{}";
47108
49039
  const props = keys2.map((key) => {
47109
- const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : `"${key}"`;
49040
+ const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
47110
49041
  let childAstNode;
47111
49042
  if (oldAstNode && typeof record[key] === "object" && record[key] !== null && !Array.isArray(record[key])) {
47112
49043
  const oldProp = oldAstNode.getProperty((p) => "getName" in p && typeof p.getName === "function" && (p.getName() === key || p.getName() === `"${key}"` || p.getName() === `'${key}'`));
@@ -47148,7 +49079,7 @@ ${indent2}}`;
47148
49079
  }
47149
49080
  } else {
47150
49081
  propsObj.addPropertyAssignment({
47151
- name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey : `"${propertyKey}"`,
49082
+ name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey : JSON.stringify(propertyKey),
47152
49083
  initializer: newInitializer
47153
49084
  });
47154
49085
  }
@@ -48219,7 +50150,7 @@ export default ${safeId}Collection;
48219
50150
  const mod = await dynamicImport(fileUrl);
48220
50151
  const exported = mod.default;
48221
50152
  if (!exported) {
48222
- console.warn(`[functions] ${file}: no default export. Skipping.`);
50153
+ logger.warn(`[functions] ${file}: no default export. Skipping.`);
48223
50154
  continue;
48224
50155
  }
48225
50156
  if (isHonoLike(exported)) {
@@ -48228,7 +50159,7 @@ export default ${safeId}Collection;
48228
50159
  name: name2,
48229
50160
  app: exported
48230
50161
  });
48231
- console.log(`⚡ Loaded function route: ${name2}`);
50162
+ logger.info(`⚡ Loaded function route: ${name2}`);
48232
50163
  continue;
48233
50164
  }
48234
50165
  if (typeof exported === "function") {
@@ -48239,20 +50170,20 @@ export default ${safeId}Collection;
48239
50170
  name: name2,
48240
50171
  app: result
48241
50172
  });
48242
- console.log(`⚡ Loaded function route: ${name2}`);
50173
+ logger.info(`⚡ Loaded function route: ${name2}`);
48243
50174
  continue;
48244
50175
  }
48245
50176
  }
48246
50177
  const exportType = typeof exported;
48247
50178
  const keys2 = exported && typeof exported === "object" ? Object.getOwnPropertyNames(Object.getPrototypeOf(exported)).slice(0, 10).join(", ") : "N/A";
48248
- console.warn(`[functions] ${file}: default export is not a Hono app or factory. Skipping.
50179
+ logger.warn(`[functions] ${file}: default export is not a Hono app or factory. Skipping.
48249
50180
  export type: ${exportType}${exported?.constructor?.name ? ` (${exported.constructor.name})` : ""}
48250
50181
  prototype methods: ${keys2}
48251
50182
  Hint: ensure the function exports a Hono app created with the same hono version as the server.
48252
50183
  The loader checks for .fetch() and .routes — any Hono-compatible app will work.`);
48253
50184
  } catch (err) {
48254
50185
  const message = err instanceof Error ? err.message : String(err);
48255
- console.error(`[functions] Failed to load ${file}: ${message}`);
50186
+ logger.error(`[functions] Failed to load ${file}: ${message}`);
48256
50187
  }
48257
50188
  }
48258
50189
  }
@@ -48301,12 +50232,12 @@ export default ${safeId}Collection;
48301
50232
  const mod = await dynamicImport(fileUrl);
48302
50233
  const exported = mod.default;
48303
50234
  if (!exported || typeof exported !== "object") {
48304
- console.warn(`[cron] ${file}: no valid default export. Skipping.`);
50235
+ logger.warn(`[cron] ${file}: no valid default export. Skipping.`);
48305
50236
  continue;
48306
50237
  }
48307
50238
  const def = exported;
48308
50239
  if (typeof def.schedule !== "string" || typeof def.handler !== "function") {
48309
- console.warn(`[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`);
50240
+ logger.warn(`[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`);
48310
50241
  continue;
48311
50242
  }
48312
50243
  const id = path__namespace.basename(file, path__namespace.extname(file));
@@ -48322,10 +50253,10 @@ export default ${safeId}Collection;
48322
50253
  id,
48323
50254
  definition
48324
50255
  });
48325
- console.log(`⏰ Loaded cron job: ${id} (${definition.schedule})`);
50256
+ logger.info(`⏰ Loaded cron job: ${id} (${definition.schedule})`);
48326
50257
  } catch (err) {
48327
50258
  const message = err instanceof Error ? err.message : String(err);
48328
- console.error(`[cron] Failed to load ${file}: ${message}`);
50259
+ logger.error(`[cron] Failed to load ${file}: ${message}`);
48329
50260
  }
48330
50261
  }
48331
50262
  }
@@ -48480,12 +50411,12 @@ export default ${safeId}Collection;
48480
50411
  for (const loaded of loadedJobs) {
48481
50412
  const validation = validateCronExpression(loaded.definition.schedule);
48482
50413
  if (!validation.valid) {
48483
- console.error(`[cron] Rejecting job "${loaded.id}": invalid schedule "${loaded.definition.schedule}" — ${validation.reason}`);
50414
+ logger.error(`[cron] Rejecting job "${loaded.id}": invalid schedule "${loaded.definition.schedule}" — ${validation.reason}`);
48484
50415
  continue;
48485
50416
  }
48486
50417
  const existing = this.jobs.get(loaded.id);
48487
50418
  if (existing) {
48488
- console.warn(`[cron] Duplicate cron job id: "${loaded.id}". Overwriting.`);
50419
+ logger.warn(`[cron] Duplicate cron job id: "${loaded.id}". Overwriting.`);
48489
50420
  this.stopJob(loaded.id);
48490
50421
  }
48491
50422
  const enabled = loaded.definition.enabled !== false;
@@ -48523,7 +50454,9 @@ export default ${safeId}Collection;
48523
50454
  }
48524
50455
  }
48525
50456
  }).catch((err) => {
48526
- console.warn("[cron] Failed to seed job stats from database:", err);
50457
+ logger.warn("[cron] Failed to seed job stats from database", {
50458
+ error: err
50459
+ });
48527
50460
  });
48528
50461
  }
48529
50462
  for (const [id, job] of this.jobs) {
@@ -48531,7 +50464,7 @@ export default ${safeId}Collection;
48531
50464
  this.scheduleNext(id);
48532
50465
  }
48533
50466
  }
48534
- console.log(`⏰ Cron scheduler started with ${this.jobs.size} job(s)`);
50467
+ logger.info(`⏰ Cron scheduler started with ${this.jobs.size} job(s)`);
48535
50468
  }
48536
50469
  /**
48537
50470
  * Stop the scheduler and clear all timers.
@@ -48605,7 +50538,7 @@ export default ${safeId}Collection;
48605
50538
  const job = this.jobs.get(id);
48606
50539
  if (!job) return void 0;
48607
50540
  if (job.executing) {
48608
- console.warn(`[cron] Skipping manual trigger of "${id}" — already executing`);
50541
+ logger.warn(`[cron] Skipping manual trigger of "${id}" — already executing`);
48609
50542
  const logEntry = {
48610
50543
  jobId: id,
48611
50544
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -48649,7 +50582,7 @@ export default ${safeId}Collection;
48649
50582
  const timer = setTimeout(async () => {
48650
50583
  if (!job.enabled || !this.started) return;
48651
50584
  if (job.executing) {
48652
- console.warn(`[cron] Skipping scheduled run of "${id}" — still executing from previous run`);
50585
+ logger.warn(`[cron] Skipping scheduled run of "${id}" — still executing from previous run`);
48653
50586
  this.scheduleNext(id);
48654
50587
  return;
48655
50588
  }
@@ -48663,7 +50596,9 @@ export default ${safeId}Collection;
48663
50596
  }
48664
50597
  job.timerId = timer;
48665
50598
  } catch (err) {
48666
- console.error(`[cron] Failed to schedule "${id}":`, err);
50599
+ logger.error(`[cron] Failed to schedule "${id}"`, {
50600
+ error: err
50601
+ });
48667
50602
  job.state = "error";
48668
50603
  job.lastError = err instanceof Error ? err.message : String(err);
48669
50604
  }
@@ -48748,13 +50683,15 @@ export default ${safeId}Collection;
48748
50683
  }
48749
50684
  if (this.store) {
48750
50685
  this.store.insertLog(logEntry).catch((persistErr) => {
48751
- console.error(`[cron] Failed to persist log for "${job.id}":`, persistErr);
50686
+ logger.error(`[cron] Failed to persist log for "${job.id}"`, {
50687
+ error: persistErr
50688
+ });
48752
50689
  });
48753
50690
  }
48754
50691
  if (success) {
48755
- console.log(`✅ [cron] "${job.id}" completed in ${durationMs}ms`);
50692
+ logger.info(`✅ [cron] "${job.id}" completed in ${durationMs}ms`);
48756
50693
  } else {
48757
- console.error(`❌ [cron] "${job.id}" failed in ${durationMs}ms: ${error2}`);
50694
+ logger.error(`❌ [cron] "${job.id}" failed in ${durationMs}ms: ${error2}`);
48758
50695
  }
48759
50696
  return logEntry;
48760
50697
  }
@@ -48872,7 +50809,7 @@ export default ${safeId}Collection;
48872
50809
  function createCronStore(driver) {
48873
50810
  const admin = driver.admin;
48874
50811
  if (!isSQLAdmin(admin)) {
48875
- console.warn("⚠️ [cron-store] DataDriver does not support SQL admin — cron logs will not be persisted.");
50812
+ logger.warn("⚠️ [cron-store] DataDriver does not support SQL admin — cron logs will not be persisted.");
48876
50813
  return void 0;
48877
50814
  }
48878
50815
  const exec = admin.executeSql.bind(admin);
@@ -48898,10 +50835,12 @@ export default ${safeId}Collection;
48898
50835
  CREATE INDEX IF NOT EXISTS idx_cron_logs_job
48899
50836
  ON ${TABLE}(job_id, started_at DESC)
48900
50837
  `);
48901
- console.log("✅ Cron logs table ready");
50838
+ logger.info("✅ Cron logs table ready");
48902
50839
  } catch (err) {
48903
- console.error("❌ Failed to create cron logs table:", err);
48904
- console.warn("⚠️ Continuing without cron log persistence.");
50840
+ logger.error("❌ Failed to create cron logs table", {
50841
+ error: err
50842
+ });
50843
+ logger.warn("⚠️ Continuing without cron log persistence.");
48905
50844
  }
48906
50845
  },
48907
50846
  async insertLog(entry) {
@@ -48924,7 +50863,9 @@ export default ${safeId}Collection;
48924
50863
  )
48925
50864
  `);
48926
50865
  } catch (err) {
48927
- console.error(`[cron-store] Failed to persist log for "${entry.jobId}":`, err);
50866
+ logger.error(`[cron-store] Failed to persist log for "${entry.jobId}"`, {
50867
+ error: err
50868
+ });
48928
50869
  }
48929
50870
  },
48930
50871
  async fetchLogs(jobId, limit = 50) {
@@ -48938,7 +50879,9 @@ export default ${safeId}Collection;
48938
50879
  `);
48939
50880
  return rows.map(rowToLogEntry);
48940
50881
  } catch (err) {
48941
- console.error(`[cron-store] Failed to fetch logs for "${jobId}":`, err);
50882
+ logger.error(`[cron-store] Failed to fetch logs for "${jobId}"`, {
50883
+ error: err
50884
+ });
48942
50885
  return [];
48943
50886
  }
48944
50887
  },
@@ -48962,7 +50905,9 @@ export default ${safeId}Collection;
48962
50905
  });
48963
50906
  }
48964
50907
  } catch (err) {
48965
- console.error("[cron-store] Failed to fetch job stats:", err);
50908
+ logger.error("[cron-store] Failed to fetch job stats", {
50909
+ error: err
50910
+ });
48966
50911
  }
48967
50912
  return stats;
48968
50913
  }
@@ -48993,8 +50938,8 @@ export default ${safeId}Collection;
48993
50938
  indexFile = "index.html"
48994
50939
  } = config;
48995
50940
  if (!fs__namespace.existsSync(frontendPath)) {
48996
- console.warn(`⚠️ Frontend build path does not exist: ${frontendPath}`);
48997
- console.warn(" SPA serving is disabled. Build your frontend first.");
50941
+ logger.warn(`⚠️ Frontend build path does not exist: ${frontendPath}`);
50942
+ logger.warn(" SPA serving is disabled. Build your frontend first.");
48998
50943
  return;
48999
50944
  }
49000
50945
  app.use("/*", serveStatic.serveStatic({
@@ -49007,13 +50952,13 @@ export default ${safeId}Collection;
49007
50952
  }
49008
50953
  const indexPath = path__namespace.join(frontendPath, indexFile);
49009
50954
  if (!fs__namespace.existsSync(indexPath)) {
49010
- console.warn(`⚠️ Index file not found: ${indexPath}`);
50955
+ logger.warn(`⚠️ Index file not found: ${indexPath}`);
49011
50956
  return next();
49012
50957
  }
49013
50958
  const html = fs__namespace.readFileSync(indexPath, "utf-8");
49014
50959
  return c.html(html);
49015
50960
  });
49016
- console.log(`✅ SPA serving enabled from: ${frontendPath}`);
50961
+ logger.info(`✅ SPA serving enabled from: ${frontendPath}`);
49017
50962
  }
49018
50963
  const MAX_PORT_ATTEMPTS = 20;
49019
50964
  const DEV_PORT_FILENAME = ".rebase-dev-port";
@@ -49021,6 +50966,19 @@ export default ${safeId}Collection;
49021
50966
  const host = options2?.host ?? "0.0.0.0";
49022
50967
  const maxAttempts = options2?.maxAttempts ?? MAX_PORT_ATTEMPTS;
49023
50968
  const portFileDir = options2?.portFileDir;
50969
+ const isProd = process.env.NODE_ENV === "production";
50970
+ if (isProd) {
50971
+ return new Promise((resolve, reject) => {
50972
+ const onError = (err) => {
50973
+ reject(err);
50974
+ };
50975
+ server.once("error", onError);
50976
+ server.listen(startPort, host, () => {
50977
+ server.removeListener("error", onError);
50978
+ resolve(startPort);
50979
+ });
50980
+ });
50981
+ }
49024
50982
  let affinityPort = null;
49025
50983
  if (portFileDir) {
49026
50984
  try {
@@ -49163,6 +51121,8 @@ export default ${safeId}Collection;
49163
51121
  DB_POOL_MAX: stringType().default("20").transform(Number),
49164
51122
  DB_POOL_IDLE_TIMEOUT: stringType().default("30000").transform(Number),
49165
51123
  DB_POOL_CONNECT_TIMEOUT: stringType().default("10000").transform(Number),
51124
+ DATABASE_DIRECT_URL: stringType().url().optional(),
51125
+ DATABASE_READ_URL: stringType().url().optional(),
49166
51126
  FORCE_LOCAL_STORAGE: optionalBoolString,
49167
51127
  STORAGE_TYPE: enumType(["local", "s3"]).default("local"),
49168
51128
  STORAGE_PATH: stringType().optional(),
@@ -49238,8 +51198,11 @@ export default ${safeId}Collection;
49238
51198
  exports2.RestApiGenerator = RestApiGenerator;
49239
51199
  exports2.S3StorageController = S3StorageController;
49240
51200
  exports2.SMTPEmailService = SMTPEmailService;
51201
+ exports2.TransformCache = TransformCache;
51202
+ exports2.TusHandler = TusHandler;
49241
51203
  exports2._resetRebaseMock = _resetRebaseMock;
49242
51204
  exports2._setRebaseMock = _setRebaseMock;
51205
+ exports2.apiKeyKeyGenerator = apiKeyKeyGenerator;
49243
51206
  exports2.authJwt = authJwt;
49244
51207
  exports2.authRoles = authRoles;
49245
51208
  exports2.authUid = authUid;
@@ -49248,6 +51211,9 @@ export default ${safeId}Collection;
49248
51211
  exports2.configureLogLevel = configureLogLevel;
49249
51212
  exports2.createAdapterAuthMiddleware = createAdapterAuthMiddleware;
49250
51213
  exports2.createAdminRoutes = createAdminRoutes;
51214
+ exports2.createApiKeyRateLimiter = createApiKeyRateLimiter;
51215
+ exports2.createApiKeyRoutes = createApiKeyRoutes;
51216
+ exports2.createApiKeyStore = createApiKeyStore;
49251
51217
  exports2.createAppleProvider = createAppleProvider;
49252
51218
  exports2.createAuthMiddleware = createAuthMiddleware;
49253
51219
  exports2.createAuthRoutes = createAuthRoutes;
@@ -49285,27 +51251,35 @@ export default ${safeId}Collection;
49285
51251
  exports2.getWelcomeEmailTemplate = getWelcomeEmailTemplate;
49286
51252
  exports2.hashPassword = hashPassword;
49287
51253
  exports2.hashRefreshToken = hashRefreshToken;
51254
+ exports2.httpMethodToOperation = httpMethodToOperation;
49288
51255
  exports2.initializeRebaseBackend = initializeRebaseBackend;
51256
+ exports2.isApiKeyToken = isApiKeyToken;
49289
51257
  exports2.isAuthAdapter = isAuthAdapter;
49290
51258
  exports2.isDatabaseAdapter = isDatabaseAdapter;
51259
+ exports2.isOperationAllowed = isOperationAllowed;
51260
+ exports2.isTransformableImage = isTransformableImage;
49291
51261
  exports2.listenWithPortRetry = listenWithPortRetry;
49292
51262
  exports2.loadCronJobsFromDirectory = loadCronJobsFromDirectory;
49293
51263
  exports2.loadEnv = loadEnv;
49294
51264
  exports2.loadFunctionsFromDirectory = loadFunctionsFromDirectory;
49295
51265
  exports2.logger = logger;
49296
51266
  exports2.optionalAuth = optionalAuth;
51267
+ exports2.parseTransformOptions = parseTransformOptions;
49297
51268
  exports2.rebase = rebase;
49298
51269
  exports2.requestLogger = requestLogger;
49299
51270
  exports2.requireAdmin = requireAdmin;
49300
51271
  exports2.requireAuth = requireAuth;
49301
51272
  exports2.resetConsole = resetConsole;
49302
- exports2.resolveAuthOverrides = resolveAuthOverrides;
51273
+ exports2.resolveAuthHooks = resolveAuthHooks;
49303
51274
  exports2.serveSPA = serveSPA;
49304
51275
  exports2.strictAuthLimiter = strictAuthLimiter;
51276
+ exports2.transformImage = transformImage;
51277
+ exports2.validateApiKey = validateApiKey;
49305
51278
  exports2.validateCronExpression = validateCronExpression;
49306
51279
  exports2.validatePasswordStrength = validatePasswordStrength;
49307
51280
  exports2.verifyAccessToken = verifyAccessToken;
49308
51281
  exports2.verifyPassword = verifyPassword;
51282
+ exports2.z = z;
49309
51283
  Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
49310
51284
  });
49311
51285
  //# sourceMappingURL=index.umd.js.map