openvibers/openvibe.chat Commits

Chat drops table_authority (plan T3): the previous release (04d2625, now the N-1 fixture, re-recorded) no longer reads it, so it goes from existing databases at boot and from schema.sql. npm test 42/42.

OP OpenVibers committed 29 Sept 2026 · c90745a942 · 4 files, +112 −111


Changes

ddiff --git a/server/db/database.js b/server/db/database.js
iindex 15b7c2a..2a458b8 100644
---- a/server/db/database.js
++++ b/server/db/database.js
@@@ -109,8 +109,9 @@ function initDb({ captureMirror = false } = {}) {
// auth/network-session.js resolves a verified Network token to its user by subject.
d.exec('CREATE INDEX IF NOT EXISTS idx_ctx_users_subject ON ctx_users(subject_id)');
// C-04 is done: Chat is the only writer of the six tables, so there is no authority left to
-- // record. The table's migration drops it from databases an earlier release created; a new
-- // database never gets it (schema.sql no longer has it).
++ // record. Drop the table from databases an earlier release created (it stayed one release for N-1);
++ // a new database never gets it (schema.sql no longer has it).
++ d.exec('DROP TABLE IF EXISTS table_authority');
if (captureMirror) installMirrorTriggers();
return d;
}
ddiff --git a/server/db/schema.sql b/server/db/schema.sql
iindex 0d7c9d4..7150537 100644
---- a/server/db/schema.sql
++++ b/server/db/schema.sql
@@@ -184,14 +184,6 @@ CREATE INDEX IF NOT EXISTS idx_mod_actions_created ON moderation_actions(created
CREATE INDEX IF NOT EXISTS idx_mod_actions_actor ON moderation_actions(actor_user_id);
CREATE INDEX IF NOT EXISTS idx_mod_actions_scope ON moderation_actions(scope_type, scope_id);
---- Kept one release for N-1 (ADR-016): the previous release reads it, and its rows say 'chat' for all six, so a
---- rollback still leaves Chat the writer. Nothing in this release reads or writes it; the next release drops it.
--CREATE TABLE IF NOT EXISTS table_authority (
-- table_name TEXT PRIMARY KEY,
-- authority TEXT NOT NULL CHECK(authority IN ('chat', 'live')),
-- note TEXT
--);
--
-- ── 2. The six chat-target tables (Chat is their only writer since C-04) ──
CREATE TABLE IF NOT EXISTS channel_moderators (
ddiff --git a/test/fixtures/n-1/client.json b/test/fixtures/n-1/client.json
iindex db9fc9c..d65ddbb 100644
---- a/test/fixtures/n-1/client.json
++++ b/test/fixtures/n-1/client.json
@@@ -1,32 +1,32 @@
{
"service": "chat",
-- "release": "a2d100db6ae74616273cfb3d9e96188c68d1cc1f",
-- "recorded_at": "2026-09-27T23:24:33.583Z",
++ "release": "04d2625d98d26fc3b7a55f7a3996e42a1ad6e4d4",
++ "recorded_at": "2026-09-29T17:34:13.359Z",
"note": "N-1 fixture: written by `npm run n-1:record`, replayed by test/n-1.test.js",
"clients": {
-- "live": "06cf38b12faa92ce5ee8bc335ce5f0110b1c6537"
++ "live": "ee2ad948a4c25b9af9c27be16e731025afa6c2d3"
},
"manifest": {
"service": "chat",
-- "release": "a2d100db6ae7",
-- "released_at": "2026-09-27T23:14:50.000Z",
-- "booted_at": "2026-09-27T23:24:28.601Z",
-- "contracts_version": "0.71.0",
++ "release": "04d2625d98d2",
++ "released_at": "2026-09-29T16:52:43.000Z",
++ "booted_at": "2026-09-29T17:34:08.214Z",
++ "contracts_version": "0.80.0",
"packages": {
-- "openvibe-shared": "1.22.0",
-- "openvibe-sdk": "0.11.0",
-- "openvibe-contracts": "0.71.0"
++ "openvibe-shared": "2.0.0",
++ "openvibe-sdk": "0.23.1",
++ "openvibe-contracts": "0.80.0"
},
"min_client_release": null,
"mixed_version_window_hours": 24,
"components": {
"shell": {
"kind": "script",
-- "version": "a2d100db6ae7"
++ "version": "04d2625d98d2"
},
"server": {
"kind": "server",
-- "version": "a2d100db6ae7"
++ "version": "04d2625d98d2"
}
},
"assets": {},
@@@ -38,7 +38,7 @@
"shell": null,
"metrics_url": "/release-metrics"
},
-- "dynamic_calls": 52,
++ "dynamic_calls": 51,
"calls": [
{
"method": "GET",
@@@ -47,7 +47,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/dashboard.js"
++ "OpenVibe.Live:public/js/dashboard.js"
]
},
{
@@@ -64,7 +64,7 @@
"totalPages": "number"
},
"from": [
-- "seadragon:public/js/dashboard.js"
++ "OpenVibe.Live:public/js/dashboard.js"
]
},
{
@@@ -74,7 +74,7 @@
"status": 400,
"kind": "json",
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -84,7 +84,7 @@
"status": 400,
"kind": "json",
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -127,7 +127,7 @@
"activeManagedId": "null"
},
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -170,7 +170,7 @@
"activeManagedId": "null"
},
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -188,7 +188,7 @@
"categories[].patterns[]": "string"
},
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -206,7 +206,7 @@
"categories[].patterns[]": "string"
},
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -222,8 +222,8 @@
"defaultProvider": "null"
},
"from": [
-- "seadragon:public/js/emotes.js",
-- "seadragon:public/js/gif-picker.js"
++ "OpenVibe.Live:public/js/emotes.js",
++ "OpenVibe.Live:public/js/gif-picker.js"
]
},
{
@@@ -239,8 +239,8 @@
"defaultProvider": "null"
},
"from": [
-- "seadragon:public/js/emotes.js",
-- "seadragon:public/js/gif-picker.js"
++ "OpenVibe.Live:public/js/emotes.js",
++ "OpenVibe.Live:public/js/gif-picker.js"
]
},
{
@@@ -250,7 +250,7 @@
"status": 503,
"kind": "json",
"from": [
-- "seadragon:public/js/gif-picker.js"
++ "OpenVibe.Live:public/js/gif-picker.js"
]
},
{
@@@ -260,7 +260,7 @@
"status": 503,
"kind": "json",
"from": [
-- "seadragon:public/js/gif-picker.js"
++ "OpenVibe.Live:public/js/gif-picker.js"
]
},
{
@@@ -270,7 +270,7 @@
"status": 503,
"kind": "json",
"from": [
-- "seadragon:public/js/gif-picker.js"
++ "OpenVibe.Live:public/js/gif-picker.js"
]
},
{
@@@ -280,7 +280,7 @@
"status": 503,
"kind": "json",
"from": [
-- "seadragon:public/js/gif-picker.js"
++ "OpenVibe.Live:public/js/gif-picker.js"
]
},
{
@@@ -399,7 +399,7 @@
"deleted_ids": "array"
},
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -440,7 +440,7 @@
"deleted_ids": "array"
},
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -561,7 +561,7 @@
"latest_id": "number"
},
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -600,7 +600,7 @@
"latest_id": "number"
},
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -668,7 +668,7 @@
"relayUser": "null"
},
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -681,7 +681,7 @@
"relayUser": "null"
},
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -837,7 +837,7 @@
"followerCount": "number"
},
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -854,7 +854,7 @@
"followerCount": "number"
},
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -898,7 +898,7 @@
"activeManagedId": "null"
},
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -942,7 +942,7 @@
"activeManagedId": "null"
},
"from": [
-- "seadragon:public/js/chat.js"
++ "OpenVibe.Live:public/js/chat.js"
]
},
{
@@@ -974,7 +974,7 @@
"messages[].display_name": "null"
},
"from": [
-- "seadragon:public/js/app-media.js"
++ "OpenVibe.Live:public/js/app-media.js"
]
},
{
@@@ -1006,7 +1006,7 @@
"messages[].display_name": "null"
},
"from": [
-- "seadragon:public/js/app-media.js"
++ "OpenVibe.Live:public/js/app-media.js"
]
},
{
@@@ -1016,8 +1016,8 @@
"status": 401,
"kind": "json",
"from": [
-- "node_modules/openvibe-sdk/src/chat.js",
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js",
++ "node_modules/openvibe-sdk/src/chat.js"
]
},
{
@@@ -1030,8 +1030,8 @@
"blocked": "array"
},
"from": [
-- "node_modules/openvibe-sdk/src/chat.js",
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js",
++ "node_modules/openvibe-sdk/src/chat.js"
]
},
{
@@@ -1041,7 +1041,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1054,7 +1054,7 @@
"blocked": "boolean"
},
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1064,9 +1064,9 @@
"status": 401,
"kind": "json",
"from": [
++ "OpenVibe.Live:public/js/messenger.js",
"node_modules/openvibe-sdk/src/chat.js",
-- "public/web/chat.js",
-- "seadragon:public/js/messenger.js"
++ "public/web/chat.js"
]
},
{
@@@ -1094,9 +1094,9 @@
"conversations[].participants[].profile_color": "string"
},
"from": [
++ "OpenVibe.Live:public/js/messenger.js",
"node_modules/openvibe-sdk/src/chat.js",
-- "public/web/chat.js",
-- "seadragon:public/js/messenger.js"
++ "public/web/chat.js"
]
},
{
@@@ -1106,7 +1106,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1132,7 +1132,7 @@
"conversation.participants[].profile_color": "string"
},
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1142,7 +1142,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1155,7 +1155,7 @@
"messages": "array"
},
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1165,7 +1165,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1178,7 +1178,7 @@
"messages": "array"
},
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1188,7 +1188,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1201,7 +1201,7 @@
"messages": "array"
},
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1234,8 +1234,8 @@
"status": 401,
"kind": "json",
"from": [
-- "node_modules/openvibe-sdk/src/chat.js",
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js",
++ "node_modules/openvibe-sdk/src/chat.js"
]
},
{
@@@ -1248,8 +1248,8 @@
"unread": "number"
},
"from": [
-- "node_modules/openvibe-sdk/src/chat.js",
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js",
++ "node_modules/openvibe-sdk/src/chat.js"
]
},
{
@@@ -1259,7 +1259,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1272,7 +1272,7 @@
"users": "array"
},
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1282,7 +1282,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1295,7 +1295,7 @@
"users": "array"
},
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1305,7 +1305,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/channel-uploads.js"
++ "OpenVibe.Live:public/js/channel-uploads.js"
]
},
{
@@@ -1323,7 +1323,7 @@
"goal.url": "null"
},
"from": [
-- "seadragon:public/js/channel-uploads.js"
++ "OpenVibe.Live:public/js/channel-uploads.js"
]
},
{
@@@ -1336,7 +1336,7 @@
"sounds": "array"
},
"from": [
-- "seadragon:public/js/emotes.js"
++ "OpenVibe.Live:public/js/emotes.js"
]
},
{
@@@ -1349,7 +1349,7 @@
"sounds": "array"
},
"from": [
-- "seadragon:public/js/emotes.js"
++ "OpenVibe.Live:public/js/emotes.js"
]
},
{
@@@ -1376,7 +1376,7 @@
},
{
"method": "GET",
-- "path": "/shared/theme-loader.js?v=d9b9af081efe",
++ "path": "/shared/theme-loader.js?v=ee4a6754d0f7",
"auth": "anon",
"accept": "html",
"status": 200,
@@@ -1387,7 +1387,7 @@
},
{
"method": "GET",
-- "path": "/shared/navbar.js?v=b50d9f47fc34",
++ "path": "/shared/navbar.js?v=b8089ee3bd11",
"auth": "anon",
"accept": "html",
"status": 200,
@@@ -1398,7 +1398,7 @@
},
{
"method": "GET",
-- "path": "/shared/footer.js?v=b99e512398d5",
++ "path": "/shared/footer.js?v=a85ab893be88",
"auth": "anon",
"accept": "html",
"status": 200,
@@@ -1563,7 +1563,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/dashboard.js"
++ "OpenVibe.Live:public/js/dashboard.js"
]
},
{
@@@ -1573,7 +1573,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/dashboard.js"
++ "OpenVibe.Live:public/js/dashboard.js"
]
},
{
@@@ -1643,8 +1643,8 @@
"status": 401,
"kind": "json",
"from": [
-- "node_modules/openvibe-sdk/src/chat.js",
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js",
++ "node_modules/openvibe-sdk/src/chat.js"
]
},
{
@@@ -1654,7 +1654,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1664,7 +1664,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1674,7 +1674,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1684,7 +1684,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1694,7 +1694,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1704,7 +1704,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/messenger.js"
++ "OpenVibe.Live:public/js/messenger.js"
]
},
{
@@@ -1714,7 +1714,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/channel-uploads.js"
++ "OpenVibe.Live:public/js/channel-uploads.js"
]
},
{
@@@ -1724,7 +1724,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/channel-uploads.js"
++ "OpenVibe.Live:public/js/channel-uploads.js"
]
},
{
@@@ -1734,7 +1734,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/channel-uploads.js"
++ "OpenVibe.Live:public/js/channel-uploads.js"
]
},
{
@@@ -1744,7 +1744,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/channel-uploads.js"
++ "OpenVibe.Live:public/js/channel-uploads.js"
]
},
{
@@@ -1754,7 +1754,7 @@
"status": 401,
"kind": "json",
"from": [
-- "seadragon:public/js/broadcast.js"
++ "OpenVibe.Live:public/js/broadcast.js"
]
}
],
ddiff --git a/test/fixtures/n-1/worker.json b/test/fixtures/n-1/worker.json
iindex 1ec90bd..3b7eef9 100644
---- a/test/fixtures/n-1/worker.json
++++ b/test/fixtures/n-1/worker.json
@@@ -1,7 +1,7 @@
{
"service": "chat",
-- "release": "a2d100db6ae74616273cfb3d9e96188c68d1cc1f",
-- "recorded_at": "2026-09-27T23:24:33.583Z",
++ "release": "04d2625d98d26fc3b7a55f7a3996e42a1ad6e4d4",
++ "recorded_at": "2026-09-29T17:34:13.359Z",
"note": "N-1 fixture: written by `npm run n-1:record`, replayed by test/n-1.test.js",
"user_version": 0,
"schema": [
@@@ -17,7 +17,7 @@
"CREATE TABLE chat_messages (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream_id INTEGER, -- Live stream (session) id\n user_id INTEGER, -- Live user id; NULL for anon\n anon_id TEXT, -- 'anon12345' format\n username TEXT,\n message TEXT NOT NULL,\n message_type TEXT DEFAULT 'chat' CHECK(message_type IN ('chat', 'system', 'donation', 'command', 'tts', 'channel-sound', 'soundboard', 'clip')),\n metadata TEXT, -- JSON sidecar for rich events (donation/goal-reached)\n is_global INTEGER DEFAULT 0,\n is_deleted INTEGER DEFAULT 0,\n is_filtered INTEGER DEFAULT 0,\n reply_to_id INTEGER REFERENCES chat_messages(id) ON DELETE SET NULL,\n source_platform TEXT,\n deleted_by INTEGER,\n deleted_at DATETIME,\n auto_delete_at DATETIME,\n timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,\n channel_user_id INTEGER, -- the broadcaster whose channel room this is\n subject_id TEXT -- Network subject of the author (usr_…), when known\n)",
"CREATE TABLE chat_meta (\n key TEXT PRIMARY KEY,\n value TEXT\n)",
"CREATE TABLE chat_timeline_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n scope TEXT NOT NULL DEFAULT 'global',\n subject_id INTEGER NOT NULL DEFAULT 0,\n ts DATETIME NOT NULL,\n label TEXT NOT NULL,\n detail TEXT DEFAULT '',\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n)",
-- "CREATE TABLE ctx_channels (\n id INTEGER PRIMARY KEY,\n user_id INTEGER UNIQUE,\n title TEXT,\n synced_at INTEGER\n)",
++ "CREATE TABLE ctx_channels (\n id INTEGER PRIMARY KEY,\n user_id INTEGER UNIQUE,\n title TEXT,\n emote_sources TEXT, -- the channel's emote source switches (Live's channels.emote_sources)\n synced_at INTEGER\n)",
"CREATE TABLE ctx_managed_streams (\n id INTEGER PRIMARY KEY,\n user_id INTEGER,\n slug TEXT,\n title TEXT,\n sort_order INTEGER DEFAULT 0,\n created_at DATETIME,\n synced_at INTEGER\n)",
"CREATE TABLE ctx_streams (\n id INTEGER PRIMARY KEY,\n user_id INTEGER,\n channel_id INTEGER,\n managed_stream_id INTEGER,\n title TEXT,\n is_live INTEGER DEFAULT 0,\n started_at DATETIME,\n ended_at DATETIME,\n created_at DATETIME,\n synced_at INTEGER\n)",
"CREATE TABLE ctx_sync (\n key TEXT PRIMARY KEY,\n value TEXT,\n updated_at INTEGER\n)",
@@@ -128,12 +128,12 @@
"INSERT INTO chat_ai_summaries (scope, subject_id, window, overview, memory_json, timeline_json, message_count, window_message_count, last_message_id, window_label, window_start, window_end, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(scope, subject_id, window) DO UPDATE SET overview = excluded.overview, memory_json = excluded.memory_json, timeline_json = excluded.timeline_json, message_count = excluded.message_count, window_message_count = excluded.window_message_count, last_message_id = excluded.last_message_id, window_label = excluded.window_label, window_start = excluded.window_start, window_end = excluded.window_end, updated_at = CURRENT_TIMESTAMP",
"INSERT INTO chat_messages (stream_id, channel_user_id, user_id, anon_id, username, message, message_type, is_global, reply_to_id, source_platform, auto_delete_at, metadata, subject_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
"INSERT INTO chat_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value",
-- "INSERT INTO ctx_channels (id, user_id, title, synced_at) VALUES (?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET user_id = excluded.user_id, title = excluded.title, synced_at = excluded.synced_at",
++ "INSERT INTO ctx_channels (id, user_id, title, emote_sources, synced_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET user_id = excluded.user_id, title = excluded.title, emote_sources = excluded.emote_sources, synced_at = excluded.synced_at",
"INSERT INTO ctx_streams (id, user_id, channel_id, managed_stream_id, title, is_live, started_at, ended_at, created_at, synced_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET user_id = excluded.user_id, channel_id = excluded.channel_id, managed_stream_id = excluded.managed_stream_id, title = excluded.title, is_live = excluded.is_live, started_at = excluded.started_at, ended_at = excluded.ended_at, created_at = excluded.created_at, synced_at = excluded.synced_at",
"INSERT INTO ctx_users (id, username, display_name, avatar_url, profile_color, role, is_banned, ban_reason, is_owner, created_at, subject_id, synced_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET username = excluded.username, display_name = excluded.display_name, avatar_url = excluded.avatar_url, profile_color = excluded.profile_color, role = excluded.role, is_banned = excluded.is_banned, ban_reason = excluded.ban_reason, is_owner = excluded.is_owner, created_at = excluded.created_at, subject_id = excluded.subject_id, synced_at = excluded.synced_at",
"INSERT INTO dm_conversations (name, is_group, created_by) VALUES (?, ?, ?)",
"INSERT INTO dm_messages (conversation_id, sender_id, message, sender_subject_id) VALUES (?, ?, ?, ?)",
-- "INSERT INTO emotes (user_id, code, url, animated, width, height, is_global, channel_owner_id, size) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
++ "INSERT INTO emotes (user_id, code, url, animated, width, height, is_global, channel_owner_id, size, media_url, media_asset_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
"INSERT INTO events_outbox (event_id, event_type, event, created_at) VALUES (?, ?, ?, ?)",
"INSERT INTO moderation_actions (scope_type, scope_id, actor_user_id, target_user_id, action_type, details, actor_subject_id) VALUES (?, ?, ?, ?, ?, ?, ?)",
"INSERT INTO network_blocks (blocker_subject, blocked_subject, active, revision, updated_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(blocker_subject, blocked_subject) DO UPDATE SET active = excluded.active, revision = excluded.revision, updated_at = excluded.updated_at WHERE excluded.revision > network_blocks.revision",
@@@ -145,8 +145,6 @@
"INSERT INTO room_members (room_id, user_id, role, last_seen_at) VALUES (?, ?, 'owner', CURRENT_TIMESTAMP)",
"INSERT INTO room_messages (room_id, user_id, subject_id, message) VALUES (?, ?, ?, ?)",
"INSERT INTO rooms (slug, name, topic, kind, visibility, owner_id, owner_subject, join_role) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
-- "INSERT INTO table_authority (table_name, authority, note) VALUES (?, 'live', ?) ON CONFLICT(table_name) DO UPDATE SET note = excluded.note",
-- "INSERT INTO table_authority (table_name, authority, note) VALUES (?, ?, ?) ON CONFLICT(table_name) DO UPDATE SET authority = excluded.authority, note = excluded.note",
"INSERT INTO token_revocations (subject_id, valid_after_ms, reason, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT(subject_id) DO UPDATE SET valid_after_ms = excluded.valid_after_ms, reason = excluded.reason, updated_at = excluded.updated_at WHERE excluded.valid_after_ms > token_revocations.valid_after_ms",
"INSERT INTO tts_voice_overrides (identity_key, voice, pitch, speed, gap, set_by, updated_at) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(identity_key) DO UPDATE SET voice=excluded.voice, pitch=excluded.pitch, speed=excluded.speed, gap=excluded.gap, set_by=excluded.set_by, updated_at=CURRENT_TIMESTAMP",
"INSERT OR IGNORE INTO channel_moderators (channel_id, user_id, added_by) VALUES (?, ?, ?)",
@@@ -176,6 +174,7 @@
"SELECT * FROM channel_sounds WHERE channel_owner_id = ? AND is_approved = 1 ORDER BY command",
"SELECT * FROM channel_sounds WHERE id = ?",
"SELECT * FROM chat_ai_summaries WHERE scope = ? AND subject_id = ? AND window = ?",
++ "SELECT * FROM chat_ai_summaries WHERE scope = ? AND subject_id = ? ORDER BY window",
"SELECT * FROM chat_messages WHERE id = ?",
"SELECT * FROM chat_timeline_events WHERE id = ?",
"SELECT * FROM ctx_channels WHERE id = ?",
@@@ -185,7 +184,9 @@
"SELECT * FROM ctx_users WHERE subject_id = ? LIMIT 2",
"SELECT * FROM dm_conversations WHERE id = ?",
"SELECT * FROM dm_messages WHERE id = ?",
++ "SELECT * FROM emotes WHERE code = ? AND ((channel_owner_id = ?) OR (channel_owner_id IS NULL AND user_id = ?)) LIMIT 1",
"SELECT * FROM emotes WHERE id = ?",
++ "SELECT * FROM emotes WHERE user_id = ? ORDER BY code",
"SELECT * FROM pending_ip_messages WHERE id = ?",
"SELECT * FROM room_attachments WHERE room_id = ? AND service = ? AND resource = ?",
"SELECT * FROM room_attachments WHERE room_id = ? ORDER BY created_at, service, resource",
@@@ -216,7 +217,6 @@
"SELECT COUNT(*) AS n FROM audio_requests WHERE room = ? AND identity_key = ? AND state IN ('queued', 'playing')",
"SELECT COUNT(*) AS n FROM audio_requests WHERE room = ? AND state IN ('queued', 'playing')",
"SELECT COUNT(*) AS n FROM live_mirror_outbox",
-- "SELECT COUNT(*) AS n FROM live_mirror_outbox WHERE tbl = ?",
"SELECT COUNT(*) AS n FROM room_members WHERE room_id = ? AND role != 'blocked'",
"SELECT COUNT(*) AS n FROM room_members m JOIN rooms r ON r.id = m.room_id AND r.archived_at IS NULL JOIN room_messages x ON x.room_id = m.room_id AND x.id > m.last_read_id AND x.is_deleted = 0 AND x.user_id != m.user_id WHERE m.user_id = ? AND m.role != 'blocked'",
"SELECT COUNT(*) AS n FROM rooms WHERE owner_id = ? AND archived_at IS NULL",
@@@ -227,11 +227,12 @@
"SELECT COUNT(*) as cnt FROM chat_messages WHERE stream_id = ? AND timestamp >= datetime(?) AND timestamp <= datetime(?) AND is_deleted = 0",
"SELECT COUNT(*) as count FROM channel_sounds WHERE channel_owner_id = ?",
"SELECT COUNT(*) as count FROM channel_sounds WHERE channel_owner_id = ? AND created_by = ?",
++ "SELECT COUNT(*) as count FROM emotes WHERE (channel_owner_id = ?) OR (channel_owner_id IS NULL AND user_id = ?)",
"SELECT DISTINCT room FROM audio_requests WHERE state = 'queued'",
"SELECT MAX(id) AS id FROM chat_messages",
"SELECT MAX(id) AS id FROM room_messages WHERE room_id = ?",
++ "SELECT MAX(id) AS m FROM chat_messages",
"SELECT MIN(timestamp) AS t FROM chat_messages WHERE anon_id = ? AND user_id IS NULL AND is_deleted = 0",
-- "SELECT authority FROM table_authority WHERE table_name = ?",
"SELECT blocker_subject FROM network_blocks WHERE blocked_subject = ? AND active = 1",
"SELECT c.* FROM dm_conversations c JOIN dm_participants p1 ON p1.conversation_id = c.id AND p1.user_id = ? JOIN dm_participants p2 ON p2.conversation_id = c.id AND p2.user_id = ? WHERE c.is_group = 0",
"SELECT c.id, c.name, c.is_group, c.updated_at, (SELECT dm.message FROM dm_messages dm WHERE dm.conversation_id = c.id ORDER BY dm.created_at DESC LIMIT 1) AS last_message, (SELECT dm.sender_id FROM dm_messages dm WHERE dm.conversation_id = c.id ORDER BY dm.created_at DESC LIMIT 1) AS last_sender_id, (SELECT dm.created_at FROM dm_messages dm WHERE dm.conversation_id = c.id ORDER BY dm.created_at DESC LIMIT 1) AS last_message_at, (SELECT COUNT(*) FROM dm_messages dm WHERE dm.conversation_id = c.id AND dm.created_at > p.last_read_at AND dm.sender_id != ?) AS unread_count FROM dm_conversations c JOIN dm_participants p ON p.conversation_id = c.id AND p.user_id = ? ORDER BY COALESCE((SELECT dm.created_at FROM dm_messages dm WHERE dm.conversation_id = c.id ORDER BY dm.created_at DESC LIMIT 1), c.created_at) DESC",
@@@ -246,8 +247,13 @@
"SELECT cm.*, u.display_name, u.username as u_username, u.role, u.avatar_url, u.profile_color FROM chat_messages cm LEFT JOIN ctx_users u ON cm.user_id = u.id WHERE cm.is_deleted = 0 AND (cm.auto_delete_at IS NULL OR datetime(cm.auto_delete_at) > CURRENT_TIMESTAMP)",
"SELECT cm.*, u.display_name, u.username as u_username, u.role, u.avatar_url, u.profile_color FROM chat_messages cm LEFT JOIN ctx_users u ON cm.user_id = u.id WHERE cm.is_deleted = 0 AND (cm.auto_delete_at IS NULL OR datetime(cm.auto_delete_at) > CURRENT_TIMESTAMP) AND cm.user_id = ?",
"SELECT cm.*, u.display_name, u.username as u_username, u.role, u.avatar_url, u.profile_color FROM chat_messages cm LEFT JOIN ctx_users u ON cm.user_id = u.id WHERE cm.is_deleted = 0 AND (cm.auto_delete_at IS NULL OR datetime(cm.auto_delete_at) > CURRENT_TIMESTAMP) AND cm.user_id = ? ORDER BY cm.timestamp DESC LIMIT ? OFFSET ?",
++ "SELECT cm.channel_id, cm.channel_id AS id, c.title, c.user_id AS user_id, c.user_id AS owner_user_id, u.username AS owner_username FROM channel_moderators cm LEFT JOIN ctx_channels c ON cm.channel_id = c.id LEFT JOIN ctx_users u ON c.user_id = u.id WHERE cm.user_id = ? ORDER BY cm.created_at ASC, cm.id ASC",
++ "SELECT cm.id, cm.user_id, cm.added_by, cm.created_at, u.username, u.display_name, u.avatar_url, a.username as added_by_username FROM channel_moderators cm LEFT JOIN ctx_users u ON cm.user_id = u.id LEFT JOIN ctx_users a ON cm.added_by = a.id WHERE cm.channel_id = ? ORDER BY cm.created_at ASC, cm.id ASC",
"SELECT created_at FROM ctx_users WHERE id = ?",
"SELECT created_at FROM room_messages WHERE room_id = ? AND user_id = ? ORDER BY id DESC LIMIT 1",
++ "SELECT e.*, u.username FROM emotes e LEFT JOIN ctx_users u ON e.user_id = u.id WHERE e.id = ?",
++ "SELECT e.*, u.username FROM emotes e LEFT JOIN ctx_users u ON e.user_id = u.id WHERE e.is_global = 1 AND e.is_approved = 1 ORDER BY code",
++ "SELECT e.*, u.username, up.username AS uploader_username, up.display_name AS uploader_display_name FROM emotes e LEFT JOIN ctx_users u ON e.user_id = u.id LEFT JOIN ctx_users up ON e.user_id = up.id WHERE ((e.channel_owner_id = ?) OR (e.channel_owner_id IS NULL AND e.user_id = ?)) AND e.is_approved = 1 ORDER BY code",
"SELECT id FROM bridge_refs WHERE boot = ? AND ref = ?",
"SELECT id FROM calls WHERE channel_id = ? AND state = 'active'",
"SELECT id FROM calls WHERE kind = 'direct' AND channel_id = ? AND state IN ('pending', 'ringing')",
@@@ -260,6 +266,7 @@
"SELECT id FROM ctx_users WHERE subject_id = ?",
"SELECT id FROM ctx_users WHERE subject_id = ? ORDER BY id LIMIT 1",
"SELECT id FROM emotes WHERE id = ?",
++ "SELECT id FROM emotes WHERE user_id = ? AND code = ? AND channel_owner_id IS NULL",
"SELECT id FROM room_messages WHERE room_id = ? AND is_deleted = 0 ORDER BY id",
"SELECT id FROM user_tags WHERE user_id = ? AND tag_id = ?",
"SELECT id, message_type, metadata FROM chat_messages WHERE is_deleted = 0 ORDER BY id DESC LIMIT 1",
@@@ -277,6 +284,7 @@
"SELECT metadata FROM chat_messages WHERE id = ?",
"SELECT ms.*, (SELECT s.is_live FROM ctx_streams s WHERE s.managed_stream_id = ms.id AND s.is_live = 1 LIMIT 1) AS is_currently_live, (SELECT s.id FROM ctx_streams s WHERE s.managed_stream_id = ms.id AND s.is_live = 1 LIMIT 1) AS live_session_id FROM ctx_managed_streams ms WHERE ms.user_id = ? ORDER BY ms.sort_order ASC, ms.created_at ASC",
"SELECT r.*, m.role, m.last_read_id, m.last_seen_at, (SELECT COUNT(*) FROM room_messages x WHERE x.room_id = r.id AND x.id > m.last_read_id AND x.is_deleted = 0 AND x.user_id != ?) AS unread FROM room_members m JOIN rooms r ON r.id = m.room_id WHERE m.user_id = ? AND m.role != 'blocked' AND r.archived_at IS NULL ORDER BY COALESCE(r.last_message_at, r.created_at) DESC",
++ "SELECT r.rowid AS id, r.platform, r.username, r.display_name, r.message_count, r.last_seen, cs.updated_at AS last_update FROM relay_users r LEFT JOIN chat_ai_summaries cs ON cs.scope = 'relay' AND cs.window = 'rolling' AND cs.subject_id = r.rowid WHERE r.last_seen >= ? AND r.message_count >= ? AND (cs.updated_at IS NULL OR cs.updated_at < r.last_seen) ORDER BY (cs.updated_at IS NULL) DESC, r.last_seen DESC LIMIT ?",
"SELECT result FROM bridge_applied WHERE key = ?",
"SELECT role FROM room_members WHERE room_id = ? AND user_id = ?",
"SELECT rowid AS id, * FROM relay_users WHERE platform = ? AND username = ?",
@@@ -287,10 +295,10 @@
"SELECT seq, event_id, event FROM events_outbox WHERE sent_at IS NULL ORDER BY seq LIMIT 100",
"SELECT seq, tbl, op, pk FROM live_mirror_outbox ORDER BY seq LIMIT ?",
"SELECT subject_id FROM ctx_users WHERE id = ?",
-- "SELECT table_name, authority FROM table_authority",
"SELECT u.id FROM network_blocks b JOIN ctx_users u ON u.subject_id = b.blocked_subject WHERE b.blocker_subject = ? AND b.active = 1",
"SELECT u.id, u.username, u.display_name, u.avatar_url, u.profile_color FROM dm_participants p JOIN ctx_users u ON u.id = p.user_id WHERE p.conversation_id = ?",
"SELECT u.id, u.username, u.display_name, u.avatar_url, u.profile_color, b.created_at as blocked_at FROM dm_blocks b JOIN ctx_users u ON u.id = b.blocked_id WHERE b.blocker_id = ? ORDER BY b.created_at DESC",
++ "SELECT user_id FROM channel_moderators WHERE channel_id = ? ORDER BY created_at ASC, id ASC",
"SELECT user_id, subject_id FROM dm_participants WHERE conversation_id = ?",
"SELECT valid_after_ms FROM token_revocations WHERE subject_id = ?",
"SELECT value FROM chat_meta WHERE key = ?",
@@@ -310,6 +318,7 @@
"UPDATE chat_messages SET message = ?, metadata = ? WHERE id = ?",
"UPDATE chat_messages SET metadata = ? WHERE id = ?",
"UPDATE chat_messages SET username = ? WHERE user_id = ?",
++ "UPDATE ctx_channels SET emote_sources = ? WHERE user_id = ?",
"UPDATE ctx_streams SET is_live = 0 WHERE id = ?",
"UPDATE ctx_users SET profile_color = ? WHERE id = ?",
"UPDATE dm_conversations SET is_group = 1 WHERE id = ?",
@@@ -330,7 +339,6 @@
"UPDATE room_messages SET is_deleted = 1, deleted_by = ? WHERE id = ?",
"UPDATE rooms SET message_count = message_count + 1, last_message_at = CURRENT_TIMESTAMP WHERE id = ?",
"UPDATE rooms SET name = ?, topic = ?, visibility = ?, slow_seconds = ?, join_role = ? WHERE id = ?",
-- "UPDATE rooms SET owner_subject = NULL WHERE id = ?",
-- "UPDATE table_authority SET authority = ? WHERE table_name = ?"
++ "UPDATE rooms SET owner_subject = NULL WHERE id = ?"
]
}