@rebasepro/server-core 0.5.0 → 0.6.1

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 (279) hide show
  1. package/dist/{server-core/src/api → api}/errors.d.ts +5 -2
  2. package/dist/{server-core/src/api → api}/rest/api-generator.d.ts +8 -2
  3. package/dist/{server-core/src/api → api}/types.d.ts +2 -0
  4. package/dist/auth/admin-user-ops.d.ts +79 -0
  5. package/dist/{server-core/src/auth → auth}/api-keys/index.d.ts +2 -2
  6. package/dist/{server-core/src/auth → auth}/auth-hooks.d.ts +37 -9
  7. package/dist/{server-core/src/auth → auth}/builtin-auth-adapter.d.ts +4 -4
  8. package/dist/{server-core/src/auth → auth}/index.d.ts +5 -2
  9. package/dist/auth/mfa-routes.d.ts +6 -0
  10. package/dist/auth/reset-password-admin.d.ts +29 -0
  11. package/dist/auth/session-routes.d.ts +25 -0
  12. package/dist/backend-CIxN4FVm.js +15 -0
  13. package/dist/backend-CIxN4FVm.js.map +1 -0
  14. package/dist/chunk-Dze3rakg.js +42 -0
  15. package/dist/{server-core/src/cron → cron}/cron-scheduler.d.ts +1 -1
  16. package/dist/dist-CZKP-Xz4.js +832 -0
  17. package/dist/dist-CZKP-Xz4.js.map +1 -0
  18. package/dist/env.d.ts +102 -0
  19. package/dist/from-VbwD7xRf.js +3849 -0
  20. package/dist/from-VbwD7xRf.js.map +1 -0
  21. package/dist/{server-core/src/index.d.ts → index.d.ts} +1 -0
  22. package/dist/index.es.js +60872 -50462
  23. package/dist/index.es.js.map +1 -1
  24. package/dist/index.umd.js +73156 -51298
  25. package/dist/index.umd.js.map +1 -1
  26. package/dist/init/docs.d.ts +4 -0
  27. package/dist/init/health.d.ts +2 -0
  28. package/dist/init/middlewares.d.ts +10 -0
  29. package/dist/init/shutdown.d.ts +11 -0
  30. package/dist/init/storage.d.ts +5 -0
  31. package/dist/{server-core/src/init.d.ts → init.d.ts} +4 -4
  32. package/dist/jwt-DHcQRGC3.js +4168 -0
  33. package/dist/jwt-DHcQRGC3.js.map +1 -0
  34. package/dist/logger-BYU66ENZ.js +94 -0
  35. package/dist/logger-BYU66ENZ.js.map +1 -0
  36. package/dist/ms-BeBSuOXN.js +125 -0
  37. package/dist/ms-BeBSuOXN.js.map +1 -0
  38. package/dist/multipart-parser-CedBDOeC.js +299 -0
  39. package/dist/multipart-parser-CedBDOeC.js.map +1 -0
  40. package/dist/src-COaj0G3P.js +1182 -0
  41. package/dist/src-COaj0G3P.js.map +1 -0
  42. package/dist/{server-core/src/storage → storage}/image-transform.d.ts +3 -1
  43. package/dist/utils/request-id.d.ts +4 -0
  44. package/package.json +29 -29
  45. package/src/api/collections_for_test/callbacks_test_collection.ts +7 -6
  46. package/src/api/errors.ts +75 -19
  47. package/src/api/graphql/graphql-schema-generator.ts +15 -10
  48. package/src/api/logs-routes.ts +5 -2
  49. package/src/api/rest/api-generator-count.test.ts +21 -10
  50. package/src/api/rest/api-generator.ts +102 -26
  51. package/src/api/rest/query-parser.ts +18 -12
  52. package/src/api/server.ts +12 -1
  53. package/src/api/types.ts +2 -0
  54. package/src/auth/adapter-middleware.ts +17 -10
  55. package/src/auth/admin-user-ops.ts +236 -0
  56. package/src/auth/api-keys/api-key-middleware.ts +15 -9
  57. package/src/auth/api-keys/api-key-permission-guard.ts +1 -1
  58. package/src/auth/api-keys/api-key-routes.ts +3 -3
  59. package/src/auth/api-keys/api-key-store.ts +14 -8
  60. package/src/auth/api-keys/index.ts +2 -2
  61. package/src/auth/apple-oauth.ts +4 -3
  62. package/src/auth/auth-hooks.ts +50 -10
  63. package/src/auth/bitbucket-oauth.ts +5 -4
  64. package/src/auth/builtin-auth-adapter.ts +60 -39
  65. package/src/auth/custom-auth-adapter.ts +4 -5
  66. package/src/auth/discord-oauth.ts +5 -4
  67. package/src/auth/facebook-oauth.ts +5 -4
  68. package/src/auth/github-oauth.ts +6 -5
  69. package/src/auth/gitlab-oauth.ts +5 -4
  70. package/src/auth/google-oauth.ts +2 -1
  71. package/src/auth/index.ts +6 -2
  72. package/src/auth/jwt.ts +5 -4
  73. package/src/auth/linkedin-oauth.ts +4 -3
  74. package/src/auth/mfa-routes.ts +299 -0
  75. package/src/auth/mfa.ts +2 -1
  76. package/src/auth/microsoft-oauth.ts +5 -4
  77. package/src/auth/middleware.ts +5 -4
  78. package/src/auth/rate-limiter.ts +1 -1
  79. package/src/auth/reset-password-admin.ts +144 -0
  80. package/src/auth/rls-scope.ts +1 -1
  81. package/src/auth/routes.ts +52 -596
  82. package/src/auth/session-routes.ts +341 -0
  83. package/src/auth/slack-oauth.ts +6 -5
  84. package/src/auth/spotify-oauth.ts +5 -4
  85. package/src/auth/twitter-oauth.ts +4 -3
  86. package/src/collections/loader.ts +5 -4
  87. package/src/cron/cron-scheduler.test.ts +23 -11
  88. package/src/cron/cron-scheduler.ts +14 -8
  89. package/src/cron/cron-store.ts +1 -1
  90. package/src/email/smtp-email-service.ts +3 -2
  91. package/src/env.ts +11 -10
  92. package/src/index.ts +1 -0
  93. package/src/init/docs.ts +47 -0
  94. package/src/init/health.ts +37 -0
  95. package/src/init/middlewares.ts +61 -0
  96. package/src/init/shutdown.ts +56 -0
  97. package/src/init/storage.ts +57 -0
  98. package/src/init.ts +85 -279
  99. package/src/serve-spa.ts +12 -5
  100. package/src/services/driver-registry.ts +4 -3
  101. package/src/storage/S3StorageController.ts +1 -2
  102. package/src/storage/image-transform.ts +25 -10
  103. package/src/storage/routes.ts +34 -11
  104. package/src/storage/storage-registry.ts +4 -3
  105. package/src/storage/tus-handler.ts +12 -12
  106. package/src/utils/request-id.ts +40 -0
  107. package/src/utils/request-logger.ts +6 -0
  108. package/test/api-generator.test.ts +90 -2
  109. package/test/api-key-permission-guard.test.ts +24 -12
  110. package/test/auth-routes.test.ts +5 -3
  111. package/test/backend-hooks-data.test.ts +99 -30
  112. package/test/custom-auth-adapter.test.ts +16 -15
  113. package/test/email-templates.test.ts +10 -5
  114. package/test/env.test.ts +10 -10
  115. package/test/function-loader.test.ts +7 -4
  116. package/test/graphql-schema-generator.test.ts +554 -0
  117. package/test/jwt-security.test.ts +1 -1
  118. package/test/query-parser.test.ts +0 -1
  119. package/test/reset-password-admin.test.ts +113 -0
  120. package/test/singleton.test.ts +5 -5
  121. package/test/smtp-email-service.test.ts +21 -21
  122. package/test/webhook-service.test.ts +22 -11
  123. package/tsconfig.json +2 -0
  124. package/tsconfig.prod.json +3 -0
  125. package/vite.config.ts +10 -5
  126. package/dist/common/src/collections/CollectionRegistry.d.ts +0 -56
  127. package/dist/common/src/collections/default-collections.d.ts +0 -9
  128. package/dist/common/src/collections/index.d.ts +0 -2
  129. package/dist/common/src/data/buildRebaseData.d.ts +0 -14
  130. package/dist/common/src/data/query_builder.d.ts +0 -55
  131. package/dist/common/src/index.d.ts +0 -4
  132. package/dist/common/src/util/builders.d.ts +0 -57
  133. package/dist/common/src/util/callbacks.d.ts +0 -6
  134. package/dist/common/src/util/collections.d.ts +0 -11
  135. package/dist/common/src/util/common.d.ts +0 -2
  136. package/dist/common/src/util/conditions.d.ts +0 -26
  137. package/dist/common/src/util/entities.d.ts +0 -58
  138. package/dist/common/src/util/enums.d.ts +0 -3
  139. package/dist/common/src/util/index.d.ts +0 -16
  140. package/dist/common/src/util/navigation_from_path.d.ts +0 -34
  141. package/dist/common/src/util/navigation_utils.d.ts +0 -20
  142. package/dist/common/src/util/parent_references_from_path.d.ts +0 -6
  143. package/dist/common/src/util/paths.d.ts +0 -14
  144. package/dist/common/src/util/permissions.d.ts +0 -14
  145. package/dist/common/src/util/references.d.ts +0 -2
  146. package/dist/common/src/util/relations.d.ts +0 -22
  147. package/dist/common/src/util/resolutions.d.ts +0 -72
  148. package/dist/common/src/util/storage.d.ts +0 -24
  149. package/dist/index-Cr1D21av.js +0 -49
  150. package/dist/index-Cr1D21av.js.map +0 -1
  151. package/dist/server-core/src/auth/admin-routes.d.ts +0 -27
  152. package/dist/server-core/src/env.d.ts +0 -137
  153. package/dist/types/src/controllers/analytics_controller.d.ts +0 -7
  154. package/dist/types/src/controllers/auth.d.ts +0 -104
  155. package/dist/types/src/controllers/client.d.ts +0 -168
  156. package/dist/types/src/controllers/collection_registry.d.ts +0 -46
  157. package/dist/types/src/controllers/customization_controller.d.ts +0 -60
  158. package/dist/types/src/controllers/data.d.ts +0 -207
  159. package/dist/types/src/controllers/data_driver.d.ts +0 -218
  160. package/dist/types/src/controllers/database_admin.d.ts +0 -11
  161. package/dist/types/src/controllers/dialogs_controller.d.ts +0 -36
  162. package/dist/types/src/controllers/effective_role.d.ts +0 -4
  163. package/dist/types/src/controllers/email.d.ts +0 -36
  164. package/dist/types/src/controllers/index.d.ts +0 -18
  165. package/dist/types/src/controllers/local_config_persistence.d.ts +0 -20
  166. package/dist/types/src/controllers/navigation.d.ts +0 -225
  167. package/dist/types/src/controllers/registry.d.ts +0 -63
  168. package/dist/types/src/controllers/side_dialogs_controller.d.ts +0 -67
  169. package/dist/types/src/controllers/side_entity_controller.d.ts +0 -97
  170. package/dist/types/src/controllers/snackbar.d.ts +0 -24
  171. package/dist/types/src/controllers/storage.d.ts +0 -171
  172. package/dist/types/src/index.d.ts +0 -4
  173. package/dist/types/src/rebase_context.d.ts +0 -122
  174. package/dist/types/src/types/auth_adapter.d.ts +0 -301
  175. package/dist/types/src/types/backend.d.ts +0 -571
  176. package/dist/types/src/types/backend_hooks.d.ts +0 -172
  177. package/dist/types/src/types/builders.d.ts +0 -15
  178. package/dist/types/src/types/chips.d.ts +0 -5
  179. package/dist/types/src/types/collections.d.ts +0 -961
  180. package/dist/types/src/types/component_ref.d.ts +0 -47
  181. package/dist/types/src/types/cron.d.ts +0 -102
  182. package/dist/types/src/types/data_source.d.ts +0 -64
  183. package/dist/types/src/types/database_adapter.d.ts +0 -94
  184. package/dist/types/src/types/entities.d.ts +0 -145
  185. package/dist/types/src/types/entity_actions.d.ts +0 -104
  186. package/dist/types/src/types/entity_callbacks.d.ts +0 -173
  187. package/dist/types/src/types/entity_link_builder.d.ts +0 -7
  188. package/dist/types/src/types/entity_overrides.d.ts +0 -10
  189. package/dist/types/src/types/entity_views.d.ts +0 -87
  190. package/dist/types/src/types/export_import.d.ts +0 -21
  191. package/dist/types/src/types/formex.d.ts +0 -40
  192. package/dist/types/src/types/index.d.ts +0 -28
  193. package/dist/types/src/types/locales.d.ts +0 -4
  194. package/dist/types/src/types/modify_collections.d.ts +0 -5
  195. package/dist/types/src/types/plugins.d.ts +0 -282
  196. package/dist/types/src/types/properties.d.ts +0 -1173
  197. package/dist/types/src/types/property_config.d.ts +0 -74
  198. package/dist/types/src/types/relations.d.ts +0 -336
  199. package/dist/types/src/types/slots.d.ts +0 -262
  200. package/dist/types/src/types/translations.d.ts +0 -900
  201. package/dist/types/src/types/user_management_delegate.d.ts +0 -86
  202. package/dist/types/src/types/websockets.d.ts +0 -78
  203. package/dist/types/src/users/index.d.ts +0 -1
  204. package/dist/types/src/users/user.d.ts +0 -50
  205. package/src/auth/admin-routes.ts +0 -534
  206. package/test/admin-routes.test.ts +0 -544
  207. package/test/backend-hooks-admin.test.ts +0 -388
  208. /package/dist/{server-core/src/api → api}/ast-schema-editor.d.ts +0 -0
  209. /package/dist/{server-core/src/api → api}/collections_for_test/callbacks_test_collection.d.ts +0 -0
  210. /package/dist/{server-core/src/api → api}/graphql/graphql-schema-generator.d.ts +0 -0
  211. /package/dist/{server-core/src/api → api}/graphql/index.d.ts +0 -0
  212. /package/dist/{server-core/src/api → api}/index.d.ts +0 -0
  213. /package/dist/{server-core/src/api → api}/logs-routes.d.ts +0 -0
  214. /package/dist/{server-core/src/api → api}/openapi-generator.d.ts +0 -0
  215. /package/dist/{server-core/src/api → api}/rest/index.d.ts +0 -0
  216. /package/dist/{server-core/src/api → api}/rest/query-parser.d.ts +0 -0
  217. /package/dist/{server-core/src/api → api}/schema-editor-routes.d.ts +0 -0
  218. /package/dist/{server-core/src/api → api}/server.d.ts +0 -0
  219. /package/dist/{server-core/src/auth → auth}/adapter-middleware.d.ts +0 -0
  220. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-middleware.d.ts +0 -0
  221. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-permission-guard.d.ts +0 -0
  222. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-routes.d.ts +0 -0
  223. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-store.d.ts +0 -0
  224. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-types.d.ts +0 -0
  225. /package/dist/{server-core/src/auth → auth}/apple-oauth.d.ts +0 -0
  226. /package/dist/{server-core/src/auth → auth}/bitbucket-oauth.d.ts +0 -0
  227. /package/dist/{server-core/src/auth → auth}/crypto-utils.d.ts +0 -0
  228. /package/dist/{server-core/src/auth → auth}/custom-auth-adapter.d.ts +0 -0
  229. /package/dist/{server-core/src/auth → auth}/discord-oauth.d.ts +0 -0
  230. /package/dist/{server-core/src/auth → auth}/facebook-oauth.d.ts +0 -0
  231. /package/dist/{server-core/src/auth → auth}/github-oauth.d.ts +0 -0
  232. /package/dist/{server-core/src/auth → auth}/gitlab-oauth.d.ts +0 -0
  233. /package/dist/{server-core/src/auth → auth}/google-oauth.d.ts +0 -0
  234. /package/dist/{server-core/src/auth → auth}/interfaces.d.ts +0 -0
  235. /package/dist/{server-core/src/auth → auth}/jwt.d.ts +0 -0
  236. /package/dist/{server-core/src/auth → auth}/linkedin-oauth.d.ts +0 -0
  237. /package/dist/{server-core/src/auth → auth}/mfa.d.ts +0 -0
  238. /package/dist/{server-core/src/auth → auth}/microsoft-oauth.d.ts +0 -0
  239. /package/dist/{server-core/src/auth → auth}/middleware.d.ts +0 -0
  240. /package/dist/{server-core/src/auth → auth}/password.d.ts +0 -0
  241. /package/dist/{server-core/src/auth → auth}/rate-limiter.d.ts +0 -0
  242. /package/dist/{server-core/src/auth → auth}/rls-scope.d.ts +0 -0
  243. /package/dist/{server-core/src/auth → auth}/routes.d.ts +0 -0
  244. /package/dist/{server-core/src/auth → auth}/slack-oauth.d.ts +0 -0
  245. /package/dist/{server-core/src/auth → auth}/spotify-oauth.d.ts +0 -0
  246. /package/dist/{server-core/src/auth → auth}/twitter-oauth.d.ts +0 -0
  247. /package/dist/{server-core/src/collections → collections}/BackendCollectionRegistry.d.ts +0 -0
  248. /package/dist/{server-core/src/collections → collections}/loader.d.ts +0 -0
  249. /package/dist/{server-core/src/cron → cron}/cron-loader.d.ts +0 -0
  250. /package/dist/{server-core/src/cron → cron}/cron-routes.d.ts +0 -0
  251. /package/dist/{server-core/src/cron → cron}/cron-store.d.ts +0 -0
  252. /package/dist/{server-core/src/cron → cron}/index.d.ts +0 -0
  253. /package/dist/{server-core/src/db → db}/interfaces.d.ts +0 -0
  254. /package/dist/{server-core/src/email → email}/index.d.ts +0 -0
  255. /package/dist/{server-core/src/email → email}/smtp-email-service.d.ts +0 -0
  256. /package/dist/{server-core/src/email → email}/templates.d.ts +0 -0
  257. /package/dist/{server-core/src/email → email}/types.d.ts +0 -0
  258. /package/dist/{server-core/src/functions → functions}/function-loader.d.ts +0 -0
  259. /package/dist/{server-core/src/functions → functions}/function-routes.d.ts +0 -0
  260. /package/dist/{server-core/src/functions → functions}/index.d.ts +0 -0
  261. /package/dist/{server-core/src/history → history}/history-routes.d.ts +0 -0
  262. /package/dist/{server-core/src/history → history}/index.d.ts +0 -0
  263. /package/dist/{server-core/src/serve-spa.d.ts → serve-spa.d.ts} +0 -0
  264. /package/dist/{server-core/src/services → services}/driver-registry.d.ts +0 -0
  265. /package/dist/{server-core/src/services → services}/webhook-service.d.ts +0 -0
  266. /package/dist/{server-core/src/singleton.d.ts → singleton.d.ts} +0 -0
  267. /package/dist/{server-core/src/storage → storage}/LocalStorageController.d.ts +0 -0
  268. /package/dist/{server-core/src/storage → storage}/S3StorageController.d.ts +0 -0
  269. /package/dist/{server-core/src/storage → storage}/index.d.ts +0 -0
  270. /package/dist/{server-core/src/storage → storage}/routes.d.ts +0 -0
  271. /package/dist/{server-core/src/storage → storage}/storage-registry.d.ts +0 -0
  272. /package/dist/{server-core/src/storage → storage}/tus-handler.d.ts +0 -0
  273. /package/dist/{server-core/src/storage → storage}/types.d.ts +0 -0
  274. /package/dist/{server-core/src/types → types}/index.d.ts +0 -0
  275. /package/dist/{server-core/src/utils → utils}/dev-port.d.ts +0 -0
  276. /package/dist/{server-core/src/utils → utils}/logger.d.ts +0 -0
  277. /package/dist/{server-core/src/utils → utils}/logging.d.ts +0 -0
  278. /package/dist/{server-core/src/utils → utils}/request-logger.d.ts +0 -0
  279. /package/dist/{server-core/src/utils → utils}/sql.d.ts +0 -0
@@ -184,8 +184,9 @@ export class GraphQLSchemaGenerator {
184
184
  id: { type: new GraphQLNonNull(GraphQLString) }
185
185
  },
186
186
  resolve: async (_, args, context: unknown) => {
187
- const ctx = context as { driver: DataDriver };
188
- const ds = ctx.driver || this.driver;
187
+ const ctx = context as { driver?: DataDriver } | undefined;
188
+ const ds = ctx?.driver;
189
+ if (!ds) throw new Error("Scoped driver not available");
189
190
  const entity = await ds.fetchEntity({
190
191
  path: collection.slug,
191
192
  entityId: args.id,
@@ -207,8 +208,9 @@ defaultValue: 0 },
207
208
  orderBy: { type: GraphQLString }
208
209
  },
209
210
  resolve: async (_, args, context: unknown) => {
210
- const ctx = context as { driver: DataDriver };
211
- const ds = ctx.driver || this.driver;
211
+ const ctx = context as { driver?: DataDriver } | undefined;
212
+ const ds = ctx?.driver;
213
+ if (!ds) throw new Error("Scoped driver not available");
212
214
  let filter: FetchCollectionProps["filter"] | undefined;
213
215
  if (args.where) {
214
216
  try {
@@ -259,8 +261,9 @@ defaultValue: 0 },
259
261
  input: { type: new GraphQLNonNull(inputType) }
260
262
  },
261
263
  resolve: async (_, args, context: unknown) => {
262
- const ctx = context as { driver: DataDriver };
263
- const ds = ctx.driver || this.driver;
264
+ const ctx = context as { driver?: DataDriver } | undefined;
265
+ const ds = ctx?.driver;
266
+ if (!ds) throw new Error("Scoped driver not available");
264
267
  const path = collection.slug;
265
268
  const entity = await ds.saveEntity({
266
269
  path,
@@ -280,8 +283,9 @@ defaultValue: 0 },
280
283
  input: { type: new GraphQLNonNull(inputType) }
281
284
  },
282
285
  resolve: async (_, args, context: unknown) => {
283
- const ctx = context as { driver: DataDriver };
284
- const ds = ctx.driver || this.driver;
286
+ const ctx = context as { driver?: DataDriver } | undefined;
287
+ const ds = ctx?.driver;
288
+ if (!ds) throw new Error("Scoped driver not available");
285
289
  const entity = await ds.saveEntity({
286
290
  path: collection.slug,
287
291
  entityId: args.id,
@@ -301,8 +305,9 @@ defaultValue: 0 },
301
305
  },
302
306
  resolve: async (_, args, context: unknown) => {
303
307
  try {
304
- const ctx = context as { driver: DataDriver };
305
- const ds = ctx.driver || this.driver;
308
+ const ctx = context as { driver?: DataDriver } | undefined;
309
+ const ds = ctx?.driver;
310
+ if (!ds) throw new Error("Scoped driver not available");
306
311
  const existingEntity = await ds.fetchEntity({
307
312
  path: collection.slug,
308
313
  entityId: args.id,
@@ -22,7 +22,8 @@ class LogRingBuffer {
22
22
 
23
23
  push(entry: Omit<LogEntry, "id">): void {
24
24
  const id = `log_${++this.idCounter}`;
25
- this.buffer.push({ ...entry, id });
25
+ this.buffer.push({ ...entry,
26
+ id });
26
27
  if (this.buffer.length > this.maxSize) {
27
28
  this.buffer.shift();
28
29
  }
@@ -95,11 +96,13 @@ export function logMiddleware(): MiddlewareHandler<HonoEnv> {
95
96
  const start = Date.now();
96
97
  await next();
97
98
  const duration = Date.now() - start;
99
+ const reqId = c.get("requestId");
98
100
  addLog("info", "api", `${c.req.method} ${c.req.path} ${c.res.status} ${duration}ms`, {
99
101
  method: c.req.method,
100
102
  path: c.req.path,
101
103
  status: c.res.status,
102
- duration
104
+ duration,
105
+ ...(reqId && { requestId: reqId })
103
106
  });
104
107
  };
105
108
  }
@@ -1,4 +1,5 @@
1
1
  import { jest } from "@jest/globals";
2
+ import { Hono } from "hono";
2
3
  import { RestApiGenerator } from "./api-generator";
3
4
  import type { DataDriver, Entity, EntityCollection, FetchCollectionProps } from "@rebasepro/types";
4
5
 
@@ -27,6 +28,21 @@ function createTestCollection(slug: string): EntityCollection {
27
28
  } as unknown as EntityCollection;
28
29
  }
29
30
 
31
+ /**
32
+ * Wraps generated routes in a parent Hono app that injects the driver
33
+ * into context — replicating what auth middleware does in production.
34
+ */
35
+ function createApp(collections: EntityCollection[], driver: DataDriver): Hono {
36
+ const parent = new Hono();
37
+ parent.use("/*", async (c, next) => {
38
+ c.set("driver", driver);
39
+ await next();
40
+ });
41
+ const generator = new RestApiGenerator(collections, driver);
42
+ parent.route("/", generator.generateRoutes());
43
+ return parent;
44
+ }
45
+
30
46
  describe("RestApiGenerator - Count Endpoint", () => {
31
47
  let driver: DataDriver;
32
48
  let collection: EntityCollection;
@@ -39,8 +55,7 @@ describe("RestApiGenerator - Count Endpoint", () => {
39
55
  });
40
56
 
41
57
  it("GET /products/count should return a count object", async () => {
42
- const generator = new RestApiGenerator([collection], driver);
43
- const app = generator.generateRoutes();
58
+ const app = createApp([collection], driver);
44
59
 
45
60
  const res = await app.request("/products/count");
46
61
  expect(res.status).toBe(200);
@@ -50,8 +65,7 @@ describe("RestApiGenerator - Count Endpoint", () => {
50
65
  });
51
66
 
52
67
  it("should pass filters to countEntities driver", async () => {
53
- const generator = new RestApiGenerator([collection], driver);
54
- const app = generator.generateRoutes();
68
+ const app = createApp([collection], driver);
55
69
 
56
70
  const res = await app.request("/products/count?status=eq.active");
57
71
  expect(res.status).toBe(200);
@@ -67,8 +81,7 @@ describe("RestApiGenerator - Count Endpoint", () => {
67
81
  });
68
82
 
69
83
  it("should pass searchString to countEntities driver", async () => {
70
- const generator = new RestApiGenerator([collection], driver);
71
- const app = generator.generateRoutes();
84
+ const app = createApp([collection], driver);
72
85
 
73
86
  const res = await app.request("/products/count?searchString=widget");
74
87
  expect(res.status).toBe(200);
@@ -83,8 +96,7 @@ describe("RestApiGenerator - Count Endpoint", () => {
83
96
 
84
97
  it("should return 0 when countEntities is not available on driver", async () => {
85
98
  const driverWithoutCount = createMockDriver({ countEntities: undefined });
86
- const generator = new RestApiGenerator([collection], driverWithoutCount);
87
- const app = generator.generateRoutes();
99
+ const app = createApp([collection], driverWithoutCount);
88
100
 
89
101
  const res = await app.request("/products/count");
90
102
  expect(res.status).toBe(200);
@@ -100,8 +112,7 @@ describe("RestApiGenerator - Count Endpoint", () => {
100
112
  countEntities: jest.fn<NonNullable<DataDriver["countEntities"]>>().mockResolvedValue(99),
101
113
  fetchEntity: fetchEntity as unknown as DataDriver["fetchEntity"]
102
114
  });
103
- const generator = new RestApiGenerator([collection], driverCustom);
104
- const app = generator.generateRoutes();
115
+ const app = createApp([collection], driverCustom);
105
116
 
106
117
  const res = await app.request("/products/count");
107
118
  expect(res.status).toBe(200);
@@ -1,10 +1,11 @@
1
1
  import { Hono } from "hono";
2
- import { DataDriver, Entity, EntityCollection, FetchCollectionProps, DataHooks, BackendHookContext, RestFetchService } from "@rebasepro/types";
2
+ import { AuthAdapter, DataDriver, Entity, EntityCollection, FetchCollectionProps, DataHooks, BackendHookContext, RestFetchService } from "@rebasepro/types";
3
3
  import { QueryOptions, HonoEnv } from "../types";
4
4
  import { ApiError, isRebaseApiError } from "../errors";
5
5
  import { parseQueryOptions } from "./query-parser";
6
6
  import { httpMethodToOperation, isOperationAllowed } from "../../auth/api-keys/api-key-permission-guard";
7
7
  import type { ApiKeyMasked } from "../../auth/api-keys/api-key-types";
8
+ import { logger } from "../../utils/logger";
8
9
 
9
10
 
10
11
  /**
@@ -16,11 +17,18 @@ export class RestApiGenerator {
16
17
  private router: Hono<HonoEnv>;
17
18
  private driver: DataDriver;
18
19
  private dataHooks?: DataHooks;
19
-
20
- constructor(collections: EntityCollection[], driver: DataDriver, dataHooks?: DataHooks) {
20
+ private authAdapter?: AuthAdapter;
21
+
22
+ constructor(
23
+ collections: EntityCollection[],
24
+ driver: DataDriver,
25
+ dataHooks?: DataHooks,
26
+ authAdapter?: AuthAdapter
27
+ ) {
21
28
  this.collections = collections;
22
29
  this.driver = driver;
23
30
  this.dataHooks = dataHooks;
31
+ this.authAdapter = authAdapter;
24
32
  this.router = new Hono<HonoEnv>();
25
33
  }
26
34
 
@@ -28,7 +36,8 @@ export class RestApiGenerator {
28
36
  private buildHookContext(c: { get: (key: string) => unknown }, method: BackendHookContext["method"]): BackendHookContext {
29
37
  const user = c.get("user") as { userId: string; roles?: string[] } | undefined;
30
38
  return {
31
- requestUser: user ? { userId: user.userId, roles: user.roles ?? [] } : undefined,
39
+ requestUser: user ? { userId: user.userId,
40
+ roles: user.roles ?? [] } : undefined,
32
41
  method
33
42
  };
34
43
  }
@@ -56,7 +65,7 @@ export class RestApiGenerator {
56
65
  */
57
66
  private enforceApiKeyPermission(
58
67
  c: { get: (key: string) => unknown; req: { method: string } },
59
- collectionSlug: string,
68
+ collectionSlug: string
60
69
  ): void {
61
70
  const apiKey = c.get("apiKey") as ApiKeyMasked | undefined;
62
71
  if (!apiKey) return; // Not an API key request — skip
@@ -65,11 +74,21 @@ export class RestApiGenerator {
65
74
  if (!isOperationAllowed(apiKey.permissions, collectionSlug, operation)) {
66
75
  throw ApiError.forbidden(
67
76
  `API key does not have "${operation}" permission for collection "${collectionSlug}"`,
68
- "API_KEY_FORBIDDEN",
77
+ "API_KEY_FORBIDDEN"
69
78
  );
70
79
  }
71
80
  }
72
81
 
82
+ /**
83
+ * Get the request-scoped driver. Throws if none is set — never falls
84
+ * back to the unscoped `this.driver` to avoid bypassing RLS/auth.
85
+ */
86
+ private getScopedDriver(c: { get: (key: string) => unknown }): DataDriver {
87
+ const driver = c.get("driver") as DataDriver | undefined;
88
+ if (!driver) throw ApiError.internal("Scoped driver not available");
89
+ return driver;
90
+ }
91
+
73
92
  /**
74
93
  * Get the typed RestFetchService from a driver if it exposes one (for include support).
75
94
  */
@@ -90,7 +109,7 @@ export class RestApiGenerator {
90
109
  const queryDict = c.req.queries();
91
110
  const queryOptions = parseQueryOptions(queryDict);
92
111
  const searchString = Array.isArray(queryDict.searchString) ? queryDict.searchString[queryDict.searchString.length - 1] : undefined;
93
- const driver = c.get("driver") || this.driver;
112
+ const driver = this.getScopedDriver(c);
94
113
 
95
114
  const total = await this.countRawEntities(driver, resolvedCollection, queryOptions, searchString);
96
115
  return c.json({ count: total });
@@ -103,7 +122,7 @@ export class RestApiGenerator {
103
122
  const queryOptions = parseQueryOptions(queryDict);
104
123
  const searchString = Array.isArray(queryDict.searchString) ? queryDict.searchString[queryDict.searchString.length - 1] : undefined;
105
124
 
106
- const driver = c.get("driver") || this.driver;
125
+ const driver = this.getScopedDriver(c);
107
126
  const fetchService = this.getFetchService(driver);
108
127
  const hookCtx = this.buildHookContext(c, "GET");
109
128
 
@@ -163,7 +182,7 @@ export class RestApiGenerator {
163
182
  const id = c.req.param("id");
164
183
  const queryDict = c.req.queries();
165
184
  const queryOptions = parseQueryOptions(queryDict);
166
- const driver = c.get("driver") || this.driver;
185
+ const driver = this.getScopedDriver(c);
167
186
  const fetchService = this.getFetchService(driver);
168
187
  const hookCtx = this.buildHookContext(c, "GET");
169
188
 
@@ -207,7 +226,7 @@ export class RestApiGenerator {
207
226
  this.router.post(basePath, async (c) => {
208
227
  try {
209
228
  this.enforceApiKeyPermission(c, collection.slug);
210
- const driver = c.get("driver") || this.driver;
229
+ const driver = this.getScopedDriver(c);
211
230
  const path = collection.slug;
212
231
  const hookCtx = this.buildHookContext(c, "POST");
213
232
 
@@ -217,6 +236,46 @@ export class RestApiGenerator {
217
236
  body = await this.dataHooks.beforeSave(path, body, undefined, hookCtx);
218
237
  }
219
238
 
239
+ const isAuth = collection.auth;
240
+ const isAuthCollection = isAuth === true || (isAuth && typeof isAuth === "object" && isAuth.enabled === true);
241
+
242
+ if (isAuthCollection && this.authAdapter?.prepareUserCreation) {
243
+ const collectionAuthConfig = typeof isAuth === "object" ? isAuth : undefined;
244
+ const prepared = await this.authAdapter.prepareUserCreation(body, collectionAuthConfig);
245
+
246
+ const entity = await driver.saveEntity({
247
+ path,
248
+ values: prepared.values,
249
+ collection: resolvedCollection,
250
+ status: "new"
251
+ });
252
+
253
+ const result = prepared.hookHandledEmail
254
+ ? { temporaryPassword: prepared.clearPassword,
255
+ invitationSent: prepared.invitationSent }
256
+ : this.authAdapter.finalizeUserCreation
257
+ ? await this.authAdapter.finalizeUserCreation(
258
+ { id: entity.id as string,
259
+ values: entity.values as Record<string, unknown> },
260
+ prepared.clearPassword
261
+ )
262
+ : { invitationSent: false };
263
+
264
+ const response = this.formatResponse(entity) as Record<string, unknown>;
265
+
266
+ if (this.dataHooks?.afterSave) {
267
+ Promise.resolve(this.dataHooks.afterSave(path, response, hookCtx)).catch(err => {
268
+ logger.error("[BackendHooks] data.afterSave error", { error: err instanceof Error ? err.message : err });
269
+ });
270
+ }
271
+
272
+ return c.json({
273
+ ...response,
274
+ invitationSent: result.invitationSent,
275
+ ...(result.temporaryPassword ? { temporaryPassword: result.temporaryPassword } : {})
276
+ }, 201);
277
+ }
278
+
220
279
  const entity = await driver.saveEntity({
221
280
  path,
222
281
  values: body,
@@ -228,14 +287,23 @@ export class RestApiGenerator {
228
287
 
229
288
  if (this.dataHooks?.afterSave) {
230
289
  Promise.resolve(this.dataHooks.afterSave(path, response as Record<string, unknown>, hookCtx)).catch(err => {
231
- console.error("[BackendHooks] data.afterSave error:", err instanceof Error ? err.message : err);
290
+ logger.error("[BackendHooks] data.afterSave error", { error: err instanceof Error ? err.message : err });
232
291
  });
233
292
  }
234
293
 
235
294
  return c.json(response, 201);
236
295
  } catch (error) {
237
296
  if (isRebaseApiError(error) && !error.code) {
238
- error.code = "BAD_REQUEST";
297
+ // Only classify as BAD_REQUEST if it's an operational error
298
+ // (e.g. validation, DB constraints). Runtime bugs like TypeError,
299
+ // RangeError etc. should remain as 500 INTERNAL_ERROR.
300
+ const isRuntimeBug = error instanceof TypeError
301
+ || error instanceof RangeError
302
+ || error instanceof SyntaxError
303
+ || error instanceof ReferenceError;
304
+ if (!isRuntimeBug) {
305
+ error.code = "BAD_REQUEST";
306
+ }
239
307
  }
240
308
  throw error;
241
309
  }
@@ -246,7 +314,7 @@ export class RestApiGenerator {
246
314
  try {
247
315
  this.enforceApiKeyPermission(c, collection.slug);
248
316
  const id = c.req.param("id");
249
- const driver = c.get("driver") || this.driver;
317
+ const driver = this.getScopedDriver(c);
250
318
  const hookCtx = this.buildHookContext(c, "PUT");
251
319
 
252
320
  const existingEntity = await driver.fetchEntity({
@@ -277,14 +345,22 @@ export class RestApiGenerator {
277
345
 
278
346
  if (this.dataHooks?.afterSave) {
279
347
  Promise.resolve(this.dataHooks.afterSave(collection.slug, response as Record<string, unknown>, hookCtx)).catch(err => {
280
- console.error("[BackendHooks] data.afterSave error:", err instanceof Error ? err.message : err);
348
+ logger.error("[BackendHooks] data.afterSave error", { error: err instanceof Error ? err.message : err });
281
349
  });
282
350
  }
283
351
 
284
352
  return c.json(response);
285
353
  } catch (error) {
286
354
  if (isRebaseApiError(error) && !error.code) {
287
- error.code = "BAD_REQUEST";
355
+ // Only classify as BAD_REQUEST if it's an operational error.
356
+ // Runtime bugs (TypeError, RangeError, etc.) stay as 500.
357
+ const isRuntimeBug = error instanceof TypeError
358
+ || error instanceof RangeError
359
+ || error instanceof SyntaxError
360
+ || error instanceof ReferenceError;
361
+ if (!isRuntimeBug) {
362
+ error.code = "BAD_REQUEST";
363
+ }
288
364
  }
289
365
  throw error;
290
366
  }
@@ -294,7 +370,7 @@ export class RestApiGenerator {
294
370
  this.router.delete(`${basePath}/:id`, async (c) => {
295
371
  this.enforceApiKeyPermission(c, collection.slug);
296
372
  const id = c.req.param("id");
297
- const driver = c.get("driver") || this.driver;
373
+ const driver = this.getScopedDriver(c);
298
374
  const hookCtx = this.buildHookContext(c, "DELETE");
299
375
 
300
376
  const existingEntity = await driver.fetchEntity({
@@ -318,7 +394,7 @@ export class RestApiGenerator {
318
394
 
319
395
  if (this.dataHooks?.afterDelete) {
320
396
  Promise.resolve(this.dataHooks.afterDelete(collection.slug, String(id), hookCtx)).catch(err => {
321
- console.error("[BackendHooks] data.afterDelete error:", err instanceof Error ? err.message : err);
397
+ logger.error("[BackendHooks] data.afterDelete error", { error: err instanceof Error ? err.message : err });
322
398
  });
323
399
  }
324
400
 
@@ -384,7 +460,7 @@ entityId };
384
460
  const parsed = parseSubPath(rawPath);
385
461
  if (!parsed) return next();
386
462
 
387
- const driver = c.get("driver") || this.driver;
463
+ const driver = this.getScopedDriver(c);
388
464
 
389
465
  this.enforceApiKeyPermission(c, c.req.param("parent"));
390
466
 
@@ -395,13 +471,13 @@ entityId };
395
471
  const queryDict = c.req.queries();
396
472
  const queryOptions = parseQueryOptions(queryDict);
397
473
  const searchString = Array.isArray(queryDict.searchString) ? queryDict.searchString[queryDict.searchString.length - 1] : undefined;
398
-
474
+
399
475
  const total = driver.countEntities ? await driver.countEntities({
400
476
  path: parsed.collectionPath,
401
477
  filter: queryOptions.where as FetchCollectionProps["filter"],
402
478
  searchString
403
479
  }) : 0;
404
-
480
+
405
481
  return c.json({ count: total });
406
482
  } else if (parsed.entityId) {
407
483
  // GET /parent/:parentId/child/:id — single entity
@@ -459,7 +535,7 @@ entityId };
459
535
  const parsed = parseSubPath(rawPath);
460
536
  if (!parsed || parsed.entityId) return next();
461
537
 
462
- const driver = c.get("driver") || this.driver;
538
+ const driver = this.getScopedDriver(c);
463
539
  const hookCtx = this.buildHookContext(c, "POST");
464
540
 
465
541
  this.enforceApiKeyPermission(c, c.req.param("parent"));
@@ -479,7 +555,7 @@ entityId };
479
555
 
480
556
  if (this.dataHooks?.afterSave) {
481
557
  Promise.resolve(this.dataHooks.afterSave(parsed.collectionPath, response as Record<string, unknown>, hookCtx)).catch(err => {
482
- console.error("[BackendHooks] data.afterSave error:", err instanceof Error ? err.message : err);
558
+ logger.error("[BackendHooks] data.afterSave error", { error: err instanceof Error ? err.message : err });
483
559
  });
484
560
  }
485
561
 
@@ -494,7 +570,7 @@ entityId };
494
570
  const parsed = parseSubPath(rawPath);
495
571
  if (!parsed || !parsed.entityId) return next();
496
572
 
497
- const driver = c.get("driver") || this.driver;
573
+ const driver = this.getScopedDriver(c);
498
574
  const hookCtx = this.buildHookContext(c, "PUT");
499
575
 
500
576
  this.enforceApiKeyPermission(c, c.req.param("parent"));
@@ -516,7 +592,7 @@ entityId };
516
592
 
517
593
  if (this.dataHooks?.afterSave) {
518
594
  Promise.resolve(this.dataHooks.afterSave(parsed.collectionPath, response as Record<string, unknown>, hookCtx)).catch(err => {
519
- console.error("[BackendHooks] data.afterSave error:", err instanceof Error ? err.message : err);
595
+ logger.error("[BackendHooks] data.afterSave error", { error: err instanceof Error ? err.message : err });
520
596
  });
521
597
  }
522
598
 
@@ -531,7 +607,7 @@ entityId };
531
607
  const parsed = parseSubPath(rawPath);
532
608
  if (!parsed || !parsed.entityId) return next();
533
609
 
534
- const driver = c.get("driver") || this.driver;
610
+ const driver = this.getScopedDriver(c);
535
611
  const hookCtx = this.buildHookContext(c, "DELETE");
536
612
 
537
613
  this.enforceApiKeyPermission(c, c.req.param("parent"));
@@ -551,7 +627,7 @@ entityId };
551
627
 
552
628
  if (this.dataHooks?.afterDelete) {
553
629
  Promise.resolve(this.dataHooks.afterDelete(parsed.collectionPath, parsed.entityId, hookCtx)).catch(err => {
554
- console.error("[BackendHooks] data.afterDelete error:", err instanceof Error ? err.message : err);
630
+ logger.error("[BackendHooks] data.afterDelete error", { error: err instanceof Error ? err.message : err });
555
631
  });
556
632
  }
557
633
 
@@ -31,17 +31,21 @@ export function parseLogicalString(str: string): FilterCondition | LogicalCondit
31
31
  str = str.trim();
32
32
  if (str.startsWith("or(") && str.endsWith(")")) {
33
33
  const inner = str.slice(3, -1);
34
- return { type: "or", conditions: parseLogicalList(inner) };
34
+ return { type: "or",
35
+ conditions: parseLogicalList(inner) };
35
36
  }
36
37
  if (str.startsWith("and(") && str.endsWith(")")) {
37
38
  const inner = str.slice(4, -1);
38
- return { type: "and", conditions: parseLogicalList(inner) };
39
+ return { type: "and",
40
+ conditions: parseLogicalList(inner) };
39
41
  }
40
-
42
+
41
43
  // It's a leaf condition: field.op.val
42
44
  const firstDot = str.indexOf(".");
43
45
  if (firstDot === -1) {
44
- return { column: str, operator: "==", value: true };
46
+ return { column: str,
47
+ operator: "==",
48
+ value: true };
45
49
  }
46
50
  const field = str.substring(0, firstDot);
47
51
  const rest = str.substring(firstDot + 1);
@@ -55,7 +59,7 @@ export function parseLogicalString(str: string): FilterCondition | LogicalCondit
55
59
  op = "eq";
56
60
  valStr = rest;
57
61
  }
58
-
62
+
59
63
  const rebaseOp = (mapOperator(op) || "==") as FilterCondition["operator"];
60
64
  let parsedVal: unknown = valStr;
61
65
  if (valStr === "true") parsedVal = true;
@@ -73,20 +77,22 @@ export function parseLogicalString(str: string): FilterCondition | LogicalCondit
73
77
  return trimmed;
74
78
  });
75
79
  }
76
-
77
- return { column: field, operator: rebaseOp, value: parsedVal };
80
+
81
+ return { column: field,
82
+ operator: rebaseOp,
83
+ value: parsedVal };
78
84
  }
79
85
 
80
86
  function parseLogicalList(str: string): (FilterCondition | LogicalCondition)[] {
81
87
  const list: (FilterCondition | LogicalCondition)[] = [];
82
88
  let depth = 0;
83
89
  let current = "";
84
-
90
+
85
91
  for (let i = 0; i < str.length; i++) {
86
92
  const char = str[i];
87
93
  if (char === "(") depth++;
88
94
  if (char === ")") depth--;
89
-
95
+
90
96
  if (char === "," && depth === 0) {
91
97
  list.push(parseLogicalString(current));
92
98
  current = "";
@@ -109,10 +115,10 @@ export function parseQueryOptions(query: Record<string, unknown>): QueryOptions
109
115
  // Pagination
110
116
  const limitVal = getLastValue(query.limit);
111
117
  if (limitVal) options.limit = parseInt(String(limitVal));
112
-
118
+
113
119
  const offsetVal = getLastValue(query.offset);
114
120
  if (offsetVal) options.offset = parseInt(String(offsetVal));
115
-
121
+
116
122
  const pageVal = getLastValue(query.page);
117
123
  if (pageVal) {
118
124
  const page = parseInt(String(pageVal));
@@ -275,7 +281,7 @@ export function parseQueryOptions(query: Record<string, unknown>): QueryOptions
275
281
  const vectorSearch: VectorSearchParams = {
276
282
  property: String(vectorSearchVal),
277
283
  vector: queryVector,
278
- distance: distanceParam,
284
+ distance: distanceParam
279
285
  };
280
286
 
281
287
  const thresholdVal = getLastValue(query.vector_threshold);
package/src/api/server.ts CHANGED
@@ -12,6 +12,8 @@ import { createSchemaEditorRoutes } from "./schema-editor-routes";
12
12
  import { createAuthMiddleware, requireAuth, requireAdmin } from "../auth/middleware";
13
13
  import { errorHandler } from "./errors";
14
14
  import { generateOpenApiSpec } from "./openapi-generator";
15
+ import { requestId } from "../utils/request-id";
16
+ import { logger } from "../utils/logger";
15
17
 
16
18
  /**
17
19
  * Simplified API server that leverages existing Rebase infrastructure
@@ -69,6 +71,9 @@ export class RebaseApiServer {
69
71
  * Setup Hono middleware
70
72
  */
71
73
  private setupMiddleware(): void {
74
+ // Request ID — must be first so all downstream middleware can access it
75
+ this.router.use("/*", requestId());
76
+
72
77
  // Security headers — use same-origin-allow-popups for COOP so that
73
78
  // OAuth popup flows (Google, etc.) can postMessage back to the opener.
74
79
  this.router.use("/*", secureHeaders({
@@ -86,6 +91,12 @@ export class RebaseApiServer {
86
91
  : (origin ?? "*"),
87
92
  credentials: this.config.cors.credentials ?? false
88
93
  }));
94
+ } else if (process.env.NODE_ENV !== "production") {
95
+ logger.warn(
96
+ "⚠️ [RebaseApiServer] No CORS configuration provided. " +
97
+ "The API will accept requests from any origin. " +
98
+ "Pass a `cors` option or configure CORS on your Hono app."
99
+ );
89
100
  }
90
101
 
91
102
  // Auth middleware
@@ -179,7 +190,7 @@ export class RebaseApiServer {
179
190
  // Schema Editor endpoints
180
191
  if (this.config.collectionsDir) {
181
192
  if (process.env.NODE_ENV === "production") {
182
- console.warn("[RebaseApiServer] Schema Editor is disabled in production environments for security.");
193
+ logger.warn("[RebaseApiServer] Schema Editor is disabled in production environments for security.");
183
194
  } else {
184
195
  // Auth middlewares applied to schema-editor via the router prefix
185
196
  // MUST be declared before .route() so they execute first
package/src/api/types.ts CHANGED
@@ -14,6 +14,8 @@ export type HonoEnv = {
14
14
  driver?: DataDriver;
15
15
  /** Set when the request is authenticated via a Service API Key. */
16
16
  apiKey?: ApiKeyMasked;
17
+ /** Unique request correlation ID (generated or propagated from X-Request-ID header). */
18
+ requestId?: string;
17
19
  }
18
20
  };
19
21