@rebasepro/server-core 0.4.0 → 0.5.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.
package/dist/index.umd.js CHANGED
@@ -2777,6 +2777,9 @@
2777
2777
  return new ApiError(503, code2, message);
2778
2778
  }
2779
2779
  }
2780
+ function isRebaseApiError(error2) {
2781
+ return error2 instanceof Error;
2782
+ }
2780
2783
  const errorHandler = (err, c) => {
2781
2784
  const error2 = err;
2782
2785
  if (error2 instanceof ApiError || error2.name === "ApiError") {
@@ -3317,9 +3320,10 @@
3317
3320
  }
3318
3321
  return c.json(response, 201);
3319
3322
  } catch (error2) {
3320
- const err = error2;
3321
- err.code = err.code || "BAD_REQUEST";
3322
- throw err;
3323
+ if (isRebaseApiError(error2) && !error2.code) {
3324
+ error2.code = "BAD_REQUEST";
3325
+ }
3326
+ throw error2;
3323
3327
  }
3324
3328
  });
3325
3329
  this.router.put(`${basePath}/:id`, async (c) => {
@@ -3355,9 +3359,10 @@
3355
3359
  }
3356
3360
  return c.json(response);
3357
3361
  } catch (error2) {
3358
- const err = error2;
3359
- err.code = err.code || "BAD_REQUEST";
3360
- throw err;
3362
+ if (isRebaseApiError(error2) && !error2.code) {
3363
+ error2.code = "BAD_REQUEST";
3364
+ }
3365
+ throw error2;
3361
3366
  }
3362
3367
  });
3363
3368
  this.router.delete(`${basePath}/:id`, async (c) => {
@@ -3434,6 +3439,7 @@
3434
3439
  if (!parsed) return next();
3435
3440
  const driver = c.get("driver") || this.driver;
3436
3441
  this.enforceApiKeyPermission(c, c.req.param("parent"));
3442
+ const hookCtx = this.buildHookContext(c, "GET");
3437
3443
  if (parsed.entityId === "count") {
3438
3444
  const queryDict = c.req.queries();
3439
3445
  const queryOptions = parseQueryOptions(queryDict);
@@ -3452,7 +3458,10 @@
3452
3458
  entityId: parsed.entityId
3453
3459
  });
3454
3460
  if (!entity) throw ApiError.notFound("Entity not found");
3455
- return c.json(this.flattenEntity(entity));
3461
+ const flatResult = this.flattenEntity(entity);
3462
+ const hookResult = await this.applyAfterRead(c.req.param("parent"), flatResult, hookCtx);
3463
+ if (!hookResult) throw ApiError.notFound("Entity not found");
3464
+ return c.json(hookResult);
3456
3465
  } else {
3457
3466
  const queryDict = c.req.queries();
3458
3467
  const queryOptions = parseQueryOptions(queryDict);
@@ -3465,13 +3474,20 @@
3465
3474
  order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
3466
3475
  searchString
3467
3476
  });
3477
+ let flatEntities = entities.map((e) => this.flattenEntity(e));
3478
+ flatEntities = await this.applyAfterReadBatch(c.req.param("parent"), flatEntities, hookCtx);
3479
+ const total = driver.countEntities ? await driver.countEntities({
3480
+ path: parsed.collectionPath,
3481
+ filter: queryOptions.where,
3482
+ searchString
3483
+ }) : flatEntities.length;
3468
3484
  return c.json({
3469
- data: entities.map((e) => this.flattenEntity(e)),
3485
+ data: flatEntities,
3470
3486
  meta: {
3471
- total: entities.length,
3487
+ total,
3472
3488
  limit: queryOptions.limit,
3473
3489
  offset: queryOptions.offset,
3474
- hasMore: false
3490
+ hasMore: (queryOptions.offset || 0) + flatEntities.length < total
3475
3491
  }
3476
3492
  });
3477
3493
  }
@@ -3483,14 +3499,24 @@
3483
3499
  const parsed = parseSubPath(rawPath);
3484
3500
  if (!parsed || parsed.entityId) return next();
3485
3501
  const driver = c.get("driver") || this.driver;
3502
+ const hookCtx = this.buildHookContext(c, "POST");
3486
3503
  this.enforceApiKeyPermission(c, c.req.param("parent"));
3487
- const body = await c.req.json().catch(() => ({}));
3504
+ let body = await c.req.json().catch(() => ({}));
3505
+ if (this.dataHooks?.beforeSave) {
3506
+ body = await this.dataHooks.beforeSave(parsed.collectionPath, body, void 0, hookCtx);
3507
+ }
3488
3508
  const entity = await driver.saveEntity({
3489
3509
  path: parsed.collectionPath,
3490
3510
  values: body,
3491
3511
  status: "new"
3492
3512
  });
3493
- return c.json(this.formatResponse(entity), 201);
3513
+ const response = this.formatResponse(entity);
3514
+ if (this.dataHooks?.afterSave) {
3515
+ Promise.resolve(this.dataHooks.afterSave(parsed.collectionPath, response, hookCtx)).catch((err) => {
3516
+ console.error("[BackendHooks] data.afterSave error:", err instanceof Error ? err.message : err);
3517
+ });
3518
+ }
3519
+ return c.json(response, 201);
3494
3520
  });
3495
3521
  this.router.put("/:parent/:parentId/:rest{.+}", async (c, next) => {
3496
3522
  const rest = c.req.param("rest");
@@ -3499,15 +3525,25 @@
3499
3525
  const parsed = parseSubPath(rawPath);
3500
3526
  if (!parsed || !parsed.entityId) return next();
3501
3527
  const driver = c.get("driver") || this.driver;
3528
+ const hookCtx = this.buildHookContext(c, "PUT");
3502
3529
  this.enforceApiKeyPermission(c, c.req.param("parent"));
3503
- const body = await c.req.json().catch(() => ({}));
3530
+ let body = await c.req.json().catch(() => ({}));
3531
+ if (this.dataHooks?.beforeSave) {
3532
+ body = await this.dataHooks.beforeSave(parsed.collectionPath, body, parsed.entityId, hookCtx);
3533
+ }
3504
3534
  const entity = await driver.saveEntity({
3505
3535
  path: parsed.collectionPath,
3506
3536
  entityId: parsed.entityId,
3507
3537
  values: body,
3508
3538
  status: "existing"
3509
3539
  });
3510
- return c.json(this.formatResponse(entity));
3540
+ const response = this.formatResponse(entity);
3541
+ if (this.dataHooks?.afterSave) {
3542
+ Promise.resolve(this.dataHooks.afterSave(parsed.collectionPath, response, hookCtx)).catch((err) => {
3543
+ console.error("[BackendHooks] data.afterSave error:", err instanceof Error ? err.message : err);
3544
+ });
3545
+ }
3546
+ return c.json(response);
3511
3547
  });
3512
3548
  this.router.delete("/:parent/:parentId/:rest{.+}", async (c, next) => {
3513
3549
  const rest = c.req.param("rest");
@@ -3516,15 +3552,24 @@
3516
3552
  const parsed = parseSubPath(rawPath);
3517
3553
  if (!parsed || !parsed.entityId) return next();
3518
3554
  const driver = c.get("driver") || this.driver;
3555
+ const hookCtx = this.buildHookContext(c, "DELETE");
3519
3556
  this.enforceApiKeyPermission(c, c.req.param("parent"));
3520
3557
  const existingEntity = await driver.fetchEntity({
3521
3558
  path: parsed.collectionPath,
3522
3559
  entityId: parsed.entityId
3523
3560
  });
3524
3561
  if (!existingEntity) throw ApiError.notFound("Entity not found");
3562
+ if (this.dataHooks?.beforeDelete) {
3563
+ await this.dataHooks.beforeDelete(parsed.collectionPath, parsed.entityId, hookCtx);
3564
+ }
3525
3565
  await driver.deleteEntity({
3526
3566
  entity: existingEntity
3527
3567
  });
3568
+ if (this.dataHooks?.afterDelete) {
3569
+ Promise.resolve(this.dataHooks.afterDelete(parsed.collectionPath, parsed.entityId, hookCtx)).catch((err) => {
3570
+ console.error("[BackendHooks] data.afterDelete error:", err instanceof Error ? err.message : err);
3571
+ });
3572
+ }
3528
3573
  return new Response(null, {
3529
3574
  status: 204
3530
3575
  });
@@ -13007,14 +13052,11 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
13007
13052
  if (!factor || factor.userId !== userCtx.userId) {
13008
13053
  throw ApiError.notFound("MFA factor not found");
13009
13054
  }
13010
- const isRecoveryCode = code2.length > 6;
13011
- let isValid2 = false;
13012
- if (isRecoveryCode) {
13055
+ const secretBuffer = base32Decode(factor.secretEncrypted);
13056
+ let isValid2 = verifyTotp(secretBuffer, code2);
13057
+ if (!isValid2) {
13013
13058
  const codeHash = hashRecoveryCode(code2);
13014
13059
  isValid2 = await authRepo.useRecoveryCode(userCtx.userId, codeHash);
13015
- } else {
13016
- const secretBuffer = base32Decode(factor.secretEncrypted);
13017
- isValid2 = verifyTotp(secretBuffer, code2);
13018
13060
  }
13019
13061
  if (!isValid2) {
13020
13062
  throw ApiError.unauthorized("Invalid verification code", "INVALID_CODE");
@@ -13530,7 +13572,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
13530
13572
  if (!payload) {
13531
13573
  return null;
13532
13574
  }
13533
- const extendedPayload = payload;
13534
13575
  let roles = payload.roles || [];
13535
13576
  try {
13536
13577
  roles = await authRepository.getUserRoleIds(payload.userId);
@@ -13539,8 +13580,8 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
13539
13580
  const isAdmin = roles.some((r) => r === "admin" || r === "schema-admin");
13540
13581
  return {
13541
13582
  uid: payload.userId,
13542
- email: extendedPayload.email ?? "",
13543
- displayName: extendedPayload.displayName ?? null,
13583
+ email: payload.email ?? "",
13584
+ displayName: payload.displayName ?? null,
13544
13585
  roles,
13545
13586
  isAdmin,
13546
13587
  rawToken: token
@@ -13560,7 +13601,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
13560
13601
  if (!payload) {
13561
13602
  return null;
13562
13603
  }
13563
- const extendedPayload = payload;
13564
13604
  let roles = payload.roles || [];
13565
13605
  try {
13566
13606
  roles = await authRepository.getUserRoleIds(payload.userId);
@@ -13569,8 +13609,8 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
13569
13609
  const isAdmin = roles.some((r) => r === "admin" || r === "schema-admin");
13570
13610
  return {
13571
13611
  uid: payload.userId,
13572
- email: extendedPayload.email ?? "",
13573
- displayName: extendedPayload.displayName ?? null,
13612
+ email: payload.email ?? "",
13613
+ displayName: payload.displayName ?? null,
13574
13614
  roles,
13575
13615
  isAdmin,
13576
13616
  rawToken: token
@@ -38609,7 +38649,9 @@ ${credentialScope}
38609
38649
  }
38610
38650
  if (bootstrapper.initializeRealtime) {
38611
38651
  const realtime = await bootstrapper.initializeRealtime({}, driverResult);
38612
- realtimeServices[b.id || bootstrapper.type] = realtime;
38652
+ if (realtime) {
38653
+ realtimeServices[b.id || bootstrapper.type] = realtime;
38654
+ }
38613
38655
  }
38614
38656
  }
38615
38657
  const driverRegistry = DefaultDriverRegistry.create(delegates);
@@ -39088,7 +39130,7 @@ ${credentialScope}
39088
39130
  });
39089
39131
  }
39090
39132
  }
39091
- if (defaultBootstrapper.initializeWebsockets) {
39133
+ if (defaultBootstrapper.initializeWebsockets && defaultRealtimeService) {
39092
39134
  await defaultBootstrapper.initializeWebsockets(config.server, defaultRealtimeService, defaultDriver, config.auth, authAdapter);
39093
39135
  }
39094
39136
  logger.info("Rebase Backend Initialized");
@@ -39134,12 +39176,11 @@ ${credentialScope}
39134
39176
  }
39135
39177
  for (const [key, rt] of Object.entries(realtimeServices)) {
39136
39178
  try {
39137
- const rtWithLifecycle = rt;
39138
- if (typeof rtWithLifecycle.destroy === "function") {
39139
- await rtWithLifecycle.destroy();
39179
+ if (typeof rt.destroy === "function") {
39180
+ await rt.destroy();
39140
39181
  logger.info(`Realtime service "${key}" destroyed`);
39141
- } else if (typeof rtWithLifecycle.stopListening === "function") {
39142
- await rtWithLifecycle.stopListening();
39182
+ } else if (typeof rt.stopListening === "function") {
39183
+ await rt.stopListening();
39143
39184
  logger.info(`Realtime service "${key}" LISTEN client stopped`);
39144
39185
  }
39145
39186
  } catch (err) {
@@ -51293,6 +51334,7 @@ export default ${safeId}Collection;
51293
51334
  exports2.isAuthAdapter = isAuthAdapter;
51294
51335
  exports2.isDatabaseAdapter = isDatabaseAdapter;
51295
51336
  exports2.isOperationAllowed = isOperationAllowed;
51337
+ exports2.isRebaseApiError = isRebaseApiError;
51296
51338
  exports2.isTransformableImage = isTransformableImage;
51297
51339
  exports2.listenWithPortRetry = listenWithPortRetry;
51298
51340
  exports2.loadCronJobsFromDirectory = loadCronJobsFromDirectory;