@rebasepro/server-core 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. package/README.md +62 -25
  2. package/dist/{server-core/src/api → api}/errors.d.ts +20 -2
  3. package/dist/{server-core/src/api → api}/rest/api-generator.d.ts +8 -2
  4. package/dist/{server-core/src/api → api}/types.d.ts +2 -0
  5. package/dist/auth/admin-user-ops.d.ts +79 -0
  6. package/dist/{server-core/src/auth → auth}/api-keys/index.d.ts +2 -2
  7. package/dist/{server-core/src/auth → auth}/auth-hooks.d.ts +37 -9
  8. package/dist/{server-core/src/auth → auth}/builtin-auth-adapter.d.ts +4 -4
  9. package/dist/{server-core/src/auth → auth}/index.d.ts +5 -2
  10. package/dist/{server-core/src/auth → auth}/jwt.d.ts +10 -0
  11. package/dist/auth/mfa-routes.d.ts +6 -0
  12. package/dist/auth/reset-password-admin.d.ts +29 -0
  13. package/dist/auth/session-routes.d.ts +25 -0
  14. package/dist/backend-CIxN4FVm.js +15 -0
  15. package/dist/backend-CIxN4FVm.js.map +1 -0
  16. package/dist/chunk-Dze3rakg.js +42 -0
  17. package/dist/{server-core/src/cron → cron}/cron-scheduler.d.ts +1 -1
  18. package/dist/dist-CZKP-Xz4.js +832 -0
  19. package/dist/dist-CZKP-Xz4.js.map +1 -0
  20. package/dist/env.d.ts +102 -0
  21. package/dist/from-VbwD7xRf.js +3849 -0
  22. package/dist/from-VbwD7xRf.js.map +1 -0
  23. package/dist/{server-core/src/index.d.ts → index.d.ts} +1 -0
  24. package/dist/index.es.js +60871 -50424
  25. package/dist/index.es.js.map +1 -1
  26. package/dist/index.umd.js +73151 -51256
  27. package/dist/index.umd.js.map +1 -1
  28. package/dist/init/docs.d.ts +4 -0
  29. package/dist/init/health.d.ts +2 -0
  30. package/dist/init/middlewares.d.ts +10 -0
  31. package/dist/init/shutdown.d.ts +11 -0
  32. package/dist/init/storage.d.ts +5 -0
  33. package/dist/{server-core/src/init.d.ts → init.d.ts} +4 -4
  34. package/dist/jwt-DHcQRGC3.js +4168 -0
  35. package/dist/jwt-DHcQRGC3.js.map +1 -0
  36. package/dist/logger-BYU66ENZ.js +94 -0
  37. package/dist/logger-BYU66ENZ.js.map +1 -0
  38. package/dist/ms-BeBSuOXN.js +125 -0
  39. package/dist/ms-BeBSuOXN.js.map +1 -0
  40. package/dist/multipart-parser-CedBDOeC.js +299 -0
  41. package/dist/multipart-parser-CedBDOeC.js.map +1 -0
  42. package/dist/src-COaj0G3P.js +1182 -0
  43. package/dist/src-COaj0G3P.js.map +1 -0
  44. package/dist/{server-core/src/storage → storage}/image-transform.d.ts +3 -1
  45. package/dist/utils/request-id.d.ts +4 -0
  46. package/package.json +29 -29
  47. package/src/api/collections_for_test/callbacks_test_collection.ts +7 -6
  48. package/src/api/errors.ts +95 -20
  49. package/src/api/graphql/graphql-schema-generator.ts +15 -10
  50. package/src/api/logs-routes.ts +5 -2
  51. package/src/api/openapi-generator.ts +1 -1
  52. package/src/api/rest/api-generator-count.test.ts +21 -10
  53. package/src/api/rest/api-generator.ts +151 -36
  54. package/src/api/rest/query-parser.ts +18 -12
  55. package/src/api/server.ts +13 -2
  56. package/src/api/types.ts +2 -0
  57. package/src/auth/adapter-middleware.ts +17 -10
  58. package/src/auth/admin-user-ops.ts +236 -0
  59. package/src/auth/api-keys/api-key-middleware.ts +15 -9
  60. package/src/auth/api-keys/api-key-permission-guard.ts +1 -1
  61. package/src/auth/api-keys/api-key-routes.ts +3 -3
  62. package/src/auth/api-keys/api-key-store.ts +14 -8
  63. package/src/auth/api-keys/index.ts +2 -2
  64. package/src/auth/apple-oauth.ts +4 -3
  65. package/src/auth/auth-hooks.ts +50 -10
  66. package/src/auth/bitbucket-oauth.ts +5 -4
  67. package/src/auth/builtin-auth-adapter.ts +64 -54
  68. package/src/auth/custom-auth-adapter.ts +4 -5
  69. package/src/auth/discord-oauth.ts +5 -4
  70. package/src/auth/facebook-oauth.ts +5 -4
  71. package/src/auth/github-oauth.ts +6 -5
  72. package/src/auth/gitlab-oauth.ts +5 -4
  73. package/src/auth/google-oauth.ts +2 -1
  74. package/src/auth/index.ts +6 -2
  75. package/src/auth/jwt.ts +15 -4
  76. package/src/auth/linkedin-oauth.ts +4 -3
  77. package/src/auth/mfa-routes.ts +299 -0
  78. package/src/auth/mfa.ts +2 -1
  79. package/src/auth/microsoft-oauth.ts +5 -4
  80. package/src/auth/middleware.ts +5 -4
  81. package/src/auth/rate-limiter.ts +1 -1
  82. package/src/auth/reset-password-admin.ts +144 -0
  83. package/src/auth/rls-scope.ts +1 -1
  84. package/src/auth/routes.ts +52 -600
  85. package/src/auth/session-routes.ts +341 -0
  86. package/src/auth/slack-oauth.ts +6 -5
  87. package/src/auth/spotify-oauth.ts +5 -4
  88. package/src/auth/twitter-oauth.ts +4 -3
  89. package/src/collections/loader.ts +5 -4
  90. package/src/cron/cron-scheduler.test.ts +23 -11
  91. package/src/cron/cron-scheduler.ts +14 -8
  92. package/src/cron/cron-store.ts +1 -1
  93. package/src/email/smtp-email-service.ts +3 -2
  94. package/src/env.ts +11 -10
  95. package/src/index.ts +1 -0
  96. package/src/init/docs.ts +47 -0
  97. package/src/init/health.ts +37 -0
  98. package/src/init/middlewares.ts +61 -0
  99. package/src/init/shutdown.ts +56 -0
  100. package/src/init/storage.ts +57 -0
  101. package/src/init.ts +93 -295
  102. package/src/serve-spa.ts +12 -5
  103. package/src/services/driver-registry.ts +4 -3
  104. package/src/storage/S3StorageController.ts +1 -2
  105. package/src/storage/image-transform.ts +25 -10
  106. package/src/storage/routes.ts +30 -11
  107. package/src/storage/storage-registry.ts +4 -3
  108. package/src/storage/tus-handler.ts +12 -12
  109. package/src/utils/request-id.ts +40 -0
  110. package/src/utils/request-logger.ts +6 -0
  111. package/test/api-generator.test.ts +90 -2
  112. package/test/api-key-permission-guard.test.ts +24 -12
  113. package/test/auth-routes.test.ts +5 -3
  114. package/test/backend-hooks-data.test.ts +99 -30
  115. package/test/custom-auth-adapter.test.ts +16 -15
  116. package/test/email-templates.test.ts +10 -5
  117. package/test/env.test.ts +10 -10
  118. package/test/function-loader.test.ts +7 -4
  119. package/test/graphql-schema-generator.test.ts +554 -0
  120. package/test/jwt-security.test.ts +1 -1
  121. package/test/query-parser.test.ts +0 -1
  122. package/test/reset-password-admin.test.ts +113 -0
  123. package/test/singleton.test.ts +5 -5
  124. package/test/smtp-email-service.test.ts +21 -21
  125. package/test/webhook-service.test.ts +22 -11
  126. package/tsconfig.json +2 -0
  127. package/tsconfig.prod.json +3 -0
  128. package/vite.config.ts +5 -5
  129. package/dist/common/src/collections/CollectionRegistry.d.ts +0 -56
  130. package/dist/common/src/collections/default-collections.d.ts +0 -9
  131. package/dist/common/src/collections/index.d.ts +0 -2
  132. package/dist/common/src/data/buildRebaseData.d.ts +0 -14
  133. package/dist/common/src/data/query_builder.d.ts +0 -55
  134. package/dist/common/src/index.d.ts +0 -4
  135. package/dist/common/src/util/builders.d.ts +0 -57
  136. package/dist/common/src/util/callbacks.d.ts +0 -6
  137. package/dist/common/src/util/collections.d.ts +0 -11
  138. package/dist/common/src/util/common.d.ts +0 -2
  139. package/dist/common/src/util/conditions.d.ts +0 -26
  140. package/dist/common/src/util/entities.d.ts +0 -58
  141. package/dist/common/src/util/enums.d.ts +0 -3
  142. package/dist/common/src/util/index.d.ts +0 -16
  143. package/dist/common/src/util/navigation_from_path.d.ts +0 -34
  144. package/dist/common/src/util/navigation_utils.d.ts +0 -20
  145. package/dist/common/src/util/parent_references_from_path.d.ts +0 -6
  146. package/dist/common/src/util/paths.d.ts +0 -14
  147. package/dist/common/src/util/permissions.d.ts +0 -6
  148. package/dist/common/src/util/references.d.ts +0 -2
  149. package/dist/common/src/util/relations.d.ts +0 -22
  150. package/dist/common/src/util/resolutions.d.ts +0 -72
  151. package/dist/common/src/util/storage.d.ts +0 -24
  152. package/dist/index-Cr1D21av.js +0 -49
  153. package/dist/index-Cr1D21av.js.map +0 -1
  154. package/dist/server-core/src/auth/admin-routes.d.ts +0 -27
  155. package/dist/server-core/src/env.d.ts +0 -137
  156. package/dist/types/src/controllers/analytics_controller.d.ts +0 -7
  157. package/dist/types/src/controllers/auth.d.ts +0 -104
  158. package/dist/types/src/controllers/client.d.ts +0 -168
  159. package/dist/types/src/controllers/collection_registry.d.ts +0 -46
  160. package/dist/types/src/controllers/customization_controller.d.ts +0 -60
  161. package/dist/types/src/controllers/data.d.ts +0 -207
  162. package/dist/types/src/controllers/data_driver.d.ts +0 -218
  163. package/dist/types/src/controllers/database_admin.d.ts +0 -11
  164. package/dist/types/src/controllers/dialogs_controller.d.ts +0 -36
  165. package/dist/types/src/controllers/effective_role.d.ts +0 -4
  166. package/dist/types/src/controllers/email.d.ts +0 -36
  167. package/dist/types/src/controllers/index.d.ts +0 -18
  168. package/dist/types/src/controllers/local_config_persistence.d.ts +0 -20
  169. package/dist/types/src/controllers/navigation.d.ts +0 -225
  170. package/dist/types/src/controllers/registry.d.ts +0 -63
  171. package/dist/types/src/controllers/side_dialogs_controller.d.ts +0 -67
  172. package/dist/types/src/controllers/side_entity_controller.d.ts +0 -97
  173. package/dist/types/src/controllers/snackbar.d.ts +0 -24
  174. package/dist/types/src/controllers/storage.d.ts +0 -171
  175. package/dist/types/src/index.d.ts +0 -4
  176. package/dist/types/src/rebase_context.d.ts +0 -122
  177. package/dist/types/src/types/auth_adapter.d.ts +0 -301
  178. package/dist/types/src/types/backend.d.ts +0 -536
  179. package/dist/types/src/types/backend_hooks.d.ts +0 -172
  180. package/dist/types/src/types/builders.d.ts +0 -15
  181. package/dist/types/src/types/chips.d.ts +0 -5
  182. package/dist/types/src/types/collections.d.ts +0 -941
  183. package/dist/types/src/types/component_ref.d.ts +0 -47
  184. package/dist/types/src/types/cron.d.ts +0 -102
  185. package/dist/types/src/types/data_source.d.ts +0 -64
  186. package/dist/types/src/types/database_adapter.d.ts +0 -94
  187. package/dist/types/src/types/entities.d.ts +0 -145
  188. package/dist/types/src/types/entity_actions.d.ts +0 -104
  189. package/dist/types/src/types/entity_callbacks.d.ts +0 -173
  190. package/dist/types/src/types/entity_link_builder.d.ts +0 -7
  191. package/dist/types/src/types/entity_overrides.d.ts +0 -10
  192. package/dist/types/src/types/entity_views.d.ts +0 -87
  193. package/dist/types/src/types/export_import.d.ts +0 -21
  194. package/dist/types/src/types/formex.d.ts +0 -40
  195. package/dist/types/src/types/index.d.ts +0 -28
  196. package/dist/types/src/types/locales.d.ts +0 -4
  197. package/dist/types/src/types/modify_collections.d.ts +0 -5
  198. package/dist/types/src/types/plugins.d.ts +0 -282
  199. package/dist/types/src/types/properties.d.ts +0 -1181
  200. package/dist/types/src/types/property_config.d.ts +0 -74
  201. package/dist/types/src/types/relations.d.ts +0 -336
  202. package/dist/types/src/types/slots.d.ts +0 -262
  203. package/dist/types/src/types/translations.d.ts +0 -900
  204. package/dist/types/src/types/user_management_delegate.d.ts +0 -86
  205. package/dist/types/src/types/websockets.d.ts +0 -78
  206. package/dist/types/src/users/index.d.ts +0 -1
  207. package/dist/types/src/users/user.d.ts +0 -50
  208. package/src/auth/admin-routes.ts +0 -534
  209. package/test/admin-routes.test.ts +0 -544
  210. package/test/backend-hooks-admin.test.ts +0 -388
  211. package/test.ts +0 -6
  212. /package/dist/{server-core/src/api → api}/ast-schema-editor.d.ts +0 -0
  213. /package/dist/{server-core/src/api → api}/collections_for_test/callbacks_test_collection.d.ts +0 -0
  214. /package/dist/{server-core/src/api → api}/graphql/graphql-schema-generator.d.ts +0 -0
  215. /package/dist/{server-core/src/api → api}/graphql/index.d.ts +0 -0
  216. /package/dist/{server-core/src/api → api}/index.d.ts +0 -0
  217. /package/dist/{server-core/src/api → api}/logs-routes.d.ts +0 -0
  218. /package/dist/{server-core/src/api → api}/openapi-generator.d.ts +0 -0
  219. /package/dist/{server-core/src/api → api}/rest/index.d.ts +0 -0
  220. /package/dist/{server-core/src/api → api}/rest/query-parser.d.ts +0 -0
  221. /package/dist/{server-core/src/api → api}/schema-editor-routes.d.ts +0 -0
  222. /package/dist/{server-core/src/api → api}/server.d.ts +0 -0
  223. /package/dist/{server-core/src/auth → auth}/adapter-middleware.d.ts +0 -0
  224. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-middleware.d.ts +0 -0
  225. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-permission-guard.d.ts +0 -0
  226. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-routes.d.ts +0 -0
  227. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-store.d.ts +0 -0
  228. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-types.d.ts +0 -0
  229. /package/dist/{server-core/src/auth → auth}/apple-oauth.d.ts +0 -0
  230. /package/dist/{server-core/src/auth → auth}/bitbucket-oauth.d.ts +0 -0
  231. /package/dist/{server-core/src/auth → auth}/crypto-utils.d.ts +0 -0
  232. /package/dist/{server-core/src/auth → auth}/custom-auth-adapter.d.ts +0 -0
  233. /package/dist/{server-core/src/auth → auth}/discord-oauth.d.ts +0 -0
  234. /package/dist/{server-core/src/auth → auth}/facebook-oauth.d.ts +0 -0
  235. /package/dist/{server-core/src/auth → auth}/github-oauth.d.ts +0 -0
  236. /package/dist/{server-core/src/auth → auth}/gitlab-oauth.d.ts +0 -0
  237. /package/dist/{server-core/src/auth → auth}/google-oauth.d.ts +0 -0
  238. /package/dist/{server-core/src/auth → auth}/interfaces.d.ts +0 -0
  239. /package/dist/{server-core/src/auth → auth}/linkedin-oauth.d.ts +0 -0
  240. /package/dist/{server-core/src/auth → auth}/mfa.d.ts +0 -0
  241. /package/dist/{server-core/src/auth → auth}/microsoft-oauth.d.ts +0 -0
  242. /package/dist/{server-core/src/auth → auth}/middleware.d.ts +0 -0
  243. /package/dist/{server-core/src/auth → auth}/password.d.ts +0 -0
  244. /package/dist/{server-core/src/auth → auth}/rate-limiter.d.ts +0 -0
  245. /package/dist/{server-core/src/auth → auth}/rls-scope.d.ts +0 -0
  246. /package/dist/{server-core/src/auth → auth}/routes.d.ts +0 -0
  247. /package/dist/{server-core/src/auth → auth}/slack-oauth.d.ts +0 -0
  248. /package/dist/{server-core/src/auth → auth}/spotify-oauth.d.ts +0 -0
  249. /package/dist/{server-core/src/auth → auth}/twitter-oauth.d.ts +0 -0
  250. /package/dist/{server-core/src/collections → collections}/BackendCollectionRegistry.d.ts +0 -0
  251. /package/dist/{server-core/src/collections → collections}/loader.d.ts +0 -0
  252. /package/dist/{server-core/src/cron → cron}/cron-loader.d.ts +0 -0
  253. /package/dist/{server-core/src/cron → cron}/cron-routes.d.ts +0 -0
  254. /package/dist/{server-core/src/cron → cron}/cron-store.d.ts +0 -0
  255. /package/dist/{server-core/src/cron → cron}/index.d.ts +0 -0
  256. /package/dist/{server-core/src/db → db}/interfaces.d.ts +0 -0
  257. /package/dist/{server-core/src/email → email}/index.d.ts +0 -0
  258. /package/dist/{server-core/src/email → email}/smtp-email-service.d.ts +0 -0
  259. /package/dist/{server-core/src/email → email}/templates.d.ts +0 -0
  260. /package/dist/{server-core/src/email → email}/types.d.ts +0 -0
  261. /package/dist/{server-core/src/functions → functions}/function-loader.d.ts +0 -0
  262. /package/dist/{server-core/src/functions → functions}/function-routes.d.ts +0 -0
  263. /package/dist/{server-core/src/functions → functions}/index.d.ts +0 -0
  264. /package/dist/{server-core/src/history → history}/history-routes.d.ts +0 -0
  265. /package/dist/{server-core/src/history → history}/index.d.ts +0 -0
  266. /package/dist/{server-core/src/serve-spa.d.ts → serve-spa.d.ts} +0 -0
  267. /package/dist/{server-core/src/services → services}/driver-registry.d.ts +0 -0
  268. /package/dist/{server-core/src/services → services}/webhook-service.d.ts +0 -0
  269. /package/dist/{server-core/src/singleton.d.ts → singleton.d.ts} +0 -0
  270. /package/dist/{server-core/src/storage → storage}/LocalStorageController.d.ts +0 -0
  271. /package/dist/{server-core/src/storage → storage}/S3StorageController.d.ts +0 -0
  272. /package/dist/{server-core/src/storage → storage}/index.d.ts +0 -0
  273. /package/dist/{server-core/src/storage → storage}/routes.d.ts +0 -0
  274. /package/dist/{server-core/src/storage → storage}/storage-registry.d.ts +0 -0
  275. /package/dist/{server-core/src/storage → storage}/tus-handler.d.ts +0 -0
  276. /package/dist/{server-core/src/storage → storage}/types.d.ts +0 -0
  277. /package/dist/{server-core/src/types → types}/index.d.ts +0 -0
  278. /package/dist/{server-core/src/utils → utils}/dev-port.d.ts +0 -0
  279. /package/dist/{server-core/src/utils → utils}/logger.d.ts +0 -0
  280. /package/dist/{server-core/src/utils → utils}/logging.d.ts +0 -0
  281. /package/dist/{server-core/src/utils → utils}/request-logger.d.ts +0 -0
  282. /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
  }
@@ -314,7 +314,7 @@ content: { "application/json": { schema: { $ref: "#/components/schemas/ErrorResp
314
314
  };
315
315
 
316
316
  // ── Subcollection routes ──────────────────────────────────────
317
- const relations = (collection as EntityCollection & { relations?: Relation[] }).relations;
317
+ const relations = collection.relations;
318
318
  if (relations && relations.length > 0) {
319
319
  for (const relation of relations) {
320
320
  const relationName = relation.relationName;
@@ -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
- import { ApiError } from "../errors";
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,15 +287,16 @@ 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
- const err = error as Error & { code?: string };
238
- err.code = err.code || "BAD_REQUEST";
239
- throw err;
296
+ if (isRebaseApiError(error) && !error.code) {
297
+ error.code = "BAD_REQUEST";
298
+ }
299
+ throw error;
240
300
  }
241
301
  });
242
302
 
@@ -245,7 +305,7 @@ export class RestApiGenerator {
245
305
  try {
246
306
  this.enforceApiKeyPermission(c, collection.slug);
247
307
  const id = c.req.param("id");
248
- const driver = c.get("driver") || this.driver;
308
+ const driver = this.getScopedDriver(c);
249
309
  const hookCtx = this.buildHookContext(c, "PUT");
250
310
 
251
311
  const existingEntity = await driver.fetchEntity({
@@ -276,15 +336,16 @@ export class RestApiGenerator {
276
336
 
277
337
  if (this.dataHooks?.afterSave) {
278
338
  Promise.resolve(this.dataHooks.afterSave(collection.slug, response as Record<string, unknown>, hookCtx)).catch(err => {
279
- console.error("[BackendHooks] data.afterSave error:", err instanceof Error ? err.message : err);
339
+ logger.error("[BackendHooks] data.afterSave error", { error: err instanceof Error ? err.message : err });
280
340
  });
281
341
  }
282
342
 
283
343
  return c.json(response);
284
344
  } catch (error) {
285
- const err = error as Error & { code?: string };
286
- err.code = err.code || "BAD_REQUEST";
287
- throw err;
345
+ if (isRebaseApiError(error) && !error.code) {
346
+ error.code = "BAD_REQUEST";
347
+ }
348
+ throw error;
288
349
  }
289
350
  });
290
351
 
@@ -292,7 +353,7 @@ export class RestApiGenerator {
292
353
  this.router.delete(`${basePath}/:id`, async (c) => {
293
354
  this.enforceApiKeyPermission(c, collection.slug);
294
355
  const id = c.req.param("id");
295
- const driver = c.get("driver") || this.driver;
356
+ const driver = this.getScopedDriver(c);
296
357
  const hookCtx = this.buildHookContext(c, "DELETE");
297
358
 
298
359
  const existingEntity = await driver.fetchEntity({
@@ -316,7 +377,7 @@ export class RestApiGenerator {
316
377
 
317
378
  if (this.dataHooks?.afterDelete) {
318
379
  Promise.resolve(this.dataHooks.afterDelete(collection.slug, String(id), hookCtx)).catch(err => {
319
- console.error("[BackendHooks] data.afterDelete error:", err instanceof Error ? err.message : err);
380
+ logger.error("[BackendHooks] data.afterDelete error", { error: err instanceof Error ? err.message : err });
320
381
  });
321
382
  }
322
383
 
@@ -382,22 +443,24 @@ entityId };
382
443
  const parsed = parseSubPath(rawPath);
383
444
  if (!parsed) return next();
384
445
 
385
- const driver = c.get("driver") || this.driver;
446
+ const driver = this.getScopedDriver(c);
386
447
 
387
448
  this.enforceApiKeyPermission(c, c.req.param("parent"));
388
449
 
450
+ const hookCtx = this.buildHookContext(c, "GET");
451
+
389
452
  if (parsed.entityId === "count") {
390
453
  // GET /parent/:parentId/child/count — count child entities
391
454
  const queryDict = c.req.queries();
392
455
  const queryOptions = parseQueryOptions(queryDict);
393
456
  const searchString = Array.isArray(queryDict.searchString) ? queryDict.searchString[queryDict.searchString.length - 1] : undefined;
394
-
457
+
395
458
  const total = driver.countEntities ? await driver.countEntities({
396
459
  path: parsed.collectionPath,
397
460
  filter: queryOptions.where as FetchCollectionProps["filter"],
398
461
  searchString
399
462
  }) : 0;
400
-
463
+
401
464
  return c.json({ count: total });
402
465
  } else if (parsed.entityId) {
403
466
  // GET /parent/:parentId/child/:id — single entity
@@ -406,7 +469,12 @@ entityId };
406
469
  entityId: parsed.entityId
407
470
  });
408
471
  if (!entity) throw ApiError.notFound("Entity not found");
409
- return c.json(this.flattenEntity(entity));
472
+
473
+ const flatResult = this.flattenEntity(entity);
474
+ const hookResult = await this.applyAfterRead(c.req.param("parent"), flatResult, hookCtx);
475
+ if (!hookResult) throw ApiError.notFound("Entity not found");
476
+
477
+ return c.json(hookResult);
410
478
  } else {
411
479
  // GET /parent/:parentId/child — list entities
412
480
  const queryDict = c.req.queries();
@@ -420,13 +488,23 @@ entityId };
420
488
  order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
421
489
  searchString
422
490
  });
491
+
492
+ let flatEntities = entities.map(e => this.flattenEntity(e));
493
+ flatEntities = await this.applyAfterReadBatch(c.req.param("parent"), flatEntities, hookCtx);
494
+
495
+ const total = driver.countEntities ? await driver.countEntities({
496
+ path: parsed.collectionPath,
497
+ filter: queryOptions.where as FetchCollectionProps["filter"],
498
+ searchString
499
+ }) : flatEntities.length;
500
+
423
501
  return c.json({
424
- data: entities.map(e => this.flattenEntity(e)),
502
+ data: flatEntities,
425
503
  meta: {
426
- total: entities.length,
504
+ total,
427
505
  limit: queryOptions.limit,
428
506
  offset: queryOptions.offset,
429
- hasMore: false
507
+ hasMore: (queryOptions.offset || 0) + flatEntities.length < total
430
508
  }
431
509
  });
432
510
  }
@@ -440,10 +518,15 @@ entityId };
440
518
  const parsed = parseSubPath(rawPath);
441
519
  if (!parsed || parsed.entityId) return next();
442
520
 
443
- const driver = c.get("driver") || this.driver;
521
+ const driver = this.getScopedDriver(c);
522
+ const hookCtx = this.buildHookContext(c, "POST");
444
523
 
445
524
  this.enforceApiKeyPermission(c, c.req.param("parent"));
446
- const body = await c.req.json().catch(() => ({}));
525
+ let body = await c.req.json().catch(() => ({}));
526
+
527
+ if (this.dataHooks?.beforeSave) {
528
+ body = await this.dataHooks.beforeSave(parsed.collectionPath, body, undefined, hookCtx);
529
+ }
447
530
 
448
531
  const entity = await driver.saveEntity({
449
532
  path: parsed.collectionPath,
@@ -451,7 +534,15 @@ entityId };
451
534
  status: "new"
452
535
  });
453
536
 
454
- return c.json(this.formatResponse(entity), 201);
537
+ const response = this.formatResponse(entity);
538
+
539
+ if (this.dataHooks?.afterSave) {
540
+ Promise.resolve(this.dataHooks.afterSave(parsed.collectionPath, response as Record<string, unknown>, hookCtx)).catch(err => {
541
+ logger.error("[BackendHooks] data.afterSave error", { error: err instanceof Error ? err.message : err });
542
+ });
543
+ }
544
+
545
+ return c.json(response, 201);
455
546
  });
456
547
 
457
548
  // PUT /<subcollection-path>/:id — update entity
@@ -462,11 +553,16 @@ entityId };
462
553
  const parsed = parseSubPath(rawPath);
463
554
  if (!parsed || !parsed.entityId) return next();
464
555
 
465
- const driver = c.get("driver") || this.driver;
556
+ const driver = this.getScopedDriver(c);
557
+ const hookCtx = this.buildHookContext(c, "PUT");
466
558
 
467
559
  this.enforceApiKeyPermission(c, c.req.param("parent"));
468
560
 
469
- const body = await c.req.json().catch(() => ({}));
561
+ let body = await c.req.json().catch(() => ({}));
562
+
563
+ if (this.dataHooks?.beforeSave) {
564
+ body = await this.dataHooks.beforeSave(parsed.collectionPath, body, parsed.entityId, hookCtx);
565
+ }
470
566
 
471
567
  const entity = await driver.saveEntity({
472
568
  path: parsed.collectionPath,
@@ -475,7 +571,15 @@ entityId };
475
571
  status: "existing"
476
572
  });
477
573
 
478
- return c.json(this.formatResponse(entity));
574
+ const response = this.formatResponse(entity);
575
+
576
+ if (this.dataHooks?.afterSave) {
577
+ Promise.resolve(this.dataHooks.afterSave(parsed.collectionPath, response as Record<string, unknown>, hookCtx)).catch(err => {
578
+ logger.error("[BackendHooks] data.afterSave error", { error: err instanceof Error ? err.message : err });
579
+ });
580
+ }
581
+
582
+ return c.json(response);
479
583
  });
480
584
 
481
585
  // DELETE /<subcollection-path>/:id — delete entity
@@ -486,7 +590,8 @@ entityId };
486
590
  const parsed = parseSubPath(rawPath);
487
591
  if (!parsed || !parsed.entityId) return next();
488
592
 
489
- const driver = c.get("driver") || this.driver;
593
+ const driver = this.getScopedDriver(c);
594
+ const hookCtx = this.buildHookContext(c, "DELETE");
490
595
 
491
596
  this.enforceApiKeyPermission(c, c.req.param("parent"));
492
597
 
@@ -497,8 +602,18 @@ entityId };
497
602
 
498
603
  if (!existingEntity) throw ApiError.notFound("Entity not found");
499
604
 
605
+ if (this.dataHooks?.beforeDelete) {
606
+ await this.dataHooks.beforeDelete(parsed.collectionPath, parsed.entityId, hookCtx);
607
+ }
608
+
500
609
  await driver.deleteEntity({ entity: existingEntity });
501
610
 
611
+ if (this.dataHooks?.afterDelete) {
612
+ Promise.resolve(this.dataHooks.afterDelete(parsed.collectionPath, parsed.entityId, hookCtx)).catch(err => {
613
+ logger.error("[BackendHooks] data.afterDelete error", { error: err instanceof Error ? err.message : err });
614
+ });
615
+ }
616
+
502
617
  return new Response(null, { status: 204 });
503
618
  });
504
619
  }
@@ -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);