@fonderie/workspaces 1.0.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.
@@ -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/dist/types.cjs ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+
16
+ // src/types.ts
17
+ var types_exports = {};
18
+ module.exports = __toCommonJS(types_exports);
19
+ //# sourceMappingURL=types.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["export type WorkspaceType = 'ORGANIZATION' | 'PERSONAL' | 'TEAM' | 'COMMUNITY' | 'VENDOR';\nexport type InvitationStatus = 'PENDING' | 'ACCEPTED' | 'REJECTED' | 'CANCELLED';\nexport type BusinessType = 'SOLE_PROP' | 'PARTNERSHIP' | 'LLC' | 'INC' | 'NONPROFIT' | 'COOPERATIVE';\n\nexport interface IWorkspaceAddress {\n\tline1?: string;\n\tline2?: string;\n\tcity?: string;\n\tstate?: string;\n\tzip?: string;\n\tcountry?: string;\n}\n\nexport interface IWorkspace {\n\tid: string;\n\tname: string;\n\tslug: string;\n\ttype: WorkspaceType;\n\tdescription: string | null;\n\tmotto: string | null;\n\tphone: string | null;\n\tbusinessType: string | null;\n\taddress: IWorkspaceAddress | null;\n\tplan: string;\n\townerId: string;\n\tisPersonal: boolean;\n\tarchivedAt: string | null;\n\tarchivedBy: string | null;\n\tcreatedAt: string;\n\tupdatedAt: string | null;\n}\n\nexport interface IRole {\n\tid: string;\n\tname: string;\n\tisSystem: boolean;\n\tactive: boolean;\n\tdescription: string | null;\n\tworkspaceId: string | null;\n}\n\nexport interface IMember {\n\tuserId: string;\n\tworkspaceId: string;\n\troleId: string;\n\troleName: string;\n\tconfirmed: boolean;\n\tcreatedAt: string;\n\tfirstName: string | null;\n\tlastName: string | null;\n\temail: string | null;\n\tprofileImageUrl: string | null;\n}\n\nexport interface IInvitation {\n\tid: string;\n\tworkspaceId: string;\n\temail: string;\n\troleId: string;\n\ttoken: string;\n\tpin: string | null;\n\tstatus: InvitationStatus;\n\texpiresAt: string;\n\tcreatedAt: string;\n}\n\nexport interface IWorkspaceSettings {\n\tlocale: string;\n\ttimezone: string;\n\tcurrency: string;\n\tdateFormat: string;\n\ttimeFormat: string;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
@@ -0,0 +1,69 @@
1
+ type WorkspaceType = 'ORGANIZATION' | 'PERSONAL' | 'TEAM' | 'COMMUNITY' | 'VENDOR';
2
+ type InvitationStatus = 'PENDING' | 'ACCEPTED' | 'REJECTED' | 'CANCELLED';
3
+ type BusinessType = 'SOLE_PROP' | 'PARTNERSHIP' | 'LLC' | 'INC' | 'NONPROFIT' | 'COOPERATIVE';
4
+ interface IWorkspaceAddress {
5
+ line1?: string;
6
+ line2?: string;
7
+ city?: string;
8
+ state?: string;
9
+ zip?: string;
10
+ country?: string;
11
+ }
12
+ interface IWorkspace {
13
+ id: string;
14
+ name: string;
15
+ slug: string;
16
+ type: WorkspaceType;
17
+ description: string | null;
18
+ motto: string | null;
19
+ phone: string | null;
20
+ businessType: string | null;
21
+ address: IWorkspaceAddress | null;
22
+ plan: string;
23
+ ownerId: string;
24
+ isPersonal: boolean;
25
+ archivedAt: string | null;
26
+ archivedBy: string | null;
27
+ createdAt: string;
28
+ updatedAt: string | null;
29
+ }
30
+ interface IRole {
31
+ id: string;
32
+ name: string;
33
+ isSystem: boolean;
34
+ active: boolean;
35
+ description: string | null;
36
+ workspaceId: string | null;
37
+ }
38
+ interface IMember {
39
+ userId: string;
40
+ workspaceId: string;
41
+ roleId: string;
42
+ roleName: string;
43
+ confirmed: boolean;
44
+ createdAt: string;
45
+ firstName: string | null;
46
+ lastName: string | null;
47
+ email: string | null;
48
+ profileImageUrl: string | null;
49
+ }
50
+ interface IInvitation {
51
+ id: string;
52
+ workspaceId: string;
53
+ email: string;
54
+ roleId: string;
55
+ token: string;
56
+ pin: string | null;
57
+ status: InvitationStatus;
58
+ expiresAt: string;
59
+ createdAt: string;
60
+ }
61
+ interface IWorkspaceSettings {
62
+ locale: string;
63
+ timezone: string;
64
+ currency: string;
65
+ dateFormat: string;
66
+ timeFormat: string;
67
+ }
68
+
69
+ export type { BusinessType, IInvitation, IMember, IRole, IWorkspace, IWorkspaceAddress, IWorkspaceSettings, InvitationStatus, WorkspaceType };
@@ -0,0 +1,69 @@
1
+ type WorkspaceType = 'ORGANIZATION' | 'PERSONAL' | 'TEAM' | 'COMMUNITY' | 'VENDOR';
2
+ type InvitationStatus = 'PENDING' | 'ACCEPTED' | 'REJECTED' | 'CANCELLED';
3
+ type BusinessType = 'SOLE_PROP' | 'PARTNERSHIP' | 'LLC' | 'INC' | 'NONPROFIT' | 'COOPERATIVE';
4
+ interface IWorkspaceAddress {
5
+ line1?: string;
6
+ line2?: string;
7
+ city?: string;
8
+ state?: string;
9
+ zip?: string;
10
+ country?: string;
11
+ }
12
+ interface IWorkspace {
13
+ id: string;
14
+ name: string;
15
+ slug: string;
16
+ type: WorkspaceType;
17
+ description: string | null;
18
+ motto: string | null;
19
+ phone: string | null;
20
+ businessType: string | null;
21
+ address: IWorkspaceAddress | null;
22
+ plan: string;
23
+ ownerId: string;
24
+ isPersonal: boolean;
25
+ archivedAt: string | null;
26
+ archivedBy: string | null;
27
+ createdAt: string;
28
+ updatedAt: string | null;
29
+ }
30
+ interface IRole {
31
+ id: string;
32
+ name: string;
33
+ isSystem: boolean;
34
+ active: boolean;
35
+ description: string | null;
36
+ workspaceId: string | null;
37
+ }
38
+ interface IMember {
39
+ userId: string;
40
+ workspaceId: string;
41
+ roleId: string;
42
+ roleName: string;
43
+ confirmed: boolean;
44
+ createdAt: string;
45
+ firstName: string | null;
46
+ lastName: string | null;
47
+ email: string | null;
48
+ profileImageUrl: string | null;
49
+ }
50
+ interface IInvitation {
51
+ id: string;
52
+ workspaceId: string;
53
+ email: string;
54
+ roleId: string;
55
+ token: string;
56
+ pin: string | null;
57
+ status: InvitationStatus;
58
+ expiresAt: string;
59
+ createdAt: string;
60
+ }
61
+ interface IWorkspaceSettings {
62
+ locale: string;
63
+ timezone: string;
64
+ currency: string;
65
+ dateFormat: string;
66
+ timeFormat: string;
67
+ }
68
+
69
+ export type { BusinessType, IInvitation, IMember, IRole, IWorkspace, IWorkspaceAddress, IWorkspaceSettings, InvitationStatus, WorkspaceType };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@fonderie/workspaces",
3
+ "version": "1.0.0",
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
+ "keywords": [
6
+ "fonderie-js",
7
+ "workspaces",
8
+ "multi-tenant",
9
+ "teams",
10
+ "organizations",
11
+ "invitations",
12
+ "rbac",
13
+ "saas",
14
+ "typescript"
15
+ ],
16
+ "license": "MIT",
17
+ "type": "module",
18
+ "engines": {
19
+ "node": ">=20"
20
+ },
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js",
25
+ "require": "./dist/index.cjs"
26
+ },
27
+ "./types": {
28
+ "types": "./dist/types.d.ts",
29
+ "import": "./dist/types.js",
30
+ "require": "./dist/types.cjs"
31
+ },
32
+ "./middleware": {
33
+ "types": "./dist/middlewares/index.d.ts",
34
+ "import": "./dist/middlewares/index.js",
35
+ "require": "./dist/middlewares/index.cjs"
36
+ },
37
+ "./migrations": {
38
+ "types": "./dist/migrations/index.d.ts",
39
+ "import": "./dist/migrations/index.js"
40
+ }
41
+ },
42
+ "main": "./dist/index.cjs",
43
+ "module": "./dist/index.js",
44
+ "types": "./dist/index.d.ts",
45
+ "scripts": {
46
+ "build": "tsup && node -e \"require('node:fs').cpSync('src/migrations/sql', 'dist/migrations/sql', {recursive:true})\"",
47
+ "dev": "tsup --watch",
48
+ "typecheck": "tsc --noEmit",
49
+ "test": "tsx --test src/__tests__/*.test.ts",
50
+ "lint": "biome lint src",
51
+ "format": "biome format --write src",
52
+ "check": "biome check --write src"
53
+ },
54
+ "peerDependencies": {
55
+ "@fonderie/billing": "^1.0.0",
56
+ "@fonderie/core": "^0.1.0",
57
+ "@fonderie/events": "^1.0.0",
58
+ "@fonderie/store": "^0.1.0"
59
+ },
60
+ "peerDependenciesMeta": {
61
+ "@fonderie/events": {
62
+ "optional": true
63
+ }
64
+ },
65
+ "devDependencies": {
66
+ "@fonderie/billing": "../billing",
67
+ "@fonderie/core": "../core",
68
+ "@fonderie/events": "../events",
69
+ "@fonderie/store": "../store",
70
+ "@types/node": "^25.6.0",
71
+ "tsup": "^8.5.1",
72
+ "tsx": "^4.21.0",
73
+ "typescript": "^6.0.3"
74
+ },
75
+ "publishConfig": {
76
+ "access": "public"
77
+ },
78
+ "files": [
79
+ "dist",
80
+ "LICENSE",
81
+ "README.md"
82
+ ],
83
+ "repository": {
84
+ "type": "git",
85
+ "url": "git+https://github.com/fonderie-js/sdk.git",
86
+ "directory": "packages/workspaces"
87
+ },
88
+ "homepage": "https://github.com/fonderie-js/sdk/tree/main/packages/workspaces#readme",
89
+ "bugs": {
90
+ "url": "https://github.com/fonderie-js/sdk/issues"
91
+ }
92
+ }