@fonderie/workspaces 1.1.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 +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1235,8 +1235,8 @@ function invitationController(store, ttl, bus) {
|
|
|
1235
1235
|
if (needsDefault) {
|
|
1236
1236
|
const [row] = await store.query(
|
|
1237
1237
|
`SELECT id FROM fonderie_roles
|
|
1238
|
-
WHERE name = '
|
|
1239
|
-
|
|
1238
|
+
WHERE name = 'GUEST' AND workspace_id IS NULL AND is_system = true
|
|
1239
|
+
LIMIT 1`
|
|
1240
1240
|
);
|
|
1241
1241
|
defaultRoleId = row?.id;
|
|
1242
1242
|
if (!defaultRoleId) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/routes.ts","../src/schemas.ts","../src/middlewares/workspace-context.ts","../src/services/members.ts","../src/services/workspaces.ts","../src/controllers/workspace.controller.ts","../src/models/workspace.model.ts","../src/models/member.model.ts","../src/services/roles.ts","../src/models/role.model.ts","../src/dtos/workspace.ts","../src/controllers/member.controller.ts","../src/controllers/role.controller.ts","../src/controllers/invitation.controller.ts","../src/services/invitations.ts","../src/models/invitation.model.ts","../src/module.ts","../src/middlewares/require-workspace.ts"],"sourcesContent":["export { WorkspacesModule } from './module';\nexport type { IWorkspacesConfig, WorkspacesMessageKey, WorkspacesEventKey } from './config';\nexport { MESSAGE_KEYS, EVENT_KEYS } from './config';\nexport type {\n\tWorkspaceType,\n\tInvitationStatus,\n\tIWorkspace,\n\tIRole,\n\tIMember,\n\tIInvitation,\n\tIWorkspaceSettings,\n} from './types';\nexport type {\n\tIWorkspaceDTO,\n\tIRoleDTO,\n\tIMemberDTO,\n\tIInvitationDTO,\n\tIWorkspaceSettingsDTO,\n} from './dtos/workspace';\nexport {\n\ttoWorkspaceDTO,\n\ttoRoleDTO,\n\ttoMemberDTO,\n\ttoInvitationDTO,\n\ttoSettingsDTO,\n} from './dtos/workspace';\nexport { withWorkspace } from './middlewares/workspace-context';\nexport { requireWorkspace } from './middlewares/require-workspace';\n\n// Request validation — enforced contract for body-taking routes; exported\n// for docs generation and typed clients.\nexport * as schemas from './schemas';\n","export const MESSAGE_KEYS = {\n\tworkspaceInvitation: 'workspace-invitation',\n} as const;\n\nexport type WorkspacesMessageKey = (typeof MESSAGE_KEYS)[keyof typeof MESSAGE_KEYS];\n\nexport const EVENT_KEYS = {\n\tpersonalWorkspaceCreated: 'fonderie.workspace.personal.created',\n} as const;\n\nexport type WorkspacesEventKey = (typeof EVENT_KEYS)[keyof typeof EVENT_KEYS];\n\nexport interface IWorkspacesConfig {\n\t// How long invitations are valid. Default: '7d'\n\tinvitationTtl?: string;\n\n\t// Default role assigned to invited members. Default: 'member'\n\tdefaultRole?: string;\n\n\t// Auto-create a personal workspace when user.registered fires.\n\t// Requires an EventBus to be passed to WorkspacesModule. Default: true\n\tpersonalWorkspace?: boolean;\n}\n","import type { IStoreAdapter } from '@fonderie/store';\nimport type { Middleware } from '@fonderie/core';\nimport type { EventBus } from '@fonderie/events';\nimport { requireAuth, validate } from '@fonderie/core/middlewares';\n\nimport {\n\tcreateRoleSchema,\n\tupdateRoleSchema,\n\taddMemberRoleSchema,\n\tupdateSettingsSchema,\n\tcreateWorkspaceSchema,\n\tupdateWorkspaceSchema,\n\tacceptInvitationSchema,\n\tcreateInvitationsSchema,\n\tsetRolePermissionsSchema,\n} from './schemas';\n\nimport type { IWorkspacesConfig } from './config';\nimport { withWorkspace } from './middlewares/workspace-context';\n\nimport { workspaceController } from './controllers/workspace.controller';\nimport { memberController } from './controllers/member.controller';\nimport { roleController } from './controllers/role.controller';\nimport { invitationController } from './controllers/invitation.controller';\n\ntype RouteDefinition = [string, string, ...Middleware[]];\n\nexport function buildWorkspaceRoutes(\n\tstore: IStoreAdapter,\n\tconfig: IWorkspacesConfig,\n\tbus?: EventBus,\n): RouteDefinition[] {\n\tconst ttl = config.invitationTtl ?? '7d';\n\tconst wsCtx = withWorkspace(store);\n\n\tconst workspace = workspaceController(store, config);\n\tconst member = memberController(store);\n\tconst role = roleController(store);\n\tconst invitation = invitationController(store, ttl, bus);\n\n\treturn [\n\t\t// ── Workspace creation + listing (no workspace context required)\n\t\t['POST', '/workspaces', requireAuth, validate(createWorkspaceSchema), workspace.create],\n\t\t['GET', '/workspaces', requireAuth, workspace.list],\n\n\t\t// ── Members (workspace resolved from X-Workspace-ID header)\n\t\t['GET', '/workspaces/members', requireAuth, wsCtx, member.list],\n\t\t['DELETE', '/workspaces/members/:userId', requireAuth, wsCtx, member.remove],\n\t\t['GET', '/workspaces/members/:userId/roles', requireAuth, wsCtx, member.getUserRoles],\n\t\t['POST', '/workspaces/members/:userId/roles', requireAuth, wsCtx, validate(addMemberRoleSchema), member.addRole],\n\t\t['DELETE', '/workspaces/members/:userId/roles/:roleId', requireAuth, wsCtx, member.removeRole],\n\n\t\t// ── Invitations\n\t\t['GET', '/workspaces/invitations', requireAuth, wsCtx, invitation.list],\n\t\t['POST', '/workspaces/invitations', requireAuth, wsCtx, validate(createInvitationsSchema), invitation.invite],\n\t\t['DELETE', '/workspaces/invitations/:inviteId', requireAuth, wsCtx, invitation.cancel],\n\t\t['POST', '/workspaces/invitations/accept', requireAuth, validate(acceptInvitationSchema), invitation.accept],\n\n\t\t// ── Roles\n\t\t['POST', '/workspaces/roles', requireAuth, wsCtx, validate(createRoleSchema), role.create],\n\t\t['GET', '/workspaces/roles', requireAuth, wsCtx, role.list],\n\t\t['GET', '/workspaces/roles/:roleId', requireAuth, wsCtx, role.get],\n\t\t['PUT', '/workspaces/roles/:roleId', requireAuth, wsCtx, validate(updateRoleSchema), role.update],\n\t\t['DELETE', '/workspaces/roles/:roleId', requireAuth, wsCtx, role.remove],\n\t\t['POST', '/workspaces/roles/:roleId/permissions', requireAuth, wsCtx, validate(setRolePermissionsSchema), role.setPermissions],\n\n\t\t// ── Workspace lifecycle\n\t\t['POST', '/workspaces/archive', requireAuth, wsCtx, workspace.archive],\n\t\t['POST', '/workspaces/restore', requireAuth, wsCtx, workspace.restore],\n\t\t['GET', '/workspaces/settings', requireAuth, wsCtx, workspace.getSettings],\n\t\t['PUT', '/workspaces/settings', requireAuth, wsCtx, validate(updateSettingsSchema), workspace.updateSettings],\n\n\t\t// ── Path-based lookup by ID (admin / cross-workspace use)\n\t\t['GET', '/workspaces/:id', requireAuth, wsCtx, workspace.get],\n\n\t\t// ── Update current workspace — ID resolved from X-Workspace-ID header\n\t\t// (or personal workspace fallback when header is absent)\n\t\t['PUT', '/workspaces', requireAuth, wsCtx, validate(updateWorkspaceSchema), workspace.update],\n\t];\n}\n","import { z } from 'zod';\n\n// Request schemas — the validation contract for every body-taking workspaces\n// route. Wired into routes.ts via @fonderie/core's validate(); exported so\n// docs generators and typed clients read the source of truth the runtime\n// enforces. Same pattern as @fonderie/auth/src/schemas.ts.\n\nconst name = z.string().trim().min(1, 'name is required').max(200);\nconst email = z.string().trim().pipe(z.email());\n\nexport const createWorkspaceSchema = z.object({\n\tname,\n\tdescription: z.string().max(2000).optional(),\n\t// PERSONAL workspaces are created automatically by the module; the\n\t// controller enforces the rule — schema just types the field.\n\ttype: z.string().max(40).optional(),\n});\n\nconst addressSchema = z\n\t.object({\n\t\tline1: z.string().max(200).optional(),\n\t\tline2: z.string().max(200).optional(),\n\t\tcity: z.string().max(100).optional(),\n\t\tregion: z.string().max(100).optional(),\n\t\tpostalCode: z.string().max(20).optional(),\n\t\tcountry: z.string().max(60).optional(),\n\t})\n\t.passthrough();\n\nexport const updateWorkspaceSchema = z\n\t.object({\n\t\tname: z.string().trim().min(1).max(200).optional(),\n\t\tdescription: z.string().max(2000).nullable().optional(),\n\t\tmotto: z.string().max(300).nullable().optional(),\n\t\tphone: z.string().max(32).nullable().optional(),\n\t\tbusinessType: z.string().max(100).nullable().optional(),\n\t\taddress: addressSchema.nullable().optional(),\n\t})\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\n\nexport const updateSettingsSchema = z\n\t.object({\n\t\tlocale: z.string().max(35).optional(),\n\t\ttimezone: z.string().max(64).optional(),\n\t\tcurrency: z.string().max(8).optional(),\n\t\tdateFormat: z.string().max(32).optional(),\n\t\ttimeFormat: z.string().max(32).optional(),\n\t})\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'No settings provided');\n\nexport const addMemberRoleSchema = z.object({ roleId: z.string().min(1, 'roleId is required') });\n\nconst inviteEntry = z.object({\n\temail,\n\troleId: z.string().min(1).optional(),\n});\n\n// POST /workspaces/invitations accepts a single invite or a batch.\nexport const createInvitationsSchema = z.union([\n\tinviteEntry,\n\tz.array(inviteEntry).min(1, 'at least one invite is required'),\n]);\n\nexport const acceptInvitationSchema = z.object({ pin: z.string().trim().min(1, 'pin is required') });\n\nexport const createRoleSchema = z.object({\n\tname,\n\tdescription: z.string().max(1000).optional(),\n});\n\nexport const updateRoleSchema = z\n\t.object({\n\t\tname: z.string().trim().min(1).max(200).optional(),\n\t\tdescription: z.string().max(1000).nullable().optional(),\n\t\tactive: z.boolean().optional(),\n\t})\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\n\nexport const setRolePermissionsSchema = z.object({\n\tpermissions: z.array(\n\t\tz.object({\n\t\t\tpermissionKey: z.string().min(1),\n\t\t\tcanCreate: z.boolean().optional(),\n\t\t\tcanRead: z.boolean().optional(),\n\t\t\tcanUpdate: z.boolean().optional(),\n\t\t\tcanDelete: z.boolean().optional(),\n\t\t}),\n\t),\n});\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { Middleware } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { getMember } from '../services/members';\nimport { findWorkspaceById, findPersonalWorkspace } from '../services/workspaces';\n\n// Resolves ctx.workspace from:\n// 1. Route param :workspaceId or :id (path-based admin routes)\n// 2. X-Workspace-ID request header (standard resource routes)\n// Validates the current user is an active member.\n// Must run after withSession.\n\nfunction makeHandler(store: IStoreAdapter): Middleware {\n\treturn async (ctx, next) => {\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst workspaceId =\n\t\t\tparams?.['workspaceId'] ??\n\t\t\tparams?.['id'] ??\n\t\t\tctx.request.headers.get('x-workspace-id') ??\n\t\t\tundefined;\n\n\t\tif (!workspaceId) {\n\t\t\t// DMZ fallback — if the caller didn't specify a workspace, use their personal one.\n\t\t\t// No membership check needed: the user is always the sole owner.\n\t\t\tif (ctx.user) {\n\t\t\t\tconst personal = await findPersonalWorkspace(ctx.user.id, store);\n\t\t\t\tif (personal) Object.assign(ctx, { workspace: personal });\n\t\t\t}\n\t\t\treturn next();\n\t\t}\n\n\t\tconst workspace = await findWorkspaceById(workspaceId, store);\n\t\tif (!workspace) {\n\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t}\n\n\t\tif (ctx.user) {\n\t\t\tconst member = await getMember(ctx.user.id, workspaceId, store);\n\t\t\tif (!member) {\n\t\t\t\treturn setApiResponse(HTTP.FORBIDDEN, 'FORBIDDEN', 'Not a member of this workspace');\n\t\t\t}\n\t\t}\n\n\t\tObject.assign(ctx, { workspace });\n\t\treturn next();\n\t};\n}\n\nexport function withWorkspace(store: IStoreAdapter): Middleware;\nexport function withWorkspace(\n\tstore: IStoreAdapter,\n\tctx: IFonderieContext,\n\tnext: () => Promise<Response>,\n): Promise<Response>;\nexport function withWorkspace(\n\tstore: IStoreAdapter,\n\tctx?: IFonderieContext,\n\tnext?: () => Promise<Response>,\n): Middleware | Promise<Response> {\n\tconst handler = makeHandler(store);\n\tif (ctx !== undefined && next !== undefined) return handler(ctx, next);\n\treturn handler;\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IMember, IRole } from '../types';\n\nconst SELECT_MEMBER = `\n\truw.user_id AS \"userId\",\n\truw.workspace_id AS \"workspaceId\",\n\truw.role_id AS \"roleId\",\n\tr.name AS \"roleName\",\n\truw.confirmed AS \"confirmed\",\n\truw.created_at AS \"createdAt\",\n\tu.first_name AS \"firstName\",\n\tu.last_name AS \"lastName\",\n\tu.email AS \"email\",\n\tu.profile_image_url AS \"profileImageUrl\"\n`;\n\nexport async function getMember(\n\tuserId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IMember | null> {\n\tconst [row] = await store.query<IMember>(\n\t\t`SELECT ${SELECT_MEMBER}\n\t\t FROM fonderie_role_user_workspaces ruw\n\t\t LEFT JOIN fonderie_roles r ON r.id = ruw.role_id\n\t\t LEFT JOIN fonderie_users u ON u.id = ruw.user_id\n\t\t WHERE ruw.user_id = $1\n\t\t AND ruw.workspace_id = $2\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false\n\t\t LIMIT 1`,\n\t\t[userId, workspaceId],\n\t);\n\treturn row ?? null;\n}\n\nexport async function listMembers(workspaceId: string, store: IStoreAdapter): Promise<IMember[]> {\n\treturn store.query<IMember>(\n\t\t`SELECT ${SELECT_MEMBER}\n\t\t FROM fonderie_role_user_workspaces ruw\n\t\t LEFT JOIN fonderie_roles r ON r.id = ruw.role_id\n\t\t LEFT JOIN fonderie_users u ON u.id = ruw.user_id\n\t\t WHERE ruw.workspace_id = $1\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false\n\t\t ORDER BY ruw.created_at ASC`,\n\t\t[workspaceId],\n\t);\n}\n\nexport async function addMember(\n\topts: { userId: string; workspaceId: string; roleId: string; confirmed?: boolean },\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t VALUES ($1, $2, $3, $4)\n\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t SET confirmed = $4, removed = false, suspended = false`,\n\t\t[opts.userId, opts.workspaceId, opts.roleId, opts.confirmed ?? true],\n\t);\n}\n\nexport async function removeMember(\n\tuserId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_role_user_workspaces\n\t\t SET removed = true\n\t\t WHERE user_id = $1\n\t\t AND workspace_id = $2`,\n\t\t[userId, workspaceId],\n\t);\n}\n\nexport async function getUserRoles(\n\tuserId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IRole[]> {\n\treturn store.query<IRole>(\n\t\t`SELECT\n\t\t r.id,\n\t\t r.name,\n\t\t r.is_system AS \"isSystem\",\n\t\t r.active,\n\t\t r.description,\n\t\t r.workspace_id AS \"workspaceId\"\n\t\t FROM fonderie_role_user_workspaces ruw\n\t\t JOIN fonderie_roles r ON r.id = ruw.role_id\n\t\t WHERE ruw.user_id = $1\n\t\t AND ruw.workspace_id = $2\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false`,\n\t\t[userId, workspaceId],\n\t);\n}\n\nexport async function addRoleToMember(\n\tuserId: string,\n\tworkspaceId: string,\n\troleId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t VALUES ($1, $2, $3, true)\n\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t SET confirmed = true, removed = false, suspended = false`,\n\t\t[userId, workspaceId, roleId],\n\t);\n}\n\nexport async function removeRoleFromMember(\n\tuserId: string,\n\tworkspaceId: string,\n\troleId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tconst remaining = await store.query<{ count: string }>(\n\t\t`SELECT COUNT(*) AS count\n\t\t FROM fonderie_role_user_workspaces\n\t\t WHERE user_id = $1\n\t\t AND workspace_id = $2\n\t\t AND removed = false`,\n\t\t[userId, workspaceId],\n\t);\n\tconst count = parseInt(remaining[0]?.count ?? '0', 10);\n\tif (count <= 1) throw new Error('Cannot remove last role from member');\n\n\tawait store.query(\n\t\t`DELETE FROM fonderie_role_user_workspaces\n\t\t WHERE user_id = $1\n\t\t AND workspace_id = $2\n\t\t AND role_id = $3`,\n\t\t[userId, workspaceId, roleId],\n\t);\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IWorkspace, IWorkspaceAddress, IWorkspaceSettings } from '../types';\n\nconst SELECT_WS = `\n\tid,\n\tname,\n\tslug,\n\ttype,\n\tdescription,\n\tmotto,\n\tphone,\n\tbusiness_type AS \"businessType\",\n\taddress,\n\tplan,\n\towner_id AS \"ownerId\",\n\tis_personal AS \"isPersonal\",\n\tarchived_at AS \"archivedAt\",\n\tarchived_by AS \"archivedBy\",\n\tcreated_at AS \"createdAt\",\n\tupdated_at AS \"updatedAt\"\n`;\n\nconst SELECT_WS_W = `\n\tw.id,\n\tw.name,\n\tw.slug,\n\tw.type,\n\tw.description,\n\tw.motto,\n\tw.phone,\n\tw.business_type AS \"businessType\",\n\tw.address,\n\tw.plan,\n\tw.owner_id AS \"ownerId\",\n\tw.is_personal AS \"isPersonal\",\n\tw.archived_at AS \"archivedAt\",\n\tw.archived_by AS \"archivedBy\",\n\tw.created_at AS \"createdAt\",\n\tw.updated_at AS \"updatedAt\"\n`;\n\nexport async function findWorkspaceById(\n\tid: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst [row] = await store.query<IWorkspace>(\n\t\t`SELECT ${SELECT_WS} FROM fonderie_workspaces WHERE id = $1`,\n\t\t[id],\n\t);\n\treturn row ?? null;\n}\n\nexport async function findWorkspacesByUserId(\n\tuserId: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspace[]> {\n\treturn store.query<IWorkspace>(\n\t\t`SELECT ${SELECT_WS_W}\n\t\t FROM fonderie_workspaces w\n\t\t JOIN fonderie_role_user_workspaces ruw ON ruw.workspace_id = w.id\n\t\t WHERE ruw.user_id = $1\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false\n\t\t GROUP BY w.id\n\t\t ORDER BY w.created_at ASC`,\n\t\t[userId],\n\t);\n}\n\nexport async function createWorkspace(\n\topts: {\n\t\tname: string;\n\t\tslug: string;\n\t\townerId: string;\n\t\ttype?: string;\n\t\tdescription?: string;\n\t\tplan?: string;\n\t},\n\tstore: IStoreAdapter,\n): Promise<IWorkspace> {\n\tconst [workspace] = await store.query<IWorkspace>(\n\t\t`INSERT INTO fonderie_workspaces (name, slug, owner_id, type, description, plan)\n\t\t VALUES ($1, $2, $3, $4, $5, $6)\n\t\t RETURNING ${SELECT_WS}`,\n\t\t[\n\t\t\topts.name,\n\t\t\topts.slug,\n\t\t\topts.ownerId,\n\t\t\topts.type ?? 'ORGANIZATION',\n\t\t\topts.description ?? null,\n\t\t\topts.plan ?? 'free',\n\t\t],\n\t);\n\n\tif (!workspace) throw new Error('Failed to create workspace');\n\treturn workspace;\n}\n\n// Returns the new workspace, or null if the personal workspace already exists (idempotent).\nexport async function createPersonalWorkspace(\n\topts: { name: string; slug: string; ownerId: string },\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst [workspace] = await store.query<IWorkspace>(\n\t\t`INSERT INTO fonderie_workspaces (name, slug, owner_id, type, is_personal)\n\t\t VALUES ($1, $2, $3, 'PERSONAL', true)\n\t\t ON CONFLICT (owner_id) WHERE is_personal = true DO NOTHING\n\t\t RETURNING ${SELECT_WS}`,\n\t\t[opts.name, opts.slug, opts.ownerId],\n\t);\n\treturn workspace ?? null;\n}\n\nexport async function findPersonalWorkspace(\n\tuserId: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst [row] = await store.query<IWorkspace>(\n\t\t`SELECT ${SELECT_WS} FROM fonderie_workspaces\n\t\t WHERE owner_id = $1 AND is_personal = true\n\t\t LIMIT 1`,\n\t\t[userId],\n\t);\n\treturn row ?? null;\n}\n\nexport async function updateWorkspace(\n\tid: string,\n\topts: {\n\t\tname?: string;\n\t\tdescription?: string | null;\n\t\tslug?: string;\n\t\tmotto?: string | null;\n\t\tphone?: string | null;\n\t\tbusinessType?: string | null;\n\t\taddress?: IWorkspaceAddress | null;\n\t},\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst sets: string[] = ['updated_at = now()'];\n\tconst params: unknown[] = [id];\n\n\tif (opts.name !== undefined) {\n\t\tparams.push(opts.name);\n\t\tsets.push(`name = $${params.length}`);\n\t}\n\tif (opts.description !== undefined) {\n\t\tparams.push(opts.description);\n\t\tsets.push(`description = $${params.length}`);\n\t}\n\tif (opts.slug !== undefined) {\n\t\tparams.push(opts.slug);\n\t\tsets.push(`slug = $${params.length}`);\n\t}\n\tif (opts.motto !== undefined) {\n\t\tparams.push(opts.motto);\n\t\tsets.push(`motto = $${params.length}`);\n\t}\n\tif (opts.phone !== undefined) {\n\t\tparams.push(opts.phone);\n\t\tsets.push(`phone = $${params.length}`);\n\t}\n\tif (opts.businessType !== undefined) {\n\t\tparams.push(opts.businessType);\n\t\tsets.push(`business_type = $${params.length}`);\n\t}\n\tif (opts.address !== undefined) {\n\t\tparams.push(JSON.stringify(opts.address ?? {}));\n\t\tsets.push(`address = $${params.length}::jsonb`);\n\t}\n\n\tconst [row] = await store.query<IWorkspace>(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET ${sets.join(', ')}\n\t\t WHERE id = $1\n\t\t RETURNING ${SELECT_WS}`,\n\t\tparams,\n\t);\n\treturn row ?? null;\n}\n\nexport async function archiveWorkspace(\n\tid: string,\n\tbyUser: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET archived_at = now(), archived_by = $2, updated_at = now()\n\t\t WHERE id = $1 AND archived_at IS NULL`,\n\t\t[id, byUser],\n\t);\n}\n\nexport async function restoreWorkspace(id: string, store: IStoreAdapter): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET archived_at = NULL, archived_by = NULL, updated_at = now()\n\t\t WHERE id = $1`,\n\t\t[id],\n\t);\n}\n\nconst SETTINGS_DEFAULTS: IWorkspaceSettings = {\n\tlocale: 'en-US',\n\ttimezone: 'UTC',\n\tcurrency: 'USD',\n\tdateFormat: 'MM/DD/YYYY',\n\ttimeFormat: 'hh:mm A',\n};\n\nexport async function getWorkspaceSettings(\n\tid: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspaceSettings> {\n\tconst [row] = await store.query<{ settings: Record<string, unknown> }>(\n\t\t`SELECT settings FROM fonderie_workspaces WHERE id = $1`,\n\t\t[id],\n\t);\n\tconst raw = row?.settings ?? {};\n\tconst s = (raw['settings'] as Record<string, unknown> | undefined) ?? raw;\n\n\treturn {\n\t\tlocale: typeof s['locale'] === 'string' ? s['locale'] : SETTINGS_DEFAULTS.locale,\n\t\ttimezone: typeof s['timezone'] === 'string' ? s['timezone'] : SETTINGS_DEFAULTS.timezone,\n\t\tcurrency: typeof s['currency'] === 'string' ? s['currency'] : SETTINGS_DEFAULTS.currency,\n\t\tdateFormat:\n\t\t\ttypeof s['dateFormat'] === 'string' ? s['dateFormat'] : SETTINGS_DEFAULTS.dateFormat,\n\t\ttimeFormat:\n\t\t\ttypeof s['timeFormat'] === 'string' ? s['timeFormat'] : SETTINGS_DEFAULTS.timeFormat,\n\t};\n}\n\nexport async function updateWorkspaceSettings(\n\tid: string,\n\tsettings: Partial<IWorkspaceSettings>,\n\tstore: IStoreAdapter,\n): Promise<IWorkspaceSettings> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET settings = settings || jsonb_build_object('settings', $2::jsonb),\n\t\t updated_at = now()\n\t\t WHERE id = $1`,\n\t\t[id, JSON.stringify(settings)],\n\t);\n\treturn getWorkspaceSettings(id, store);\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IWorkspacesConfig } from '../config';\nimport type { IWorkspace } from '../types';\nimport { WorkspaceModel } from '../models/workspace.model';\nimport { MemberModel } from '../models/member.model';\nimport { RoleModel } from '../models/role.model';\nimport { toWorkspaceDTO, toSettingsDTO } from '../dtos/workspace';\n\nexport function workspaceController(store: IStoreAdapter, config: IWorkspacesConfig) {\n\tconst workspaces = new WorkspaceModel(store);\n\tconst members = new MemberModel(store);\n\tconst roles = new RoleModel(store);\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.user)\n\t\t\t\treturn setApiResponse(HTTP.UNAUTHORIZED, 'UNAUTHORIZED', 'Authentication required');\n\t\t\tconst list = await workspaces.findByUserId(ctx.user.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACES_FETCHED', 'Workspaces retrieved successfully.', {\n\t\t\t\tworkspaces: list.map(toWorkspaceDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync create(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst name = body?.['name'];\n\t\t\tconst description = body?.['description'];\n\t\t\tconst type = body?.['type'];\n\n\t\t\tif (typeof name !== 'string' || name.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'name is required');\n\t\t\t}\n\n\t\t\tif (type === 'PERSONAL') {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t'Personal workspaces are created automatically',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst slug = name\n\t\t\t\t.toLowerCase()\n\t\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t\t.replace(/^-|-$/g, '');\n\n\t\t\tconst workspace = await store.transaction(async (tx) => {\n\t\t\t\tconst wsOpts: Parameters<typeof workspaces.create>[0] = {\n\t\t\t\t\tname: name.trim(),\n\t\t\t\t\tslug,\n\t\t\t\t\townerId: ctx.user!.id,\n\t\t\t\t\ttype: typeof type === 'string' ? type : 'ORGANIZATION',\n\t\t\t\t};\n\t\t\t\tif (typeof description === 'string') wsOpts.description = description;\n\n\t\t\t\tconst wsModel = new WorkspaceModel(tx);\n\t\t\t\tconst roleModel = new RoleModel(tx);\n\t\t\t\tconst memModel = new MemberModel(tx);\n\n\t\t\t\tconst ws = await wsModel.create(wsOpts);\n\n\t\t\t\tconst adminRole = await roleModel.findSystem('ADMIN');\n\t\t\t\tif (!adminRole) throw new Error('System ADMIN role not found');\n\t\t\t\tawait memModel.add({ userId: ctx.user!.id, workspaceId: ws.id, roleId: adminRole.id });\n\n\t\t\t\treturn ws;\n\t\t\t});\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'WORKSPACE_CREATED', 'Workspace created successfully.', {\n\t\t\t\tworkspace: toWorkspaceDTO(workspace),\n\t\t\t});\n\t\t},\n\n\t\tasync get(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_FETCHED', 'Workspace retrieved successfully.', {\n\t\t\t\tworkspace: toWorkspaceDTO(ctx.workspace as IWorkspace),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst opts: Parameters<typeof workspaces.update>[1] = {};\n\n\t\t\tif (typeof body?.['name'] === 'string') opts.name = body['name'].trim();\n\t\t\tif (body?.['description'] !== undefined)\n\t\t\t\topts.description = typeof body['description'] === 'string' ? body['description'] : null;\n\t\t\tif (body?.['motto'] !== undefined)\n\t\t\t\topts.motto = typeof body['motto'] === 'string' ? body['motto'] : null;\n\t\t\tif (body?.['phone'] !== undefined)\n\t\t\t\topts.phone = typeof body['phone'] === 'string' ? body['phone'].trim() : null;\n\t\t\tif (body?.['businessType'] !== undefined)\n\t\t\t\topts.businessType = typeof body['businessType'] === 'string' ? body['businessType'] : null;\n\t\t\tif (body?.['address'] !== undefined && typeof body['address'] === 'object')\n\t\t\t\topts.address = (body['address'] ?? null) as NonNullable<\n\t\t\t\t\tParameters<typeof workspaces.update>[1]\n\t\t\t\t>['address'] & (object | null);\n\n\t\t\tconst workspace = await workspaces.update(ctx.workspace.id, opts);\n\t\t\tif (!workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_UPDATED', 'Workspace updated successfully.', {\n\t\t\t\tworkspace: toWorkspaceDTO(workspace),\n\t\t\t});\n\t\t},\n\n\t\tasync archive(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\tif (ctx.workspace.isPersonal) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.FORBIDDEN,\n\t\t\t\t\t'FORBIDDEN',\n\t\t\t\t\t'Personal workspaces cannot be archived',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tawait workspaces.archive(ctx.workspace.id, ctx.user!.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_ARCHIVED', 'Workspace archived successfully.');\n\t\t},\n\n\t\tasync restore(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tawait workspaces.restore(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_RESTORED', 'Workspace restored successfully.');\n\t\t},\n\n\t\tasync getSettings(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst settings = await workspaces.getSettings(ctx.workspace.id);\n\t\t\treturn setApiResponse(\n\t\t\t\tHTTP.OK,\n\t\t\t\t'SETTINGS_FETCHED',\n\t\t\t\t'Workspace settings retrieved successfully.',\n\t\t\t\t{\n\t\t\t\t\tsettings: toSettingsDTO(settings),\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tasync updateSettings(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tif (!body || Object.keys(body).length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'No settings provided');\n\t\t\t}\n\n\t\t\tconst patch: Record<string, string> = {};\n\t\t\tfor (const key of ['locale', 'timezone', 'currency', 'dateFormat', 'timeFormat'] as const) {\n\t\t\t\tif (typeof body[key] === 'string') patch[key] = body[key] as string;\n\t\t\t}\n\n\t\t\tconst settings = await workspaces.updateSettings(ctx.workspace.id, patch);\n\t\t\treturn setApiResponse(\n\t\t\t\tHTTP.OK,\n\t\t\t\t'SETTINGS_UPDATED',\n\t\t\t\t'Workspace settings updated successfully.',\n\t\t\t\t{\n\t\t\t\t\tsettings: toSettingsDTO(settings),\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t};\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IWorkspace, IWorkspaceSettings } from '../types';\nimport {\n\tfindWorkspaceById,\n\tfindWorkspacesByUserId,\n\tcreateWorkspace,\n\tcreatePersonalWorkspace,\n\tfindPersonalWorkspace,\n\tupdateWorkspace,\n\tarchiveWorkspace,\n\trestoreWorkspace,\n\tgetWorkspaceSettings,\n\tupdateWorkspaceSettings,\n} from '../services/workspaces';\n\nexport class WorkspaceModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tfindById(id: string): Promise<IWorkspace | null> {\n\t\treturn findWorkspaceById(id, this.store);\n\t}\n\n\tfindByUserId(userId: string): Promise<IWorkspace[]> {\n\t\treturn findWorkspacesByUserId(userId, this.store);\n\t}\n\n\tcreate(opts: Parameters<typeof createWorkspace>[0]): Promise<IWorkspace> {\n\t\treturn createWorkspace(opts, this.store);\n\t}\n\n\tcreatePersonal(opts: Parameters<typeof createPersonalWorkspace>[0]): Promise<IWorkspace | null> {\n\t\treturn createPersonalWorkspace(opts, this.store);\n\t}\n\n\tfindPersonal(userId: string): Promise<IWorkspace | null> {\n\t\treturn findPersonalWorkspace(userId, this.store);\n\t}\n\n\tupdate(id: string, opts: Parameters<typeof updateWorkspace>[1]): Promise<IWorkspace | null> {\n\t\treturn updateWorkspace(id, opts, this.store);\n\t}\n\n\tarchive(id: string, byUser: string): Promise<void> {\n\t\treturn archiveWorkspace(id, byUser, this.store);\n\t}\n\n\trestore(id: string): Promise<void> {\n\t\treturn restoreWorkspace(id, this.store);\n\t}\n\n\tgetSettings(id: string): Promise<IWorkspaceSettings> {\n\t\treturn getWorkspaceSettings(id, this.store);\n\t}\n\n\tupdateSettings(id: string, settings: Partial<IWorkspaceSettings>): Promise<IWorkspaceSettings> {\n\t\treturn updateWorkspaceSettings(id, settings, this.store);\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IMember, IRole } from '../types';\nimport {\n\tgetMember,\n\tlistMembers,\n\taddMember,\n\tremoveMember,\n\tgetUserRoles,\n\taddRoleToMember,\n\tremoveRoleFromMember,\n} from '../services/members';\n\nexport class MemberModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tget(userId: string, workspaceId: string): Promise<IMember | null> {\n\t\treturn getMember(userId, workspaceId, this.store);\n\t}\n\n\tlist(workspaceId: string): Promise<IMember[]> {\n\t\treturn listMembers(workspaceId, this.store);\n\t}\n\n\tadd(opts: Parameters<typeof addMember>[0]): Promise<void> {\n\t\treturn addMember(opts, this.store);\n\t}\n\n\tremove(userId: string, workspaceId: string): Promise<void> {\n\t\treturn removeMember(userId, workspaceId, this.store);\n\t}\n\n\tgetUserRoles(userId: string, workspaceId: string): Promise<IRole[]> {\n\t\treturn getUserRoles(userId, workspaceId, this.store);\n\t}\n\n\taddRole(userId: string, workspaceId: string, roleId: string): Promise<void> {\n\t\treturn addRoleToMember(userId, workspaceId, roleId, this.store);\n\t}\n\n\tremoveRole(userId: string, workspaceId: string, roleId: string): Promise<void> {\n\t\treturn removeRoleFromMember(userId, workspaceId, roleId, this.store);\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IRole } from '../types';\n\nconst SELECT_ROLE = `\n\tid,\n\tname,\n\tis_system AS \"isSystem\",\n\tactive,\n\tdescription,\n\tworkspace_id AS \"workspaceId\"\n`;\n\nexport async function createRole(\n\topts: { name: string; workspaceId: string; description?: string },\n\tstore: IStoreAdapter,\n): Promise<IRole> {\n\tconst [role] = await store.query<IRole>(\n\t\t`INSERT INTO fonderie_roles (name, workspace_id, description)\n\t\t VALUES ($1, $2, $3)\n\t\t RETURNING ${SELECT_ROLE}`,\n\t\t[opts.name, opts.workspaceId, opts.description ?? null],\n\t);\n\tif (!role) throw new Error('Failed to create role');\n\treturn role;\n}\n\nexport async function findSystemRole(name: string, store: IStoreAdapter): Promise<IRole | null> {\n\tconst [row] = await store.query<IRole>(\n\t\t`SELECT ${SELECT_ROLE} FROM fonderie_roles WHERE name = $1 AND is_system = true LIMIT 1`,\n\t\t[name],\n\t);\n\treturn row ?? null;\n}\n\nexport async function getRoleById(id: string, store: IStoreAdapter): Promise<IRole | null> {\n\tconst [row] = await store.query<IRole>(\n\t\t`SELECT ${SELECT_ROLE} FROM fonderie_roles WHERE id = $1`,\n\t\t[id],\n\t);\n\treturn row ?? null;\n}\n\nexport async function listWorkspaceRoles(\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IRole[]> {\n\treturn store.query<IRole>(\n\t\t`SELECT ${SELECT_ROLE}\n\t\t FROM fonderie_roles\n\t\t WHERE workspace_id = $1 OR is_system = true\n\t\t ORDER BY is_system DESC, name ASC`,\n\t\t[workspaceId],\n\t);\n}\n\nexport async function updateRole(\n\tid: string,\n\topts: { name?: string; description?: string | null; active?: boolean },\n\tstore: IStoreAdapter,\n): Promise<IRole | null> {\n\tconst sets: string[] = [];\n\tconst params: unknown[] = [id];\n\n\tif (opts.name !== undefined) {\n\t\tparams.push(opts.name);\n\t\tsets.push(`name = $${params.length}`);\n\t}\n\tif (opts.description !== undefined) {\n\t\tparams.push(opts.description);\n\t\tsets.push(`description = $${params.length}`);\n\t}\n\tif (opts.active !== undefined) {\n\t\tparams.push(opts.active);\n\t\tsets.push(`active = $${params.length}`);\n\t}\n\n\tif (sets.length === 0) return getRoleById(id, store);\n\n\tconst [row] = await store.query<IRole>(\n\t\t`UPDATE fonderie_roles\n\t\t SET ${sets.join(', ')}\n\t\t WHERE id = $1 AND is_system = false\n\t\t RETURNING ${SELECT_ROLE}`,\n\t\tparams,\n\t);\n\treturn row ?? null;\n}\n\nexport async function deleteRole(\n\tid: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`DELETE FROM fonderie_roles\n\t\t WHERE id = $1 AND workspace_id = $2 AND is_system = false`,\n\t\t[id, workspaceId],\n\t);\n}\n\nexport async function setRolePermissions(\n\troleId: string,\n\tworkspaceId: string,\n\tpermissions: Array<{\n\t\tpermissionKey: string;\n\t\tcanCreate: boolean;\n\t\tcanRead: boolean;\n\t\tcanUpdate: boolean;\n\t\tcanDelete: boolean;\n\t}>,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tif (permissions.length === 0) {\n\t\tawait store.query(\n\t\t\t`DELETE FROM fonderie_role_permissions WHERE role_id = $1 AND workspace_id = $2`,\n\t\t\t[roleId, workspaceId],\n\t\t);\n\t\treturn;\n\t}\n\n\tawait store.transaction(async (tx) => {\n\t\tawait tx.query(\n\t\t\t`DELETE FROM fonderie_role_permissions WHERE role_id = $1 AND workspace_id = $2`,\n\t\t\t[roleId, workspaceId],\n\t\t);\n\n\t\tfor (const p of permissions) {\n\t\t\tawait tx.query(\n\t\t\t\t`INSERT INTO fonderie_role_permissions\n\t\t\t\t (role_id, workspace_id, permission_key, can_create, can_read, can_update, can_delete)\n\t\t\t\t VALUES ($1, $2, $3, $4, $5, $6, $7)\n\t\t\t\t ON CONFLICT (role_id, permission_key)\n\t\t\t\t DO UPDATE SET\n\t\t\t\t can_create = $4, can_read = $5,\n\t\t\t\t can_update = $6, can_delete = $7`,\n\t\t\t\t[roleId, workspaceId, p.permissionKey, p.canCreate, p.canRead, p.canUpdate, p.canDelete],\n\t\t\t);\n\t\t}\n\t});\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IRole } from '../types';\nimport {\n\tcreateRole,\n\tfindSystemRole,\n\tgetRoleById,\n\tlistWorkspaceRoles,\n\tupdateRole,\n\tdeleteRole,\n\tsetRolePermissions,\n} from '../services/roles';\n\nexport class RoleModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tcreate(opts: Parameters<typeof createRole>[0]): Promise<IRole> {\n\t\treturn createRole(opts, this.store);\n\t}\n\n\tfindSystem(name: string): Promise<IRole | null> {\n\t\treturn findSystemRole(name, this.store);\n\t}\n\n\tfindById(id: string): Promise<IRole | null> {\n\t\treturn getRoleById(id, this.store);\n\t}\n\n\tlist(workspaceId: string): Promise<IRole[]> {\n\t\treturn listWorkspaceRoles(workspaceId, this.store);\n\t}\n\n\tupdate(id: string, opts: Parameters<typeof updateRole>[1]): Promise<IRole | null> {\n\t\treturn updateRole(id, opts, this.store);\n\t}\n\n\tdelete(id: string, workspaceId: string): Promise<void> {\n\t\treturn deleteRole(id, workspaceId, this.store);\n\t}\n\n\tsetPermissions(\n\t\troleId: string,\n\t\tworkspaceId: string,\n\t\tpermissions: Parameters<typeof setRolePermissions>[2],\n\t): Promise<void> {\n\t\treturn setRolePermissions(roleId, workspaceId, permissions, this.store);\n\t}\n}\n","import { stringOrEmpty, booleanOrFalse } from '@fonderie/core/parser';\n\nimport type { IWorkspace, IRole, IMember, IInvitation, IWorkspaceSettings } from '../types';\n\nexport interface IWorkspaceAddressDTO {\n\tline1: string;\n\tline2: string;\n\tcity: string;\n\tstate: string;\n\tzip: string;\n\tcountry: string;\n}\n\nexport interface IWorkspaceDTO {\n\tid: string;\n\tname: string;\n\tslug: string;\n\ttype: string;\n\tdescription: string;\n\tmotto: string;\n\tphone: string;\n\tbusinessType: string;\n\taddress: IWorkspaceAddressDTO;\n\tplan: string;\n\townerId: string;\n\tisPersonal: boolean;\n\tisArchived: boolean;\n\tarchivedAt: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface IRoleDTO {\n\tid: string;\n\tname: string;\n\tisSystem: boolean;\n\tactive: boolean;\n\tdescription: string;\n\tworkspaceId: string;\n}\n\nexport interface IMemberDTO {\n\tuserId: string;\n\tworkspaceId: string;\n\troleId: string;\n\troleName: string;\n\tconfirmed: boolean;\n\tcreatedAt: string;\n}\n\nexport interface IInvitationDTO {\n\tid: string;\n\tworkspaceId: string;\n\temail: string;\n\troleId: string;\n\ttoken: string;\n\tstatus: string;\n\texpiresAt: string;\n\tcreatedAt: string;\n}\n\nexport interface IWorkspaceSettingsDTO {\n\tlocale: string;\n\ttimezone: string;\n\tcurrency: string;\n\tdateFormat: string;\n\ttimeFormat: string;\n}\n\nexport function toWorkspaceDTO(ws: IWorkspace): IWorkspaceDTO {\n\tconst addr = ws.address ?? {};\n\treturn {\n\t\tid: stringOrEmpty(ws.id),\n\t\tname: stringOrEmpty(ws.name),\n\t\tslug: stringOrEmpty(ws.slug),\n\t\ttype: stringOrEmpty(ws.type),\n\t\tdescription: stringOrEmpty(ws.description),\n\t\tmotto: stringOrEmpty(ws.motto),\n\t\tphone: stringOrEmpty(ws.phone),\n\t\tbusinessType: stringOrEmpty(ws.businessType),\n\t\taddress: {\n\t\t\tline1: stringOrEmpty(addr.line1),\n\t\t\tline2: stringOrEmpty(addr.line2),\n\t\t\tcity: stringOrEmpty(addr.city),\n\t\t\tstate: stringOrEmpty(addr.state),\n\t\t\tzip: stringOrEmpty(addr.zip),\n\t\t\tcountry: stringOrEmpty(addr.country),\n\t\t},\n\t\tplan: stringOrEmpty(ws.plan),\n\t\townerId: stringOrEmpty(ws.ownerId),\n\t\tisPersonal: booleanOrFalse(ws.isPersonal),\n\t\tisArchived: ws.archivedAt !== null,\n\t\tarchivedAt: stringOrEmpty(ws.archivedAt),\n\t\tcreatedAt: stringOrEmpty(ws.createdAt),\n\t\tupdatedAt: stringOrEmpty(ws.updatedAt),\n\t};\n}\n\nexport function toRoleDTO(role: IRole): IRoleDTO {\n\treturn {\n\t\tid: stringOrEmpty(role.id),\n\t\tname: stringOrEmpty(role.name),\n\t\tisSystem: booleanOrFalse(role.isSystem),\n\t\tactive: role.active !== false,\n\t\tdescription: stringOrEmpty(role.description),\n\t\tworkspaceId: stringOrEmpty(role.workspaceId),\n\t};\n}\n\nexport function toMemberDTO(m: IMember): IMemberDTO {\n\treturn {\n\t\tuserId: stringOrEmpty(m.userId),\n\t\tworkspaceId: stringOrEmpty(m.workspaceId),\n\t\troleId: stringOrEmpty(m.roleId),\n\t\troleName: stringOrEmpty(m.roleName),\n\t\tconfirmed: booleanOrFalse(m.confirmed),\n\t\tcreatedAt: stringOrEmpty(m.createdAt),\n\t};\n}\n\nexport function toInvitationDTO(inv: IInvitation): IInvitationDTO {\n\treturn {\n\t\tid: stringOrEmpty(inv.id),\n\t\tworkspaceId: stringOrEmpty(inv.workspaceId),\n\t\temail: stringOrEmpty(inv.email),\n\t\troleId: stringOrEmpty(inv.roleId),\n\t\ttoken: stringOrEmpty(inv.token),\n\t\tstatus: stringOrEmpty(inv.status),\n\t\texpiresAt: stringOrEmpty(inv.expiresAt),\n\t\tcreatedAt: stringOrEmpty(inv.createdAt),\n\t};\n}\n\nexport function toSettingsDTO(s: IWorkspaceSettings): IWorkspaceSettingsDTO {\n\treturn {\n\t\tlocale: stringOrEmpty(s.locale),\n\t\ttimezone: stringOrEmpty(s.timezone),\n\t\tcurrency: stringOrEmpty(s.currency),\n\t\tdateFormat: stringOrEmpty(s.dateFormat),\n\t\ttimeFormat: stringOrEmpty(s.timeFormat),\n\t};\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { MemberModel } from '../models/member.model';\nimport { toMemberDTO, toRoleDTO } from '../dtos/workspace';\n\nexport function memberController(store: IStoreAdapter) {\n\tconst members = new MemberModel(store);\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst list = await members.list(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'MEMBERS_FETCHED', 'Members retrieved successfully.', {\n\t\t\t\tmembers: list.map(toMemberDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\tif (ctx.workspace.isPersonal) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.FORBIDDEN,\n\t\t\t\t\t'FORBIDDEN',\n\t\t\t\t\t'Personal workspaces do not support member management',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\t\t\tif (userId === ctx.user?.id) {\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'INVALID_OPERATION', 'Cannot remove yourself');\n\t\t\t}\n\n\t\t\tawait members.remove(userId, ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'MEMBER_REMOVED', 'Member removed successfully.');\n\t\t},\n\n\t\tasync getUserRoles(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\n\t\t\tconst roles = await members.getUserRoles(userId, ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLES_FETCHED', 'Member roles retrieved successfully.', {\n\t\t\t\troles: roles.map(toRoleDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync addRole(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\t\t\tconst roleId = (body?.['roleId'] ?? params?.['roleId']) as string | undefined;\n\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\t\t\tif (!roleId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\n\t\t\tawait members.addRole(userId, ctx.workspace.id, roleId);\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_ASSIGNED', 'Role assigned successfully.');\n\t\t},\n\n\t\tasync removeRole(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\t\t\tconst roleId = params?.['roleId'];\n\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\t\t\tif (!roleId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\n\t\t\ttry {\n\t\t\t\tawait members.removeRole(userId, ctx.workspace.id, roleId);\n\t\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_REMOVED', 'Role removed successfully.');\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : 'Failed';\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'OPERATION_FAILED', message);\n\t\t\t}\n\t\t},\n\t};\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { RoleModel } from '../models/role.model';\nimport { toRoleDTO } from '../dtos/workspace';\n\nexport function roleController(store: IStoreAdapter) {\n\tconst roles = new RoleModel(store);\n\n\treturn {\n\t\tasync create(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst name = body?.['name'];\n\t\t\tconst description = body?.['description'];\n\n\t\t\tif (typeof name !== 'string' || name.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'name is required');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst opts: Parameters<typeof roles.create>[0] = {\n\t\t\t\t\tname: name.trim(),\n\t\t\t\t\tworkspaceId: ctx.workspace.id,\n\t\t\t\t};\n\n\t\t\t\tif (typeof description === 'string') {\n\t\t\t\t\topts.description = description;\n\t\t\t\t}\n\n\t\t\t\tconst role = await roles.create(opts);\n\t\t\t\treturn setApiResponse(HTTP.CREATED, 'ROLE_CREATED', 'Role created successfully.', {\n\t\t\t\t\trole: toRoleDTO(role),\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : 'Failed to create role';\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'OPERATION_FAILED', message);\n\t\t\t}\n\t\t},\n\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst list = await roles.list(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLES_FETCHED', 'Roles retrieved successfully.', {\n\t\t\t\troles: list.map(toRoleDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync get(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tconst role = await roles.findById(roleId);\n\t\t\tif (!role) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Role not found');\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_FETCHED', 'Role retrieved successfully.', {\n\t\t\t\trole: toRoleDTO(role),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tconst opts: { name?: string; description?: string | null; active?: boolean } = {};\n\n\t\t\tif (typeof body?.['name'] === 'string') {\n\t\t\t\topts.name = body['name'];\n\t\t\t}\n\n\t\t\tif (typeof body?.['description'] === 'string') {\n\t\t\t\topts.description = body['description'];\n\t\t\t}\n\n\t\t\tif (body?.['description'] === null) {\n\t\t\t\topts.description = null;\n\t\t\t}\n\n\t\t\tif (typeof body?.['active'] === 'boolean') {\n\t\t\t\topts.active = body['active'];\n\t\t\t}\n\n\t\t\tconst role = await roles.update(roleId, opts);\n\t\t\tif (!role) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Role not found or is a system role');\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_UPDATED', 'Role updated successfully.', {\n\t\t\t\trole: toRoleDTO(role),\n\t\t\t});\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tawait roles.delete(roleId, ctx.workspace.id);\n\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_DELETED', 'Role deleted successfully.');\n\t\t},\n\n\t\tasync setPermissions(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tconst perms = body?.['permissions'];\n\t\t\tif (!Array.isArray(perms)) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t'permissions array is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst normalized = perms\n\t\t\t\t.map((p: unknown) => {\n\t\t\t\t\tconst perm = p as Record<string, unknown>;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpermissionKey: String(perm['permissionKey'] ?? ''),\n\t\t\t\t\t\tcanCreate: Boolean(perm['canCreate']),\n\t\t\t\t\t\tcanRead: Boolean(perm['canRead']),\n\t\t\t\t\t\tcanUpdate: Boolean(perm['canUpdate']),\n\t\t\t\t\t\tcanDelete: Boolean(perm['canDelete']),\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.filter((p) => p.permissionKey.length > 0);\n\n\t\t\tawait roles.setPermissions(roleId, ctx.workspace.id, normalized);\n\n\t\t\treturn setApiResponse(HTTP.OK, 'PERMISSIONS_SET', 'Role permissions updated successfully.');\n\t\t},\n\t};\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { ICourierMessage } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport type { EventBus } from '@fonderie/events';\nimport { NOTIFICATION_EVENT } from '@fonderie/events';\n\nimport { getPlanLimit } from '@fonderie/billing';\nimport { MESSAGE_KEYS } from '../config';\nimport { InvitationModel } from '../models/invitation.model';\nimport { toInvitationDTO } from '../dtos/workspace';\n\nexport function invitationController(store: IStoreAdapter, ttl: string, bus?: EventBus) {\n\tconst invitations = new InvitationModel(store);\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst list = await invitations.list(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'INVITATIONS_FETCHED', 'Invitations retrieved successfully.', {\n\t\t\t\tinvitations: list.map(toInvitationDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync invite(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\tif (ctx.workspace.isPersonal) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.FORBIDDEN,\n\t\t\t\t\t'FORBIDDEN',\n\t\t\t\t\t'Personal workspaces do not support invitations',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as unknown;\n\t\t\tconst entries = Array.isArray(body) ? body as Record<string, unknown>[] : [body as Record<string, unknown>];\n\n\t\t\tif (!entries.length) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'at least one invite is required');\n\t\t\t}\n\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (typeof entry?.['email'] !== 'string') {\n\t\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'email is required for every invite');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Seat limit — enforced automatically when billing module is registered.\n\t\t\t// getPlanLimit reads from ctx.meta['billing'] (fail-open when billing absent).\n\t\t\tconst seatLimit = getPlanLimit(ctx, 'seats');\n\t\t\tif (seatLimit !== null) {\n\t\t\t\tconst [countRow] = await store.query<{ count: string }>(\n\t\t\t\t\t`SELECT COUNT(*) AS count FROM fonderie_role_user_workspaces\n\t\t\t\t\t WHERE workspace_id = $1 AND removed = false AND suspended = false`,\n\t\t\t\t\t[ctx.workspace.id],\n\t\t\t\t);\n\t\t\t\tconst total = parseInt(countRow!.count, 10);\n\t\t\t\tconst occupied = ctx.workspace.isPersonal ? total : Math.max(0, total - 1);\n\t\t\t\tif (occupied + entries.length > seatLimit) {\n\t\t\t\t\treturn setApiResponse(\n\t\t\t\t\t\tHTTP.PAYMENT_REQUIRED,\n\t\t\t\t\t\t'SEAT_LIMIT_REACHED',\n\t\t\t\t\t\t`Your plan allows ${seatLimit} seat${seatLimit === 1 ? '' : 's'}. Upgrade to invite more members.`,\n\t\t\t\t\t\t{ limit: seatLimit },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Resolve default role once for entries that omit roleId.\n\t\t\tlet defaultRoleId: string | undefined;\n\t\t\tconst needsDefault = entries.some((e) => !e['roleId']);\n\t\t\tif (needsDefault) {\n\t\t\t\tconst [row] = await store.query<{ id: string }>(\n\t\t\t\t\t`SELECT id FROM fonderie_roles\n\t\t\t\t\t WHERE name = 'ADMIN' AND workspace_id = $1 LIMIT 1`,\n\t\t\t\t\t[ctx.workspace.id],\n\t\t\t\t);\n\t\t\t\tdefaultRoleId = row?.id;\n\t\t\t\tif (!defaultRoleId) {\n\t\t\t\t\treturn setApiResponse(HTTP.SERVER_ERROR, 'SERVER_ERROR', 'Default role not found');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst results = await Promise.all(\n\t\t\t\tentries.map(async (entry) => {\n\t\t\t\t\tconst email = entry['email'] as string;\n\t\t\t\t\tconst resolvedRoleId = (entry['roleId'] as string | undefined) ?? defaultRoleId!;\n\n\t\t\t\t\tconst invitation = await invitations.create({\n\t\t\t\t\t\tworkspaceId: ctx.workspace!.id,\n\t\t\t\t\t\temail,\n\t\t\t\t\t\troleId: resolvedRoleId,\n\t\t\t\t\t\tttl,\n\t\t\t\t\t});\n\n\t\t\t\t\tbus\n\t\t\t\t\t\t?.emit(NOTIFICATION_EVENT, {\n\t\t\t\t\t\t\ttype: MESSAGE_KEYS.workspaceInvitation,\n\t\t\t\t\t\t\trecipient: { email, phone: null, deviceToken: null },\n\t\t\t\t\t\t\tdata: { token: invitation.token, pin: invitation.pin },\n\t\t\t\t\t\t} satisfies ICourierMessage)\n\t\t\t\t\t\t.catch(() => {});\n\n\t\t\t\t\treturn { invitationId: invitation.id, email };\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'INVITATIONS_SENT', 'Invitations sent successfully.', {\n\t\t\t\tinvitations: results,\n\t\t\t});\n\t\t},\n\n\t\tasync cancel(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst invitationId = params?.['inviteId'];\n\n\t\t\tif (!invitationId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'inviteId is required');\n\n\t\t\tawait invitations.cancel(invitationId, ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'INVITATION_CANCELLED', 'Invitation cancelled successfully.');\n\t\t},\n\n\t\tasync accept(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst pin = body?.['pin'];\n\n\t\t\tif (typeof pin !== 'string') {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'pin is required');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { workspaceId } = await invitations.acceptByPin({ pin, userId: ctx.user!.id });\n\t\t\t\treturn setApiResponse(HTTP.OK, 'INVITATION_ACCEPTED', 'Invitation accepted successfully.', {\n\t\t\t\t\tworkspaceId,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : 'Invalid invitation';\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'INVITATION_FAILED', message);\n\t\t\t}\n\t\t},\n\t};\n}\n","import { randomBytes } from 'node:crypto';\n\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IInvitation } from '../types';\n\nfunction generateToken(): string {\n\treturn randomBytes(32).toString('hex');\n}\n\nfunction generatePin(): string {\n\treturn Math.floor(100000 + Math.random() * 900000).toString();\n}\n\nfunction parseTtl(ttl: string): number {\n\tconst units: Record<string, number> = {\n\t\ts: 1000,\n\t\tm: 60_000,\n\t\th: 3_600_000,\n\t\td: 86_400_000,\n\t};\n\tconst match = ttl.match(/^(\\d+)([smhd])$/);\n\tif (!match) return 7 * 86_400_000;\n\tconst [, n, unit] = match;\n\treturn parseInt(n!, 10) * (units[unit!] ?? 0);\n}\n\nconst SELECT_INV = `\n\tid,\n\tworkspace_id AS \"workspaceId\",\n\temail,\n\trole_id AS \"roleId\",\n\ttoken,\n\tpin,\n\tstatus,\n\texpires_at AS \"expiresAt\",\n\tcreated_at AS \"createdAt\"\n`;\n\nexport async function createInvitation(\n\topts: { workspaceId: string; email: string; roleId: string; ttl?: string },\n\tstore: IStoreAdapter,\n): Promise<IInvitation> {\n\tconst token = generateToken();\n\tconst pin = generatePin();\n\tconst expiresAt = new Date(Date.now() + parseTtl(opts.ttl ?? '7d'));\n\n\tconst [invitation] = await store.query<IInvitation>(\n\t\t`INSERT INTO fonderie_workspace_invitations\n\t\t (workspace_id, email, role_id, token, pin, expires_at)\n\t\t VALUES ($1, $2, $3, $4, $5, $6)\n\t\t ON CONFLICT DO NOTHING\n\t\t RETURNING ${SELECT_INV}`,\n\t\t[opts.workspaceId, opts.email, opts.roleId, token, pin, expiresAt],\n\t);\n\n\tif (!invitation) {\n\t\t// Already pending — update it\n\t\tconst [updated] = await store.query<IInvitation>(\n\t\t\t`UPDATE fonderie_workspace_invitations\n\t\t\t SET token = $4, pin = $5, expires_at = $6, role_id = $3, status = 'PENDING'\n\t\t\t WHERE workspace_id = $1 AND email = $2 AND status = 'PENDING'\n\t\t\t RETURNING ${SELECT_INV}`,\n\t\t\t[opts.workspaceId, opts.email, opts.roleId, token, pin, expiresAt],\n\t\t);\n\t\tif (!updated) throw new Error('Failed to create invitation');\n\t\treturn updated;\n\t}\n\n\treturn invitation;\n}\n\nexport async function listInvitations(\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IInvitation[]> {\n\treturn store.query<IInvitation>(\n\t\t`SELECT ${SELECT_INV}\n\t\t FROM fonderie_workspace_invitations\n\t\t WHERE workspace_id = $1 AND status = 'PENDING'\n\t\t ORDER BY created_at DESC`,\n\t\t[workspaceId],\n\t);\n}\n\nexport async function cancelInvitation(\n\tinvitationId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspace_invitations\n\t\t SET status = 'CANCELLED'\n\t\t WHERE id = $1 AND workspace_id = $2 AND status = 'PENDING'`,\n\t\t[invitationId, workspaceId],\n\t);\n}\n\nexport async function acceptInvitationByPin(\n\topts: { pin: string; userId: string },\n\tstore: IStoreAdapter,\n): Promise<{ workspaceId: string; roleId: string }> {\n\tconst [inv] = await store.query<{\n\t\tid: string;\n\t\tworkspaceId: string;\n\t\troleId: string;\n\t\texpiresAt: string;\n\t}>(\n\t\t`SELECT id, workspace_id AS \"workspaceId\", role_id AS \"roleId\", expires_at AS \"expiresAt\"\n\t\t FROM fonderie_workspace_invitations\n\t\t WHERE pin = $1 AND status = 'PENDING'`,\n\t\t[opts.pin],\n\t);\n\n\tif (!inv) throw new Error('Invalid PIN');\n\tif (new Date() > new Date(inv.expiresAt)) throw new Error('Invitation expired');\n\n\tawait store.transaction(async (tx) => {\n\t\tawait Promise.all([\n\t\t\ttx.query(\n\t\t\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t\t\t VALUES ($1, $2, $3, true)\n\t\t\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t\t\t SET confirmed = true, removed = false`,\n\t\t\t\t[opts.userId, inv.workspaceId, inv.roleId],\n\t\t\t),\n\t\t\ttx.query(`UPDATE fonderie_workspace_invitations SET status = 'ACCEPTED' WHERE id = $1`, [\n\t\t\t\tinv.id,\n\t\t\t]),\n\t\t]);\n\t});\n\n\treturn { workspaceId: inv.workspaceId, roleId: inv.roleId };\n}\n\nexport async function acceptInvitationByToken(\n\ttoken: string,\n\tuserId: string,\n\tstore: IStoreAdapter,\n): Promise<{ workspaceId: string; roleId: string }> {\n\tconst [inv] = await store.query<{\n\t\tid: string;\n\t\tworkspaceId: string;\n\t\troleId: string;\n\t\texpiresAt: string;\n\t}>(\n\t\t`SELECT id, workspace_id AS \"workspaceId\", role_id AS \"roleId\", expires_at AS \"expiresAt\"\n\t\t FROM fonderie_workspace_invitations\n\t\t WHERE token = $1 AND status = 'PENDING'`,\n\t\t[token],\n\t);\n\n\tif (!inv) throw new Error('Invalid token');\n\tif (new Date() > new Date(inv.expiresAt)) throw new Error('Invitation expired');\n\n\tawait store.transaction(async (tx) => {\n\t\tawait Promise.all([\n\t\t\ttx.query(\n\t\t\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t\t\t VALUES ($1, $2, $3, true)\n\t\t\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t\t\t SET confirmed = true, removed = false`,\n\t\t\t\t[userId, inv.workspaceId, inv.roleId],\n\t\t\t),\n\t\t\ttx.query(`UPDATE fonderie_workspace_invitations SET status = 'ACCEPTED' WHERE id = $1`, [\n\t\t\t\tinv.id,\n\t\t\t]),\n\t\t]);\n\t});\n\n\treturn { workspaceId: inv.workspaceId, roleId: inv.roleId };\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IInvitation } from '../types';\nimport {\n\tcreateInvitation,\n\tlistInvitations,\n\tcancelInvitation,\n\tacceptInvitationByPin,\n} from '../services/invitations';\n\nexport class InvitationModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tcreate(opts: Parameters<typeof createInvitation>[0]): Promise<IInvitation> {\n\t\treturn createInvitation(opts, this.store);\n\t}\n\n\tlist(workspaceId: string): Promise<IInvitation[]> {\n\t\treturn listInvitations(workspaceId, this.store);\n\t}\n\n\tcancel(invitationId: string, workspaceId: string): Promise<void> {\n\t\treturn cancelInvitation(invitationId, workspaceId, this.store);\n\t}\n\n\tacceptByPin(\n\t\topts: Parameters<typeof acceptInvitationByPin>[0],\n\t): Promise<{ workspaceId: string; roleId: string }> {\n\t\treturn acceptInvitationByPin(opts, this.store);\n\t}\n}\n","import type { IFonderieModule, IFonderieApp } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport type { EventBus } from '@fonderie/events';\n\nimport type { IWorkspacesConfig } from './config';\nimport { EVENT_KEYS } from './config';\nimport { buildWorkspaceRoutes } from './routes';\nimport { WorkspaceModel } from './models/workspace.model';\nimport { RoleModel } from './models/role.model';\nimport { MemberModel } from './models/member.model';\n\n// Mirror of @fonderie/auth EVENT_KEYS.userRegistered — avoids a runtime\n// dependency on the auth package while remaining explicit about the contract.\nconst AUTH_USER_REGISTERED = 'fonderie.user.registered' as const;\n\ninterface UserRegisteredPayload {\n\tuserId: string;\n}\n\nexport class WorkspacesModule implements IFonderieModule {\n\treadonly name = '@fonderie/workspaces';\n\treadonly deps = ['@fonderie/auth', '@fonderie/billing'];\n\n\tconstructor(\n\t\tprivate store: IStoreAdapter,\n\t\tprivate config: IWorkspacesConfig = {},\n\t\tprivate bus?: EventBus,\n\t) {}\n\n\tinstall(app: IFonderieApp): void {\n\t\tif (this.bus && this.config.personalWorkspace !== false) {\n\t\t\tthis.bus.on<UserRegisteredPayload>(\n\t\t\t\tAUTH_USER_REGISTERED,\n\t\t\t\t(payload) => this.provisionPersonalWorkspace(payload),\n\t\t\t\t'workspaces',\n\t\t\t);\n\t\t}\n\n\t\tconst routes = buildWorkspaceRoutes(this.store, this.config, this.bus);\n\t\tfor (const [method, path, ...handlers] of routes) {\n\t\t\tapp.addRoute(method, path, ...handlers);\n\t\t}\n\t}\n\n\tprivate async provisionPersonalWorkspace(payload: UserRegisteredPayload): Promise<void> {\n\t\tconst name = 'My Workspace';\n\t\tconst slug = `${payload.userId}-personal`;\n\n\t\tlet workspaceId: string | undefined;\n\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tconst wsModel = new WorkspaceModel(tx);\n\t\t\tconst roleModel = new RoleModel(tx);\n\t\t\tconst memModel = new MemberModel(tx);\n\n\t\t\tconst ws = await wsModel.createPersonal({ name, slug, ownerId: payload.userId });\n\t\t\tif (!ws) return; // already exists — idempotent replay\n\n\t\t\tworkspaceId = ws.id;\n\n\t\t\tconst adminRole = await roleModel.findSystem('ADMIN');\n\t\t\tif (!adminRole) throw new Error('System ADMIN role not found');\n\n\t\t\tawait memModel.add({\n\t\t\t\tuserId: payload.userId,\n\t\t\t\tworkspaceId: ws.id,\n\t\t\t\troleId: adminRole.id,\n\t\t\t\tconfirmed: true,\n\t\t\t});\n\t\t});\n\n\t\tif (workspaceId) {\n\t\t\tthis.bus\n\t\t\t\t?.emit(EVENT_KEYS.personalWorkspaceCreated, {\n\t\t\t\t\tworkspaceId,\n\t\t\t\t\tuserId: payload.userId,\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\t\t}\n\t}\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { Middleware } from '@fonderie/core';\n\n// Ensures ctx.workspace is set — use after withWorkspace\n\nexport const requireWorkspace: Middleware = async (ctx, next) => {\n\tif (!ctx.workspace) {\n\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'WORKSPACE_REQUIRED', 'Workspace context required');\n\t}\n\treturn next();\n};\n"],"mappings":"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","names":["import_core","name","name","name","import_core","import_core","name","import_core","email","name","import_core"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/routes.ts","../src/schemas.ts","../src/middlewares/workspace-context.ts","../src/services/members.ts","../src/services/workspaces.ts","../src/controllers/workspace.controller.ts","../src/models/workspace.model.ts","../src/models/member.model.ts","../src/services/roles.ts","../src/models/role.model.ts","../src/dtos/workspace.ts","../src/controllers/member.controller.ts","../src/controllers/role.controller.ts","../src/controllers/invitation.controller.ts","../src/services/invitations.ts","../src/models/invitation.model.ts","../src/module.ts","../src/middlewares/require-workspace.ts"],"sourcesContent":["export { WorkspacesModule } from './module';\nexport type { IWorkspacesConfig, WorkspacesMessageKey, WorkspacesEventKey } from './config';\nexport { MESSAGE_KEYS, EVENT_KEYS } from './config';\nexport type {\n\tWorkspaceType,\n\tInvitationStatus,\n\tIWorkspace,\n\tIRole,\n\tIMember,\n\tIInvitation,\n\tIWorkspaceSettings,\n} from './types';\nexport type {\n\tIWorkspaceDTO,\n\tIRoleDTO,\n\tIMemberDTO,\n\tIInvitationDTO,\n\tIWorkspaceSettingsDTO,\n} from './dtos/workspace';\nexport {\n\ttoWorkspaceDTO,\n\ttoRoleDTO,\n\ttoMemberDTO,\n\ttoInvitationDTO,\n\ttoSettingsDTO,\n} from './dtos/workspace';\nexport { withWorkspace } from './middlewares/workspace-context';\nexport { requireWorkspace } from './middlewares/require-workspace';\n\n// Request validation — enforced contract for body-taking routes; exported\n// for docs generation and typed clients.\nexport * as schemas from './schemas';\n","export const MESSAGE_KEYS = {\n\tworkspaceInvitation: 'workspace-invitation',\n} as const;\n\nexport type WorkspacesMessageKey = (typeof MESSAGE_KEYS)[keyof typeof MESSAGE_KEYS];\n\nexport const EVENT_KEYS = {\n\tpersonalWorkspaceCreated: 'fonderie.workspace.personal.created',\n} as const;\n\nexport type WorkspacesEventKey = (typeof EVENT_KEYS)[keyof typeof EVENT_KEYS];\n\nexport interface IWorkspacesConfig {\n\t// How long invitations are valid. Default: '7d'\n\tinvitationTtl?: string;\n\n\t// Default role assigned to invited members. Default: 'member'\n\tdefaultRole?: string;\n\n\t// Auto-create a personal workspace when user.registered fires.\n\t// Requires an EventBus to be passed to WorkspacesModule. Default: true\n\tpersonalWorkspace?: boolean;\n}\n","import type { IStoreAdapter } from '@fonderie/store';\nimport type { Middleware } from '@fonderie/core';\nimport type { EventBus } from '@fonderie/events';\nimport { requireAuth, validate } from '@fonderie/core/middlewares';\n\nimport {\n\tcreateRoleSchema,\n\tupdateRoleSchema,\n\taddMemberRoleSchema,\n\tupdateSettingsSchema,\n\tcreateWorkspaceSchema,\n\tupdateWorkspaceSchema,\n\tacceptInvitationSchema,\n\tcreateInvitationsSchema,\n\tsetRolePermissionsSchema,\n} from './schemas';\n\nimport type { IWorkspacesConfig } from './config';\nimport { withWorkspace } from './middlewares/workspace-context';\n\nimport { workspaceController } from './controllers/workspace.controller';\nimport { memberController } from './controllers/member.controller';\nimport { roleController } from './controllers/role.controller';\nimport { invitationController } from './controllers/invitation.controller';\n\ntype RouteDefinition = [string, string, ...Middleware[]];\n\nexport function buildWorkspaceRoutes(\n\tstore: IStoreAdapter,\n\tconfig: IWorkspacesConfig,\n\tbus?: EventBus,\n): RouteDefinition[] {\n\tconst ttl = config.invitationTtl ?? '7d';\n\tconst wsCtx = withWorkspace(store);\n\n\tconst workspace = workspaceController(store, config);\n\tconst member = memberController(store);\n\tconst role = roleController(store);\n\tconst invitation = invitationController(store, ttl, bus);\n\n\treturn [\n\t\t// ── Workspace creation + listing (no workspace context required)\n\t\t['POST', '/workspaces', requireAuth, validate(createWorkspaceSchema), workspace.create],\n\t\t['GET', '/workspaces', requireAuth, workspace.list],\n\n\t\t// ── Members (workspace resolved from X-Workspace-ID header)\n\t\t['GET', '/workspaces/members', requireAuth, wsCtx, member.list],\n\t\t['DELETE', '/workspaces/members/:userId', requireAuth, wsCtx, member.remove],\n\t\t['GET', '/workspaces/members/:userId/roles', requireAuth, wsCtx, member.getUserRoles],\n\t\t['POST', '/workspaces/members/:userId/roles', requireAuth, wsCtx, validate(addMemberRoleSchema), member.addRole],\n\t\t['DELETE', '/workspaces/members/:userId/roles/:roleId', requireAuth, wsCtx, member.removeRole],\n\n\t\t// ── Invitations\n\t\t['GET', '/workspaces/invitations', requireAuth, wsCtx, invitation.list],\n\t\t['POST', '/workspaces/invitations', requireAuth, wsCtx, validate(createInvitationsSchema), invitation.invite],\n\t\t['DELETE', '/workspaces/invitations/:inviteId', requireAuth, wsCtx, invitation.cancel],\n\t\t['POST', '/workspaces/invitations/accept', requireAuth, validate(acceptInvitationSchema), invitation.accept],\n\n\t\t// ── Roles\n\t\t['POST', '/workspaces/roles', requireAuth, wsCtx, validate(createRoleSchema), role.create],\n\t\t['GET', '/workspaces/roles', requireAuth, wsCtx, role.list],\n\t\t['GET', '/workspaces/roles/:roleId', requireAuth, wsCtx, role.get],\n\t\t['PUT', '/workspaces/roles/:roleId', requireAuth, wsCtx, validate(updateRoleSchema), role.update],\n\t\t['DELETE', '/workspaces/roles/:roleId', requireAuth, wsCtx, role.remove],\n\t\t['POST', '/workspaces/roles/:roleId/permissions', requireAuth, wsCtx, validate(setRolePermissionsSchema), role.setPermissions],\n\n\t\t// ── Workspace lifecycle\n\t\t['POST', '/workspaces/archive', requireAuth, wsCtx, workspace.archive],\n\t\t['POST', '/workspaces/restore', requireAuth, wsCtx, workspace.restore],\n\t\t['GET', '/workspaces/settings', requireAuth, wsCtx, workspace.getSettings],\n\t\t['PUT', '/workspaces/settings', requireAuth, wsCtx, validate(updateSettingsSchema), workspace.updateSettings],\n\n\t\t// ── Path-based lookup by ID (admin / cross-workspace use)\n\t\t['GET', '/workspaces/:id', requireAuth, wsCtx, workspace.get],\n\n\t\t// ── Update current workspace — ID resolved from X-Workspace-ID header\n\t\t// (or personal workspace fallback when header is absent)\n\t\t['PUT', '/workspaces', requireAuth, wsCtx, validate(updateWorkspaceSchema), workspace.update],\n\t];\n}\n","import { z } from 'zod';\n\n// Request schemas — the validation contract for every body-taking workspaces\n// route. Wired into routes.ts via @fonderie/core's validate(); exported so\n// docs generators and typed clients read the source of truth the runtime\n// enforces. Same pattern as @fonderie/auth/src/schemas.ts.\n\nconst name = z.string().trim().min(1, 'name is required').max(200);\nconst email = z.string().trim().pipe(z.email());\n\nexport const createWorkspaceSchema = z.object({\n\tname,\n\tdescription: z.string().max(2000).optional(),\n\t// PERSONAL workspaces are created automatically by the module; the\n\t// controller enforces the rule — schema just types the field.\n\ttype: z.string().max(40).optional(),\n});\n\nconst addressSchema = z\n\t.object({\n\t\tline1: z.string().max(200).optional(),\n\t\tline2: z.string().max(200).optional(),\n\t\tcity: z.string().max(100).optional(),\n\t\tregion: z.string().max(100).optional(),\n\t\tpostalCode: z.string().max(20).optional(),\n\t\tcountry: z.string().max(60).optional(),\n\t})\n\t.passthrough();\n\nexport const updateWorkspaceSchema = z\n\t.object({\n\t\tname: z.string().trim().min(1).max(200).optional(),\n\t\tdescription: z.string().max(2000).nullable().optional(),\n\t\tmotto: z.string().max(300).nullable().optional(),\n\t\tphone: z.string().max(32).nullable().optional(),\n\t\tbusinessType: z.string().max(100).nullable().optional(),\n\t\taddress: addressSchema.nullable().optional(),\n\t})\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\n\nexport const updateSettingsSchema = z\n\t.object({\n\t\tlocale: z.string().max(35).optional(),\n\t\ttimezone: z.string().max(64).optional(),\n\t\tcurrency: z.string().max(8).optional(),\n\t\tdateFormat: z.string().max(32).optional(),\n\t\ttimeFormat: z.string().max(32).optional(),\n\t})\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'No settings provided');\n\nexport const addMemberRoleSchema = z.object({ roleId: z.string().min(1, 'roleId is required') });\n\nconst inviteEntry = z.object({\n\temail,\n\troleId: z.string().min(1).optional(),\n});\n\n// POST /workspaces/invitations accepts a single invite or a batch.\nexport const createInvitationsSchema = z.union([\n\tinviteEntry,\n\tz.array(inviteEntry).min(1, 'at least one invite is required'),\n]);\n\nexport const acceptInvitationSchema = z.object({ pin: z.string().trim().min(1, 'pin is required') });\n\nexport const createRoleSchema = z.object({\n\tname,\n\tdescription: z.string().max(1000).optional(),\n});\n\nexport const updateRoleSchema = z\n\t.object({\n\t\tname: z.string().trim().min(1).max(200).optional(),\n\t\tdescription: z.string().max(1000).nullable().optional(),\n\t\tactive: z.boolean().optional(),\n\t})\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\n\nexport const setRolePermissionsSchema = z.object({\n\tpermissions: z.array(\n\t\tz.object({\n\t\t\tpermissionKey: z.string().min(1),\n\t\t\tcanCreate: z.boolean().optional(),\n\t\t\tcanRead: z.boolean().optional(),\n\t\t\tcanUpdate: z.boolean().optional(),\n\t\t\tcanDelete: z.boolean().optional(),\n\t\t}),\n\t),\n});\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { Middleware } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { getMember } from '../services/members';\nimport { findWorkspaceById, findPersonalWorkspace } from '../services/workspaces';\n\n// Resolves ctx.workspace from:\n// 1. Route param :workspaceId or :id (path-based admin routes)\n// 2. X-Workspace-ID request header (standard resource routes)\n// Validates the current user is an active member.\n// Must run after withSession.\n\nfunction makeHandler(store: IStoreAdapter): Middleware {\n\treturn async (ctx, next) => {\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst workspaceId =\n\t\t\tparams?.['workspaceId'] ??\n\t\t\tparams?.['id'] ??\n\t\t\tctx.request.headers.get('x-workspace-id') ??\n\t\t\tundefined;\n\n\t\tif (!workspaceId) {\n\t\t\t// DMZ fallback — if the caller didn't specify a workspace, use their personal one.\n\t\t\t// No membership check needed: the user is always the sole owner.\n\t\t\tif (ctx.user) {\n\t\t\t\tconst personal = await findPersonalWorkspace(ctx.user.id, store);\n\t\t\t\tif (personal) Object.assign(ctx, { workspace: personal });\n\t\t\t}\n\t\t\treturn next();\n\t\t}\n\n\t\tconst workspace = await findWorkspaceById(workspaceId, store);\n\t\tif (!workspace) {\n\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t}\n\n\t\tif (ctx.user) {\n\t\t\tconst member = await getMember(ctx.user.id, workspaceId, store);\n\t\t\tif (!member) {\n\t\t\t\treturn setApiResponse(HTTP.FORBIDDEN, 'FORBIDDEN', 'Not a member of this workspace');\n\t\t\t}\n\t\t}\n\n\t\tObject.assign(ctx, { workspace });\n\t\treturn next();\n\t};\n}\n\nexport function withWorkspace(store: IStoreAdapter): Middleware;\nexport function withWorkspace(\n\tstore: IStoreAdapter,\n\tctx: IFonderieContext,\n\tnext: () => Promise<Response>,\n): Promise<Response>;\nexport function withWorkspace(\n\tstore: IStoreAdapter,\n\tctx?: IFonderieContext,\n\tnext?: () => Promise<Response>,\n): Middleware | Promise<Response> {\n\tconst handler = makeHandler(store);\n\tif (ctx !== undefined && next !== undefined) return handler(ctx, next);\n\treturn handler;\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IMember, IRole } from '../types';\n\nconst SELECT_MEMBER = `\n\truw.user_id AS \"userId\",\n\truw.workspace_id AS \"workspaceId\",\n\truw.role_id AS \"roleId\",\n\tr.name AS \"roleName\",\n\truw.confirmed AS \"confirmed\",\n\truw.created_at AS \"createdAt\",\n\tu.first_name AS \"firstName\",\n\tu.last_name AS \"lastName\",\n\tu.email AS \"email\",\n\tu.profile_image_url AS \"profileImageUrl\"\n`;\n\nexport async function getMember(\n\tuserId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IMember | null> {\n\tconst [row] = await store.query<IMember>(\n\t\t`SELECT ${SELECT_MEMBER}\n\t\t FROM fonderie_role_user_workspaces ruw\n\t\t LEFT JOIN fonderie_roles r ON r.id = ruw.role_id\n\t\t LEFT JOIN fonderie_users u ON u.id = ruw.user_id\n\t\t WHERE ruw.user_id = $1\n\t\t AND ruw.workspace_id = $2\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false\n\t\t LIMIT 1`,\n\t\t[userId, workspaceId],\n\t);\n\treturn row ?? null;\n}\n\nexport async function listMembers(workspaceId: string, store: IStoreAdapter): Promise<IMember[]> {\n\treturn store.query<IMember>(\n\t\t`SELECT ${SELECT_MEMBER}\n\t\t FROM fonderie_role_user_workspaces ruw\n\t\t LEFT JOIN fonderie_roles r ON r.id = ruw.role_id\n\t\t LEFT JOIN fonderie_users u ON u.id = ruw.user_id\n\t\t WHERE ruw.workspace_id = $1\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false\n\t\t ORDER BY ruw.created_at ASC`,\n\t\t[workspaceId],\n\t);\n}\n\nexport async function addMember(\n\topts: { userId: string; workspaceId: string; roleId: string; confirmed?: boolean },\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t VALUES ($1, $2, $3, $4)\n\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t SET confirmed = $4, removed = false, suspended = false`,\n\t\t[opts.userId, opts.workspaceId, opts.roleId, opts.confirmed ?? true],\n\t);\n}\n\nexport async function removeMember(\n\tuserId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_role_user_workspaces\n\t\t SET removed = true\n\t\t WHERE user_id = $1\n\t\t AND workspace_id = $2`,\n\t\t[userId, workspaceId],\n\t);\n}\n\nexport async function getUserRoles(\n\tuserId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IRole[]> {\n\treturn store.query<IRole>(\n\t\t`SELECT\n\t\t r.id,\n\t\t r.name,\n\t\t r.is_system AS \"isSystem\",\n\t\t r.active,\n\t\t r.description,\n\t\t r.workspace_id AS \"workspaceId\"\n\t\t FROM fonderie_role_user_workspaces ruw\n\t\t JOIN fonderie_roles r ON r.id = ruw.role_id\n\t\t WHERE ruw.user_id = $1\n\t\t AND ruw.workspace_id = $2\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false`,\n\t\t[userId, workspaceId],\n\t);\n}\n\nexport async function addRoleToMember(\n\tuserId: string,\n\tworkspaceId: string,\n\troleId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t VALUES ($1, $2, $3, true)\n\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t SET confirmed = true, removed = false, suspended = false`,\n\t\t[userId, workspaceId, roleId],\n\t);\n}\n\nexport async function removeRoleFromMember(\n\tuserId: string,\n\tworkspaceId: string,\n\troleId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tconst remaining = await store.query<{ count: string }>(\n\t\t`SELECT COUNT(*) AS count\n\t\t FROM fonderie_role_user_workspaces\n\t\t WHERE user_id = $1\n\t\t AND workspace_id = $2\n\t\t AND removed = false`,\n\t\t[userId, workspaceId],\n\t);\n\tconst count = parseInt(remaining[0]?.count ?? '0', 10);\n\tif (count <= 1) throw new Error('Cannot remove last role from member');\n\n\tawait store.query(\n\t\t`DELETE FROM fonderie_role_user_workspaces\n\t\t WHERE user_id = $1\n\t\t AND workspace_id = $2\n\t\t AND role_id = $3`,\n\t\t[userId, workspaceId, roleId],\n\t);\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IWorkspace, IWorkspaceAddress, IWorkspaceSettings } from '../types';\n\nconst SELECT_WS = `\n\tid,\n\tname,\n\tslug,\n\ttype,\n\tdescription,\n\tmotto,\n\tphone,\n\tbusiness_type AS \"businessType\",\n\taddress,\n\tplan,\n\towner_id AS \"ownerId\",\n\tis_personal AS \"isPersonal\",\n\tarchived_at AS \"archivedAt\",\n\tarchived_by AS \"archivedBy\",\n\tcreated_at AS \"createdAt\",\n\tupdated_at AS \"updatedAt\"\n`;\n\nconst SELECT_WS_W = `\n\tw.id,\n\tw.name,\n\tw.slug,\n\tw.type,\n\tw.description,\n\tw.motto,\n\tw.phone,\n\tw.business_type AS \"businessType\",\n\tw.address,\n\tw.plan,\n\tw.owner_id AS \"ownerId\",\n\tw.is_personal AS \"isPersonal\",\n\tw.archived_at AS \"archivedAt\",\n\tw.archived_by AS \"archivedBy\",\n\tw.created_at AS \"createdAt\",\n\tw.updated_at AS \"updatedAt\"\n`;\n\nexport async function findWorkspaceById(\n\tid: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst [row] = await store.query<IWorkspace>(\n\t\t`SELECT ${SELECT_WS} FROM fonderie_workspaces WHERE id = $1`,\n\t\t[id],\n\t);\n\treturn row ?? null;\n}\n\nexport async function findWorkspacesByUserId(\n\tuserId: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspace[]> {\n\treturn store.query<IWorkspace>(\n\t\t`SELECT ${SELECT_WS_W}\n\t\t FROM fonderie_workspaces w\n\t\t JOIN fonderie_role_user_workspaces ruw ON ruw.workspace_id = w.id\n\t\t WHERE ruw.user_id = $1\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false\n\t\t GROUP BY w.id\n\t\t ORDER BY w.created_at ASC`,\n\t\t[userId],\n\t);\n}\n\nexport async function createWorkspace(\n\topts: {\n\t\tname: string;\n\t\tslug: string;\n\t\townerId: string;\n\t\ttype?: string;\n\t\tdescription?: string;\n\t\tplan?: string;\n\t},\n\tstore: IStoreAdapter,\n): Promise<IWorkspace> {\n\tconst [workspace] = await store.query<IWorkspace>(\n\t\t`INSERT INTO fonderie_workspaces (name, slug, owner_id, type, description, plan)\n\t\t VALUES ($1, $2, $3, $4, $5, $6)\n\t\t RETURNING ${SELECT_WS}`,\n\t\t[\n\t\t\topts.name,\n\t\t\topts.slug,\n\t\t\topts.ownerId,\n\t\t\topts.type ?? 'ORGANIZATION',\n\t\t\topts.description ?? null,\n\t\t\topts.plan ?? 'free',\n\t\t],\n\t);\n\n\tif (!workspace) throw new Error('Failed to create workspace');\n\treturn workspace;\n}\n\n// Returns the new workspace, or null if the personal workspace already exists (idempotent).\nexport async function createPersonalWorkspace(\n\topts: { name: string; slug: string; ownerId: string },\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst [workspace] = await store.query<IWorkspace>(\n\t\t`INSERT INTO fonderie_workspaces (name, slug, owner_id, type, is_personal)\n\t\t VALUES ($1, $2, $3, 'PERSONAL', true)\n\t\t ON CONFLICT (owner_id) WHERE is_personal = true DO NOTHING\n\t\t RETURNING ${SELECT_WS}`,\n\t\t[opts.name, opts.slug, opts.ownerId],\n\t);\n\treturn workspace ?? null;\n}\n\nexport async function findPersonalWorkspace(\n\tuserId: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst [row] = await store.query<IWorkspace>(\n\t\t`SELECT ${SELECT_WS} FROM fonderie_workspaces\n\t\t WHERE owner_id = $1 AND is_personal = true\n\t\t LIMIT 1`,\n\t\t[userId],\n\t);\n\treturn row ?? null;\n}\n\nexport async function updateWorkspace(\n\tid: string,\n\topts: {\n\t\tname?: string;\n\t\tdescription?: string | null;\n\t\tslug?: string;\n\t\tmotto?: string | null;\n\t\tphone?: string | null;\n\t\tbusinessType?: string | null;\n\t\taddress?: IWorkspaceAddress | null;\n\t},\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst sets: string[] = ['updated_at = now()'];\n\tconst params: unknown[] = [id];\n\n\tif (opts.name !== undefined) {\n\t\tparams.push(opts.name);\n\t\tsets.push(`name = $${params.length}`);\n\t}\n\tif (opts.description !== undefined) {\n\t\tparams.push(opts.description);\n\t\tsets.push(`description = $${params.length}`);\n\t}\n\tif (opts.slug !== undefined) {\n\t\tparams.push(opts.slug);\n\t\tsets.push(`slug = $${params.length}`);\n\t}\n\tif (opts.motto !== undefined) {\n\t\tparams.push(opts.motto);\n\t\tsets.push(`motto = $${params.length}`);\n\t}\n\tif (opts.phone !== undefined) {\n\t\tparams.push(opts.phone);\n\t\tsets.push(`phone = $${params.length}`);\n\t}\n\tif (opts.businessType !== undefined) {\n\t\tparams.push(opts.businessType);\n\t\tsets.push(`business_type = $${params.length}`);\n\t}\n\tif (opts.address !== undefined) {\n\t\tparams.push(JSON.stringify(opts.address ?? {}));\n\t\tsets.push(`address = $${params.length}::jsonb`);\n\t}\n\n\tconst [row] = await store.query<IWorkspace>(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET ${sets.join(', ')}\n\t\t WHERE id = $1\n\t\t RETURNING ${SELECT_WS}`,\n\t\tparams,\n\t);\n\treturn row ?? null;\n}\n\nexport async function archiveWorkspace(\n\tid: string,\n\tbyUser: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET archived_at = now(), archived_by = $2, updated_at = now()\n\t\t WHERE id = $1 AND archived_at IS NULL`,\n\t\t[id, byUser],\n\t);\n}\n\nexport async function restoreWorkspace(id: string, store: IStoreAdapter): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET archived_at = NULL, archived_by = NULL, updated_at = now()\n\t\t WHERE id = $1`,\n\t\t[id],\n\t);\n}\n\nconst SETTINGS_DEFAULTS: IWorkspaceSettings = {\n\tlocale: 'en-US',\n\ttimezone: 'UTC',\n\tcurrency: 'USD',\n\tdateFormat: 'MM/DD/YYYY',\n\ttimeFormat: 'hh:mm A',\n};\n\nexport async function getWorkspaceSettings(\n\tid: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspaceSettings> {\n\tconst [row] = await store.query<{ settings: Record<string, unknown> }>(\n\t\t`SELECT settings FROM fonderie_workspaces WHERE id = $1`,\n\t\t[id],\n\t);\n\tconst raw = row?.settings ?? {};\n\tconst s = (raw['settings'] as Record<string, unknown> | undefined) ?? raw;\n\n\treturn {\n\t\tlocale: typeof s['locale'] === 'string' ? s['locale'] : SETTINGS_DEFAULTS.locale,\n\t\ttimezone: typeof s['timezone'] === 'string' ? s['timezone'] : SETTINGS_DEFAULTS.timezone,\n\t\tcurrency: typeof s['currency'] === 'string' ? s['currency'] : SETTINGS_DEFAULTS.currency,\n\t\tdateFormat:\n\t\t\ttypeof s['dateFormat'] === 'string' ? s['dateFormat'] : SETTINGS_DEFAULTS.dateFormat,\n\t\ttimeFormat:\n\t\t\ttypeof s['timeFormat'] === 'string' ? s['timeFormat'] : SETTINGS_DEFAULTS.timeFormat,\n\t};\n}\n\nexport async function updateWorkspaceSettings(\n\tid: string,\n\tsettings: Partial<IWorkspaceSettings>,\n\tstore: IStoreAdapter,\n): Promise<IWorkspaceSettings> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET settings = settings || jsonb_build_object('settings', $2::jsonb),\n\t\t updated_at = now()\n\t\t WHERE id = $1`,\n\t\t[id, JSON.stringify(settings)],\n\t);\n\treturn getWorkspaceSettings(id, store);\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IWorkspacesConfig } from '../config';\nimport type { IWorkspace } from '../types';\nimport { WorkspaceModel } from '../models/workspace.model';\nimport { MemberModel } from '../models/member.model';\nimport { RoleModel } from '../models/role.model';\nimport { toWorkspaceDTO, toSettingsDTO } from '../dtos/workspace';\n\nexport function workspaceController(store: IStoreAdapter, config: IWorkspacesConfig) {\n\tconst workspaces = new WorkspaceModel(store);\n\tconst members = new MemberModel(store);\n\tconst roles = new RoleModel(store);\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.user)\n\t\t\t\treturn setApiResponse(HTTP.UNAUTHORIZED, 'UNAUTHORIZED', 'Authentication required');\n\t\t\tconst list = await workspaces.findByUserId(ctx.user.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACES_FETCHED', 'Workspaces retrieved successfully.', {\n\t\t\t\tworkspaces: list.map(toWorkspaceDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync create(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst name = body?.['name'];\n\t\t\tconst description = body?.['description'];\n\t\t\tconst type = body?.['type'];\n\n\t\t\tif (typeof name !== 'string' || name.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'name is required');\n\t\t\t}\n\n\t\t\tif (type === 'PERSONAL') {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t'Personal workspaces are created automatically',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst slug = name\n\t\t\t\t.toLowerCase()\n\t\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t\t.replace(/^-|-$/g, '');\n\n\t\t\tconst workspace = await store.transaction(async (tx) => {\n\t\t\t\tconst wsOpts: Parameters<typeof workspaces.create>[0] = {\n\t\t\t\t\tname: name.trim(),\n\t\t\t\t\tslug,\n\t\t\t\t\townerId: ctx.user!.id,\n\t\t\t\t\ttype: typeof type === 'string' ? type : 'ORGANIZATION',\n\t\t\t\t};\n\t\t\t\tif (typeof description === 'string') wsOpts.description = description;\n\n\t\t\t\tconst wsModel = new WorkspaceModel(tx);\n\t\t\t\tconst roleModel = new RoleModel(tx);\n\t\t\t\tconst memModel = new MemberModel(tx);\n\n\t\t\t\tconst ws = await wsModel.create(wsOpts);\n\n\t\t\t\tconst adminRole = await roleModel.findSystem('ADMIN');\n\t\t\t\tif (!adminRole) throw new Error('System ADMIN role not found');\n\t\t\t\tawait memModel.add({ userId: ctx.user!.id, workspaceId: ws.id, roleId: adminRole.id });\n\n\t\t\t\treturn ws;\n\t\t\t});\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'WORKSPACE_CREATED', 'Workspace created successfully.', {\n\t\t\t\tworkspace: toWorkspaceDTO(workspace),\n\t\t\t});\n\t\t},\n\n\t\tasync get(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_FETCHED', 'Workspace retrieved successfully.', {\n\t\t\t\tworkspace: toWorkspaceDTO(ctx.workspace as IWorkspace),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst opts: Parameters<typeof workspaces.update>[1] = {};\n\n\t\t\tif (typeof body?.['name'] === 'string') opts.name = body['name'].trim();\n\t\t\tif (body?.['description'] !== undefined)\n\t\t\t\topts.description = typeof body['description'] === 'string' ? body['description'] : null;\n\t\t\tif (body?.['motto'] !== undefined)\n\t\t\t\topts.motto = typeof body['motto'] === 'string' ? body['motto'] : null;\n\t\t\tif (body?.['phone'] !== undefined)\n\t\t\t\topts.phone = typeof body['phone'] === 'string' ? body['phone'].trim() : null;\n\t\t\tif (body?.['businessType'] !== undefined)\n\t\t\t\topts.businessType = typeof body['businessType'] === 'string' ? body['businessType'] : null;\n\t\t\tif (body?.['address'] !== undefined && typeof body['address'] === 'object')\n\t\t\t\topts.address = (body['address'] ?? null) as NonNullable<\n\t\t\t\t\tParameters<typeof workspaces.update>[1]\n\t\t\t\t>['address'] & (object | null);\n\n\t\t\tconst workspace = await workspaces.update(ctx.workspace.id, opts);\n\t\t\tif (!workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_UPDATED', 'Workspace updated successfully.', {\n\t\t\t\tworkspace: toWorkspaceDTO(workspace),\n\t\t\t});\n\t\t},\n\n\t\tasync archive(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\tif (ctx.workspace.isPersonal) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.FORBIDDEN,\n\t\t\t\t\t'FORBIDDEN',\n\t\t\t\t\t'Personal workspaces cannot be archived',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tawait workspaces.archive(ctx.workspace.id, ctx.user!.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_ARCHIVED', 'Workspace archived successfully.');\n\t\t},\n\n\t\tasync restore(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tawait workspaces.restore(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_RESTORED', 'Workspace restored successfully.');\n\t\t},\n\n\t\tasync getSettings(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst settings = await workspaces.getSettings(ctx.workspace.id);\n\t\t\treturn setApiResponse(\n\t\t\t\tHTTP.OK,\n\t\t\t\t'SETTINGS_FETCHED',\n\t\t\t\t'Workspace settings retrieved successfully.',\n\t\t\t\t{\n\t\t\t\t\tsettings: toSettingsDTO(settings),\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tasync updateSettings(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tif (!body || Object.keys(body).length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'No settings provided');\n\t\t\t}\n\n\t\t\tconst patch: Record<string, string> = {};\n\t\t\tfor (const key of ['locale', 'timezone', 'currency', 'dateFormat', 'timeFormat'] as const) {\n\t\t\t\tif (typeof body[key] === 'string') patch[key] = body[key] as string;\n\t\t\t}\n\n\t\t\tconst settings = await workspaces.updateSettings(ctx.workspace.id, patch);\n\t\t\treturn setApiResponse(\n\t\t\t\tHTTP.OK,\n\t\t\t\t'SETTINGS_UPDATED',\n\t\t\t\t'Workspace settings updated successfully.',\n\t\t\t\t{\n\t\t\t\t\tsettings: toSettingsDTO(settings),\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t};\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IWorkspace, IWorkspaceSettings } from '../types';\nimport {\n\tfindWorkspaceById,\n\tfindWorkspacesByUserId,\n\tcreateWorkspace,\n\tcreatePersonalWorkspace,\n\tfindPersonalWorkspace,\n\tupdateWorkspace,\n\tarchiveWorkspace,\n\trestoreWorkspace,\n\tgetWorkspaceSettings,\n\tupdateWorkspaceSettings,\n} from '../services/workspaces';\n\nexport class WorkspaceModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tfindById(id: string): Promise<IWorkspace | null> {\n\t\treturn findWorkspaceById(id, this.store);\n\t}\n\n\tfindByUserId(userId: string): Promise<IWorkspace[]> {\n\t\treturn findWorkspacesByUserId(userId, this.store);\n\t}\n\n\tcreate(opts: Parameters<typeof createWorkspace>[0]): Promise<IWorkspace> {\n\t\treturn createWorkspace(opts, this.store);\n\t}\n\n\tcreatePersonal(opts: Parameters<typeof createPersonalWorkspace>[0]): Promise<IWorkspace | null> {\n\t\treturn createPersonalWorkspace(opts, this.store);\n\t}\n\n\tfindPersonal(userId: string): Promise<IWorkspace | null> {\n\t\treturn findPersonalWorkspace(userId, this.store);\n\t}\n\n\tupdate(id: string, opts: Parameters<typeof updateWorkspace>[1]): Promise<IWorkspace | null> {\n\t\treturn updateWorkspace(id, opts, this.store);\n\t}\n\n\tarchive(id: string, byUser: string): Promise<void> {\n\t\treturn archiveWorkspace(id, byUser, this.store);\n\t}\n\n\trestore(id: string): Promise<void> {\n\t\treturn restoreWorkspace(id, this.store);\n\t}\n\n\tgetSettings(id: string): Promise<IWorkspaceSettings> {\n\t\treturn getWorkspaceSettings(id, this.store);\n\t}\n\n\tupdateSettings(id: string, settings: Partial<IWorkspaceSettings>): Promise<IWorkspaceSettings> {\n\t\treturn updateWorkspaceSettings(id, settings, this.store);\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IMember, IRole } from '../types';\nimport {\n\tgetMember,\n\tlistMembers,\n\taddMember,\n\tremoveMember,\n\tgetUserRoles,\n\taddRoleToMember,\n\tremoveRoleFromMember,\n} from '../services/members';\n\nexport class MemberModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tget(userId: string, workspaceId: string): Promise<IMember | null> {\n\t\treturn getMember(userId, workspaceId, this.store);\n\t}\n\n\tlist(workspaceId: string): Promise<IMember[]> {\n\t\treturn listMembers(workspaceId, this.store);\n\t}\n\n\tadd(opts: Parameters<typeof addMember>[0]): Promise<void> {\n\t\treturn addMember(opts, this.store);\n\t}\n\n\tremove(userId: string, workspaceId: string): Promise<void> {\n\t\treturn removeMember(userId, workspaceId, this.store);\n\t}\n\n\tgetUserRoles(userId: string, workspaceId: string): Promise<IRole[]> {\n\t\treturn getUserRoles(userId, workspaceId, this.store);\n\t}\n\n\taddRole(userId: string, workspaceId: string, roleId: string): Promise<void> {\n\t\treturn addRoleToMember(userId, workspaceId, roleId, this.store);\n\t}\n\n\tremoveRole(userId: string, workspaceId: string, roleId: string): Promise<void> {\n\t\treturn removeRoleFromMember(userId, workspaceId, roleId, this.store);\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IRole } from '../types';\n\nconst SELECT_ROLE = `\n\tid,\n\tname,\n\tis_system AS \"isSystem\",\n\tactive,\n\tdescription,\n\tworkspace_id AS \"workspaceId\"\n`;\n\nexport async function createRole(\n\topts: { name: string; workspaceId: string; description?: string },\n\tstore: IStoreAdapter,\n): Promise<IRole> {\n\tconst [role] = await store.query<IRole>(\n\t\t`INSERT INTO fonderie_roles (name, workspace_id, description)\n\t\t VALUES ($1, $2, $3)\n\t\t RETURNING ${SELECT_ROLE}`,\n\t\t[opts.name, opts.workspaceId, opts.description ?? null],\n\t);\n\tif (!role) throw new Error('Failed to create role');\n\treturn role;\n}\n\nexport async function findSystemRole(name: string, store: IStoreAdapter): Promise<IRole | null> {\n\tconst [row] = await store.query<IRole>(\n\t\t`SELECT ${SELECT_ROLE} FROM fonderie_roles WHERE name = $1 AND is_system = true LIMIT 1`,\n\t\t[name],\n\t);\n\treturn row ?? null;\n}\n\nexport async function getRoleById(id: string, store: IStoreAdapter): Promise<IRole | null> {\n\tconst [row] = await store.query<IRole>(\n\t\t`SELECT ${SELECT_ROLE} FROM fonderie_roles WHERE id = $1`,\n\t\t[id],\n\t);\n\treturn row ?? null;\n}\n\nexport async function listWorkspaceRoles(\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IRole[]> {\n\treturn store.query<IRole>(\n\t\t`SELECT ${SELECT_ROLE}\n\t\t FROM fonderie_roles\n\t\t WHERE workspace_id = $1 OR is_system = true\n\t\t ORDER BY is_system DESC, name ASC`,\n\t\t[workspaceId],\n\t);\n}\n\nexport async function updateRole(\n\tid: string,\n\topts: { name?: string; description?: string | null; active?: boolean },\n\tstore: IStoreAdapter,\n): Promise<IRole | null> {\n\tconst sets: string[] = [];\n\tconst params: unknown[] = [id];\n\n\tif (opts.name !== undefined) {\n\t\tparams.push(opts.name);\n\t\tsets.push(`name = $${params.length}`);\n\t}\n\tif (opts.description !== undefined) {\n\t\tparams.push(opts.description);\n\t\tsets.push(`description = $${params.length}`);\n\t}\n\tif (opts.active !== undefined) {\n\t\tparams.push(opts.active);\n\t\tsets.push(`active = $${params.length}`);\n\t}\n\n\tif (sets.length === 0) return getRoleById(id, store);\n\n\tconst [row] = await store.query<IRole>(\n\t\t`UPDATE fonderie_roles\n\t\t SET ${sets.join(', ')}\n\t\t WHERE id = $1 AND is_system = false\n\t\t RETURNING ${SELECT_ROLE}`,\n\t\tparams,\n\t);\n\treturn row ?? null;\n}\n\nexport async function deleteRole(\n\tid: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`DELETE FROM fonderie_roles\n\t\t WHERE id = $1 AND workspace_id = $2 AND is_system = false`,\n\t\t[id, workspaceId],\n\t);\n}\n\nexport async function setRolePermissions(\n\troleId: string,\n\tworkspaceId: string,\n\tpermissions: Array<{\n\t\tpermissionKey: string;\n\t\tcanCreate: boolean;\n\t\tcanRead: boolean;\n\t\tcanUpdate: boolean;\n\t\tcanDelete: boolean;\n\t}>,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tif (permissions.length === 0) {\n\t\tawait store.query(\n\t\t\t`DELETE FROM fonderie_role_permissions WHERE role_id = $1 AND workspace_id = $2`,\n\t\t\t[roleId, workspaceId],\n\t\t);\n\t\treturn;\n\t}\n\n\tawait store.transaction(async (tx) => {\n\t\tawait tx.query(\n\t\t\t`DELETE FROM fonderie_role_permissions WHERE role_id = $1 AND workspace_id = $2`,\n\t\t\t[roleId, workspaceId],\n\t\t);\n\n\t\tfor (const p of permissions) {\n\t\t\tawait tx.query(\n\t\t\t\t`INSERT INTO fonderie_role_permissions\n\t\t\t\t (role_id, workspace_id, permission_key, can_create, can_read, can_update, can_delete)\n\t\t\t\t VALUES ($1, $2, $3, $4, $5, $6, $7)\n\t\t\t\t ON CONFLICT (role_id, permission_key)\n\t\t\t\t DO UPDATE SET\n\t\t\t\t can_create = $4, can_read = $5,\n\t\t\t\t can_update = $6, can_delete = $7`,\n\t\t\t\t[roleId, workspaceId, p.permissionKey, p.canCreate, p.canRead, p.canUpdate, p.canDelete],\n\t\t\t);\n\t\t}\n\t});\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IRole } from '../types';\nimport {\n\tcreateRole,\n\tfindSystemRole,\n\tgetRoleById,\n\tlistWorkspaceRoles,\n\tupdateRole,\n\tdeleteRole,\n\tsetRolePermissions,\n} from '../services/roles';\n\nexport class RoleModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tcreate(opts: Parameters<typeof createRole>[0]): Promise<IRole> {\n\t\treturn createRole(opts, this.store);\n\t}\n\n\tfindSystem(name: string): Promise<IRole | null> {\n\t\treturn findSystemRole(name, this.store);\n\t}\n\n\tfindById(id: string): Promise<IRole | null> {\n\t\treturn getRoleById(id, this.store);\n\t}\n\n\tlist(workspaceId: string): Promise<IRole[]> {\n\t\treturn listWorkspaceRoles(workspaceId, this.store);\n\t}\n\n\tupdate(id: string, opts: Parameters<typeof updateRole>[1]): Promise<IRole | null> {\n\t\treturn updateRole(id, opts, this.store);\n\t}\n\n\tdelete(id: string, workspaceId: string): Promise<void> {\n\t\treturn deleteRole(id, workspaceId, this.store);\n\t}\n\n\tsetPermissions(\n\t\troleId: string,\n\t\tworkspaceId: string,\n\t\tpermissions: Parameters<typeof setRolePermissions>[2],\n\t): Promise<void> {\n\t\treturn setRolePermissions(roleId, workspaceId, permissions, this.store);\n\t}\n}\n","import { stringOrEmpty, booleanOrFalse } from '@fonderie/core/parser';\n\nimport type { IWorkspace, IRole, IMember, IInvitation, IWorkspaceSettings } from '../types';\n\nexport interface IWorkspaceAddressDTO {\n\tline1: string;\n\tline2: string;\n\tcity: string;\n\tstate: string;\n\tzip: string;\n\tcountry: string;\n}\n\nexport interface IWorkspaceDTO {\n\tid: string;\n\tname: string;\n\tslug: string;\n\ttype: string;\n\tdescription: string;\n\tmotto: string;\n\tphone: string;\n\tbusinessType: string;\n\taddress: IWorkspaceAddressDTO;\n\tplan: string;\n\townerId: string;\n\tisPersonal: boolean;\n\tisArchived: boolean;\n\tarchivedAt: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface IRoleDTO {\n\tid: string;\n\tname: string;\n\tisSystem: boolean;\n\tactive: boolean;\n\tdescription: string;\n\tworkspaceId: string;\n}\n\nexport interface IMemberDTO {\n\tuserId: string;\n\tworkspaceId: string;\n\troleId: string;\n\troleName: string;\n\tconfirmed: boolean;\n\tcreatedAt: string;\n}\n\nexport interface IInvitationDTO {\n\tid: string;\n\tworkspaceId: string;\n\temail: string;\n\troleId: string;\n\ttoken: string;\n\tstatus: string;\n\texpiresAt: string;\n\tcreatedAt: string;\n}\n\nexport interface IWorkspaceSettingsDTO {\n\tlocale: string;\n\ttimezone: string;\n\tcurrency: string;\n\tdateFormat: string;\n\ttimeFormat: string;\n}\n\nexport function toWorkspaceDTO(ws: IWorkspace): IWorkspaceDTO {\n\tconst addr = ws.address ?? {};\n\treturn {\n\t\tid: stringOrEmpty(ws.id),\n\t\tname: stringOrEmpty(ws.name),\n\t\tslug: stringOrEmpty(ws.slug),\n\t\ttype: stringOrEmpty(ws.type),\n\t\tdescription: stringOrEmpty(ws.description),\n\t\tmotto: stringOrEmpty(ws.motto),\n\t\tphone: stringOrEmpty(ws.phone),\n\t\tbusinessType: stringOrEmpty(ws.businessType),\n\t\taddress: {\n\t\t\tline1: stringOrEmpty(addr.line1),\n\t\t\tline2: stringOrEmpty(addr.line2),\n\t\t\tcity: stringOrEmpty(addr.city),\n\t\t\tstate: stringOrEmpty(addr.state),\n\t\t\tzip: stringOrEmpty(addr.zip),\n\t\t\tcountry: stringOrEmpty(addr.country),\n\t\t},\n\t\tplan: stringOrEmpty(ws.plan),\n\t\townerId: stringOrEmpty(ws.ownerId),\n\t\tisPersonal: booleanOrFalse(ws.isPersonal),\n\t\tisArchived: ws.archivedAt !== null,\n\t\tarchivedAt: stringOrEmpty(ws.archivedAt),\n\t\tcreatedAt: stringOrEmpty(ws.createdAt),\n\t\tupdatedAt: stringOrEmpty(ws.updatedAt),\n\t};\n}\n\nexport function toRoleDTO(role: IRole): IRoleDTO {\n\treturn {\n\t\tid: stringOrEmpty(role.id),\n\t\tname: stringOrEmpty(role.name),\n\t\tisSystem: booleanOrFalse(role.isSystem),\n\t\tactive: role.active !== false,\n\t\tdescription: stringOrEmpty(role.description),\n\t\tworkspaceId: stringOrEmpty(role.workspaceId),\n\t};\n}\n\nexport function toMemberDTO(m: IMember): IMemberDTO {\n\treturn {\n\t\tuserId: stringOrEmpty(m.userId),\n\t\tworkspaceId: stringOrEmpty(m.workspaceId),\n\t\troleId: stringOrEmpty(m.roleId),\n\t\troleName: stringOrEmpty(m.roleName),\n\t\tconfirmed: booleanOrFalse(m.confirmed),\n\t\tcreatedAt: stringOrEmpty(m.createdAt),\n\t};\n}\n\nexport function toInvitationDTO(inv: IInvitation): IInvitationDTO {\n\treturn {\n\t\tid: stringOrEmpty(inv.id),\n\t\tworkspaceId: stringOrEmpty(inv.workspaceId),\n\t\temail: stringOrEmpty(inv.email),\n\t\troleId: stringOrEmpty(inv.roleId),\n\t\ttoken: stringOrEmpty(inv.token),\n\t\tstatus: stringOrEmpty(inv.status),\n\t\texpiresAt: stringOrEmpty(inv.expiresAt),\n\t\tcreatedAt: stringOrEmpty(inv.createdAt),\n\t};\n}\n\nexport function toSettingsDTO(s: IWorkspaceSettings): IWorkspaceSettingsDTO {\n\treturn {\n\t\tlocale: stringOrEmpty(s.locale),\n\t\ttimezone: stringOrEmpty(s.timezone),\n\t\tcurrency: stringOrEmpty(s.currency),\n\t\tdateFormat: stringOrEmpty(s.dateFormat),\n\t\ttimeFormat: stringOrEmpty(s.timeFormat),\n\t};\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { MemberModel } from '../models/member.model';\nimport { toMemberDTO, toRoleDTO } from '../dtos/workspace';\n\nexport function memberController(store: IStoreAdapter) {\n\tconst members = new MemberModel(store);\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst list = await members.list(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'MEMBERS_FETCHED', 'Members retrieved successfully.', {\n\t\t\t\tmembers: list.map(toMemberDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\tif (ctx.workspace.isPersonal) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.FORBIDDEN,\n\t\t\t\t\t'FORBIDDEN',\n\t\t\t\t\t'Personal workspaces do not support member management',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\t\t\tif (userId === ctx.user?.id) {\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'INVALID_OPERATION', 'Cannot remove yourself');\n\t\t\t}\n\n\t\t\tawait members.remove(userId, ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'MEMBER_REMOVED', 'Member removed successfully.');\n\t\t},\n\n\t\tasync getUserRoles(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\n\t\t\tconst roles = await members.getUserRoles(userId, ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLES_FETCHED', 'Member roles retrieved successfully.', {\n\t\t\t\troles: roles.map(toRoleDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync addRole(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\t\t\tconst roleId = (body?.['roleId'] ?? params?.['roleId']) as string | undefined;\n\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\t\t\tif (!roleId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\n\t\t\tawait members.addRole(userId, ctx.workspace.id, roleId);\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_ASSIGNED', 'Role assigned successfully.');\n\t\t},\n\n\t\tasync removeRole(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\t\t\tconst roleId = params?.['roleId'];\n\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\t\t\tif (!roleId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\n\t\t\ttry {\n\t\t\t\tawait members.removeRole(userId, ctx.workspace.id, roleId);\n\t\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_REMOVED', 'Role removed successfully.');\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : 'Failed';\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'OPERATION_FAILED', message);\n\t\t\t}\n\t\t},\n\t};\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { RoleModel } from '../models/role.model';\nimport { toRoleDTO } from '../dtos/workspace';\n\nexport function roleController(store: IStoreAdapter) {\n\tconst roles = new RoleModel(store);\n\n\treturn {\n\t\tasync create(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst name = body?.['name'];\n\t\t\tconst description = body?.['description'];\n\n\t\t\tif (typeof name !== 'string' || name.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'name is required');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst opts: Parameters<typeof roles.create>[0] = {\n\t\t\t\t\tname: name.trim(),\n\t\t\t\t\tworkspaceId: ctx.workspace.id,\n\t\t\t\t};\n\n\t\t\t\tif (typeof description === 'string') {\n\t\t\t\t\topts.description = description;\n\t\t\t\t}\n\n\t\t\t\tconst role = await roles.create(opts);\n\t\t\t\treturn setApiResponse(HTTP.CREATED, 'ROLE_CREATED', 'Role created successfully.', {\n\t\t\t\t\trole: toRoleDTO(role),\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : 'Failed to create role';\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'OPERATION_FAILED', message);\n\t\t\t}\n\t\t},\n\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst list = await roles.list(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLES_FETCHED', 'Roles retrieved successfully.', {\n\t\t\t\troles: list.map(toRoleDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync get(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tconst role = await roles.findById(roleId);\n\t\t\tif (!role) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Role not found');\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_FETCHED', 'Role retrieved successfully.', {\n\t\t\t\trole: toRoleDTO(role),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tconst opts: { name?: string; description?: string | null; active?: boolean } = {};\n\n\t\t\tif (typeof body?.['name'] === 'string') {\n\t\t\t\topts.name = body['name'];\n\t\t\t}\n\n\t\t\tif (typeof body?.['description'] === 'string') {\n\t\t\t\topts.description = body['description'];\n\t\t\t}\n\n\t\t\tif (body?.['description'] === null) {\n\t\t\t\topts.description = null;\n\t\t\t}\n\n\t\t\tif (typeof body?.['active'] === 'boolean') {\n\t\t\t\topts.active = body['active'];\n\t\t\t}\n\n\t\t\tconst role = await roles.update(roleId, opts);\n\t\t\tif (!role) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Role not found or is a system role');\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_UPDATED', 'Role updated successfully.', {\n\t\t\t\trole: toRoleDTO(role),\n\t\t\t});\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tawait roles.delete(roleId, ctx.workspace.id);\n\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_DELETED', 'Role deleted successfully.');\n\t\t},\n\n\t\tasync setPermissions(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tconst perms = body?.['permissions'];\n\t\t\tif (!Array.isArray(perms)) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t'permissions array is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst normalized = perms\n\t\t\t\t.map((p: unknown) => {\n\t\t\t\t\tconst perm = p as Record<string, unknown>;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpermissionKey: String(perm['permissionKey'] ?? ''),\n\t\t\t\t\t\tcanCreate: Boolean(perm['canCreate']),\n\t\t\t\t\t\tcanRead: Boolean(perm['canRead']),\n\t\t\t\t\t\tcanUpdate: Boolean(perm['canUpdate']),\n\t\t\t\t\t\tcanDelete: Boolean(perm['canDelete']),\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.filter((p) => p.permissionKey.length > 0);\n\n\t\t\tawait roles.setPermissions(roleId, ctx.workspace.id, normalized);\n\n\t\t\treturn setApiResponse(HTTP.OK, 'PERMISSIONS_SET', 'Role permissions updated successfully.');\n\t\t},\n\t};\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { ICourierMessage } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport type { EventBus } from '@fonderie/events';\nimport { NOTIFICATION_EVENT } from '@fonderie/events';\n\nimport { getPlanLimit } from '@fonderie/billing';\nimport { MESSAGE_KEYS } from '../config';\nimport { InvitationModel } from '../models/invitation.model';\nimport { toInvitationDTO } from '../dtos/workspace';\n\nexport function invitationController(store: IStoreAdapter, ttl: string, bus?: EventBus) {\n\tconst invitations = new InvitationModel(store);\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst list = await invitations.list(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'INVITATIONS_FETCHED', 'Invitations retrieved successfully.', {\n\t\t\t\tinvitations: list.map(toInvitationDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync invite(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\tif (ctx.workspace.isPersonal) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.FORBIDDEN,\n\t\t\t\t\t'FORBIDDEN',\n\t\t\t\t\t'Personal workspaces do not support invitations',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as unknown;\n\t\t\tconst entries = Array.isArray(body) ? body as Record<string, unknown>[] : [body as Record<string, unknown>];\n\n\t\t\tif (!entries.length) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'at least one invite is required');\n\t\t\t}\n\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (typeof entry?.['email'] !== 'string') {\n\t\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'email is required for every invite');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Seat limit — enforced automatically when billing module is registered.\n\t\t\t// getPlanLimit reads from ctx.meta['billing'] (fail-open when billing absent).\n\t\t\tconst seatLimit = getPlanLimit(ctx, 'seats');\n\t\t\tif (seatLimit !== null) {\n\t\t\t\tconst [countRow] = await store.query<{ count: string }>(\n\t\t\t\t\t`SELECT COUNT(*) AS count FROM fonderie_role_user_workspaces\n\t\t\t\t\t WHERE workspace_id = $1 AND removed = false AND suspended = false`,\n\t\t\t\t\t[ctx.workspace.id],\n\t\t\t\t);\n\t\t\t\tconst total = parseInt(countRow!.count, 10);\n\t\t\t\tconst occupied = ctx.workspace.isPersonal ? total : Math.max(0, total - 1);\n\t\t\t\tif (occupied + entries.length > seatLimit) {\n\t\t\t\t\treturn setApiResponse(\n\t\t\t\t\t\tHTTP.PAYMENT_REQUIRED,\n\t\t\t\t\t\t'SEAT_LIMIT_REACHED',\n\t\t\t\t\t\t`Your plan allows ${seatLimit} seat${seatLimit === 1 ? '' : 's'}. Upgrade to invite more members.`,\n\t\t\t\t\t\t{ limit: seatLimit },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Resolve default role once for entries that omit roleId.\n\t\t\t// Invitations must never default to a privileged role: fall back to\n\t\t\t// the seeded system GUEST role (least privilege). Granting anything\n\t\t\t// more requires an explicit roleId from GET /workspaces/roles.\n\t\t\tlet defaultRoleId: string | undefined;\n\t\t\tconst needsDefault = entries.some((e) => !e['roleId']);\n\t\t\tif (needsDefault) {\n\t\t\t\tconst [row] = await store.query<{ id: string }>(\n\t\t\t\t\t`SELECT id FROM fonderie_roles\n\t\t\t\t\t WHERE name = 'GUEST' AND workspace_id IS NULL AND is_system = true\n\t\t\t\t\t LIMIT 1`,\n\t\t\t\t);\n\t\t\t\tdefaultRoleId = row?.id;\n\t\t\t\tif (!defaultRoleId) {\n\t\t\t\t\treturn setApiResponse(HTTP.SERVER_ERROR, 'SERVER_ERROR', 'Default role not found');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst results = await Promise.all(\n\t\t\t\tentries.map(async (entry) => {\n\t\t\t\t\tconst email = entry['email'] as string;\n\t\t\t\t\tconst resolvedRoleId = (entry['roleId'] as string | undefined) ?? defaultRoleId!;\n\n\t\t\t\t\tconst invitation = await invitations.create({\n\t\t\t\t\t\tworkspaceId: ctx.workspace!.id,\n\t\t\t\t\t\temail,\n\t\t\t\t\t\troleId: resolvedRoleId,\n\t\t\t\t\t\tttl,\n\t\t\t\t\t});\n\n\t\t\t\t\tbus\n\t\t\t\t\t\t?.emit(NOTIFICATION_EVENT, {\n\t\t\t\t\t\t\ttype: MESSAGE_KEYS.workspaceInvitation,\n\t\t\t\t\t\t\trecipient: { email, phone: null, deviceToken: null },\n\t\t\t\t\t\t\tdata: { token: invitation.token, pin: invitation.pin },\n\t\t\t\t\t\t} satisfies ICourierMessage)\n\t\t\t\t\t\t.catch(() => {});\n\n\t\t\t\t\treturn { invitationId: invitation.id, email };\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'INVITATIONS_SENT', 'Invitations sent successfully.', {\n\t\t\t\tinvitations: results,\n\t\t\t});\n\t\t},\n\n\t\tasync cancel(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst invitationId = params?.['inviteId'];\n\n\t\t\tif (!invitationId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'inviteId is required');\n\n\t\t\tawait invitations.cancel(invitationId, ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'INVITATION_CANCELLED', 'Invitation cancelled successfully.');\n\t\t},\n\n\t\tasync accept(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst pin = body?.['pin'];\n\n\t\t\tif (typeof pin !== 'string') {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'pin is required');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { workspaceId } = await invitations.acceptByPin({ pin, userId: ctx.user!.id });\n\t\t\t\treturn setApiResponse(HTTP.OK, 'INVITATION_ACCEPTED', 'Invitation accepted successfully.', {\n\t\t\t\t\tworkspaceId,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : 'Invalid invitation';\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'INVITATION_FAILED', message);\n\t\t\t}\n\t\t},\n\t};\n}\n","import { randomBytes } from 'node:crypto';\n\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IInvitation } from '../types';\n\nfunction generateToken(): string {\n\treturn randomBytes(32).toString('hex');\n}\n\nfunction generatePin(): string {\n\treturn Math.floor(100000 + Math.random() * 900000).toString();\n}\n\nfunction parseTtl(ttl: string): number {\n\tconst units: Record<string, number> = {\n\t\ts: 1000,\n\t\tm: 60_000,\n\t\th: 3_600_000,\n\t\td: 86_400_000,\n\t};\n\tconst match = ttl.match(/^(\\d+)([smhd])$/);\n\tif (!match) return 7 * 86_400_000;\n\tconst [, n, unit] = match;\n\treturn parseInt(n!, 10) * (units[unit!] ?? 0);\n}\n\nconst SELECT_INV = `\n\tid,\n\tworkspace_id AS \"workspaceId\",\n\temail,\n\trole_id AS \"roleId\",\n\ttoken,\n\tpin,\n\tstatus,\n\texpires_at AS \"expiresAt\",\n\tcreated_at AS \"createdAt\"\n`;\n\nexport async function createInvitation(\n\topts: { workspaceId: string; email: string; roleId: string; ttl?: string },\n\tstore: IStoreAdapter,\n): Promise<IInvitation> {\n\tconst token = generateToken();\n\tconst pin = generatePin();\n\tconst expiresAt = new Date(Date.now() + parseTtl(opts.ttl ?? '7d'));\n\n\tconst [invitation] = await store.query<IInvitation>(\n\t\t`INSERT INTO fonderie_workspace_invitations\n\t\t (workspace_id, email, role_id, token, pin, expires_at)\n\t\t VALUES ($1, $2, $3, $4, $5, $6)\n\t\t ON CONFLICT DO NOTHING\n\t\t RETURNING ${SELECT_INV}`,\n\t\t[opts.workspaceId, opts.email, opts.roleId, token, pin, expiresAt],\n\t);\n\n\tif (!invitation) {\n\t\t// Already pending — update it\n\t\tconst [updated] = await store.query<IInvitation>(\n\t\t\t`UPDATE fonderie_workspace_invitations\n\t\t\t SET token = $4, pin = $5, expires_at = $6, role_id = $3, status = 'PENDING'\n\t\t\t WHERE workspace_id = $1 AND email = $2 AND status = 'PENDING'\n\t\t\t RETURNING ${SELECT_INV}`,\n\t\t\t[opts.workspaceId, opts.email, opts.roleId, token, pin, expiresAt],\n\t\t);\n\t\tif (!updated) throw new Error('Failed to create invitation');\n\t\treturn updated;\n\t}\n\n\treturn invitation;\n}\n\nexport async function listInvitations(\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IInvitation[]> {\n\treturn store.query<IInvitation>(\n\t\t`SELECT ${SELECT_INV}\n\t\t FROM fonderie_workspace_invitations\n\t\t WHERE workspace_id = $1 AND status = 'PENDING'\n\t\t ORDER BY created_at DESC`,\n\t\t[workspaceId],\n\t);\n}\n\nexport async function cancelInvitation(\n\tinvitationId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspace_invitations\n\t\t SET status = 'CANCELLED'\n\t\t WHERE id = $1 AND workspace_id = $2 AND status = 'PENDING'`,\n\t\t[invitationId, workspaceId],\n\t);\n}\n\nexport async function acceptInvitationByPin(\n\topts: { pin: string; userId: string },\n\tstore: IStoreAdapter,\n): Promise<{ workspaceId: string; roleId: string }> {\n\tconst [inv] = await store.query<{\n\t\tid: string;\n\t\tworkspaceId: string;\n\t\troleId: string;\n\t\texpiresAt: string;\n\t}>(\n\t\t`SELECT id, workspace_id AS \"workspaceId\", role_id AS \"roleId\", expires_at AS \"expiresAt\"\n\t\t FROM fonderie_workspace_invitations\n\t\t WHERE pin = $1 AND status = 'PENDING'`,\n\t\t[opts.pin],\n\t);\n\n\tif (!inv) throw new Error('Invalid PIN');\n\tif (new Date() > new Date(inv.expiresAt)) throw new Error('Invitation expired');\n\n\tawait store.transaction(async (tx) => {\n\t\tawait Promise.all([\n\t\t\ttx.query(\n\t\t\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t\t\t VALUES ($1, $2, $3, true)\n\t\t\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t\t\t SET confirmed = true, removed = false`,\n\t\t\t\t[opts.userId, inv.workspaceId, inv.roleId],\n\t\t\t),\n\t\t\ttx.query(`UPDATE fonderie_workspace_invitations SET status = 'ACCEPTED' WHERE id = $1`, [\n\t\t\t\tinv.id,\n\t\t\t]),\n\t\t]);\n\t});\n\n\treturn { workspaceId: inv.workspaceId, roleId: inv.roleId };\n}\n\nexport async function acceptInvitationByToken(\n\ttoken: string,\n\tuserId: string,\n\tstore: IStoreAdapter,\n): Promise<{ workspaceId: string; roleId: string }> {\n\tconst [inv] = await store.query<{\n\t\tid: string;\n\t\tworkspaceId: string;\n\t\troleId: string;\n\t\texpiresAt: string;\n\t}>(\n\t\t`SELECT id, workspace_id AS \"workspaceId\", role_id AS \"roleId\", expires_at AS \"expiresAt\"\n\t\t FROM fonderie_workspace_invitations\n\t\t WHERE token = $1 AND status = 'PENDING'`,\n\t\t[token],\n\t);\n\n\tif (!inv) throw new Error('Invalid token');\n\tif (new Date() > new Date(inv.expiresAt)) throw new Error('Invitation expired');\n\n\tawait store.transaction(async (tx) => {\n\t\tawait Promise.all([\n\t\t\ttx.query(\n\t\t\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t\t\t VALUES ($1, $2, $3, true)\n\t\t\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t\t\t SET confirmed = true, removed = false`,\n\t\t\t\t[userId, inv.workspaceId, inv.roleId],\n\t\t\t),\n\t\t\ttx.query(`UPDATE fonderie_workspace_invitations SET status = 'ACCEPTED' WHERE id = $1`, [\n\t\t\t\tinv.id,\n\t\t\t]),\n\t\t]);\n\t});\n\n\treturn { workspaceId: inv.workspaceId, roleId: inv.roleId };\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IInvitation } from '../types';\nimport {\n\tcreateInvitation,\n\tlistInvitations,\n\tcancelInvitation,\n\tacceptInvitationByPin,\n} from '../services/invitations';\n\nexport class InvitationModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tcreate(opts: Parameters<typeof createInvitation>[0]): Promise<IInvitation> {\n\t\treturn createInvitation(opts, this.store);\n\t}\n\n\tlist(workspaceId: string): Promise<IInvitation[]> {\n\t\treturn listInvitations(workspaceId, this.store);\n\t}\n\n\tcancel(invitationId: string, workspaceId: string): Promise<void> {\n\t\treturn cancelInvitation(invitationId, workspaceId, this.store);\n\t}\n\n\tacceptByPin(\n\t\topts: Parameters<typeof acceptInvitationByPin>[0],\n\t): Promise<{ workspaceId: string; roleId: string }> {\n\t\treturn acceptInvitationByPin(opts, this.store);\n\t}\n}\n","import type { IFonderieModule, IFonderieApp } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport type { EventBus } from '@fonderie/events';\n\nimport type { IWorkspacesConfig } from './config';\nimport { EVENT_KEYS } from './config';\nimport { buildWorkspaceRoutes } from './routes';\nimport { WorkspaceModel } from './models/workspace.model';\nimport { RoleModel } from './models/role.model';\nimport { MemberModel } from './models/member.model';\n\n// Mirror of @fonderie/auth EVENT_KEYS.userRegistered — avoids a runtime\n// dependency on the auth package while remaining explicit about the contract.\nconst AUTH_USER_REGISTERED = 'fonderie.user.registered' as const;\n\ninterface UserRegisteredPayload {\n\tuserId: string;\n}\n\nexport class WorkspacesModule implements IFonderieModule {\n\treadonly name = '@fonderie/workspaces';\n\treadonly deps = ['@fonderie/auth', '@fonderie/billing'];\n\n\tconstructor(\n\t\tprivate store: IStoreAdapter,\n\t\tprivate config: IWorkspacesConfig = {},\n\t\tprivate bus?: EventBus,\n\t) {}\n\n\tinstall(app: IFonderieApp): void {\n\t\tif (this.bus && this.config.personalWorkspace !== false) {\n\t\t\tthis.bus.on<UserRegisteredPayload>(\n\t\t\t\tAUTH_USER_REGISTERED,\n\t\t\t\t(payload) => this.provisionPersonalWorkspace(payload),\n\t\t\t\t'workspaces',\n\t\t\t);\n\t\t}\n\n\t\tconst routes = buildWorkspaceRoutes(this.store, this.config, this.bus);\n\t\tfor (const [method, path, ...handlers] of routes) {\n\t\t\tapp.addRoute(method, path, ...handlers);\n\t\t}\n\t}\n\n\tprivate async provisionPersonalWorkspace(payload: UserRegisteredPayload): Promise<void> {\n\t\tconst name = 'My Workspace';\n\t\tconst slug = `${payload.userId}-personal`;\n\n\t\tlet workspaceId: string | undefined;\n\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tconst wsModel = new WorkspaceModel(tx);\n\t\t\tconst roleModel = new RoleModel(tx);\n\t\t\tconst memModel = new MemberModel(tx);\n\n\t\t\tconst ws = await wsModel.createPersonal({ name, slug, ownerId: payload.userId });\n\t\t\tif (!ws) return; // already exists — idempotent replay\n\n\t\t\tworkspaceId = ws.id;\n\n\t\t\tconst adminRole = await roleModel.findSystem('ADMIN');\n\t\t\tif (!adminRole) throw new Error('System ADMIN role not found');\n\n\t\t\tawait memModel.add({\n\t\t\t\tuserId: payload.userId,\n\t\t\t\tworkspaceId: ws.id,\n\t\t\t\troleId: adminRole.id,\n\t\t\t\tconfirmed: true,\n\t\t\t});\n\t\t});\n\n\t\tif (workspaceId) {\n\t\t\tthis.bus\n\t\t\t\t?.emit(EVENT_KEYS.personalWorkspaceCreated, {\n\t\t\t\t\tworkspaceId,\n\t\t\t\t\tuserId: payload.userId,\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\t\t}\n\t}\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { Middleware } from '@fonderie/core';\n\n// Ensures ctx.workspace is set — use after withWorkspace\n\nexport const requireWorkspace: Middleware = async (ctx, next) => {\n\tif (!ctx.workspace) {\n\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'WORKSPACE_REQUIRED', 'Workspace context required');\n\t}\n\treturn next();\n};\n"],"mappings":"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","names":["import_core","name","name","name","import_core","import_core","name","import_core","email","name","import_core"]}
|
package/dist/index.js
CHANGED
|
@@ -1205,8 +1205,8 @@ function invitationController(store, ttl, bus) {
|
|
|
1205
1205
|
if (needsDefault) {
|
|
1206
1206
|
const [row] = await store.query(
|
|
1207
1207
|
`SELECT id FROM fonderie_roles
|
|
1208
|
-
WHERE name = '
|
|
1209
|
-
|
|
1208
|
+
WHERE name = 'GUEST' AND workspace_id IS NULL AND is_system = true
|
|
1209
|
+
LIMIT 1`
|
|
1210
1210
|
);
|
|
1211
1211
|
defaultRoleId = row?.id;
|
|
1212
1212
|
if (!defaultRoleId) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config.ts","../src/routes.ts","../src/schemas.ts","../src/middlewares/workspace-context.ts","../src/services/members.ts","../src/services/workspaces.ts","../src/controllers/workspace.controller.ts","../src/models/workspace.model.ts","../src/models/member.model.ts","../src/services/roles.ts","../src/models/role.model.ts","../src/dtos/workspace.ts","../src/controllers/member.controller.ts","../src/controllers/role.controller.ts","../src/controllers/invitation.controller.ts","../src/services/invitations.ts","../src/models/invitation.model.ts","../src/module.ts","../src/middlewares/require-workspace.ts"],"sourcesContent":["export const MESSAGE_KEYS = {\n\tworkspaceInvitation: 'workspace-invitation',\n} as const;\n\nexport type WorkspacesMessageKey = (typeof MESSAGE_KEYS)[keyof typeof MESSAGE_KEYS];\n\nexport const EVENT_KEYS = {\n\tpersonalWorkspaceCreated: 'fonderie.workspace.personal.created',\n} as const;\n\nexport type WorkspacesEventKey = (typeof EVENT_KEYS)[keyof typeof EVENT_KEYS];\n\nexport interface IWorkspacesConfig {\n\t// How long invitations are valid. Default: '7d'\n\tinvitationTtl?: string;\n\n\t// Default role assigned to invited members. Default: 'member'\n\tdefaultRole?: string;\n\n\t// Auto-create a personal workspace when user.registered fires.\n\t// Requires an EventBus to be passed to WorkspacesModule. Default: true\n\tpersonalWorkspace?: boolean;\n}\n","import type { IStoreAdapter } from '@fonderie/store';\nimport type { Middleware } from '@fonderie/core';\nimport type { EventBus } from '@fonderie/events';\nimport { requireAuth, validate } from '@fonderie/core/middlewares';\n\nimport {\n\tcreateRoleSchema,\n\tupdateRoleSchema,\n\taddMemberRoleSchema,\n\tupdateSettingsSchema,\n\tcreateWorkspaceSchema,\n\tupdateWorkspaceSchema,\n\tacceptInvitationSchema,\n\tcreateInvitationsSchema,\n\tsetRolePermissionsSchema,\n} from './schemas';\n\nimport type { IWorkspacesConfig } from './config';\nimport { withWorkspace } from './middlewares/workspace-context';\n\nimport { workspaceController } from './controllers/workspace.controller';\nimport { memberController } from './controllers/member.controller';\nimport { roleController } from './controllers/role.controller';\nimport { invitationController } from './controllers/invitation.controller';\n\ntype RouteDefinition = [string, string, ...Middleware[]];\n\nexport function buildWorkspaceRoutes(\n\tstore: IStoreAdapter,\n\tconfig: IWorkspacesConfig,\n\tbus?: EventBus,\n): RouteDefinition[] {\n\tconst ttl = config.invitationTtl ?? '7d';\n\tconst wsCtx = withWorkspace(store);\n\n\tconst workspace = workspaceController(store, config);\n\tconst member = memberController(store);\n\tconst role = roleController(store);\n\tconst invitation = invitationController(store, ttl, bus);\n\n\treturn [\n\t\t// ── Workspace creation + listing (no workspace context required)\n\t\t['POST', '/workspaces', requireAuth, validate(createWorkspaceSchema), workspace.create],\n\t\t['GET', '/workspaces', requireAuth, workspace.list],\n\n\t\t// ── Members (workspace resolved from X-Workspace-ID header)\n\t\t['GET', '/workspaces/members', requireAuth, wsCtx, member.list],\n\t\t['DELETE', '/workspaces/members/:userId', requireAuth, wsCtx, member.remove],\n\t\t['GET', '/workspaces/members/:userId/roles', requireAuth, wsCtx, member.getUserRoles],\n\t\t['POST', '/workspaces/members/:userId/roles', requireAuth, wsCtx, validate(addMemberRoleSchema), member.addRole],\n\t\t['DELETE', '/workspaces/members/:userId/roles/:roleId', requireAuth, wsCtx, member.removeRole],\n\n\t\t// ── Invitations\n\t\t['GET', '/workspaces/invitations', requireAuth, wsCtx, invitation.list],\n\t\t['POST', '/workspaces/invitations', requireAuth, wsCtx, validate(createInvitationsSchema), invitation.invite],\n\t\t['DELETE', '/workspaces/invitations/:inviteId', requireAuth, wsCtx, invitation.cancel],\n\t\t['POST', '/workspaces/invitations/accept', requireAuth, validate(acceptInvitationSchema), invitation.accept],\n\n\t\t// ── Roles\n\t\t['POST', '/workspaces/roles', requireAuth, wsCtx, validate(createRoleSchema), role.create],\n\t\t['GET', '/workspaces/roles', requireAuth, wsCtx, role.list],\n\t\t['GET', '/workspaces/roles/:roleId', requireAuth, wsCtx, role.get],\n\t\t['PUT', '/workspaces/roles/:roleId', requireAuth, wsCtx, validate(updateRoleSchema), role.update],\n\t\t['DELETE', '/workspaces/roles/:roleId', requireAuth, wsCtx, role.remove],\n\t\t['POST', '/workspaces/roles/:roleId/permissions', requireAuth, wsCtx, validate(setRolePermissionsSchema), role.setPermissions],\n\n\t\t// ── Workspace lifecycle\n\t\t['POST', '/workspaces/archive', requireAuth, wsCtx, workspace.archive],\n\t\t['POST', '/workspaces/restore', requireAuth, wsCtx, workspace.restore],\n\t\t['GET', '/workspaces/settings', requireAuth, wsCtx, workspace.getSettings],\n\t\t['PUT', '/workspaces/settings', requireAuth, wsCtx, validate(updateSettingsSchema), workspace.updateSettings],\n\n\t\t// ── Path-based lookup by ID (admin / cross-workspace use)\n\t\t['GET', '/workspaces/:id', requireAuth, wsCtx, workspace.get],\n\n\t\t// ── Update current workspace — ID resolved from X-Workspace-ID header\n\t\t// (or personal workspace fallback when header is absent)\n\t\t['PUT', '/workspaces', requireAuth, wsCtx, validate(updateWorkspaceSchema), workspace.update],\n\t];\n}\n","import { z } from 'zod';\n\n// Request schemas — the validation contract for every body-taking workspaces\n// route. Wired into routes.ts via @fonderie/core's validate(); exported so\n// docs generators and typed clients read the source of truth the runtime\n// enforces. Same pattern as @fonderie/auth/src/schemas.ts.\n\nconst name = z.string().trim().min(1, 'name is required').max(200);\nconst email = z.string().trim().pipe(z.email());\n\nexport const createWorkspaceSchema = z.object({\n\tname,\n\tdescription: z.string().max(2000).optional(),\n\t// PERSONAL workspaces are created automatically by the module; the\n\t// controller enforces the rule — schema just types the field.\n\ttype: z.string().max(40).optional(),\n});\n\nconst addressSchema = z\n\t.object({\n\t\tline1: z.string().max(200).optional(),\n\t\tline2: z.string().max(200).optional(),\n\t\tcity: z.string().max(100).optional(),\n\t\tregion: z.string().max(100).optional(),\n\t\tpostalCode: z.string().max(20).optional(),\n\t\tcountry: z.string().max(60).optional(),\n\t})\n\t.passthrough();\n\nexport const updateWorkspaceSchema = z\n\t.object({\n\t\tname: z.string().trim().min(1).max(200).optional(),\n\t\tdescription: z.string().max(2000).nullable().optional(),\n\t\tmotto: z.string().max(300).nullable().optional(),\n\t\tphone: z.string().max(32).nullable().optional(),\n\t\tbusinessType: z.string().max(100).nullable().optional(),\n\t\taddress: addressSchema.nullable().optional(),\n\t})\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\n\nexport const updateSettingsSchema = z\n\t.object({\n\t\tlocale: z.string().max(35).optional(),\n\t\ttimezone: z.string().max(64).optional(),\n\t\tcurrency: z.string().max(8).optional(),\n\t\tdateFormat: z.string().max(32).optional(),\n\t\ttimeFormat: z.string().max(32).optional(),\n\t})\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'No settings provided');\n\nexport const addMemberRoleSchema = z.object({ roleId: z.string().min(1, 'roleId is required') });\n\nconst inviteEntry = z.object({\n\temail,\n\troleId: z.string().min(1).optional(),\n});\n\n// POST /workspaces/invitations accepts a single invite or a batch.\nexport const createInvitationsSchema = z.union([\n\tinviteEntry,\n\tz.array(inviteEntry).min(1, 'at least one invite is required'),\n]);\n\nexport const acceptInvitationSchema = z.object({ pin: z.string().trim().min(1, 'pin is required') });\n\nexport const createRoleSchema = z.object({\n\tname,\n\tdescription: z.string().max(1000).optional(),\n});\n\nexport const updateRoleSchema = z\n\t.object({\n\t\tname: z.string().trim().min(1).max(200).optional(),\n\t\tdescription: z.string().max(1000).nullable().optional(),\n\t\tactive: z.boolean().optional(),\n\t})\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\n\nexport const setRolePermissionsSchema = z.object({\n\tpermissions: z.array(\n\t\tz.object({\n\t\t\tpermissionKey: z.string().min(1),\n\t\t\tcanCreate: z.boolean().optional(),\n\t\t\tcanRead: z.boolean().optional(),\n\t\t\tcanUpdate: z.boolean().optional(),\n\t\t\tcanDelete: z.boolean().optional(),\n\t\t}),\n\t),\n});\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { Middleware } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { getMember } from '../services/members';\nimport { findWorkspaceById, findPersonalWorkspace } from '../services/workspaces';\n\n// Resolves ctx.workspace from:\n// 1. Route param :workspaceId or :id (path-based admin routes)\n// 2. X-Workspace-ID request header (standard resource routes)\n// Validates the current user is an active member.\n// Must run after withSession.\n\nfunction makeHandler(store: IStoreAdapter): Middleware {\n\treturn async (ctx, next) => {\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst workspaceId =\n\t\t\tparams?.['workspaceId'] ??\n\t\t\tparams?.['id'] ??\n\t\t\tctx.request.headers.get('x-workspace-id') ??\n\t\t\tundefined;\n\n\t\tif (!workspaceId) {\n\t\t\t// DMZ fallback — if the caller didn't specify a workspace, use their personal one.\n\t\t\t// No membership check needed: the user is always the sole owner.\n\t\t\tif (ctx.user) {\n\t\t\t\tconst personal = await findPersonalWorkspace(ctx.user.id, store);\n\t\t\t\tif (personal) Object.assign(ctx, { workspace: personal });\n\t\t\t}\n\t\t\treturn next();\n\t\t}\n\n\t\tconst workspace = await findWorkspaceById(workspaceId, store);\n\t\tif (!workspace) {\n\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t}\n\n\t\tif (ctx.user) {\n\t\t\tconst member = await getMember(ctx.user.id, workspaceId, store);\n\t\t\tif (!member) {\n\t\t\t\treturn setApiResponse(HTTP.FORBIDDEN, 'FORBIDDEN', 'Not a member of this workspace');\n\t\t\t}\n\t\t}\n\n\t\tObject.assign(ctx, { workspace });\n\t\treturn next();\n\t};\n}\n\nexport function withWorkspace(store: IStoreAdapter): Middleware;\nexport function withWorkspace(\n\tstore: IStoreAdapter,\n\tctx: IFonderieContext,\n\tnext: () => Promise<Response>,\n): Promise<Response>;\nexport function withWorkspace(\n\tstore: IStoreAdapter,\n\tctx?: IFonderieContext,\n\tnext?: () => Promise<Response>,\n): Middleware | Promise<Response> {\n\tconst handler = makeHandler(store);\n\tif (ctx !== undefined && next !== undefined) return handler(ctx, next);\n\treturn handler;\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IMember, IRole } from '../types';\n\nconst SELECT_MEMBER = `\n\truw.user_id AS \"userId\",\n\truw.workspace_id AS \"workspaceId\",\n\truw.role_id AS \"roleId\",\n\tr.name AS \"roleName\",\n\truw.confirmed AS \"confirmed\",\n\truw.created_at AS \"createdAt\",\n\tu.first_name AS \"firstName\",\n\tu.last_name AS \"lastName\",\n\tu.email AS \"email\",\n\tu.profile_image_url AS \"profileImageUrl\"\n`;\n\nexport async function getMember(\n\tuserId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IMember | null> {\n\tconst [row] = await store.query<IMember>(\n\t\t`SELECT ${SELECT_MEMBER}\n\t\t FROM fonderie_role_user_workspaces ruw\n\t\t LEFT JOIN fonderie_roles r ON r.id = ruw.role_id\n\t\t LEFT JOIN fonderie_users u ON u.id = ruw.user_id\n\t\t WHERE ruw.user_id = $1\n\t\t AND ruw.workspace_id = $2\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false\n\t\t LIMIT 1`,\n\t\t[userId, workspaceId],\n\t);\n\treturn row ?? null;\n}\n\nexport async function listMembers(workspaceId: string, store: IStoreAdapter): Promise<IMember[]> {\n\treturn store.query<IMember>(\n\t\t`SELECT ${SELECT_MEMBER}\n\t\t FROM fonderie_role_user_workspaces ruw\n\t\t LEFT JOIN fonderie_roles r ON r.id = ruw.role_id\n\t\t LEFT JOIN fonderie_users u ON u.id = ruw.user_id\n\t\t WHERE ruw.workspace_id = $1\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false\n\t\t ORDER BY ruw.created_at ASC`,\n\t\t[workspaceId],\n\t);\n}\n\nexport async function addMember(\n\topts: { userId: string; workspaceId: string; roleId: string; confirmed?: boolean },\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t VALUES ($1, $2, $3, $4)\n\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t SET confirmed = $4, removed = false, suspended = false`,\n\t\t[opts.userId, opts.workspaceId, opts.roleId, opts.confirmed ?? true],\n\t);\n}\n\nexport async function removeMember(\n\tuserId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_role_user_workspaces\n\t\t SET removed = true\n\t\t WHERE user_id = $1\n\t\t AND workspace_id = $2`,\n\t\t[userId, workspaceId],\n\t);\n}\n\nexport async function getUserRoles(\n\tuserId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IRole[]> {\n\treturn store.query<IRole>(\n\t\t`SELECT\n\t\t r.id,\n\t\t r.name,\n\t\t r.is_system AS \"isSystem\",\n\t\t r.active,\n\t\t r.description,\n\t\t r.workspace_id AS \"workspaceId\"\n\t\t FROM fonderie_role_user_workspaces ruw\n\t\t JOIN fonderie_roles r ON r.id = ruw.role_id\n\t\t WHERE ruw.user_id = $1\n\t\t AND ruw.workspace_id = $2\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false`,\n\t\t[userId, workspaceId],\n\t);\n}\n\nexport async function addRoleToMember(\n\tuserId: string,\n\tworkspaceId: string,\n\troleId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t VALUES ($1, $2, $3, true)\n\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t SET confirmed = true, removed = false, suspended = false`,\n\t\t[userId, workspaceId, roleId],\n\t);\n}\n\nexport async function removeRoleFromMember(\n\tuserId: string,\n\tworkspaceId: string,\n\troleId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tconst remaining = await store.query<{ count: string }>(\n\t\t`SELECT COUNT(*) AS count\n\t\t FROM fonderie_role_user_workspaces\n\t\t WHERE user_id = $1\n\t\t AND workspace_id = $2\n\t\t AND removed = false`,\n\t\t[userId, workspaceId],\n\t);\n\tconst count = parseInt(remaining[0]?.count ?? '0', 10);\n\tif (count <= 1) throw new Error('Cannot remove last role from member');\n\n\tawait store.query(\n\t\t`DELETE FROM fonderie_role_user_workspaces\n\t\t WHERE user_id = $1\n\t\t AND workspace_id = $2\n\t\t AND role_id = $3`,\n\t\t[userId, workspaceId, roleId],\n\t);\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IWorkspace, IWorkspaceAddress, IWorkspaceSettings } from '../types';\n\nconst SELECT_WS = `\n\tid,\n\tname,\n\tslug,\n\ttype,\n\tdescription,\n\tmotto,\n\tphone,\n\tbusiness_type AS \"businessType\",\n\taddress,\n\tplan,\n\towner_id AS \"ownerId\",\n\tis_personal AS \"isPersonal\",\n\tarchived_at AS \"archivedAt\",\n\tarchived_by AS \"archivedBy\",\n\tcreated_at AS \"createdAt\",\n\tupdated_at AS \"updatedAt\"\n`;\n\nconst SELECT_WS_W = `\n\tw.id,\n\tw.name,\n\tw.slug,\n\tw.type,\n\tw.description,\n\tw.motto,\n\tw.phone,\n\tw.business_type AS \"businessType\",\n\tw.address,\n\tw.plan,\n\tw.owner_id AS \"ownerId\",\n\tw.is_personal AS \"isPersonal\",\n\tw.archived_at AS \"archivedAt\",\n\tw.archived_by AS \"archivedBy\",\n\tw.created_at AS \"createdAt\",\n\tw.updated_at AS \"updatedAt\"\n`;\n\nexport async function findWorkspaceById(\n\tid: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst [row] = await store.query<IWorkspace>(\n\t\t`SELECT ${SELECT_WS} FROM fonderie_workspaces WHERE id = $1`,\n\t\t[id],\n\t);\n\treturn row ?? null;\n}\n\nexport async function findWorkspacesByUserId(\n\tuserId: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspace[]> {\n\treturn store.query<IWorkspace>(\n\t\t`SELECT ${SELECT_WS_W}\n\t\t FROM fonderie_workspaces w\n\t\t JOIN fonderie_role_user_workspaces ruw ON ruw.workspace_id = w.id\n\t\t WHERE ruw.user_id = $1\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false\n\t\t GROUP BY w.id\n\t\t ORDER BY w.created_at ASC`,\n\t\t[userId],\n\t);\n}\n\nexport async function createWorkspace(\n\topts: {\n\t\tname: string;\n\t\tslug: string;\n\t\townerId: string;\n\t\ttype?: string;\n\t\tdescription?: string;\n\t\tplan?: string;\n\t},\n\tstore: IStoreAdapter,\n): Promise<IWorkspace> {\n\tconst [workspace] = await store.query<IWorkspace>(\n\t\t`INSERT INTO fonderie_workspaces (name, slug, owner_id, type, description, plan)\n\t\t VALUES ($1, $2, $3, $4, $5, $6)\n\t\t RETURNING ${SELECT_WS}`,\n\t\t[\n\t\t\topts.name,\n\t\t\topts.slug,\n\t\t\topts.ownerId,\n\t\t\topts.type ?? 'ORGANIZATION',\n\t\t\topts.description ?? null,\n\t\t\topts.plan ?? 'free',\n\t\t],\n\t);\n\n\tif (!workspace) throw new Error('Failed to create workspace');\n\treturn workspace;\n}\n\n// Returns the new workspace, or null if the personal workspace already exists (idempotent).\nexport async function createPersonalWorkspace(\n\topts: { name: string; slug: string; ownerId: string },\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst [workspace] = await store.query<IWorkspace>(\n\t\t`INSERT INTO fonderie_workspaces (name, slug, owner_id, type, is_personal)\n\t\t VALUES ($1, $2, $3, 'PERSONAL', true)\n\t\t ON CONFLICT (owner_id) WHERE is_personal = true DO NOTHING\n\t\t RETURNING ${SELECT_WS}`,\n\t\t[opts.name, opts.slug, opts.ownerId],\n\t);\n\treturn workspace ?? null;\n}\n\nexport async function findPersonalWorkspace(\n\tuserId: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst [row] = await store.query<IWorkspace>(\n\t\t`SELECT ${SELECT_WS} FROM fonderie_workspaces\n\t\t WHERE owner_id = $1 AND is_personal = true\n\t\t LIMIT 1`,\n\t\t[userId],\n\t);\n\treturn row ?? null;\n}\n\nexport async function updateWorkspace(\n\tid: string,\n\topts: {\n\t\tname?: string;\n\t\tdescription?: string | null;\n\t\tslug?: string;\n\t\tmotto?: string | null;\n\t\tphone?: string | null;\n\t\tbusinessType?: string | null;\n\t\taddress?: IWorkspaceAddress | null;\n\t},\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst sets: string[] = ['updated_at = now()'];\n\tconst params: unknown[] = [id];\n\n\tif (opts.name !== undefined) {\n\t\tparams.push(opts.name);\n\t\tsets.push(`name = $${params.length}`);\n\t}\n\tif (opts.description !== undefined) {\n\t\tparams.push(opts.description);\n\t\tsets.push(`description = $${params.length}`);\n\t}\n\tif (opts.slug !== undefined) {\n\t\tparams.push(opts.slug);\n\t\tsets.push(`slug = $${params.length}`);\n\t}\n\tif (opts.motto !== undefined) {\n\t\tparams.push(opts.motto);\n\t\tsets.push(`motto = $${params.length}`);\n\t}\n\tif (opts.phone !== undefined) {\n\t\tparams.push(opts.phone);\n\t\tsets.push(`phone = $${params.length}`);\n\t}\n\tif (opts.businessType !== undefined) {\n\t\tparams.push(opts.businessType);\n\t\tsets.push(`business_type = $${params.length}`);\n\t}\n\tif (opts.address !== undefined) {\n\t\tparams.push(JSON.stringify(opts.address ?? {}));\n\t\tsets.push(`address = $${params.length}::jsonb`);\n\t}\n\n\tconst [row] = await store.query<IWorkspace>(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET ${sets.join(', ')}\n\t\t WHERE id = $1\n\t\t RETURNING ${SELECT_WS}`,\n\t\tparams,\n\t);\n\treturn row ?? null;\n}\n\nexport async function archiveWorkspace(\n\tid: string,\n\tbyUser: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET archived_at = now(), archived_by = $2, updated_at = now()\n\t\t WHERE id = $1 AND archived_at IS NULL`,\n\t\t[id, byUser],\n\t);\n}\n\nexport async function restoreWorkspace(id: string, store: IStoreAdapter): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET archived_at = NULL, archived_by = NULL, updated_at = now()\n\t\t WHERE id = $1`,\n\t\t[id],\n\t);\n}\n\nconst SETTINGS_DEFAULTS: IWorkspaceSettings = {\n\tlocale: 'en-US',\n\ttimezone: 'UTC',\n\tcurrency: 'USD',\n\tdateFormat: 'MM/DD/YYYY',\n\ttimeFormat: 'hh:mm A',\n};\n\nexport async function getWorkspaceSettings(\n\tid: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspaceSettings> {\n\tconst [row] = await store.query<{ settings: Record<string, unknown> }>(\n\t\t`SELECT settings FROM fonderie_workspaces WHERE id = $1`,\n\t\t[id],\n\t);\n\tconst raw = row?.settings ?? {};\n\tconst s = (raw['settings'] as Record<string, unknown> | undefined) ?? raw;\n\n\treturn {\n\t\tlocale: typeof s['locale'] === 'string' ? s['locale'] : SETTINGS_DEFAULTS.locale,\n\t\ttimezone: typeof s['timezone'] === 'string' ? s['timezone'] : SETTINGS_DEFAULTS.timezone,\n\t\tcurrency: typeof s['currency'] === 'string' ? s['currency'] : SETTINGS_DEFAULTS.currency,\n\t\tdateFormat:\n\t\t\ttypeof s['dateFormat'] === 'string' ? s['dateFormat'] : SETTINGS_DEFAULTS.dateFormat,\n\t\ttimeFormat:\n\t\t\ttypeof s['timeFormat'] === 'string' ? s['timeFormat'] : SETTINGS_DEFAULTS.timeFormat,\n\t};\n}\n\nexport async function updateWorkspaceSettings(\n\tid: string,\n\tsettings: Partial<IWorkspaceSettings>,\n\tstore: IStoreAdapter,\n): Promise<IWorkspaceSettings> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET settings = settings || jsonb_build_object('settings', $2::jsonb),\n\t\t updated_at = now()\n\t\t WHERE id = $1`,\n\t\t[id, JSON.stringify(settings)],\n\t);\n\treturn getWorkspaceSettings(id, store);\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IWorkspacesConfig } from '../config';\nimport type { IWorkspace } from '../types';\nimport { WorkspaceModel } from '../models/workspace.model';\nimport { MemberModel } from '../models/member.model';\nimport { RoleModel } from '../models/role.model';\nimport { toWorkspaceDTO, toSettingsDTO } from '../dtos/workspace';\n\nexport function workspaceController(store: IStoreAdapter, config: IWorkspacesConfig) {\n\tconst workspaces = new WorkspaceModel(store);\n\tconst members = new MemberModel(store);\n\tconst roles = new RoleModel(store);\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.user)\n\t\t\t\treturn setApiResponse(HTTP.UNAUTHORIZED, 'UNAUTHORIZED', 'Authentication required');\n\t\t\tconst list = await workspaces.findByUserId(ctx.user.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACES_FETCHED', 'Workspaces retrieved successfully.', {\n\t\t\t\tworkspaces: list.map(toWorkspaceDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync create(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst name = body?.['name'];\n\t\t\tconst description = body?.['description'];\n\t\t\tconst type = body?.['type'];\n\n\t\t\tif (typeof name !== 'string' || name.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'name is required');\n\t\t\t}\n\n\t\t\tif (type === 'PERSONAL') {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t'Personal workspaces are created automatically',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst slug = name\n\t\t\t\t.toLowerCase()\n\t\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t\t.replace(/^-|-$/g, '');\n\n\t\t\tconst workspace = await store.transaction(async (tx) => {\n\t\t\t\tconst wsOpts: Parameters<typeof workspaces.create>[0] = {\n\t\t\t\t\tname: name.trim(),\n\t\t\t\t\tslug,\n\t\t\t\t\townerId: ctx.user!.id,\n\t\t\t\t\ttype: typeof type === 'string' ? type : 'ORGANIZATION',\n\t\t\t\t};\n\t\t\t\tif (typeof description === 'string') wsOpts.description = description;\n\n\t\t\t\tconst wsModel = new WorkspaceModel(tx);\n\t\t\t\tconst roleModel = new RoleModel(tx);\n\t\t\t\tconst memModel = new MemberModel(tx);\n\n\t\t\t\tconst ws = await wsModel.create(wsOpts);\n\n\t\t\t\tconst adminRole = await roleModel.findSystem('ADMIN');\n\t\t\t\tif (!adminRole) throw new Error('System ADMIN role not found');\n\t\t\t\tawait memModel.add({ userId: ctx.user!.id, workspaceId: ws.id, roleId: adminRole.id });\n\n\t\t\t\treturn ws;\n\t\t\t});\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'WORKSPACE_CREATED', 'Workspace created successfully.', {\n\t\t\t\tworkspace: toWorkspaceDTO(workspace),\n\t\t\t});\n\t\t},\n\n\t\tasync get(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_FETCHED', 'Workspace retrieved successfully.', {\n\t\t\t\tworkspace: toWorkspaceDTO(ctx.workspace as IWorkspace),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst opts: Parameters<typeof workspaces.update>[1] = {};\n\n\t\t\tif (typeof body?.['name'] === 'string') opts.name = body['name'].trim();\n\t\t\tif (body?.['description'] !== undefined)\n\t\t\t\topts.description = typeof body['description'] === 'string' ? body['description'] : null;\n\t\t\tif (body?.['motto'] !== undefined)\n\t\t\t\topts.motto = typeof body['motto'] === 'string' ? body['motto'] : null;\n\t\t\tif (body?.['phone'] !== undefined)\n\t\t\t\topts.phone = typeof body['phone'] === 'string' ? body['phone'].trim() : null;\n\t\t\tif (body?.['businessType'] !== undefined)\n\t\t\t\topts.businessType = typeof body['businessType'] === 'string' ? body['businessType'] : null;\n\t\t\tif (body?.['address'] !== undefined && typeof body['address'] === 'object')\n\t\t\t\topts.address = (body['address'] ?? null) as NonNullable<\n\t\t\t\t\tParameters<typeof workspaces.update>[1]\n\t\t\t\t>['address'] & (object | null);\n\n\t\t\tconst workspace = await workspaces.update(ctx.workspace.id, opts);\n\t\t\tif (!workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_UPDATED', 'Workspace updated successfully.', {\n\t\t\t\tworkspace: toWorkspaceDTO(workspace),\n\t\t\t});\n\t\t},\n\n\t\tasync archive(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\tif (ctx.workspace.isPersonal) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.FORBIDDEN,\n\t\t\t\t\t'FORBIDDEN',\n\t\t\t\t\t'Personal workspaces cannot be archived',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tawait workspaces.archive(ctx.workspace.id, ctx.user!.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_ARCHIVED', 'Workspace archived successfully.');\n\t\t},\n\n\t\tasync restore(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tawait workspaces.restore(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_RESTORED', 'Workspace restored successfully.');\n\t\t},\n\n\t\tasync getSettings(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst settings = await workspaces.getSettings(ctx.workspace.id);\n\t\t\treturn setApiResponse(\n\t\t\t\tHTTP.OK,\n\t\t\t\t'SETTINGS_FETCHED',\n\t\t\t\t'Workspace settings retrieved successfully.',\n\t\t\t\t{\n\t\t\t\t\tsettings: toSettingsDTO(settings),\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tasync updateSettings(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tif (!body || Object.keys(body).length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'No settings provided');\n\t\t\t}\n\n\t\t\tconst patch: Record<string, string> = {};\n\t\t\tfor (const key of ['locale', 'timezone', 'currency', 'dateFormat', 'timeFormat'] as const) {\n\t\t\t\tif (typeof body[key] === 'string') patch[key] = body[key] as string;\n\t\t\t}\n\n\t\t\tconst settings = await workspaces.updateSettings(ctx.workspace.id, patch);\n\t\t\treturn setApiResponse(\n\t\t\t\tHTTP.OK,\n\t\t\t\t'SETTINGS_UPDATED',\n\t\t\t\t'Workspace settings updated successfully.',\n\t\t\t\t{\n\t\t\t\t\tsettings: toSettingsDTO(settings),\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t};\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IWorkspace, IWorkspaceSettings } from '../types';\nimport {\n\tfindWorkspaceById,\n\tfindWorkspacesByUserId,\n\tcreateWorkspace,\n\tcreatePersonalWorkspace,\n\tfindPersonalWorkspace,\n\tupdateWorkspace,\n\tarchiveWorkspace,\n\trestoreWorkspace,\n\tgetWorkspaceSettings,\n\tupdateWorkspaceSettings,\n} from '../services/workspaces';\n\nexport class WorkspaceModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tfindById(id: string): Promise<IWorkspace | null> {\n\t\treturn findWorkspaceById(id, this.store);\n\t}\n\n\tfindByUserId(userId: string): Promise<IWorkspace[]> {\n\t\treturn findWorkspacesByUserId(userId, this.store);\n\t}\n\n\tcreate(opts: Parameters<typeof createWorkspace>[0]): Promise<IWorkspace> {\n\t\treturn createWorkspace(opts, this.store);\n\t}\n\n\tcreatePersonal(opts: Parameters<typeof createPersonalWorkspace>[0]): Promise<IWorkspace | null> {\n\t\treturn createPersonalWorkspace(opts, this.store);\n\t}\n\n\tfindPersonal(userId: string): Promise<IWorkspace | null> {\n\t\treturn findPersonalWorkspace(userId, this.store);\n\t}\n\n\tupdate(id: string, opts: Parameters<typeof updateWorkspace>[1]): Promise<IWorkspace | null> {\n\t\treturn updateWorkspace(id, opts, this.store);\n\t}\n\n\tarchive(id: string, byUser: string): Promise<void> {\n\t\treturn archiveWorkspace(id, byUser, this.store);\n\t}\n\n\trestore(id: string): Promise<void> {\n\t\treturn restoreWorkspace(id, this.store);\n\t}\n\n\tgetSettings(id: string): Promise<IWorkspaceSettings> {\n\t\treturn getWorkspaceSettings(id, this.store);\n\t}\n\n\tupdateSettings(id: string, settings: Partial<IWorkspaceSettings>): Promise<IWorkspaceSettings> {\n\t\treturn updateWorkspaceSettings(id, settings, this.store);\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IMember, IRole } from '../types';\nimport {\n\tgetMember,\n\tlistMembers,\n\taddMember,\n\tremoveMember,\n\tgetUserRoles,\n\taddRoleToMember,\n\tremoveRoleFromMember,\n} from '../services/members';\n\nexport class MemberModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tget(userId: string, workspaceId: string): Promise<IMember | null> {\n\t\treturn getMember(userId, workspaceId, this.store);\n\t}\n\n\tlist(workspaceId: string): Promise<IMember[]> {\n\t\treturn listMembers(workspaceId, this.store);\n\t}\n\n\tadd(opts: Parameters<typeof addMember>[0]): Promise<void> {\n\t\treturn addMember(opts, this.store);\n\t}\n\n\tremove(userId: string, workspaceId: string): Promise<void> {\n\t\treturn removeMember(userId, workspaceId, this.store);\n\t}\n\n\tgetUserRoles(userId: string, workspaceId: string): Promise<IRole[]> {\n\t\treturn getUserRoles(userId, workspaceId, this.store);\n\t}\n\n\taddRole(userId: string, workspaceId: string, roleId: string): Promise<void> {\n\t\treturn addRoleToMember(userId, workspaceId, roleId, this.store);\n\t}\n\n\tremoveRole(userId: string, workspaceId: string, roleId: string): Promise<void> {\n\t\treturn removeRoleFromMember(userId, workspaceId, roleId, this.store);\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IRole } from '../types';\n\nconst SELECT_ROLE = `\n\tid,\n\tname,\n\tis_system AS \"isSystem\",\n\tactive,\n\tdescription,\n\tworkspace_id AS \"workspaceId\"\n`;\n\nexport async function createRole(\n\topts: { name: string; workspaceId: string; description?: string },\n\tstore: IStoreAdapter,\n): Promise<IRole> {\n\tconst [role] = await store.query<IRole>(\n\t\t`INSERT INTO fonderie_roles (name, workspace_id, description)\n\t\t VALUES ($1, $2, $3)\n\t\t RETURNING ${SELECT_ROLE}`,\n\t\t[opts.name, opts.workspaceId, opts.description ?? null],\n\t);\n\tif (!role) throw new Error('Failed to create role');\n\treturn role;\n}\n\nexport async function findSystemRole(name: string, store: IStoreAdapter): Promise<IRole | null> {\n\tconst [row] = await store.query<IRole>(\n\t\t`SELECT ${SELECT_ROLE} FROM fonderie_roles WHERE name = $1 AND is_system = true LIMIT 1`,\n\t\t[name],\n\t);\n\treturn row ?? null;\n}\n\nexport async function getRoleById(id: string, store: IStoreAdapter): Promise<IRole | null> {\n\tconst [row] = await store.query<IRole>(\n\t\t`SELECT ${SELECT_ROLE} FROM fonderie_roles WHERE id = $1`,\n\t\t[id],\n\t);\n\treturn row ?? null;\n}\n\nexport async function listWorkspaceRoles(\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IRole[]> {\n\treturn store.query<IRole>(\n\t\t`SELECT ${SELECT_ROLE}\n\t\t FROM fonderie_roles\n\t\t WHERE workspace_id = $1 OR is_system = true\n\t\t ORDER BY is_system DESC, name ASC`,\n\t\t[workspaceId],\n\t);\n}\n\nexport async function updateRole(\n\tid: string,\n\topts: { name?: string; description?: string | null; active?: boolean },\n\tstore: IStoreAdapter,\n): Promise<IRole | null> {\n\tconst sets: string[] = [];\n\tconst params: unknown[] = [id];\n\n\tif (opts.name !== undefined) {\n\t\tparams.push(opts.name);\n\t\tsets.push(`name = $${params.length}`);\n\t}\n\tif (opts.description !== undefined) {\n\t\tparams.push(opts.description);\n\t\tsets.push(`description = $${params.length}`);\n\t}\n\tif (opts.active !== undefined) {\n\t\tparams.push(opts.active);\n\t\tsets.push(`active = $${params.length}`);\n\t}\n\n\tif (sets.length === 0) return getRoleById(id, store);\n\n\tconst [row] = await store.query<IRole>(\n\t\t`UPDATE fonderie_roles\n\t\t SET ${sets.join(', ')}\n\t\t WHERE id = $1 AND is_system = false\n\t\t RETURNING ${SELECT_ROLE}`,\n\t\tparams,\n\t);\n\treturn row ?? null;\n}\n\nexport async function deleteRole(\n\tid: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`DELETE FROM fonderie_roles\n\t\t WHERE id = $1 AND workspace_id = $2 AND is_system = false`,\n\t\t[id, workspaceId],\n\t);\n}\n\nexport async function setRolePermissions(\n\troleId: string,\n\tworkspaceId: string,\n\tpermissions: Array<{\n\t\tpermissionKey: string;\n\t\tcanCreate: boolean;\n\t\tcanRead: boolean;\n\t\tcanUpdate: boolean;\n\t\tcanDelete: boolean;\n\t}>,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tif (permissions.length === 0) {\n\t\tawait store.query(\n\t\t\t`DELETE FROM fonderie_role_permissions WHERE role_id = $1 AND workspace_id = $2`,\n\t\t\t[roleId, workspaceId],\n\t\t);\n\t\treturn;\n\t}\n\n\tawait store.transaction(async (tx) => {\n\t\tawait tx.query(\n\t\t\t`DELETE FROM fonderie_role_permissions WHERE role_id = $1 AND workspace_id = $2`,\n\t\t\t[roleId, workspaceId],\n\t\t);\n\n\t\tfor (const p of permissions) {\n\t\t\tawait tx.query(\n\t\t\t\t`INSERT INTO fonderie_role_permissions\n\t\t\t\t (role_id, workspace_id, permission_key, can_create, can_read, can_update, can_delete)\n\t\t\t\t VALUES ($1, $2, $3, $4, $5, $6, $7)\n\t\t\t\t ON CONFLICT (role_id, permission_key)\n\t\t\t\t DO UPDATE SET\n\t\t\t\t can_create = $4, can_read = $5,\n\t\t\t\t can_update = $6, can_delete = $7`,\n\t\t\t\t[roleId, workspaceId, p.permissionKey, p.canCreate, p.canRead, p.canUpdate, p.canDelete],\n\t\t\t);\n\t\t}\n\t});\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IRole } from '../types';\nimport {\n\tcreateRole,\n\tfindSystemRole,\n\tgetRoleById,\n\tlistWorkspaceRoles,\n\tupdateRole,\n\tdeleteRole,\n\tsetRolePermissions,\n} from '../services/roles';\n\nexport class RoleModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tcreate(opts: Parameters<typeof createRole>[0]): Promise<IRole> {\n\t\treturn createRole(opts, this.store);\n\t}\n\n\tfindSystem(name: string): Promise<IRole | null> {\n\t\treturn findSystemRole(name, this.store);\n\t}\n\n\tfindById(id: string): Promise<IRole | null> {\n\t\treturn getRoleById(id, this.store);\n\t}\n\n\tlist(workspaceId: string): Promise<IRole[]> {\n\t\treturn listWorkspaceRoles(workspaceId, this.store);\n\t}\n\n\tupdate(id: string, opts: Parameters<typeof updateRole>[1]): Promise<IRole | null> {\n\t\treturn updateRole(id, opts, this.store);\n\t}\n\n\tdelete(id: string, workspaceId: string): Promise<void> {\n\t\treturn deleteRole(id, workspaceId, this.store);\n\t}\n\n\tsetPermissions(\n\t\troleId: string,\n\t\tworkspaceId: string,\n\t\tpermissions: Parameters<typeof setRolePermissions>[2],\n\t): Promise<void> {\n\t\treturn setRolePermissions(roleId, workspaceId, permissions, this.store);\n\t}\n}\n","import { stringOrEmpty, booleanOrFalse } from '@fonderie/core/parser';\n\nimport type { IWorkspace, IRole, IMember, IInvitation, IWorkspaceSettings } from '../types';\n\nexport interface IWorkspaceAddressDTO {\n\tline1: string;\n\tline2: string;\n\tcity: string;\n\tstate: string;\n\tzip: string;\n\tcountry: string;\n}\n\nexport interface IWorkspaceDTO {\n\tid: string;\n\tname: string;\n\tslug: string;\n\ttype: string;\n\tdescription: string;\n\tmotto: string;\n\tphone: string;\n\tbusinessType: string;\n\taddress: IWorkspaceAddressDTO;\n\tplan: string;\n\townerId: string;\n\tisPersonal: boolean;\n\tisArchived: boolean;\n\tarchivedAt: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface IRoleDTO {\n\tid: string;\n\tname: string;\n\tisSystem: boolean;\n\tactive: boolean;\n\tdescription: string;\n\tworkspaceId: string;\n}\n\nexport interface IMemberDTO {\n\tuserId: string;\n\tworkspaceId: string;\n\troleId: string;\n\troleName: string;\n\tconfirmed: boolean;\n\tcreatedAt: string;\n}\n\nexport interface IInvitationDTO {\n\tid: string;\n\tworkspaceId: string;\n\temail: string;\n\troleId: string;\n\ttoken: string;\n\tstatus: string;\n\texpiresAt: string;\n\tcreatedAt: string;\n}\n\nexport interface IWorkspaceSettingsDTO {\n\tlocale: string;\n\ttimezone: string;\n\tcurrency: string;\n\tdateFormat: string;\n\ttimeFormat: string;\n}\n\nexport function toWorkspaceDTO(ws: IWorkspace): IWorkspaceDTO {\n\tconst addr = ws.address ?? {};\n\treturn {\n\t\tid: stringOrEmpty(ws.id),\n\t\tname: stringOrEmpty(ws.name),\n\t\tslug: stringOrEmpty(ws.slug),\n\t\ttype: stringOrEmpty(ws.type),\n\t\tdescription: stringOrEmpty(ws.description),\n\t\tmotto: stringOrEmpty(ws.motto),\n\t\tphone: stringOrEmpty(ws.phone),\n\t\tbusinessType: stringOrEmpty(ws.businessType),\n\t\taddress: {\n\t\t\tline1: stringOrEmpty(addr.line1),\n\t\t\tline2: stringOrEmpty(addr.line2),\n\t\t\tcity: stringOrEmpty(addr.city),\n\t\t\tstate: stringOrEmpty(addr.state),\n\t\t\tzip: stringOrEmpty(addr.zip),\n\t\t\tcountry: stringOrEmpty(addr.country),\n\t\t},\n\t\tplan: stringOrEmpty(ws.plan),\n\t\townerId: stringOrEmpty(ws.ownerId),\n\t\tisPersonal: booleanOrFalse(ws.isPersonal),\n\t\tisArchived: ws.archivedAt !== null,\n\t\tarchivedAt: stringOrEmpty(ws.archivedAt),\n\t\tcreatedAt: stringOrEmpty(ws.createdAt),\n\t\tupdatedAt: stringOrEmpty(ws.updatedAt),\n\t};\n}\n\nexport function toRoleDTO(role: IRole): IRoleDTO {\n\treturn {\n\t\tid: stringOrEmpty(role.id),\n\t\tname: stringOrEmpty(role.name),\n\t\tisSystem: booleanOrFalse(role.isSystem),\n\t\tactive: role.active !== false,\n\t\tdescription: stringOrEmpty(role.description),\n\t\tworkspaceId: stringOrEmpty(role.workspaceId),\n\t};\n}\n\nexport function toMemberDTO(m: IMember): IMemberDTO {\n\treturn {\n\t\tuserId: stringOrEmpty(m.userId),\n\t\tworkspaceId: stringOrEmpty(m.workspaceId),\n\t\troleId: stringOrEmpty(m.roleId),\n\t\troleName: stringOrEmpty(m.roleName),\n\t\tconfirmed: booleanOrFalse(m.confirmed),\n\t\tcreatedAt: stringOrEmpty(m.createdAt),\n\t};\n}\n\nexport function toInvitationDTO(inv: IInvitation): IInvitationDTO {\n\treturn {\n\t\tid: stringOrEmpty(inv.id),\n\t\tworkspaceId: stringOrEmpty(inv.workspaceId),\n\t\temail: stringOrEmpty(inv.email),\n\t\troleId: stringOrEmpty(inv.roleId),\n\t\ttoken: stringOrEmpty(inv.token),\n\t\tstatus: stringOrEmpty(inv.status),\n\t\texpiresAt: stringOrEmpty(inv.expiresAt),\n\t\tcreatedAt: stringOrEmpty(inv.createdAt),\n\t};\n}\n\nexport function toSettingsDTO(s: IWorkspaceSettings): IWorkspaceSettingsDTO {\n\treturn {\n\t\tlocale: stringOrEmpty(s.locale),\n\t\ttimezone: stringOrEmpty(s.timezone),\n\t\tcurrency: stringOrEmpty(s.currency),\n\t\tdateFormat: stringOrEmpty(s.dateFormat),\n\t\ttimeFormat: stringOrEmpty(s.timeFormat),\n\t};\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { MemberModel } from '../models/member.model';\nimport { toMemberDTO, toRoleDTO } from '../dtos/workspace';\n\nexport function memberController(store: IStoreAdapter) {\n\tconst members = new MemberModel(store);\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst list = await members.list(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'MEMBERS_FETCHED', 'Members retrieved successfully.', {\n\t\t\t\tmembers: list.map(toMemberDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\tif (ctx.workspace.isPersonal) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.FORBIDDEN,\n\t\t\t\t\t'FORBIDDEN',\n\t\t\t\t\t'Personal workspaces do not support member management',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\t\t\tif (userId === ctx.user?.id) {\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'INVALID_OPERATION', 'Cannot remove yourself');\n\t\t\t}\n\n\t\t\tawait members.remove(userId, ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'MEMBER_REMOVED', 'Member removed successfully.');\n\t\t},\n\n\t\tasync getUserRoles(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\n\t\t\tconst roles = await members.getUserRoles(userId, ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLES_FETCHED', 'Member roles retrieved successfully.', {\n\t\t\t\troles: roles.map(toRoleDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync addRole(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\t\t\tconst roleId = (body?.['roleId'] ?? params?.['roleId']) as string | undefined;\n\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\t\t\tif (!roleId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\n\t\t\tawait members.addRole(userId, ctx.workspace.id, roleId);\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_ASSIGNED', 'Role assigned successfully.');\n\t\t},\n\n\t\tasync removeRole(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\t\t\tconst roleId = params?.['roleId'];\n\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\t\t\tif (!roleId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\n\t\t\ttry {\n\t\t\t\tawait members.removeRole(userId, ctx.workspace.id, roleId);\n\t\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_REMOVED', 'Role removed successfully.');\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : 'Failed';\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'OPERATION_FAILED', message);\n\t\t\t}\n\t\t},\n\t};\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { RoleModel } from '../models/role.model';\nimport { toRoleDTO } from '../dtos/workspace';\n\nexport function roleController(store: IStoreAdapter) {\n\tconst roles = new RoleModel(store);\n\n\treturn {\n\t\tasync create(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst name = body?.['name'];\n\t\t\tconst description = body?.['description'];\n\n\t\t\tif (typeof name !== 'string' || name.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'name is required');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst opts: Parameters<typeof roles.create>[0] = {\n\t\t\t\t\tname: name.trim(),\n\t\t\t\t\tworkspaceId: ctx.workspace.id,\n\t\t\t\t};\n\n\t\t\t\tif (typeof description === 'string') {\n\t\t\t\t\topts.description = description;\n\t\t\t\t}\n\n\t\t\t\tconst role = await roles.create(opts);\n\t\t\t\treturn setApiResponse(HTTP.CREATED, 'ROLE_CREATED', 'Role created successfully.', {\n\t\t\t\t\trole: toRoleDTO(role),\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : 'Failed to create role';\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'OPERATION_FAILED', message);\n\t\t\t}\n\t\t},\n\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst list = await roles.list(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLES_FETCHED', 'Roles retrieved successfully.', {\n\t\t\t\troles: list.map(toRoleDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync get(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tconst role = await roles.findById(roleId);\n\t\t\tif (!role) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Role not found');\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_FETCHED', 'Role retrieved successfully.', {\n\t\t\t\trole: toRoleDTO(role),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tconst opts: { name?: string; description?: string | null; active?: boolean } = {};\n\n\t\t\tif (typeof body?.['name'] === 'string') {\n\t\t\t\topts.name = body['name'];\n\t\t\t}\n\n\t\t\tif (typeof body?.['description'] === 'string') {\n\t\t\t\topts.description = body['description'];\n\t\t\t}\n\n\t\t\tif (body?.['description'] === null) {\n\t\t\t\topts.description = null;\n\t\t\t}\n\n\t\t\tif (typeof body?.['active'] === 'boolean') {\n\t\t\t\topts.active = body['active'];\n\t\t\t}\n\n\t\t\tconst role = await roles.update(roleId, opts);\n\t\t\tif (!role) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Role not found or is a system role');\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_UPDATED', 'Role updated successfully.', {\n\t\t\t\trole: toRoleDTO(role),\n\t\t\t});\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tawait roles.delete(roleId, ctx.workspace.id);\n\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_DELETED', 'Role deleted successfully.');\n\t\t},\n\n\t\tasync setPermissions(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tconst perms = body?.['permissions'];\n\t\t\tif (!Array.isArray(perms)) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t'permissions array is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst normalized = perms\n\t\t\t\t.map((p: unknown) => {\n\t\t\t\t\tconst perm = p as Record<string, unknown>;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpermissionKey: String(perm['permissionKey'] ?? ''),\n\t\t\t\t\t\tcanCreate: Boolean(perm['canCreate']),\n\t\t\t\t\t\tcanRead: Boolean(perm['canRead']),\n\t\t\t\t\t\tcanUpdate: Boolean(perm['canUpdate']),\n\t\t\t\t\t\tcanDelete: Boolean(perm['canDelete']),\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.filter((p) => p.permissionKey.length > 0);\n\n\t\t\tawait roles.setPermissions(roleId, ctx.workspace.id, normalized);\n\n\t\t\treturn setApiResponse(HTTP.OK, 'PERMISSIONS_SET', 'Role permissions updated successfully.');\n\t\t},\n\t};\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { ICourierMessage } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport type { EventBus } from '@fonderie/events';\nimport { NOTIFICATION_EVENT } from '@fonderie/events';\n\nimport { getPlanLimit } from '@fonderie/billing';\nimport { MESSAGE_KEYS } from '../config';\nimport { InvitationModel } from '../models/invitation.model';\nimport { toInvitationDTO } from '../dtos/workspace';\n\nexport function invitationController(store: IStoreAdapter, ttl: string, bus?: EventBus) {\n\tconst invitations = new InvitationModel(store);\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst list = await invitations.list(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'INVITATIONS_FETCHED', 'Invitations retrieved successfully.', {\n\t\t\t\tinvitations: list.map(toInvitationDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync invite(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\tif (ctx.workspace.isPersonal) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.FORBIDDEN,\n\t\t\t\t\t'FORBIDDEN',\n\t\t\t\t\t'Personal workspaces do not support invitations',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as unknown;\n\t\t\tconst entries = Array.isArray(body) ? body as Record<string, unknown>[] : [body as Record<string, unknown>];\n\n\t\t\tif (!entries.length) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'at least one invite is required');\n\t\t\t}\n\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (typeof entry?.['email'] !== 'string') {\n\t\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'email is required for every invite');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Seat limit — enforced automatically when billing module is registered.\n\t\t\t// getPlanLimit reads from ctx.meta['billing'] (fail-open when billing absent).\n\t\t\tconst seatLimit = getPlanLimit(ctx, 'seats');\n\t\t\tif (seatLimit !== null) {\n\t\t\t\tconst [countRow] = await store.query<{ count: string }>(\n\t\t\t\t\t`SELECT COUNT(*) AS count FROM fonderie_role_user_workspaces\n\t\t\t\t\t WHERE workspace_id = $1 AND removed = false AND suspended = false`,\n\t\t\t\t\t[ctx.workspace.id],\n\t\t\t\t);\n\t\t\t\tconst total = parseInt(countRow!.count, 10);\n\t\t\t\tconst occupied = ctx.workspace.isPersonal ? total : Math.max(0, total - 1);\n\t\t\t\tif (occupied + entries.length > seatLimit) {\n\t\t\t\t\treturn setApiResponse(\n\t\t\t\t\t\tHTTP.PAYMENT_REQUIRED,\n\t\t\t\t\t\t'SEAT_LIMIT_REACHED',\n\t\t\t\t\t\t`Your plan allows ${seatLimit} seat${seatLimit === 1 ? '' : 's'}. Upgrade to invite more members.`,\n\t\t\t\t\t\t{ limit: seatLimit },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Resolve default role once for entries that omit roleId.\n\t\t\tlet defaultRoleId: string | undefined;\n\t\t\tconst needsDefault = entries.some((e) => !e['roleId']);\n\t\t\tif (needsDefault) {\n\t\t\t\tconst [row] = await store.query<{ id: string }>(\n\t\t\t\t\t`SELECT id FROM fonderie_roles\n\t\t\t\t\t WHERE name = 'ADMIN' AND workspace_id = $1 LIMIT 1`,\n\t\t\t\t\t[ctx.workspace.id],\n\t\t\t\t);\n\t\t\t\tdefaultRoleId = row?.id;\n\t\t\t\tif (!defaultRoleId) {\n\t\t\t\t\treturn setApiResponse(HTTP.SERVER_ERROR, 'SERVER_ERROR', 'Default role not found');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst results = await Promise.all(\n\t\t\t\tentries.map(async (entry) => {\n\t\t\t\t\tconst email = entry['email'] as string;\n\t\t\t\t\tconst resolvedRoleId = (entry['roleId'] as string | undefined) ?? defaultRoleId!;\n\n\t\t\t\t\tconst invitation = await invitations.create({\n\t\t\t\t\t\tworkspaceId: ctx.workspace!.id,\n\t\t\t\t\t\temail,\n\t\t\t\t\t\troleId: resolvedRoleId,\n\t\t\t\t\t\tttl,\n\t\t\t\t\t});\n\n\t\t\t\t\tbus\n\t\t\t\t\t\t?.emit(NOTIFICATION_EVENT, {\n\t\t\t\t\t\t\ttype: MESSAGE_KEYS.workspaceInvitation,\n\t\t\t\t\t\t\trecipient: { email, phone: null, deviceToken: null },\n\t\t\t\t\t\t\tdata: { token: invitation.token, pin: invitation.pin },\n\t\t\t\t\t\t} satisfies ICourierMessage)\n\t\t\t\t\t\t.catch(() => {});\n\n\t\t\t\t\treturn { invitationId: invitation.id, email };\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'INVITATIONS_SENT', 'Invitations sent successfully.', {\n\t\t\t\tinvitations: results,\n\t\t\t});\n\t\t},\n\n\t\tasync cancel(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst invitationId = params?.['inviteId'];\n\n\t\t\tif (!invitationId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'inviteId is required');\n\n\t\t\tawait invitations.cancel(invitationId, ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'INVITATION_CANCELLED', 'Invitation cancelled successfully.');\n\t\t},\n\n\t\tasync accept(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst pin = body?.['pin'];\n\n\t\t\tif (typeof pin !== 'string') {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'pin is required');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { workspaceId } = await invitations.acceptByPin({ pin, userId: ctx.user!.id });\n\t\t\t\treturn setApiResponse(HTTP.OK, 'INVITATION_ACCEPTED', 'Invitation accepted successfully.', {\n\t\t\t\t\tworkspaceId,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : 'Invalid invitation';\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'INVITATION_FAILED', message);\n\t\t\t}\n\t\t},\n\t};\n}\n","import { randomBytes } from 'node:crypto';\n\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IInvitation } from '../types';\n\nfunction generateToken(): string {\n\treturn randomBytes(32).toString('hex');\n}\n\nfunction generatePin(): string {\n\treturn Math.floor(100000 + Math.random() * 900000).toString();\n}\n\nfunction parseTtl(ttl: string): number {\n\tconst units: Record<string, number> = {\n\t\ts: 1000,\n\t\tm: 60_000,\n\t\th: 3_600_000,\n\t\td: 86_400_000,\n\t};\n\tconst match = ttl.match(/^(\\d+)([smhd])$/);\n\tif (!match) return 7 * 86_400_000;\n\tconst [, n, unit] = match;\n\treturn parseInt(n!, 10) * (units[unit!] ?? 0);\n}\n\nconst SELECT_INV = `\n\tid,\n\tworkspace_id AS \"workspaceId\",\n\temail,\n\trole_id AS \"roleId\",\n\ttoken,\n\tpin,\n\tstatus,\n\texpires_at AS \"expiresAt\",\n\tcreated_at AS \"createdAt\"\n`;\n\nexport async function createInvitation(\n\topts: { workspaceId: string; email: string; roleId: string; ttl?: string },\n\tstore: IStoreAdapter,\n): Promise<IInvitation> {\n\tconst token = generateToken();\n\tconst pin = generatePin();\n\tconst expiresAt = new Date(Date.now() + parseTtl(opts.ttl ?? '7d'));\n\n\tconst [invitation] = await store.query<IInvitation>(\n\t\t`INSERT INTO fonderie_workspace_invitations\n\t\t (workspace_id, email, role_id, token, pin, expires_at)\n\t\t VALUES ($1, $2, $3, $4, $5, $6)\n\t\t ON CONFLICT DO NOTHING\n\t\t RETURNING ${SELECT_INV}`,\n\t\t[opts.workspaceId, opts.email, opts.roleId, token, pin, expiresAt],\n\t);\n\n\tif (!invitation) {\n\t\t// Already pending — update it\n\t\tconst [updated] = await store.query<IInvitation>(\n\t\t\t`UPDATE fonderie_workspace_invitations\n\t\t\t SET token = $4, pin = $5, expires_at = $6, role_id = $3, status = 'PENDING'\n\t\t\t WHERE workspace_id = $1 AND email = $2 AND status = 'PENDING'\n\t\t\t RETURNING ${SELECT_INV}`,\n\t\t\t[opts.workspaceId, opts.email, opts.roleId, token, pin, expiresAt],\n\t\t);\n\t\tif (!updated) throw new Error('Failed to create invitation');\n\t\treturn updated;\n\t}\n\n\treturn invitation;\n}\n\nexport async function listInvitations(\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IInvitation[]> {\n\treturn store.query<IInvitation>(\n\t\t`SELECT ${SELECT_INV}\n\t\t FROM fonderie_workspace_invitations\n\t\t WHERE workspace_id = $1 AND status = 'PENDING'\n\t\t ORDER BY created_at DESC`,\n\t\t[workspaceId],\n\t);\n}\n\nexport async function cancelInvitation(\n\tinvitationId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspace_invitations\n\t\t SET status = 'CANCELLED'\n\t\t WHERE id = $1 AND workspace_id = $2 AND status = 'PENDING'`,\n\t\t[invitationId, workspaceId],\n\t);\n}\n\nexport async function acceptInvitationByPin(\n\topts: { pin: string; userId: string },\n\tstore: IStoreAdapter,\n): Promise<{ workspaceId: string; roleId: string }> {\n\tconst [inv] = await store.query<{\n\t\tid: string;\n\t\tworkspaceId: string;\n\t\troleId: string;\n\t\texpiresAt: string;\n\t}>(\n\t\t`SELECT id, workspace_id AS \"workspaceId\", role_id AS \"roleId\", expires_at AS \"expiresAt\"\n\t\t FROM fonderie_workspace_invitations\n\t\t WHERE pin = $1 AND status = 'PENDING'`,\n\t\t[opts.pin],\n\t);\n\n\tif (!inv) throw new Error('Invalid PIN');\n\tif (new Date() > new Date(inv.expiresAt)) throw new Error('Invitation expired');\n\n\tawait store.transaction(async (tx) => {\n\t\tawait Promise.all([\n\t\t\ttx.query(\n\t\t\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t\t\t VALUES ($1, $2, $3, true)\n\t\t\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t\t\t SET confirmed = true, removed = false`,\n\t\t\t\t[opts.userId, inv.workspaceId, inv.roleId],\n\t\t\t),\n\t\t\ttx.query(`UPDATE fonderie_workspace_invitations SET status = 'ACCEPTED' WHERE id = $1`, [\n\t\t\t\tinv.id,\n\t\t\t]),\n\t\t]);\n\t});\n\n\treturn { workspaceId: inv.workspaceId, roleId: inv.roleId };\n}\n\nexport async function acceptInvitationByToken(\n\ttoken: string,\n\tuserId: string,\n\tstore: IStoreAdapter,\n): Promise<{ workspaceId: string; roleId: string }> {\n\tconst [inv] = await store.query<{\n\t\tid: string;\n\t\tworkspaceId: string;\n\t\troleId: string;\n\t\texpiresAt: string;\n\t}>(\n\t\t`SELECT id, workspace_id AS \"workspaceId\", role_id AS \"roleId\", expires_at AS \"expiresAt\"\n\t\t FROM fonderie_workspace_invitations\n\t\t WHERE token = $1 AND status = 'PENDING'`,\n\t\t[token],\n\t);\n\n\tif (!inv) throw new Error('Invalid token');\n\tif (new Date() > new Date(inv.expiresAt)) throw new Error('Invitation expired');\n\n\tawait store.transaction(async (tx) => {\n\t\tawait Promise.all([\n\t\t\ttx.query(\n\t\t\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t\t\t VALUES ($1, $2, $3, true)\n\t\t\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t\t\t SET confirmed = true, removed = false`,\n\t\t\t\t[userId, inv.workspaceId, inv.roleId],\n\t\t\t),\n\t\t\ttx.query(`UPDATE fonderie_workspace_invitations SET status = 'ACCEPTED' WHERE id = $1`, [\n\t\t\t\tinv.id,\n\t\t\t]),\n\t\t]);\n\t});\n\n\treturn { workspaceId: inv.workspaceId, roleId: inv.roleId };\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IInvitation } from '../types';\nimport {\n\tcreateInvitation,\n\tlistInvitations,\n\tcancelInvitation,\n\tacceptInvitationByPin,\n} from '../services/invitations';\n\nexport class InvitationModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tcreate(opts: Parameters<typeof createInvitation>[0]): Promise<IInvitation> {\n\t\treturn createInvitation(opts, this.store);\n\t}\n\n\tlist(workspaceId: string): Promise<IInvitation[]> {\n\t\treturn listInvitations(workspaceId, this.store);\n\t}\n\n\tcancel(invitationId: string, workspaceId: string): Promise<void> {\n\t\treturn cancelInvitation(invitationId, workspaceId, this.store);\n\t}\n\n\tacceptByPin(\n\t\topts: Parameters<typeof acceptInvitationByPin>[0],\n\t): Promise<{ workspaceId: string; roleId: string }> {\n\t\treturn acceptInvitationByPin(opts, this.store);\n\t}\n}\n","import type { IFonderieModule, IFonderieApp } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport type { EventBus } from '@fonderie/events';\n\nimport type { IWorkspacesConfig } from './config';\nimport { EVENT_KEYS } from './config';\nimport { buildWorkspaceRoutes } from './routes';\nimport { WorkspaceModel } from './models/workspace.model';\nimport { RoleModel } from './models/role.model';\nimport { MemberModel } from './models/member.model';\n\n// Mirror of @fonderie/auth EVENT_KEYS.userRegistered — avoids a runtime\n// dependency on the auth package while remaining explicit about the contract.\nconst AUTH_USER_REGISTERED = 'fonderie.user.registered' as const;\n\ninterface UserRegisteredPayload {\n\tuserId: string;\n}\n\nexport class WorkspacesModule implements IFonderieModule {\n\treadonly name = '@fonderie/workspaces';\n\treadonly deps = ['@fonderie/auth', '@fonderie/billing'];\n\n\tconstructor(\n\t\tprivate store: IStoreAdapter,\n\t\tprivate config: IWorkspacesConfig = {},\n\t\tprivate bus?: EventBus,\n\t) {}\n\n\tinstall(app: IFonderieApp): void {\n\t\tif (this.bus && this.config.personalWorkspace !== false) {\n\t\t\tthis.bus.on<UserRegisteredPayload>(\n\t\t\t\tAUTH_USER_REGISTERED,\n\t\t\t\t(payload) => this.provisionPersonalWorkspace(payload),\n\t\t\t\t'workspaces',\n\t\t\t);\n\t\t}\n\n\t\tconst routes = buildWorkspaceRoutes(this.store, this.config, this.bus);\n\t\tfor (const [method, path, ...handlers] of routes) {\n\t\t\tapp.addRoute(method, path, ...handlers);\n\t\t}\n\t}\n\n\tprivate async provisionPersonalWorkspace(payload: UserRegisteredPayload): Promise<void> {\n\t\tconst name = 'My Workspace';\n\t\tconst slug = `${payload.userId}-personal`;\n\n\t\tlet workspaceId: string | undefined;\n\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tconst wsModel = new WorkspaceModel(tx);\n\t\t\tconst roleModel = new RoleModel(tx);\n\t\t\tconst memModel = new MemberModel(tx);\n\n\t\t\tconst ws = await wsModel.createPersonal({ name, slug, ownerId: payload.userId });\n\t\t\tif (!ws) return; // already exists — idempotent replay\n\n\t\t\tworkspaceId = ws.id;\n\n\t\t\tconst adminRole = await roleModel.findSystem('ADMIN');\n\t\t\tif (!adminRole) throw new Error('System ADMIN role not found');\n\n\t\t\tawait memModel.add({\n\t\t\t\tuserId: payload.userId,\n\t\t\t\tworkspaceId: ws.id,\n\t\t\t\troleId: adminRole.id,\n\t\t\t\tconfirmed: true,\n\t\t\t});\n\t\t});\n\n\t\tif (workspaceId) {\n\t\t\tthis.bus\n\t\t\t\t?.emit(EVENT_KEYS.personalWorkspaceCreated, {\n\t\t\t\t\tworkspaceId,\n\t\t\t\t\tuserId: payload.userId,\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\t\t}\n\t}\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { Middleware } from '@fonderie/core';\n\n// Ensures ctx.workspace is set — use after withWorkspace\n\nexport const requireWorkspace: Middleware = async (ctx, next) => {\n\tif (!ctx.workspace) {\n\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'WORKSPACE_REQUIRED', 'Workspace context required');\n\t}\n\treturn next();\n};\n"],"mappings":"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","names":["setApiResponse","HTTP","name","name","setApiResponse","HTTP","name","setApiResponse","HTTP","setApiResponse","HTTP","setApiResponse","HTTP","setApiResponse","HTTP","name","setApiResponse","HTTP","setApiResponse","HTTP","email","name","setApiResponse","HTTP"]}
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/routes.ts","../src/schemas.ts","../src/middlewares/workspace-context.ts","../src/services/members.ts","../src/services/workspaces.ts","../src/controllers/workspace.controller.ts","../src/models/workspace.model.ts","../src/models/member.model.ts","../src/services/roles.ts","../src/models/role.model.ts","../src/dtos/workspace.ts","../src/controllers/member.controller.ts","../src/controllers/role.controller.ts","../src/controllers/invitation.controller.ts","../src/services/invitations.ts","../src/models/invitation.model.ts","../src/module.ts","../src/middlewares/require-workspace.ts"],"sourcesContent":["export const MESSAGE_KEYS = {\n\tworkspaceInvitation: 'workspace-invitation',\n} as const;\n\nexport type WorkspacesMessageKey = (typeof MESSAGE_KEYS)[keyof typeof MESSAGE_KEYS];\n\nexport const EVENT_KEYS = {\n\tpersonalWorkspaceCreated: 'fonderie.workspace.personal.created',\n} as const;\n\nexport type WorkspacesEventKey = (typeof EVENT_KEYS)[keyof typeof EVENT_KEYS];\n\nexport interface IWorkspacesConfig {\n\t// How long invitations are valid. Default: '7d'\n\tinvitationTtl?: string;\n\n\t// Default role assigned to invited members. Default: 'member'\n\tdefaultRole?: string;\n\n\t// Auto-create a personal workspace when user.registered fires.\n\t// Requires an EventBus to be passed to WorkspacesModule. Default: true\n\tpersonalWorkspace?: boolean;\n}\n","import type { IStoreAdapter } from '@fonderie/store';\nimport type { Middleware } from '@fonderie/core';\nimport type { EventBus } from '@fonderie/events';\nimport { requireAuth, validate } from '@fonderie/core/middlewares';\n\nimport {\n\tcreateRoleSchema,\n\tupdateRoleSchema,\n\taddMemberRoleSchema,\n\tupdateSettingsSchema,\n\tcreateWorkspaceSchema,\n\tupdateWorkspaceSchema,\n\tacceptInvitationSchema,\n\tcreateInvitationsSchema,\n\tsetRolePermissionsSchema,\n} from './schemas';\n\nimport type { IWorkspacesConfig } from './config';\nimport { withWorkspace } from './middlewares/workspace-context';\n\nimport { workspaceController } from './controllers/workspace.controller';\nimport { memberController } from './controllers/member.controller';\nimport { roleController } from './controllers/role.controller';\nimport { invitationController } from './controllers/invitation.controller';\n\ntype RouteDefinition = [string, string, ...Middleware[]];\n\nexport function buildWorkspaceRoutes(\n\tstore: IStoreAdapter,\n\tconfig: IWorkspacesConfig,\n\tbus?: EventBus,\n): RouteDefinition[] {\n\tconst ttl = config.invitationTtl ?? '7d';\n\tconst wsCtx = withWorkspace(store);\n\n\tconst workspace = workspaceController(store, config);\n\tconst member = memberController(store);\n\tconst role = roleController(store);\n\tconst invitation = invitationController(store, ttl, bus);\n\n\treturn [\n\t\t// ── Workspace creation + listing (no workspace context required)\n\t\t['POST', '/workspaces', requireAuth, validate(createWorkspaceSchema), workspace.create],\n\t\t['GET', '/workspaces', requireAuth, workspace.list],\n\n\t\t// ── Members (workspace resolved from X-Workspace-ID header)\n\t\t['GET', '/workspaces/members', requireAuth, wsCtx, member.list],\n\t\t['DELETE', '/workspaces/members/:userId', requireAuth, wsCtx, member.remove],\n\t\t['GET', '/workspaces/members/:userId/roles', requireAuth, wsCtx, member.getUserRoles],\n\t\t['POST', '/workspaces/members/:userId/roles', requireAuth, wsCtx, validate(addMemberRoleSchema), member.addRole],\n\t\t['DELETE', '/workspaces/members/:userId/roles/:roleId', requireAuth, wsCtx, member.removeRole],\n\n\t\t// ── Invitations\n\t\t['GET', '/workspaces/invitations', requireAuth, wsCtx, invitation.list],\n\t\t['POST', '/workspaces/invitations', requireAuth, wsCtx, validate(createInvitationsSchema), invitation.invite],\n\t\t['DELETE', '/workspaces/invitations/:inviteId', requireAuth, wsCtx, invitation.cancel],\n\t\t['POST', '/workspaces/invitations/accept', requireAuth, validate(acceptInvitationSchema), invitation.accept],\n\n\t\t// ── Roles\n\t\t['POST', '/workspaces/roles', requireAuth, wsCtx, validate(createRoleSchema), role.create],\n\t\t['GET', '/workspaces/roles', requireAuth, wsCtx, role.list],\n\t\t['GET', '/workspaces/roles/:roleId', requireAuth, wsCtx, role.get],\n\t\t['PUT', '/workspaces/roles/:roleId', requireAuth, wsCtx, validate(updateRoleSchema), role.update],\n\t\t['DELETE', '/workspaces/roles/:roleId', requireAuth, wsCtx, role.remove],\n\t\t['POST', '/workspaces/roles/:roleId/permissions', requireAuth, wsCtx, validate(setRolePermissionsSchema), role.setPermissions],\n\n\t\t// ── Workspace lifecycle\n\t\t['POST', '/workspaces/archive', requireAuth, wsCtx, workspace.archive],\n\t\t['POST', '/workspaces/restore', requireAuth, wsCtx, workspace.restore],\n\t\t['GET', '/workspaces/settings', requireAuth, wsCtx, workspace.getSettings],\n\t\t['PUT', '/workspaces/settings', requireAuth, wsCtx, validate(updateSettingsSchema), workspace.updateSettings],\n\n\t\t// ── Path-based lookup by ID (admin / cross-workspace use)\n\t\t['GET', '/workspaces/:id', requireAuth, wsCtx, workspace.get],\n\n\t\t// ── Update current workspace — ID resolved from X-Workspace-ID header\n\t\t// (or personal workspace fallback when header is absent)\n\t\t['PUT', '/workspaces', requireAuth, wsCtx, validate(updateWorkspaceSchema), workspace.update],\n\t];\n}\n","import { z } from 'zod';\n\n// Request schemas — the validation contract for every body-taking workspaces\n// route. Wired into routes.ts via @fonderie/core's validate(); exported so\n// docs generators and typed clients read the source of truth the runtime\n// enforces. Same pattern as @fonderie/auth/src/schemas.ts.\n\nconst name = z.string().trim().min(1, 'name is required').max(200);\nconst email = z.string().trim().pipe(z.email());\n\nexport const createWorkspaceSchema = z.object({\n\tname,\n\tdescription: z.string().max(2000).optional(),\n\t// PERSONAL workspaces are created automatically by the module; the\n\t// controller enforces the rule — schema just types the field.\n\ttype: z.string().max(40).optional(),\n});\n\nconst addressSchema = z\n\t.object({\n\t\tline1: z.string().max(200).optional(),\n\t\tline2: z.string().max(200).optional(),\n\t\tcity: z.string().max(100).optional(),\n\t\tregion: z.string().max(100).optional(),\n\t\tpostalCode: z.string().max(20).optional(),\n\t\tcountry: z.string().max(60).optional(),\n\t})\n\t.passthrough();\n\nexport const updateWorkspaceSchema = z\n\t.object({\n\t\tname: z.string().trim().min(1).max(200).optional(),\n\t\tdescription: z.string().max(2000).nullable().optional(),\n\t\tmotto: z.string().max(300).nullable().optional(),\n\t\tphone: z.string().max(32).nullable().optional(),\n\t\tbusinessType: z.string().max(100).nullable().optional(),\n\t\taddress: addressSchema.nullable().optional(),\n\t})\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\n\nexport const updateSettingsSchema = z\n\t.object({\n\t\tlocale: z.string().max(35).optional(),\n\t\ttimezone: z.string().max(64).optional(),\n\t\tcurrency: z.string().max(8).optional(),\n\t\tdateFormat: z.string().max(32).optional(),\n\t\ttimeFormat: z.string().max(32).optional(),\n\t})\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'No settings provided');\n\nexport const addMemberRoleSchema = z.object({ roleId: z.string().min(1, 'roleId is required') });\n\nconst inviteEntry = z.object({\n\temail,\n\troleId: z.string().min(1).optional(),\n});\n\n// POST /workspaces/invitations accepts a single invite or a batch.\nexport const createInvitationsSchema = z.union([\n\tinviteEntry,\n\tz.array(inviteEntry).min(1, 'at least one invite is required'),\n]);\n\nexport const acceptInvitationSchema = z.object({ pin: z.string().trim().min(1, 'pin is required') });\n\nexport const createRoleSchema = z.object({\n\tname,\n\tdescription: z.string().max(1000).optional(),\n});\n\nexport const updateRoleSchema = z\n\t.object({\n\t\tname: z.string().trim().min(1).max(200).optional(),\n\t\tdescription: z.string().max(1000).nullable().optional(),\n\t\tactive: z.boolean().optional(),\n\t})\n\t.refine((o) => Object.values(o).some((v) => v !== undefined), 'Provide at least one field');\n\nexport const setRolePermissionsSchema = z.object({\n\tpermissions: z.array(\n\t\tz.object({\n\t\t\tpermissionKey: z.string().min(1),\n\t\t\tcanCreate: z.boolean().optional(),\n\t\t\tcanRead: z.boolean().optional(),\n\t\t\tcanUpdate: z.boolean().optional(),\n\t\t\tcanDelete: z.boolean().optional(),\n\t\t}),\n\t),\n});\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { Middleware } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { getMember } from '../services/members';\nimport { findWorkspaceById, findPersonalWorkspace } from '../services/workspaces';\n\n// Resolves ctx.workspace from:\n// 1. Route param :workspaceId or :id (path-based admin routes)\n// 2. X-Workspace-ID request header (standard resource routes)\n// Validates the current user is an active member.\n// Must run after withSession.\n\nfunction makeHandler(store: IStoreAdapter): Middleware {\n\treturn async (ctx, next) => {\n\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\tconst workspaceId =\n\t\t\tparams?.['workspaceId'] ??\n\t\t\tparams?.['id'] ??\n\t\t\tctx.request.headers.get('x-workspace-id') ??\n\t\t\tundefined;\n\n\t\tif (!workspaceId) {\n\t\t\t// DMZ fallback — if the caller didn't specify a workspace, use their personal one.\n\t\t\t// No membership check needed: the user is always the sole owner.\n\t\t\tif (ctx.user) {\n\t\t\t\tconst personal = await findPersonalWorkspace(ctx.user.id, store);\n\t\t\t\tif (personal) Object.assign(ctx, { workspace: personal });\n\t\t\t}\n\t\t\treturn next();\n\t\t}\n\n\t\tconst workspace = await findWorkspaceById(workspaceId, store);\n\t\tif (!workspace) {\n\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t}\n\n\t\tif (ctx.user) {\n\t\t\tconst member = await getMember(ctx.user.id, workspaceId, store);\n\t\t\tif (!member) {\n\t\t\t\treturn setApiResponse(HTTP.FORBIDDEN, 'FORBIDDEN', 'Not a member of this workspace');\n\t\t\t}\n\t\t}\n\n\t\tObject.assign(ctx, { workspace });\n\t\treturn next();\n\t};\n}\n\nexport function withWorkspace(store: IStoreAdapter): Middleware;\nexport function withWorkspace(\n\tstore: IStoreAdapter,\n\tctx: IFonderieContext,\n\tnext: () => Promise<Response>,\n): Promise<Response>;\nexport function withWorkspace(\n\tstore: IStoreAdapter,\n\tctx?: IFonderieContext,\n\tnext?: () => Promise<Response>,\n): Middleware | Promise<Response> {\n\tconst handler = makeHandler(store);\n\tif (ctx !== undefined && next !== undefined) return handler(ctx, next);\n\treturn handler;\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IMember, IRole } from '../types';\n\nconst SELECT_MEMBER = `\n\truw.user_id AS \"userId\",\n\truw.workspace_id AS \"workspaceId\",\n\truw.role_id AS \"roleId\",\n\tr.name AS \"roleName\",\n\truw.confirmed AS \"confirmed\",\n\truw.created_at AS \"createdAt\",\n\tu.first_name AS \"firstName\",\n\tu.last_name AS \"lastName\",\n\tu.email AS \"email\",\n\tu.profile_image_url AS \"profileImageUrl\"\n`;\n\nexport async function getMember(\n\tuserId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IMember | null> {\n\tconst [row] = await store.query<IMember>(\n\t\t`SELECT ${SELECT_MEMBER}\n\t\t FROM fonderie_role_user_workspaces ruw\n\t\t LEFT JOIN fonderie_roles r ON r.id = ruw.role_id\n\t\t LEFT JOIN fonderie_users u ON u.id = ruw.user_id\n\t\t WHERE ruw.user_id = $1\n\t\t AND ruw.workspace_id = $2\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false\n\t\t LIMIT 1`,\n\t\t[userId, workspaceId],\n\t);\n\treturn row ?? null;\n}\n\nexport async function listMembers(workspaceId: string, store: IStoreAdapter): Promise<IMember[]> {\n\treturn store.query<IMember>(\n\t\t`SELECT ${SELECT_MEMBER}\n\t\t FROM fonderie_role_user_workspaces ruw\n\t\t LEFT JOIN fonderie_roles r ON r.id = ruw.role_id\n\t\t LEFT JOIN fonderie_users u ON u.id = ruw.user_id\n\t\t WHERE ruw.workspace_id = $1\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false\n\t\t ORDER BY ruw.created_at ASC`,\n\t\t[workspaceId],\n\t);\n}\n\nexport async function addMember(\n\topts: { userId: string; workspaceId: string; roleId: string; confirmed?: boolean },\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t VALUES ($1, $2, $3, $4)\n\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t SET confirmed = $4, removed = false, suspended = false`,\n\t\t[opts.userId, opts.workspaceId, opts.roleId, opts.confirmed ?? true],\n\t);\n}\n\nexport async function removeMember(\n\tuserId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_role_user_workspaces\n\t\t SET removed = true\n\t\t WHERE user_id = $1\n\t\t AND workspace_id = $2`,\n\t\t[userId, workspaceId],\n\t);\n}\n\nexport async function getUserRoles(\n\tuserId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IRole[]> {\n\treturn store.query<IRole>(\n\t\t`SELECT\n\t\t r.id,\n\t\t r.name,\n\t\t r.is_system AS \"isSystem\",\n\t\t r.active,\n\t\t r.description,\n\t\t r.workspace_id AS \"workspaceId\"\n\t\t FROM fonderie_role_user_workspaces ruw\n\t\t JOIN fonderie_roles r ON r.id = ruw.role_id\n\t\t WHERE ruw.user_id = $1\n\t\t AND ruw.workspace_id = $2\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false`,\n\t\t[userId, workspaceId],\n\t);\n}\n\nexport async function addRoleToMember(\n\tuserId: string,\n\tworkspaceId: string,\n\troleId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t VALUES ($1, $2, $3, true)\n\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t SET confirmed = true, removed = false, suspended = false`,\n\t\t[userId, workspaceId, roleId],\n\t);\n}\n\nexport async function removeRoleFromMember(\n\tuserId: string,\n\tworkspaceId: string,\n\troleId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tconst remaining = await store.query<{ count: string }>(\n\t\t`SELECT COUNT(*) AS count\n\t\t FROM fonderie_role_user_workspaces\n\t\t WHERE user_id = $1\n\t\t AND workspace_id = $2\n\t\t AND removed = false`,\n\t\t[userId, workspaceId],\n\t);\n\tconst count = parseInt(remaining[0]?.count ?? '0', 10);\n\tif (count <= 1) throw new Error('Cannot remove last role from member');\n\n\tawait store.query(\n\t\t`DELETE FROM fonderie_role_user_workspaces\n\t\t WHERE user_id = $1\n\t\t AND workspace_id = $2\n\t\t AND role_id = $3`,\n\t\t[userId, workspaceId, roleId],\n\t);\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IWorkspace, IWorkspaceAddress, IWorkspaceSettings } from '../types';\n\nconst SELECT_WS = `\n\tid,\n\tname,\n\tslug,\n\ttype,\n\tdescription,\n\tmotto,\n\tphone,\n\tbusiness_type AS \"businessType\",\n\taddress,\n\tplan,\n\towner_id AS \"ownerId\",\n\tis_personal AS \"isPersonal\",\n\tarchived_at AS \"archivedAt\",\n\tarchived_by AS \"archivedBy\",\n\tcreated_at AS \"createdAt\",\n\tupdated_at AS \"updatedAt\"\n`;\n\nconst SELECT_WS_W = `\n\tw.id,\n\tw.name,\n\tw.slug,\n\tw.type,\n\tw.description,\n\tw.motto,\n\tw.phone,\n\tw.business_type AS \"businessType\",\n\tw.address,\n\tw.plan,\n\tw.owner_id AS \"ownerId\",\n\tw.is_personal AS \"isPersonal\",\n\tw.archived_at AS \"archivedAt\",\n\tw.archived_by AS \"archivedBy\",\n\tw.created_at AS \"createdAt\",\n\tw.updated_at AS \"updatedAt\"\n`;\n\nexport async function findWorkspaceById(\n\tid: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst [row] = await store.query<IWorkspace>(\n\t\t`SELECT ${SELECT_WS} FROM fonderie_workspaces WHERE id = $1`,\n\t\t[id],\n\t);\n\treturn row ?? null;\n}\n\nexport async function findWorkspacesByUserId(\n\tuserId: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspace[]> {\n\treturn store.query<IWorkspace>(\n\t\t`SELECT ${SELECT_WS_W}\n\t\t FROM fonderie_workspaces w\n\t\t JOIN fonderie_role_user_workspaces ruw ON ruw.workspace_id = w.id\n\t\t WHERE ruw.user_id = $1\n\t\t AND ruw.removed = false\n\t\t AND ruw.suspended = false\n\t\t GROUP BY w.id\n\t\t ORDER BY w.created_at ASC`,\n\t\t[userId],\n\t);\n}\n\nexport async function createWorkspace(\n\topts: {\n\t\tname: string;\n\t\tslug: string;\n\t\townerId: string;\n\t\ttype?: string;\n\t\tdescription?: string;\n\t\tplan?: string;\n\t},\n\tstore: IStoreAdapter,\n): Promise<IWorkspace> {\n\tconst [workspace] = await store.query<IWorkspace>(\n\t\t`INSERT INTO fonderie_workspaces (name, slug, owner_id, type, description, plan)\n\t\t VALUES ($1, $2, $3, $4, $5, $6)\n\t\t RETURNING ${SELECT_WS}`,\n\t\t[\n\t\t\topts.name,\n\t\t\topts.slug,\n\t\t\topts.ownerId,\n\t\t\topts.type ?? 'ORGANIZATION',\n\t\t\topts.description ?? null,\n\t\t\topts.plan ?? 'free',\n\t\t],\n\t);\n\n\tif (!workspace) throw new Error('Failed to create workspace');\n\treturn workspace;\n}\n\n// Returns the new workspace, or null if the personal workspace already exists (idempotent).\nexport async function createPersonalWorkspace(\n\topts: { name: string; slug: string; ownerId: string },\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst [workspace] = await store.query<IWorkspace>(\n\t\t`INSERT INTO fonderie_workspaces (name, slug, owner_id, type, is_personal)\n\t\t VALUES ($1, $2, $3, 'PERSONAL', true)\n\t\t ON CONFLICT (owner_id) WHERE is_personal = true DO NOTHING\n\t\t RETURNING ${SELECT_WS}`,\n\t\t[opts.name, opts.slug, opts.ownerId],\n\t);\n\treturn workspace ?? null;\n}\n\nexport async function findPersonalWorkspace(\n\tuserId: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst [row] = await store.query<IWorkspace>(\n\t\t`SELECT ${SELECT_WS} FROM fonderie_workspaces\n\t\t WHERE owner_id = $1 AND is_personal = true\n\t\t LIMIT 1`,\n\t\t[userId],\n\t);\n\treturn row ?? null;\n}\n\nexport async function updateWorkspace(\n\tid: string,\n\topts: {\n\t\tname?: string;\n\t\tdescription?: string | null;\n\t\tslug?: string;\n\t\tmotto?: string | null;\n\t\tphone?: string | null;\n\t\tbusinessType?: string | null;\n\t\taddress?: IWorkspaceAddress | null;\n\t},\n\tstore: IStoreAdapter,\n): Promise<IWorkspace | null> {\n\tconst sets: string[] = ['updated_at = now()'];\n\tconst params: unknown[] = [id];\n\n\tif (opts.name !== undefined) {\n\t\tparams.push(opts.name);\n\t\tsets.push(`name = $${params.length}`);\n\t}\n\tif (opts.description !== undefined) {\n\t\tparams.push(opts.description);\n\t\tsets.push(`description = $${params.length}`);\n\t}\n\tif (opts.slug !== undefined) {\n\t\tparams.push(opts.slug);\n\t\tsets.push(`slug = $${params.length}`);\n\t}\n\tif (opts.motto !== undefined) {\n\t\tparams.push(opts.motto);\n\t\tsets.push(`motto = $${params.length}`);\n\t}\n\tif (opts.phone !== undefined) {\n\t\tparams.push(opts.phone);\n\t\tsets.push(`phone = $${params.length}`);\n\t}\n\tif (opts.businessType !== undefined) {\n\t\tparams.push(opts.businessType);\n\t\tsets.push(`business_type = $${params.length}`);\n\t}\n\tif (opts.address !== undefined) {\n\t\tparams.push(JSON.stringify(opts.address ?? {}));\n\t\tsets.push(`address = $${params.length}::jsonb`);\n\t}\n\n\tconst [row] = await store.query<IWorkspace>(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET ${sets.join(', ')}\n\t\t WHERE id = $1\n\t\t RETURNING ${SELECT_WS}`,\n\t\tparams,\n\t);\n\treturn row ?? null;\n}\n\nexport async function archiveWorkspace(\n\tid: string,\n\tbyUser: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET archived_at = now(), archived_by = $2, updated_at = now()\n\t\t WHERE id = $1 AND archived_at IS NULL`,\n\t\t[id, byUser],\n\t);\n}\n\nexport async function restoreWorkspace(id: string, store: IStoreAdapter): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET archived_at = NULL, archived_by = NULL, updated_at = now()\n\t\t WHERE id = $1`,\n\t\t[id],\n\t);\n}\n\nconst SETTINGS_DEFAULTS: IWorkspaceSettings = {\n\tlocale: 'en-US',\n\ttimezone: 'UTC',\n\tcurrency: 'USD',\n\tdateFormat: 'MM/DD/YYYY',\n\ttimeFormat: 'hh:mm A',\n};\n\nexport async function getWorkspaceSettings(\n\tid: string,\n\tstore: IStoreAdapter,\n): Promise<IWorkspaceSettings> {\n\tconst [row] = await store.query<{ settings: Record<string, unknown> }>(\n\t\t`SELECT settings FROM fonderie_workspaces WHERE id = $1`,\n\t\t[id],\n\t);\n\tconst raw = row?.settings ?? {};\n\tconst s = (raw['settings'] as Record<string, unknown> | undefined) ?? raw;\n\n\treturn {\n\t\tlocale: typeof s['locale'] === 'string' ? s['locale'] : SETTINGS_DEFAULTS.locale,\n\t\ttimezone: typeof s['timezone'] === 'string' ? s['timezone'] : SETTINGS_DEFAULTS.timezone,\n\t\tcurrency: typeof s['currency'] === 'string' ? s['currency'] : SETTINGS_DEFAULTS.currency,\n\t\tdateFormat:\n\t\t\ttypeof s['dateFormat'] === 'string' ? s['dateFormat'] : SETTINGS_DEFAULTS.dateFormat,\n\t\ttimeFormat:\n\t\t\ttypeof s['timeFormat'] === 'string' ? s['timeFormat'] : SETTINGS_DEFAULTS.timeFormat,\n\t};\n}\n\nexport async function updateWorkspaceSettings(\n\tid: string,\n\tsettings: Partial<IWorkspaceSettings>,\n\tstore: IStoreAdapter,\n): Promise<IWorkspaceSettings> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspaces\n\t\t SET settings = settings || jsonb_build_object('settings', $2::jsonb),\n\t\t updated_at = now()\n\t\t WHERE id = $1`,\n\t\t[id, JSON.stringify(settings)],\n\t);\n\treturn getWorkspaceSettings(id, store);\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IWorkspacesConfig } from '../config';\nimport type { IWorkspace } from '../types';\nimport { WorkspaceModel } from '../models/workspace.model';\nimport { MemberModel } from '../models/member.model';\nimport { RoleModel } from '../models/role.model';\nimport { toWorkspaceDTO, toSettingsDTO } from '../dtos/workspace';\n\nexport function workspaceController(store: IStoreAdapter, config: IWorkspacesConfig) {\n\tconst workspaces = new WorkspaceModel(store);\n\tconst members = new MemberModel(store);\n\tconst roles = new RoleModel(store);\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.user)\n\t\t\t\treturn setApiResponse(HTTP.UNAUTHORIZED, 'UNAUTHORIZED', 'Authentication required');\n\t\t\tconst list = await workspaces.findByUserId(ctx.user.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACES_FETCHED', 'Workspaces retrieved successfully.', {\n\t\t\t\tworkspaces: list.map(toWorkspaceDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync create(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst name = body?.['name'];\n\t\t\tconst description = body?.['description'];\n\t\t\tconst type = body?.['type'];\n\n\t\t\tif (typeof name !== 'string' || name.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'name is required');\n\t\t\t}\n\n\t\t\tif (type === 'PERSONAL') {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t'Personal workspaces are created automatically',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst slug = name\n\t\t\t\t.toLowerCase()\n\t\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t\t.replace(/^-|-$/g, '');\n\n\t\t\tconst workspace = await store.transaction(async (tx) => {\n\t\t\t\tconst wsOpts: Parameters<typeof workspaces.create>[0] = {\n\t\t\t\t\tname: name.trim(),\n\t\t\t\t\tslug,\n\t\t\t\t\townerId: ctx.user!.id,\n\t\t\t\t\ttype: typeof type === 'string' ? type : 'ORGANIZATION',\n\t\t\t\t};\n\t\t\t\tif (typeof description === 'string') wsOpts.description = description;\n\n\t\t\t\tconst wsModel = new WorkspaceModel(tx);\n\t\t\t\tconst roleModel = new RoleModel(tx);\n\t\t\t\tconst memModel = new MemberModel(tx);\n\n\t\t\t\tconst ws = await wsModel.create(wsOpts);\n\n\t\t\t\tconst adminRole = await roleModel.findSystem('ADMIN');\n\t\t\t\tif (!adminRole) throw new Error('System ADMIN role not found');\n\t\t\t\tawait memModel.add({ userId: ctx.user!.id, workspaceId: ws.id, roleId: adminRole.id });\n\n\t\t\t\treturn ws;\n\t\t\t});\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'WORKSPACE_CREATED', 'Workspace created successfully.', {\n\t\t\t\tworkspace: toWorkspaceDTO(workspace),\n\t\t\t});\n\t\t},\n\n\t\tasync get(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_FETCHED', 'Workspace retrieved successfully.', {\n\t\t\t\tworkspace: toWorkspaceDTO(ctx.workspace as IWorkspace),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst opts: Parameters<typeof workspaces.update>[1] = {};\n\n\t\t\tif (typeof body?.['name'] === 'string') opts.name = body['name'].trim();\n\t\t\tif (body?.['description'] !== undefined)\n\t\t\t\topts.description = typeof body['description'] === 'string' ? body['description'] : null;\n\t\t\tif (body?.['motto'] !== undefined)\n\t\t\t\topts.motto = typeof body['motto'] === 'string' ? body['motto'] : null;\n\t\t\tif (body?.['phone'] !== undefined)\n\t\t\t\topts.phone = typeof body['phone'] === 'string' ? body['phone'].trim() : null;\n\t\t\tif (body?.['businessType'] !== undefined)\n\t\t\t\topts.businessType = typeof body['businessType'] === 'string' ? body['businessType'] : null;\n\t\t\tif (body?.['address'] !== undefined && typeof body['address'] === 'object')\n\t\t\t\topts.address = (body['address'] ?? null) as NonNullable<\n\t\t\t\t\tParameters<typeof workspaces.update>[1]\n\t\t\t\t>['address'] & (object | null);\n\n\t\t\tconst workspace = await workspaces.update(ctx.workspace.id, opts);\n\t\t\tif (!workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_UPDATED', 'Workspace updated successfully.', {\n\t\t\t\tworkspace: toWorkspaceDTO(workspace),\n\t\t\t});\n\t\t},\n\n\t\tasync archive(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\tif (ctx.workspace.isPersonal) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.FORBIDDEN,\n\t\t\t\t\t'FORBIDDEN',\n\t\t\t\t\t'Personal workspaces cannot be archived',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tawait workspaces.archive(ctx.workspace.id, ctx.user!.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_ARCHIVED', 'Workspace archived successfully.');\n\t\t},\n\n\t\tasync restore(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tawait workspaces.restore(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'WORKSPACE_RESTORED', 'Workspace restored successfully.');\n\t\t},\n\n\t\tasync getSettings(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst settings = await workspaces.getSettings(ctx.workspace.id);\n\t\t\treturn setApiResponse(\n\t\t\t\tHTTP.OK,\n\t\t\t\t'SETTINGS_FETCHED',\n\t\t\t\t'Workspace settings retrieved successfully.',\n\t\t\t\t{\n\t\t\t\t\tsettings: toSettingsDTO(settings),\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tasync updateSettings(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tif (!body || Object.keys(body).length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'No settings provided');\n\t\t\t}\n\n\t\t\tconst patch: Record<string, string> = {};\n\t\t\tfor (const key of ['locale', 'timezone', 'currency', 'dateFormat', 'timeFormat'] as const) {\n\t\t\t\tif (typeof body[key] === 'string') patch[key] = body[key] as string;\n\t\t\t}\n\n\t\t\tconst settings = await workspaces.updateSettings(ctx.workspace.id, patch);\n\t\t\treturn setApiResponse(\n\t\t\t\tHTTP.OK,\n\t\t\t\t'SETTINGS_UPDATED',\n\t\t\t\t'Workspace settings updated successfully.',\n\t\t\t\t{\n\t\t\t\t\tsettings: toSettingsDTO(settings),\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t};\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IWorkspace, IWorkspaceSettings } from '../types';\nimport {\n\tfindWorkspaceById,\n\tfindWorkspacesByUserId,\n\tcreateWorkspace,\n\tcreatePersonalWorkspace,\n\tfindPersonalWorkspace,\n\tupdateWorkspace,\n\tarchiveWorkspace,\n\trestoreWorkspace,\n\tgetWorkspaceSettings,\n\tupdateWorkspaceSettings,\n} from '../services/workspaces';\n\nexport class WorkspaceModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tfindById(id: string): Promise<IWorkspace | null> {\n\t\treturn findWorkspaceById(id, this.store);\n\t}\n\n\tfindByUserId(userId: string): Promise<IWorkspace[]> {\n\t\treturn findWorkspacesByUserId(userId, this.store);\n\t}\n\n\tcreate(opts: Parameters<typeof createWorkspace>[0]): Promise<IWorkspace> {\n\t\treturn createWorkspace(opts, this.store);\n\t}\n\n\tcreatePersonal(opts: Parameters<typeof createPersonalWorkspace>[0]): Promise<IWorkspace | null> {\n\t\treturn createPersonalWorkspace(opts, this.store);\n\t}\n\n\tfindPersonal(userId: string): Promise<IWorkspace | null> {\n\t\treturn findPersonalWorkspace(userId, this.store);\n\t}\n\n\tupdate(id: string, opts: Parameters<typeof updateWorkspace>[1]): Promise<IWorkspace | null> {\n\t\treturn updateWorkspace(id, opts, this.store);\n\t}\n\n\tarchive(id: string, byUser: string): Promise<void> {\n\t\treturn archiveWorkspace(id, byUser, this.store);\n\t}\n\n\trestore(id: string): Promise<void> {\n\t\treturn restoreWorkspace(id, this.store);\n\t}\n\n\tgetSettings(id: string): Promise<IWorkspaceSettings> {\n\t\treturn getWorkspaceSettings(id, this.store);\n\t}\n\n\tupdateSettings(id: string, settings: Partial<IWorkspaceSettings>): Promise<IWorkspaceSettings> {\n\t\treturn updateWorkspaceSettings(id, settings, this.store);\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IMember, IRole } from '../types';\nimport {\n\tgetMember,\n\tlistMembers,\n\taddMember,\n\tremoveMember,\n\tgetUserRoles,\n\taddRoleToMember,\n\tremoveRoleFromMember,\n} from '../services/members';\n\nexport class MemberModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tget(userId: string, workspaceId: string): Promise<IMember | null> {\n\t\treturn getMember(userId, workspaceId, this.store);\n\t}\n\n\tlist(workspaceId: string): Promise<IMember[]> {\n\t\treturn listMembers(workspaceId, this.store);\n\t}\n\n\tadd(opts: Parameters<typeof addMember>[0]): Promise<void> {\n\t\treturn addMember(opts, this.store);\n\t}\n\n\tremove(userId: string, workspaceId: string): Promise<void> {\n\t\treturn removeMember(userId, workspaceId, this.store);\n\t}\n\n\tgetUserRoles(userId: string, workspaceId: string): Promise<IRole[]> {\n\t\treturn getUserRoles(userId, workspaceId, this.store);\n\t}\n\n\taddRole(userId: string, workspaceId: string, roleId: string): Promise<void> {\n\t\treturn addRoleToMember(userId, workspaceId, roleId, this.store);\n\t}\n\n\tremoveRole(userId: string, workspaceId: string, roleId: string): Promise<void> {\n\t\treturn removeRoleFromMember(userId, workspaceId, roleId, this.store);\n\t}\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IRole } from '../types';\n\nconst SELECT_ROLE = `\n\tid,\n\tname,\n\tis_system AS \"isSystem\",\n\tactive,\n\tdescription,\n\tworkspace_id AS \"workspaceId\"\n`;\n\nexport async function createRole(\n\topts: { name: string; workspaceId: string; description?: string },\n\tstore: IStoreAdapter,\n): Promise<IRole> {\n\tconst [role] = await store.query<IRole>(\n\t\t`INSERT INTO fonderie_roles (name, workspace_id, description)\n\t\t VALUES ($1, $2, $3)\n\t\t RETURNING ${SELECT_ROLE}`,\n\t\t[opts.name, opts.workspaceId, opts.description ?? null],\n\t);\n\tif (!role) throw new Error('Failed to create role');\n\treturn role;\n}\n\nexport async function findSystemRole(name: string, store: IStoreAdapter): Promise<IRole | null> {\n\tconst [row] = await store.query<IRole>(\n\t\t`SELECT ${SELECT_ROLE} FROM fonderie_roles WHERE name = $1 AND is_system = true LIMIT 1`,\n\t\t[name],\n\t);\n\treturn row ?? null;\n}\n\nexport async function getRoleById(id: string, store: IStoreAdapter): Promise<IRole | null> {\n\tconst [row] = await store.query<IRole>(\n\t\t`SELECT ${SELECT_ROLE} FROM fonderie_roles WHERE id = $1`,\n\t\t[id],\n\t);\n\treturn row ?? null;\n}\n\nexport async function listWorkspaceRoles(\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IRole[]> {\n\treturn store.query<IRole>(\n\t\t`SELECT ${SELECT_ROLE}\n\t\t FROM fonderie_roles\n\t\t WHERE workspace_id = $1 OR is_system = true\n\t\t ORDER BY is_system DESC, name ASC`,\n\t\t[workspaceId],\n\t);\n}\n\nexport async function updateRole(\n\tid: string,\n\topts: { name?: string; description?: string | null; active?: boolean },\n\tstore: IStoreAdapter,\n): Promise<IRole | null> {\n\tconst sets: string[] = [];\n\tconst params: unknown[] = [id];\n\n\tif (opts.name !== undefined) {\n\t\tparams.push(opts.name);\n\t\tsets.push(`name = $${params.length}`);\n\t}\n\tif (opts.description !== undefined) {\n\t\tparams.push(opts.description);\n\t\tsets.push(`description = $${params.length}`);\n\t}\n\tif (opts.active !== undefined) {\n\t\tparams.push(opts.active);\n\t\tsets.push(`active = $${params.length}`);\n\t}\n\n\tif (sets.length === 0) return getRoleById(id, store);\n\n\tconst [row] = await store.query<IRole>(\n\t\t`UPDATE fonderie_roles\n\t\t SET ${sets.join(', ')}\n\t\t WHERE id = $1 AND is_system = false\n\t\t RETURNING ${SELECT_ROLE}`,\n\t\tparams,\n\t);\n\treturn row ?? null;\n}\n\nexport async function deleteRole(\n\tid: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`DELETE FROM fonderie_roles\n\t\t WHERE id = $1 AND workspace_id = $2 AND is_system = false`,\n\t\t[id, workspaceId],\n\t);\n}\n\nexport async function setRolePermissions(\n\troleId: string,\n\tworkspaceId: string,\n\tpermissions: Array<{\n\t\tpermissionKey: string;\n\t\tcanCreate: boolean;\n\t\tcanRead: boolean;\n\t\tcanUpdate: boolean;\n\t\tcanDelete: boolean;\n\t}>,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tif (permissions.length === 0) {\n\t\tawait store.query(\n\t\t\t`DELETE FROM fonderie_role_permissions WHERE role_id = $1 AND workspace_id = $2`,\n\t\t\t[roleId, workspaceId],\n\t\t);\n\t\treturn;\n\t}\n\n\tawait store.transaction(async (tx) => {\n\t\tawait tx.query(\n\t\t\t`DELETE FROM fonderie_role_permissions WHERE role_id = $1 AND workspace_id = $2`,\n\t\t\t[roleId, workspaceId],\n\t\t);\n\n\t\tfor (const p of permissions) {\n\t\t\tawait tx.query(\n\t\t\t\t`INSERT INTO fonderie_role_permissions\n\t\t\t\t (role_id, workspace_id, permission_key, can_create, can_read, can_update, can_delete)\n\t\t\t\t VALUES ($1, $2, $3, $4, $5, $6, $7)\n\t\t\t\t ON CONFLICT (role_id, permission_key)\n\t\t\t\t DO UPDATE SET\n\t\t\t\t can_create = $4, can_read = $5,\n\t\t\t\t can_update = $6, can_delete = $7`,\n\t\t\t\t[roleId, workspaceId, p.permissionKey, p.canCreate, p.canRead, p.canUpdate, p.canDelete],\n\t\t\t);\n\t\t}\n\t});\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IRole } from '../types';\nimport {\n\tcreateRole,\n\tfindSystemRole,\n\tgetRoleById,\n\tlistWorkspaceRoles,\n\tupdateRole,\n\tdeleteRole,\n\tsetRolePermissions,\n} from '../services/roles';\n\nexport class RoleModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tcreate(opts: Parameters<typeof createRole>[0]): Promise<IRole> {\n\t\treturn createRole(opts, this.store);\n\t}\n\n\tfindSystem(name: string): Promise<IRole | null> {\n\t\treturn findSystemRole(name, this.store);\n\t}\n\n\tfindById(id: string): Promise<IRole | null> {\n\t\treturn getRoleById(id, this.store);\n\t}\n\n\tlist(workspaceId: string): Promise<IRole[]> {\n\t\treturn listWorkspaceRoles(workspaceId, this.store);\n\t}\n\n\tupdate(id: string, opts: Parameters<typeof updateRole>[1]): Promise<IRole | null> {\n\t\treturn updateRole(id, opts, this.store);\n\t}\n\n\tdelete(id: string, workspaceId: string): Promise<void> {\n\t\treturn deleteRole(id, workspaceId, this.store);\n\t}\n\n\tsetPermissions(\n\t\troleId: string,\n\t\tworkspaceId: string,\n\t\tpermissions: Parameters<typeof setRolePermissions>[2],\n\t): Promise<void> {\n\t\treturn setRolePermissions(roleId, workspaceId, permissions, this.store);\n\t}\n}\n","import { stringOrEmpty, booleanOrFalse } from '@fonderie/core/parser';\n\nimport type { IWorkspace, IRole, IMember, IInvitation, IWorkspaceSettings } from '../types';\n\nexport interface IWorkspaceAddressDTO {\n\tline1: string;\n\tline2: string;\n\tcity: string;\n\tstate: string;\n\tzip: string;\n\tcountry: string;\n}\n\nexport interface IWorkspaceDTO {\n\tid: string;\n\tname: string;\n\tslug: string;\n\ttype: string;\n\tdescription: string;\n\tmotto: string;\n\tphone: string;\n\tbusinessType: string;\n\taddress: IWorkspaceAddressDTO;\n\tplan: string;\n\townerId: string;\n\tisPersonal: boolean;\n\tisArchived: boolean;\n\tarchivedAt: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface IRoleDTO {\n\tid: string;\n\tname: string;\n\tisSystem: boolean;\n\tactive: boolean;\n\tdescription: string;\n\tworkspaceId: string;\n}\n\nexport interface IMemberDTO {\n\tuserId: string;\n\tworkspaceId: string;\n\troleId: string;\n\troleName: string;\n\tconfirmed: boolean;\n\tcreatedAt: string;\n}\n\nexport interface IInvitationDTO {\n\tid: string;\n\tworkspaceId: string;\n\temail: string;\n\troleId: string;\n\ttoken: string;\n\tstatus: string;\n\texpiresAt: string;\n\tcreatedAt: string;\n}\n\nexport interface IWorkspaceSettingsDTO {\n\tlocale: string;\n\ttimezone: string;\n\tcurrency: string;\n\tdateFormat: string;\n\ttimeFormat: string;\n}\n\nexport function toWorkspaceDTO(ws: IWorkspace): IWorkspaceDTO {\n\tconst addr = ws.address ?? {};\n\treturn {\n\t\tid: stringOrEmpty(ws.id),\n\t\tname: stringOrEmpty(ws.name),\n\t\tslug: stringOrEmpty(ws.slug),\n\t\ttype: stringOrEmpty(ws.type),\n\t\tdescription: stringOrEmpty(ws.description),\n\t\tmotto: stringOrEmpty(ws.motto),\n\t\tphone: stringOrEmpty(ws.phone),\n\t\tbusinessType: stringOrEmpty(ws.businessType),\n\t\taddress: {\n\t\t\tline1: stringOrEmpty(addr.line1),\n\t\t\tline2: stringOrEmpty(addr.line2),\n\t\t\tcity: stringOrEmpty(addr.city),\n\t\t\tstate: stringOrEmpty(addr.state),\n\t\t\tzip: stringOrEmpty(addr.zip),\n\t\t\tcountry: stringOrEmpty(addr.country),\n\t\t},\n\t\tplan: stringOrEmpty(ws.plan),\n\t\townerId: stringOrEmpty(ws.ownerId),\n\t\tisPersonal: booleanOrFalse(ws.isPersonal),\n\t\tisArchived: ws.archivedAt !== null,\n\t\tarchivedAt: stringOrEmpty(ws.archivedAt),\n\t\tcreatedAt: stringOrEmpty(ws.createdAt),\n\t\tupdatedAt: stringOrEmpty(ws.updatedAt),\n\t};\n}\n\nexport function toRoleDTO(role: IRole): IRoleDTO {\n\treturn {\n\t\tid: stringOrEmpty(role.id),\n\t\tname: stringOrEmpty(role.name),\n\t\tisSystem: booleanOrFalse(role.isSystem),\n\t\tactive: role.active !== false,\n\t\tdescription: stringOrEmpty(role.description),\n\t\tworkspaceId: stringOrEmpty(role.workspaceId),\n\t};\n}\n\nexport function toMemberDTO(m: IMember): IMemberDTO {\n\treturn {\n\t\tuserId: stringOrEmpty(m.userId),\n\t\tworkspaceId: stringOrEmpty(m.workspaceId),\n\t\troleId: stringOrEmpty(m.roleId),\n\t\troleName: stringOrEmpty(m.roleName),\n\t\tconfirmed: booleanOrFalse(m.confirmed),\n\t\tcreatedAt: stringOrEmpty(m.createdAt),\n\t};\n}\n\nexport function toInvitationDTO(inv: IInvitation): IInvitationDTO {\n\treturn {\n\t\tid: stringOrEmpty(inv.id),\n\t\tworkspaceId: stringOrEmpty(inv.workspaceId),\n\t\temail: stringOrEmpty(inv.email),\n\t\troleId: stringOrEmpty(inv.roleId),\n\t\ttoken: stringOrEmpty(inv.token),\n\t\tstatus: stringOrEmpty(inv.status),\n\t\texpiresAt: stringOrEmpty(inv.expiresAt),\n\t\tcreatedAt: stringOrEmpty(inv.createdAt),\n\t};\n}\n\nexport function toSettingsDTO(s: IWorkspaceSettings): IWorkspaceSettingsDTO {\n\treturn {\n\t\tlocale: stringOrEmpty(s.locale),\n\t\ttimezone: stringOrEmpty(s.timezone),\n\t\tcurrency: stringOrEmpty(s.currency),\n\t\tdateFormat: stringOrEmpty(s.dateFormat),\n\t\ttimeFormat: stringOrEmpty(s.timeFormat),\n\t};\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { MemberModel } from '../models/member.model';\nimport { toMemberDTO, toRoleDTO } from '../dtos/workspace';\n\nexport function memberController(store: IStoreAdapter) {\n\tconst members = new MemberModel(store);\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst list = await members.list(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'MEMBERS_FETCHED', 'Members retrieved successfully.', {\n\t\t\t\tmembers: list.map(toMemberDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\tif (ctx.workspace.isPersonal) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.FORBIDDEN,\n\t\t\t\t\t'FORBIDDEN',\n\t\t\t\t\t'Personal workspaces do not support member management',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\t\t\tif (userId === ctx.user?.id) {\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'INVALID_OPERATION', 'Cannot remove yourself');\n\t\t\t}\n\n\t\t\tawait members.remove(userId, ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'MEMBER_REMOVED', 'Member removed successfully.');\n\t\t},\n\n\t\tasync getUserRoles(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\n\t\t\tconst roles = await members.getUserRoles(userId, ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLES_FETCHED', 'Member roles retrieved successfully.', {\n\t\t\t\troles: roles.map(toRoleDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync addRole(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\t\t\tconst roleId = (body?.['roleId'] ?? params?.['roleId']) as string | undefined;\n\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\t\t\tif (!roleId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\n\t\t\tawait members.addRole(userId, ctx.workspace.id, roleId);\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_ASSIGNED', 'Role assigned successfully.');\n\t\t},\n\n\t\tasync removeRole(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst userId = params?.['userId'];\n\t\t\tconst roleId = params?.['roleId'];\n\n\t\t\tif (!userId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'userId is required');\n\t\t\tif (!roleId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\n\t\t\ttry {\n\t\t\t\tawait members.removeRole(userId, ctx.workspace.id, roleId);\n\t\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_REMOVED', 'Role removed successfully.');\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : 'Failed';\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'OPERATION_FAILED', message);\n\t\t\t}\n\t\t},\n\t};\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport { RoleModel } from '../models/role.model';\nimport { toRoleDTO } from '../dtos/workspace';\n\nexport function roleController(store: IStoreAdapter) {\n\tconst roles = new RoleModel(store);\n\n\treturn {\n\t\tasync create(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst name = body?.['name'];\n\t\t\tconst description = body?.['description'];\n\n\t\t\tif (typeof name !== 'string' || name.trim().length === 0) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'name is required');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst opts: Parameters<typeof roles.create>[0] = {\n\t\t\t\t\tname: name.trim(),\n\t\t\t\t\tworkspaceId: ctx.workspace.id,\n\t\t\t\t};\n\n\t\t\t\tif (typeof description === 'string') {\n\t\t\t\t\topts.description = description;\n\t\t\t\t}\n\n\t\t\t\tconst role = await roles.create(opts);\n\t\t\t\treturn setApiResponse(HTTP.CREATED, 'ROLE_CREATED', 'Role created successfully.', {\n\t\t\t\t\trole: toRoleDTO(role),\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : 'Failed to create role';\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'OPERATION_FAILED', message);\n\t\t\t}\n\t\t},\n\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst list = await roles.list(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLES_FETCHED', 'Roles retrieved successfully.', {\n\t\t\t\troles: list.map(toRoleDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync get(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tconst role = await roles.findById(roleId);\n\t\t\tif (!role) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Role not found');\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_FETCHED', 'Role retrieved successfully.', {\n\t\t\t\trole: toRoleDTO(role),\n\t\t\t});\n\t\t},\n\n\t\tasync update(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tconst opts: { name?: string; description?: string | null; active?: boolean } = {};\n\n\t\t\tif (typeof body?.['name'] === 'string') {\n\t\t\t\topts.name = body['name'];\n\t\t\t}\n\n\t\t\tif (typeof body?.['description'] === 'string') {\n\t\t\t\topts.description = body['description'];\n\t\t\t}\n\n\t\t\tif (body?.['description'] === null) {\n\t\t\t\topts.description = null;\n\t\t\t}\n\n\t\t\tif (typeof body?.['active'] === 'boolean') {\n\t\t\t\topts.active = body['active'];\n\t\t\t}\n\n\t\t\tconst role = await roles.update(roleId, opts);\n\t\t\tif (!role) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Role not found or is a system role');\n\t\t\t}\n\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_UPDATED', 'Role updated successfully.', {\n\t\t\t\trole: toRoleDTO(role),\n\t\t\t});\n\t\t},\n\n\t\tasync remove(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tawait roles.delete(roleId, ctx.workspace.id);\n\n\t\t\treturn setApiResponse(HTTP.OK, 'ROLE_DELETED', 'Role deleted successfully.');\n\t\t},\n\n\t\tasync setPermissions(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) {\n\t\t\t\treturn setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\t}\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst roleId = params?.['roleId'];\n\t\t\tif (!roleId) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'roleId is required');\n\t\t\t}\n\n\t\t\tconst perms = body?.['permissions'];\n\t\t\tif (!Array.isArray(perms)) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.UNPROCESSABLE,\n\t\t\t\t\t'INVALID_PARAMETER',\n\t\t\t\t\t'permissions array is required',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst normalized = perms\n\t\t\t\t.map((p: unknown) => {\n\t\t\t\t\tconst perm = p as Record<string, unknown>;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpermissionKey: String(perm['permissionKey'] ?? ''),\n\t\t\t\t\t\tcanCreate: Boolean(perm['canCreate']),\n\t\t\t\t\t\tcanRead: Boolean(perm['canRead']),\n\t\t\t\t\t\tcanUpdate: Boolean(perm['canUpdate']),\n\t\t\t\t\t\tcanDelete: Boolean(perm['canDelete']),\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.filter((p) => p.permissionKey.length > 0);\n\n\t\t\tawait roles.setPermissions(roleId, ctx.workspace.id, normalized);\n\n\t\t\treturn setApiResponse(HTTP.OK, 'PERMISSIONS_SET', 'Role permissions updated successfully.');\n\t\t},\n\t};\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { IFonderieContext } from '@fonderie/core';\nimport type { ICourierMessage } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport type { EventBus } from '@fonderie/events';\nimport { NOTIFICATION_EVENT } from '@fonderie/events';\n\nimport { getPlanLimit } from '@fonderie/billing';\nimport { MESSAGE_KEYS } from '../config';\nimport { InvitationModel } from '../models/invitation.model';\nimport { toInvitationDTO } from '../dtos/workspace';\n\nexport function invitationController(store: IStoreAdapter, ttl: string, bus?: EventBus) {\n\tconst invitations = new InvitationModel(store);\n\n\treturn {\n\t\tasync list(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst list = await invitations.list(ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'INVITATIONS_FETCHED', 'Invitations retrieved successfully.', {\n\t\t\t\tinvitations: list.map(toInvitationDTO),\n\t\t\t});\n\t\t},\n\n\t\tasync invite(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\t\t\tif (ctx.workspace.isPersonal) {\n\t\t\t\treturn setApiResponse(\n\t\t\t\t\tHTTP.FORBIDDEN,\n\t\t\t\t\t'FORBIDDEN',\n\t\t\t\t\t'Personal workspaces do not support invitations',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst body = ctx.meta['body'] as unknown;\n\t\t\tconst entries = Array.isArray(body) ? body as Record<string, unknown>[] : [body as Record<string, unknown>];\n\n\t\t\tif (!entries.length) {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'at least one invite is required');\n\t\t\t}\n\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (typeof entry?.['email'] !== 'string') {\n\t\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'email is required for every invite');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Seat limit — enforced automatically when billing module is registered.\n\t\t\t// getPlanLimit reads from ctx.meta['billing'] (fail-open when billing absent).\n\t\t\tconst seatLimit = getPlanLimit(ctx, 'seats');\n\t\t\tif (seatLimit !== null) {\n\t\t\t\tconst [countRow] = await store.query<{ count: string }>(\n\t\t\t\t\t`SELECT COUNT(*) AS count FROM fonderie_role_user_workspaces\n\t\t\t\t\t WHERE workspace_id = $1 AND removed = false AND suspended = false`,\n\t\t\t\t\t[ctx.workspace.id],\n\t\t\t\t);\n\t\t\t\tconst total = parseInt(countRow!.count, 10);\n\t\t\t\tconst occupied = ctx.workspace.isPersonal ? total : Math.max(0, total - 1);\n\t\t\t\tif (occupied + entries.length > seatLimit) {\n\t\t\t\t\treturn setApiResponse(\n\t\t\t\t\t\tHTTP.PAYMENT_REQUIRED,\n\t\t\t\t\t\t'SEAT_LIMIT_REACHED',\n\t\t\t\t\t\t`Your plan allows ${seatLimit} seat${seatLimit === 1 ? '' : 's'}. Upgrade to invite more members.`,\n\t\t\t\t\t\t{ limit: seatLimit },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Resolve default role once for entries that omit roleId.\n\t\t\t// Invitations must never default to a privileged role: fall back to\n\t\t\t// the seeded system GUEST role (least privilege). Granting anything\n\t\t\t// more requires an explicit roleId from GET /workspaces/roles.\n\t\t\tlet defaultRoleId: string | undefined;\n\t\t\tconst needsDefault = entries.some((e) => !e['roleId']);\n\t\t\tif (needsDefault) {\n\t\t\t\tconst [row] = await store.query<{ id: string }>(\n\t\t\t\t\t`SELECT id FROM fonderie_roles\n\t\t\t\t\t WHERE name = 'GUEST' AND workspace_id IS NULL AND is_system = true\n\t\t\t\t\t LIMIT 1`,\n\t\t\t\t);\n\t\t\t\tdefaultRoleId = row?.id;\n\t\t\t\tif (!defaultRoleId) {\n\t\t\t\t\treturn setApiResponse(HTTP.SERVER_ERROR, 'SERVER_ERROR', 'Default role not found');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst results = await Promise.all(\n\t\t\t\tentries.map(async (entry) => {\n\t\t\t\t\tconst email = entry['email'] as string;\n\t\t\t\t\tconst resolvedRoleId = (entry['roleId'] as string | undefined) ?? defaultRoleId!;\n\n\t\t\t\t\tconst invitation = await invitations.create({\n\t\t\t\t\t\tworkspaceId: ctx.workspace!.id,\n\t\t\t\t\t\temail,\n\t\t\t\t\t\troleId: resolvedRoleId,\n\t\t\t\t\t\tttl,\n\t\t\t\t\t});\n\n\t\t\t\t\tbus\n\t\t\t\t\t\t?.emit(NOTIFICATION_EVENT, {\n\t\t\t\t\t\t\ttype: MESSAGE_KEYS.workspaceInvitation,\n\t\t\t\t\t\t\trecipient: { email, phone: null, deviceToken: null },\n\t\t\t\t\t\t\tdata: { token: invitation.token, pin: invitation.pin },\n\t\t\t\t\t\t} satisfies ICourierMessage)\n\t\t\t\t\t\t.catch(() => {});\n\n\t\t\t\t\treturn { invitationId: invitation.id, email };\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\treturn setApiResponse(HTTP.CREATED, 'INVITATIONS_SENT', 'Invitations sent successfully.', {\n\t\t\t\tinvitations: results,\n\t\t\t});\n\t\t},\n\n\t\tasync cancel(ctx: IFonderieContext): Promise<Response> {\n\t\t\tif (!ctx.workspace) return setApiResponse(HTTP.NOT_FOUND, 'NOT_FOUND', 'Workspace not found');\n\n\t\t\tconst params = ctx.meta['params'] as Record<string, string> | undefined;\n\t\t\tconst invitationId = params?.['inviteId'];\n\n\t\t\tif (!invitationId)\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'inviteId is required');\n\n\t\t\tawait invitations.cancel(invitationId, ctx.workspace.id);\n\t\t\treturn setApiResponse(HTTP.OK, 'INVITATION_CANCELLED', 'Invitation cancelled successfully.');\n\t\t},\n\n\t\tasync accept(ctx: IFonderieContext): Promise<Response> {\n\t\t\tconst body = ctx.meta['body'] as Record<string, unknown> | undefined;\n\t\t\tconst pin = body?.['pin'];\n\n\t\t\tif (typeof pin !== 'string') {\n\t\t\t\treturn setApiResponse(HTTP.UNPROCESSABLE, 'INVALID_PARAMETER', 'pin is required');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst { workspaceId } = await invitations.acceptByPin({ pin, userId: ctx.user!.id });\n\t\t\t\treturn setApiResponse(HTTP.OK, 'INVITATION_ACCEPTED', 'Invitation accepted successfully.', {\n\t\t\t\t\tworkspaceId,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : 'Invalid invitation';\n\t\t\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'INVITATION_FAILED', message);\n\t\t\t}\n\t\t},\n\t};\n}\n","import { randomBytes } from 'node:crypto';\n\nimport type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IInvitation } from '../types';\n\nfunction generateToken(): string {\n\treturn randomBytes(32).toString('hex');\n}\n\nfunction generatePin(): string {\n\treturn Math.floor(100000 + Math.random() * 900000).toString();\n}\n\nfunction parseTtl(ttl: string): number {\n\tconst units: Record<string, number> = {\n\t\ts: 1000,\n\t\tm: 60_000,\n\t\th: 3_600_000,\n\t\td: 86_400_000,\n\t};\n\tconst match = ttl.match(/^(\\d+)([smhd])$/);\n\tif (!match) return 7 * 86_400_000;\n\tconst [, n, unit] = match;\n\treturn parseInt(n!, 10) * (units[unit!] ?? 0);\n}\n\nconst SELECT_INV = `\n\tid,\n\tworkspace_id AS \"workspaceId\",\n\temail,\n\trole_id AS \"roleId\",\n\ttoken,\n\tpin,\n\tstatus,\n\texpires_at AS \"expiresAt\",\n\tcreated_at AS \"createdAt\"\n`;\n\nexport async function createInvitation(\n\topts: { workspaceId: string; email: string; roleId: string; ttl?: string },\n\tstore: IStoreAdapter,\n): Promise<IInvitation> {\n\tconst token = generateToken();\n\tconst pin = generatePin();\n\tconst expiresAt = new Date(Date.now() + parseTtl(opts.ttl ?? '7d'));\n\n\tconst [invitation] = await store.query<IInvitation>(\n\t\t`INSERT INTO fonderie_workspace_invitations\n\t\t (workspace_id, email, role_id, token, pin, expires_at)\n\t\t VALUES ($1, $2, $3, $4, $5, $6)\n\t\t ON CONFLICT DO NOTHING\n\t\t RETURNING ${SELECT_INV}`,\n\t\t[opts.workspaceId, opts.email, opts.roleId, token, pin, expiresAt],\n\t);\n\n\tif (!invitation) {\n\t\t// Already pending — update it\n\t\tconst [updated] = await store.query<IInvitation>(\n\t\t\t`UPDATE fonderie_workspace_invitations\n\t\t\t SET token = $4, pin = $5, expires_at = $6, role_id = $3, status = 'PENDING'\n\t\t\t WHERE workspace_id = $1 AND email = $2 AND status = 'PENDING'\n\t\t\t RETURNING ${SELECT_INV}`,\n\t\t\t[opts.workspaceId, opts.email, opts.roleId, token, pin, expiresAt],\n\t\t);\n\t\tif (!updated) throw new Error('Failed to create invitation');\n\t\treturn updated;\n\t}\n\n\treturn invitation;\n}\n\nexport async function listInvitations(\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<IInvitation[]> {\n\treturn store.query<IInvitation>(\n\t\t`SELECT ${SELECT_INV}\n\t\t FROM fonderie_workspace_invitations\n\t\t WHERE workspace_id = $1 AND status = 'PENDING'\n\t\t ORDER BY created_at DESC`,\n\t\t[workspaceId],\n\t);\n}\n\nexport async function cancelInvitation(\n\tinvitationId: string,\n\tworkspaceId: string,\n\tstore: IStoreAdapter,\n): Promise<void> {\n\tawait store.query(\n\t\t`UPDATE fonderie_workspace_invitations\n\t\t SET status = 'CANCELLED'\n\t\t WHERE id = $1 AND workspace_id = $2 AND status = 'PENDING'`,\n\t\t[invitationId, workspaceId],\n\t);\n}\n\nexport async function acceptInvitationByPin(\n\topts: { pin: string; userId: string },\n\tstore: IStoreAdapter,\n): Promise<{ workspaceId: string; roleId: string }> {\n\tconst [inv] = await store.query<{\n\t\tid: string;\n\t\tworkspaceId: string;\n\t\troleId: string;\n\t\texpiresAt: string;\n\t}>(\n\t\t`SELECT id, workspace_id AS \"workspaceId\", role_id AS \"roleId\", expires_at AS \"expiresAt\"\n\t\t FROM fonderie_workspace_invitations\n\t\t WHERE pin = $1 AND status = 'PENDING'`,\n\t\t[opts.pin],\n\t);\n\n\tif (!inv) throw new Error('Invalid PIN');\n\tif (new Date() > new Date(inv.expiresAt)) throw new Error('Invitation expired');\n\n\tawait store.transaction(async (tx) => {\n\t\tawait Promise.all([\n\t\t\ttx.query(\n\t\t\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t\t\t VALUES ($1, $2, $3, true)\n\t\t\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t\t\t SET confirmed = true, removed = false`,\n\t\t\t\t[opts.userId, inv.workspaceId, inv.roleId],\n\t\t\t),\n\t\t\ttx.query(`UPDATE fonderie_workspace_invitations SET status = 'ACCEPTED' WHERE id = $1`, [\n\t\t\t\tinv.id,\n\t\t\t]),\n\t\t]);\n\t});\n\n\treturn { workspaceId: inv.workspaceId, roleId: inv.roleId };\n}\n\nexport async function acceptInvitationByToken(\n\ttoken: string,\n\tuserId: string,\n\tstore: IStoreAdapter,\n): Promise<{ workspaceId: string; roleId: string }> {\n\tconst [inv] = await store.query<{\n\t\tid: string;\n\t\tworkspaceId: string;\n\t\troleId: string;\n\t\texpiresAt: string;\n\t}>(\n\t\t`SELECT id, workspace_id AS \"workspaceId\", role_id AS \"roleId\", expires_at AS \"expiresAt\"\n\t\t FROM fonderie_workspace_invitations\n\t\t WHERE token = $1 AND status = 'PENDING'`,\n\t\t[token],\n\t);\n\n\tif (!inv) throw new Error('Invalid token');\n\tif (new Date() > new Date(inv.expiresAt)) throw new Error('Invitation expired');\n\n\tawait store.transaction(async (tx) => {\n\t\tawait Promise.all([\n\t\t\ttx.query(\n\t\t\t\t`INSERT INTO fonderie_role_user_workspaces (user_id, workspace_id, role_id, confirmed)\n\t\t\t\t VALUES ($1, $2, $3, true)\n\t\t\t\t ON CONFLICT (user_id, workspace_id, role_id) DO UPDATE\n\t\t\t\t SET confirmed = true, removed = false`,\n\t\t\t\t[userId, inv.workspaceId, inv.roleId],\n\t\t\t),\n\t\t\ttx.query(`UPDATE fonderie_workspace_invitations SET status = 'ACCEPTED' WHERE id = $1`, [\n\t\t\t\tinv.id,\n\t\t\t]),\n\t\t]);\n\t});\n\n\treturn { workspaceId: inv.workspaceId, roleId: inv.roleId };\n}\n","import type { IStoreAdapter } from '@fonderie/store';\n\nimport type { IInvitation } from '../types';\nimport {\n\tcreateInvitation,\n\tlistInvitations,\n\tcancelInvitation,\n\tacceptInvitationByPin,\n} from '../services/invitations';\n\nexport class InvitationModel {\n\tconstructor(private readonly store: IStoreAdapter) {}\n\n\tcreate(opts: Parameters<typeof createInvitation>[0]): Promise<IInvitation> {\n\t\treturn createInvitation(opts, this.store);\n\t}\n\n\tlist(workspaceId: string): Promise<IInvitation[]> {\n\t\treturn listInvitations(workspaceId, this.store);\n\t}\n\n\tcancel(invitationId: string, workspaceId: string): Promise<void> {\n\t\treturn cancelInvitation(invitationId, workspaceId, this.store);\n\t}\n\n\tacceptByPin(\n\t\topts: Parameters<typeof acceptInvitationByPin>[0],\n\t): Promise<{ workspaceId: string; roleId: string }> {\n\t\treturn acceptInvitationByPin(opts, this.store);\n\t}\n}\n","import type { IFonderieModule, IFonderieApp } from '@fonderie/core';\nimport type { IStoreAdapter } from '@fonderie/store';\nimport type { EventBus } from '@fonderie/events';\n\nimport type { IWorkspacesConfig } from './config';\nimport { EVENT_KEYS } from './config';\nimport { buildWorkspaceRoutes } from './routes';\nimport { WorkspaceModel } from './models/workspace.model';\nimport { RoleModel } from './models/role.model';\nimport { MemberModel } from './models/member.model';\n\n// Mirror of @fonderie/auth EVENT_KEYS.userRegistered — avoids a runtime\n// dependency on the auth package while remaining explicit about the contract.\nconst AUTH_USER_REGISTERED = 'fonderie.user.registered' as const;\n\ninterface UserRegisteredPayload {\n\tuserId: string;\n}\n\nexport class WorkspacesModule implements IFonderieModule {\n\treadonly name = '@fonderie/workspaces';\n\treadonly deps = ['@fonderie/auth', '@fonderie/billing'];\n\n\tconstructor(\n\t\tprivate store: IStoreAdapter,\n\t\tprivate config: IWorkspacesConfig = {},\n\t\tprivate bus?: EventBus,\n\t) {}\n\n\tinstall(app: IFonderieApp): void {\n\t\tif (this.bus && this.config.personalWorkspace !== false) {\n\t\t\tthis.bus.on<UserRegisteredPayload>(\n\t\t\t\tAUTH_USER_REGISTERED,\n\t\t\t\t(payload) => this.provisionPersonalWorkspace(payload),\n\t\t\t\t'workspaces',\n\t\t\t);\n\t\t}\n\n\t\tconst routes = buildWorkspaceRoutes(this.store, this.config, this.bus);\n\t\tfor (const [method, path, ...handlers] of routes) {\n\t\t\tapp.addRoute(method, path, ...handlers);\n\t\t}\n\t}\n\n\tprivate async provisionPersonalWorkspace(payload: UserRegisteredPayload): Promise<void> {\n\t\tconst name = 'My Workspace';\n\t\tconst slug = `${payload.userId}-personal`;\n\n\t\tlet workspaceId: string | undefined;\n\n\t\tawait this.store.transaction(async (tx) => {\n\t\t\tconst wsModel = new WorkspaceModel(tx);\n\t\t\tconst roleModel = new RoleModel(tx);\n\t\t\tconst memModel = new MemberModel(tx);\n\n\t\t\tconst ws = await wsModel.createPersonal({ name, slug, ownerId: payload.userId });\n\t\t\tif (!ws) return; // already exists — idempotent replay\n\n\t\t\tworkspaceId = ws.id;\n\n\t\t\tconst adminRole = await roleModel.findSystem('ADMIN');\n\t\t\tif (!adminRole) throw new Error('System ADMIN role not found');\n\n\t\t\tawait memModel.add({\n\t\t\t\tuserId: payload.userId,\n\t\t\t\tworkspaceId: ws.id,\n\t\t\t\troleId: adminRole.id,\n\t\t\t\tconfirmed: true,\n\t\t\t});\n\t\t});\n\n\t\tif (workspaceId) {\n\t\t\tthis.bus\n\t\t\t\t?.emit(EVENT_KEYS.personalWorkspaceCreated, {\n\t\t\t\t\tworkspaceId,\n\t\t\t\t\tuserId: payload.userId,\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\t\t}\n\t}\n}\n","import { setApiResponse, HTTP } from '@fonderie/core';\nimport type { Middleware } from '@fonderie/core';\n\n// Ensures ctx.workspace is set — use after withWorkspace\n\nexport const requireWorkspace: Middleware = async (ctx, next) => {\n\tif (!ctx.workspace) {\n\t\treturn setApiResponse(HTTP.BAD_REQUEST, 'WORKSPACE_REQUIRED', 'Workspace context required');\n\t}\n\treturn next();\n};\n"],"mappings":"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","names":["setApiResponse","HTTP","name","name","setApiResponse","HTTP","name","setApiResponse","HTTP","setApiResponse","HTTP","setApiResponse","HTTP","setApiResponse","HTTP","name","setApiResponse","HTTP","setApiResponse","HTTP","email","name","setApiResponse","HTTP"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fonderie/workspaces",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
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",
|