improved a lot of things
This commit is contained in:
@@ -1 +1,2 @@
|
||||
export {};
|
||||
export * from './types.js';
|
||||
export * from './queries.js';
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
// ── Query interface ───────────────────────────────────────────────────────────
|
||||
// These functions describe the Origin Graph query surface. They accept a
|
||||
// generic `db` client (matching Supabase's SupabaseClient shape) so the
|
||||
// package doesn't take a hard dependency on @supabase/supabase-js. Wire them
|
||||
// to TanStack Query's queryFn in the app layer.
|
||||
|
||||
import type {
|
||||
Artboard,
|
||||
Workspace,
|
||||
IntentDiff,
|
||||
DesignLanguageFile,
|
||||
AgentSession,
|
||||
TeamMember,
|
||||
InsertArtboard,
|
||||
InsertIntentDiff,
|
||||
InsertAgentSession,
|
||||
DiffStatus,
|
||||
ArtboardAncestry,
|
||||
} from './types.js';
|
||||
|
||||
// ── Minimal Supabase client interface ────────────────────────────────────────
|
||||
|
||||
export interface DbClient {
|
||||
from(table: string): {
|
||||
select(cols?: string): DbQuery;
|
||||
insert(row: unknown): DbMutation;
|
||||
update(row: unknown): DbMutation;
|
||||
delete(): DbMutation;
|
||||
};
|
||||
rpc(fn: string, args?: unknown): Promise<{ data: unknown; error: DbError | null }>;
|
||||
}
|
||||
|
||||
export interface DbQuery {
|
||||
eq(col: string, val: unknown): DbQuery;
|
||||
in(col: string, vals: unknown[]): DbQuery;
|
||||
order(col: string, opts?: { ascending?: boolean }): DbQuery;
|
||||
limit(n: number): DbQuery;
|
||||
single(): Promise<{ data: unknown; error: DbError | null }>;
|
||||
then(resolve: (result: { data: unknown[]; error: DbError | null }) => void): void;
|
||||
}
|
||||
|
||||
export interface DbMutation {
|
||||
eq(col: string, val: unknown): DbMutation;
|
||||
select(cols?: string): DbMutation;
|
||||
single(): Promise<{ data: unknown; error: DbError | null }>;
|
||||
then(resolve: (result: { data: unknown; error: DbError | null }) => void): void;
|
||||
}
|
||||
|
||||
export interface DbError {
|
||||
message: string;
|
||||
code?: string;
|
||||
}
|
||||
|
||||
// ── Artboard queries ──────────────────────────────────────────────────────────
|
||||
|
||||
export async function getArtboards(db: DbClient, workspaceId: string): Promise<Artboard[]> {
|
||||
const { data, error } = await (db
|
||||
.from('artboards')
|
||||
.select('*')
|
||||
.eq('workspace_id', workspaceId)
|
||||
.order('created_at', { ascending: false }) as unknown as Promise<{ data: Artboard[]; error: DbError | null }>);
|
||||
if (error) throw new Error(error.message);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function getArtboard(db: DbClient, id: string): Promise<Artboard> {
|
||||
const { data, error } = await (db
|
||||
.from('artboards')
|
||||
.select('*')
|
||||
.eq('id', id)
|
||||
.single() as Promise<{ data: Artboard; error: DbError | null }>);
|
||||
if (error) throw new Error(error.message);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function createArtboard(db: DbClient, row: InsertArtboard): Promise<Artboard> {
|
||||
const { data, error } = await (db
|
||||
.from('artboards')
|
||||
.insert(row)
|
||||
.select()
|
||||
.single() as Promise<{ data: Artboard; error: DbError | null }>);
|
||||
if (error) throw new Error(error.message);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function getArtboardAncestors(db: DbClient, artboardId: string): Promise<ArtboardAncestry[]> {
|
||||
const { data, error } = await (db
|
||||
.from('artboard_ancestry')
|
||||
.select('*')
|
||||
.eq('artboard_id', artboardId)
|
||||
.order('depth', { ascending: true }) as unknown as Promise<{ data: ArtboardAncestry[]; error: DbError | null }>);
|
||||
if (error) throw new Error(error.message);
|
||||
return data;
|
||||
}
|
||||
|
||||
// ── Intent diff queries ───────────────────────────────────────────────────────
|
||||
|
||||
export async function getDiffs(db: DbClient, artboardId: string): Promise<IntentDiff[]> {
|
||||
const { data, error } = await (db
|
||||
.from('intent_diffs')
|
||||
.select('*')
|
||||
.eq('artboard_id', artboardId)
|
||||
.order('created_at', { ascending: false }) as unknown as Promise<{ data: IntentDiff[]; error: DbError | null }>);
|
||||
if (error) throw new Error(error.message);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function getDiffsByStatus(db: DbClient, workspaceId: string, status: DiffStatus): Promise<IntentDiff[]> {
|
||||
// Join through artboards for workspace scoping
|
||||
const { data, error } = await (db.rpc('get_diffs_by_status', { p_workspace_id: workspaceId, p_status: status }) as Promise<{ data: IntentDiff[]; error: DbError | null }>);
|
||||
if (error) throw new Error(error.message);
|
||||
return data as IntentDiff[];
|
||||
}
|
||||
|
||||
export async function createDiff(db: DbClient, row: InsertIntentDiff): Promise<IntentDiff> {
|
||||
const { data, error } = await (db
|
||||
.from('intent_diffs')
|
||||
.insert(row)
|
||||
.select()
|
||||
.single() as Promise<{ data: IntentDiff; error: DbError | null }>);
|
||||
if (error) throw new Error(error.message);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function getDiff(db: DbClient, id: string): Promise<IntentDiff> {
|
||||
const { data, error } = await (db
|
||||
.from('intent_diffs')
|
||||
.select('*')
|
||||
.eq('id', id)
|
||||
.single() as Promise<{ data: IntentDiff; error: DbError | null }>);
|
||||
if (error) throw new Error(error.message);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function updateDiffStatus(
|
||||
db: DbClient,
|
||||
id: string,
|
||||
status: DiffStatus,
|
||||
notes?: string
|
||||
): Promise<IntentDiff> {
|
||||
const { data, error } = await (db
|
||||
.from('intent_diffs')
|
||||
.update({ status, ...(notes !== undefined ? { notes } : {}) })
|
||||
.eq('id', id)
|
||||
.select()
|
||||
.single() as Promise<{ data: IntentDiff; error: DbError | null }>);
|
||||
if (error) throw new Error(error.message);
|
||||
return data;
|
||||
}
|
||||
|
||||
// ── Design language file queries ──────────────────────────────────────────────
|
||||
|
||||
export async function getActiveDesignLanguageFile(
|
||||
db: DbClient,
|
||||
workspaceId: string
|
||||
): Promise<DesignLanguageFile | null> {
|
||||
const { data, error } = await (db
|
||||
.from('design_language_files')
|
||||
.select('*')
|
||||
.eq('workspace_id', workspaceId)
|
||||
.order('version', { ascending: false })
|
||||
.limit(1)
|
||||
.single() as Promise<{ data: DesignLanguageFile | null; error: DbError | null }>);
|
||||
if (error?.code === 'PGRST116') return null; // No rows found
|
||||
if (error) throw new Error(error.message);
|
||||
return data;
|
||||
}
|
||||
|
||||
// ── Agent session queries ─────────────────────────────────────────────────────
|
||||
|
||||
export async function createAgentSession(db: DbClient, row: InsertAgentSession): Promise<AgentSession> {
|
||||
const { data, error } = await (db
|
||||
.from('agent_sessions')
|
||||
.insert(row)
|
||||
.select()
|
||||
.single() as Promise<{ data: AgentSession; error: DbError | null }>);
|
||||
if (error) throw new Error(error.message);
|
||||
return data;
|
||||
}
|
||||
|
||||
// ── Workspace queries ─────────────────────────────────────────────────────────
|
||||
|
||||
export async function getWorkspace(db: DbClient, id: string): Promise<Workspace> {
|
||||
const { data, error } = await (db
|
||||
.from('workspaces')
|
||||
.select('*')
|
||||
.eq('id', id)
|
||||
.single() as Promise<{ data: Workspace; error: DbError | null }>);
|
||||
if (error) throw new Error(error.message);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function getTeamMembers(db: DbClient, workspaceId: string): Promise<TeamMember[]> {
|
||||
const { data, error } = await (db
|
||||
.from('team_members')
|
||||
.select('*')
|
||||
.eq('workspace_id', workspaceId) as unknown as Promise<{ data: TeamMember[]; error: DbError | null }>);
|
||||
if (error) throw new Error(error.message);
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
// ── Enums ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
export const OriginTypeSchema = z.enum([
|
||||
'GIT_COMMIT',
|
||||
'LINEAR_ISSUE',
|
||||
'SLACK_MESSAGE',
|
||||
'URL',
|
||||
'FORK',
|
||||
]);
|
||||
export type OriginType = z.infer<typeof OriginTypeSchema>;
|
||||
|
||||
export const DiffStatusSchema = z.enum([
|
||||
'DRAFT',
|
||||
'EXPORTED',
|
||||
'IMPLEMENTED',
|
||||
'BLOCKED',
|
||||
]);
|
||||
export type DiffStatus = z.infer<typeof DiffStatusSchema>;
|
||||
|
||||
export const TeamRoleSchema = z.enum([
|
||||
'OWNER',
|
||||
'DESIGNER',
|
||||
'ENGINEER',
|
||||
'PM',
|
||||
'VIEWER',
|
||||
]);
|
||||
export type TeamRole = z.infer<typeof TeamRoleSchema>;
|
||||
|
||||
export const AgentTypeSchema = z.enum(['CURSOR', 'CLAUDE_CODE', 'GENERIC']);
|
||||
export type AgentType = z.infer<typeof AgentTypeSchema>;
|
||||
|
||||
export const WorkspacePlanSchema = z.enum(['FREE', 'TEAM', 'ENTERPRISE']);
|
||||
export type WorkspacePlan = z.infer<typeof WorkspacePlanSchema>;
|
||||
|
||||
// ── Row types (match PostgreSQL columns 1:1) ──────────────────────────────────
|
||||
|
||||
export const WorkspaceSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
owner_id: z.string(),
|
||||
plan: WorkspacePlanSchema,
|
||||
settings_jsonb: z.record(z.unknown()),
|
||||
created_at: z.string().datetime(),
|
||||
updated_at: z.string().datetime(),
|
||||
});
|
||||
export type Workspace = z.infer<typeof WorkspaceSchema>;
|
||||
|
||||
export const ArtboardSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
workspace_id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
origin_id: z.string().uuid().nullable(),
|
||||
parent_artboard_id: z.string().uuid().nullable(),
|
||||
metadata_jsonb: z.record(z.unknown()),
|
||||
created_at: z.string().datetime(),
|
||||
updated_at: z.string().datetime(),
|
||||
});
|
||||
export type Artboard = z.infer<typeof ArtboardSchema>;
|
||||
|
||||
export const OriginSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
type: OriginTypeSchema,
|
||||
source_ref: z.string(),
|
||||
source_metadata_jsonb: z.record(z.unknown()),
|
||||
created_at: z.string().datetime(),
|
||||
updated_at: z.string().datetime(),
|
||||
});
|
||||
export type Origin = z.infer<typeof OriginSchema>;
|
||||
|
||||
export const IntentDiffSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
artboard_id: z.string().uuid(),
|
||||
author_id: z.string(),
|
||||
changes_jsonb: z.record(z.unknown()),
|
||||
summary: z.string(),
|
||||
status: DiffStatusSchema,
|
||||
/** Free-text notes from the coding agent (e.g. why it was blocked) */
|
||||
notes: z.string().optional(),
|
||||
created_at: z.string().datetime(),
|
||||
updated_at: z.string().datetime(),
|
||||
});
|
||||
export type IntentDiff = z.infer<typeof IntentDiffSchema>;
|
||||
|
||||
export const AgentSessionSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
artboard_id: z.string().uuid(),
|
||||
diff_id: z.string().uuid().nullable(),
|
||||
agent_type: AgentTypeSchema,
|
||||
messages_jsonb: z.array(z.record(z.unknown())),
|
||||
status: z.enum(['ACTIVE', 'COMPLETED', 'FAILED']),
|
||||
created_at: z.string().datetime(),
|
||||
updated_at: z.string().datetime(),
|
||||
});
|
||||
export type AgentSession = z.infer<typeof AgentSessionSchema>;
|
||||
|
||||
export const DesignLanguageFileSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
workspace_id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
schema_jsonb: z.record(z.unknown()),
|
||||
version: z.number().int(),
|
||||
created_at: z.string().datetime(),
|
||||
updated_at: z.string().datetime(),
|
||||
});
|
||||
export type DesignLanguageFile = z.infer<typeof DesignLanguageFileSchema>;
|
||||
|
||||
export const TeamMemberSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
workspace_id: z.string().uuid(),
|
||||
user_id: z.string(),
|
||||
role: TeamRoleSchema,
|
||||
created_at: z.string().datetime(),
|
||||
updated_at: z.string().datetime(),
|
||||
});
|
||||
export type TeamMember = z.infer<typeof TeamMemberSchema>;
|
||||
|
||||
// ── Ancestry (from materialized view) ────────────────────────────────────────
|
||||
|
||||
export interface ArtboardAncestry {
|
||||
artboard_id: string;
|
||||
ancestor_id: string;
|
||||
depth: number;
|
||||
}
|
||||
|
||||
// ── Insert types (omit server-set fields) ────────────────────────────────────
|
||||
|
||||
export type InsertWorkspace = Omit<Workspace, 'id' | 'created_at' | 'updated_at'>;
|
||||
export type InsertArtboard = Omit<Artboard, 'id' | 'created_at' | 'updated_at'>;
|
||||
export type InsertOrigin = Omit<Origin, 'id' | 'created_at' | 'updated_at'>;
|
||||
export type InsertIntentDiff = Omit<IntentDiff, 'id' | 'created_at' | 'updated_at'>;
|
||||
export type InsertAgentSession = Omit<AgentSession, 'id' | 'created_at' | 'updated_at'>;
|
||||
export type InsertDesignLanguageFile = Omit<DesignLanguageFile, 'id' | 'created_at' | 'updated_at'>;
|
||||
export type InsertTeamMember = Omit<TeamMember, 'id' | 'created_at' | 'updated_at'>;
|
||||
Reference in New Issue
Block a user