chore: generate

This commit is contained in:
opencode-agent[bot]
2026-03-25 14:48:44 +00:00
parent b0017bf1b9
commit 971383661a
4 changed files with 261 additions and 202 deletions
@@ -2,9 +2,7 @@
"version": "7", "version": "7",
"dialect": "sqlite", "dialect": "sqlite",
"id": "f13dfa58-7fb4-47a2-8f6b-dc70258e14ed", "id": "f13dfa58-7fb4-47a2-8f6b-dc70258e14ed",
"prevIds": [ "prevIds": ["37e1554d-af4c-43f2-aa7c-307fb49a315e"],
"37e1554d-af4c-43f2-aa7c-307fb49a315e"
],
"ddl": [ "ddl": [
{ {
"name": "account_state", "name": "account_state",
@@ -969,13 +967,9 @@
"table": "event" "table": "event"
}, },
{ {
"columns": [ "columns": ["active_account_id"],
"active_account_id"
],
"tableTo": "account", "tableTo": "account",
"columnsTo": [ "columnsTo": ["id"],
"id"
],
"onUpdate": "NO ACTION", "onUpdate": "NO ACTION",
"onDelete": "SET NULL", "onDelete": "SET NULL",
"nameExplicit": false, "nameExplicit": false,
@@ -984,13 +978,9 @@
"table": "account_state" "table": "account_state"
}, },
{ {
"columns": [ "columns": ["project_id"],
"project_id"
],
"tableTo": "project", "tableTo": "project",
"columnsTo": [ "columnsTo": ["id"],
"id"
],
"onUpdate": "NO ACTION", "onUpdate": "NO ACTION",
"onDelete": "CASCADE", "onDelete": "CASCADE",
"nameExplicit": false, "nameExplicit": false,
@@ -999,13 +989,9 @@
"table": "workspace" "table": "workspace"
}, },
{ {
"columns": [ "columns": ["session_id"],
"session_id"
],
"tableTo": "session", "tableTo": "session",
"columnsTo": [ "columnsTo": ["id"],
"id"
],
"onUpdate": "NO ACTION", "onUpdate": "NO ACTION",
"onDelete": "CASCADE", "onDelete": "CASCADE",
"nameExplicit": false, "nameExplicit": false,
@@ -1014,13 +1000,9 @@
"table": "message" "table": "message"
}, },
{ {
"columns": [ "columns": ["message_id"],
"message_id"
],
"tableTo": "message", "tableTo": "message",
"columnsTo": [ "columnsTo": ["id"],
"id"
],
"onUpdate": "NO ACTION", "onUpdate": "NO ACTION",
"onDelete": "CASCADE", "onDelete": "CASCADE",
"nameExplicit": false, "nameExplicit": false,
@@ -1029,13 +1011,9 @@
"table": "part" "table": "part"
}, },
{ {
"columns": [ "columns": ["project_id"],
"project_id"
],
"tableTo": "project", "tableTo": "project",
"columnsTo": [ "columnsTo": ["id"],
"id"
],
"onUpdate": "NO ACTION", "onUpdate": "NO ACTION",
"onDelete": "CASCADE", "onDelete": "CASCADE",
"nameExplicit": false, "nameExplicit": false,
@@ -1044,13 +1022,9 @@
"table": "permission" "table": "permission"
}, },
{ {
"columns": [ "columns": ["project_id"],
"project_id"
],
"tableTo": "project", "tableTo": "project",
"columnsTo": [ "columnsTo": ["id"],
"id"
],
"onUpdate": "NO ACTION", "onUpdate": "NO ACTION",
"onDelete": "CASCADE", "onDelete": "CASCADE",
"nameExplicit": false, "nameExplicit": false,
@@ -1059,13 +1033,9 @@
"table": "session" "table": "session"
}, },
{ {
"columns": [ "columns": ["session_id"],
"session_id"
],
"tableTo": "session", "tableTo": "session",
"columnsTo": [ "columnsTo": ["id"],
"id"
],
"onUpdate": "NO ACTION", "onUpdate": "NO ACTION",
"onDelete": "CASCADE", "onDelete": "CASCADE",
"nameExplicit": false, "nameExplicit": false,
@@ -1074,13 +1044,9 @@
"table": "todo" "table": "todo"
}, },
{ {
"columns": [ "columns": ["session_id"],
"session_id"
],
"tableTo": "session", "tableTo": "session",
"columnsTo": [ "columnsTo": ["id"],
"id"
],
"onUpdate": "NO ACTION", "onUpdate": "NO ACTION",
"onDelete": "CASCADE", "onDelete": "CASCADE",
"nameExplicit": false, "nameExplicit": false,
@@ -1089,13 +1055,9 @@
"table": "session_share" "table": "session_share"
}, },
{ {
"columns": [ "columns": ["aggregate_id"],
"aggregate_id"
],
"tableTo": "event_sequence", "tableTo": "event_sequence",
"columnsTo": [ "columnsTo": ["aggregate_id"],
"aggregate_id"
],
"onUpdate": "NO ACTION", "onUpdate": "NO ACTION",
"onDelete": "CASCADE", "onDelete": "CASCADE",
"nameExplicit": false, "nameExplicit": false,
@@ -1104,119 +1066,91 @@
"table": "event" "table": "event"
}, },
{ {
"columns": [ "columns": ["email", "url"],
"email",
"url"
],
"nameExplicit": false, "nameExplicit": false,
"name": "control_account_pk", "name": "control_account_pk",
"entityType": "pks", "entityType": "pks",
"table": "control_account" "table": "control_account"
}, },
{ {
"columns": [ "columns": ["session_id", "position"],
"session_id",
"position"
],
"nameExplicit": false, "nameExplicit": false,
"name": "todo_pk", "name": "todo_pk",
"entityType": "pks", "entityType": "pks",
"table": "todo" "table": "todo"
}, },
{ {
"columns": [ "columns": ["id"],
"id"
],
"nameExplicit": false, "nameExplicit": false,
"name": "account_state_pk", "name": "account_state_pk",
"table": "account_state", "table": "account_state",
"entityType": "pks" "entityType": "pks"
}, },
{ {
"columns": [ "columns": ["id"],
"id"
],
"nameExplicit": false, "nameExplicit": false,
"name": "account_pk", "name": "account_pk",
"table": "account", "table": "account",
"entityType": "pks" "entityType": "pks"
}, },
{ {
"columns": [ "columns": ["id"],
"id"
],
"nameExplicit": false, "nameExplicit": false,
"name": "workspace_pk", "name": "workspace_pk",
"table": "workspace", "table": "workspace",
"entityType": "pks" "entityType": "pks"
}, },
{ {
"columns": [ "columns": ["id"],
"id"
],
"nameExplicit": false, "nameExplicit": false,
"name": "project_pk", "name": "project_pk",
"table": "project", "table": "project",
"entityType": "pks" "entityType": "pks"
}, },
{ {
"columns": [ "columns": ["id"],
"id"
],
"nameExplicit": false, "nameExplicit": false,
"name": "message_pk", "name": "message_pk",
"table": "message", "table": "message",
"entityType": "pks" "entityType": "pks"
}, },
{ {
"columns": [ "columns": ["id"],
"id"
],
"nameExplicit": false, "nameExplicit": false,
"name": "part_pk", "name": "part_pk",
"table": "part", "table": "part",
"entityType": "pks" "entityType": "pks"
}, },
{ {
"columns": [ "columns": ["project_id"],
"project_id"
],
"nameExplicit": false, "nameExplicit": false,
"name": "permission_pk", "name": "permission_pk",
"table": "permission", "table": "permission",
"entityType": "pks" "entityType": "pks"
}, },
{ {
"columns": [ "columns": ["id"],
"id"
],
"nameExplicit": false, "nameExplicit": false,
"name": "session_pk", "name": "session_pk",
"table": "session", "table": "session",
"entityType": "pks" "entityType": "pks"
}, },
{ {
"columns": [ "columns": ["session_id"],
"session_id"
],
"nameExplicit": false, "nameExplicit": false,
"name": "session_share_pk", "name": "session_share_pk",
"table": "session_share", "table": "session_share",
"entityType": "pks" "entityType": "pks"
}, },
{ {
"columns": [ "columns": ["aggregate_id"],
"aggregate_id"
],
"nameExplicit": false, "nameExplicit": false,
"name": "event_sequence_pk", "name": "event_sequence_pk",
"table": "event_sequence", "table": "event_sequence",
"entityType": "pks" "entityType": "pks"
}, },
{ {
"columns": [ "columns": ["id"],
"id"
],
"nameExplicit": false, "nameExplicit": false,
"name": "event_pk", "name": "event_pk",
"table": "event", "table": "event",
@@ -1334,4 +1268,4 @@
} }
], ],
"renames": [] "renames": []
} }
+25 -27
View File
@@ -1,13 +1,11 @@
tl;dr All of these APIs work, are properly type-checked, and are sync events are backwards compatible with `Bus`: tl;dr All of these APIs work, are properly type-checked, and are sync events are backwards compatible with `Bus`:
```ts ```ts
// The schema from `Updated` typechecks the object correctly // The schema from `Updated` typechecks the object correctly
SyncEvent.run(Updated, { sessionID: id, info: { title: "foo"} }) SyncEvent.run(Updated, { sessionID: id, info: { title: "foo" } })
// `subscribeAll` passes a generic sync event // `subscribeAll` passes a generic sync event
SyncEvent.subscribeAll(event => { SyncEvent.subscribeAll((event) => {
// These will be type-checked correctly // These will be type-checked correctly
event.id event.id
event.seq event.seq
@@ -17,13 +15,13 @@ SyncEvent.subscribeAll(event => {
}) })
// This works, but you shouldn't publish sync event like this (should fail in the future) // This works, but you shouldn't publish sync event like this (should fail in the future)
Bus.publish(Updated, { sessionID: id, info: { title: "foo"} }) Bus.publish(Updated, { sessionID: id, info: { title: "foo" } })
// Update event is fully type-checked // Update event is fully type-checked
Bus.subscribe(Updated, event => event.properties.info.title) Bus.subscribe(Updated, (event) => event.properties.info.title)
// Update event is fully type-checked // Update event is fully type-checked
client.subscribe("session.updated", evt => evt.properties.info.title) client.subscribe("session.updated", (evt) => evt.properties.info.title)
``` ```
# Goal # Goal
@@ -120,10 +118,10 @@ It's very important that types are correct when working with events. Event defin
```ts ```ts
// The schema from `Updated` typechecks the object correctly // The schema from `Updated` typechecks the object correctly
SyncEvent.run(Updated, { sessionID: id, info: { title: "foo"} }) SyncEvent.run(Updated, { sessionID: id, info: { title: "foo" } })
// `subscribeAll` passes a generic sync event // `subscribeAll` passes a generic sync event
SyncEvent.subscribeAll(event => { SyncEvent.subscribeAll((event) => {
// These will be type-checked correctly // These will be type-checked correctly
event.id event.id
event.seq event.seq
@@ -133,13 +131,13 @@ SyncEvent.subscribeAll(event => {
}) })
// This works, but you shouldn't publish sync event like this (should fail in the future) // This works, but you shouldn't publish sync event like this (should fail in the future)
Bus.publish(Updated, { sessionID: id, info: { title: "foo"} }) Bus.publish(Updated, { sessionID: id, info: { title: "foo" } })
// Update event is fully type-checked // Update event is fully type-checked
Bus.subscribe(Updated, event => event.properties.info.title) Bus.subscribe(Updated, (event) => event.properties.info.title)
// Update event is fully type-checked // Update event is fully type-checked
client.subscribe("session.updated", evt => evt.properties.info.title) client.subscribe("session.updated", (evt) => evt.properties.info.title)
``` ```
The last two examples look similar to `SyncEvent.run`, but they were the cause of a lot of grief. Those are existing APIs that we can't break, but we are passing in the new sync event definitions to these APIs, which sometimes have a different event shape. The last two examples look similar to `SyncEvent.run`, but they were the cause of a lot of grief. Those are existing APIs that we can't break, but we are passing in the new sync event definitions to these APIs, which sometimes have a different event shape.
@@ -153,7 +151,7 @@ const Update = SyncEvent.define({
aggregate: "sessionID", aggregate: "sessionID",
schema: z.object({ schema: z.object({
sessionID: SessionID.zod, sessionID: SessionID.zod,
info: partialSchema(Info) info: partialSchema(Info),
}), }),
busSchema: z.object({ busSchema: z.object({
sessionID: SessionID.zod, sessionID: SessionID.zod,
@@ -162,20 +160,20 @@ const Update = SyncEvent.define({
}) })
``` ```
*Important*: the conversion done in `convertEvent` is not automatically type-checked with `busSchema`. It's very important they match, but because we need this at type-checking time this needs to live here. _Important_: the conversion done in `convertEvent` is not automatically type-checked with `busSchema`. It's very important they match, but because we need this at type-checking time this needs to live here.
Internally, the way this works is `busSchema` is stored on a `properties` field which is what the bus system expects. Doing this made everything with `Bus` "just work". This is why you can pass a sync event to the bus APIs. Internally, the way this works is `busSchema` is stored on a `properties` field which is what the bus system expects. Doing this made everything with `Bus` "just work". This is why you can pass a sync event to the bus APIs.
*Alternatives* _Alternatives_
These are some other paths I explored: These are some other paths I explored:
* Providing a way to subscribe to individual sync events, and change all the instances of `Bus.subscribe` in our code to it. Then you are directly only working with sync events always. - Providing a way to subscribe to individual sync events, and change all the instances of `Bus.subscribe` in our code to it. Then you are directly only working with sync events always.
* Two big problems. First, `Bus` is instance-scoped, and we'd need to make the sync event system instance-scoped too for backwards compat. If we didn't, those listeners would get calls for events they weren't expecting. - Two big problems. First, `Bus` is instance-scoped, and we'd need to make the sync event system instance-scoped too for backwards compat. If we didn't, those listeners would get calls for events they weren't expecting.
* Second, we can't change consumers of our SDK. So they still have to use the old events, and we might as well stick with them for consistency - Second, we can't change consumers of our SDK. So they still have to use the old events, and we might as well stick with them for consistency
* Directly add sync event support to bus system - Directly add sync event support to bus system
* I explored adding sync events to the bus, but due to backwards compat, it only made it more complicated (still need to support both shapes) - I explored adding sync events to the bus, but due to backwards compat, it only made it more complicated (still need to support both shapes)
* I explored a `convertSchema` function to convert the event schema at runtime so we didn't need `busSchema` - I explored a `convertSchema` function to convert the event schema at runtime so we didn't need `busSchema`
* Fatal flaw: we need type-checking done earlier. We can't do this at run-time. This worked for consumers of our SDK (because it gets generated TS types from the converted schema) but breaks for our internal usage of `Bus.subscribe` calls - Fatal flaw: we need type-checking done earlier. We can't do this at run-time. This worked for consumers of our SDK (because it gets generated TS types from the converted schema) but breaks for our internal usage of `Bus.subscribe` calls
I explored many other permutations of the above solutions. What we have today I think is the best balance of backwards compatibility while opening a path forward for the new events. I explored many other permutations of the above solutions. What we have today I think is the best balance of backwards compatibility while opening a path forward for the new events.
+18 -18
View File
@@ -1068,36 +1068,36 @@ export type SyncEventSessionUpdated = {
data: { data: {
sessionID: string sessionID: string
info: { info: {
id?: string id: string | null
slug?: string slug: string | null
projectID?: string projectID: string | null
workspaceID?: string workspaceID: string | null
directory?: string directory: string | null
parentID?: string parentID: string | null
summary?: { summary: {
additions: number additions: number
deletions: number deletions: number
files: number files: number
diffs?: Array<FileDiff> diffs?: Array<FileDiff>
} } | null
share?: { share?: {
url?: string url: string | null
} }
title?: string title: string | null
version?: string version: string | null
time?: { time?: {
created?: number created: number | null
updated?: number updated: number | null
compacting?: number compacting: number | null
archived?: number archived: number | null
} }
permission?: PermissionRuleset permission: PermissionRuleset | null
revert?: { revert: {
messageID: string messageID: string
partID?: string partID?: string
snapshot?: string snapshot?: string
diff?: string diff?: string
} } | null
} }
} }
} }
+185 -58
View File
@@ -10048,102 +10048,229 @@
"type": "object", "type": "object",
"properties": { "properties": {
"id": { "id": {
"type": "string", "anyOf": [
"pattern": "^ses.*" {
"type": "string",
"pattern": "^ses.*"
},
{
"type": "null"
}
]
}, },
"slug": { "slug": {
"type": "string" "anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}, },
"projectID": { "projectID": {
"type": "string" "anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}, },
"workspaceID": { "workspaceID": {
"type": "string", "anyOf": [
"pattern": "^wrk.*" {
"type": "string",
"pattern": "^wrk.*"
},
{
"type": "null"
}
]
}, },
"directory": { "directory": {
"type": "string" "anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}, },
"parentID": { "parentID": {
"type": "string", "anyOf": [
"pattern": "^ses.*" {
"type": "string",
"pattern": "^ses.*"
},
{
"type": "null"
}
]
}, },
"summary": { "summary": {
"type": "object", "anyOf": [
"properties": { {
"additions": { "type": "object",
"type": "number" "properties": {
"additions": {
"type": "number"
},
"deletions": {
"type": "number"
},
"files": {
"type": "number"
},
"diffs": {
"type": "array",
"items": {
"$ref": "#/components/schemas/FileDiff"
}
}
},
"required": ["additions", "deletions", "files"]
}, },
"deletions": { {
"type": "number" "type": "null"
},
"files": {
"type": "number"
},
"diffs": {
"type": "array",
"items": {
"$ref": "#/components/schemas/FileDiff"
}
} }
}, ]
"required": ["additions", "deletions", "files"]
}, },
"share": { "share": {
"type": "object", "type": "object",
"properties": { "properties": {
"url": { "url": {
"type": "string" "anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
} }
} },
"required": ["url"]
}, },
"title": { "title": {
"type": "string" "anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}, },
"version": { "version": {
"type": "string" "anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}, },
"time": { "time": {
"type": "object", "type": "object",
"properties": { "properties": {
"created": { "created": {
"type": "number" "anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
}, },
"updated": { "updated": {
"type": "number" "anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
}, },
"compacting": { "compacting": {
"type": "number" "anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
}, },
"archived": { "archived": {
"type": "number" "anyOf": [
} {
} "type": "number"
}, },
"permission": { {
"$ref": "#/components/schemas/PermissionRuleset" "type": "null"
}, }
"revert": { ]
"type": "object",
"properties": {
"messageID": {
"type": "string",
"pattern": "^msg.*"
},
"partID": {
"type": "string",
"pattern": "^prt.*"
},
"snapshot": {
"type": "string"
},
"diff": {
"type": "string"
} }
}, },
"required": ["messageID"] "required": ["created", "updated", "compacting", "archived"]
},
"permission": {
"anyOf": [
{
"$ref": "#/components/schemas/PermissionRuleset"
},
{
"type": "null"
}
]
},
"revert": {
"anyOf": [
{
"type": "object",
"properties": {
"messageID": {
"type": "string",
"pattern": "^msg.*"
},
"partID": {
"type": "string",
"pattern": "^prt.*"
},
"snapshot": {
"type": "string"
},
"diff": {
"type": "string"
}
},
"required": ["messageID"]
},
{
"type": "null"
}
]
} }
} },
"required": [
"id",
"slug",
"projectID",
"workspaceID",
"directory",
"parentID",
"summary",
"title",
"version",
"permission",
"revert"
]
} }
}, },
"required": ["sessionID", "info"] "required": ["sessionID", "info"]