@baseworks/organization 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/chunk-5UCSEIJS.js +64 -0
  2. package/dist/cli.d.ts +18 -0
  3. package/dist/cli.js +534 -0
  4. package/dist/index.d.ts +199 -0
  5. package/dist/index.js +335 -0
  6. package/dist/schema/pg/index.d.ts +562 -0
  7. package/dist/schema/pg/index.js +62 -0
  8. package/dist/schema/sqlite/index.d.ts +604 -0
  9. package/dist/schema/sqlite/index.js +12 -0
  10. package/package.json +37 -0
  11. package/src/__tests__/cli-env.test.ts +158 -0
  12. package/src/__tests__/cli-org.test.ts +154 -0
  13. package/src/__tests__/cli-proj.test.ts +157 -0
  14. package/src/__tests__/cli-ws.test.ts +156 -0
  15. package/src/__tests__/helpers.ts +29 -0
  16. package/src/cli.ts +682 -0
  17. package/src/index.ts +5 -0
  18. package/src/operations/bootstrap.ts +50 -0
  19. package/src/repo/environments.ts +82 -0
  20. package/src/repo/index.ts +9 -0
  21. package/src/repo/organizations.ts +96 -0
  22. package/src/repo/projects.ts +106 -0
  23. package/src/repo/workspaces.ts +87 -0
  24. package/src/schema/environments.ts +14 -0
  25. package/src/schema/index.ts +5 -0
  26. package/src/schema/organizations.ts +11 -0
  27. package/src/schema/pg/environments.ts +14 -0
  28. package/src/schema/pg/index.ts +4 -0
  29. package/src/schema/pg/organizations.ts +11 -0
  30. package/src/schema/pg/projects.ts +16 -0
  31. package/src/schema/pg/workspaces.ts +15 -0
  32. package/src/schema/projects.ts +16 -0
  33. package/src/schema/sqlite/environments.ts +14 -0
  34. package/src/schema/sqlite/index.ts +4 -0
  35. package/src/schema/sqlite/organizations.ts +11 -0
  36. package/src/schema/sqlite/projects.ts +16 -0
  37. package/src/schema/sqlite/workspaces.ts +15 -0
  38. package/src/schema/workspaces.ts +15 -0
  39. package/src/types.ts +88 -0
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ export * from './schema';
2
+ export * from './types';
3
+ export * from './repo';
4
+ export * from './operations/bootstrap';
5
+ export { generateId, generateShortId } from '@baseworks/core';
@@ -0,0 +1,50 @@
1
+ import { createOrganizationRepo } from '../repo/organizations.js'
2
+ import { createWorkspaceRepo } from '../repo/workspaces.js'
3
+ import { createProjectRepo } from '../repo/projects.js'
4
+ import type { Organization, Workspace, Project } from '../types.js'
5
+
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ type AnyDB = any
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ type AnySchema = any
10
+
11
+ export interface OrganizationBootstrapResult {
12
+ organization: Organization
13
+ defaultWorkspace: Workspace
14
+ defaultProject: Project
15
+ }
16
+
17
+ export interface OrganizationBootstrapInput {
18
+ name: string
19
+ slug?: string
20
+ workspaceName?: string
21
+ projectName?: string
22
+ }
23
+
24
+ // Creates org + default workspace + default project in sequence.
25
+ // Wrap in a db.transaction() at the call site if atomicity is required.
26
+ export async function createOrganizationWithDefaults(
27
+ db: AnyDB,
28
+ schema: AnySchema,
29
+ input: OrganizationBootstrapInput,
30
+ ): Promise<OrganizationBootstrapResult> {
31
+ const orgRepo = createOrganizationRepo(db, schema)
32
+ const workspaceRepo = createWorkspaceRepo(db, schema)
33
+ const projectRepo = createProjectRepo(db, schema)
34
+
35
+ const organization = await orgRepo.create({ name: input.name, slug: input.slug })
36
+
37
+ const defaultWorkspace = await workspaceRepo.create({
38
+ organizationId: organization.id,
39
+ name: input.workspaceName ?? 'Default',
40
+ isDefault: true,
41
+ })
42
+
43
+ const defaultProject = await projectRepo.create({
44
+ workspaceId: defaultWorkspace.id,
45
+ name: input.projectName ?? 'Default',
46
+ isDefault: true,
47
+ })
48
+
49
+ return { organization, defaultWorkspace, defaultProject }
50
+ }
@@ -0,0 +1,82 @@
1
+ import { and, eq, or } from 'drizzle-orm'
2
+ import { generateId, generateShortId, generateSlug } from '@baseworks/core'
3
+ import type { Environment } from '../types.js'
4
+
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ type AnyDB = any
7
+
8
+ async function resolveSlug(db: AnyDB, envsTable: any, projectId: string, base: string): Promise<string> {
9
+ let slug = base
10
+ let i = 2
11
+ while (true) {
12
+ const rows = await db
13
+ .select({ id: envsTable.id })
14
+ .from(envsTable)
15
+ .where(and(eq(envsTable.projectId, projectId), eq(envsTable.slug, slug)))
16
+ .limit(1)
17
+ if (rows.length === 0) return slug
18
+ slug = `${base}-${i++}`
19
+ }
20
+ }
21
+
22
+ export function createEnvironmentRepo(db: AnyDB, schema: { environments: any }) {
23
+ const { environments } = schema
24
+
25
+ return {
26
+ async create(data: { projectId: string; name: string; slug?: string; id?: string }): Promise<Environment> {
27
+ const now = Date.now()
28
+ const base = data.slug ?? generateSlug(data.name)
29
+ const slug = await resolveSlug(db, environments, data.projectId, base)
30
+ const row = {
31
+ id: data.id ?? generateId(),
32
+ projectId: data.projectId,
33
+ shortId: generateShortId(),
34
+ slug,
35
+ name: data.name,
36
+ createdAt: now,
37
+ updatedAt: now,
38
+ }
39
+ await db.insert(environments).values(row)
40
+ return row
41
+ },
42
+
43
+ async findById(id: string): Promise<Environment | undefined> {
44
+ const rows = await db.select().from(environments).where(eq(environments.id, id)).limit(1)
45
+ return rows[0]
46
+ },
47
+
48
+ async findByShortId(shortId: string): Promise<Environment | undefined> {
49
+ const rows = await db.select().from(environments).where(eq(environments.shortId, shortId)).limit(1)
50
+ return rows[0]
51
+ },
52
+
53
+ async findByIdentifier(projectId: string, identifier: string): Promise<Environment | undefined> {
54
+ const rows = await db
55
+ .select()
56
+ .from(environments)
57
+ .where(and(eq(environments.projectId, projectId), or(eq(environments.shortId, identifier), eq(environments.slug, identifier))))
58
+ .limit(1)
59
+ return rows[0]
60
+ },
61
+
62
+ async findByRef(projectId: string, ref: string): Promise<Environment | undefined> {
63
+ return this.findByIdentifier(projectId, ref)
64
+ },
65
+
66
+ async listByProject(projectId: string): Promise<Environment[]> {
67
+ return db.select().from(environments).where(eq(environments.projectId, projectId))
68
+ },
69
+
70
+ async update(id: string, data: { name?: string; slug?: string }): Promise<Environment | undefined> {
71
+ await db.update(environments).set({ ...data, updatedAt: Date.now() }).where(eq(environments.id, id))
72
+ const rows = await db.select().from(environments).where(eq(environments.id, id)).limit(1)
73
+ return rows[0]
74
+ },
75
+
76
+ async delete(id: string): Promise<void> {
77
+ await db.delete(environments).where(eq(environments.id, id))
78
+ },
79
+ }
80
+ }
81
+
82
+ export type EnvironmentRepo = ReturnType<typeof createEnvironmentRepo>
@@ -0,0 +1,9 @@
1
+ export { createOrganizationRepo } from './organizations.js'
2
+ export { createWorkspaceRepo } from './workspaces.js'
3
+ export { createProjectRepo } from './projects.js'
4
+ export { createEnvironmentRepo } from './environments.js'
5
+
6
+ export type { OrganizationRepo } from './organizations.js'
7
+ export type { WorkspaceRepo } from './workspaces.js'
8
+ export type { ProjectRepo } from './projects.js'
9
+ export type { EnvironmentRepo } from './environments.js'
@@ -0,0 +1,96 @@
1
+ import { eq, desc } from 'drizzle-orm'
2
+ import { generateId, generateShortId, generateSlug } from '@baseworks/core'
3
+ import type { Organization } from '../types.js'
4
+
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ type AnyDB = any
7
+
8
+ export function createOrganizationRepo(db: AnyDB, schema: { organizations: any }) {
9
+ const { organizations } = schema
10
+
11
+ return {
12
+ async create(data: { name: string; slug?: string; id?: string }): Promise<Organization> {
13
+ const now = Date.now()
14
+ const shortId = generateShortId()
15
+ const row = {
16
+ id: data.id ?? generateId(),
17
+ shortId,
18
+ slug: data.slug ?? generateSlug(data.name),
19
+ name: data.name,
20
+ metadata: null,
21
+ createdAt: now,
22
+ updatedAt: now,
23
+ }
24
+ await db.insert(organizations).values(row)
25
+ return row
26
+ },
27
+
28
+ async findById(id: string): Promise<Organization | undefined> {
29
+ const rows = await db.select().from(organizations).where(eq(organizations.id, id)).limit(1)
30
+ return rows[0]
31
+ },
32
+
33
+ async findByShortId(shortId: string): Promise<Organization | undefined> {
34
+ const rows = await db.select().from(organizations).where(eq(organizations.shortId, shortId)).limit(1)
35
+ return rows[0]
36
+ },
37
+
38
+ async findBySlug(slug: string): Promise<Organization | undefined> {
39
+ const rows = await db.select().from(organizations).where(eq(organizations.slug, slug)).limit(1)
40
+ return rows[0]
41
+ },
42
+
43
+ async findByRef(ref: string): Promise<Organization | undefined> {
44
+ const bySlug = await db.select().from(organizations).where(eq(organizations.slug, ref)).limit(1)
45
+ if (bySlug[0]) return bySlug[0]
46
+ const byShortId = await db.select().from(organizations).where(eq(organizations.shortId, ref)).limit(1)
47
+ return byShortId[0]
48
+ },
49
+
50
+ async isSlugAvailable(slug: string): Promise<boolean> {
51
+ const rows = await db.select({ id: organizations.id }).from(organizations).where(eq(organizations.slug, slug)).limit(1)
52
+ return rows.length === 0
53
+ },
54
+
55
+ async claimSlug(id: string, slug: string): Promise<void> {
56
+ await db.update(organizations).set({ slug, updatedAt: Date.now() }).where(eq(organizations.id, id))
57
+ },
58
+
59
+ async update(id: string, data: { name?: string; slug?: string; metadata?: string | null }): Promise<Organization | undefined> {
60
+ await db.update(organizations).set({ ...data, updatedAt: Date.now() }).where(eq(organizations.id, id))
61
+ const rows = await db.select().from(organizations).where(eq(organizations.id, id)).limit(1)
62
+ return rows[0]
63
+ },
64
+
65
+ async patchMetadata(id: string, patch: Record<string, unknown>): Promise<Organization | undefined> {
66
+ const rows = await db.select().from(organizations).where(eq(organizations.id, id)).limit(1)
67
+ const org = rows[0]
68
+ if (!org) return undefined
69
+
70
+ const current: Record<string, unknown> = org.metadata ? JSON.parse(org.metadata) : {}
71
+ const merged = { ...current }
72
+ for (const [k, v] of Object.entries(patch)) {
73
+ if (v === null) delete merged[k]
74
+ else merged[k] = v
75
+ }
76
+
77
+ await db.update(organizations).set({ metadata: JSON.stringify(merged), updatedAt: Date.now() }).where(eq(organizations.id, id))
78
+ const updated = await db.select().from(organizations).where(eq(organizations.id, id)).limit(1)
79
+ return updated[0]
80
+ },
81
+
82
+ async delete(id: string): Promise<void> {
83
+ await db.delete(organizations).where(eq(organizations.id, id))
84
+ },
85
+
86
+ async listAll(): Promise<Organization[]> {
87
+ return db.select().from(organizations).orderBy(desc(organizations.createdAt))
88
+ },
89
+
90
+ async hardDelete(id: string): Promise<void> {
91
+ await db.delete(organizations).where(eq(organizations.id, id))
92
+ },
93
+ }
94
+ }
95
+
96
+ export type OrganizationRepo = ReturnType<typeof createOrganizationRepo>
@@ -0,0 +1,106 @@
1
+ import { and, eq } from 'drizzle-orm'
2
+ import { generateId, generateShortId, generateSlug } from '@baseworks/core'
3
+ import type { Project } from '../types.js'
4
+
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ type AnyDB = any
7
+
8
+ async function resolveSlug(db: AnyDB, projectsTable: any, workspaceId: string, base: string): Promise<string> {
9
+ let slug = base
10
+ let i = 2
11
+ while (true) {
12
+ const rows = await db
13
+ .select({ id: projectsTable.id })
14
+ .from(projectsTable)
15
+ .where(and(eq(projectsTable.workspaceId, workspaceId), eq(projectsTable.slug, slug)))
16
+ .limit(1)
17
+ if (rows.length === 0) return slug
18
+ slug = `${base}-${i++}`
19
+ }
20
+ }
21
+
22
+ export function createProjectRepo(db: AnyDB, schema: { projects: any }) {
23
+ const { projects } = schema
24
+
25
+ return {
26
+ async create(data: { workspaceId: string; name: string; slug?: string; isDefault?: boolean; id?: string }): Promise<Project> {
27
+ const now = Date.now()
28
+ const base = data.slug ?? generateSlug(data.name)
29
+ const slug = await resolveSlug(db, projects, data.workspaceId, base)
30
+ const row = {
31
+ id: data.id ?? generateId(),
32
+ workspaceId: data.workspaceId,
33
+ shortId: generateShortId(),
34
+ slug,
35
+ name: data.name,
36
+ isDefault: data.isDefault ?? false,
37
+ metadata: null,
38
+ createdAt: now,
39
+ updatedAt: now,
40
+ }
41
+ await db.insert(projects).values(row)
42
+ return row
43
+ },
44
+
45
+ async findById(id: string): Promise<Project | undefined> {
46
+ const rows = await db.select().from(projects).where(eq(projects.id, id)).limit(1)
47
+ return rows[0]
48
+ },
49
+
50
+ async findByShortId(shortId: string): Promise<Project | undefined> {
51
+ const rows = await db.select().from(projects).where(eq(projects.shortId, shortId)).limit(1)
52
+ return rows[0]
53
+ },
54
+
55
+ async findBySlug(workspaceId: string, slug: string): Promise<Project | undefined> {
56
+ const rows = await db.select().from(projects).where(and(eq(projects.workspaceId, workspaceId), eq(projects.slug, slug))).limit(1)
57
+ return rows[0]
58
+ },
59
+
60
+ async findByRef(workspaceId: string, ref: string): Promise<Project | undefined> {
61
+ const bySlug = await db.select().from(projects).where(and(eq(projects.workspaceId, workspaceId), eq(projects.slug, ref))).limit(1)
62
+ if (bySlug[0]) return bySlug[0]
63
+ const byShortId = await db.select().from(projects).where(and(eq(projects.workspaceId, workspaceId), eq(projects.shortId, ref))).limit(1)
64
+ return byShortId[0]
65
+ },
66
+
67
+ async listByWorkspace(workspaceId: string): Promise<Project[]> {
68
+ return db.select().from(projects).where(eq(projects.workspaceId, workspaceId))
69
+ },
70
+
71
+ async findDefault(workspaceId: string): Promise<Project | undefined> {
72
+ const rows = await db.select().from(projects).where(and(eq(projects.workspaceId, workspaceId), eq(projects.isDefault, true))).limit(1)
73
+ return rows[0]
74
+ },
75
+
76
+ async update(id: string, data: { name?: string }): Promise<Project | undefined> {
77
+ await db.update(projects).set({ ...data, updatedAt: Date.now() }).where(eq(projects.id, id))
78
+ const rows = await db.select().from(projects).where(eq(projects.id, id)).limit(1)
79
+ return rows[0]
80
+ },
81
+
82
+ async delete(id: string): Promise<void> {
83
+ await db.delete(projects).where(eq(projects.id, id))
84
+ },
85
+
86
+ async getMetadata(id: string): Promise<Record<string, unknown>> {
87
+ const rows = await db.select({ metadata: projects.metadata }).from(projects).where(eq(projects.id, id)).limit(1)
88
+ const row = rows[0]
89
+ if (!row?.metadata) return {}
90
+ try { return JSON.parse(row.metadata) as Record<string, unknown> } catch { return {} }
91
+ },
92
+
93
+ async patchMetadata(id: string, patch: Record<string, unknown>): Promise<Record<string, unknown>> {
94
+ const existing = await this.getMetadata(id)
95
+ const next: Record<string, unknown> = { ...existing }
96
+ for (const [k, v] of Object.entries(patch)) {
97
+ if (v === null) delete next[k]
98
+ else next[k] = v
99
+ }
100
+ await db.update(projects).set({ metadata: JSON.stringify(next), updatedAt: Date.now() }).where(eq(projects.id, id))
101
+ return next
102
+ },
103
+ }
104
+ }
105
+
106
+ export type ProjectRepo = ReturnType<typeof createProjectRepo>
@@ -0,0 +1,87 @@
1
+ import { and, eq } from 'drizzle-orm'
2
+ import { generateId, generateShortId, generateSlug } from '@baseworks/core'
3
+ import type { Workspace } from '../types.js'
4
+
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ type AnyDB = any
7
+
8
+ async function resolveSlug(db: AnyDB, workspacesTable: any, organizationId: string, base: string): Promise<string> {
9
+ let slug = base
10
+ let i = 2
11
+ while (true) {
12
+ const rows = await db
13
+ .select({ id: workspacesTable.id })
14
+ .from(workspacesTable)
15
+ .where(and(eq(workspacesTable.organizationId, organizationId), eq(workspacesTable.slug, slug)))
16
+ .limit(1)
17
+ if (rows.length === 0) return slug
18
+ slug = `${base}-${i++}`
19
+ }
20
+ }
21
+
22
+ export function createWorkspaceRepo(db: AnyDB, schema: { workspaces: any }) {
23
+ const { workspaces } = schema
24
+
25
+ return {
26
+ async create(data: { organizationId: string; name: string; slug?: string; isDefault?: boolean; id?: string }): Promise<Workspace> {
27
+ const now = Date.now()
28
+ const base = data.slug ?? generateSlug(data.name)
29
+ const slug = await resolveSlug(db, workspaces, data.organizationId, base)
30
+ const row = {
31
+ id: data.id ?? generateId(),
32
+ organizationId: data.organizationId,
33
+ shortId: generateShortId(),
34
+ slug,
35
+ name: data.name,
36
+ isDefault: data.isDefault ?? false,
37
+ createdAt: now,
38
+ updatedAt: now,
39
+ }
40
+ await db.insert(workspaces).values(row)
41
+ return row
42
+ },
43
+
44
+ async findById(id: string): Promise<Workspace | undefined> {
45
+ const rows = await db.select().from(workspaces).where(eq(workspaces.id, id)).limit(1)
46
+ return rows[0]
47
+ },
48
+
49
+ async findByShortId(shortId: string): Promise<Workspace | undefined> {
50
+ const rows = await db.select().from(workspaces).where(eq(workspaces.shortId, shortId)).limit(1)
51
+ return rows[0]
52
+ },
53
+
54
+ async findBySlug(organizationId: string, slug: string): Promise<Workspace | undefined> {
55
+ const rows = await db.select().from(workspaces).where(and(eq(workspaces.organizationId, organizationId), eq(workspaces.slug, slug))).limit(1)
56
+ return rows[0]
57
+ },
58
+
59
+ async findByRef(organizationId: string, ref: string): Promise<Workspace | undefined> {
60
+ const bySlug = await db.select().from(workspaces).where(and(eq(workspaces.organizationId, organizationId), eq(workspaces.slug, ref))).limit(1)
61
+ if (bySlug[0]) return bySlug[0]
62
+ const byShortId = await db.select().from(workspaces).where(and(eq(workspaces.organizationId, organizationId), eq(workspaces.shortId, ref))).limit(1)
63
+ return byShortId[0]
64
+ },
65
+
66
+ async listByOrg(organizationId: string): Promise<Workspace[]> {
67
+ return db.select().from(workspaces).where(eq(workspaces.organizationId, organizationId))
68
+ },
69
+
70
+ async findDefault(organizationId: string): Promise<Workspace | undefined> {
71
+ const rows = await db.select().from(workspaces).where(and(eq(workspaces.organizationId, organizationId), eq(workspaces.isDefault, true))).limit(1)
72
+ return rows[0]
73
+ },
74
+
75
+ async update(id: string, data: { name?: string }): Promise<Workspace | undefined> {
76
+ await db.update(workspaces).set({ ...data, updatedAt: Date.now() }).where(eq(workspaces.id, id))
77
+ const rows = await db.select().from(workspaces).where(eq(workspaces.id, id)).limit(1)
78
+ return rows[0]
79
+ },
80
+
81
+ async delete(id: string): Promise<void> {
82
+ await db.delete(workspaces).where(eq(workspaces.id, id))
83
+ },
84
+ }
85
+ }
86
+
87
+ export type WorkspaceRepo = ReturnType<typeof createWorkspaceRepo>
@@ -0,0 +1,14 @@
1
+ import { integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core';
2
+ import { projects } from './projects';
3
+
4
+ export const environments = sqliteTable('environments', {
5
+ id: text('id').primaryKey(),
6
+ projectId: text('project_id').notNull().references(() => projects.id, { onDelete: 'cascade' }),
7
+ shortId: text('short_id').notNull().unique(),
8
+ slug: text('slug').notNull(),
9
+ name: text('name').notNull(),
10
+ createdAt: integer('created_at').notNull(),
11
+ updatedAt: integer('updated_at').notNull(),
12
+ }, (t) => ({
13
+ projectSlugUniq: uniqueIndex('environments_project_slug_uniq').on(t.projectId, t.slug),
14
+ }));
@@ -0,0 +1,5 @@
1
+ // Default export: SQLite (backward compat for existing D1/Turso projects)
2
+ export { organizations } from './sqlite/organizations.js'
3
+ export { workspaces } from './sqlite/workspaces.js'
4
+ export { projects } from './sqlite/projects.js'
5
+ export { environments } from './sqlite/environments.js'
@@ -0,0 +1,11 @@
1
+ import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
2
+
3
+ export const organizations = sqliteTable('organizations', {
4
+ id: text('id').primaryKey(),
5
+ shortId: text('short_id').notNull().unique(),
6
+ slug: text('slug').notNull().unique(),
7
+ name: text('name').notNull(),
8
+ metadata: text('metadata'), // JSON: { logo_url, website, ... }
9
+ createdAt: integer('created_at').notNull(),
10
+ updatedAt: integer('updated_at').notNull(),
11
+ });
@@ -0,0 +1,14 @@
1
+ import { bigint, pgTable, text, uniqueIndex } from 'drizzle-orm/pg-core'
2
+ import { projects } from './projects.js'
3
+
4
+ export const environments = pgTable('environments', {
5
+ id: text('id').primaryKey(),
6
+ projectId: text('project_id').notNull().references(() => projects.id, { onDelete: 'cascade' }),
7
+ shortId: text('short_id').notNull().unique(),
8
+ slug: text('slug').notNull(),
9
+ name: text('name').notNull(),
10
+ createdAt: bigint('created_at', { mode: 'number' }).notNull(),
11
+ updatedAt: bigint('updated_at', { mode: 'number' }).notNull(),
12
+ }, (t) => [
13
+ uniqueIndex('environments_project_slug_uniq').on(t.projectId, t.slug),
14
+ ])
@@ -0,0 +1,4 @@
1
+ export { organizations } from './organizations.js'
2
+ export { workspaces } from './workspaces.js'
3
+ export { projects } from './projects.js'
4
+ export { environments } from './environments.js'
@@ -0,0 +1,11 @@
1
+ import { bigint, pgTable, text } from 'drizzle-orm/pg-core'
2
+
3
+ export const organizations = pgTable('organizations', {
4
+ id: text('id').primaryKey(),
5
+ shortId: text('short_id').notNull().unique(),
6
+ slug: text('slug').notNull().unique(),
7
+ name: text('name').notNull(),
8
+ metadata: text('metadata'),
9
+ createdAt: bigint('created_at', { mode: 'number' }).notNull(),
10
+ updatedAt: bigint('updated_at', { mode: 'number' }).notNull(),
11
+ })
@@ -0,0 +1,16 @@
1
+ import { bigint, boolean, pgTable, text, uniqueIndex } from 'drizzle-orm/pg-core'
2
+ import { workspaces } from './workspaces.js'
3
+
4
+ export const projects = pgTable('projects', {
5
+ id: text('id').primaryKey(),
6
+ workspaceId: text('workspace_id').notNull().references(() => workspaces.id, { onDelete: 'cascade' }),
7
+ shortId: text('short_id').notNull().unique(),
8
+ slug: text('slug').notNull(),
9
+ name: text('name').notNull(),
10
+ isDefault: boolean('is_default').notNull().default(false),
11
+ metadata: text('metadata'),
12
+ createdAt: bigint('created_at', { mode: 'number' }).notNull(),
13
+ updatedAt: bigint('updated_at', { mode: 'number' }).notNull(),
14
+ }, (t) => [
15
+ uniqueIndex('projects_workspace_slug_uniq').on(t.workspaceId, t.slug),
16
+ ])
@@ -0,0 +1,15 @@
1
+ import { bigint, boolean, pgTable, text, uniqueIndex } from 'drizzle-orm/pg-core'
2
+ import { organizations } from './organizations.js'
3
+
4
+ export const workspaces = pgTable('workspaces', {
5
+ id: text('id').primaryKey(),
6
+ organizationId: text('organization_id').notNull().references(() => organizations.id, { onDelete: 'cascade' }),
7
+ shortId: text('short_id').notNull().unique(),
8
+ slug: text('slug').notNull(),
9
+ name: text('name').notNull(),
10
+ isDefault: boolean('is_default').notNull().default(false),
11
+ createdAt: bigint('created_at', { mode: 'number' }).notNull(),
12
+ updatedAt: bigint('updated_at', { mode: 'number' }).notNull(),
13
+ }, (t) => [
14
+ uniqueIndex('workspaces_org_slug_uniq').on(t.organizationId, t.slug),
15
+ ])
@@ -0,0 +1,16 @@
1
+ import { integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core';
2
+ import { workspaces } from './workspaces';
3
+
4
+ export const projects = sqliteTable('projects', {
5
+ id: text('id').primaryKey(),
6
+ workspaceId: text('workspace_id').notNull().references(() => workspaces.id, { onDelete: 'cascade' }),
7
+ shortId: text('short_id').notNull().unique(),
8
+ slug: text('slug').notNull(),
9
+ name: text('name').notNull(),
10
+ isDefault: integer('is_default', { mode: 'boolean' }).notNull().default(false),
11
+ metadata: text('metadata'),
12
+ createdAt: integer('created_at').notNull(),
13
+ updatedAt: integer('updated_at').notNull(),
14
+ }, (t) => ({
15
+ workspaceSlugUniq: uniqueIndex('projects_workspace_slug_uniq').on(t.workspaceId, t.slug),
16
+ }));
@@ -0,0 +1,14 @@
1
+ import { integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core';
2
+ import { projects } from './projects.js';
3
+
4
+ export const environments = sqliteTable('environments', {
5
+ id: text('id').primaryKey(),
6
+ projectId: text('project_id').notNull().references(() => projects.id, { onDelete: 'cascade' }),
7
+ shortId: text('short_id').notNull().unique(),
8
+ slug: text('slug').notNull(),
9
+ name: text('name').notNull(),
10
+ createdAt: integer('created_at').notNull(),
11
+ updatedAt: integer('updated_at').notNull(),
12
+ }, (t) => ({
13
+ projectSlugUniq: uniqueIndex('environments_project_slug_uniq').on(t.projectId, t.slug),
14
+ }));
@@ -0,0 +1,4 @@
1
+ export { organizations } from './organizations.js'
2
+ export { workspaces } from './workspaces.js'
3
+ export { projects } from './projects.js'
4
+ export { environments } from './environments.js'
@@ -0,0 +1,11 @@
1
+ import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
2
+
3
+ export const organizations = sqliteTable('organizations', {
4
+ id: text('id').primaryKey(),
5
+ shortId: text('short_id').notNull().unique(),
6
+ slug: text('slug').notNull().unique(),
7
+ name: text('name').notNull(),
8
+ metadata: text('metadata'), // JSON: { logo_url, website, ... }
9
+ createdAt: integer('created_at').notNull(),
10
+ updatedAt: integer('updated_at').notNull(),
11
+ });
@@ -0,0 +1,16 @@
1
+ import { integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core';
2
+ import { workspaces } from './workspaces.js';
3
+
4
+ export const projects = sqliteTable('projects', {
5
+ id: text('id').primaryKey(),
6
+ workspaceId: text('workspace_id').notNull().references(() => workspaces.id, { onDelete: 'cascade' }),
7
+ shortId: text('short_id').notNull().unique(),
8
+ slug: text('slug').notNull(),
9
+ name: text('name').notNull(),
10
+ isDefault: integer('is_default', { mode: 'boolean' }).notNull().default(false),
11
+ metadata: text('metadata'),
12
+ createdAt: integer('created_at').notNull(),
13
+ updatedAt: integer('updated_at').notNull(),
14
+ }, (t) => ({
15
+ workspaceSlugUniq: uniqueIndex('projects_workspace_slug_uniq').on(t.workspaceId, t.slug),
16
+ }));
@@ -0,0 +1,15 @@
1
+ import { integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core';
2
+ import { organizations } from './organizations.js';
3
+
4
+ export const workspaces = sqliteTable('workspaces', {
5
+ id: text('id').primaryKey(),
6
+ organizationId: text('organization_id').notNull().references(() => organizations.id, { onDelete: 'cascade' }),
7
+ shortId: text('short_id').notNull().unique(),
8
+ slug: text('slug').notNull(),
9
+ name: text('name').notNull(),
10
+ isDefault: integer('is_default', { mode: 'boolean' }).notNull().default(false),
11
+ createdAt: integer('created_at').notNull(),
12
+ updatedAt: integer('updated_at').notNull(),
13
+ }, (t) => ({
14
+ orgSlugUniq: uniqueIndex('workspaces_org_slug_uniq').on(t.organizationId, t.slug),
15
+ }));
@@ -0,0 +1,15 @@
1
+ import { integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core';
2
+ import { organizations } from './organizations';
3
+
4
+ export const workspaces = sqliteTable('workspaces', {
5
+ id: text('id').primaryKey(),
6
+ organizationId: text('organization_id').notNull().references(() => organizations.id, { onDelete: 'cascade' }),
7
+ shortId: text('short_id').notNull().unique(),
8
+ slug: text('slug').notNull(),
9
+ name: text('name').notNull(),
10
+ isDefault: integer('is_default', { mode: 'boolean' }).notNull().default(false),
11
+ createdAt: integer('created_at').notNull(),
12
+ updatedAt: integer('updated_at').notNull(),
13
+ }, (t) => ({
14
+ orgSlugUniq: uniqueIndex('workspaces_org_slug_uniq').on(t.organizationId, t.slug),
15
+ }));