@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;
@@ -0,0 +1,5 @@
1
+ ALTER TABLE fonderie_workspaces
2
+ ADD COLUMN IF NOT EXISTS motto TEXT,
3
+ ADD COLUMN IF NOT EXISTS phone TEXT,
4
+ ADD COLUMN IF NOT EXISTS business_type TEXT,
5
+ ADD COLUMN IF NOT EXISTS address JSONB NOT NULL DEFAULT '{}';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fonderie/workspaces",
3
- "version": "1.2.0",
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/fonderie-js/sdk.git",
86
+ "url": "git+https://github.com/fonderiejs/sdk.git",
86
87
  "directory": "packages/workspaces"
87
88
  },
88
- "homepage": "https://github.com/fonderie-js/sdk/tree/main/packages/workspaces#readme",
89
+ "homepage": "https://github.com/fonderiejs/sdk/tree/main/packages/workspaces#readme",
89
90
  "bugs": {
90
- "url": "https://github.com/fonderie-js/sdk/issues"
91
+ "url": "https://github.com/fonderiejs/sdk/issues"
91
92
  },
92
93
  "dependencies": {
93
94
  "zod": "^4.4.3"