@fonderie/workspaces 1.0.0 → 1.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.
package/dist/index.cjs CHANGED
@@ -4,8 +4,8 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
6
  var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
7
+ for (var name2 in all)
8
+ __defProp(target, name2, { get: all[name2], enumerable: true });
9
9
  };
10
10
  var __copyProps = (to, from, except, desc) => {
11
11
  if (from && typeof from === "object" || typeof from === "function") {
@@ -24,6 +24,7 @@ __export(index_exports, {
24
24
  MESSAGE_KEYS: () => MESSAGE_KEYS,
25
25
  WorkspacesModule: () => WorkspacesModule,
26
26
  requireWorkspace: () => requireWorkspace,
27
+ schemas: () => schemas_exports,
27
28
  toInvitationDTO: () => toInvitationDTO,
28
29
  toMemberDTO: () => toMemberDTO,
29
30
  toRoleDTO: () => toRoleDTO,
@@ -44,6 +45,83 @@ var EVENT_KEYS = {
44
45
  // src/routes.ts
45
46
  var import_middlewares = require("@fonderie/core/middlewares");
46
47
 
48
+ // src/schemas.ts
49
+ var schemas_exports = {};
50
+ __export(schemas_exports, {
51
+ acceptInvitationSchema: () => acceptInvitationSchema,
52
+ addMemberRoleSchema: () => addMemberRoleSchema,
53
+ createInvitationsSchema: () => createInvitationsSchema,
54
+ createRoleSchema: () => createRoleSchema,
55
+ createWorkspaceSchema: () => createWorkspaceSchema,
56
+ setRolePermissionsSchema: () => setRolePermissionsSchema,
57
+ updateRoleSchema: () => updateRoleSchema,
58
+ updateSettingsSchema: () => updateSettingsSchema,
59
+ updateWorkspaceSchema: () => updateWorkspaceSchema
60
+ });
61
+ var import_zod = require("zod");
62
+ var name = import_zod.z.string().trim().min(1, "name is required").max(200);
63
+ var email = import_zod.z.string().trim().pipe(import_zod.z.email());
64
+ var createWorkspaceSchema = import_zod.z.object({
65
+ name,
66
+ description: import_zod.z.string().max(2e3).optional(),
67
+ // PERSONAL workspaces are created automatically by the module; the
68
+ // controller enforces the rule — schema just types the field.
69
+ type: import_zod.z.string().max(40).optional()
70
+ });
71
+ var addressSchema = import_zod.z.object({
72
+ line1: import_zod.z.string().max(200).optional(),
73
+ line2: import_zod.z.string().max(200).optional(),
74
+ city: import_zod.z.string().max(100).optional(),
75
+ region: import_zod.z.string().max(100).optional(),
76
+ postalCode: import_zod.z.string().max(20).optional(),
77
+ country: import_zod.z.string().max(60).optional()
78
+ }).passthrough();
79
+ var updateWorkspaceSchema = import_zod.z.object({
80
+ name: import_zod.z.string().trim().min(1).max(200).optional(),
81
+ description: import_zod.z.string().max(2e3).nullable().optional(),
82
+ motto: import_zod.z.string().max(300).nullable().optional(),
83
+ phone: import_zod.z.string().max(32).nullable().optional(),
84
+ businessType: import_zod.z.string().max(100).nullable().optional(),
85
+ address: addressSchema.nullable().optional()
86
+ }).refine((o) => Object.values(o).some((v) => v !== void 0), "Provide at least one field");
87
+ var updateSettingsSchema = import_zod.z.object({
88
+ locale: import_zod.z.string().max(35).optional(),
89
+ timezone: import_zod.z.string().max(64).optional(),
90
+ currency: import_zod.z.string().max(8).optional(),
91
+ dateFormat: import_zod.z.string().max(32).optional(),
92
+ timeFormat: import_zod.z.string().max(32).optional()
93
+ }).refine((o) => Object.values(o).some((v) => v !== void 0), "No settings provided");
94
+ var addMemberRoleSchema = import_zod.z.object({ roleId: import_zod.z.string().min(1, "roleId is required") });
95
+ var inviteEntry = import_zod.z.object({
96
+ email,
97
+ roleId: import_zod.z.string().min(1).optional()
98
+ });
99
+ var createInvitationsSchema = import_zod.z.union([
100
+ inviteEntry,
101
+ import_zod.z.array(inviteEntry).min(1, "at least one invite is required")
102
+ ]);
103
+ var acceptInvitationSchema = import_zod.z.object({ pin: import_zod.z.string().trim().min(1, "pin is required") });
104
+ var createRoleSchema = import_zod.z.object({
105
+ name,
106
+ description: import_zod.z.string().max(1e3).optional()
107
+ });
108
+ var updateRoleSchema = import_zod.z.object({
109
+ name: import_zod.z.string().trim().min(1).max(200).optional(),
110
+ description: import_zod.z.string().max(1e3).nullable().optional(),
111
+ active: import_zod.z.boolean().optional()
112
+ }).refine((o) => Object.values(o).some((v) => v !== void 0), "Provide at least one field");
113
+ var setRolePermissionsSchema = import_zod.z.object({
114
+ permissions: import_zod.z.array(
115
+ import_zod.z.object({
116
+ permissionKey: import_zod.z.string().min(1),
117
+ canCreate: import_zod.z.boolean().optional(),
118
+ canRead: import_zod.z.boolean().optional(),
119
+ canUpdate: import_zod.z.boolean().optional(),
120
+ canDelete: import_zod.z.boolean().optional()
121
+ })
122
+ )
123
+ });
124
+
47
125
  // src/middlewares/workspace-context.ts
48
126
  var import_core = require("@fonderie/core");
49
127
 
@@ -456,10 +534,10 @@ async function createRole(opts, store) {
456
534
  if (!role) throw new Error("Failed to create role");
457
535
  return role;
458
536
  }
459
- async function findSystemRole(name, store) {
537
+ async function findSystemRole(name2, store) {
460
538
  const [row] = await store.query(
461
539
  `SELECT ${SELECT_ROLE} FROM fonderie_roles WHERE name = $1 AND is_system = true LIMIT 1`,
462
- [name]
540
+ [name2]
463
541
  );
464
542
  return row ?? null;
465
543
  }
@@ -548,8 +626,8 @@ var RoleModel = class {
548
626
  create(opts) {
549
627
  return createRole(opts, this.store);
550
628
  }
551
- findSystem(name) {
552
- return findSystemRole(name, this.store);
629
+ findSystem(name2) {
630
+ return findSystemRole(name2, this.store);
553
631
  }
554
632
  findById(id) {
555
633
  return getRoleById(id, this.store);
@@ -656,10 +734,10 @@ function workspaceController(store, config) {
656
734
  },
657
735
  async create(ctx) {
658
736
  const body = ctx.meta["body"];
659
- const name = body?.["name"];
737
+ const name2 = body?.["name"];
660
738
  const description = body?.["description"];
661
739
  const type = body?.["type"];
662
- if (typeof name !== "string" || name.trim().length === 0) {
740
+ if (typeof name2 !== "string" || name2.trim().length === 0) {
663
741
  return (0, import_core2.setApiResponse)(import_core2.HTTP.UNPROCESSABLE, "INVALID_PARAMETER", "name is required");
664
742
  }
665
743
  if (type === "PERSONAL") {
@@ -669,10 +747,10 @@ function workspaceController(store, config) {
669
747
  "Personal workspaces are created automatically"
670
748
  );
671
749
  }
672
- const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
750
+ const slug = name2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
673
751
  const workspace = await store.transaction(async (tx) => {
674
752
  const wsOpts = {
675
- name: name.trim(),
753
+ name: name2.trim(),
676
754
  slug,
677
755
  ownerId: ctx.user.id,
678
756
  type: typeof type === "string" ? type : "ORGANIZATION"
@@ -711,7 +789,7 @@ function workspaceController(store, config) {
711
789
  if (body?.["businessType"] !== void 0)
712
790
  opts.businessType = typeof body["businessType"] === "string" ? body["businessType"] : null;
713
791
  if (body?.["address"] !== void 0 && typeof body["address"] === "object")
714
- opts.address = body["address"];
792
+ opts.address = body["address"] ?? null;
715
793
  const workspace = await workspaces.update(ctx.workspace.id, opts);
716
794
  if (!workspace) return (0, import_core2.setApiResponse)(import_core2.HTTP.NOT_FOUND, "NOT_FOUND", "Workspace not found");
717
795
  return (0, import_core2.setApiResponse)(import_core2.HTTP.OK, "WORKSPACE_UPDATED", "Workspace updated successfully.", {
@@ -855,14 +933,14 @@ function roleController(store) {
855
933
  return (0, import_core4.setApiResponse)(import_core4.HTTP.NOT_FOUND, "NOT_FOUND", "Workspace not found");
856
934
  }
857
935
  const body = ctx.meta["body"];
858
- const name = body?.["name"];
936
+ const name2 = body?.["name"];
859
937
  const description = body?.["description"];
860
- if (typeof name !== "string" || name.trim().length === 0) {
938
+ if (typeof name2 !== "string" || name2.trim().length === 0) {
861
939
  return (0, import_core4.setApiResponse)(import_core4.HTTP.UNPROCESSABLE, "INVALID_PARAMETER", "name is required");
862
940
  }
863
941
  try {
864
942
  const opts = {
865
- name: name.trim(),
943
+ name: name2.trim(),
866
944
  workspaceId: ctx.workspace.id
867
945
  };
868
946
  if (typeof description === "string") {
@@ -1167,21 +1245,21 @@ function invitationController(store, ttl, bus) {
1167
1245
  }
1168
1246
  const results = await Promise.all(
1169
1247
  entries.map(async (entry) => {
1170
- const email = entry["email"];
1248
+ const email2 = entry["email"];
1171
1249
  const resolvedRoleId = entry["roleId"] ?? defaultRoleId;
1172
1250
  const invitation = await invitations.create({
1173
1251
  workspaceId: ctx.workspace.id,
1174
- email,
1252
+ email: email2,
1175
1253
  roleId: resolvedRoleId,
1176
1254
  ttl
1177
1255
  });
1178
1256
  bus?.emit(import_events.NOTIFICATION_EVENT, {
1179
1257
  type: MESSAGE_KEYS.workspaceInvitation,
1180
- recipient: { email, phone: null, deviceToken: null },
1258
+ recipient: { email: email2, phone: null, deviceToken: null },
1181
1259
  data: { token: invitation.token, pin: invitation.pin }
1182
1260
  }).catch(() => {
1183
1261
  });
1184
- return { invitationId: invitation.id, email };
1262
+ return { invitationId: invitation.id, email: email2 };
1185
1263
  })
1186
1264
  );
1187
1265
  return (0, import_core5.setApiResponse)(import_core5.HTTP.CREATED, "INVITATIONS_SENT", "Invitations sent successfully.", {
@@ -1226,36 +1304,36 @@ function buildWorkspaceRoutes(store, config, bus) {
1226
1304
  const invitation = invitationController(store, ttl, bus);
1227
1305
  return [
1228
1306
  // ── Workspace creation + listing (no workspace context required)
1229
- ["POST", "/workspaces", import_middlewares.requireAuth, workspace.create],
1307
+ ["POST", "/workspaces", import_middlewares.requireAuth, (0, import_middlewares.validate)(createWorkspaceSchema), workspace.create],
1230
1308
  ["GET", "/workspaces", import_middlewares.requireAuth, workspace.list],
1231
1309
  // ── Members (workspace resolved from X-Workspace-ID header)
1232
1310
  ["GET", "/workspaces/members", import_middlewares.requireAuth, wsCtx, member.list],
1233
1311
  ["DELETE", "/workspaces/members/:userId", import_middlewares.requireAuth, wsCtx, member.remove],
1234
1312
  ["GET", "/workspaces/members/:userId/roles", import_middlewares.requireAuth, wsCtx, member.getUserRoles],
1235
- ["POST", "/workspaces/members/:userId/roles", import_middlewares.requireAuth, wsCtx, member.addRole],
1313
+ ["POST", "/workspaces/members/:userId/roles", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(addMemberRoleSchema), member.addRole],
1236
1314
  ["DELETE", "/workspaces/members/:userId/roles/:roleId", import_middlewares.requireAuth, wsCtx, member.removeRole],
1237
1315
  // ── Invitations
1238
1316
  ["GET", "/workspaces/invitations", import_middlewares.requireAuth, wsCtx, invitation.list],
1239
- ["POST", "/workspaces/invitations", import_middlewares.requireAuth, wsCtx, invitation.invite],
1317
+ ["POST", "/workspaces/invitations", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(createInvitationsSchema), invitation.invite],
1240
1318
  ["DELETE", "/workspaces/invitations/:inviteId", import_middlewares.requireAuth, wsCtx, invitation.cancel],
1241
- ["POST", "/workspaces/invitations/accept", import_middlewares.requireAuth, invitation.accept],
1319
+ ["POST", "/workspaces/invitations/accept", import_middlewares.requireAuth, (0, import_middlewares.validate)(acceptInvitationSchema), invitation.accept],
1242
1320
  // ── Roles
1243
- ["POST", "/workspaces/roles", import_middlewares.requireAuth, wsCtx, role.create],
1321
+ ["POST", "/workspaces/roles", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(createRoleSchema), role.create],
1244
1322
  ["GET", "/workspaces/roles", import_middlewares.requireAuth, wsCtx, role.list],
1245
1323
  ["GET", "/workspaces/roles/:roleId", import_middlewares.requireAuth, wsCtx, role.get],
1246
- ["PUT", "/workspaces/roles/:roleId", import_middlewares.requireAuth, wsCtx, role.update],
1324
+ ["PUT", "/workspaces/roles/:roleId", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(updateRoleSchema), role.update],
1247
1325
  ["DELETE", "/workspaces/roles/:roleId", import_middlewares.requireAuth, wsCtx, role.remove],
1248
- ["POST", "/workspaces/roles/:roleId/permissions", import_middlewares.requireAuth, wsCtx, role.setPermissions],
1326
+ ["POST", "/workspaces/roles/:roleId/permissions", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(setRolePermissionsSchema), role.setPermissions],
1249
1327
  // ── Workspace lifecycle
1250
1328
  ["POST", "/workspaces/archive", import_middlewares.requireAuth, wsCtx, workspace.archive],
1251
1329
  ["POST", "/workspaces/restore", import_middlewares.requireAuth, wsCtx, workspace.restore],
1252
1330
  ["GET", "/workspaces/settings", import_middlewares.requireAuth, wsCtx, workspace.getSettings],
1253
- ["PUT", "/workspaces/settings", import_middlewares.requireAuth, wsCtx, workspace.updateSettings],
1331
+ ["PUT", "/workspaces/settings", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(updateSettingsSchema), workspace.updateSettings],
1254
1332
  // ── Path-based lookup by ID (admin / cross-workspace use)
1255
1333
  ["GET", "/workspaces/:id", import_middlewares.requireAuth, wsCtx, workspace.get],
1256
1334
  // ── Update current workspace — ID resolved from X-Workspace-ID header
1257
1335
  // (or personal workspace fallback when header is absent)
1258
- ["PUT", "/workspaces", import_middlewares.requireAuth, wsCtx, workspace.update]
1336
+ ["PUT", "/workspaces", import_middlewares.requireAuth, wsCtx, (0, import_middlewares.validate)(updateWorkspaceSchema), workspace.update]
1259
1337
  ];
1260
1338
  }
1261
1339
 
@@ -1286,14 +1364,14 @@ var WorkspacesModule = class {
1286
1364
  }
1287
1365
  }
1288
1366
  async provisionPersonalWorkspace(payload) {
1289
- const name = "My Workspace";
1367
+ const name2 = "My Workspace";
1290
1368
  const slug = `${payload.userId}-personal`;
1291
1369
  let workspaceId;
1292
1370
  await this.store.transaction(async (tx) => {
1293
1371
  const wsModel = new WorkspaceModel(tx);
1294
1372
  const roleModel = new RoleModel(tx);
1295
1373
  const memModel = new MemberModel(tx);
1296
- const ws = await wsModel.createPersonal({ name, slug, ownerId: payload.userId });
1374
+ const ws = await wsModel.createPersonal({ name: name2, slug, ownerId: payload.userId });
1297
1375
  if (!ws) return;
1298
1376
  workspaceId = ws.id;
1299
1377
  const adminRole = await roleModel.findSystem("ADMIN");
@@ -1329,6 +1407,7 @@ var requireWorkspace = async (ctx, next) => {
1329
1407
  MESSAGE_KEYS,
1330
1408
  WorkspacesModule,
1331
1409
  requireWorkspace,
1410
+ schemas,
1332
1411
  toInvitationDTO,
1333
1412
  toMemberDTO,
1334
1413
  toRoleDTO,