deepspace 0.23.0 → 0.23.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/CHANGELOG.md +15 -0
- package/dist/build.d.ts +2 -1
- package/dist/build.js +89 -16
- package/dist/build.js.map +1 -1
- package/dist/{chunk-A7VKZRCB.js → chunk-UUCAFDYW.js} +2 -2
- package/dist/{chunk-A7VKZRCB.js.map → chunk-UUCAFDYW.js.map} +1 -1
- package/dist/cli.js +735 -559
- package/dist/cli.js.map +1 -1
- package/dist/documentation-client-core.js +1 -1
- package/dist/documentation-default-renderer.cjs +1 -1
- package/dist/documentation-react.js +1 -1
- package/dist/documentation-runtime.js +1 -1
- package/dist/documentation-server-core.js +1 -1
- package/dist/documentation.js +1 -1
- package/dist/documentation.js.map +1 -1
- package/dist/index.d.ts +20 -2
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +10 -0
- package/dist/schema.js.map +1 -1
- package/dist/server.d.ts +84 -14
- package/dist/server.js +261 -88
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +70 -3
- package/dist/worker.js +444 -272
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
package/dist/schema.d.ts
CHANGED
|
@@ -86,6 +86,16 @@ interface CollectionSchema {
|
|
|
86
86
|
permissions: Record<string, RolePermissions>;
|
|
87
87
|
/** Default role assigned on the `users` collection. */
|
|
88
88
|
defaultRole?: string;
|
|
89
|
+
/**
|
|
90
|
+
* `users` collection only: what the `user.list` roster shows a non-admin
|
|
91
|
+
* whose `read` policy is row-scoped (`'own'`, `'team'`, …). Default
|
|
92
|
+
* `'public-identity'`: every registered user's id/name/imageUrl/role — the
|
|
93
|
+
* row policy keeps guarding full-row reads on the records/query path.
|
|
94
|
+
* `'read-policy'`: the roster contains only the rows the caller's read
|
|
95
|
+
* policy grants (still projected to public identity) — for apps that scope
|
|
96
|
+
* users to a tenant/team and must not show names across it.
|
|
97
|
+
*/
|
|
98
|
+
roster?: 'public-identity' | 'read-policy';
|
|
89
99
|
}
|
|
90
100
|
|
|
91
101
|
/**
|
package/dist/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/schemas/registry.ts","../src/server/schemas/ai-chat.ts","../src/server/schemas/messaging.ts","../src/server/schemas/conversation.ts","../src/server/schemas/directory.ts","../src/server/schemas/workspace.ts"],"sourcesContent":["/**\n * Collection Schema Definitions & Validation\n *\n * All collections use typed SQL columns. No document-mode / fields-based storage.\n */\n\nimport type {\n CollectionSchema,\n ColumnDefinition,\n ColumnInterpretation,\n PermissionLevel,\n RolePermissions,\n} from '../../shared/types'\n\nexport type {\n CollectionSchema,\n ColumnDefinition,\n ColumnInterpretation,\n PermissionLevel,\n RolePermissions,\n} from '../../shared/types'\n\n// ============================================================================\n// Column Definitions\n// ============================================================================\n\nexport interface ResolvedColumn {\n id: string\n name: string\n storage: 'number' | 'text'\n interpretation: ColumnInterpretation\n expression?: string\n readonly: boolean\n userBound?: boolean\n immutable?: boolean\n required?: boolean\n default?: unknown\n timestampTrigger?: { field: string; value?: unknown }\n}\n\n// ============================================================================\n// Column Helpers\n// ============================================================================\n\nexport function collectionTableName(name: string): string {\n return `c_${name.replace(/[^a-zA-Z0-9_]/g, '_')}`\n}\n\nexport function columnId(name: string): string {\n return `col_${name.toLowerCase().replace(/[^a-z0-9]/g, '_')}`\n}\n\nexport function resolveColumn(col: ColumnDefinition): ResolvedColumn {\n const interp: ColumnInterpretation =\n typeof col.interpretation === 'string'\n ? ({ kind: col.interpretation } as ColumnInterpretation)\n : col.interpretation\n return {\n id: col.id ?? columnId(col.name),\n name: col.name,\n storage: col.storage,\n interpretation: interp,\n expression: col.expression,\n readonly: !!col.expression,\n userBound: col.userBound,\n immutable: col.immutable,\n required: col.required,\n default: col.default,\n timestampTrigger: col.timestampTrigger,\n }\n}\n\nexport function rowToData(\n row: Record<string, unknown>,\n columns: ResolvedColumn[],\n): Record<string, unknown> {\n const data: Record<string, unknown> = {}\n for (const col of columns) {\n if (row[col.id] !== undefined && row[col.id] !== null) {\n let val = row[col.id]\n if (col.interpretation.kind === 'json' && typeof val === 'string') {\n try {\n val = JSON.parse(val as string)\n } catch {\n /* keep as string */\n }\n }\n data[col.name] = val\n }\n }\n return data\n}\n\nexport function dataToColumnValues(\n data: Record<string, unknown>,\n columns: ResolvedColumn[],\n): Record<string, unknown> {\n const values: Record<string, unknown> = {}\n for (const col of columns) {\n if (col.expression || col.readonly) continue\n // Record mutations use public schema names only. Accepting physical column\n // ids here lets a second key bypass name-based RBAC/userBound checks.\n const val = data[col.name]\n if (val !== undefined) {\n values[col.id] = coerceValue(val, col.storage, col.interpretation)\n }\n }\n return values\n}\n\nexport function coerceValue(\n value: unknown,\n storage: 'number' | 'text',\n interpretation: ColumnInterpretation,\n): unknown {\n if (value === null || value === undefined || value === '') return null\n\n if (storage === 'text') {\n if (interpretation.kind === 'json') {\n if (typeof value === 'string') return value\n try {\n return JSON.stringify(value)\n } catch {\n return null\n }\n }\n return typeof value === 'string' ? value : String(value)\n }\n\n if (typeof value === 'number') return isFinite(value) ? value : null\n if (typeof value === 'boolean') return value ? 1 : 0\n if (typeof value !== 'string') return String(value)\n\n const s = value.trim()\n\n if (interpretation.kind === 'boolean') {\n const lower = s.toLowerCase()\n if (lower === 'true' || lower === '1' || lower === 'yes') return 1\n if (lower === 'false' || lower === '0' || lower === 'no') return 0\n return s\n }\n\n if (interpretation.kind === 'percent') {\n const m = s.match(/^(-?\\d+(?:\\.\\d+)?)\\s*%$/)\n if (m) return parseFloat(m[1]) / 100\n }\n\n if (interpretation.kind === 'currency') {\n const cleaned = s.replace(/[^0-9.-]/g, '')\n const n = parseFloat(cleaned)\n if (!isNaN(n)) return n\n return s\n }\n\n if (interpretation.kind === 'date' || interpretation.kind === 'datetime') {\n const d = new Date(s)\n if (!isNaN(d.getTime())) return Math.floor(d.getTime() / 1000)\n return s\n }\n\n const cleaned = s.replace(/[$€£¥,\\s]/g, '')\n const n = parseFloat(cleaned)\n if (!isNaN(n) && isFinite(n)) return n\n\n return s\n}\n\nexport function buildTableSelect(collectionName: string, columns: ResolvedColumn[]): string {\n const tbl = collectionTableName(collectionName)\n const parts: string[] = ['_row_id', '_created_by', '_created_at', '_updated_at']\n for (const col of columns) {\n if (col.expression) {\n parts.push(`(${col.expression}) AS \"${col.id}\"`)\n } else {\n parts.push(`\"${col.id}\"`)\n }\n }\n return `SELECT ${parts.join(', ')} FROM \"${tbl}\"`\n}\n\nexport interface User {\n id: string\n email: string\n name: string\n imageUrl?: string\n role: string\n createdAt: string\n lastSeenAt: string\n}\n\nexport interface StoredRecord {\n collection: string\n recordId: string\n data: Record<string, unknown>\n createdBy: string\n createdAt: string\n updatedAt: string\n}\n\n// ============================================================================\n// Permission Checking\n// ============================================================================\n\nexport interface PermissionContext {\n isTeamMember: (teamId: string, userId: string) => boolean\n}\n\nexport const noopPermissionContext: PermissionContext = {\n isTeamMember: () => false,\n}\n\nexport function getRolePermissions(schema: CollectionSchema, role: string): RolePermissions {\n if (schema.permissions[role]) return schema.permissions[role]\n if (schema.permissions['*']) return schema.permissions['*']\n return { read: false, create: false, update: false, delete: false }\n}\n\nexport function isOwner(\n schema: CollectionSchema,\n record: { data: Record<string, unknown>; createdBy: string },\n userId: string,\n): boolean {\n if (schema.ownerField) return record.data[schema.ownerField] === userId\n return record.createdBy === userId\n}\n\nfunction isCollaborator(\n schema: CollectionSchema,\n record: { data: Record<string, unknown> },\n userId: string,\n): boolean {\n if (!schema.collaboratorsField) return false\n const collab = record.data[schema.collaboratorsField]\n if (typeof collab === 'string') {\n try {\n return (JSON.parse(collab) as string[]).includes(userId)\n } catch {\n return false\n }\n }\n if (Array.isArray(collab)) return collab.includes(userId)\n return false\n}\n\nfunction passesVisibilityCheck(\n schema: CollectionSchema,\n record: { data: Record<string, unknown> },\n): boolean {\n if (!schema.visibilityField) return false\n if (typeof schema.visibilityField === 'string') {\n return record.data[schema.visibilityField] === 'public'\n }\n return record.data[schema.visibilityField.field] === schema.visibilityField.value\n}\n\nfunction checkPermissionLevel(\n level: PermissionLevel,\n schema: CollectionSchema,\n record: { data: Record<string, unknown>; createdBy: string; recordId?: string },\n userId: string,\n ctx: PermissionContext = noopPermissionContext,\n): boolean {\n if (level === true) return true\n if (level === false) return false\n if (level === 'own') return isOwner(schema, record, userId)\n if (level === 'unclaimed-or-own') {\n if (schema.ownerField) {\n const ownerVal = record.data[schema.ownerField]\n if (!ownerVal || ownerVal === '') return true\n }\n return isOwner(schema, record, userId)\n }\n if (level === 'collaborator') {\n return isOwner(schema, record, userId) || isCollaborator(schema, record, userId)\n }\n if (level === 'team' || level === 'access') {\n if (isOwner(schema, record, userId)) return true\n if (isCollaborator(schema, record, userId)) return true\n if (schema.teamField) {\n const teamId = record.data[schema.teamField]\n if (typeof teamId === 'string' && ctx.isTeamMember(teamId, userId)) return true\n }\n return false\n }\n if (level === 'published') {\n return isOwner(schema, record, userId) || passesVisibilityCheck(schema, record)\n }\n if (level === 'shared') {\n return (\n isOwner(schema, record, userId) ||\n isCollaborator(schema, record, userId) ||\n passesVisibilityCheck(schema, record)\n )\n }\n return false\n}\n\nexport function canRead(\n schema: CollectionSchema,\n role: string,\n record: { data: Record<string, unknown>; createdBy: string; recordId?: string },\n userId: string,\n ctx: PermissionContext = noopPermissionContext,\n): boolean {\n return checkPermissionLevel(getRolePermissions(schema, role).read, schema, record, userId, ctx)\n}\n\nexport function canCreate(schema: CollectionSchema, role: string): boolean {\n return getRolePermissions(schema, role).create === true\n}\n\nexport function canUpdate(\n schema: CollectionSchema,\n role: string,\n record: { data: Record<string, unknown>; createdBy: string; recordId?: string },\n userId: string,\n ctx: PermissionContext = noopPermissionContext,\n): boolean {\n return checkPermissionLevel(getRolePermissions(schema, role).update, schema, record, userId, ctx)\n}\n\nexport function canDelete(\n schema: CollectionSchema,\n role: string,\n record: { data: Record<string, unknown>; createdBy: string; recordId?: string },\n userId: string,\n ctx: PermissionContext = noopPermissionContext,\n): boolean {\n return checkPermissionLevel(getRolePermissions(schema, role).delete, schema, record, userId, ctx)\n}\n\n/** Check if a caller-supplied field write violates writableFields restrictions. */\nexport function checkFieldPermissions(\n schema: CollectionSchema,\n role: string,\n newData: Record<string, unknown>,\n existingData?: Record<string, unknown>,\n): string | null {\n const perms = getRolePermissions(schema, role)\n if (!perms.writableFields) return null // no restrictions\n\n const allowedFields = new Set(perms.writableFields)\n for (const key of Object.keys(newData)) {\n if (existingData && newData[key] === existingData[key]) continue\n if (!allowedFields.has(key)) {\n return `Role '${role}' cannot modify field '${key}'`\n }\n }\n return null\n}\n\n/**\n * `unclaimed-or-own` lets a client claim an empty owner field, but never lets\n * them choose another user's id. Check the post-write record so the invariant\n * covers both creation and updates while still allowing an owner to unclaim.\n */\nexport function checkUnclaimedOwnerTransition(\n schema: CollectionSchema,\n role: string,\n nextData: Record<string, unknown>,\n userId: string,\n): string | null {\n const permissions = getRolePermissions(schema, role)\n if (permissions.update !== 'unclaimed-or-own' || !schema.ownerField) return null\n\n const nextOwner = nextData[schema.ownerField]\n if (nextOwner === undefined || nextOwner === null || nextOwner === '' || nextOwner === userId) {\n return null\n }\n\n return `Role '${role}' can set '${schema.ownerField}' only to their own user id or leave it unclaimed`\n}\n\n// ============================================================================\n// System-Managed Columns (users collection)\n// ============================================================================\n\n/**\n * Columns the system *assigns* — from the identity provider at registration,\n * or from an admin action. A caller who sends one of these has chosen a value\n * and means it, so a record write that would change one is refused loudly\n * rather than silently dropped.\n */\nexport const SYSTEM_ASSIGNED_COLUMNS = new Set(['email', 'name', 'imageUrl', 'role'])\n\n/**\n * Columns the system *maintains* on its own schedule, without telling anyone.\n * `registerUser` bumps `lastSeenAt` on every WS connect and `handleUserUpdate`\n * rewrites it on every 60s presence heartbeat; neither broadcasts, so every\n * client's copy goes stale silently and there is no heal path short of a\n * refetch. Read-modify-write echo (`put(id, { ...rec.data, field })`) is the\n * SDK's own house style, so refusing these would refuse writes over a value the\n * caller never chose and cannot keep current. They are preserved silently.\n */\nexport const SYSTEM_MAINTAINED_COLUMNS = new Set(['createdAt', 'lastSeenAt'])\n\n/** Every `users` column owned by the system rather than by record writes. */\nexport const SYSTEM_MANAGED_COLUMNS = new Set([\n ...SYSTEM_ASSIGNED_COLUMNS,\n ...SYSTEM_MAINTAINED_COLUMNS,\n])\n\n/** Standard user columns. Apps spread these into their users schema. */\nexport const USERS_COLUMNS: ColumnDefinition[] = [\n { name: 'email', storage: 'text', interpretation: 'plain' },\n { name: 'name', storage: 'text', interpretation: 'plain' },\n { name: 'imageUrl', storage: 'text', interpretation: 'plain' },\n { name: 'role', storage: 'text', interpretation: 'plain' },\n { name: 'createdAt', storage: 'text', interpretation: { kind: 'datetime' } },\n { name: 'lastSeenAt', storage: 'text', interpretation: { kind: 'datetime' } },\n]\n\nexport const BASE_USERS_SCHEMA: CollectionSchema = {\n name: 'users',\n columns: [...USERS_COLUMNS],\n permissions: {\n '*': { read: 'own', create: false, update: false, delete: false },\n admin: { read: true, create: false, update: true, delete: true },\n },\n}\n\n// ============================================================================\n// Schema Registry\n// ============================================================================\n\n/**\n * Lint a CollectionSchema for declarations that look like they should\n * enforce something but don't, due to interactions between top-level\n * fields (visibilityField, ownerField) and per-role permission levels.\n *\n * The SDK has historically silently accepted schemas that imply more\n * enforcement than they actually deliver — e.g., `visibilityField` set\n * but every role's `read: true` means \"anyone can read everything\"\n * regardless of `visibility`. Warn loudly at registration so app\n * authors notice before shipping a privacy bug.\n *\n * Returns an array of warning messages. Empty = clean.\n */\nexport function lintSchema(schema: CollectionSchema): string[] {\n const warnings: string[] = []\n const VISIBILITY_AWARE: ReadonlyArray<PermissionLevel> = ['published', 'shared']\n\n // Roles that grant unconditional read despite visibilityField being declared.\n if (schema.visibilityField) {\n const offendingRoles = Object.entries(schema.permissions)\n .filter(([, perms]) => perms.read === true)\n .map(([role]) => role)\n if (offendingRoles.length > 0) {\n const usesAware = Object.values(schema.permissions).some(\n (p) => typeof p.read === 'string' && VISIBILITY_AWARE.includes(p.read as PermissionLevel),\n )\n if (!usesAware) {\n warnings.push(\n `[${schema.name}] visibilityField is declared but no role uses read: 'published' or 'shared'. ` +\n `Roles with read: true (${offendingRoles.join(', ')}) will see every row regardless of visibility. ` +\n `Change those to read: 'published' (owner OR public) or 'shared' (owner OR collaborator OR public) ` +\n `to actually enforce the filter, or remove visibilityField if you don't intend to gate reads by it.`,\n )\n }\n }\n }\n\n // An ordinary client role that can create rows must not be able to claim\n // someone else's ownership. Server-written collections legitimately carry\n // a recipient/subject id instead: their non-privileged roles all have\n // create:false, while owner/admin writes are already fully trusted.\n if (schema.ownerField) {\n const col = schema.columns.find((c) => c.name === schema.ownerField)\n const spoofableRoles = Object.entries(schema.permissions)\n .filter(\n ([role, permissions]) =>\n role !== 'owner' &&\n role !== 'admin' &&\n permissions.create === true &&\n permissions.update !== 'unclaimed-or-own',\n )\n .map(([role]) => role)\n if (col && !col.userBound && spoofableRoles.length > 0) {\n warnings.push(\n `[${schema.name}] ownerField is '${schema.ownerField}' but that column is not marked userBound: true. ` +\n `Client role(s) ${spoofableRoles.join(', ')} can create a row with someone else's id in this field, bypassing 'own' permission checks. ` +\n `Add userBound: true (and ideally immutable: true) to the column.`,\n )\n }\n }\n\n // userBound columns that aren't text storage — userBound sets the\n // field to the caller's userId (a string), so non-text storage is a\n // schema mistake that will surface as a coercion error at write time.\n for (const col of schema.columns) {\n if (col.userBound && col.storage !== 'text') {\n warnings.push(\n `[${schema.name}] column '${col.name}' is userBound but storage is '${col.storage}'. ` +\n `userBound stamps the user id (a string); use storage: 'text'.`,\n )\n }\n }\n\n return warnings\n}\n\nexport class SchemaRegistry {\n private trusted: Map<string, CollectionSchema> = new Map()\n\n constructor(schemas: CollectionSchema[] = []) {\n for (const schema of schemas) {\n this.registerTrusted(schema)\n }\n }\n\n registerTrusted(schema: CollectionSchema): void {\n for (const w of lintSchema(schema)) {\n console.warn(`[schema-lint] ${w}`)\n }\n this.trusted.set(schema.name, schema)\n }\n\n get(name: string): CollectionSchema | undefined {\n return this.trusted.get(name)\n }\n\n has(name: string): boolean {\n return this.trusted.has(name)\n }\n\n all(): CollectionSchema[] {\n return Array.from(this.trusted.values())\n }\n\n names(): string[] {\n return Array.from(this.trusted.keys())\n }\n}\n\n// ============================================================================\n// Permission Analysis\n// ============================================================================\n\nexport type PermissionSource = 'explicit' | 'wildcard' | 'default-deny'\n\nexport interface ResolvedPermission {\n level: PermissionLevel | boolean\n source: PermissionSource\n}\n\nexport interface CollectionPermissionSummary {\n collection: string\n ownerField?: string\n collaboratorsField?: string\n teamField?: string\n columns: ColumnDefinition[]\n permissions: Record<\n string,\n {\n read: ResolvedPermission\n create: ResolvedPermission\n update: ResolvedPermission\n delete: ResolvedPermission\n writableFields?: string[]\n }\n >\n}\n\nexport interface PermissionAnalysis {\n roles: string[]\n collections: CollectionPermissionSummary[]\n}\n\nexport function analyzePermissions(schemas: CollectionSchema[]): PermissionAnalysis {\n const roleSet = new Set<string>()\n for (const schema of schemas) {\n for (const role of Object.keys(schema.permissions)) roleSet.add(role)\n }\n roleSet.delete('*')\n const roles = Array.from(roleSet)\n\n const collections: CollectionPermissionSummary[] = schemas.map((schema) => {\n const permissions: CollectionPermissionSummary['permissions'] = {}\n for (const role of roles) {\n let source: PermissionSource\n let perms: RolePermissions\n if (schema.permissions[role]) {\n source = 'explicit'\n perms = schema.permissions[role]\n } else if (schema.permissions['*']) {\n source = 'wildcard'\n perms = schema.permissions['*']\n } else {\n source = 'default-deny'\n perms = { read: false, create: false, update: false, delete: false }\n }\n permissions[role] = {\n read: { level: perms.read, source },\n create: { level: perms.create, source },\n update: { level: perms.update, source },\n delete: { level: perms.delete, source },\n writableFields: perms.writableFields,\n }\n }\n return {\n collection: schema.name,\n ownerField: schema.ownerField,\n collaboratorsField: schema.collaboratorsField,\n teamField: schema.teamField,\n columns: schema.columns,\n permissions,\n }\n })\n\n return { roles, collections }\n}\n","/**\n * AI Chat Schemas\n *\n * Pre-built collection schemas for DO-backed AI chat history.\n * The worker is the only writer; the client reads via `useQuery`.\n */\n\nimport type { CollectionSchema } from './registry'\n\n// member.create is false on both collections: writes flow exclusively through\n// the worker's /api/ai/* routes, which use X-App-Action to bypass RBAC.\n// Allowing direct WS creates would let a user PUT fake `role: 'assistant'`\n// rows into their own chats and have the next turn's loadMessages feed them\n// to the LLM as if they were real assistant outputs.\nexport const AI_CHATS_SCHEMA: CollectionSchema = {\n name: 'ai-chats',\n columns: [\n { name: 'userId', storage: 'text', interpretation: 'plain', userBound: true, immutable: true, required: true },\n { name: 'title', storage: 'text', interpretation: 'plain' },\n { name: 'model', storage: 'text', interpretation: 'plain' },\n { name: 'compactedSummary', storage: 'text', interpretation: 'plain' },\n { name: 'compactedThroughId', storage: 'text', interpretation: 'plain' },\n ],\n ownerField: 'userId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: 'own', create: false, update: 'own', delete: 'own' },\n viewer: { read: 'own', create: false, update: false, delete: false },\n },\n}\n\nexport const AI_MESSAGES_SCHEMA: CollectionSchema = {\n name: 'ai-messages',\n columns: [\n { name: 'chatId', storage: 'text', interpretation: 'plain', immutable: true, required: true },\n { name: 'userId', storage: 'text', interpretation: 'plain', userBound: true, immutable: true, required: true },\n { name: 'role', storage: 'text', interpretation: 'plain', required: true },\n { name: 'content', storage: 'text', interpretation: 'plain' },\n { name: 'parts', storage: 'text', interpretation: { kind: 'json' } },\n ],\n ownerField: 'userId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: 'own', create: false, update: 'own', delete: 'own' },\n viewer: { read: 'own', create: false, update: false, delete: false },\n },\n}\n","/**\n * Messaging Schemas\n *\n * Pre-built collection schemas for messaging functionality.\n * Any app can import these to add channels, messages, reactions, etc.\n *\n * @example\n * ```typescript\n * import { CHANNELS_SCHEMA, MESSAGES_SCHEMA, REACTIONS_SCHEMA } from 'deepspace/worker'\n * export const schemas = [usersSchema, CHANNELS_SCHEMA, MESSAGES_SCHEMA, REACTIONS_SCHEMA]\n * ```\n */\n\nimport type { CollectionSchema } from './registry'\n\nexport const CHANNELS_SCHEMA: CollectionSchema = {\n name: 'channels',\n columns: [\n { name: 'name', storage: 'text', interpretation: 'plain' },\n { name: 'description', storage: 'text', interpretation: 'plain' },\n {\n name: 'type',\n storage: 'text',\n interpretation: { kind: 'select', options: ['public', 'private', 'dm'] },\n },\n {\n name: 'createdBy',\n storage: 'text',\n interpretation: 'plain',\n userBound: true,\n immutable: true,\n },\n { name: 'archived', storage: 'number', interpretation: { kind: 'boolean' } },\n ],\n ownerField: 'createdBy',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: true, create: true, update: 'own', delete: false },\n viewer: { read: true, create: false, update: false, delete: false },\n },\n}\n\nexport const MESSAGES_SCHEMA: CollectionSchema = {\n name: 'messages',\n columns: [\n { name: 'channelId', storage: 'text', interpretation: 'plain' },\n { name: 'content', storage: 'text', interpretation: 'plain' },\n {\n name: 'authorId',\n storage: 'text',\n interpretation: 'plain',\n userBound: true,\n immutable: true,\n },\n { name: 'parentMessageId', storage: 'text', interpretation: 'plain' },\n { name: 'edited', storage: 'number', interpretation: { kind: 'boolean' } },\n { name: 'editedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n // Soft-delete flag. Slack-style messaging keeps the row + parent IDs\n // intact (so threads remain coherent) and renders a tombstone instead.\n // Hard-delete via remove() still works if an app wants it.\n { name: 'deleted', storage: 'number', interpretation: { kind: 'boolean' } },\n ],\n ownerField: 'authorId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: true, create: true, update: 'own', delete: 'own' },\n viewer: { read: true, create: false, update: false, delete: false },\n },\n}\n\nexport const REACTIONS_SCHEMA: CollectionSchema = {\n name: 'reactions',\n columns: [\n { name: 'messageId', storage: 'text', interpretation: 'plain' },\n { name: 'channelId', storage: 'text', interpretation: 'plain' },\n { name: 'emoji', storage: 'text', interpretation: 'plain' },\n { name: 'userId', storage: 'text', interpretation: 'plain', userBound: true, immutable: true },\n ],\n ownerField: 'userId',\n uniqueOn: ['messageId', 'emoji', 'userId'],\n permissions: {\n admin: { read: true, create: true, update: false, delete: true },\n member: { read: true, create: true, update: false, delete: 'own' },\n viewer: { read: true, create: false, update: false, delete: false },\n },\n}\n\nexport const CHANNEL_MEMBERS_SCHEMA: CollectionSchema = {\n name: 'channel-members',\n columns: [\n { name: 'channelId', storage: 'text', interpretation: 'plain' },\n { name: 'userId', storage: 'text', interpretation: 'plain', userBound: true, immutable: true },\n { name: 'joinedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n ],\n ownerField: 'userId',\n uniqueOn: ['channelId', 'userId'],\n permissions: {\n admin: { read: true, create: true, update: false, delete: true },\n member: { read: true, create: true, update: false, delete: 'own' },\n viewer: { read: true, create: false, update: false, delete: false },\n },\n}\n\nexport const CHANNEL_INVITATIONS_SCHEMA: CollectionSchema = {\n name: 'channel-invitations',\n columns: [\n {\n name: 'channelId',\n storage: 'text',\n interpretation: 'plain',\n required: true,\n immutable: true,\n },\n {\n name: 'invitedUserId',\n storage: 'text',\n interpretation: 'plain',\n required: true,\n immutable: true,\n },\n {\n name: 'invitedBy',\n storage: 'text',\n interpretation: 'plain',\n userBound: true,\n immutable: true,\n },\n {\n name: 'status',\n storage: 'text',\n interpretation: { kind: 'select', options: ['pending', 'accepted', 'declined'] },\n required: true,\n },\n ],\n uniqueOn: ['channelId', 'invitedUserId'],\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: true, create: true, update: true, delete: false },\n viewer: { read: true, create: false, update: false, delete: false },\n },\n}\n\nexport const READ_RECEIPTS_SCHEMA: CollectionSchema = {\n name: 'read-receipts',\n columns: [\n { name: 'channelId', storage: 'text', interpretation: 'plain' },\n { name: 'userId', storage: 'text', interpretation: 'plain', userBound: true, immutable: true },\n { name: 'lastReadAt', storage: 'text', interpretation: { kind: 'datetime' } },\n ],\n ownerField: 'userId',\n uniqueOn: ['channelId', 'userId'],\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: true, create: true, update: 'own', delete: 'own' },\n viewer: { read: false, create: false, update: false, delete: false },\n },\n}\n","/**\n * Shared conversation schemas for RecordRoom-based conversations.\n *\n * These define the collections inside a per-conversation RecordRoom DO.\n * Used by apps that have messaging/conversation features (slack-clone,\n * helpdesk, mail, reddit-clone, etc.).\n *\n * Each conversation gets its own RecordRoom DO keyed by `conv:{id}`.\n */\n\nimport type { CollectionSchema } from './registry'\n\n// Re-export data interfaces from types (single source of truth)\nexport type {\n ConvMessageData,\n ConvReactionData,\n ConvMemberData,\n ConvReadCursorData,\n ConvVoteData,\n} from '../../shared/types'\n\n// ============================================================================\n// Schemas\n// ============================================================================\n\nexport const CONVERSATION_SCHEMAS: CollectionSchema[] = [\n {\n name: 'conv_messages',\n columns: [\n { name: 'Content', storage: 'text', interpretation: 'plain' },\n { name: 'AuthorId', storage: 'text', interpretation: 'plain' },\n { name: 'ParentId', storage: 'text', interpretation: 'plain' },\n { name: 'Edited', storage: 'number', interpretation: 'boolean' },\n { name: 'MessageType', storage: 'text', interpretation: 'plain' },\n { name: 'Metadata', storage: 'text', interpretation: 'json' },\n ],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: 'own', delete: 'own' },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'AuthorId',\n },\n {\n name: 'conv_reactions',\n columns: [\n { name: 'MessageId', storage: 'text', interpretation: 'plain' },\n { name: 'Emoji', storage: 'text', interpretation: 'plain' },\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n ],\n uniqueOn: ['MessageId', 'Emoji', 'UserId'],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: false, delete: 'own' },\n admin: { read: true, create: true, update: false, delete: true },\n },\n ownerField: 'UserId',\n },\n {\n name: 'conv_members',\n columns: [\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n { name: 'UserName', storage: 'text', interpretation: 'plain' },\n { name: 'Role', storage: 'text', interpretation: 'plain' },\n ],\n uniqueOn: ['UserId'],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: 'own', delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'UserId',\n },\n {\n name: 'conv_read_cursors',\n columns: [\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n { name: 'LastReadAt', storage: 'text', interpretation: 'plain' },\n ],\n uniqueOn: ['UserId'],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: 'own', delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'UserId',\n },\n]\n\n/**\n * Voting schemas for Reddit-style apps.\n * Add these alongside CONVERSATION_SCHEMAS for apps that need voting.\n */\nexport const VOTING_SCHEMAS: CollectionSchema[] = [\n {\n name: 'conv_votes',\n columns: [\n { name: 'TargetId', storage: 'text', interpretation: 'plain' },\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n { name: 'Direction', storage: 'number', interpretation: 'plain' },\n ],\n uniqueOn: ['TargetId', 'UserId'],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: 'own', delete: 'own' },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'UserId',\n },\n]\n","/**\n * Directory DO Schemas\n *\n * Purpose-built collections for the `dir:{appName}` global DO type.\n * This is the cross-app-visible directory layer — any app can subscribe\n * to another app's directory in real-time via SHARED_CONNECTIONS.\n *\n * Five collections covering all standard communication/social patterns:\n * - conversations: channels, DMs, email threads, support chats\n * - conversation_state: per-user metadata (read cursor, stars, labels, folders)\n * - communities: groups, forums, boards, projects\n * - memberships: user membership in communities\n * - posts: feed items, tweets, Q&A questions, announcements\n *\n * Domain-specific data (tickets, CRM, procurement) stays in app DOs.\n * Message content stays in conv:{id} DOs (CONVERSATION_SCHEMAS).\n */\n\nimport type { CollectionSchema } from './registry'\n\n// Re-export data interfaces from types (single source of truth)\nexport type {\n DirectoryConversationData,\n ConversationStateData,\n DirectoryCommunityData,\n DirectoryMembershipData,\n DirectoryPostData,\n} from '../../shared/types'\n\n// ============================================================================\n// Schemas\n// ============================================================================\n\nconst conversationsSchema: CollectionSchema = {\n name: 'conversations',\n columns: [\n { name: 'Name', storage: 'text', interpretation: 'plain' },\n { name: 'Description', storage: 'text', interpretation: 'plain' },\n { name: 'Type', storage: 'text', interpretation: { kind: 'select', options: ['public', 'private', 'dm', 'group', 'email', 'outbound-email', 'support', 'announcement'] } },\n { name: 'Visibility', storage: 'text', interpretation: { kind: 'select', options: ['public', 'private', 'restricted'] } },\n { name: 'CreatedBy', storage: 'text', interpretation: 'plain' },\n { name: 'ParticipantHash', storage: 'text', interpretation: 'plain' },\n { name: 'ParticipantIds', storage: 'text', interpretation: 'json' },\n { name: 'Status', storage: 'text', interpretation: { kind: 'select', options: ['active', 'archived', 'resolved', 'closed'] } },\n { name: 'AssigneeId', storage: 'text', interpretation: 'plain' },\n { name: 'LinkedRef', storage: 'text', interpretation: 'json' },\n { name: 'LastMessageAt', storage: 'text', interpretation: { kind: 'datetime' } },\n { name: 'LastMessagePreview', storage: 'text', interpretation: 'plain' },\n { name: 'LastMessageAuthor', storage: 'text', interpretation: 'plain' },\n { name: 'MessageCount', storage: 'number', interpretation: 'plain' },\n ],\n collaboratorsField: 'ParticipantIds',\n visibilityField: 'Visibility',\n permissions: {\n viewer: { read: 'shared', create: false, update: false, delete: false },\n member: { read: 'shared', create: true, update: true, delete: 'own' },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'CreatedBy',\n}\n\nconst conversationStateSchema: CollectionSchema = {\n name: 'conversation_state',\n columns: [\n { name: 'ConversationId', storage: 'text', interpretation: 'plain' },\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n { name: 'LastReadAt', storage: 'text', interpretation: { kind: 'datetime' } },\n { name: 'LastReadMessageCount', storage: 'number', interpretation: 'plain' },\n { name: 'Starred', storage: 'number', interpretation: 'boolean' },\n { name: 'Archived', storage: 'number', interpretation: 'boolean' },\n { name: 'Trashed', storage: 'number', interpretation: 'boolean' },\n { name: 'Labels', storage: 'text', interpretation: 'json' },\n { name: 'Folder', storage: 'text', interpretation: 'plain' },\n ],\n uniqueOn: ['ConversationId', 'UserId'],\n ownerField: 'UserId',\n permissions: {\n viewer: { read: false, create: false, update: false, delete: false },\n member: { read: 'own', create: true, update: 'own', delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n}\n\nconst communitiesSchema: CollectionSchema = {\n name: 'communities',\n columns: [\n { name: 'Name', storage: 'text', interpretation: 'plain' },\n { name: 'Description', storage: 'text', interpretation: 'plain' },\n { name: 'CreatedBy', storage: 'text', interpretation: 'plain' },\n { name: 'Type', storage: 'text', interpretation: { kind: 'select', options: ['community', 'forum', 'board', 'project', 'server'] } },\n { name: 'Visibility', storage: 'text', interpretation: { kind: 'select', options: ['public', 'private', 'restricted'] } },\n { name: 'MemberCount', storage: 'number', interpretation: 'plain' },\n { name: 'Rules', storage: 'text', interpretation: 'json' },\n { name: 'IconUrl', storage: 'text', interpretation: 'plain' },\n { name: 'CoverUrl', storage: 'text', interpretation: 'plain' },\n ],\n uniqueOn: ['Name'],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: 'own', delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'CreatedBy',\n}\n\nconst membershipsSchema: CollectionSchema = {\n name: 'memberships',\n columns: [\n { name: 'CommunityId', storage: 'text', interpretation: 'plain' },\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n { name: 'UserName', storage: 'text', interpretation: 'plain' },\n { name: 'Role', storage: 'text', interpretation: { kind: 'select', options: ['member', 'moderator', 'admin'] } },\n { name: 'JoinedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n ],\n uniqueOn: ['CommunityId', 'UserId'],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: 'own', delete: 'own' },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'UserId',\n}\n\nconst postsSchema: CollectionSchema = {\n name: 'posts',\n columns: [\n { name: 'Title', storage: 'text', interpretation: 'plain' },\n { name: 'Content', storage: 'text', interpretation: 'plain' },\n { name: 'AuthorId', storage: 'text', interpretation: 'plain' },\n { name: 'Type', storage: 'text', interpretation: { kind: 'select', options: ['post', 'question', 'answer', 'tweet', 'announcement', 'link'] } },\n { name: 'CommunityId', storage: 'text', interpretation: 'plain' },\n { name: 'ParentId', storage: 'text', interpretation: 'plain' },\n { name: 'ConversationId', storage: 'text', interpretation: 'plain' },\n { name: 'Status', storage: 'text', interpretation: { kind: 'select', options: ['draft', 'published', 'pinned', 'archived', 'accepted'] } },\n { name: 'Tags', storage: 'text', interpretation: 'json' },\n { name: 'LinkUrl', storage: 'text', interpretation: 'plain' },\n ],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: 'own', delete: 'own' },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'AuthorId',\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\n/** All directory schemas for the `dir:{appName}` global DO type. */\nexport const DIRECTORY_SCHEMAS: CollectionSchema[] = [\n conversationsSchema,\n conversationStateSchema,\n communitiesSchema,\n membershipsSchema,\n postsSchema,\n]\n","/**\n * Workspace DO Schemas\n *\n * All collections for the workspace:default Durable Object.\n * This DO consolidates shared, cross-app business data:\n *\n * - teams / team_members — workspace-wide team management (replaces built-in teams)\n * - tasks / projects / tags — team-scoped task management\n * - people — shared contacts directory\n * - transactions / accounts — shared financial ledger\n *\n * Team-scoped collections use teamField RBAC: members see only\n * their team's records; admins see everything.\n */\n\nimport type { CollectionSchema } from './registry'\n\n// ============================================================================\n// Teams\n// ============================================================================\n\nexport const workspaceTeamsSchema: CollectionSchema = {\n name: 'teams',\n columns: [\n { name: 'Name', storage: 'text', interpretation: 'plain' },\n { name: 'CreatedBy', storage: 'text', interpretation: 'plain' },\n { name: 'IsOpen', storage: 'number', interpretation: { kind: 'boolean' } },\n ],\n teamField: '_rowId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: 'team', create: true, update: 'team', delete: 'own' },\n },\n}\n\nexport const workspaceTeamMembersSchema: CollectionSchema = {\n name: 'team_members',\n columns: [\n { name: 'TeamId', storage: 'text', interpretation: 'plain' },\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n { name: 'RoleInTeam', storage: 'text', interpretation: { kind: 'select', options: ['admin', 'member'] } },\n { name: 'JoinedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n { name: 'Email', storage: 'text', interpretation: { kind: 'email' } },\n { name: 'Status', storage: 'text', interpretation: { kind: 'select', options: ['active', 'invited'] } },\n ],\n teamField: 'TeamId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: 'team', create: true, update: 'team', delete: 'team' },\n },\n}\n\n// ============================================================================\n// Tasks / Projects / Tags (team-scoped)\n// ============================================================================\n\nexport const workspaceTasksSchema: CollectionSchema = {\n name: 'tasks',\n columns: [\n { name: 'TeamId', storage: 'text', interpretation: 'plain' },\n { name: 'Title', storage: 'text', interpretation: 'plain' },\n { name: 'Notes', storage: 'text', interpretation: 'plain' },\n { name: 'Completed', storage: 'number', interpretation: { kind: 'boolean' } },\n { name: 'CompletedAt', storage: 'number', interpretation: 'plain' },\n { name: 'Deleted', storage: 'number', interpretation: { kind: 'boolean' } },\n { name: 'DeletedAt', storage: 'number', interpretation: 'plain' },\n { name: 'Priority', storage: 'text', interpretation: { kind: 'select', options: ['none', 'low', 'medium', 'high'] } },\n { name: 'DueDate', storage: 'text', interpretation: 'plain' },\n { name: 'ProjectId', storage: 'text', interpretation: 'plain' },\n { name: 'KanbanStatus', storage: 'text', interpretation: { kind: 'select', options: ['backlog', 'ready', 'in_progress', 'review', 'done'] } },\n { name: 'Order', storage: 'number', interpretation: 'plain' },\n { name: 'AssignedUser', storage: 'text', interpretation: { kind: 'json' } },\n { name: 'AssignedBy', storage: 'text', interpretation: { kind: 'json' } },\n { name: 'TagIds', storage: 'text', interpretation: { kind: 'json' } },\n { name: 'CreatedAt', storage: 'number', interpretation: 'plain' },\n ],\n teamField: 'TeamId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: 'team', create: true, update: 'team', delete: 'team' },\n viewer: { read: false, create: false, update: false, delete: false },\n },\n}\n\nexport const workspaceProjectsSchema: CollectionSchema = {\n name: 'projects',\n columns: [\n { name: 'TeamId', storage: 'text', interpretation: 'plain' },\n { name: 'Title', storage: 'text', interpretation: 'plain' },\n { name: 'Notes', storage: 'text', interpretation: 'plain' },\n { name: 'Color', storage: 'text', interpretation: 'plain' },\n { name: 'ParentId', storage: 'text', interpretation: 'plain' },\n { name: 'Order', storage: 'number', interpretation: 'plain' },\n { name: 'CreatedAt', storage: 'number', interpretation: 'plain' },\n ],\n teamField: 'TeamId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: 'team', create: true, update: 'team', delete: 'team' },\n viewer: { read: false, create: false, update: false, delete: false },\n },\n}\n\nexport const workspaceTagsSchema: CollectionSchema = {\n name: 'tags',\n columns: [\n { name: 'TeamId', storage: 'text', interpretation: 'plain' },\n { name: 'Name', storage: 'text', interpretation: 'plain' },\n { name: 'Color', storage: 'text', interpretation: 'plain' },\n { name: 'CreatedAt', storage: 'number', interpretation: 'plain' },\n ],\n teamField: 'TeamId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: 'team', create: true, update: 'team', delete: 'team' },\n viewer: { read: false, create: false, update: false, delete: false },\n },\n}\n\n// ============================================================================\n// People (shared contacts directory)\n// ============================================================================\n\nexport const workspacePeopleSchema: CollectionSchema = {\n name: 'people',\n columns: [\n { name: 'Name', storage: 'text', interpretation: 'plain' },\n { name: 'Email', storage: 'text', interpretation: { kind: 'email' } },\n { name: 'Type', storage: 'text', interpretation: { kind: 'select', options: ['employee', 'customer', 'vendor', 'contact'] } },\n { name: 'Status', storage: 'text', interpretation: { kind: 'select', options: ['active', 'inactive', 'archived'] } },\n { name: 'CompanyId', storage: 'text', interpretation: 'plain' },\n { name: 'Department', storage: 'text', interpretation: 'plain' },\n { name: 'Title', storage: 'text', interpretation: 'plain' },\n { name: 'LastContactedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n { name: 'Metadata', storage: 'text', interpretation: { kind: 'json' } },\n ],\n permissions: {\n '*': { read: true, create: true, update: true, delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n}\n\n// ============================================================================\n// Ledger (shared financial data)\n// ============================================================================\n\nexport const workspaceTransactionsSchema: CollectionSchema = {\n name: 'transactions',\n columns: [\n { name: 'Type', storage: 'text', interpretation: { kind: 'select', options: ['revenue', 'expense', 'payment', 'transfer', 'invoice', 'refund'] } },\n { name: 'Amount', storage: 'number', interpretation: { kind: 'currency', symbol: '$', decimals: 2 } },\n { name: 'Currency', storage: 'text', interpretation: 'plain' },\n { name: 'Description', storage: 'text', interpretation: 'plain' },\n { name: 'Category', storage: 'text', interpretation: 'plain' },\n { name: 'CounterpartyRef', storage: 'text', interpretation: 'plain' },\n { name: 'OrderRef', storage: 'text', interpretation: 'plain' },\n { name: 'Account', storage: 'text', interpretation: 'plain' },\n ],\n permissions: {\n '*': { read: true, create: true, update: true, delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n}\n\nexport const workspaceAccountsSchema: CollectionSchema = {\n name: 'accounts',\n columns: [\n { name: 'Name', storage: 'text', interpretation: 'plain' },\n { name: 'Balance', storage: 'number', interpretation: { kind: 'currency', symbol: '$', decimals: 2 } },\n { name: 'Currency', storage: 'text', interpretation: 'plain' },\n ],\n permissions: {\n '*': { read: true, create: true, update: true, delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n}\n\n// ============================================================================\n// Content Sharing (universal cross-app content discovery)\n// ============================================================================\n\n/**\n * Universal sharing index. Any app can create share records for any content type.\n *\n * ContentType: 'document' | 'slide' | 'spreadsheet' | ... (extensible)\n * ShareType: 'channel' | 'team' | 'direct' | 'link' | 'org' (extensible)\n * ShareTarget: the ID of the target (channelId, teamId, userId, linkId, orgId)\n * Permission: 'view' | 'edit' — what the share grants\n *\n * Content itself lives in the owner's app DO.\n * This table is the discovery layer: \"what has been shared, with whom, and how\".\n */\nexport const workspaceContentSharesSchema: CollectionSchema = {\n name: 'content_shares',\n ownerField: 'OwnerId',\n columns: [\n { name: 'ContentType', storage: 'text', interpretation: { kind: 'select', options: ['document', 'slide', 'spreadsheet'] } },\n { name: 'ContentId', storage: 'text', interpretation: 'plain' },\n { name: 'OwnerId', storage: 'text', interpretation: 'plain' },\n { name: 'OwnerName', storage: 'text', interpretation: 'plain' },\n { name: 'Title', storage: 'text', interpretation: 'plain' },\n { name: 'ShareType', storage: 'text', interpretation: { kind: 'select', options: ['self', 'channel', 'team', 'direct', 'link', 'org'] } },\n { name: 'ShareTarget', storage: 'text', interpretation: 'plain' },\n { name: 'Permission', storage: 'text', interpretation: { kind: 'select', options: ['view', 'edit'] } },\n { name: 'SharedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n { name: 'SharedBy', storage: 'text', interpretation: 'plain' },\n { name: 'SourceApp', storage: 'text', interpretation: 'plain' },\n { name: 'WordCount', storage: 'number', interpretation: 'plain' },\n { name: 'LastEditedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n ],\n permissions: {\n '*': { read: true, create: true, update: true, delete: 'own' },\n admin: { read: true, create: true, update: true, delete: true },\n },\n}\n\n// ============================================================================\n// Form Responses (cross-app: deepspace-forms → sheets-editor)\n// ============================================================================\n\nexport const workspaceFormResponsesSchema: CollectionSchema = {\n name: 'form_responses',\n columns: [\n { name: 'FormId', storage: 'text', interpretation: 'plain' },\n { name: 'FormTitle', storage: 'text', interpretation: 'plain' },\n { name: 'RespondentId', storage: 'text', interpretation: 'plain' },\n { name: 'RespondentEmail', storage: 'text', interpretation: { kind: 'email' } },\n { name: 'RespondentName', storage: 'text', interpretation: 'plain' },\n { name: 'Data', storage: 'text', interpretation: { kind: 'json' } },\n { name: 'SubmittedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n ],\n permissions: {\n '*': { read: true, create: true, update: false, delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n}\n\n// ============================================================================\n// Email Handles (cross-app user identity)\n// ============================================================================\n\n/**\n * Maps DeepSpace users to their claimed @app.space email handles.\n * Shared across all apps so any app can look up a user's email address.\n * All handles are under the @app.space domain.\n */\nexport const workspaceEmailHandlesSchema: CollectionSchema = {\n name: 'email_handles',\n columns: [\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n { name: 'Handle', storage: 'text', interpretation: 'plain' },\n { name: 'EmailAddress', storage: 'text', interpretation: { kind: 'email' } },\n { name: 'Status', storage: 'text', interpretation: { kind: 'select', options: ['active', 'disabled'] } },\n ],\n uniqueOn: ['Handle'],\n ownerField: 'UserId',\n permissions: {\n '*': { read: true, create: true, update: 'own', delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n}\n\n// ============================================================================\n// Export\n// ============================================================================\n\nexport const WORKSPACE_SCHEMAS: CollectionSchema[] = [\n workspaceTeamsSchema,\n workspaceTeamMembersSchema,\n workspaceTasksSchema,\n workspaceProjectsSchema,\n workspaceTagsSchema,\n workspacePeopleSchema,\n workspaceTransactionsSchema,\n workspaceAccountsSchema,\n workspaceContentSharesSchema,\n workspaceFormResponsesSchema,\n workspaceEmailHandlesSchema,\n]\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server/schemas/registry.ts","../src/server/schemas/ai-chat.ts","../src/server/schemas/messaging.ts","../src/server/schemas/conversation.ts","../src/server/schemas/directory.ts","../src/server/schemas/workspace.ts"],"sourcesContent":["/**\n * Collection Schema Definitions & Validation\n *\n * All collections use typed SQL columns. No document-mode / fields-based storage.\n */\n\nimport type {\n CollectionSchema,\n ColumnDefinition,\n ColumnInterpretation,\n PermissionLevel,\n RolePermissions,\n} from '../../shared/types'\n\nexport type {\n CollectionSchema,\n ColumnDefinition,\n ColumnInterpretation,\n PermissionLevel,\n RolePermissions,\n} from '../../shared/types'\n\n// ============================================================================\n// Column Definitions\n// ============================================================================\n\nexport interface ResolvedColumn {\n id: string\n name: string\n storage: 'number' | 'text'\n interpretation: ColumnInterpretation\n expression?: string\n readonly: boolean\n userBound?: boolean\n immutable?: boolean\n required?: boolean\n default?: unknown\n timestampTrigger?: { field: string; value?: unknown }\n}\n\n// ============================================================================\n// Column Helpers\n// ============================================================================\n\nexport function collectionTableName(name: string): string {\n return `c_${name.replace(/[^a-zA-Z0-9_]/g, '_')}`\n}\n\nexport function columnId(name: string): string {\n return `col_${name.toLowerCase().replace(/[^a-z0-9]/g, '_')}`\n}\n\nexport function resolveColumn(col: ColumnDefinition): ResolvedColumn {\n const interp: ColumnInterpretation =\n typeof col.interpretation === 'string'\n ? ({ kind: col.interpretation } as ColumnInterpretation)\n : col.interpretation\n return {\n id: col.id ?? columnId(col.name),\n name: col.name,\n storage: col.storage,\n interpretation: interp,\n expression: col.expression,\n readonly: !!col.expression,\n userBound: col.userBound,\n immutable: col.immutable,\n required: col.required,\n default: col.default,\n timestampTrigger: col.timestampTrigger,\n }\n}\n\nexport function rowToData(\n row: Record<string, unknown>,\n columns: ResolvedColumn[],\n): Record<string, unknown> {\n const data: Record<string, unknown> = {}\n for (const col of columns) {\n if (row[col.id] !== undefined && row[col.id] !== null) {\n let val = row[col.id]\n if (col.interpretation.kind === 'json' && typeof val === 'string') {\n try {\n val = JSON.parse(val as string)\n } catch {\n /* keep as string */\n }\n }\n data[col.name] = val\n }\n }\n return data\n}\n\nexport function dataToColumnValues(\n data: Record<string, unknown>,\n columns: ResolvedColumn[],\n): Record<string, unknown> {\n const values: Record<string, unknown> = {}\n for (const col of columns) {\n if (col.expression || col.readonly) continue\n // Record mutations use public schema names only. Accepting physical column\n // ids here lets a second key bypass name-based RBAC/userBound checks.\n const val = data[col.name]\n if (val !== undefined) {\n values[col.id] = coerceValue(val, col.storage, col.interpretation)\n }\n }\n return values\n}\n\nexport function coerceValue(\n value: unknown,\n storage: 'number' | 'text',\n interpretation: ColumnInterpretation,\n): unknown {\n if (value === null || value === undefined || value === '') return null\n\n if (storage === 'text') {\n if (interpretation.kind === 'json') {\n if (typeof value === 'string') return value\n try {\n return JSON.stringify(value)\n } catch {\n return null\n }\n }\n return typeof value === 'string' ? value : String(value)\n }\n\n if (typeof value === 'number') return isFinite(value) ? value : null\n if (typeof value === 'boolean') return value ? 1 : 0\n if (typeof value !== 'string') return String(value)\n\n const s = value.trim()\n\n if (interpretation.kind === 'boolean') {\n const lower = s.toLowerCase()\n if (lower === 'true' || lower === '1' || lower === 'yes') return 1\n if (lower === 'false' || lower === '0' || lower === 'no') return 0\n return s\n }\n\n if (interpretation.kind === 'percent') {\n const m = s.match(/^(-?\\d+(?:\\.\\d+)?)\\s*%$/)\n if (m) return parseFloat(m[1]) / 100\n }\n\n if (interpretation.kind === 'currency') {\n const cleaned = s.replace(/[^0-9.-]/g, '')\n const n = parseFloat(cleaned)\n if (!isNaN(n)) return n\n return s\n }\n\n if (interpretation.kind === 'date' || interpretation.kind === 'datetime') {\n const d = new Date(s)\n if (!isNaN(d.getTime())) return Math.floor(d.getTime() / 1000)\n return s\n }\n\n const cleaned = s.replace(/[$€£¥,\\s]/g, '')\n const n = parseFloat(cleaned)\n if (!isNaN(n) && isFinite(n)) return n\n\n return s\n}\n\nexport function buildTableSelect(collectionName: string, columns: ResolvedColumn[]): string {\n const tbl = collectionTableName(collectionName)\n const parts: string[] = ['_row_id', '_created_by', '_created_at', '_updated_at']\n for (const col of columns) {\n if (col.expression) {\n parts.push(`(${col.expression}) AS \"${col.id}\"`)\n } else {\n parts.push(`\"${col.id}\"`)\n }\n }\n return `SELECT ${parts.join(', ')} FROM \"${tbl}\"`\n}\n\nexport interface User {\n id: string\n email: string\n name: string\n imageUrl?: string\n role: string\n createdAt: string\n lastSeenAt: string\n}\n\nexport interface StoredRecord {\n collection: string\n recordId: string\n data: Record<string, unknown>\n createdBy: string\n createdAt: string\n updatedAt: string\n}\n\n// ============================================================================\n// Permission Checking\n// ============================================================================\n\nexport interface PermissionContext {\n isTeamMember: (teamId: string, userId: string) => boolean\n}\n\nexport const noopPermissionContext: PermissionContext = {\n isTeamMember: () => false,\n}\n\nexport function getRolePermissions(schema: CollectionSchema, role: string): RolePermissions {\n if (schema.permissions[role]) return schema.permissions[role]\n if (schema.permissions['*']) return schema.permissions['*']\n return { read: false, create: false, update: false, delete: false }\n}\n\nexport function isOwner(\n schema: CollectionSchema,\n record: { data: Record<string, unknown>; createdBy: string },\n userId: string,\n): boolean {\n if (schema.ownerField) return record.data[schema.ownerField] === userId\n return record.createdBy === userId\n}\n\nfunction isCollaborator(\n schema: CollectionSchema,\n record: { data: Record<string, unknown> },\n userId: string,\n): boolean {\n if (!schema.collaboratorsField) return false\n const collab = record.data[schema.collaboratorsField]\n if (typeof collab === 'string') {\n try {\n return (JSON.parse(collab) as string[]).includes(userId)\n } catch {\n return false\n }\n }\n if (Array.isArray(collab)) return collab.includes(userId)\n return false\n}\n\nfunction passesVisibilityCheck(\n schema: CollectionSchema,\n record: { data: Record<string, unknown> },\n): boolean {\n if (!schema.visibilityField) return false\n if (typeof schema.visibilityField === 'string') {\n return record.data[schema.visibilityField] === 'public'\n }\n return record.data[schema.visibilityField.field] === schema.visibilityField.value\n}\n\nfunction checkPermissionLevel(\n level: PermissionLevel,\n schema: CollectionSchema,\n record: { data: Record<string, unknown>; createdBy: string; recordId?: string },\n userId: string,\n ctx: PermissionContext = noopPermissionContext,\n): boolean {\n if (level === true) return true\n if (level === false) return false\n if (level === 'own') return isOwner(schema, record, userId)\n if (level === 'unclaimed-or-own') {\n if (schema.ownerField) {\n const ownerVal = record.data[schema.ownerField]\n if (!ownerVal || ownerVal === '') return true\n }\n return isOwner(schema, record, userId)\n }\n if (level === 'collaborator') {\n return isOwner(schema, record, userId) || isCollaborator(schema, record, userId)\n }\n if (level === 'team' || level === 'access') {\n if (isOwner(schema, record, userId)) return true\n if (isCollaborator(schema, record, userId)) return true\n if (schema.teamField) {\n const teamId = record.data[schema.teamField]\n if (typeof teamId === 'string' && ctx.isTeamMember(teamId, userId)) return true\n }\n return false\n }\n if (level === 'published') {\n return isOwner(schema, record, userId) || passesVisibilityCheck(schema, record)\n }\n if (level === 'shared') {\n return (\n isOwner(schema, record, userId) ||\n isCollaborator(schema, record, userId) ||\n passesVisibilityCheck(schema, record)\n )\n }\n return false\n}\n\nexport function canRead(\n schema: CollectionSchema,\n role: string,\n record: { data: Record<string, unknown>; createdBy: string; recordId?: string },\n userId: string,\n ctx: PermissionContext = noopPermissionContext,\n): boolean {\n return checkPermissionLevel(getRolePermissions(schema, role).read, schema, record, userId, ctx)\n}\n\nexport function canCreate(schema: CollectionSchema, role: string): boolean {\n return getRolePermissions(schema, role).create === true\n}\n\nexport function canUpdate(\n schema: CollectionSchema,\n role: string,\n record: { data: Record<string, unknown>; createdBy: string; recordId?: string },\n userId: string,\n ctx: PermissionContext = noopPermissionContext,\n): boolean {\n return checkPermissionLevel(getRolePermissions(schema, role).update, schema, record, userId, ctx)\n}\n\nexport function canDelete(\n schema: CollectionSchema,\n role: string,\n record: { data: Record<string, unknown>; createdBy: string; recordId?: string },\n userId: string,\n ctx: PermissionContext = noopPermissionContext,\n): boolean {\n return checkPermissionLevel(getRolePermissions(schema, role).delete, schema, record, userId, ctx)\n}\n\n/** Check if a caller-supplied field write violates writableFields restrictions. */\nexport function checkFieldPermissions(\n schema: CollectionSchema,\n role: string,\n newData: Record<string, unknown>,\n existingData?: Record<string, unknown>,\n): string | null {\n const perms = getRolePermissions(schema, role)\n if (!perms.writableFields) return null // no restrictions\n\n const allowedFields = new Set(perms.writableFields)\n for (const key of Object.keys(newData)) {\n if (existingData && newData[key] === existingData[key]) continue\n if (!allowedFields.has(key)) {\n return `Role '${role}' cannot modify field '${key}'`\n }\n }\n return null\n}\n\n/**\n * `unclaimed-or-own` lets a client claim an empty owner field, but never lets\n * them choose another user's id. Check the post-write record so the invariant\n * covers both creation and updates while still allowing an owner to unclaim.\n */\nexport function checkUnclaimedOwnerTransition(\n schema: CollectionSchema,\n role: string,\n nextData: Record<string, unknown>,\n userId: string,\n): string | null {\n const permissions = getRolePermissions(schema, role)\n if (permissions.update !== 'unclaimed-or-own' || !schema.ownerField) return null\n\n const nextOwner = nextData[schema.ownerField]\n if (nextOwner === undefined || nextOwner === null || nextOwner === '' || nextOwner === userId) {\n return null\n }\n\n return `Role '${role}' can set '${schema.ownerField}' only to their own user id or leave it unclaimed`\n}\n\n// ============================================================================\n// System-Managed Columns (users collection)\n// ============================================================================\n\n/**\n * Columns the system *assigns* — from the identity provider at registration,\n * or from an admin action. A caller who sends one of these has chosen a value\n * and means it, so a record write that would change one is refused loudly\n * rather than silently dropped.\n */\nexport const SYSTEM_ASSIGNED_COLUMNS = new Set(['email', 'name', 'imageUrl', 'role'])\n\n/**\n * Columns the system *maintains* on its own schedule, without telling anyone.\n * `registerUser` bumps `lastSeenAt` on every WS connect and `handleUserUpdate`\n * rewrites it on every 60s presence heartbeat; neither broadcasts, so every\n * client's copy goes stale silently and there is no heal path short of a\n * refetch. Read-modify-write echo (`put(id, { ...rec.data, field })`) is the\n * SDK's own house style, so refusing these would refuse writes over a value the\n * caller never chose and cannot keep current. They are preserved silently.\n */\nexport const SYSTEM_MAINTAINED_COLUMNS = new Set(['createdAt', 'lastSeenAt'])\n\n/** Every `users` column owned by the system rather than by record writes. */\nexport const SYSTEM_MANAGED_COLUMNS = new Set([\n ...SYSTEM_ASSIGNED_COLUMNS,\n ...SYSTEM_MAINTAINED_COLUMNS,\n])\n\n/** Standard user columns. Apps spread these into their users schema. */\nexport const USERS_COLUMNS: ColumnDefinition[] = [\n { name: 'email', storage: 'text', interpretation: 'plain' },\n { name: 'name', storage: 'text', interpretation: 'plain' },\n { name: 'imageUrl', storage: 'text', interpretation: 'plain' },\n { name: 'role', storage: 'text', interpretation: 'plain' },\n { name: 'createdAt', storage: 'text', interpretation: { kind: 'datetime' } },\n { name: 'lastSeenAt', storage: 'text', interpretation: { kind: 'datetime' } },\n]\n\nexport const BASE_USERS_SCHEMA: CollectionSchema = {\n name: 'users',\n columns: [...USERS_COLUMNS],\n permissions: {\n '*': { read: 'own', create: false, update: false, delete: false },\n admin: { read: true, create: false, update: true, delete: true },\n },\n}\n\n// ============================================================================\n// Schema Registry\n// ============================================================================\n\n/**\n * Lint a CollectionSchema for declarations that look like they should\n * enforce something but don't, due to interactions between top-level\n * fields (visibilityField, ownerField) and per-role permission levels.\n *\n * The SDK has historically silently accepted schemas that imply more\n * enforcement than they actually deliver — e.g., `visibilityField` set\n * but every role's `read: true` means \"anyone can read everything\"\n * regardless of `visibility`. Warn loudly at registration so app\n * authors notice before shipping a privacy bug.\n *\n * Returns an array of warning messages. Empty = clean.\n */\nexport function lintSchema(schema: CollectionSchema): string[] {\n const warnings: string[] = []\n const VISIBILITY_AWARE: ReadonlyArray<PermissionLevel> = ['published', 'shared']\n\n // Roles that grant unconditional read despite visibilityField being declared.\n if (schema.visibilityField) {\n const offendingRoles = Object.entries(schema.permissions)\n .filter(([, perms]) => perms.read === true)\n .map(([role]) => role)\n if (offendingRoles.length > 0) {\n const usesAware = Object.values(schema.permissions).some(\n (p) => typeof p.read === 'string' && VISIBILITY_AWARE.includes(p.read as PermissionLevel),\n )\n if (!usesAware) {\n warnings.push(\n `[${schema.name}] visibilityField is declared but no role uses read: 'published' or 'shared'. ` +\n `Roles with read: true (${offendingRoles.join(', ')}) will see every row regardless of visibility. ` +\n `Change those to read: 'published' (owner OR public) or 'shared' (owner OR collaborator OR public) ` +\n `to actually enforce the filter, or remove visibilityField if you don't intend to gate reads by it.`,\n )\n }\n }\n }\n\n // An ordinary client role that can create rows must not be able to claim\n // someone else's ownership. Server-written collections legitimately carry\n // a recipient/subject id instead: their non-privileged roles all have\n // create:false, while owner/admin writes are already fully trusted.\n if (schema.ownerField) {\n const col = schema.columns.find((c) => c.name === schema.ownerField)\n const spoofableRoles = Object.entries(schema.permissions)\n .filter(\n ([role, permissions]) =>\n role !== 'owner' &&\n role !== 'admin' &&\n permissions.create === true &&\n permissions.update !== 'unclaimed-or-own',\n )\n .map(([role]) => role)\n if (col && !col.userBound && spoofableRoles.length > 0) {\n warnings.push(\n `[${schema.name}] ownerField is '${schema.ownerField}' but that column is not marked userBound: true. ` +\n `Client role(s) ${spoofableRoles.join(', ')} can create a row with someone else's id in this field, bypassing 'own' permission checks. ` +\n `Add userBound: true (and ideally immutable: true) to the column.`,\n )\n }\n }\n\n // userBound columns that aren't text storage — userBound sets the\n // field to the caller's userId (a string), so non-text storage is a\n // schema mistake that will surface as a coercion error at write time.\n for (const col of schema.columns) {\n if (col.userBound && col.storage !== 'text') {\n warnings.push(\n `[${schema.name}] column '${col.name}' is userBound but storage is '${col.storage}'. ` +\n `userBound stamps the user id (a string); use storage: 'text'.`,\n )\n }\n }\n\n return warnings\n}\n\n/**\n * Lint a whole schema set: every per-schema warning, plus the rules that\n * need to see the set. The `'team'` permission level resolves membership from\n * a `team_members` collection (`teamId`, `userId`, `status`); without one it\n * denies everything, silently — a mistake that presents as a sync bug, not\n * as a schema error, so name it here.\n */\nexport function lintSchemas(schemas: CollectionSchema[]): string[] {\n return [...schemas.flatMap((schema) => lintSchema(schema)), ...lintSchemaSet(schemas)]\n}\n\n/** The rules that need to see the whole schema set (per-schema rules live in\n * `lintSchema`; `lintSchemas` composes both). */\nexport function lintSchemaSet(schemas: CollectionSchema[]): string[] {\n const warnings: string[] = []\n if (!schemas.some((schema) => schema.name === 'team_members')) {\n for (const schema of schemas) {\n const teamRoles = Object.entries(schema.permissions)\n .filter(([, p]) => [p.read, p.update, p.delete].includes('team'))\n .map(([role]) => role)\n if (teamRoles.length > 0) {\n warnings.push(\n `[${schema.name}] role(s) ${teamRoles.join(', ')} use the 'team' permission level, but no ` +\n `'team_members' collection is registered. 'team' resolves membership from team_members ` +\n `(teamId, userId, status), so without it those roles are denied everything — silently. ` +\n `Register a team_members schema or use another level.`,\n )\n }\n }\n }\n return warnings\n}\n\nexport class SchemaRegistry {\n private trusted: Map<string, CollectionSchema> = new Map()\n\n constructor(schemas: CollectionSchema[] = []) {\n // registerTrusted runs the per-schema lint; only the set-level rules are\n // this loop's to add.\n for (const w of lintSchemaSet(schemas)) console.warn(`[schema-lint] ${w}`)\n for (const schema of schemas) this.registerTrusted(schema)\n }\n\n registerTrusted(schema: CollectionSchema): void {\n for (const w of lintSchema(schema)) {\n console.warn(`[schema-lint] ${w}`)\n }\n this.trusted.set(schema.name, schema)\n }\n\n get(name: string): CollectionSchema | undefined {\n return this.trusted.get(name)\n }\n\n has(name: string): boolean {\n return this.trusted.has(name)\n }\n\n all(): CollectionSchema[] {\n return Array.from(this.trusted.values())\n }\n\n names(): string[] {\n return Array.from(this.trusted.keys())\n }\n}\n\n// ============================================================================\n// Permission Analysis\n// ============================================================================\n\nexport type PermissionSource = 'explicit' | 'wildcard' | 'default-deny'\n\nexport interface ResolvedPermission {\n level: PermissionLevel | boolean\n source: PermissionSource\n}\n\nexport interface CollectionPermissionSummary {\n collection: string\n ownerField?: string\n collaboratorsField?: string\n teamField?: string\n columns: ColumnDefinition[]\n permissions: Record<\n string,\n {\n read: ResolvedPermission\n create: ResolvedPermission\n update: ResolvedPermission\n delete: ResolvedPermission\n writableFields?: string[]\n }\n >\n}\n\nexport interface PermissionAnalysis {\n roles: string[]\n collections: CollectionPermissionSummary[]\n}\n\nexport function analyzePermissions(schemas: CollectionSchema[]): PermissionAnalysis {\n const roleSet = new Set<string>()\n for (const schema of schemas) {\n for (const role of Object.keys(schema.permissions)) roleSet.add(role)\n }\n roleSet.delete('*')\n const roles = Array.from(roleSet)\n\n const collections: CollectionPermissionSummary[] = schemas.map((schema) => {\n const permissions: CollectionPermissionSummary['permissions'] = {}\n for (const role of roles) {\n let source: PermissionSource\n let perms: RolePermissions\n if (schema.permissions[role]) {\n source = 'explicit'\n perms = schema.permissions[role]\n } else if (schema.permissions['*']) {\n source = 'wildcard'\n perms = schema.permissions['*']\n } else {\n source = 'default-deny'\n perms = { read: false, create: false, update: false, delete: false }\n }\n permissions[role] = {\n read: { level: perms.read, source },\n create: { level: perms.create, source },\n update: { level: perms.update, source },\n delete: { level: perms.delete, source },\n writableFields: perms.writableFields,\n }\n }\n return {\n collection: schema.name,\n ownerField: schema.ownerField,\n collaboratorsField: schema.collaboratorsField,\n teamField: schema.teamField,\n columns: schema.columns,\n permissions,\n }\n })\n\n return { roles, collections }\n}\n","/**\n * AI Chat Schemas\n *\n * Pre-built collection schemas for DO-backed AI chat history.\n * The worker is the only writer; the client reads via `useQuery`.\n */\n\nimport type { CollectionSchema } from './registry'\n\n// member.create is false on both collections: writes flow exclusively through\n// the worker's /api/ai/* routes, which use X-App-Action to bypass RBAC.\n// Allowing direct WS creates would let a user PUT fake `role: 'assistant'`\n// rows into their own chats and have the next turn's loadMessages feed them\n// to the LLM as if they were real assistant outputs.\nexport const AI_CHATS_SCHEMA: CollectionSchema = {\n name: 'ai-chats',\n columns: [\n { name: 'userId', storage: 'text', interpretation: 'plain', userBound: true, immutable: true, required: true },\n { name: 'title', storage: 'text', interpretation: 'plain' },\n { name: 'model', storage: 'text', interpretation: 'plain' },\n { name: 'compactedSummary', storage: 'text', interpretation: 'plain' },\n { name: 'compactedThroughId', storage: 'text', interpretation: 'plain' },\n ],\n ownerField: 'userId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: 'own', create: false, update: 'own', delete: 'own' },\n viewer: { read: 'own', create: false, update: false, delete: false },\n },\n}\n\nexport const AI_MESSAGES_SCHEMA: CollectionSchema = {\n name: 'ai-messages',\n columns: [\n { name: 'chatId', storage: 'text', interpretation: 'plain', immutable: true, required: true },\n { name: 'userId', storage: 'text', interpretation: 'plain', userBound: true, immutable: true, required: true },\n { name: 'role', storage: 'text', interpretation: 'plain', required: true },\n { name: 'content', storage: 'text', interpretation: 'plain' },\n { name: 'parts', storage: 'text', interpretation: { kind: 'json' } },\n ],\n ownerField: 'userId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: 'own', create: false, update: 'own', delete: 'own' },\n viewer: { read: 'own', create: false, update: false, delete: false },\n },\n}\n","/**\n * Messaging Schemas\n *\n * Pre-built collection schemas for messaging functionality.\n * Any app can import these to add channels, messages, reactions, etc.\n *\n * @example\n * ```typescript\n * import { CHANNELS_SCHEMA, MESSAGES_SCHEMA, REACTIONS_SCHEMA } from 'deepspace/worker'\n * export const schemas = [usersSchema, CHANNELS_SCHEMA, MESSAGES_SCHEMA, REACTIONS_SCHEMA]\n * ```\n */\n\nimport type { CollectionSchema } from './registry'\n\nexport const CHANNELS_SCHEMA: CollectionSchema = {\n name: 'channels',\n columns: [\n { name: 'name', storage: 'text', interpretation: 'plain' },\n { name: 'description', storage: 'text', interpretation: 'plain' },\n {\n name: 'type',\n storage: 'text',\n interpretation: { kind: 'select', options: ['public', 'private', 'dm'] },\n },\n {\n name: 'createdBy',\n storage: 'text',\n interpretation: 'plain',\n userBound: true,\n immutable: true,\n },\n { name: 'archived', storage: 'number', interpretation: { kind: 'boolean' } },\n ],\n ownerField: 'createdBy',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: true, create: true, update: 'own', delete: false },\n viewer: { read: true, create: false, update: false, delete: false },\n },\n}\n\nexport const MESSAGES_SCHEMA: CollectionSchema = {\n name: 'messages',\n columns: [\n { name: 'channelId', storage: 'text', interpretation: 'plain' },\n { name: 'content', storage: 'text', interpretation: 'plain' },\n {\n name: 'authorId',\n storage: 'text',\n interpretation: 'plain',\n userBound: true,\n immutable: true,\n },\n { name: 'parentMessageId', storage: 'text', interpretation: 'plain' },\n { name: 'edited', storage: 'number', interpretation: { kind: 'boolean' } },\n { name: 'editedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n // Soft-delete flag. Slack-style messaging keeps the row + parent IDs\n // intact (so threads remain coherent) and renders a tombstone instead.\n // Hard-delete via remove() still works if an app wants it.\n { name: 'deleted', storage: 'number', interpretation: { kind: 'boolean' } },\n ],\n ownerField: 'authorId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: true, create: true, update: 'own', delete: 'own' },\n viewer: { read: true, create: false, update: false, delete: false },\n },\n}\n\nexport const REACTIONS_SCHEMA: CollectionSchema = {\n name: 'reactions',\n columns: [\n { name: 'messageId', storage: 'text', interpretation: 'plain' },\n { name: 'channelId', storage: 'text', interpretation: 'plain' },\n { name: 'emoji', storage: 'text', interpretation: 'plain' },\n { name: 'userId', storage: 'text', interpretation: 'plain', userBound: true, immutable: true },\n ],\n ownerField: 'userId',\n uniqueOn: ['messageId', 'emoji', 'userId'],\n permissions: {\n admin: { read: true, create: true, update: false, delete: true },\n member: { read: true, create: true, update: false, delete: 'own' },\n viewer: { read: true, create: false, update: false, delete: false },\n },\n}\n\nexport const CHANNEL_MEMBERS_SCHEMA: CollectionSchema = {\n name: 'channel-members',\n columns: [\n { name: 'channelId', storage: 'text', interpretation: 'plain' },\n { name: 'userId', storage: 'text', interpretation: 'plain', userBound: true, immutable: true },\n { name: 'joinedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n ],\n ownerField: 'userId',\n uniqueOn: ['channelId', 'userId'],\n permissions: {\n admin: { read: true, create: true, update: false, delete: true },\n member: { read: true, create: true, update: false, delete: 'own' },\n viewer: { read: true, create: false, update: false, delete: false },\n },\n}\n\nexport const CHANNEL_INVITATIONS_SCHEMA: CollectionSchema = {\n name: 'channel-invitations',\n columns: [\n {\n name: 'channelId',\n storage: 'text',\n interpretation: 'plain',\n required: true,\n immutable: true,\n },\n {\n name: 'invitedUserId',\n storage: 'text',\n interpretation: 'plain',\n required: true,\n immutable: true,\n },\n {\n name: 'invitedBy',\n storage: 'text',\n interpretation: 'plain',\n userBound: true,\n immutable: true,\n },\n {\n name: 'status',\n storage: 'text',\n interpretation: { kind: 'select', options: ['pending', 'accepted', 'declined'] },\n required: true,\n },\n ],\n uniqueOn: ['channelId', 'invitedUserId'],\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: true, create: true, update: true, delete: false },\n viewer: { read: true, create: false, update: false, delete: false },\n },\n}\n\nexport const READ_RECEIPTS_SCHEMA: CollectionSchema = {\n name: 'read-receipts',\n columns: [\n { name: 'channelId', storage: 'text', interpretation: 'plain' },\n { name: 'userId', storage: 'text', interpretation: 'plain', userBound: true, immutable: true },\n { name: 'lastReadAt', storage: 'text', interpretation: { kind: 'datetime' } },\n ],\n ownerField: 'userId',\n uniqueOn: ['channelId', 'userId'],\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: true, create: true, update: 'own', delete: 'own' },\n viewer: { read: false, create: false, update: false, delete: false },\n },\n}\n","/**\n * Shared conversation schemas for RecordRoom-based conversations.\n *\n * These define the collections inside a per-conversation RecordRoom DO.\n * Used by apps that have messaging/conversation features (slack-clone,\n * helpdesk, mail, reddit-clone, etc.).\n *\n * Each conversation gets its own RecordRoom DO keyed by `conv:{id}`.\n */\n\nimport type { CollectionSchema } from './registry'\n\n// Re-export data interfaces from types (single source of truth)\nexport type {\n ConvMessageData,\n ConvReactionData,\n ConvMemberData,\n ConvReadCursorData,\n ConvVoteData,\n} from '../../shared/types'\n\n// ============================================================================\n// Schemas\n// ============================================================================\n\nexport const CONVERSATION_SCHEMAS: CollectionSchema[] = [\n {\n name: 'conv_messages',\n columns: [\n { name: 'Content', storage: 'text', interpretation: 'plain' },\n { name: 'AuthorId', storage: 'text', interpretation: 'plain' },\n { name: 'ParentId', storage: 'text', interpretation: 'plain' },\n { name: 'Edited', storage: 'number', interpretation: 'boolean' },\n { name: 'MessageType', storage: 'text', interpretation: 'plain' },\n { name: 'Metadata', storage: 'text', interpretation: 'json' },\n ],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: 'own', delete: 'own' },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'AuthorId',\n },\n {\n name: 'conv_reactions',\n columns: [\n { name: 'MessageId', storage: 'text', interpretation: 'plain' },\n { name: 'Emoji', storage: 'text', interpretation: 'plain' },\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n ],\n uniqueOn: ['MessageId', 'Emoji', 'UserId'],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: false, delete: 'own' },\n admin: { read: true, create: true, update: false, delete: true },\n },\n ownerField: 'UserId',\n },\n {\n name: 'conv_members',\n columns: [\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n { name: 'UserName', storage: 'text', interpretation: 'plain' },\n { name: 'Role', storage: 'text', interpretation: 'plain' },\n ],\n uniqueOn: ['UserId'],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: 'own', delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'UserId',\n },\n {\n name: 'conv_read_cursors',\n columns: [\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n { name: 'LastReadAt', storage: 'text', interpretation: 'plain' },\n ],\n uniqueOn: ['UserId'],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: 'own', delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'UserId',\n },\n]\n\n/**\n * Voting schemas for Reddit-style apps.\n * Add these alongside CONVERSATION_SCHEMAS for apps that need voting.\n */\nexport const VOTING_SCHEMAS: CollectionSchema[] = [\n {\n name: 'conv_votes',\n columns: [\n { name: 'TargetId', storage: 'text', interpretation: 'plain' },\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n { name: 'Direction', storage: 'number', interpretation: 'plain' },\n ],\n uniqueOn: ['TargetId', 'UserId'],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: 'own', delete: 'own' },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'UserId',\n },\n]\n","/**\n * Directory DO Schemas\n *\n * Purpose-built collections for the `dir:{appName}` global DO type.\n * This is the cross-app-visible directory layer — any app can subscribe\n * to another app's directory in real-time via SHARED_CONNECTIONS.\n *\n * Five collections covering all standard communication/social patterns:\n * - conversations: channels, DMs, email threads, support chats\n * - conversation_state: per-user metadata (read cursor, stars, labels, folders)\n * - communities: groups, forums, boards, projects\n * - memberships: user membership in communities\n * - posts: feed items, tweets, Q&A questions, announcements\n *\n * Domain-specific data (tickets, CRM, procurement) stays in app DOs.\n * Message content stays in conv:{id} DOs (CONVERSATION_SCHEMAS).\n */\n\nimport type { CollectionSchema } from './registry'\n\n// Re-export data interfaces from types (single source of truth)\nexport type {\n DirectoryConversationData,\n ConversationStateData,\n DirectoryCommunityData,\n DirectoryMembershipData,\n DirectoryPostData,\n} from '../../shared/types'\n\n// ============================================================================\n// Schemas\n// ============================================================================\n\nconst conversationsSchema: CollectionSchema = {\n name: 'conversations',\n columns: [\n { name: 'Name', storage: 'text', interpretation: 'plain' },\n { name: 'Description', storage: 'text', interpretation: 'plain' },\n { name: 'Type', storage: 'text', interpretation: { kind: 'select', options: ['public', 'private', 'dm', 'group', 'email', 'outbound-email', 'support', 'announcement'] } },\n { name: 'Visibility', storage: 'text', interpretation: { kind: 'select', options: ['public', 'private', 'restricted'] } },\n { name: 'CreatedBy', storage: 'text', interpretation: 'plain' },\n { name: 'ParticipantHash', storage: 'text', interpretation: 'plain' },\n { name: 'ParticipantIds', storage: 'text', interpretation: 'json' },\n { name: 'Status', storage: 'text', interpretation: { kind: 'select', options: ['active', 'archived', 'resolved', 'closed'] } },\n { name: 'AssigneeId', storage: 'text', interpretation: 'plain' },\n { name: 'LinkedRef', storage: 'text', interpretation: 'json' },\n { name: 'LastMessageAt', storage: 'text', interpretation: { kind: 'datetime' } },\n { name: 'LastMessagePreview', storage: 'text', interpretation: 'plain' },\n { name: 'LastMessageAuthor', storage: 'text', interpretation: 'plain' },\n { name: 'MessageCount', storage: 'number', interpretation: 'plain' },\n ],\n collaboratorsField: 'ParticipantIds',\n visibilityField: 'Visibility',\n permissions: {\n viewer: { read: 'shared', create: false, update: false, delete: false },\n member: { read: 'shared', create: true, update: true, delete: 'own' },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'CreatedBy',\n}\n\nconst conversationStateSchema: CollectionSchema = {\n name: 'conversation_state',\n columns: [\n { name: 'ConversationId', storage: 'text', interpretation: 'plain' },\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n { name: 'LastReadAt', storage: 'text', interpretation: { kind: 'datetime' } },\n { name: 'LastReadMessageCount', storage: 'number', interpretation: 'plain' },\n { name: 'Starred', storage: 'number', interpretation: 'boolean' },\n { name: 'Archived', storage: 'number', interpretation: 'boolean' },\n { name: 'Trashed', storage: 'number', interpretation: 'boolean' },\n { name: 'Labels', storage: 'text', interpretation: 'json' },\n { name: 'Folder', storage: 'text', interpretation: 'plain' },\n ],\n uniqueOn: ['ConversationId', 'UserId'],\n ownerField: 'UserId',\n permissions: {\n viewer: { read: false, create: false, update: false, delete: false },\n member: { read: 'own', create: true, update: 'own', delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n}\n\nconst communitiesSchema: CollectionSchema = {\n name: 'communities',\n columns: [\n { name: 'Name', storage: 'text', interpretation: 'plain' },\n { name: 'Description', storage: 'text', interpretation: 'plain' },\n { name: 'CreatedBy', storage: 'text', interpretation: 'plain' },\n { name: 'Type', storage: 'text', interpretation: { kind: 'select', options: ['community', 'forum', 'board', 'project', 'server'] } },\n { name: 'Visibility', storage: 'text', interpretation: { kind: 'select', options: ['public', 'private', 'restricted'] } },\n { name: 'MemberCount', storage: 'number', interpretation: 'plain' },\n { name: 'Rules', storage: 'text', interpretation: 'json' },\n { name: 'IconUrl', storage: 'text', interpretation: 'plain' },\n { name: 'CoverUrl', storage: 'text', interpretation: 'plain' },\n ],\n uniqueOn: ['Name'],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: 'own', delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'CreatedBy',\n}\n\nconst membershipsSchema: CollectionSchema = {\n name: 'memberships',\n columns: [\n { name: 'CommunityId', storage: 'text', interpretation: 'plain' },\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n { name: 'UserName', storage: 'text', interpretation: 'plain' },\n { name: 'Role', storage: 'text', interpretation: { kind: 'select', options: ['member', 'moderator', 'admin'] } },\n { name: 'JoinedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n ],\n uniqueOn: ['CommunityId', 'UserId'],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: 'own', delete: 'own' },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'UserId',\n}\n\nconst postsSchema: CollectionSchema = {\n name: 'posts',\n columns: [\n { name: 'Title', storage: 'text', interpretation: 'plain' },\n { name: 'Content', storage: 'text', interpretation: 'plain' },\n { name: 'AuthorId', storage: 'text', interpretation: 'plain' },\n { name: 'Type', storage: 'text', interpretation: { kind: 'select', options: ['post', 'question', 'answer', 'tweet', 'announcement', 'link'] } },\n { name: 'CommunityId', storage: 'text', interpretation: 'plain' },\n { name: 'ParentId', storage: 'text', interpretation: 'plain' },\n { name: 'ConversationId', storage: 'text', interpretation: 'plain' },\n { name: 'Status', storage: 'text', interpretation: { kind: 'select', options: ['draft', 'published', 'pinned', 'archived', 'accepted'] } },\n { name: 'Tags', storage: 'text', interpretation: 'json' },\n { name: 'LinkUrl', storage: 'text', interpretation: 'plain' },\n ],\n permissions: {\n viewer: { read: true, create: false, update: false, delete: false },\n member: { read: true, create: true, update: 'own', delete: 'own' },\n admin: { read: true, create: true, update: true, delete: true },\n },\n ownerField: 'AuthorId',\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\n/** All directory schemas for the `dir:{appName}` global DO type. */\nexport const DIRECTORY_SCHEMAS: CollectionSchema[] = [\n conversationsSchema,\n conversationStateSchema,\n communitiesSchema,\n membershipsSchema,\n postsSchema,\n]\n","/**\n * Workspace DO Schemas\n *\n * All collections for the workspace:default Durable Object.\n * This DO consolidates shared, cross-app business data:\n *\n * - teams / team_members — workspace-wide team management (replaces built-in teams)\n * - tasks / projects / tags — team-scoped task management\n * - people — shared contacts directory\n * - transactions / accounts — shared financial ledger\n *\n * Team-scoped collections use teamField RBAC: members see only\n * their team's records; admins see everything.\n */\n\nimport type { CollectionSchema } from './registry'\n\n// ============================================================================\n// Teams\n// ============================================================================\n\nexport const workspaceTeamsSchema: CollectionSchema = {\n name: 'teams',\n columns: [\n { name: 'Name', storage: 'text', interpretation: 'plain' },\n { name: 'CreatedBy', storage: 'text', interpretation: 'plain' },\n { name: 'IsOpen', storage: 'number', interpretation: { kind: 'boolean' } },\n ],\n teamField: '_rowId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: 'team', create: true, update: 'team', delete: 'own' },\n },\n}\n\nexport const workspaceTeamMembersSchema: CollectionSchema = {\n name: 'team_members',\n columns: [\n { name: 'TeamId', storage: 'text', interpretation: 'plain' },\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n { name: 'RoleInTeam', storage: 'text', interpretation: { kind: 'select', options: ['admin', 'member'] } },\n { name: 'JoinedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n { name: 'Email', storage: 'text', interpretation: { kind: 'email' } },\n { name: 'Status', storage: 'text', interpretation: { kind: 'select', options: ['active', 'invited'] } },\n ],\n teamField: 'TeamId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: 'team', create: true, update: 'team', delete: 'team' },\n },\n}\n\n// ============================================================================\n// Tasks / Projects / Tags (team-scoped)\n// ============================================================================\n\nexport const workspaceTasksSchema: CollectionSchema = {\n name: 'tasks',\n columns: [\n { name: 'TeamId', storage: 'text', interpretation: 'plain' },\n { name: 'Title', storage: 'text', interpretation: 'plain' },\n { name: 'Notes', storage: 'text', interpretation: 'plain' },\n { name: 'Completed', storage: 'number', interpretation: { kind: 'boolean' } },\n { name: 'CompletedAt', storage: 'number', interpretation: 'plain' },\n { name: 'Deleted', storage: 'number', interpretation: { kind: 'boolean' } },\n { name: 'DeletedAt', storage: 'number', interpretation: 'plain' },\n { name: 'Priority', storage: 'text', interpretation: { kind: 'select', options: ['none', 'low', 'medium', 'high'] } },\n { name: 'DueDate', storage: 'text', interpretation: 'plain' },\n { name: 'ProjectId', storage: 'text', interpretation: 'plain' },\n { name: 'KanbanStatus', storage: 'text', interpretation: { kind: 'select', options: ['backlog', 'ready', 'in_progress', 'review', 'done'] } },\n { name: 'Order', storage: 'number', interpretation: 'plain' },\n { name: 'AssignedUser', storage: 'text', interpretation: { kind: 'json' } },\n { name: 'AssignedBy', storage: 'text', interpretation: { kind: 'json' } },\n { name: 'TagIds', storage: 'text', interpretation: { kind: 'json' } },\n { name: 'CreatedAt', storage: 'number', interpretation: 'plain' },\n ],\n teamField: 'TeamId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: 'team', create: true, update: 'team', delete: 'team' },\n viewer: { read: false, create: false, update: false, delete: false },\n },\n}\n\nexport const workspaceProjectsSchema: CollectionSchema = {\n name: 'projects',\n columns: [\n { name: 'TeamId', storage: 'text', interpretation: 'plain' },\n { name: 'Title', storage: 'text', interpretation: 'plain' },\n { name: 'Notes', storage: 'text', interpretation: 'plain' },\n { name: 'Color', storage: 'text', interpretation: 'plain' },\n { name: 'ParentId', storage: 'text', interpretation: 'plain' },\n { name: 'Order', storage: 'number', interpretation: 'plain' },\n { name: 'CreatedAt', storage: 'number', interpretation: 'plain' },\n ],\n teamField: 'TeamId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: 'team', create: true, update: 'team', delete: 'team' },\n viewer: { read: false, create: false, update: false, delete: false },\n },\n}\n\nexport const workspaceTagsSchema: CollectionSchema = {\n name: 'tags',\n columns: [\n { name: 'TeamId', storage: 'text', interpretation: 'plain' },\n { name: 'Name', storage: 'text', interpretation: 'plain' },\n { name: 'Color', storage: 'text', interpretation: 'plain' },\n { name: 'CreatedAt', storage: 'number', interpretation: 'plain' },\n ],\n teamField: 'TeamId',\n permissions: {\n admin: { read: true, create: true, update: true, delete: true },\n member: { read: 'team', create: true, update: 'team', delete: 'team' },\n viewer: { read: false, create: false, update: false, delete: false },\n },\n}\n\n// ============================================================================\n// People (shared contacts directory)\n// ============================================================================\n\nexport const workspacePeopleSchema: CollectionSchema = {\n name: 'people',\n columns: [\n { name: 'Name', storage: 'text', interpretation: 'plain' },\n { name: 'Email', storage: 'text', interpretation: { kind: 'email' } },\n { name: 'Type', storage: 'text', interpretation: { kind: 'select', options: ['employee', 'customer', 'vendor', 'contact'] } },\n { name: 'Status', storage: 'text', interpretation: { kind: 'select', options: ['active', 'inactive', 'archived'] } },\n { name: 'CompanyId', storage: 'text', interpretation: 'plain' },\n { name: 'Department', storage: 'text', interpretation: 'plain' },\n { name: 'Title', storage: 'text', interpretation: 'plain' },\n { name: 'LastContactedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n { name: 'Metadata', storage: 'text', interpretation: { kind: 'json' } },\n ],\n permissions: {\n '*': { read: true, create: true, update: true, delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n}\n\n// ============================================================================\n// Ledger (shared financial data)\n// ============================================================================\n\nexport const workspaceTransactionsSchema: CollectionSchema = {\n name: 'transactions',\n columns: [\n { name: 'Type', storage: 'text', interpretation: { kind: 'select', options: ['revenue', 'expense', 'payment', 'transfer', 'invoice', 'refund'] } },\n { name: 'Amount', storage: 'number', interpretation: { kind: 'currency', symbol: '$', decimals: 2 } },\n { name: 'Currency', storage: 'text', interpretation: 'plain' },\n { name: 'Description', storage: 'text', interpretation: 'plain' },\n { name: 'Category', storage: 'text', interpretation: 'plain' },\n { name: 'CounterpartyRef', storage: 'text', interpretation: 'plain' },\n { name: 'OrderRef', storage: 'text', interpretation: 'plain' },\n { name: 'Account', storage: 'text', interpretation: 'plain' },\n ],\n permissions: {\n '*': { read: true, create: true, update: true, delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n}\n\nexport const workspaceAccountsSchema: CollectionSchema = {\n name: 'accounts',\n columns: [\n { name: 'Name', storage: 'text', interpretation: 'plain' },\n { name: 'Balance', storage: 'number', interpretation: { kind: 'currency', symbol: '$', decimals: 2 } },\n { name: 'Currency', storage: 'text', interpretation: 'plain' },\n ],\n permissions: {\n '*': { read: true, create: true, update: true, delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n}\n\n// ============================================================================\n// Content Sharing (universal cross-app content discovery)\n// ============================================================================\n\n/**\n * Universal sharing index. Any app can create share records for any content type.\n *\n * ContentType: 'document' | 'slide' | 'spreadsheet' | ... (extensible)\n * ShareType: 'channel' | 'team' | 'direct' | 'link' | 'org' (extensible)\n * ShareTarget: the ID of the target (channelId, teamId, userId, linkId, orgId)\n * Permission: 'view' | 'edit' — what the share grants\n *\n * Content itself lives in the owner's app DO.\n * This table is the discovery layer: \"what has been shared, with whom, and how\".\n */\nexport const workspaceContentSharesSchema: CollectionSchema = {\n name: 'content_shares',\n ownerField: 'OwnerId',\n columns: [\n { name: 'ContentType', storage: 'text', interpretation: { kind: 'select', options: ['document', 'slide', 'spreadsheet'] } },\n { name: 'ContentId', storage: 'text', interpretation: 'plain' },\n { name: 'OwnerId', storage: 'text', interpretation: 'plain' },\n { name: 'OwnerName', storage: 'text', interpretation: 'plain' },\n { name: 'Title', storage: 'text', interpretation: 'plain' },\n { name: 'ShareType', storage: 'text', interpretation: { kind: 'select', options: ['self', 'channel', 'team', 'direct', 'link', 'org'] } },\n { name: 'ShareTarget', storage: 'text', interpretation: 'plain' },\n { name: 'Permission', storage: 'text', interpretation: { kind: 'select', options: ['view', 'edit'] } },\n { name: 'SharedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n { name: 'SharedBy', storage: 'text', interpretation: 'plain' },\n { name: 'SourceApp', storage: 'text', interpretation: 'plain' },\n { name: 'WordCount', storage: 'number', interpretation: 'plain' },\n { name: 'LastEditedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n ],\n permissions: {\n '*': { read: true, create: true, update: true, delete: 'own' },\n admin: { read: true, create: true, update: true, delete: true },\n },\n}\n\n// ============================================================================\n// Form Responses (cross-app: deepspace-forms → sheets-editor)\n// ============================================================================\n\nexport const workspaceFormResponsesSchema: CollectionSchema = {\n name: 'form_responses',\n columns: [\n { name: 'FormId', storage: 'text', interpretation: 'plain' },\n { name: 'FormTitle', storage: 'text', interpretation: 'plain' },\n { name: 'RespondentId', storage: 'text', interpretation: 'plain' },\n { name: 'RespondentEmail', storage: 'text', interpretation: { kind: 'email' } },\n { name: 'RespondentName', storage: 'text', interpretation: 'plain' },\n { name: 'Data', storage: 'text', interpretation: { kind: 'json' } },\n { name: 'SubmittedAt', storage: 'text', interpretation: { kind: 'datetime' } },\n ],\n permissions: {\n '*': { read: true, create: true, update: false, delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n}\n\n// ============================================================================\n// Email Handles (cross-app user identity)\n// ============================================================================\n\n/**\n * Maps DeepSpace users to their claimed @app.space email handles.\n * Shared across all apps so any app can look up a user's email address.\n * All handles are under the @app.space domain.\n */\nexport const workspaceEmailHandlesSchema: CollectionSchema = {\n name: 'email_handles',\n columns: [\n { name: 'UserId', storage: 'text', interpretation: 'plain' },\n { name: 'Handle', storage: 'text', interpretation: 'plain' },\n { name: 'EmailAddress', storage: 'text', interpretation: { kind: 'email' } },\n { name: 'Status', storage: 'text', interpretation: { kind: 'select', options: ['active', 'disabled'] } },\n ],\n uniqueOn: ['Handle'],\n ownerField: 'UserId',\n permissions: {\n '*': { read: true, create: true, update: 'own', delete: false },\n admin: { read: true, create: true, update: true, delete: true },\n },\n}\n\n// ============================================================================\n// Export\n// ============================================================================\n\nexport const WORKSPACE_SCHEMAS: CollectionSchema[] = [\n workspaceTeamsSchema,\n workspaceTeamMembersSchema,\n workspaceTasksSchema,\n workspaceProjectsSchema,\n workspaceTagsSchema,\n workspacePeopleSchema,\n workspaceTransactionsSchema,\n workspaceAccountsSchema,\n workspaceContentSharesSchema,\n workspaceFormResponsesSchema,\n workspaceEmailHandlesSchema,\n]\n"],"mappings":"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","names":[]}
|
package/dist/server.d.ts
CHANGED
|
@@ -143,6 +143,17 @@ type ToolResult = {
|
|
|
143
143
|
* `actions.query`) stay unbounded.
|
|
144
144
|
*/
|
|
145
145
|
declare const DEFAULT_QUERY_LIMIT = 50;
|
|
146
|
+
/**
|
|
147
|
+
* Page size `records.deleteWhere` uses when the caller omits `limit`, and the
|
|
148
|
+
* ceiling it clamps any larger `limit` to.
|
|
149
|
+
*
|
|
150
|
+
* The tool exists so a cascade costs one subrequest per *page* instead of one
|
|
151
|
+
* per row (see `deleteChatCascade`); the ceiling keeps a single call's SQL and
|
|
152
|
+
* delete broadcasts bounded, and callers page by repeating the call until it
|
|
153
|
+
* reports fewer deletions than the page size.
|
|
154
|
+
*/
|
|
155
|
+
declare const DEFAULT_DELETE_WHERE_LIMIT = 100;
|
|
156
|
+
declare const MAX_DELETE_WHERE_LIMIT = 500;
|
|
146
157
|
/**
|
|
147
158
|
* Fill in assistant-only parameter defaults for a built-in tool call.
|
|
148
159
|
*
|
|
@@ -450,6 +461,16 @@ interface CollectionSchema {
|
|
|
450
461
|
permissions: Record<string, RolePermissions>;
|
|
451
462
|
/** Default role assigned on the `users` collection. */
|
|
452
463
|
defaultRole?: string;
|
|
464
|
+
/**
|
|
465
|
+
* `users` collection only: what the `user.list` roster shows a non-admin
|
|
466
|
+
* whose `read` policy is row-scoped (`'own'`, `'team'`, …). Default
|
|
467
|
+
* `'public-identity'`: every registered user's id/name/imageUrl/role — the
|
|
468
|
+
* row policy keeps guarding full-row reads on the records/query path.
|
|
469
|
+
* `'read-policy'`: the roster contains only the rows the caller's read
|
|
470
|
+
* policy grants (still projected to public identity) — for apps that scope
|
|
471
|
+
* users to a tenant/team and must not show names across it.
|
|
472
|
+
*/
|
|
473
|
+
roster?: 'public-identity' | 'read-policy';
|
|
453
474
|
}
|
|
454
475
|
interface Query {
|
|
455
476
|
collection: string;
|
|
@@ -1246,6 +1267,13 @@ declare function makeDefaultSummarizer(env: DeepSpaceAIEnv, options?: {
|
|
|
1246
1267
|
* that skips it is writing across users, so route new consumers through
|
|
1247
1268
|
* `getChat` first.
|
|
1248
1269
|
*
|
|
1270
|
+
* `updateChat` and `appendMessage` re-run that precheck themselves and report
|
|
1271
|
+
* `false` instead of writing when the chat is gone: `records.update` /
|
|
1272
|
+
* `records.create` are upserts, so a write racing the user's delete would
|
|
1273
|
+
* resurrect the chat as a ghost row or orphan messages under a chat that no
|
|
1274
|
+
* longer exists. The guard lives in the helpers, not the routes, so every copy
|
|
1275
|
+
* of the scaffolded chat routes gets it.
|
|
1276
|
+
*
|
|
1249
1277
|
* The tools API returns records as `{ recordId, data, createdAt, updatedAt }`
|
|
1250
1278
|
* envelopes; helpers below flatten them into ChatRow / ChatMessageRow.
|
|
1251
1279
|
*/
|
|
@@ -1283,9 +1311,32 @@ declare function createChat(stub: DurableObjectStub, userId: string, opts?: {
|
|
|
1283
1311
|
title?: string;
|
|
1284
1312
|
model?: string;
|
|
1285
1313
|
}): Promise<ChatRow>;
|
|
1286
|
-
|
|
1314
|
+
/**
|
|
1315
|
+
* Patch a chat the caller owns. Returns true if the row was written, false if
|
|
1316
|
+
* the chat no longer exists (or never belonged to the caller).
|
|
1317
|
+
*
|
|
1318
|
+
* The `getChat` precheck is not redundant with the route's own: `records.update`
|
|
1319
|
+
* is an upsert on the DO — same code path as create — so an unguarded patch of
|
|
1320
|
+
* a chat the user deleted mid-stream *recreates* it as a title-less ghost whose
|
|
1321
|
+
* messages are already cascaded away. Writing only over a row that still exists
|
|
1322
|
+
* is the one place that can be prevented for every caller, so the guard lives
|
|
1323
|
+
* here rather than at each call site. A tiny TOCTOU window remains between the
|
|
1324
|
+
* read and the write (closing it needs a compare-and-set on the DO); a delete
|
|
1325
|
+
* landing inside that window is the same rare race as before, not the routine
|
|
1326
|
+
* "user deleted the chat while the stream ran" case this closes.
|
|
1327
|
+
*/
|
|
1328
|
+
declare function updateChat(stub: DurableObjectStub, chatId: string, userId: string, patch: Partial<Pick<ChatRow, 'title' | 'model' | 'compactedSummary' | 'compactedThroughId'>>): Promise<boolean>;
|
|
1287
1329
|
declare function deleteChatCascade(stub: DurableObjectStub, chatId: string, userId: string): Promise<void>;
|
|
1288
1330
|
declare function loadMessages(stub: DurableObjectStub, chatId: string, userId: string): Promise<ChatMessageRow[]>;
|
|
1331
|
+
/**
|
|
1332
|
+
* Append one message to a chat the caller owns. Returns true if the row was
|
|
1333
|
+
* written, false if the chat is gone (or never belonged to the caller).
|
|
1334
|
+
*
|
|
1335
|
+
* Same write-after-delete guard as `updateChat`, for the same reason: a turn
|
|
1336
|
+
* that finishes after the user deleted the chat would otherwise write messages
|
|
1337
|
+
* whose parent row no longer exists — invisible in every listing and never
|
|
1338
|
+
* cascaded again.
|
|
1339
|
+
*/
|
|
1289
1340
|
declare function appendMessage(stub: DurableObjectStub, msg: {
|
|
1290
1341
|
id: string;
|
|
1291
1342
|
chatId: string;
|
|
@@ -1293,7 +1344,7 @@ declare function appendMessage(stub: DurableObjectStub, msg: {
|
|
|
1293
1344
|
role: 'user' | 'assistant' | 'system';
|
|
1294
1345
|
content: string;
|
|
1295
1346
|
parts?: unknown[];
|
|
1296
|
-
}): Promise<
|
|
1347
|
+
}): Promise<boolean>;
|
|
1297
1348
|
|
|
1298
1349
|
/**
|
|
1299
1350
|
* Per-binding usage metering — record Vectorize / Workers AI / etc. costs
|
|
@@ -2033,6 +2084,17 @@ declare const BASE_USERS_SCHEMA: CollectionSchema;
|
|
|
2033
2084
|
* Returns an array of warning messages. Empty = clean.
|
|
2034
2085
|
*/
|
|
2035
2086
|
declare function lintSchema(schema: CollectionSchema): string[];
|
|
2087
|
+
/**
|
|
2088
|
+
* Lint a whole schema set: every per-schema warning, plus the rules that
|
|
2089
|
+
* need to see the set. The `'team'` permission level resolves membership from
|
|
2090
|
+
* a `team_members` collection (`teamId`, `userId`, `status`); without one it
|
|
2091
|
+
* denies everything, silently — a mistake that presents as a sync bug, not
|
|
2092
|
+
* as a schema error, so name it here.
|
|
2093
|
+
*/
|
|
2094
|
+
declare function lintSchemas(schemas: CollectionSchema[]): string[];
|
|
2095
|
+
/** The rules that need to see the whole schema set (per-schema rules live in
|
|
2096
|
+
* `lintSchema`; `lintSchemas` composes both). */
|
|
2097
|
+
declare function lintSchemaSet(schemas: CollectionSchema[]): string[];
|
|
2036
2098
|
declare class SchemaRegistry {
|
|
2037
2099
|
private trusted;
|
|
2038
2100
|
constructor(schemas?: CollectionSchema[]);
|
|
@@ -3043,15 +3105,14 @@ declare function handleSubscribe(ctx: SubscriptionContext, ws: WebSocket, attach
|
|
|
3043
3105
|
* The client handles unsubscription locally.
|
|
3044
3106
|
*/
|
|
3045
3107
|
declare function handleUnsubscribe(_ctx: SubscriptionContext, _ws: WebSocket, _attachment: ConnectionAttachment, _payload: UnsubscribePayload): void;
|
|
3046
|
-
/**
|
|
3047
|
-
*
|
|
3048
|
-
*
|
|
3049
|
-
*
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
declare function executeQuery(ctx: SubscriptionContext, query: Query, userId: string, userRole: string, skipUserRbac?: boolean): RecordResult[];
|
|
3108
|
+
/** Out-param for {@link executeQuery}: `capped` is set when a limited,
|
|
3109
|
+
* row-filtered scan stopped on {@link MAX_FILTERED_SCAN_ROWS} rather than on
|
|
3110
|
+
* exhausting the rows — the one case a short result does NOT mean "no more
|
|
3111
|
+
* matches". */
|
|
3112
|
+
interface QueryScanState {
|
|
3113
|
+
capped: boolean;
|
|
3114
|
+
}
|
|
3115
|
+
declare function executeQuery(ctx: SubscriptionContext, query: Query, userId: string, userRole: string, skipUserRbac?: boolean, scanState?: QueryScanState): RecordResult[];
|
|
3055
3116
|
/**
|
|
3056
3117
|
* Check if a record matches a subscription's query and permissions
|
|
3057
3118
|
*/
|
|
@@ -3163,12 +3224,21 @@ declare function getAllUsers(sql: SqlStorage, schemaRegistry?: SchemaRegistry):
|
|
|
3163
3224
|
declare function registerUser(sql: SqlStorage, userId: string, name: string, email: string, imageUrl: string | undefined, isAdmin: boolean, defaultRole?: string, schemaRegistry?: SchemaRegistry): Promise<User>;
|
|
3164
3225
|
/** Handle user list requests without bypassing the users collection's privacy boundary. */
|
|
3165
3226
|
declare function handleUserList(ctx: UserContext, ws: WebSocket, attachment: ConnectionAttachment): void;
|
|
3227
|
+
/**
|
|
3228
|
+
* Push the current roster to every connected socket (except `except`). The
|
|
3229
|
+
* client asks for `user.list` once per connection and never again, so anything
|
|
3230
|
+
* that changes what the roster shows — a user registering, a rename, a new
|
|
3231
|
+
* avatar, a role change — must push, or every open tab keeps rendering the
|
|
3232
|
+
* peer it has never seen as "Unknown" until it reconnects. The roster is read
|
|
3233
|
+
* and projected once; each socket then gets its own view of it.
|
|
3234
|
+
*/
|
|
3235
|
+
declare function broadcastUserList(ctx: UserContext, except?: WebSocket | ReadonlySet<WebSocket>): void;
|
|
3166
3236
|
/**
|
|
3167
3237
|
* Handle user profile update.
|
|
3168
3238
|
*
|
|
3169
3239
|
* Called when the client's profile loads after the initial WS connection.
|
|
3170
|
-
* Updates the user's name/email/imageUrl in c_users and
|
|
3171
|
-
*
|
|
3240
|
+
* Updates the user's name/email/imageUrl in c_users and pushes the updated
|
|
3241
|
+
* roster to every connected client so names refresh in real time.
|
|
3172
3242
|
*/
|
|
3173
3243
|
interface UserUpdatePayload {
|
|
3174
3244
|
name?: string;
|
|
@@ -4632,4 +4702,4 @@ declare class RefundError extends Error {
|
|
|
4632
4702
|
constructor(message: string, status: number);
|
|
4633
4703
|
}
|
|
4634
4704
|
|
|
4635
|
-
export { ACCOUNT_STORAGE_LIMIT_BYTES, AI_CHATS_SCHEMA, AI_MESSAGES_SCHEMA, ALLOWED_BINDING_TYPES, APP_ID_RE, APP_NAME_RULES, AUTO_PROVISIONABLE_TYPES, AUTO_PROVISION_SENTINEL, type ActionContext, type ActionHandler, type ActionResult, type ActionTools, type ApiWorkerEnv, type AppNameResolution, type AppNameValidation, type AuthWorkerEnv, BASE_USERS_SCHEMA, BUILT_IN_TOOLS, BaseRoom, CHANNELS_SCHEMA, CHANNEL_INVITATIONS_SCHEMA, CHANNEL_MEMBERS_SCHEMA, CONVERSATION_SCHEMAS, COST_RATES, CancelSubscriptionError, type CancelSubscriptionOpts, type CancelSubscriptionResult, CanvasRoom, type CanvasShape, type ChatContextConfig, type ChatMessageRow, type ChatRow, type ChatTurn, type CollectionPermissionSummary, type CollectionSchema, type ColumnDefinition, type ColumnInterpretation, type ComposioToolsOptions, type ConvMemberData, type ConvMessageData, type ConvReactionData, type ConvReadCursorData, type ConvVoteData, type ConversationStateData, type CronContext, type CronExecution, CronRoom, type CronRoomConfig, type CronTask, type CustomBinding, type CustomBindingManifest, DEFAULT_CONTEXT_CONFIG, DEFAULT_DO_MANIFEST, DEFAULT_QUERY_LIMIT, DEPLOYED_APPS_DEFAULTS, DIRECTORY_SCHEMAS, type DOBindings, type DOManifest, type DOManifestEntry, type DebugApiContext, type DeepSpaceAIEnv, type DeepSpaceAIOptions, type DeepSpaceAgentDiagnosticContext, DeepSpaceAgentModelError, DeepSpaceAgentProfileError, type DeepSpaceAgentStream, type DeepSpaceAgentStreamOptions, type DeepSpaceAuth, type DeepSpaceAuthConfig, type DirectoryCommunityData, type DirectoryConversationData, type DirectoryMembershipData, type DirectoryPostData, type DoMigrationDirective, type DoMigrationPlan, type ExistingDOBinding, GLOBAL_DO_TYPES, GLOBAL_DO_TYPE_NAMES, type GetActionData, type GlobalDOType, type Job, type JobContext, JobRoom, type JobRoomConfig, type JobStatus, type JwtClaims, type JwtVerifierConfig, MAX_APP_FILE_BYTES, MAX_DEPLOY_ASSET_FILE_BYTES, MAX_UPLOAD_PARTS, MAX_UPLOAD_REQUEST_BYTES, MESSAGES_SCHEMA, type MutateActionData, type PermissionAnalysis, type PermissionContext, type PermissionLevel, type PermissionSource, type PlanInfo, type PlatformWorkerEnv, type PrefixResult, type PresencePeer, PresenceRoom, type QueryActionData, REACTIONS_SCHEMA, READ_RECEIPTS_SCHEMA, REGISTRY_INSTANCE, RESERVED_BINDING_NAMES, RESERVED_COLLECTION_NAMES, RESOURCE_ID_RE, type RecordContext, RecordRoom, type RecordRoomConfig, RefundError, type RefundOpts, type RefundResult, RegistryClientError, type ResolvedColumn, type ResolvedPermission, type RolePermissions, type RunMigrationsResult, STRICT_APP_ID_RE, SYSTEM_ASSIGNED_COLUMNS, SYSTEM_COLLECTION_SCHEMAS, SYSTEM_MAINTAINED_COLUMNS, SYSTEM_MANAGED_COLUMNS, SchemaRegistry, type ScopeContext, type ScopedR2Auth, type ScopedR2Config, type ScopedR2Handler, type ScreenshotEnv, type ScreenshotResult, type SharedConnection, type StorageAdmission, type StoredRecord, SubscriptionAuthError, type SubscriptionContext, type SubscriptionRead, SubscriptionRequiredError, type SubscriptionStatus, type Summarizer, TEST_ACCOUNT_EMAIL_SUFFIX, type TokenDebugInfo, type ToolResult, type ToolSchema, type ToolsApiContext, UPLOAD_PART_BYTES, USERS_COLUMNS, type User, type UserAttachment, type UserContext, VOTING_SCHEMAS, type ValidationError, type VerifiedAuth, type VerifyOutcome, type VerifyResult, type Viewport, WORKSPACE_SCHEMAS, type YjsContext, YjsRoom, analyzePermissions, apiWorkerFetch, appendMessage, applyAiToolDefaults, applySlidingWindow, authWorkerFetch, bindingManifestFromOutputConfig, broadcastChange, broadcastYjsUpdate, buildCronContext, buildTableSelect, buildUiParts, callRegistry, canCreate, canDelete, canRead, canUpdate, cancelSubscription, capToolResultSize, captureScreenshot, checkFieldPermissions, checkUnclaimedOwnerTransition, coerceValue, collectionTableName, columnId, composioTools, computeDoMigration, computeHmacHex, createChat, createDeepSpaceAI, createDeepSpaceAuth, createScopedR2Handler, dataToColumnValues, decodeJwtPayload, deepSpaceAgentErrorSummary, deleteChatCascade, deleteRecord, enqueueJob, executeQuery, formatBytes, getAllUsers, getChat, getGlobalDOSchemas, getGlobalDOType, getOrCreateYjsDoc, getRecord, getRolePermissions, getSubscription, getUser, getYjsDocKey, handleApiRequest, handleDelete, handlePut, handleSetRole, handleSubscribe, handleToolsRequest, handleUnsubscribe, handleUserList, handleUserUpdate, handleYjsBinaryMessage, handleYjsJoin, handleYjsLeave, isAutoProvision, isFilesVerbPath, isOwner, isTestAccountClaims, isTestAccountEmail, isTestAccountTier, lintSchema, loadMessages, makeDefaultSummarizer, matchOrigin, meterAi, meterUsage, meterVectorize, mintAppId, mintUlid, noopPermissionContext, parseQuotaLimit, platformCorsPatterns, platformWorkerFetch, prepareMessagesWithCompaction, priceBindingUsageEvent, putRecord, readRecord, recordMatchesSubscription, refundInvoice, registerUser, registryErrorJson, registryStub, requireSubscription, resolveAppName, resolveAppRole, resolveColumn, rowToData, runMigrations, saveYjsDoc, storageLimitForTier, storageQuotaMessage, streamDeepSpaceAgent, timingSafeEqualHex, timingSafeEqualStrings, totalChars, truncateOldToolResults, turnsToCoreMessages, unwrapToolOutput, updateChat, validateAppName, validateBindingManifest, validateDoManifest, verifyJwt, workspaceAccountsSchema, workspaceContentSharesSchema, workspaceEmailHandlesSchema, workspaceFormResponsesSchema, workspacePeopleSchema, workspaceProjectsSchema, workspaceTagsSchema, workspaceTasksSchema, workspaceTeamMembersSchema, workspaceTeamsSchema, workspaceTransactionsSchema };
|
|
4705
|
+
export { ACCOUNT_STORAGE_LIMIT_BYTES, AI_CHATS_SCHEMA, AI_MESSAGES_SCHEMA, ALLOWED_BINDING_TYPES, APP_ID_RE, APP_NAME_RULES, AUTO_PROVISIONABLE_TYPES, AUTO_PROVISION_SENTINEL, type ActionContext, type ActionHandler, type ActionResult, type ActionTools, type ApiWorkerEnv, type AppNameResolution, type AppNameValidation, type AuthWorkerEnv, BASE_USERS_SCHEMA, BUILT_IN_TOOLS, BaseRoom, CHANNELS_SCHEMA, CHANNEL_INVITATIONS_SCHEMA, CHANNEL_MEMBERS_SCHEMA, CONVERSATION_SCHEMAS, COST_RATES, CancelSubscriptionError, type CancelSubscriptionOpts, type CancelSubscriptionResult, CanvasRoom, type CanvasShape, type ChatContextConfig, type ChatMessageRow, type ChatRow, type ChatTurn, type CollectionPermissionSummary, type CollectionSchema, type ColumnDefinition, type ColumnInterpretation, type ComposioToolsOptions, type ConvMemberData, type ConvMessageData, type ConvReactionData, type ConvReadCursorData, type ConvVoteData, type ConversationStateData, type CronContext, type CronExecution, CronRoom, type CronRoomConfig, type CronTask, type CustomBinding, type CustomBindingManifest, DEFAULT_CONTEXT_CONFIG, DEFAULT_DELETE_WHERE_LIMIT, DEFAULT_DO_MANIFEST, DEFAULT_QUERY_LIMIT, DEPLOYED_APPS_DEFAULTS, DIRECTORY_SCHEMAS, type DOBindings, type DOManifest, type DOManifestEntry, type DebugApiContext, type DeepSpaceAIEnv, type DeepSpaceAIOptions, type DeepSpaceAgentDiagnosticContext, DeepSpaceAgentModelError, DeepSpaceAgentProfileError, type DeepSpaceAgentStream, type DeepSpaceAgentStreamOptions, type DeepSpaceAuth, type DeepSpaceAuthConfig, type DirectoryCommunityData, type DirectoryConversationData, type DirectoryMembershipData, type DirectoryPostData, type DoMigrationDirective, type DoMigrationPlan, type ExistingDOBinding, GLOBAL_DO_TYPES, GLOBAL_DO_TYPE_NAMES, type GetActionData, type GlobalDOType, type Job, type JobContext, JobRoom, type JobRoomConfig, type JobStatus, type JwtClaims, type JwtVerifierConfig, MAX_APP_FILE_BYTES, MAX_DELETE_WHERE_LIMIT, MAX_DEPLOY_ASSET_FILE_BYTES, MAX_UPLOAD_PARTS, MAX_UPLOAD_REQUEST_BYTES, MESSAGES_SCHEMA, type MutateActionData, type PermissionAnalysis, type PermissionContext, type PermissionLevel, type PermissionSource, type PlanInfo, type PlatformWorkerEnv, type PrefixResult, type PresencePeer, PresenceRoom, type QueryActionData, REACTIONS_SCHEMA, READ_RECEIPTS_SCHEMA, REGISTRY_INSTANCE, RESERVED_BINDING_NAMES, RESERVED_COLLECTION_NAMES, RESOURCE_ID_RE, type RecordContext, RecordRoom, type RecordRoomConfig, RefundError, type RefundOpts, type RefundResult, RegistryClientError, type ResolvedColumn, type ResolvedPermission, type RolePermissions, type RunMigrationsResult, STRICT_APP_ID_RE, SYSTEM_ASSIGNED_COLUMNS, SYSTEM_COLLECTION_SCHEMAS, SYSTEM_MAINTAINED_COLUMNS, SYSTEM_MANAGED_COLUMNS, SchemaRegistry, type ScopeContext, type ScopedR2Auth, type ScopedR2Config, type ScopedR2Handler, type ScreenshotEnv, type ScreenshotResult, type SharedConnection, type StorageAdmission, type StoredRecord, SubscriptionAuthError, type SubscriptionContext, type SubscriptionRead, SubscriptionRequiredError, type SubscriptionStatus, type Summarizer, TEST_ACCOUNT_EMAIL_SUFFIX, type TokenDebugInfo, type ToolResult, type ToolSchema, type ToolsApiContext, UPLOAD_PART_BYTES, USERS_COLUMNS, type User, type UserAttachment, type UserContext, VOTING_SCHEMAS, type ValidationError, type VerifiedAuth, type VerifyOutcome, type VerifyResult, type Viewport, WORKSPACE_SCHEMAS, type YjsContext, YjsRoom, analyzePermissions, apiWorkerFetch, appendMessage, applyAiToolDefaults, applySlidingWindow, authWorkerFetch, bindingManifestFromOutputConfig, broadcastChange, broadcastUserList, broadcastYjsUpdate, buildCronContext, buildTableSelect, buildUiParts, callRegistry, canCreate, canDelete, canRead, canUpdate, cancelSubscription, capToolResultSize, captureScreenshot, checkFieldPermissions, checkUnclaimedOwnerTransition, coerceValue, collectionTableName, columnId, composioTools, computeDoMigration, computeHmacHex, createChat, createDeepSpaceAI, createDeepSpaceAuth, createScopedR2Handler, dataToColumnValues, decodeJwtPayload, deepSpaceAgentErrorSummary, deleteChatCascade, deleteRecord, enqueueJob, executeQuery, formatBytes, getAllUsers, getChat, getGlobalDOSchemas, getGlobalDOType, getOrCreateYjsDoc, getRecord, getRolePermissions, getSubscription, getUser, getYjsDocKey, handleApiRequest, handleDelete, handlePut, handleSetRole, handleSubscribe, handleToolsRequest, handleUnsubscribe, handleUserList, handleUserUpdate, handleYjsBinaryMessage, handleYjsJoin, handleYjsLeave, isAutoProvision, isFilesVerbPath, isOwner, isTestAccountClaims, isTestAccountEmail, isTestAccountTier, lintSchema, lintSchemaSet, lintSchemas, loadMessages, makeDefaultSummarizer, matchOrigin, meterAi, meterUsage, meterVectorize, mintAppId, mintUlid, noopPermissionContext, parseQuotaLimit, platformCorsPatterns, platformWorkerFetch, prepareMessagesWithCompaction, priceBindingUsageEvent, putRecord, readRecord, recordMatchesSubscription, refundInvoice, registerUser, registryErrorJson, registryStub, requireSubscription, resolveAppName, resolveAppRole, resolveColumn, rowToData, runMigrations, saveYjsDoc, storageLimitForTier, storageQuotaMessage, streamDeepSpaceAgent, timingSafeEqualHex, timingSafeEqualStrings, totalChars, truncateOldToolResults, turnsToCoreMessages, unwrapToolOutput, updateChat, validateAppName, validateBindingManifest, validateDoManifest, verifyJwt, workspaceAccountsSchema, workspaceContentSharesSchema, workspaceEmailHandlesSchema, workspaceFormResponsesSchema, workspacePeopleSchema, workspaceProjectsSchema, workspaceTagsSchema, workspaceTasksSchema, workspaceTeamMembersSchema, workspaceTeamsSchema, workspaceTransactionsSchema };
|