@astralibx/staff-engine 0.2.0 → 0.2.2

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/README.md CHANGED
@@ -70,6 +70,20 @@ app.listen(3000);
70
70
  ### Permissions
71
71
 
72
72
  - **Runtime-configurable groups via API** -- `POST /permission-groups` creates a named group with permission entries. Groups have `groupId`, `label`, `sortOrder`, and an array of entries (key, label, type). No redeploy required to define new permissions.
73
+
74
+ > **Note:** Permission groups use `label` for display text and `groupId` for the unique identifier -- not `name`.
75
+
76
+ ```ts
77
+ await engine.permissions.createGroup({
78
+ groupId: 'chat-management', // unique identifier, kebab-case
79
+ label: 'Chat Management', // display text shown in UI
80
+ permissions: [
81
+ { key: 'chat:view', label: 'View chats', type: 'view' },
82
+ { key: 'chat:edit', label: 'Edit chats', type: 'edit' },
83
+ ],
84
+ sortOrder: 1,
85
+ });
86
+ ```
73
87
  - **Edit-to-view cascade** -- when granting a permission ending in `.edit`, the corresponding `.view` key is automatically required. The engine validates this on `PUT /:id/permissions`.
74
88
  - **Permission cache (Redis or in-memory)** -- each staff member's resolved permission list is cached after the first lookup. TTL is `permissionCacheTtlMs` (default 5 min). Cache is invalidated immediately on `updatePermissions` or `updateStatus`.
75
89
  - **Owner bypasses all checks** -- `requirePermission` middleware skips the permission check entirely when `req.user.role === 'owner'`.
@@ -122,6 +136,27 @@ The factory function returns a single `StaffEngine` object:
122
136
  | `engine.models` | Mongoose models (`Staff`, `PermissionGroup`) |
123
137
  | `engine.destroy()` | Flush permission cache and clean up resources |
124
138
 
139
+ ## Seeding Data
140
+
141
+ Schema factory functions are exported so you can seed data or run scripts without creating a full engine instance:
142
+
143
+ ```ts
144
+ import { createStaffModel, createPermissionGroupModel } from '@astralibx/staff-engine';
145
+
146
+ const Staff = createStaffModel(connection);
147
+ const PermissionGroup = createPermissionGroupModel(connection);
148
+
149
+ await PermissionGroup.create({
150
+ groupId: 'admin',
151
+ label: 'Admin',
152
+ permissions: [
153
+ { key: 'chat:view', label: 'View chats', type: 'view' },
154
+ { key: 'chat:edit', label: 'Edit chats', type: 'edit' },
155
+ ],
156
+ sortOrder: 1,
157
+ });
158
+ ```
159
+
125
160
  ## Redis Key Prefix (Required for Multi-Project Deployments)
126
161
 
127
162
  > **WARNING:** If multiple projects share the same Redis server, you MUST set a unique `keyPrefix` per project. Without this, rate limiter state and permission cache entries will collide across projects.
package/dist/index.cjs CHANGED
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
- var zod = require('zod');
4
3
  var core = require('@astralibx/core');
5
4
  var staffTypes = require('@astralibx/staff-types');
6
5
  var mongoose = require('mongoose');
6
+ var zod = require('zod');
7
7
  var jwt2 = require('jsonwebtoken');
8
8
  var express = require('express');
9
9
 
@@ -403,6 +403,50 @@ function validatePermissionPairs(permissions, allGroups) {
403
403
  throw new InvalidPermissionError(missingViewKeys);
404
404
  }
405
405
  }
406
+ var StaffEngineConfigSchema = zod.z.object({
407
+ db: zod.z.object({
408
+ connection: zod.z.unknown().refine((v) => v !== void 0 && v !== null, {
409
+ message: "db.connection is required"
410
+ }),
411
+ collectionPrefix: zod.z.string().optional()
412
+ }),
413
+ redis: zod.z.object({
414
+ connection: zod.z.unknown(),
415
+ keyPrefix: zod.z.string().optional()
416
+ }).optional(),
417
+ logger: zod.z.object({
418
+ info: zod.z.function(),
419
+ warn: zod.z.function(),
420
+ error: zod.z.function()
421
+ }).optional(),
422
+ tenantId: zod.z.string().optional(),
423
+ auth: zod.z.object({
424
+ jwtSecret: zod.z.string().min(1),
425
+ staffTokenExpiry: zod.z.string().optional(),
426
+ ownerTokenExpiry: zod.z.string().optional(),
427
+ permissionCacheTtlMs: zod.z.number().int().positive().optional()
428
+ }),
429
+ adapters: zod.z.object({
430
+ hashPassword: zod.z.function(),
431
+ comparePassword: zod.z.function()
432
+ }),
433
+ hooks: zod.z.object({
434
+ onStaffCreated: zod.z.function().optional(),
435
+ onLogin: zod.z.function().optional(),
436
+ onLoginFailed: zod.z.function().optional(),
437
+ onPermissionsChanged: zod.z.function().optional(),
438
+ onStatusChanged: zod.z.function().optional(),
439
+ onMetric: zod.z.function().optional()
440
+ }).optional(),
441
+ options: zod.z.object({
442
+ requireEmailUniqueness: zod.z.boolean().optional(),
443
+ allowSelfPasswordChange: zod.z.boolean().optional(),
444
+ rateLimiter: zod.z.object({
445
+ windowMs: zod.z.number().int().positive().optional(),
446
+ maxAttempts: zod.z.number().int().positive().optional()
447
+ }).optional()
448
+ }).optional()
449
+ });
406
450
 
407
451
  // src/services/staff.service.ts
408
452
  var StaffService = class {
@@ -411,105 +455,22 @@ var StaffService = class {
411
455
  adapters;
412
456
  hooks;
413
457
  permissionCache;
414
- rateLimiter;
415
458
  logger;
416
459
  tenantId;
417
- jwtSecret;
418
- staffTokenExpiry;
419
- ownerTokenExpiry;
420
460
  requireEmailUniqueness;
421
- allowSelfPasswordChange;
422
461
  constructor(deps) {
423
462
  this.Staff = deps.Staff;
424
463
  this.PermissionGroup = deps.PermissionGroup;
425
464
  this.adapters = deps.adapters;
426
465
  this.hooks = deps.hooks;
427
466
  this.permissionCache = deps.permissionCache;
428
- this.rateLimiter = deps.rateLimiter;
429
467
  this.logger = deps.logger;
430
468
  this.tenantId = deps.tenantId;
431
- this.jwtSecret = deps.jwtSecret;
432
- this.staffTokenExpiry = deps.staffTokenExpiry;
433
- this.ownerTokenExpiry = deps.ownerTokenExpiry;
434
469
  this.requireEmailUniqueness = deps.requireEmailUniqueness;
435
- this.allowSelfPasswordChange = deps.allowSelfPasswordChange;
436
470
  }
437
471
  get tenantFilter() {
438
472
  return this.tenantId ? { tenantId: this.tenantId } : {};
439
473
  }
440
- generateToken(staffId, role) {
441
- const expiresIn = role === staffTypes.STAFF_ROLE.Owner ? this.ownerTokenExpiry : this.staffTokenExpiry;
442
- return jwt2__default.default.sign({ staffId, role }, this.jwtSecret, { expiresIn });
443
- }
444
- async setupOwner(data) {
445
- const count = await this.Staff.countDocuments(this.tenantFilter);
446
- if (count > 0) throw new SetupError();
447
- const hashedPassword = await this.adapters.hashPassword(data.password);
448
- let staff;
449
- try {
450
- const doc = await this.Staff.create({
451
- name: data.name,
452
- email: data.email.toLowerCase().trim(),
453
- password: hashedPassword,
454
- role: staffTypes.STAFF_ROLE.Owner,
455
- status: staffTypes.STAFF_STATUS.Active,
456
- permissions: [],
457
- ...this.tenantFilter
458
- });
459
- staff = doc.toObject();
460
- } catch (err) {
461
- if (err && typeof err === "object" && "code" in err && err.code === 11e3) {
462
- throw new SetupError();
463
- }
464
- throw err;
465
- }
466
- const token = this.generateToken(staff._id.toString(), staffTypes.STAFF_ROLE.Owner);
467
- this.logger.info("Owner setup complete", { staffId: staff._id.toString() });
468
- this.hooks.onStaffCreated?.(staff);
469
- this.hooks.onMetric?.({ name: "staff_setup_complete", value: 1 });
470
- return { staff, token };
471
- }
472
- async login(email, password, ip) {
473
- if (ip) {
474
- const limit = await this.rateLimiter.checkLimit(ip);
475
- if (!limit.allowed) {
476
- this.hooks.onLoginFailed?.(email, ip);
477
- throw new RateLimitError(limit.retryAfterMs);
478
- }
479
- }
480
- const staff = await this.Staff.findOne({
481
- email: email.toLowerCase().trim(),
482
- ...this.tenantFilter
483
- }).select("+password");
484
- if (!staff) {
485
- if (ip) await this.rateLimiter.recordAttempt(ip);
486
- this.hooks.onLoginFailed?.(email, ip);
487
- throw new AuthenticationError(ERROR_CODE.InvalidCredentials);
488
- }
489
- const valid = await this.adapters.comparePassword(password, staff.password);
490
- if (!valid) {
491
- if (ip) await this.rateLimiter.recordAttempt(ip);
492
- this.hooks.onLoginFailed?.(email, ip);
493
- throw new AuthenticationError(ERROR_CODE.InvalidCredentials);
494
- }
495
- if (staff.status === staffTypes.STAFF_STATUS.Inactive) {
496
- throw new AuthenticationError(ERROR_CODE.AccountInactive, ERROR_MESSAGE.AccountInactive);
497
- }
498
- if (staff.status === staffTypes.STAFF_STATUS.Pending) {
499
- throw new AuthenticationError(ERROR_CODE.AccountPending, ERROR_MESSAGE.AccountPending);
500
- }
501
- staff.lastLoginAt = /* @__PURE__ */ new Date();
502
- if (ip) staff.lastLoginIp = ip;
503
- await staff.save();
504
- if (ip) await this.rateLimiter.reset(ip);
505
- const token = this.generateToken(staff._id.toString(), staff.role);
506
- this.hooks.onLogin?.(staff.toObject(), ip);
507
- this.hooks.onMetric?.({ name: "staff_login", value: 1, labels: { role: staff.role } });
508
- this.logger.info("Staff login", { staffId: staff._id.toString() });
509
- const staffObj = staff.toObject();
510
- delete staffObj.password;
511
- return { staff: staffObj, token };
512
- }
513
474
  async create(data) {
514
475
  if (this.requireEmailUniqueness) {
515
476
  const existing = await this.Staff.findOne({
@@ -610,6 +571,106 @@ var StaffService = class {
610
571
  this.logger.info("Staff status updated", { staffId, oldStatus, newStatus: status });
611
572
  return staff.toObject();
612
573
  }
574
+ };
575
+ var AuthService = class {
576
+ Staff;
577
+ adapters;
578
+ hooks;
579
+ rateLimiter;
580
+ logger;
581
+ tenantId;
582
+ jwtSecret;
583
+ staffTokenExpiry;
584
+ ownerTokenExpiry;
585
+ allowSelfPasswordChange;
586
+ constructor(deps) {
587
+ this.Staff = deps.Staff;
588
+ this.adapters = deps.adapters;
589
+ this.hooks = deps.hooks;
590
+ this.rateLimiter = deps.rateLimiter;
591
+ this.logger = deps.logger;
592
+ this.tenantId = deps.tenantId;
593
+ this.jwtSecret = deps.jwtSecret;
594
+ this.staffTokenExpiry = deps.staffTokenExpiry;
595
+ this.ownerTokenExpiry = deps.ownerTokenExpiry;
596
+ this.allowSelfPasswordChange = deps.allowSelfPasswordChange;
597
+ }
598
+ get tenantFilter() {
599
+ return this.tenantId ? { tenantId: this.tenantId } : {};
600
+ }
601
+ generateToken(staffId, role) {
602
+ const expiresIn = role === staffTypes.STAFF_ROLE.Owner ? this.ownerTokenExpiry : this.staffTokenExpiry;
603
+ return jwt2__default.default.sign({ staffId, role }, this.jwtSecret, { expiresIn });
604
+ }
605
+ async setupOwner(data) {
606
+ const count = await this.Staff.countDocuments(this.tenantFilter);
607
+ if (count > 0) throw new SetupError();
608
+ const hashedPassword = await this.adapters.hashPassword(data.password);
609
+ let staff;
610
+ try {
611
+ const doc = await this.Staff.create({
612
+ name: data.name,
613
+ email: data.email.toLowerCase().trim(),
614
+ password: hashedPassword,
615
+ role: staffTypes.STAFF_ROLE.Owner,
616
+ status: staffTypes.STAFF_STATUS.Active,
617
+ permissions: [],
618
+ ...this.tenantFilter
619
+ });
620
+ staff = doc.toObject();
621
+ } catch (err) {
622
+ if (err && typeof err === "object" && "code" in err && err.code === 11e3) {
623
+ throw new SetupError();
624
+ }
625
+ throw err;
626
+ }
627
+ const token = this.generateToken(staff._id.toString(), staffTypes.STAFF_ROLE.Owner);
628
+ this.logger.info("Owner setup complete", { staffId: staff._id.toString() });
629
+ this.hooks.onStaffCreated?.(staff);
630
+ this.hooks.onMetric?.({ name: "staff_setup_complete", value: 1 });
631
+ return { staff, token };
632
+ }
633
+ async login(email, password, ip) {
634
+ if (ip) {
635
+ const limit = await this.rateLimiter.checkLimit(ip);
636
+ if (!limit.allowed) {
637
+ this.hooks.onLoginFailed?.(email, ip);
638
+ throw new RateLimitError(limit.retryAfterMs);
639
+ }
640
+ }
641
+ const staff = await this.Staff.findOne({
642
+ email: email.toLowerCase().trim(),
643
+ ...this.tenantFilter
644
+ }).select("+password");
645
+ if (!staff) {
646
+ if (ip) await this.rateLimiter.recordAttempt(ip);
647
+ this.hooks.onLoginFailed?.(email, ip);
648
+ throw new AuthenticationError(ERROR_CODE.InvalidCredentials);
649
+ }
650
+ const valid = await this.adapters.comparePassword(password, staff.password);
651
+ if (!valid) {
652
+ if (ip) await this.rateLimiter.recordAttempt(ip);
653
+ this.hooks.onLoginFailed?.(email, ip);
654
+ throw new AuthenticationError(ERROR_CODE.InvalidCredentials);
655
+ }
656
+ if (staff.status === staffTypes.STAFF_STATUS.Inactive) {
657
+ throw new AuthenticationError(ERROR_CODE.AccountInactive, ERROR_MESSAGE.AccountInactive);
658
+ }
659
+ if (staff.status === staffTypes.STAFF_STATUS.Pending) {
660
+ throw new AuthenticationError(ERROR_CODE.AccountPending, ERROR_MESSAGE.AccountPending);
661
+ }
662
+ staff.lastLoginAt = /* @__PURE__ */ new Date();
663
+ if (ip) staff.lastLoginIp = ip;
664
+ await staff.save();
665
+ if (ip) await this.rateLimiter.reset(ip);
666
+ const token = this.generateToken(staff._id.toString(), staff.role);
667
+ this.hooks.onLogin?.(staff.toObject(), ip);
668
+ this.hooks.onMetric?.({ name: "staff_login", value: 1, labels: { role: staff.role } });
669
+ this.logger.info("Staff login", { staffId: staff._id.toString() });
670
+ const staffObj = staff.toObject();
671
+ delete staffObj.password;
672
+ return { staff: staffObj, token };
673
+ }
613
674
  async resetPassword(staffId, newPassword) {
614
675
  const staff = await this.Staff.findOne({ _id: staffId, ...this.tenantFilter });
615
676
  if (!staff) throw new StaffNotFoundError(staffId);
@@ -637,11 +698,11 @@ function createAuthMiddleware(jwtSecret, permissionCache, StaffModel, logger, te
637
698
  if (!payload.staffId || !payload.role) return null;
638
699
  const filter = { _id: payload.staffId };
639
700
  if (tenantId) filter.tenantId = tenantId;
640
- const staff = await StaffModel.findOne(filter).select("status role").lean();
701
+ const staff = await StaffModel.findOne(filter).select("name email status role").lean();
641
702
  if (!staff) return null;
642
703
  if (staff.status !== staffTypes.STAFF_STATUS.Active) return null;
643
704
  const permissions = await permissionCache.get(payload.staffId);
644
- return { staffId: payload.staffId, role: staff.role, permissions };
705
+ return { staffId: payload.staffId, name: staff.name, email: staff.email, role: staff.role, permissions };
645
706
  } catch {
646
707
  return null;
647
708
  }
@@ -762,11 +823,11 @@ function handleStaffError(res, error, logger) {
762
823
  }
763
824
 
764
825
  // src/routes/auth.routes.ts
765
- function createAuthRoutes(staffService, auth, logger, allowSelfPasswordChange) {
826
+ function createAuthRoutes(staffService, authService, auth, logger, allowSelfPasswordChange) {
766
827
  const router = express.Router();
767
828
  router.post("/setup", async (req, res) => {
768
829
  try {
769
- const result = await staffService.setupOwner(req.body);
830
+ const result = await authService.setupOwner(req.body);
770
831
  core.sendSuccess(res, result, 201);
771
832
  } catch (error) {
772
833
  handleStaffError(res, error, logger);
@@ -776,7 +837,7 @@ function createAuthRoutes(staffService, auth, logger, allowSelfPasswordChange) {
776
837
  try {
777
838
  const { email, password } = req.body;
778
839
  const ip = req.ip || req.socket.remoteAddress || "";
779
- const result = await staffService.login(email, password, ip);
840
+ const result = await authService.login(email, password, ip);
780
841
  core.sendSuccess(res, result);
781
842
  } catch (error) {
782
843
  handleStaffError(res, error, logger);
@@ -796,7 +857,7 @@ function createAuthRoutes(staffService, auth, logger, allowSelfPasswordChange) {
796
857
  try {
797
858
  const user = req.user;
798
859
  const { oldPassword, newPassword } = req.body;
799
- await staffService.changeOwnPassword(user.staffId, oldPassword, newPassword);
860
+ await authService.changeOwnPassword(user.staffId, oldPassword, newPassword);
800
861
  core.sendSuccess(res, { message: "Password changed successfully" });
801
862
  } catch (error) {
802
863
  handleStaffError(res, error, logger);
@@ -805,7 +866,7 @@ function createAuthRoutes(staffService, auth, logger, allowSelfPasswordChange) {
805
866
  }
806
867
  return router;
807
868
  }
808
- function createStaffRoutes(staffService, logger) {
869
+ function createStaffRoutes(staffService, logger, authService) {
809
870
  const router = express.Router();
810
871
  router.get("/", async (req, res) => {
811
872
  try {
@@ -861,7 +922,10 @@ function createStaffRoutes(staffService, logger) {
861
922
  });
862
923
  router.put("/:staffId/password", async (req, res) => {
863
924
  try {
864
- await staffService.resetPassword(req.params["staffId"], req.body.password);
925
+ if (!authService) {
926
+ throw new Error("AuthService not available");
927
+ }
928
+ await authService.resetPassword(req.params["staffId"], req.body.password);
865
929
  core.sendSuccess(res, { message: "Password reset successfully" });
866
930
  } catch (error) {
867
931
  handleStaffError(res, error, logger);
@@ -909,55 +973,11 @@ function createPermissionGroupRoutes(permissionService, auth, logger) {
909
973
  // src/routes/index.ts
910
974
  function createRoutes(services, auth, logger, allowSelfPasswordChange) {
911
975
  const router = express.Router();
912
- router.use("/", createAuthRoutes(services.staff, auth, logger, allowSelfPasswordChange));
913
- router.use("/", auth.verifyToken, auth.ownerOnly, createStaffRoutes(services.staff, logger));
976
+ router.use("/", createAuthRoutes(services.staff, services.auth, auth, logger, allowSelfPasswordChange));
977
+ router.use("/", auth.verifyToken, auth.ownerOnly, createStaffRoutes(services.staff, logger, services.auth));
914
978
  router.use("/permission-groups", createPermissionGroupRoutes(services.permissions, auth, logger));
915
979
  return router;
916
980
  }
917
- var StaffEngineConfigSchema = zod.z.object({
918
- db: zod.z.object({
919
- connection: zod.z.unknown().refine((v) => v !== void 0 && v !== null, {
920
- message: "db.connection is required"
921
- }),
922
- collectionPrefix: zod.z.string().optional()
923
- }),
924
- redis: zod.z.object({
925
- connection: zod.z.unknown(),
926
- keyPrefix: zod.z.string().optional()
927
- }).optional(),
928
- logger: zod.z.object({
929
- info: zod.z.function(),
930
- warn: zod.z.function(),
931
- error: zod.z.function()
932
- }).optional(),
933
- tenantId: zod.z.string().optional(),
934
- auth: zod.z.object({
935
- jwtSecret: zod.z.string().min(1),
936
- staffTokenExpiry: zod.z.string().optional(),
937
- ownerTokenExpiry: zod.z.string().optional(),
938
- permissionCacheTtlMs: zod.z.number().int().positive().optional()
939
- }),
940
- adapters: zod.z.object({
941
- hashPassword: zod.z.function(),
942
- comparePassword: zod.z.function()
943
- }),
944
- hooks: zod.z.object({
945
- onStaffCreated: zod.z.function().optional(),
946
- onLogin: zod.z.function().optional(),
947
- onLoginFailed: zod.z.function().optional(),
948
- onPermissionsChanged: zod.z.function().optional(),
949
- onStatusChanged: zod.z.function().optional(),
950
- onMetric: zod.z.function().optional()
951
- }).optional(),
952
- options: zod.z.object({
953
- requireEmailUniqueness: zod.z.boolean().optional(),
954
- allowSelfPasswordChange: zod.z.boolean().optional(),
955
- rateLimiter: zod.z.object({
956
- windowMs: zod.z.number().int().positive().optional(),
957
- maxAttempts: zod.z.number().int().positive().optional()
958
- }).optional()
959
- }).optional()
960
- });
961
981
  function createStaffEngine(config) {
962
982
  const parseResult = StaffEngineConfigSchema.safeParse(config);
963
983
  if (!parseResult.success) {
@@ -1012,13 +1032,20 @@ function createStaffEngine(config) {
1012
1032
  adapters: config.adapters,
1013
1033
  hooks: config.hooks ?? {},
1014
1034
  permissionCache,
1035
+ logger,
1036
+ tenantId: config.tenantId,
1037
+ requireEmailUniqueness: resolvedOptions.requireEmailUniqueness
1038
+ });
1039
+ const authService = new AuthService({
1040
+ Staff: StaffModel,
1041
+ adapters: config.adapters,
1042
+ hooks: config.hooks ?? {},
1015
1043
  rateLimiter,
1016
1044
  logger,
1017
1045
  tenantId: config.tenantId,
1018
1046
  jwtSecret: resolvedAuth.jwtSecret,
1019
1047
  staffTokenExpiry: resolvedAuth.staffTokenExpiry,
1020
1048
  ownerTokenExpiry: resolvedAuth.ownerTokenExpiry,
1021
- requireEmailUniqueness: resolvedOptions.requireEmailUniqueness,
1022
1049
  allowSelfPasswordChange: resolvedOptions.allowSelfPasswordChange
1023
1050
  });
1024
1051
  const auth = createAuthMiddleware(
@@ -1029,7 +1056,7 @@ function createStaffEngine(config) {
1029
1056
  config.tenantId
1030
1057
  );
1031
1058
  const routes = createRoutes(
1032
- { staff: staffService, permissions: permissionService },
1059
+ { staff: staffService, auth: authService, permissions: permissionService },
1033
1060
  auth,
1034
1061
  logger,
1035
1062
  resolvedOptions.allowSelfPasswordChange
@@ -1042,6 +1069,7 @@ function createStaffEngine(config) {
1042
1069
  routes,
1043
1070
  auth,
1044
1071
  staff: staffService,
1072
+ authService,
1045
1073
  permissions: permissionService,
1046
1074
  models: { Staff: StaffModel, PermissionGroup: PermissionGroupModel },
1047
1075
  destroy
@@ -1057,6 +1085,7 @@ Object.defineProperty(exports, "DEFAULT_OPTIONS", {
1057
1085
  get: function () { return staffTypes.DEFAULT_OPTIONS; }
1058
1086
  });
1059
1087
  exports.AlxStaffError = AlxStaffError;
1088
+ exports.AuthService = AuthService;
1060
1089
  exports.AuthenticationError = AuthenticationError;
1061
1090
  exports.AuthorizationError = AuthorizationError;
1062
1091
  exports.DEFAULTS = DEFAULTS;
@@ -1073,6 +1102,7 @@ exports.PermissionService = PermissionService;
1073
1102
  exports.RateLimitError = RateLimitError;
1074
1103
  exports.RateLimiterService = RateLimiterService;
1075
1104
  exports.SetupError = SetupError;
1105
+ exports.StaffEngineConfigSchema = StaffEngineConfigSchema;
1076
1106
  exports.StaffNotFoundError = StaffNotFoundError;
1077
1107
  exports.StaffService = StaffService;
1078
1108
  exports.TokenError = TokenError;