@fonderie/workspaces 1.2.2 → 2.1.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.
@@ -15,6 +15,7 @@ new WorkspacesModule(store: IStoreAdapter, config?: IWorkspacesConfig, bus?: Eve
15
15
  interface IWorkspacesConfig {
16
16
  invitationTtl?: string;
17
17
  personalWorkspace?: boolean;
18
+ routes?: Partial<Record<WorkspaceRouteId, WorkspaceRouteOverride>>;
18
19
  }
19
20
 
20
21
  type WorkspacesMessageKey = (typeof MESSAGE_KEYS)[keyof typeof MESSAGE_KEYS];
package/dist/index.cjs CHANGED
@@ -1061,7 +1061,6 @@ function roleController(store) {
1061
1061
  // src/controllers/invitation.controller.ts
1062
1062
  var import_core5 = require("@fonderie/core");
1063
1063
  var import_events = require("@fonderie/events");
1064
- var import_billing = require("@fonderie/billing");
1065
1064
 
1066
1065
  // src/services/invitations.ts
1067
1066
  var import_node_crypto = require("crypto");
@@ -1183,6 +1182,12 @@ var InvitationModel = class {
1183
1182
  };
1184
1183
 
1185
1184
  // src/controllers/invitation.controller.ts
1185
+ function seatLimitFromMeta(ctx) {
1186
+ const billing = ctx.meta["billing"];
1187
+ const status = billing?.statuses?.["seats"];
1188
+ if (!status || status.type === "feature" || typeof status.limit !== "number") return null;
1189
+ return status.limit;
1190
+ }
1186
1191
  function invitationController(store, ttl, bus) {
1187
1192
  const invitations = new InvitationModel(store);
1188
1193
  return {
@@ -1212,7 +1217,7 @@ function invitationController(store, ttl, bus) {
1212
1217
  return (0, import_core5.setApiResponse)(import_core5.HTTP.UNPROCESSABLE, "INVALID_PARAMETER", "email is required for every invite");
1213
1218
  }
1214
1219
  }
1215
- const seatLimit = (0, import_billing.getPlanLimit)(ctx, "seats");
1220
+ const seatLimit = seatLimitFromMeta(ctx);
1216
1221
  if (seatLimit !== null) {
1217
1222
  const [countRow] = await store.query(
1218
1223
  `SELECT COUNT(*) AS count FROM fonderie_role_user_workspaces
@@ -1302,38 +1307,45 @@ function buildWorkspaceRoutes(store, config, bus) {
1302
1307
  const member = memberController(store);
1303
1308
  const role = roleController(store);
1304
1309
  const invitation = invitationController(store, ttl, bus);
1310
+ const R = (id, method, path, ...handlers) => {
1311
+ const o = config.routes?.[id];
1312
+ if (!o) return [method, path, ...handlers];
1313
+ if (typeof o === "string") return [method, o, ...handlers];
1314
+ return [o.method ?? method, o.path ?? path, ...handlers];
1315
+ };
1305
1316
  return [
1306
1317
  // ── Workspace creation + listing (no workspace context required)
1307
- ["POST", "/workspaces", import_middlewares.requireAuth, (0, import_middlewares.validate)(createWorkspaceSchema), workspace.create],
1308
- ["GET", "/workspaces", import_middlewares.requireAuth, workspace.list],
1318
+ R("createWorkspace", "POST", "/workspaces", import_middlewares.requireAuth, (0, import_middlewares.validate)(createWorkspaceSchema), workspace.create),
1319
+ R("listWorkspaces", "GET", "/workspaces", import_middlewares.requireAuth, workspace.list),
1309
1320
  // ── Members (workspace resolved from X-Workspace-ID header)
1310
- ["GET", "/workspaces/members", import_middlewares.requireAuth, wsCtx, member.list],
1311
- ["DELETE", "/workspaces/members/:userId", import_middlewares.requireAuth, wsCtx, member.remove],
1312
- ["GET", "/workspaces/members/:userId/roles", import_middlewares.requireAuth, wsCtx, member.getUserRoles],
1313
- ["POST", "/workspaces/members/:userId/roles", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(addMemberRoleSchema), member.addRole],
1314
- ["DELETE", "/workspaces/members/:userId/roles/:roleId", import_middlewares.requireAuth, wsCtx, member.removeRole],
1321
+ R("listMembers", "GET", "/workspaces/members", import_middlewares.requireAuth, wsCtx, member.list),
1322
+ R("removeMember", "DELETE", "/workspaces/members/:userId", import_middlewares.requireAuth, wsCtx, member.remove),
1323
+ R("getMemberRoles", "GET", "/workspaces/members/:userId/roles", import_middlewares.requireAuth, wsCtx, member.getUserRoles),
1324
+ R("addMemberRole", "POST", "/workspaces/members/:userId/roles", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(addMemberRoleSchema), member.addRole),
1325
+ R("removeMemberRole", "DELETE", "/workspaces/members/:userId/roles/:roleId", import_middlewares.requireAuth, wsCtx, member.removeRole),
1315
1326
  // ── Invitations
1316
- ["GET", "/workspaces/invitations", import_middlewares.requireAuth, wsCtx, invitation.list],
1317
- ["POST", "/workspaces/invitations", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(createInvitationsSchema), invitation.invite],
1318
- ["DELETE", "/workspaces/invitations/:inviteId", import_middlewares.requireAuth, wsCtx, invitation.cancel],
1319
- ["POST", "/workspaces/invitations/accept", import_middlewares.requireAuth, (0, import_middlewares.validate)(acceptInvitationSchema), invitation.accept],
1327
+ R("listInvitations", "GET", "/workspaces/invitations", import_middlewares.requireAuth, wsCtx, invitation.list),
1328
+ R("invite", "POST", "/workspaces/invitations", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(createInvitationsSchema), invitation.invite),
1329
+ R("cancelInvitation", "DELETE", "/workspaces/invitations/:inviteId", import_middlewares.requireAuth, wsCtx, invitation.cancel),
1330
+ R("acceptInvitation", "POST", "/workspaces/invitations/accept", import_middlewares.requireAuth, (0, import_middlewares.validate)(acceptInvitationSchema), invitation.accept),
1320
1331
  // ── Roles
1321
- ["POST", "/workspaces/roles", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(createRoleSchema), role.create],
1322
- ["GET", "/workspaces/roles", import_middlewares.requireAuth, wsCtx, role.list],
1323
- ["GET", "/workspaces/roles/:roleId", import_middlewares.requireAuth, wsCtx, role.get],
1324
- ["PUT", "/workspaces/roles/:roleId", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(updateRoleSchema), role.update],
1325
- ["DELETE", "/workspaces/roles/:roleId", import_middlewares.requireAuth, wsCtx, role.remove],
1326
- ["POST", "/workspaces/roles/:roleId/permissions", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(setRolePermissionsSchema), role.setPermissions],
1332
+ R("createRole", "POST", "/workspaces/roles", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(createRoleSchema), role.create),
1333
+ R("listRoles", "GET", "/workspaces/roles", import_middlewares.requireAuth, wsCtx, role.list),
1334
+ R("getRole", "GET", "/workspaces/roles/:roleId", import_middlewares.requireAuth, wsCtx, role.get),
1335
+ R("updateRole", "PUT", "/workspaces/roles/:roleId", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(updateRoleSchema), role.update),
1336
+ R("removeRole", "DELETE", "/workspaces/roles/:roleId", import_middlewares.requireAuth, wsCtx, role.remove),
1337
+ R("setRolePermissions", "POST", "/workspaces/roles/:roleId/permissions", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(setRolePermissionsSchema), role.setPermissions),
1327
1338
  // ── Workspace lifecycle
1328
- ["POST", "/workspaces/archive", import_middlewares.requireAuth, wsCtx, workspace.archive],
1329
- ["POST", "/workspaces/restore", import_middlewares.requireAuth, wsCtx, workspace.restore],
1330
- ["GET", "/workspaces/settings", import_middlewares.requireAuth, wsCtx, workspace.getSettings],
1331
- ["PUT", "/workspaces/settings", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(updateSettingsSchema), workspace.updateSettings],
1339
+ R("archive", "POST", "/workspaces/archive", import_middlewares.requireAuth, wsCtx, workspace.archive),
1340
+ R("restore", "POST", "/workspaces/restore", import_middlewares.requireAuth, wsCtx, workspace.restore),
1341
+ R("getSettings", "GET", "/workspaces/settings", import_middlewares.requireAuth, wsCtx, workspace.getSettings),
1342
+ R("updateSettings", "PUT", "/workspaces/settings", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(updateSettingsSchema), workspace.updateSettings),
1332
1343
  // ── Path-based lookup by ID (admin / cross-workspace use)
1333
- ["GET", "/workspaces/:id", import_middlewares.requireAuth, wsCtx, workspace.get],
1334
- // ── Update current workspace — ID resolved from X-Workspace-ID header
1335
- // (or personal workspace fallback when header is absent)
1336
- ["PUT", "/workspaces", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(updateWorkspaceSchema), workspace.update]
1344
+ R("getWorkspace", "GET", "/workspaces/:id", import_middlewares.requireAuth, wsCtx, workspace.get),
1345
+ // ── Update current workspace — ID from :id path param (wsCtx) or the
1346
+ // X-Workspace-ID header (or personal-workspace fallback when absent). Set
1347
+ // routes.updateWorkspace = '/workspaces/:id' to match a path-id frontend.
1348
+ R("updateWorkspace", "PUT", "/workspaces", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(updateWorkspaceSchema), workspace.update)
1337
1349
  ];
1338
1350
  }
1339
1351
 
@@ -1349,7 +1361,9 @@ var WorkspacesModule = class {
1349
1361
  config;
1350
1362
  bus;
1351
1363
  name = "@fonderie/workspaces";
1352
- deps = ["@fonderie/auth", "@fonderie/billing"];
1364
+ // billing is OPTIONAL — seat limits activate if it's registered (read via
1365
+ // ctx.meta['billing']), otherwise invitations are unlimited. Not a hard dep.
1366
+ deps = ["@fonderie/auth"];
1353
1367
  install(app) {
1354
1368
  if (this.bus && this.config.personalWorkspace !== false) {
1355
1369
  this.bus.on(