@fonderie/workspaces 1.0.0 → 1.1.1
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 +110 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +74 -1
- package/dist/index.d.ts +74 -1
- package/dist/index.js +114 -30
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.d.ts +3 -0
- package/package.json +9 -6
- package/dist/migrations/sql/001_workspaces.sql +0 -85
- package/dist/migrations/sql/002_personal_workspace.sql +0 -8
- package/dist/migrations/sql/003_organization_profile.sql +0 -5
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
|
|
8
|
-
__defProp(target,
|
|
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(
|
|
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
|
-
[
|
|
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(
|
|
552
|
-
return findSystemRole(
|
|
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
|
|
737
|
+
const name2 = body?.["name"];
|
|
660
738
|
const description = body?.["description"];
|
|
661
739
|
const type = body?.["type"];
|
|
662
|
-
if (typeof
|
|
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 =
|
|
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:
|
|
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
|
|
936
|
+
const name2 = body?.["name"];
|
|
859
937
|
const description = body?.["description"];
|
|
860
|
-
if (typeof
|
|
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:
|
|
943
|
+
name: name2.trim(),
|
|
866
944
|
workspaceId: ctx.workspace.id
|
|
867
945
|
};
|
|
868
946
|
if (typeof description === "string") {
|
|
@@ -1157,8 +1235,8 @@ function invitationController(store, ttl, bus) {
|
|
|
1157
1235
|
if (needsDefault) {
|
|
1158
1236
|
const [row] = await store.query(
|
|
1159
1237
|
`SELECT id FROM fonderie_roles
|
|
1160
|
-
WHERE name = '
|
|
1161
|
-
|
|
1238
|
+
WHERE name = 'GUEST' AND workspace_id IS NULL AND is_system = true
|
|
1239
|
+
LIMIT 1`
|
|
1162
1240
|
);
|
|
1163
1241
|
defaultRoleId = row?.id;
|
|
1164
1242
|
if (!defaultRoleId) {
|
|
@@ -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
|
|
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
|
|
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,
|