@fonderie/workspaces 1.2.0 → 1.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.
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<!-- GENERATED — do not edit. Regenerate with: npm run docs:signatures -->
|
|
2
|
+
|
|
3
|
+
# @fonderie/workspaces — outcomes
|
|
4
|
+
|
|
5
|
+
What this package does to a running app: tables its migrations create,
|
|
6
|
+
rows it seeds, routes it registers. Generated from the migration SQL and
|
|
7
|
+
route tables in source — trust this file instead of reading `dist/` or
|
|
8
|
+
downloading tarballs.
|
|
9
|
+
|
|
10
|
+
## Database tables (after all migrations)
|
|
11
|
+
|
|
12
|
+
### `fonderie_role_user_workspaces`
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
user_id UUID NOT NULL
|
|
16
|
+
workspace_id UUID NOT NULL
|
|
17
|
+
role_id UUID NOT NULL
|
|
18
|
+
confirmed BOOLEAN NOT NULL DEFAULT false
|
|
19
|
+
removed BOOLEAN NOT NULL DEFAULT false
|
|
20
|
+
suspended BOOLEAN NOT NULL DEFAULT false
|
|
21
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
22
|
+
-- PRIMARY KEY (user_id, workspace_id, role_id)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### `fonderie_roles`
|
|
26
|
+
|
|
27
|
+
```sql
|
|
28
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
|
|
29
|
+
name TEXT NOT NULL
|
|
30
|
+
workspace_id UUID
|
|
31
|
+
is_system BOOLEAN NOT NULL DEFAULT false
|
|
32
|
+
active BOOLEAN NOT NULL DEFAULT true
|
|
33
|
+
description TEXT
|
|
34
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### `fonderie_workspace_invitations`
|
|
38
|
+
|
|
39
|
+
```sql
|
|
40
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
|
|
41
|
+
workspace_id UUID NOT NULL
|
|
42
|
+
email TEXT NOT NULL
|
|
43
|
+
role_id UUID NOT NULL
|
|
44
|
+
token TEXT UNIQUE
|
|
45
|
+
pin TEXT
|
|
46
|
+
status TEXT NOT NULL DEFAULT 'PENDING'
|
|
47
|
+
expires_at TIMESTAMPTZ NOT NULL
|
|
48
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### `fonderie_workspaces`
|
|
52
|
+
|
|
53
|
+
```sql
|
|
54
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
|
|
55
|
+
name TEXT NOT NULL
|
|
56
|
+
slug TEXT NOT NULL
|
|
57
|
+
type TEXT NOT NULL DEFAULT 'ORGANIZATION'
|
|
58
|
+
description TEXT
|
|
59
|
+
plan TEXT NOT NULL DEFAULT 'free'
|
|
60
|
+
owner_id UUID NOT NULL
|
|
61
|
+
settings JSONB NOT NULL DEFAULT '{}'
|
|
62
|
+
archived_at TIMESTAMPTZ
|
|
63
|
+
archived_by UUID
|
|
64
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
65
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
66
|
+
is_personal BOOLEAN NOT NULL DEFAULT false
|
|
67
|
+
motto TEXT
|
|
68
|
+
phone TEXT
|
|
69
|
+
business_type TEXT
|
|
70
|
+
address JSONB NOT NULL DEFAULT '{}'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Raw SQL ships in `node_modules/@fonderie/workspaces/dist/migrations/sql/` — read it there if you must; never download tarballs.
|
|
74
|
+
|
|
75
|
+
## Seeded rows (behavioral contract)
|
|
76
|
+
|
|
77
|
+
```sql
|
|
78
|
+
INSERT INTO fonderie_roles (name, workspace_id, is_system, description) VALUES ('ADMIN', NULL, true, 'System administrator with full access'), ('GUEST', NULL, true, 'Guest user with read-only access') ON CONFLICT DO NOTHING;
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## HTTP routes registered
|
|
82
|
+
|
|
83
|
+
| Method | Path | Middleware chain (auth / validation / handler) |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| GET | `/workspaces` | `requireAuth → workspace.list` |
|
|
86
|
+
| POST | `/workspaces` | `requireAuth → validate(createWorkspaceSchema) → workspace.create` |
|
|
87
|
+
| PUT | `/workspaces` | `requireAuth → wsCtx → validate(updateWorkspaceSchema) → workspace.update` |
|
|
88
|
+
| GET | `/workspaces/:id` | `requireAuth → wsCtx → workspace.get` |
|
|
89
|
+
| POST | `/workspaces/archive` | `requireAuth → wsCtx → workspace.archive` |
|
|
90
|
+
| GET | `/workspaces/invitations` | `requireAuth → wsCtx → invitation.list` |
|
|
91
|
+
| POST | `/workspaces/invitations` | `requireAuth → wsCtx → validate(createInvitationsSchema) → invitation.invite` |
|
|
92
|
+
| DELETE | `/workspaces/invitations/:inviteId` | `requireAuth → wsCtx → invitation.cancel` |
|
|
93
|
+
| POST | `/workspaces/invitations/accept` | `requireAuth → validate(acceptInvitationSchema) → invitation.accept` |
|
|
94
|
+
| GET | `/workspaces/members` | `requireAuth → wsCtx → member.list` |
|
|
95
|
+
| DELETE | `/workspaces/members/:userId` | `requireAuth → wsCtx → member.remove` |
|
|
96
|
+
| GET | `/workspaces/members/:userId/roles` | `requireAuth → wsCtx → member.getUserRoles` |
|
|
97
|
+
| POST | `/workspaces/members/:userId/roles` | `requireAuth → wsCtx → validate(addMemberRoleSchema) → member.addRole` |
|
|
98
|
+
| DELETE | `/workspaces/members/:userId/roles/:roleId` | `requireAuth → wsCtx → member.removeRole` |
|
|
99
|
+
| POST | `/workspaces/restore` | `requireAuth → wsCtx → workspace.restore` |
|
|
100
|
+
| GET | `/workspaces/roles` | `requireAuth → wsCtx → role.list` |
|
|
101
|
+
| POST | `/workspaces/roles` | `requireAuth → wsCtx → validate(createRoleSchema) → role.create` |
|
|
102
|
+
| DELETE | `/workspaces/roles/:roleId` | `requireAuth → wsCtx → role.remove` |
|
|
103
|
+
| GET | `/workspaces/roles/:roleId` | `requireAuth → wsCtx → role.get` |
|
|
104
|
+
| PUT | `/workspaces/roles/:roleId` | `requireAuth → wsCtx → validate(updateRoleSchema) → role.update` |
|
|
105
|
+
| POST | `/workspaces/roles/:roleId/permissions` | `requireAuth → wsCtx → validate(setRolePermissionsSchema) → role.setPermissions` |
|
|
106
|
+
| GET | `/workspaces/settings` | `requireAuth → wsCtx → workspace.getSettings` |
|
|
107
|
+
| PUT | `/workspaces/settings` | `requireAuth → wsCtx → validate(updateSettingsSchema) → workspace.updateSettings` |
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<!-- GENERATED — do not edit. Regenerate with: npm run docs:signatures -->
|
|
2
|
+
|
|
3
|
+
# @fonderie/workspaces — signatures
|
|
4
|
+
|
|
5
|
+
## @fonderie/workspaces
|
|
6
|
+
|
|
7
|
+
Subpath exports: `@fonderie/workspaces/types`, `@fonderie/workspaces/middleware`, `@fonderie/workspaces/migrations`
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
new WorkspacesModule(store: IStoreAdapter, config?: IWorkspacesConfig, bus?: EventBus | undefined): WorkspacesModule
|
|
11
|
+
.name: "@fonderie/workspaces"
|
|
12
|
+
.deps: string[]
|
|
13
|
+
.install(app: IFonderieApp): void
|
|
14
|
+
|
|
15
|
+
interface IWorkspacesConfig {
|
|
16
|
+
invitationTtl?: string;
|
|
17
|
+
personalWorkspace?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type WorkspacesMessageKey = (typeof MESSAGE_KEYS)[keyof typeof MESSAGE_KEYS];
|
|
21
|
+
|
|
22
|
+
type WorkspacesEventKey = (typeof EVENT_KEYS)[keyof typeof EVENT_KEYS];
|
|
23
|
+
|
|
24
|
+
const MESSAGE_KEYS: { readonly workspaceInvitation: "workspace-invitation"; }
|
|
25
|
+
|
|
26
|
+
const EVENT_KEYS: { readonly personalWorkspaceCreated: "fonderie.workspace.personal.created"; }
|
|
27
|
+
|
|
28
|
+
type WorkspaceType = 'ORGANIZATION' | 'PERSONAL' | 'TEAM' | 'COMMUNITY' | 'VENDOR';
|
|
29
|
+
|
|
30
|
+
type InvitationStatus = 'PENDING' | 'ACCEPTED' | 'REJECTED' | 'CANCELLED';
|
|
31
|
+
|
|
32
|
+
interface IWorkspace {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
slug: string;
|
|
36
|
+
type: WorkspaceType;
|
|
37
|
+
description: string | null;
|
|
38
|
+
motto: string | null;
|
|
39
|
+
phone: string | null;
|
|
40
|
+
businessType: string | null;
|
|
41
|
+
address: IWorkspaceAddress | null;
|
|
42
|
+
plan: string;
|
|
43
|
+
ownerId: string;
|
|
44
|
+
isPersonal: boolean;
|
|
45
|
+
archivedAt: string | null;
|
|
46
|
+
archivedBy: string | null;
|
|
47
|
+
createdAt: string;
|
|
48
|
+
updatedAt: string | null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface IRole {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
isSystem: boolean;
|
|
55
|
+
active: boolean;
|
|
56
|
+
description: string | null;
|
|
57
|
+
workspaceId: string | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface IMember {
|
|
61
|
+
userId: string;
|
|
62
|
+
workspaceId: string;
|
|
63
|
+
roleId: string;
|
|
64
|
+
roleName: string;
|
|
65
|
+
confirmed: boolean;
|
|
66
|
+
createdAt: string;
|
|
67
|
+
firstName: string | null;
|
|
68
|
+
lastName: string | null;
|
|
69
|
+
email: string | null;
|
|
70
|
+
profileImageUrl: string | null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface IInvitation {
|
|
74
|
+
id: string;
|
|
75
|
+
workspaceId: string;
|
|
76
|
+
email: string;
|
|
77
|
+
roleId: string;
|
|
78
|
+
token: string;
|
|
79
|
+
pin: string | null;
|
|
80
|
+
status: InvitationStatus;
|
|
81
|
+
expiresAt: string;
|
|
82
|
+
createdAt: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface IWorkspaceSettings {
|
|
86
|
+
locale: string;
|
|
87
|
+
timezone: string;
|
|
88
|
+
currency: string;
|
|
89
|
+
dateFormat: string;
|
|
90
|
+
timeFormat: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
interface IWorkspaceDTO {
|
|
94
|
+
id: string;
|
|
95
|
+
name: string;
|
|
96
|
+
slug: string;
|
|
97
|
+
type: string;
|
|
98
|
+
description: string;
|
|
99
|
+
motto: string;
|
|
100
|
+
phone: string;
|
|
101
|
+
businessType: string;
|
|
102
|
+
address: IWorkspaceAddressDTO;
|
|
103
|
+
plan: string;
|
|
104
|
+
ownerId: string;
|
|
105
|
+
isPersonal: boolean;
|
|
106
|
+
isArchived: boolean;
|
|
107
|
+
archivedAt: string;
|
|
108
|
+
createdAt: string;
|
|
109
|
+
updatedAt: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface IRoleDTO {
|
|
113
|
+
id: string;
|
|
114
|
+
name: string;
|
|
115
|
+
isSystem: boolean;
|
|
116
|
+
active: boolean;
|
|
117
|
+
description: string;
|
|
118
|
+
workspaceId: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
interface IMemberDTO {
|
|
122
|
+
userId: string;
|
|
123
|
+
workspaceId: string;
|
|
124
|
+
roleId: string;
|
|
125
|
+
roleName: string;
|
|
126
|
+
confirmed: boolean;
|
|
127
|
+
createdAt: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
interface IInvitationDTO {
|
|
131
|
+
id: string;
|
|
132
|
+
workspaceId: string;
|
|
133
|
+
email: string;
|
|
134
|
+
roleId: string;
|
|
135
|
+
token: string;
|
|
136
|
+
status: string;
|
|
137
|
+
expiresAt: string;
|
|
138
|
+
createdAt: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
interface IWorkspaceSettingsDTO {
|
|
142
|
+
locale: string;
|
|
143
|
+
timezone: string;
|
|
144
|
+
currency: string;
|
|
145
|
+
dateFormat: string;
|
|
146
|
+
timeFormat: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function toWorkspaceDTO(ws: IWorkspace): IWorkspaceDTO
|
|
150
|
+
|
|
151
|
+
function toRoleDTO(role: IRole): IRoleDTO
|
|
152
|
+
|
|
153
|
+
function toMemberDTO(m: IMember): IMemberDTO
|
|
154
|
+
|
|
155
|
+
function toInvitationDTO(inv: IInvitation): IInvitationDTO
|
|
156
|
+
|
|
157
|
+
function toSettingsDTO(s: IWorkspaceSettings): IWorkspaceSettingsDTO
|
|
158
|
+
|
|
159
|
+
function withWorkspace(store: IStoreAdapter): Middleware
|
|
160
|
+
|
|
161
|
+
function requireWorkspace(ctx: IFonderieContext, next: () => Promise<Response>): Promise<Response>
|
|
162
|
+
|
|
163
|
+
namespace schemas — exports: acceptInvitationSchema, addMemberRoleSchema, createInvitationsSchema, createRoleSchema, createWorkspaceSchema, setRolePermissionsSchema, updateRoleSchema, updateSettingsSchema, updateWorkspaceSchema
|
|
164
|
+
```
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
-- fonderie_workspaces
|
|
2
|
+
CREATE TABLE IF NOT EXISTS fonderie_workspaces (
|
|
3
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
4
|
+
name TEXT NOT NULL,
|
|
5
|
+
slug TEXT NOT NULL,
|
|
6
|
+
type TEXT NOT NULL DEFAULT 'ORGANIZATION',
|
|
7
|
+
description TEXT,
|
|
8
|
+
plan TEXT NOT NULL DEFAULT 'free',
|
|
9
|
+
owner_id UUID NOT NULL,
|
|
10
|
+
settings JSONB NOT NULL DEFAULT '{}',
|
|
11
|
+
archived_at TIMESTAMPTZ,
|
|
12
|
+
archived_by UUID,
|
|
13
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
14
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_fw_slug
|
|
18
|
+
ON fonderie_workspaces (slug) WHERE archived_at IS NULL;
|
|
19
|
+
|
|
20
|
+
CREATE INDEX IF NOT EXISTS idx_fw_owner
|
|
21
|
+
ON fonderie_workspaces (owner_id);
|
|
22
|
+
|
|
23
|
+
-- fonderie_roles
|
|
24
|
+
CREATE TABLE IF NOT EXISTS fonderie_roles (
|
|
25
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
26
|
+
name TEXT NOT NULL,
|
|
27
|
+
workspace_id UUID,
|
|
28
|
+
is_system BOOLEAN NOT NULL DEFAULT false,
|
|
29
|
+
active BOOLEAN NOT NULL DEFAULT true,
|
|
30
|
+
description TEXT,
|
|
31
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_fr_name_ws
|
|
35
|
+
ON fonderie_roles (name, workspace_id) WHERE workspace_id IS NOT NULL;
|
|
36
|
+
|
|
37
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_fr_name_system
|
|
38
|
+
ON fonderie_roles (name) WHERE workspace_id IS NULL;
|
|
39
|
+
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_fr_workspace
|
|
41
|
+
ON fonderie_roles (workspace_id);
|
|
42
|
+
|
|
43
|
+
-- Seed system roles
|
|
44
|
+
INSERT INTO fonderie_roles (name, workspace_id, is_system, description)
|
|
45
|
+
VALUES
|
|
46
|
+
('ADMIN', NULL, true, 'System administrator with full access'),
|
|
47
|
+
('GUEST', NULL, true, 'Guest user with read-only access')
|
|
48
|
+
ON CONFLICT DO NOTHING;
|
|
49
|
+
|
|
50
|
+
-- fonderie_role_user_workspaces: one row per (user, workspace, role) assignment
|
|
51
|
+
CREATE TABLE IF NOT EXISTS fonderie_role_user_workspaces (
|
|
52
|
+
user_id UUID NOT NULL,
|
|
53
|
+
workspace_id UUID NOT NULL,
|
|
54
|
+
role_id UUID NOT NULL,
|
|
55
|
+
confirmed BOOLEAN NOT NULL DEFAULT false,
|
|
56
|
+
removed BOOLEAN NOT NULL DEFAULT false,
|
|
57
|
+
suspended BOOLEAN NOT NULL DEFAULT false,
|
|
58
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
59
|
+
PRIMARY KEY (user_id, workspace_id, role_id)
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
CREATE INDEX IF NOT EXISTS idx_fruw_user_ws
|
|
63
|
+
ON fonderie_role_user_workspaces (user_id, workspace_id);
|
|
64
|
+
|
|
65
|
+
CREATE INDEX IF NOT EXISTS idx_fruw_workspace
|
|
66
|
+
ON fonderie_role_user_workspaces (workspace_id);
|
|
67
|
+
|
|
68
|
+
-- fonderie_workspace_invitations
|
|
69
|
+
CREATE TABLE IF NOT EXISTS fonderie_workspace_invitations (
|
|
70
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
71
|
+
workspace_id UUID NOT NULL,
|
|
72
|
+
email TEXT NOT NULL,
|
|
73
|
+
role_id UUID NOT NULL,
|
|
74
|
+
token TEXT UNIQUE,
|
|
75
|
+
pin TEXT,
|
|
76
|
+
status TEXT NOT NULL DEFAULT 'PENDING',
|
|
77
|
+
expires_at TIMESTAMPTZ NOT NULL,
|
|
78
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
CREATE INDEX IF NOT EXISTS idx_fwi_ws_email
|
|
82
|
+
ON fonderie_workspace_invitations (workspace_id, email) WHERE status = 'PENDING';
|
|
83
|
+
|
|
84
|
+
CREATE INDEX IF NOT EXISTS idx_fwi_token
|
|
85
|
+
ON fonderie_workspace_invitations (token) WHERE token IS NOT NULL;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- Add is_personal flag to fonderie_workspaces
|
|
2
|
+
ALTER TABLE fonderie_workspaces
|
|
3
|
+
ADD COLUMN IF NOT EXISTS is_personal BOOLEAN NOT NULL DEFAULT false;
|
|
4
|
+
|
|
5
|
+
-- One personal workspace per user — enforced at the DB level
|
|
6
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_fw_personal_owner
|
|
7
|
+
ON fonderie_workspaces (owner_id)
|
|
8
|
+
WHERE is_personal = true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fonderie/workspaces",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Multi-tenant team layer — create workspaces, manage members, define custom roles, handle token-based invitations, and scope any route to a specific workspace.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fonderie-js",
|
|
@@ -77,17 +77,18 @@
|
|
|
77
77
|
},
|
|
78
78
|
"files": [
|
|
79
79
|
"dist",
|
|
80
|
+
"brain",
|
|
80
81
|
"LICENSE",
|
|
81
82
|
"README.md"
|
|
82
83
|
],
|
|
83
84
|
"repository": {
|
|
84
85
|
"type": "git",
|
|
85
|
-
"url": "git+https://github.com/
|
|
86
|
+
"url": "git+https://github.com/fonderiejs/sdk.git",
|
|
86
87
|
"directory": "packages/workspaces"
|
|
87
88
|
},
|
|
88
|
-
"homepage": "https://github.com/
|
|
89
|
+
"homepage": "https://github.com/fonderiejs/sdk/tree/main/packages/workspaces#readme",
|
|
89
90
|
"bugs": {
|
|
90
|
-
"url": "https://github.com/
|
|
91
|
+
"url": "https://github.com/fonderiejs/sdk/issues"
|
|
91
92
|
},
|
|
92
93
|
"dependencies": {
|
|
93
94
|
"zod": "^4.4.3"
|