Module: database/repositories/workspaceRepo

Workspace CRUD backed by SQLite (ACL-001).

Each workspace is an isolated tenant. All entity tables (projects, tests, runs, activities) carry a workspaceId foreign key so queries can be scoped to the authenticated user's workspace.

Default workspace

On first startup after migration 004, ensureDefaultWorkspaces creates a "Default" workspace for every existing user and backfills workspaceId on all orphaned entity rows. This makes the migration non-breaking for existing single-user deployments.

Source:

Methods

(static) addMember(workspaceId, userId, roleopt) → {Object}

Add a user to a workspace with a given role.

Parameters:
Name Type Attributes Default Description
workspaceId string
userId string
role string <optional>
'viewer'

— 'admin' | 'qa_lead' | 'viewer'

Source:
Returns:

The membership row.

Type
Object

(static) create(opts) → {Object}

Create a workspace and add the creator as admin.

Parameters:
Name Type Description
opts Object
Properties
Name Type Description
name string

— Display name.

slug string

— URL-friendly identifier.

ownerId string

— User ID of the creator.

Source:
Returns:

The created workspace.

Type
Object

(static) ensureDefaultWorkspaces()

Ensure every existing user has at least one workspace.

Called once on startup after migration 004. For each user without a workspace membership, creates a personal workspace.

Orphaned entity backfill

Entities (projects, tests, runs) have no userId column, so there is no reliable way to attribute them to individual users. The backfill strategy depends on how many orphan users exist:

  • Single orphan user: All orphaned entities are assigned to that user's workspace. This is the common case (single-user deployment upgrading).
  • Multiple orphan users: A shared "Default" workspace is created and all orphaned entities are assigned there. All orphan users are added as members. The first orphan user is the admin; the rest are viewers. This prevents the first-user-claims-everything bug and preserves data visibility for all existing users.

Activities (which have userId) are always attributed to the correct user's workspace when possible.

This is idempotent — calling it multiple times is safe.

Source:

(static) getById(id) → {Object|undefined}

Get a workspace by ID.

Parameters:
Name Type Description
id string
Source:
Returns:
Type
Object | undefined

(static) getBySlug(slug) → {Object|undefined}

Get a workspace by slug.

Parameters:
Name Type Description
slug string
Source:
Returns:
Type
Object | undefined

(static) getByUserId(userId) → {Array.<Object>}

Get all workspaces for a user (via workspace_members). Results are sorted with owned workspaces first, then by creation date.

Parameters:
Name Type Description
userId string
Source:
Returns:

Workspace rows augmented with role from the membership.

Type
Array.<Object>

(static) getMembers(workspaceId) → {Array.<Object>}

Get all members of a workspace.

Parameters:
Name Type Description
workspaceId string
Source:
Returns:
Type
Array.<Object>

(static) getMembership(workspaceId, userId) → {Object|undefined}

Get a user's membership in a specific workspace.

Parameters:
Name Type Description
workspaceId string
userId string
Source:
Returns:

— { id, workspaceId, userId, role, joinedAt }

Type
Object | undefined

(static) getOwnedWorkspaceIds(userId) → {Array.<string>}

Get IDs of all workspaces owned by a user. Used by account deletion to collect owned project IDs before cascade delete.

Parameters:
Name Type Description
userId string
Source:
Returns:
Type
Array.<string>

(static) removeMember(workspaceId, userId) → {boolean}

Remove a member from a workspace.

Parameters:
Name Type Description
workspaceId string
userId string
Source:
Returns:

Whether the membership was found and removed.

Type
boolean

(static) update(id, fields)

Update workspace fields.

Parameters:
Name Type Description
id string
fields Object

— { name?, slug? }

Source:

(static) updateMemberRole(workspaceId, userId, role) → {boolean}

Update a member's role.

Parameters:
Name Type Description
workspaceId string
userId string
role string

— 'admin' | 'qa_lead' | 'viewer'

Source:
Returns:

Whether the membership was found and updated.

Type
boolean

(inner) slugify(name) → {string}

Generate a URL-friendly slug from a name.

Parameters:
Name Type Description
name string
Source:
Returns:
Type
string