@korajs/server 0.1.5 → 0.1.7
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/dist/index.cjs +97 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -1
- package/dist/index.d.ts +48 -1
- package/dist/index.js +96 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -41,6 +41,7 @@ __export(index_exports, {
|
|
|
41
41
|
WsServerTransport: () => WsServerTransport,
|
|
42
42
|
createKoraServer: () => createKoraServer,
|
|
43
43
|
createPostgresServerStore: () => createPostgresServerStore,
|
|
44
|
+
createProductionServer: () => createProductionServer,
|
|
44
45
|
createSqliteServerStore: () => createSqliteServerStore
|
|
45
46
|
});
|
|
46
47
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -1189,6 +1190,101 @@ var TokenAuthProvider = class {
|
|
|
1189
1190
|
function createKoraServer(config) {
|
|
1190
1191
|
return new KoraSyncServer(config);
|
|
1191
1192
|
}
|
|
1193
|
+
|
|
1194
|
+
// src/server/production-server.ts
|
|
1195
|
+
var MIME_TYPES = {
|
|
1196
|
+
".html": "text/html",
|
|
1197
|
+
".js": "text/javascript",
|
|
1198
|
+
".css": "text/css",
|
|
1199
|
+
".json": "application/json",
|
|
1200
|
+
".png": "image/png",
|
|
1201
|
+
".jpg": "image/jpeg",
|
|
1202
|
+
".svg": "image/svg+xml",
|
|
1203
|
+
".ico": "image/x-icon",
|
|
1204
|
+
".wasm": "application/wasm",
|
|
1205
|
+
".woff": "font/woff",
|
|
1206
|
+
".woff2": "font/woff2",
|
|
1207
|
+
".map": "application/json"
|
|
1208
|
+
};
|
|
1209
|
+
function createProductionServer(config) {
|
|
1210
|
+
const port = config.port ?? (Number(process.env.PORT) || 3001);
|
|
1211
|
+
const staticDir = config.staticDir ?? "./dist";
|
|
1212
|
+
const syncPath = config.syncPath ?? "/kora-sync";
|
|
1213
|
+
const syncServer = new KoraSyncServer({
|
|
1214
|
+
store: config.store,
|
|
1215
|
+
...config.syncOptions
|
|
1216
|
+
});
|
|
1217
|
+
let httpServer = null;
|
|
1218
|
+
return {
|
|
1219
|
+
async start() {
|
|
1220
|
+
const { createServer } = await import("http");
|
|
1221
|
+
const { createReadStream, existsSync, statSync } = await import("fs");
|
|
1222
|
+
const { extname, join, resolve } = await import("path");
|
|
1223
|
+
const { WebSocketServer } = await import("ws");
|
|
1224
|
+
const distDir = resolve(staticDir);
|
|
1225
|
+
httpServer = createServer((req, res) => {
|
|
1226
|
+
res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
|
|
1227
|
+
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
|
|
1228
|
+
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
1229
|
+
let filePath = join(distDir, url.pathname);
|
|
1230
|
+
if (!extname(filePath)) {
|
|
1231
|
+
const indexPath = join(filePath, "index.html");
|
|
1232
|
+
if (existsSync(indexPath)) {
|
|
1233
|
+
filePath = indexPath;
|
|
1234
|
+
} else {
|
|
1235
|
+
filePath = join(distDir, "index.html");
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
if (!existsSync(filePath)) {
|
|
1239
|
+
filePath = join(distDir, "index.html");
|
|
1240
|
+
}
|
|
1241
|
+
try {
|
|
1242
|
+
const stat = statSync(filePath);
|
|
1243
|
+
if (stat.isDirectory()) {
|
|
1244
|
+
filePath = join(filePath, "index.html");
|
|
1245
|
+
}
|
|
1246
|
+
} catch {
|
|
1247
|
+
filePath = join(distDir, "index.html");
|
|
1248
|
+
}
|
|
1249
|
+
if (!existsSync(filePath)) {
|
|
1250
|
+
res.writeHead(404);
|
|
1251
|
+
res.end("Not Found");
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
const ext = extname(filePath);
|
|
1255
|
+
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
1256
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
1257
|
+
createReadStream(filePath).pipe(res);
|
|
1258
|
+
});
|
|
1259
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
1260
|
+
httpServer.on("upgrade", (req, socket, head) => {
|
|
1261
|
+
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
1262
|
+
if (url.pathname === syncPath) {
|
|
1263
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
1264
|
+
const transport = new WsServerTransport(ws);
|
|
1265
|
+
syncServer.handleConnection(transport);
|
|
1266
|
+
});
|
|
1267
|
+
} else {
|
|
1268
|
+
socket.destroy();
|
|
1269
|
+
}
|
|
1270
|
+
});
|
|
1271
|
+
return new Promise((resolve2) => {
|
|
1272
|
+
httpServer.listen(port, "0.0.0.0", () => {
|
|
1273
|
+
resolve2(`http://localhost:${port}`);
|
|
1274
|
+
});
|
|
1275
|
+
});
|
|
1276
|
+
},
|
|
1277
|
+
async stop() {
|
|
1278
|
+
await syncServer.stop();
|
|
1279
|
+
if (httpServer) {
|
|
1280
|
+
await new Promise((resolve) => {
|
|
1281
|
+
httpServer.close(() => resolve());
|
|
1282
|
+
});
|
|
1283
|
+
httpServer = null;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1192
1288
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1193
1289
|
0 && (module.exports = {
|
|
1194
1290
|
ClientSession,
|
|
@@ -1202,6 +1298,7 @@ function createKoraServer(config) {
|
|
|
1202
1298
|
WsServerTransport,
|
|
1203
1299
|
createKoraServer,
|
|
1204
1300
|
createPostgresServerStore,
|
|
1301
|
+
createProductionServer,
|
|
1205
1302
|
createSqliteServerStore
|
|
1206
1303
|
});
|
|
1207
1304
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","../src/store/memory-server-store.ts","../src/store/postgres-server-store.ts","../src/store/drizzle-pg-schema.ts","../src/store/sqlite-server-store.ts","../src/store/drizzle-schema.ts","../src/transport/http-server-transport.ts","../src/transport/ws-server-transport.ts","../src/session/client-session.ts","../src/scopes/server-scope-filter.ts","../src/server/kora-sync-server.ts","../src/auth/no-auth.ts","../src/auth/token-auth.ts","../src/server/create-server.ts"],"sourcesContent":["// @korajs/server — public API\n// Every export here is a public API commitment. Be explicit.\n\n// === Types ===\nexport type {\n\tAuthContext,\n\tAuthProvider,\n\tHttpSyncRequest,\n\tHttpSyncResponse,\n\tKoraSyncServerConfig,\n\tServerStatus,\n} from './types'\n\nexport type { ServerStore } from './store/server-store'\n\nexport type {\n\tServerCloseHandler,\n\tServerErrorHandler,\n\tServerMessageHandler,\n\tServerTransport,\n} from './transport/server-transport'\n\nexport type {\n\tHttpPollResponse,\n} from './transport/http-server-transport'\n\nexport type {\n\tWsWebSocket,\n\tWsServerTransportOptions,\n} from './transport/ws-server-transport'\n\nexport type {\n\tClientSessionOptions,\n\tRelayCallback,\n\tSessionState,\n} from './session/client-session'\n\nexport type {\n\tWsServerConstructor,\n\tWsServerLike,\n} from './server/kora-sync-server'\n\nexport type { TokenAuthProviderOptions } from './auth/token-auth'\n\n// === Classes ===\nexport { MemoryServerStore } from './store/memory-server-store'\nexport { PostgresServerStore } from './store/postgres-server-store'\nexport { SqliteServerStore } from './store/sqlite-server-store'\nexport { HttpServerTransport } from './transport/http-server-transport'\nexport { WsServerTransport } from './transport/ws-server-transport'\nexport { ClientSession } from './session/client-session'\nexport { KoraSyncServer } from './server/kora-sync-server'\nexport { NoAuthProvider } from './auth/no-auth'\nexport { TokenAuthProvider } from './auth/token-auth'\n\n// === Factory Functions ===\nexport { createKoraServer } from './server/create-server'\nexport { createPostgresServerStore } from './store/postgres-server-store'\nexport { createSqliteServerStore } from './store/sqlite-server-store'\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","import type { Operation, VersionVector } from '@korajs/core'\nimport { generateUUIDv7 } from '@korajs/core'\nimport type { ApplyResult } from '@korajs/sync'\nimport type { ServerStore } from './server-store'\n\n/**\n * In-memory server store for testing and quick prototyping.\n * Not suitable for production — data does not survive process restart.\n */\nexport class MemoryServerStore implements ServerStore {\n\tprivate readonly nodeId: string\n\tprivate readonly operations: Operation[] = []\n\tprivate readonly operationIndex = new Map<string, Operation>()\n\tprivate readonly versionVector: Map<string, number> = new Map()\n\tprivate closed = false\n\n\tconstructor(nodeId?: string) {\n\t\tthis.nodeId = nodeId ?? generateUUIDv7()\n\t}\n\n\tgetVersionVector(): VersionVector {\n\t\treturn new Map(this.versionVector)\n\t}\n\n\tgetNodeId(): string {\n\t\treturn this.nodeId\n\t}\n\n\tasync applyRemoteOperation(op: Operation): Promise<ApplyResult> {\n\t\tthis.assertOpen()\n\n\t\t// Content-addressed dedup: same id = same content\n\t\tif (this.operationIndex.has(op.id)) {\n\t\t\treturn 'duplicate'\n\t\t}\n\n\t\tthis.operations.push(op)\n\t\tthis.operationIndex.set(op.id, op)\n\n\t\t// Advance version vector\n\t\tconst currentSeq = this.versionVector.get(op.nodeId) ?? 0\n\t\tif (op.sequenceNumber > currentSeq) {\n\t\t\tthis.versionVector.set(op.nodeId, op.sequenceNumber)\n\t\t}\n\n\t\treturn 'applied'\n\t}\n\n\tasync getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]> {\n\t\tthis.assertOpen()\n\n\t\treturn this.operations\n\t\t\t.filter(\n\t\t\t\t(op) => op.nodeId === nodeId && op.sequenceNumber >= fromSeq && op.sequenceNumber <= toSeq,\n\t\t\t)\n\t\t\t.sort((a, b) => a.sequenceNumber - b.sequenceNumber)\n\t}\n\n\tasync getOperationCount(): Promise<number> {\n\t\tthis.assertOpen()\n\t\treturn this.operations.length\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.closed = true\n\t}\n\n\t// --- Testing helpers (not on interface) ---\n\n\t/**\n\t * Get all stored operations (for test assertions).\n\t */\n\tgetAllOperations(): Operation[] {\n\t\treturn [...this.operations]\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('MemoryServerStore is closed')\n\t\t}\n\t}\n}\n","import type { Operation, VersionVector } from '@korajs/core'\nimport { generateUUIDv7 } from '@korajs/core'\nimport type { ApplyResult } from '@korajs/sync'\nimport { and, asc, between, count, eq, sql } from 'drizzle-orm'\nimport type { PostgresJsDatabase } from 'drizzle-orm/postgres-js'\nimport { pgOperations, pgSyncState } from './drizzle-pg-schema'\nimport type { ServerStore } from './server-store'\n\n/**\n * PostgreSQL-backed server store using Drizzle ORM.\n * All reads and writes go through Drizzle's typed query builder.\n * DDL stays as raw SQL via Drizzle's sql template (standard practice without drizzle-kit).\n */\nexport class PostgresServerStore implements ServerStore {\n\tprivate readonly nodeId: string\n\tprivate readonly db: PostgresJsDatabase\n\tprivate readonly versionVector: VersionVector = new Map()\n\tprivate readonly ready: Promise<void>\n\tprivate closed = false\n\n\tconstructor(db: PostgresJsDatabase, nodeId?: string) {\n\t\tthis.db = db\n\t\tthis.nodeId = nodeId ?? generateUUIDv7()\n\t\tthis.ready = this.initialize()\n\t}\n\n\tgetVersionVector(): VersionVector {\n\t\tthis.assertOpen()\n\t\treturn new Map(this.versionVector)\n\t}\n\n\tgetNodeId(): string {\n\t\treturn this.nodeId\n\t}\n\n\tasync applyRemoteOperation(op: Operation): Promise<ApplyResult> {\n\t\tthis.assertOpen()\n\t\tawait this.ready\n\n\t\t// Content-addressed dedup check\n\t\tconst existing = await this.db\n\t\t\t.select({ id: pgOperations.id })\n\t\t\t.from(pgOperations)\n\t\t\t.where(eq(pgOperations.id, op.id))\n\t\t\t.limit(1)\n\n\t\tif (existing.length > 0) {\n\t\t\treturn 'duplicate'\n\t\t}\n\n\t\tconst now = Date.now()\n\t\tconst row = this.serializeOperation(op, now)\n\n\t\tawait this.db.transaction(async (tx) => {\n\t\t\t// Insert operation with dedup\n\t\t\tawait tx\n\t\t\t\t.insert(pgOperations)\n\t\t\t\t.values(row)\n\t\t\t\t.onConflictDoNothing({ target: pgOperations.id })\n\n\t\t\t// Upsert version vector: advance max sequence number with GREATEST\n\t\t\tawait tx\n\t\t\t\t.insert(pgSyncState)\n\t\t\t\t.values({\n\t\t\t\t\tnodeId: op.nodeId,\n\t\t\t\t\tmaxSequenceNumber: op.sequenceNumber,\n\t\t\t\t\tlastSeenAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoUpdate({\n\t\t\t\t\ttarget: pgSyncState.nodeId,\n\t\t\t\t\tset: {\n\t\t\t\t\t\tmaxSequenceNumber: sql`GREATEST(${pgSyncState.maxSequenceNumber}, ${op.sequenceNumber})`,\n\t\t\t\t\t\tlastSeenAt: sql`${now}`,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t})\n\n\t\t// Update in-memory version vector cache\n\t\tconst currentMax = this.versionVector.get(op.nodeId) ?? 0\n\t\tif (op.sequenceNumber > currentMax) {\n\t\t\tthis.versionVector.set(op.nodeId, op.sequenceNumber)\n\t\t}\n\n\t\treturn 'applied'\n\t}\n\n\tasync getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]> {\n\t\tthis.assertOpen()\n\t\tawait this.ready\n\n\t\tconst rows = await this.db\n\t\t\t.select()\n\t\t\t.from(pgOperations)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(pgOperations.nodeId, nodeId),\n\t\t\t\t\tbetween(pgOperations.sequenceNumber, fromSeq, toSeq),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.orderBy(asc(pgOperations.sequenceNumber))\n\n\t\treturn rows.map((row) => this.deserializeOperation(row))\n\t}\n\n\tasync getOperationCount(): Promise<number> {\n\t\tthis.assertOpen()\n\t\tawait this.ready\n\n\t\tconst result = await this.db.select({ value: count() }).from(pgOperations)\n\t\treturn result[0]?.value ?? 0\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.closed = true\n\t}\n\n\tprivate async initialize(): Promise<void> {\n\t\tawait this.ensureTables()\n\n\t\t// Hydrate in-memory version vector cache via Drizzle query\n\t\tconst rows = await this.db\n\t\t\t.select({\n\t\t\t\tnodeId: pgSyncState.nodeId,\n\t\t\t\tmaxSequenceNumber: pgSyncState.maxSequenceNumber,\n\t\t\t})\n\t\t\t.from(pgSyncState)\n\n\t\tfor (const row of rows) {\n\t\t\tthis.versionVector.set(row.nodeId, row.maxSequenceNumber)\n\t\t}\n\t}\n\n\t/**\n\t * Create tables if they don't exist.\n\t * Uses raw SQL via Drizzle's sql template — standard DDL practice without drizzle-kit.\n\t */\n\tprivate async ensureTables(): Promise<void> {\n\t\tawait this.db.execute(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS operations (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tnode_id TEXT NOT NULL,\n\t\t\t\ttype TEXT NOT NULL,\n\t\t\t\tcollection TEXT NOT NULL,\n\t\t\t\trecord_id TEXT NOT NULL,\n\t\t\t\tdata TEXT,\n\t\t\t\tprevious_data TEXT,\n\t\t\t\twall_time INTEGER NOT NULL,\n\t\t\t\tlogical INTEGER NOT NULL,\n\t\t\t\ttimestamp_node_id TEXT NOT NULL,\n\t\t\t\tsequence_number INTEGER NOT NULL,\n\t\t\t\tcausal_deps TEXT NOT NULL DEFAULT '[]',\n\t\t\t\tschema_version INTEGER NOT NULL,\n\t\t\t\treceived_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\n\t\tawait this.db.execute(\n\t\t\tsql`CREATE INDEX IF NOT EXISTS idx_node_seq ON operations (node_id, sequence_number)`,\n\t\t)\n\t\tawait this.db.execute(\n\t\t\tsql`CREATE INDEX IF NOT EXISTS idx_collection ON operations (collection)`,\n\t\t)\n\t\tawait this.db.execute(\n\t\t\tsql`CREATE INDEX IF NOT EXISTS idx_received ON operations (received_at)`,\n\t\t)\n\n\t\tawait this.db.execute(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS sync_state (\n\t\t\t\tnode_id TEXT PRIMARY KEY,\n\t\t\t\tmax_sequence_number INTEGER NOT NULL,\n\t\t\t\tlast_seen_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\t}\n\n\tprivate serializeOperation(\n\t\top: Operation,\n\t\treceivedAt: number,\n\t): typeof pgOperations.$inferInsert {\n\t\treturn {\n\t\t\tid: op.id,\n\t\t\tnodeId: op.nodeId,\n\t\t\ttype: op.type,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tdata: op.data !== null ? JSON.stringify(op.data) : null,\n\t\t\tpreviousData: op.previousData !== null ? JSON.stringify(op.previousData) : null,\n\t\t\twallTime: op.timestamp.wallTime,\n\t\t\tlogical: op.timestamp.logical,\n\t\t\ttimestampNodeId: op.timestamp.nodeId,\n\t\t\tsequenceNumber: op.sequenceNumber,\n\t\t\tcausalDeps: JSON.stringify(op.causalDeps),\n\t\t\tschemaVersion: op.schemaVersion,\n\t\t\treceivedAt,\n\t\t}\n\t}\n\n\tprivate deserializeOperation(row: typeof pgOperations.$inferSelect): Operation {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tnodeId: row.nodeId,\n\t\t\ttype: row.type as Operation['type'],\n\t\t\tcollection: row.collection,\n\t\t\trecordId: row.recordId,\n\t\t\tdata: row.data !== null ? JSON.parse(row.data) : null,\n\t\t\tpreviousData: row.previousData !== null ? JSON.parse(row.previousData) : null,\n\t\t\ttimestamp: {\n\t\t\t\twallTime: row.wallTime,\n\t\t\t\tlogical: row.logical,\n\t\t\t\tnodeId: row.timestampNodeId,\n\t\t\t},\n\t\t\tsequenceNumber: row.sequenceNumber,\n\t\t\tcausalDeps: JSON.parse(row.causalDeps),\n\t\t\tschemaVersion: row.schemaVersion,\n\t\t}\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('PostgresServerStore is closed')\n\t\t}\n\t}\n}\n\n/**\n * Creates a PostgresServerStore from a PostgreSQL connection string.\n *\n * Uses runtime dynamic imports so projects that do not use PostgreSQL\n * do not need to install `postgres`. Wraps the postgres client with\n * Drizzle ORM for typed query building.\n */\nexport async function createPostgresServerStore(options: {\n\tconnectionString: string\n\tnodeId?: string\n}): Promise<PostgresServerStore> {\n\tconst { postgresClient, drizzleFn } = await loadPostgresDeps()\n\tconst client = postgresClient(options.connectionString)\n\tconst db = drizzleFn(client)\n\n\treturn new PostgresServerStore(db, options.nodeId)\n}\n\nasync function loadPostgresDeps(): Promise<{\n\tpostgresClient: (connectionString: string) => unknown\n\tdrizzleFn: (client: unknown) => PostgresJsDatabase\n}> {\n\ttry {\n\t\tconst dynamicImport = new Function('specifier', 'return import(specifier)') as (\n\t\t\tspecifier: string,\n\t\t) => Promise<unknown>\n\n\t\tconst postgresMod = (await dynamicImport('postgres')) as { default: (cs: string) => unknown }\n\t\tconst drizzleMod = (await dynamicImport('drizzle-orm/postgres-js')) as {\n\t\t\tdrizzle: (client: unknown) => PostgresJsDatabase\n\t\t}\n\n\t\treturn {\n\t\t\tpostgresClient: postgresMod.default,\n\t\t\tdrizzleFn: drizzleMod.drizzle,\n\t\t}\n\t} catch {\n\t\tthrow new Error(\n\t\t\t'PostgreSQL backend requires the \"postgres\" package. Install it in your project dependencies.',\n\t\t)\n\t}\n}\n","import { index, integer, pgTable, text } from 'drizzle-orm/pg-core'\n\n/**\n * Drizzle schema for the Kora sync server's PostgreSQL database.\n *\n * Two tables:\n * - `pgOperations` — the append-only operation log (content-addressed by id)\n * - `pgSyncState` — tracks the max sequence number seen per node (version vector)\n *\n * Column structure mirrors the SQLite drizzle-schema.ts but uses pgTable.\n */\n\nexport const pgOperations = pgTable(\n\t'operations',\n\t{\n\t\tid: text('id').primaryKey(),\n\t\tnodeId: text('node_id').notNull(),\n\t\ttype: text('type').notNull(),\n\t\tcollection: text('collection').notNull(),\n\t\trecordId: text('record_id').notNull(),\n\t\tdata: text('data'), // JSON-serialized, null for deletes\n\t\tpreviousData: text('previous_data'), // JSON-serialized, null for insert/delete\n\t\twallTime: integer('wall_time').notNull(),\n\t\tlogical: integer('logical').notNull(),\n\t\ttimestampNodeId: text('timestamp_node_id').notNull(),\n\t\tsequenceNumber: integer('sequence_number').notNull(),\n\t\tcausalDeps: text('causal_deps').notNull().default('[]'), // JSON array of op IDs\n\t\tschemaVersion: integer('schema_version').notNull(),\n\t\treceivedAt: integer('received_at').notNull(),\n\t},\n\t(table) => ({\n\t\tnodeSeqIdx: index('idx_pg_node_seq').on(table.nodeId, table.sequenceNumber),\n\t\tcollectionIdx: index('idx_pg_collection').on(table.collection),\n\t\treceivedIdx: index('idx_pg_received').on(table.receivedAt),\n\t}),\n)\n\nexport const pgSyncState = pgTable('sync_state', {\n\tnodeId: text('node_id').primaryKey(),\n\tmaxSequenceNumber: integer('max_sequence_number').notNull(),\n\tlastSeenAt: integer('last_seen_at').notNull(),\n})\n","import { createRequire } from 'node:module'\nimport type { Operation, VersionVector } from '@korajs/core'\nimport { generateUUIDv7 } from '@korajs/core'\nimport type { ApplyResult } from '@korajs/sync'\nimport { and, asc, between, count, eq, sql } from 'drizzle-orm'\nimport type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'\nimport { operations, syncState } from './drizzle-schema'\nimport type { ServerStore } from './server-store'\n\n// better-sqlite3 is a native CJS addon that cannot be loaded via ESM import().\n// createRequire provides a CJS require() that works in both ESM and CJS contexts.\n// tsup's shims option ensures import.meta.url is available in CJS builds.\nconst esmRequire = createRequire(import.meta.url)\n\n/**\n * SQLite-backed server store using Drizzle ORM.\n * Persists operations and version vectors to a real database file,\n * surviving process restarts.\n */\nexport class SqliteServerStore implements ServerStore {\n\tprivate readonly nodeId: string\n\tprivate readonly db: BetterSQLite3Database\n\tprivate closed = false\n\n\tconstructor(db: BetterSQLite3Database, nodeId?: string) {\n\t\tthis.db = db\n\t\tthis.nodeId = nodeId ?? generateUUIDv7()\n\t\tthis.ensureTables()\n\t}\n\n\tgetVersionVector(): VersionVector {\n\t\tthis.assertOpen()\n\t\tconst rows = this.db.select().from(syncState).all()\n\t\tconst vv: VersionVector = new Map()\n\t\tfor (const row of rows) {\n\t\t\tvv.set(row.nodeId, row.maxSequenceNumber)\n\t\t}\n\t\treturn vv\n\t}\n\n\tgetNodeId(): string {\n\t\treturn this.nodeId\n\t}\n\n\tasync applyRemoteOperation(op: Operation): Promise<ApplyResult> {\n\t\tthis.assertOpen()\n\n\t\tconst now = Date.now()\n\t\tconst row = this.serializeOperation(op, now)\n\n\t\t// Use a transaction for atomicity: insert op + update version vector\n\t\tconst result = this.db.transaction((tx) => {\n\t\t\t// Content-addressed dedup via onConflictDoNothing\n\t\t\tconst insertResult = tx\n\t\t\t\t.insert(operations)\n\t\t\t\t.values(row)\n\t\t\t\t.onConflictDoNothing({ target: operations.id })\n\t\t\t\t.run()\n\n\t\t\tif (insertResult.changes === 0) {\n\t\t\t\treturn 'duplicate' as const\n\t\t\t}\n\n\t\t\t// Advance version vector: upsert with MAX to ensure monotonic progress\n\t\t\ttx.insert(syncState)\n\t\t\t\t.values({\n\t\t\t\t\tnodeId: op.nodeId,\n\t\t\t\t\tmaxSequenceNumber: op.sequenceNumber,\n\t\t\t\t\tlastSeenAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoUpdate({\n\t\t\t\t\ttarget: syncState.nodeId,\n\t\t\t\t\tset: {\n\t\t\t\t\t\tmaxSequenceNumber: sql`MAX(${syncState.maxSequenceNumber}, ${op.sequenceNumber})`,\n\t\t\t\t\t\tlastSeenAt: sql`${now}`,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\t.run()\n\n\t\t\treturn 'applied' as const\n\t\t})\n\n\t\treturn result\n\t}\n\n\tasync getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]> {\n\t\tthis.assertOpen()\n\n\t\tconst rows = this.db\n\t\t\t.select()\n\t\t\t.from(operations)\n\t\t\t.where(and(eq(operations.nodeId, nodeId), between(operations.sequenceNumber, fromSeq, toSeq)))\n\t\t\t.orderBy(asc(operations.sequenceNumber))\n\t\t\t.all()\n\n\t\treturn rows.map((row) => this.deserializeOperation(row))\n\t}\n\n\tasync getOperationCount(): Promise<number> {\n\t\tthis.assertOpen()\n\n\t\tconst result = this.db.select({ value: count() }).from(operations).all()\n\t\treturn result[0]?.value ?? 0\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.closed = true\n\t}\n\n\t/**\n\t * Create the operations and sync_state tables if they don't exist.\n\t * Uses raw SQL via Drizzle's sql template — standard practice for DDL without drizzle-kit.\n\t */\n\tprivate ensureTables(): void {\n\t\tthis.db.run(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS operations (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tnode_id TEXT NOT NULL,\n\t\t\t\ttype TEXT NOT NULL,\n\t\t\t\tcollection TEXT NOT NULL,\n\t\t\t\trecord_id TEXT NOT NULL,\n\t\t\t\tdata TEXT,\n\t\t\t\tprevious_data TEXT,\n\t\t\t\twall_time INTEGER NOT NULL,\n\t\t\t\tlogical INTEGER NOT NULL,\n\t\t\t\ttimestamp_node_id TEXT NOT NULL,\n\t\t\t\tsequence_number INTEGER NOT NULL,\n\t\t\t\tcausal_deps TEXT NOT NULL DEFAULT '[]',\n\t\t\t\tschema_version INTEGER NOT NULL,\n\t\t\t\treceived_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE INDEX IF NOT EXISTS idx_node_seq ON operations (node_id, sequence_number)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE INDEX IF NOT EXISTS idx_collection ON operations (collection)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE INDEX IF NOT EXISTS idx_received ON operations (received_at)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS sync_state (\n\t\t\t\tnode_id TEXT PRIMARY KEY,\n\t\t\t\tmax_sequence_number INTEGER NOT NULL,\n\t\t\t\tlast_seen_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\t}\n\n\tprivate serializeOperation(\n\t\top: Operation,\n\t\treceivedAt: number,\n\t): typeof operations.$inferInsert {\n\t\treturn {\n\t\t\tid: op.id,\n\t\t\tnodeId: op.nodeId,\n\t\t\ttype: op.type,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tdata: op.data !== null ? JSON.stringify(op.data) : null,\n\t\t\tpreviousData: op.previousData !== null ? JSON.stringify(op.previousData) : null,\n\t\t\twallTime: op.timestamp.wallTime,\n\t\t\tlogical: op.timestamp.logical,\n\t\t\ttimestampNodeId: op.timestamp.nodeId,\n\t\t\tsequenceNumber: op.sequenceNumber,\n\t\t\tcausalDeps: JSON.stringify(op.causalDeps),\n\t\t\tschemaVersion: op.schemaVersion,\n\t\t\treceivedAt,\n\t\t}\n\t}\n\n\tprivate deserializeOperation(row: typeof operations.$inferSelect): Operation {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tnodeId: row.nodeId,\n\t\t\ttype: row.type as Operation['type'],\n\t\t\tcollection: row.collection,\n\t\t\trecordId: row.recordId,\n\t\t\tdata: row.data !== null ? JSON.parse(row.data) : null,\n\t\t\tpreviousData: row.previousData !== null ? JSON.parse(row.previousData) : null,\n\t\t\ttimestamp: {\n\t\t\t\twallTime: row.wallTime,\n\t\t\t\tlogical: row.logical,\n\t\t\t\tnodeId: row.timestampNodeId,\n\t\t\t},\n\t\t\tsequenceNumber: row.sequenceNumber,\n\t\t\tcausalDeps: JSON.parse(row.causalDeps),\n\t\t\tschemaVersion: row.schemaVersion,\n\t\t}\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('SqliteServerStore is closed')\n\t\t}\n\t}\n}\n\n/**\n * Creates a SqliteServerStore with a file-backed or in-memory database.\n * Handles database creation, Drizzle wrapping, and table setup.\n *\n * @param options - Configuration options\n * @param options.filename - Path to SQLite database file. Defaults to ':memory:' for testing.\n * @param options.nodeId - Server node ID. Auto-generated if not provided.\n * @returns A ready-to-use SqliteServerStore\n *\n * @example\n * ```typescript\n * import { createSqliteServerStore } from '@korajs/server'\n *\n * const store = createSqliteServerStore({ filename: './kora-server.db' })\n * const server = createKoraServer({ store, port: 3001 })\n * ```\n */\nexport function createSqliteServerStore(options: {\n\tfilename?: string\n\tnodeId?: string\n}): SqliteServerStore {\n\t// better-sqlite3 is a native CJS addon — use esmRequire (from createRequire)\n\t// so this works in both ESM and CJS contexts.\n\tconst Database = esmRequire('better-sqlite3')\n\tconst { drizzle } = esmRequire('drizzle-orm/better-sqlite3')\n\n\tconst filename = options.filename ?? ':memory:'\n\tconst sqlite = new Database(filename)\n\n\t// Enable WAL mode for better concurrent read/write performance\n\tsqlite.pragma('journal_mode = WAL')\n\n\tconst db = drizzle(sqlite)\n\treturn new SqliteServerStore(db, options.nodeId)\n}\n","import { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'\n\n/**\n * Drizzle schema for the Kora sync server's SQLite database.\n *\n * Two tables:\n * - `operations` — the append-only operation log (content-addressed by id)\n * - `syncState` — tracks the max sequence number seen per node (version vector)\n */\n\nexport const operations = sqliteTable(\n\t'operations',\n\t{\n\t\tid: text('id').primaryKey(),\n\t\tnodeId: text('node_id').notNull(),\n\t\ttype: text('type').notNull(),\n\t\tcollection: text('collection').notNull(),\n\t\trecordId: text('record_id').notNull(),\n\t\tdata: text('data'), // JSON-serialized, null for deletes\n\t\tpreviousData: text('previous_data'), // JSON-serialized, null for insert/delete\n\t\twallTime: integer('wall_time').notNull(),\n\t\tlogical: integer('logical').notNull(),\n\t\ttimestampNodeId: text('timestamp_node_id').notNull(),\n\t\tsequenceNumber: integer('sequence_number').notNull(),\n\t\tcausalDeps: text('causal_deps').notNull().default('[]'), // JSON array of op IDs\n\t\tschemaVersion: integer('schema_version').notNull(),\n\t\treceivedAt: integer('received_at').notNull(),\n\t},\n\t(table) => ({\n\t\tnodeSeqIdx: index('idx_node_seq').on(table.nodeId, table.sequenceNumber),\n\t\tcollectionIdx: index('idx_collection').on(table.collection),\n\t\treceivedIdx: index('idx_received').on(table.receivedAt),\n\t}),\n)\n\nexport const syncState = sqliteTable('sync_state', {\n\tnodeId: text('node_id').primaryKey(),\n\tmaxSequenceNumber: integer('max_sequence_number').notNull(),\n\tlastSeenAt: integer('last_seen_at').notNull(),\n})\n","import { SyncError } from '@korajs/core'\nimport type { MessageSerializer } from '@korajs/sync'\nimport type {\n\tServerCloseHandler,\n\tServerErrorHandler,\n\tServerMessageHandler,\n\tServerTransport,\n} from './server-transport'\n\nexport interface HttpPollResponse {\n\tstatus: 200 | 204 | 304 | 410\n\tbody?: string | Uint8Array\n\theaders?: Record<string, string>\n}\n\ninterface QueuedMessage {\n\tetag: string\n\tcontentType: string\n\tpayload: string | Uint8Array\n}\n\n/**\n * Server-side transport for HTTP long-polling clients.\n *\n * Incoming client messages are pushed via POST, while outbound server\n * messages are pulled via GET long-poll requests.\n */\nexport class HttpServerTransport implements ServerTransport {\n\tprivate readonly serializer: MessageSerializer\n\n\tprivate messageHandler: ServerMessageHandler | null = null\n\tprivate closeHandler: ServerCloseHandler | null = null\n\tprivate errorHandler: ServerErrorHandler | null = null\n\n\tprivate connected = true\n\tprivate nextSequence = 1\n\tprivate readonly queue: QueuedMessage[] = []\n\n\tconstructor(serializer: MessageSerializer) {\n\t\tthis.serializer = serializer\n\t}\n\n\tsend(message: import('@korajs/sync').SyncMessage): void {\n\t\tif (!this.connected) return\n\n\t\tconst encoded = this.serializer.encode(message)\n\t\tconst isBinary = encoded instanceof Uint8Array\n\t\tthis.queue.push({\n\t\t\tetag: this.makeEtag(this.nextSequence++),\n\t\t\tcontentType: isBinary ? 'application/x-protobuf' : 'application/json',\n\t\t\tpayload: encoded,\n\t\t})\n\t}\n\n\tonMessage(handler: ServerMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t}\n\n\tonClose(handler: ServerCloseHandler): void {\n\t\tthis.closeHandler = handler\n\t}\n\n\tonError(handler: ServerErrorHandler): void {\n\t\tthis.errorHandler = handler\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.connected\n\t}\n\n\tclose(code = 1000, reason = 'transport closed'): void {\n\t\tif (!this.connected) return\n\t\tthis.connected = false\n\t\tthis.queue.length = 0\n\t\tthis.closeHandler?.(code, reason)\n\t}\n\n\treceive(payload: string | Uint8Array): void {\n\t\tif (!this.connected) {\n\t\t\tthrow new SyncError('HTTP server transport is closed')\n\t\t}\n\n\t\ttry {\n\t\t\tconst message = this.serializer.decode(payload)\n\t\t\tthis.messageHandler?.(message)\n\t\t} catch (error) {\n\t\t\tthis.errorHandler?.(error instanceof Error ? error : new Error(String(error)))\n\t\t}\n\t}\n\n\tpoll(ifNoneMatch?: string): HttpPollResponse {\n\t\tif (!this.connected) {\n\t\t\treturn { status: 410 }\n\t\t}\n\n\t\tconst next = this.queue[0]\n\t\tif (!next) {\n\t\t\treturn { status: 204 }\n\t\t}\n\n\t\tif (ifNoneMatch && ifNoneMatch === next.etag) {\n\t\t\treturn {\n\t\t\t\tstatus: 304,\n\t\t\t\theaders: { etag: next.etag },\n\t\t\t}\n\t\t}\n\n\t\tthis.queue.shift()\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\tbody: next.payload,\n\t\t\theaders: {\n\t\t\t\t'content-type': next.contentType,\n\t\t\t\tetag: next.etag,\n\t\t\t},\n\t\t}\n\t}\n\n\tprivate makeEtag(sequence: number): string {\n\t\treturn `W/\"${sequence}\"`\n\t}\n}\n","import { SyncError } from '@korajs/core'\nimport type { SyncMessage } from '@korajs/sync'\nimport { JsonMessageSerializer } from '@korajs/sync'\nimport type { MessageSerializer } from '@korajs/sync'\nimport type {\n\tServerCloseHandler,\n\tServerErrorHandler,\n\tServerMessageHandler,\n\tServerTransport,\n} from './server-transport'\n\n/** WebSocket ready states (mirrors ws constants) */\nconst WS_OPEN = 1\n\n/**\n * Minimal interface for a ws.WebSocket instance.\n * Allows dependency injection for testing without importing ws directly.\n */\nexport interface WsWebSocket {\n\treadyState: number\n\tsend(data: string | Uint8Array, callback?: (err?: Error) => void): void\n\tclose(code?: number, reason?: string): void\n\ton(event: string, listener: (...args: unknown[]) => void): void\n\tremoveAllListeners(): void\n}\n\n/**\n * Options for WsServerTransport.\n */\nexport interface WsServerTransportOptions {\n\t/** Message serializer. Defaults to JsonMessageSerializer. */\n\tserializer?: MessageSerializer\n}\n\n/**\n * Server-side transport wrapping a ws.WebSocket connection.\n * Created for each incoming client connection.\n */\nexport class WsServerTransport implements ServerTransport {\n\tprivate readonly ws: WsWebSocket\n\tprivate readonly serializer: MessageSerializer\n\tprivate messageHandler: ServerMessageHandler | null = null\n\tprivate closeHandler: ServerCloseHandler | null = null\n\tprivate errorHandler: ServerErrorHandler | null = null\n\n\tconstructor(ws: WsWebSocket, options?: WsServerTransportOptions) {\n\t\tthis.ws = ws\n\t\tthis.serializer = options?.serializer ?? new JsonMessageSerializer()\n\t\tthis.setupListeners()\n\t}\n\n\tsend(message: SyncMessage): void {\n\t\tif (this.ws.readyState !== WS_OPEN) {\n\t\t\tthrow new SyncError('Cannot send message: WebSocket is not open', {\n\t\t\t\treadyState: this.ws.readyState,\n\t\t\t\tmessageType: message.type,\n\t\t\t})\n\t\t}\n\n\t\tconst encoded = this.serializer.encode(message)\n\t\tthis.ws.send(encoded)\n\t}\n\n\tonMessage(handler: ServerMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t}\n\n\tonClose(handler: ServerCloseHandler): void {\n\t\tthis.closeHandler = handler\n\t}\n\n\tonError(handler: ServerErrorHandler): void {\n\t\tthis.errorHandler = handler\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.ws.readyState === WS_OPEN\n\t}\n\n\tclose(code?: number, reason?: string): void {\n\t\tthis.ws.close(code ?? 1000, reason ?? 'server closing')\n\t}\n\n\tprivate setupListeners(): void {\n\t\tthis.ws.on('message', (data: unknown) => {\n\t\t\ttry {\n\t\t\t\tif (\n\t\t\t\t\ttypeof data !== 'string' &&\n\t\t\t\t\t!(data instanceof Uint8Array) &&\n\t\t\t\t\t!(data instanceof ArrayBuffer)\n\t\t\t\t) {\n\t\t\t\t\tthrow new SyncError('Unsupported WebSocket payload type', {\n\t\t\t\t\t\tpayloadType: typeof data,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tconst decoded = this.serializer.decode(data)\n\t\t\t\tthis.messageHandler?.(decoded)\n\t\t\t} catch (err) {\n\t\t\t\tthis.errorHandler?.(err instanceof Error ? err : new Error(String(err)))\n\t\t\t}\n\t\t})\n\n\t\tthis.ws.on('close', (code: unknown, reason: unknown) => {\n\t\t\tthis.closeHandler?.(Number(code) || 1006, String(reason || 'connection closed'))\n\t\t})\n\n\t\tthis.ws.on('error', (err: unknown) => {\n\t\t\tthis.errorHandler?.(err instanceof Error ? err : new Error(String(err)))\n\t\t})\n\t}\n}\n","import type { KoraEventEmitter, Operation } from '@korajs/core'\nimport { SyncError, generateUUIDv7 } from '@korajs/core'\nimport { topologicalSort } from '@korajs/core/internal'\nimport type {\n\tHandshakeMessage,\n\tMessageSerializer,\n\tOperationBatchMessage,\n\tSyncMessage,\n\tWireFormat,\n} from '@korajs/sync'\nimport {\n\tNegotiatedMessageSerializer,\n\tversionVectorToWire,\n\twireToVersionVector,\n} from '@korajs/sync'\nimport type { ServerStore } from '../store/server-store'\nimport { operationMatchesScopes } from '../scopes/server-scope-filter'\nimport type { ServerTransport } from '../transport/server-transport'\nimport type { AuthContext, AuthProvider } from '../types'\n\nconst DEFAULT_BATCH_SIZE = 100\nconst DEFAULT_SCHEMA_VERSION = 1\n\n/**\n * Possible states for a client session.\n */\nexport type SessionState = 'connected' | 'authenticated' | 'syncing' | 'streaming' | 'closed'\n\n/**\n * Callback invoked when a session has new operations to relay to other sessions.\n */\nexport type RelayCallback = (sourceSessionId: string, operations: Operation[]) => void\n\n/**\n * Options for creating a ClientSession.\n */\nexport interface ClientSessionOptions {\n\t/** Unique session identifier */\n\tsessionId: string\n\t/** Transport for this client connection */\n\ttransport: ServerTransport\n\t/** Server-side operation store */\n\tstore: ServerStore\n\t/** Authentication provider (optional) */\n\tauth?: AuthProvider\n\t/** Message serializer */\n\tserializer?: MessageSerializer\n\t/** Event emitter for DevTools integration */\n\temitter?: KoraEventEmitter\n\t/** Max operations per sync batch */\n\tbatchSize?: number\n\t/** Schema version the server expects */\n\tschemaVersion?: number\n\t/** Called when this session has operations to relay to other sessions */\n\tonRelay?: RelayCallback\n\t/** Called when this session closes */\n\tonClose?: (sessionId: string) => void\n}\n\n/**\n * Handles the sync protocol for a single connected client.\n *\n * Lifecycle: connected → (authenticated) → syncing → streaming → closed\n *\n * The session:\n * 1. Receives a handshake from the client\n * 2. Authenticates if an AuthProvider is configured\n * 3. Sends back a HandshakeResponse with the server's version vector\n * 4. Computes and sends the server's delta to the client (paginated)\n * 5. Processes incoming operation batches from the client\n * 6. Transitions to streaming for real-time bidirectional sync\n * 7. Relays new operations to other sessions via the RelayCallback\n */\nexport class ClientSession {\n\tprivate state: SessionState = 'connected'\n\tprivate clientNodeId: string | null = null\n\tprivate authContext: AuthContext | null = null\n\n\tprivate readonly sessionId: string\n\tprivate readonly transport: ServerTransport\n\tprivate readonly store: ServerStore\n\tprivate readonly auth: AuthProvider | null\n\tprivate readonly serializer: MessageSerializer\n\tprivate readonly emitter: KoraEventEmitter | null\n\tprivate readonly batchSize: number\n\tprivate readonly schemaVersion: number\n\tprivate readonly onRelay: RelayCallback | null\n\tprivate readonly onClose: ((sessionId: string) => void) | null\n\n\tconstructor(options: ClientSessionOptions) {\n\t\tthis.sessionId = options.sessionId\n\t\tthis.transport = options.transport\n\t\tthis.store = options.store\n\t\tthis.auth = options.auth ?? null\n\t\tthis.serializer = options.serializer ?? new NegotiatedMessageSerializer('json')\n\t\tthis.emitter = options.emitter ?? null\n\t\tthis.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE\n\t\tthis.schemaVersion = options.schemaVersion ?? DEFAULT_SCHEMA_VERSION\n\t\tthis.onRelay = options.onRelay ?? null\n\t\tthis.onClose = options.onClose ?? null\n\t}\n\n\t/**\n\t * Start handling messages from the client transport.\n\t */\n\tstart(): void {\n\t\tthis.transport.onMessage((msg) => this.handleMessage(msg))\n\t\tthis.transport.onClose((_code, _reason) => this.handleTransportClose())\n\t\tthis.transport.onError((_err) => {\n\t\t\t// Transport errors during active session cause close\n\t\t\tif (this.state !== 'closed') {\n\t\t\t\tthis.handleTransportClose()\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Relay operations from another session to this client.\n\t * Only relays if the session is in streaming state and transport is connected.\n\t */\n\trelayOperations(operations: Operation[]): void {\n\t\tif (this.state !== 'streaming' || !this.transport.isConnected()) return\n\t\tif (operations.length === 0) return\n\n\t\tconst visibleOperations = operations.filter((op) =>\n\t\t\toperationMatchesScopes(op, this.authContext?.scopes),\n\t\t)\n\t\tif (visibleOperations.length === 0) return\n\n\t\tconst serializedOps = visibleOperations.map((op) => this.serializer.encodeOperation(op))\n\t\tconst msg: SyncMessage = {\n\t\t\ttype: 'operation-batch',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\toperations: serializedOps,\n\t\t\tisFinal: true,\n\t\t\tbatchIndex: 0,\n\t\t}\n\t\tthis.transport.send(msg)\n\t}\n\n\t/**\n\t * Close this session.\n\t */\n\tclose(reason?: string): void {\n\t\tif (this.state === 'closed') return\n\t\tthis.state = 'closed'\n\n\t\tif (this.transport.isConnected()) {\n\t\t\tthis.transport.close(1000, reason ?? 'session closed')\n\t\t}\n\n\t\tthis.onClose?.(this.sessionId)\n\t}\n\n\t// --- Getters ---\n\n\tgetState(): SessionState {\n\t\treturn this.state\n\t}\n\n\tgetSessionId(): string {\n\t\treturn this.sessionId\n\t}\n\n\tgetClientNodeId(): string | null {\n\t\treturn this.clientNodeId\n\t}\n\n\tgetAuthContext(): AuthContext | null {\n\t\treturn this.authContext\n\t}\n\n\tisStreaming(): boolean {\n\t\treturn this.state === 'streaming'\n\t}\n\n\t// --- Private protocol handlers ---\n\n\tprivate handleMessage(message: SyncMessage): void {\n\t\tswitch (message.type) {\n\t\t\tcase 'handshake':\n\t\t\t\tthis.handleHandshake(message)\n\t\t\t\tbreak\n\t\t\tcase 'operation-batch':\n\t\t\t\tthis.handleOperationBatch(message)\n\t\t\t\tbreak\n\t\t\t// Acknowledgments from clients are noted but no action needed on server\n\t\t\tcase 'acknowledgment':\n\t\t\t\tbreak\n\t\t\tcase 'error':\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\tprivate async handleHandshake(msg: HandshakeMessage): Promise<void> {\n\t\t// Only accept handshake in 'connected' state (prevent duplicate handshakes)\n\t\tif (this.state !== 'connected') {\n\t\t\tthis.sendError('DUPLICATE_HANDSHAKE', 'Handshake already completed', false)\n\t\t\treturn\n\t\t}\n\n\t\tthis.clientNodeId = msg.nodeId\n\n\t\t// Authenticate if provider is configured\n\t\tif (this.auth) {\n\t\t\tconst token = msg.authToken ?? ''\n\t\t\tconst context = await this.auth.authenticate(token)\n\t\t\tif (!context) {\n\t\t\t\tthis.sendError('AUTH_FAILED', 'Authentication failed', false)\n\t\t\t\tthis.close('authentication failed')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.authContext = context\n\t\t\tthis.state = 'authenticated'\n\t\t}\n\n\t\t// Send handshake response with server's version vector\n\t\tconst serverVector = this.store.getVersionVector()\n\t\tconst selectedWireFormat = selectWireFormat(msg.supportedWireFormats)\n\t\tthis.setSerializerWireFormat(selectedWireFormat)\n\t\tconst response: SyncMessage = {\n\t\t\ttype: 'handshake-response',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\tnodeId: this.store.getNodeId(),\n\t\t\tversionVector: versionVectorToWire(serverVector),\n\t\t\tschemaVersion: this.schemaVersion,\n\t\t\taccepted: true,\n\t\t\tselectedWireFormat,\n\t\t}\n\t\tthis.transport.send(response)\n\n\t\tthis.emitter?.emit({ type: 'sync:connected', nodeId: msg.nodeId })\n\n\t\t// Transition to syncing and send delta\n\t\tthis.state = 'syncing'\n\t\tconst clientVector = wireToVersionVector(msg.versionVector)\n\t\tawait this.sendDelta(clientVector)\n\n\t\t// Transition to streaming after delta is sent\n\t\tthis.state = 'streaming'\n\t}\n\n\tprivate async handleOperationBatch(msg: OperationBatchMessage): Promise<void> {\n\t\tconst operations = msg.operations.map((s) => this.serializer.decodeOperation(s))\n\t\tconst applied: Operation[] = []\n\n\t\tfor (const op of operations) {\n\t\t\tif (!operationMatchesScopes(op, this.authContext?.scopes)) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst result = await this.store.applyRemoteOperation(op)\n\t\t\tif (result === 'applied') {\n\t\t\t\tapplied.push(op)\n\t\t\t}\n\t\t}\n\n\t\tif (operations.length > 0) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:received',\n\t\t\t\toperations,\n\t\t\t\tbatchSize: operations.length,\n\t\t\t})\n\t\t}\n\n\t\t// Send acknowledgment\n\t\tconst lastOp = operations[operations.length - 1]\n\t\tconst ack: SyncMessage = {\n\t\t\ttype: 'acknowledgment',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\tacknowledgedMessageId: msg.messageId,\n\t\t\tlastSequenceNumber: lastOp ? lastOp.sequenceNumber : 0,\n\t\t}\n\t\tthis.transport.send(ack)\n\n\t\t// Relay only newly applied operations to other sessions\n\t\tif (applied.length > 0) {\n\t\t\tthis.onRelay?.(this.sessionId, applied)\n\t\t}\n\t}\n\n\tprivate async sendDelta(clientVector: Map<string, number>): Promise<void> {\n\t\tconst serverVector = this.store.getVersionVector()\n\t\tconst missing: Operation[] = []\n\n\t\tfor (const [nodeId, serverSeq] of serverVector) {\n\t\t\tconst clientSeq = clientVector.get(nodeId) ?? 0\n\t\t\tif (serverSeq > clientSeq) {\n\t\t\t\tconst ops = await this.store.getOperationRange(nodeId, clientSeq + 1, serverSeq)\n\t\t\t\tconst visible = ops.filter((op) => operationMatchesScopes(op, this.authContext?.scopes))\n\t\t\t\tmissing.push(...visible)\n\t\t\t}\n\t\t}\n\n\t\tif (missing.length === 0) {\n\t\t\t// Send empty final batch to signal delta is complete\n\t\t\tconst emptyBatch: SyncMessage = {\n\t\t\t\ttype: 'operation-batch',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\toperations: [],\n\t\t\t\tisFinal: true,\n\t\t\t\tbatchIndex: 0,\n\t\t\t}\n\t\t\tthis.transport.send(emptyBatch)\n\t\t\treturn\n\t\t}\n\n\t\t// Sort causally and paginate\n\t\tconst sorted = topologicalSort(missing)\n\t\tconst totalBatches = Math.ceil(sorted.length / this.batchSize)\n\n\t\tfor (let i = 0; i < totalBatches; i++) {\n\t\t\tconst start = i * this.batchSize\n\t\t\tconst batchOps = sorted.slice(start, start + this.batchSize)\n\t\t\tconst serializedOps = batchOps.map((op) => this.serializer.encodeOperation(op))\n\n\t\t\tconst batchMsg: SyncMessage = {\n\t\t\t\ttype: 'operation-batch',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\toperations: serializedOps,\n\t\t\t\tisFinal: i === totalBatches - 1,\n\t\t\t\tbatchIndex: i,\n\t\t\t}\n\t\t\tthis.transport.send(batchMsg)\n\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:sent',\n\t\t\t\toperations: batchOps,\n\t\t\t\tbatchSize: batchOps.length,\n\t\t\t})\n\t\t}\n\t}\n\n\tprivate sendError(code: string, message: string, retriable: boolean): void {\n\t\tconst errorMsg: SyncMessage = {\n\t\t\ttype: 'error',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\tcode,\n\t\t\tmessage,\n\t\t\tretriable,\n\t\t}\n\t\tthis.transport.send(errorMsg)\n\t}\n\n\tprivate setSerializerWireFormat(format: WireFormat): void {\n\t\tif (typeof this.serializer.setWireFormat === 'function') {\n\t\t\tthis.serializer.setWireFormat(format)\n\t\t}\n\t}\n\n\tprivate handleTransportClose(): void {\n\t\tif (this.state === 'closed') return\n\t\tthis.state = 'closed'\n\t\tthis.emitter?.emit({ type: 'sync:disconnected', reason: 'transport closed' })\n\t\tthis.onClose?.(this.sessionId)\n\t}\n}\n\nfunction selectWireFormat(supportedWireFormats?: WireFormat[]): WireFormat {\n\tif (supportedWireFormats?.includes('protobuf')) {\n\t\treturn 'protobuf'\n\t}\n\n\treturn 'json'\n}\n","import type { Operation } from '@korajs/core'\n\n/**\n * Per-collection scope map from auth context.\n */\nexport type ScopeMap = Record<string, Record<string, unknown>>\n\n/**\n * Returns true if an operation is visible to a session based on its scopes.\n *\n * Rules:\n * - No scopes configured => visible\n * - Collection missing from scope map => hidden\n * - All scoped field/value pairs must match the operation snapshot\n */\nexport function operationMatchesScopes(op: Operation, scopes: ScopeMap | undefined): boolean {\n\tif (!scopes) return true\n\n\tconst collectionScope = scopes[op.collection]\n\tif (!collectionScope) return false\n\n\tconst snapshot = buildSnapshot(op)\n\tif (!snapshot) return false\n\n\tfor (const [field, expected] of Object.entries(collectionScope)) {\n\t\tif (snapshot[field] !== expected) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\nfunction buildSnapshot(op: Operation): Record<string, unknown> | null {\n\tconst previous = asRecord(op.previousData)\n\tconst next = asRecord(op.data)\n\n\tif (!previous && !next) return null\n\n\treturn {\n\t\t...(previous ?? {}),\n\t\t...(next ?? {}),\n\t}\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) {\n\t\treturn null\n\t}\n\n\treturn value as Record<string, unknown>\n}\n","import type { KoraEventEmitter, Operation } from '@korajs/core'\nimport { SyncError, generateUUIDv7 } from '@korajs/core'\nimport type { MessageSerializer } from '@korajs/sync'\nimport { JsonMessageSerializer } from '@korajs/sync'\nimport { ClientSession } from '../session/client-session'\nimport type { ServerStore } from '../store/server-store'\nimport { HttpServerTransport } from '../transport/http-server-transport'\nimport type { ServerTransport } from '../transport/server-transport'\nimport { WsServerTransport } from '../transport/ws-server-transport'\nimport type {\n\tAuthProvider,\n\tHttpSyncRequest,\n\tHttpSyncResponse,\n\tKoraSyncServerConfig,\n\tServerStatus,\n} from '../types'\n\nconst DEFAULT_MAX_CONNECTIONS = 0 // unlimited\nconst DEFAULT_BATCH_SIZE = 100\nconst DEFAULT_SCHEMA_VERSION = 1\nconst DEFAULT_HOST = '0.0.0.0'\nconst DEFAULT_PATH = '/'\n\n/**\n * Minimal interface for a ws.WebSocketServer instance.\n * Allows dependency injection for testing without importing ws directly.\n */\nexport interface WsServerLike {\n\ton(event: string, listener: (...args: unknown[]) => void): void\n\tclose(callback?: (err?: Error) => void): void\n\taddress(): { port: number } | string | null\n}\n\n/**\n * Constructor type for creating a WebSocket server.\n */\nexport type WsServerConstructor = new (options: {\n\tport?: number\n\thost?: string\n\tpath?: string\n}) => WsServerLike\n\n/**\n * Self-hosted sync server. Accepts WebSocket connections from clients,\n * handles the sync protocol, stores operations, and relays changes\n * between connected clients.\n *\n * Two modes of operation:\n * 1. **Standalone**: Call `start()` with a port — creates its own WebSocket server.\n * 2. **Attach**: Call `handleConnection(transport)` — attach to an existing HTTP server.\n */\nexport class KoraSyncServer {\n\tprivate readonly store: ServerStore\n\tprivate readonly auth: AuthProvider | null\n\tprivate readonly serializer: MessageSerializer\n\tprivate readonly emitter: KoraEventEmitter | null\n\tprivate readonly maxConnections: number\n\tprivate readonly batchSize: number\n\tprivate readonly schemaVersion: number\n\tprivate readonly port: number | undefined\n\tprivate readonly host: string\n\tprivate readonly path: string\n\n\tprivate readonly sessions = new Map<string, ClientSession>()\n\tprivate readonly httpClients = new Map<string, { sessionId: string; transport: HttpServerTransport }>()\n\tprivate readonly httpSessionToClient = new Map<string, string>()\n\tprivate wsServer: WsServerLike | null = null\n\tprivate running = false\n\n\tconstructor(config: KoraSyncServerConfig) {\n\t\tthis.store = config.store\n\t\tthis.auth = config.auth ?? null\n\t\tthis.serializer = config.serializer ?? new JsonMessageSerializer()\n\t\tthis.emitter = config.emitter ?? null\n\t\tthis.maxConnections = config.maxConnections ?? DEFAULT_MAX_CONNECTIONS\n\t\tthis.batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE\n\t\tthis.schemaVersion = config.schemaVersion ?? DEFAULT_SCHEMA_VERSION\n\t\tthis.port = config.port\n\t\tthis.host = config.host ?? DEFAULT_HOST\n\t\tthis.path = config.path ?? DEFAULT_PATH\n\t}\n\n\t/**\n\t * Start the WebSocket server in standalone mode.\n\t *\n\t * @param wsServerImpl - Optional WebSocket server constructor for testing\n\t */\n\tasync start(wsServerImpl?: WsServerConstructor): Promise<void> {\n\t\tif (this.running) {\n\t\t\tthrow new SyncError('Server is already running', { port: this.port })\n\t\t}\n\n\t\tif (!wsServerImpl && this.port === undefined) {\n\t\t\tthrow new SyncError(\n\t\t\t\t'Port is required for standalone mode. Provide port in config or use handleConnection() for attach mode.',\n\t\t\t\t{},\n\t\t\t)\n\t\t}\n\n\t\tif (wsServerImpl) {\n\t\t\tthis.wsServer = new wsServerImpl({\n\t\t\t\tport: this.port,\n\t\t\t\thost: this.host,\n\t\t\t\tpath: this.path,\n\t\t\t})\n\t\t} else {\n\t\t\t// Dynamic import of ws — only needed in standalone mode\n\t\t\tconst { WebSocketServer } = await import('ws')\n\t\t\tthis.wsServer = new WebSocketServer({\n\t\t\t\tport: this.port,\n\t\t\t\thost: this.host,\n\t\t\t\tpath: this.path,\n\t\t\t})\n\t\t}\n\n\t\tthis.wsServer.on('connection', (ws: unknown) => {\n\t\t\tconst transport = new WsServerTransport(\n\t\t\t\tws as import('../transport/ws-server-transport').WsWebSocket,\n\t\t\t\t{\n\t\t\t\t\tserializer: this.serializer,\n\t\t\t\t},\n\t\t\t)\n\t\t\tthis.handleConnection(transport)\n\t\t})\n\n\t\tthis.running = true\n\t}\n\n\t/**\n\t * Stop the server. Closes all sessions and the WebSocket server.\n\t */\n\tasync stop(): Promise<void> {\n\t\t// Close all active sessions (works in both standalone and attach mode)\n\t\tfor (const session of this.sessions.values()) {\n\t\t\tsession.close('server shutting down')\n\t\t}\n\t\tthis.sessions.clear()\n\t\tthis.httpClients.clear()\n\t\tthis.httpSessionToClient.clear()\n\n\t\t// Close WebSocket server (standalone mode only)\n\t\tif (this.wsServer) {\n\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\tthis.wsServer?.close(() => resolve())\n\t\t\t})\n\t\t\tthis.wsServer = null\n\t\t}\n\n\t\tthis.running = false\n\t}\n\n\t/**\n\t * Handle one HTTP sync request for a long-polling client.\n\t *\n\t * A stable `clientId` identifies the logical connection across requests.\n\t */\n\tasync handleHttpRequest(request: HttpSyncRequest): Promise<HttpSyncResponse> {\n\t\tif (!request.clientId || request.clientId.trim().length === 0) {\n\t\t\treturn { status: 400 }\n\t\t}\n\n\t\tconst client = this.getOrCreateHttpClient(request.clientId)\n\n\t\tif (request.method === 'POST') {\n\t\t\tif (request.body === undefined) {\n\t\t\t\treturn { status: 400 }\n\t\t\t}\n\n\t\t\tconst payload = normalizeHttpBody(request.body, request.contentType)\n\t\t\tclient.transport.receive(payload)\n\t\t\treturn { status: 202 }\n\t\t}\n\n\t\tif (request.method === 'GET') {\n\t\t\tconst polled = client.transport.poll(request.ifNoneMatch)\n\t\t\treturn {\n\t\t\t\tstatus: polled.status,\n\t\t\t\tbody: polled.body,\n\t\t\t\theaders: polled.headers,\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tstatus: 405,\n\t\t\theaders: { allow: 'GET, POST' },\n\t\t}\n\t}\n\n\t/**\n\t * Handle an incoming client connection (attach mode).\n\t * Creates a new ClientSession for the transport.\n\t *\n\t * @param transport - The server transport for the new connection\n\t * @returns The session ID\n\t */\n\thandleConnection(transport: ServerTransport): string {\n\t\t// Check max connections\n\t\tif (this.maxConnections > 0 && this.sessions.size >= this.maxConnections) {\n\t\t\ttransport.send({\n\t\t\t\ttype: 'error',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\tcode: 'MAX_CONNECTIONS',\n\t\t\t\tmessage: `Server has reached maximum connections (${this.maxConnections})`,\n\t\t\t\tretriable: true,\n\t\t\t})\n\t\t\ttransport.close(4029, 'max connections reached')\n\t\t\tthrow new SyncError('Maximum connections reached', {\n\t\t\t\tcurrent: this.sessions.size,\n\t\t\t\tmax: this.maxConnections,\n\t\t\t})\n\t\t}\n\n\t\tconst sessionId = generateUUIDv7()\n\n\t\tconst session = new ClientSession({\n\t\t\tsessionId,\n\t\t\ttransport,\n\t\t\tstore: this.store,\n\t\t\tauth: this.auth ?? undefined,\n\t\t\tserializer: this.serializer,\n\t\t\temitter: this.emitter ?? undefined,\n\t\t\tbatchSize: this.batchSize,\n\t\t\tschemaVersion: this.schemaVersion,\n\t\t\tonRelay: (sourceSessionId, operations) => {\n\t\t\t\tthis.handleRelay(sourceSessionId, operations)\n\t\t\t},\n\t\t\tonClose: (sid) => {\n\t\t\t\tthis.handleSessionClose(sid)\n\t\t\t},\n\t\t})\n\n\t\tthis.sessions.set(sessionId, session)\n\t\tsession.start()\n\n\t\treturn sessionId\n\t}\n\n\t/**\n\t * Get the current server status.\n\t */\n\tasync getStatus(): Promise<ServerStatus> {\n\t\treturn {\n\t\t\trunning: this.running,\n\t\t\tconnectedClients: this.sessions.size,\n\t\t\tport: this.port ?? null,\n\t\t\ttotalOperations: await this.store.getOperationCount(),\n\t\t}\n\t}\n\n\t/**\n\t * Get the number of currently connected clients.\n\t */\n\tgetConnectionCount(): number {\n\t\treturn this.sessions.size\n\t}\n\n\t// --- Private ---\n\n\tprivate handleRelay(sourceSessionId: string, operations: Operation[]): void {\n\t\tfor (const [sessionId, session] of this.sessions) {\n\t\t\tif (sessionId === sourceSessionId) continue\n\t\t\tsession.relayOperations(operations)\n\t\t}\n\t}\n\n\tprivate handleSessionClose(sessionId: string): void {\n\t\tthis.sessions.delete(sessionId)\n\n\t\tconst clientId = this.httpSessionToClient.get(sessionId)\n\t\tif (clientId) {\n\t\t\tthis.httpSessionToClient.delete(sessionId)\n\t\t\tthis.httpClients.delete(clientId)\n\t\t}\n\t}\n\n\tprivate getOrCreateHttpClient(clientId: string): { sessionId: string; transport: HttpServerTransport } {\n\t\tconst existing = this.httpClients.get(clientId)\n\t\tif (existing) {\n\t\t\treturn existing\n\t\t}\n\n\t\tconst transport = new HttpServerTransport(this.serializer)\n\t\tconst sessionId = this.handleConnection(transport)\n\t\tconst client = { sessionId, transport }\n\n\t\tthis.httpClients.set(clientId, client)\n\t\tthis.httpSessionToClient.set(sessionId, clientId)\n\n\t\treturn client\n\t}\n}\n\nfunction normalizeHttpBody(body: string | Uint8Array, contentType?: string): string | Uint8Array {\n\tif (body instanceof Uint8Array) {\n\t\treturn body\n\t}\n\n\tif (contentType?.includes('application/x-protobuf')) {\n\t\treturn new TextEncoder().encode(body)\n\t}\n\n\treturn body\n}\n","import type { AuthContext, AuthProvider } from '../types'\n\n/**\n * Auth provider that accepts all connections.\n * Returns a default anonymous context for any token.\n * Useful for development and testing.\n */\nexport class NoAuthProvider implements AuthProvider {\n\tasync authenticate(_token: string): Promise<AuthContext> {\n\t\treturn { userId: 'anonymous' }\n\t}\n}\n","import type { AuthContext, AuthProvider } from '../types'\n\n/**\n * Options for creating a TokenAuthProvider.\n */\nexport interface TokenAuthProviderOptions {\n\t/**\n\t * Validate a token and return an AuthContext if valid, or null if rejected.\n\t * This is where you implement your auth logic (JWT verification, database lookup, etc.).\n\t */\n\tvalidate: (token: string) => Promise<AuthContext | null>\n}\n\n/**\n * Token-based auth provider that delegates validation to a user-provided function.\n *\n * @example\n * ```typescript\n * const auth = new TokenAuthProvider({\n * validate: async (token) => {\n * const user = await verifyJWT(token)\n * return user ? { userId: user.id } : null\n * }\n * })\n * ```\n */\nexport class TokenAuthProvider implements AuthProvider {\n\tprivate readonly validate: (token: string) => Promise<AuthContext | null>\n\n\tconstructor(options: TokenAuthProviderOptions) {\n\t\tthis.validate = options.validate\n\t}\n\n\tasync authenticate(token: string): Promise<AuthContext | null> {\n\t\treturn this.validate(token)\n\t}\n}\n","import type { KoraSyncServerConfig } from '../types'\nimport { KoraSyncServer } from './kora-sync-server'\n\n/**\n * Factory function to create a KoraSyncServer.\n *\n * @param config - Server configuration\n * @returns A new KoraSyncServer instance\n *\n * @example\n * ```typescript\n * const server = createKoraServer({\n * store: new MemoryServerStore(),\n * port: 3000,\n * })\n * await server.start()\n * ```\n */\nexport function createKoraServer(config: KoraSyncServerConfig): KoraSyncServer {\n\treturn new KoraSyncServer(config)\n}\n"],"mappings":"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","names":["import_core","import_core","import_drizzle_orm","import_core","import_core","import_core","import_sync","operations","import_core","import_sync","DEFAULT_BATCH_SIZE","DEFAULT_SCHEMA_VERSION","operations"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","../src/store/memory-server-store.ts","../src/store/postgres-server-store.ts","../src/store/drizzle-pg-schema.ts","../src/store/sqlite-server-store.ts","../src/store/drizzle-schema.ts","../src/transport/http-server-transport.ts","../src/transport/ws-server-transport.ts","../src/session/client-session.ts","../src/scopes/server-scope-filter.ts","../src/server/kora-sync-server.ts","../src/auth/no-auth.ts","../src/auth/token-auth.ts","../src/server/create-server.ts","../src/server/production-server.ts"],"sourcesContent":["// @korajs/server — public API\n// Every export here is a public API commitment. Be explicit.\n\n// === Types ===\nexport type {\n\tAuthContext,\n\tAuthProvider,\n\tHttpSyncRequest,\n\tHttpSyncResponse,\n\tKoraSyncServerConfig,\n\tServerStatus,\n} from './types'\n\nexport type { ServerStore } from './store/server-store'\n\nexport type {\n\tServerCloseHandler,\n\tServerErrorHandler,\n\tServerMessageHandler,\n\tServerTransport,\n} from './transport/server-transport'\n\nexport type {\n\tHttpPollResponse,\n} from './transport/http-server-transport'\n\nexport type {\n\tWsWebSocket,\n\tWsServerTransportOptions,\n} from './transport/ws-server-transport'\n\nexport type {\n\tClientSessionOptions,\n\tRelayCallback,\n\tSessionState,\n} from './session/client-session'\n\nexport type {\n\tWsServerConstructor,\n\tWsServerLike,\n} from './server/kora-sync-server'\n\nexport type {\n\tProductionServerConfig,\n\tProductionServer,\n} from './server/production-server'\n\nexport type { TokenAuthProviderOptions } from './auth/token-auth'\n\n// === Classes ===\nexport { MemoryServerStore } from './store/memory-server-store'\nexport { PostgresServerStore } from './store/postgres-server-store'\nexport { SqliteServerStore } from './store/sqlite-server-store'\nexport { HttpServerTransport } from './transport/http-server-transport'\nexport { WsServerTransport } from './transport/ws-server-transport'\nexport { ClientSession } from './session/client-session'\nexport { KoraSyncServer } from './server/kora-sync-server'\nexport { NoAuthProvider } from './auth/no-auth'\nexport { TokenAuthProvider } from './auth/token-auth'\n\n// === Factory Functions ===\nexport { createKoraServer } from './server/create-server'\nexport { createProductionServer } from './server/production-server'\nexport { createPostgresServerStore } from './store/postgres-server-store'\nexport { createSqliteServerStore } from './store/sqlite-server-store'\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","import type { Operation, VersionVector } from '@korajs/core'\nimport { generateUUIDv7 } from '@korajs/core'\nimport type { ApplyResult } from '@korajs/sync'\nimport type { ServerStore } from './server-store'\n\n/**\n * In-memory server store for testing and quick prototyping.\n * Not suitable for production — data does not survive process restart.\n */\nexport class MemoryServerStore implements ServerStore {\n\tprivate readonly nodeId: string\n\tprivate readonly operations: Operation[] = []\n\tprivate readonly operationIndex = new Map<string, Operation>()\n\tprivate readonly versionVector: Map<string, number> = new Map()\n\tprivate closed = false\n\n\tconstructor(nodeId?: string) {\n\t\tthis.nodeId = nodeId ?? generateUUIDv7()\n\t}\n\n\tgetVersionVector(): VersionVector {\n\t\treturn new Map(this.versionVector)\n\t}\n\n\tgetNodeId(): string {\n\t\treturn this.nodeId\n\t}\n\n\tasync applyRemoteOperation(op: Operation): Promise<ApplyResult> {\n\t\tthis.assertOpen()\n\n\t\t// Content-addressed dedup: same id = same content\n\t\tif (this.operationIndex.has(op.id)) {\n\t\t\treturn 'duplicate'\n\t\t}\n\n\t\tthis.operations.push(op)\n\t\tthis.operationIndex.set(op.id, op)\n\n\t\t// Advance version vector\n\t\tconst currentSeq = this.versionVector.get(op.nodeId) ?? 0\n\t\tif (op.sequenceNumber > currentSeq) {\n\t\t\tthis.versionVector.set(op.nodeId, op.sequenceNumber)\n\t\t}\n\n\t\treturn 'applied'\n\t}\n\n\tasync getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]> {\n\t\tthis.assertOpen()\n\n\t\treturn this.operations\n\t\t\t.filter(\n\t\t\t\t(op) => op.nodeId === nodeId && op.sequenceNumber >= fromSeq && op.sequenceNumber <= toSeq,\n\t\t\t)\n\t\t\t.sort((a, b) => a.sequenceNumber - b.sequenceNumber)\n\t}\n\n\tasync getOperationCount(): Promise<number> {\n\t\tthis.assertOpen()\n\t\treturn this.operations.length\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.closed = true\n\t}\n\n\t// --- Testing helpers (not on interface) ---\n\n\t/**\n\t * Get all stored operations (for test assertions).\n\t */\n\tgetAllOperations(): Operation[] {\n\t\treturn [...this.operations]\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('MemoryServerStore is closed')\n\t\t}\n\t}\n}\n","import type { Operation, VersionVector } from '@korajs/core'\nimport { generateUUIDv7 } from '@korajs/core'\nimport type { ApplyResult } from '@korajs/sync'\nimport { and, asc, between, count, eq, sql } from 'drizzle-orm'\nimport type { PostgresJsDatabase } from 'drizzle-orm/postgres-js'\nimport { pgOperations, pgSyncState } from './drizzle-pg-schema'\nimport type { ServerStore } from './server-store'\n\n/**\n * PostgreSQL-backed server store using Drizzle ORM.\n * All reads and writes go through Drizzle's typed query builder.\n * DDL stays as raw SQL via Drizzle's sql template (standard practice without drizzle-kit).\n */\nexport class PostgresServerStore implements ServerStore {\n\tprivate readonly nodeId: string\n\tprivate readonly db: PostgresJsDatabase\n\tprivate readonly versionVector: VersionVector = new Map()\n\tprivate readonly ready: Promise<void>\n\tprivate closed = false\n\n\tconstructor(db: PostgresJsDatabase, nodeId?: string) {\n\t\tthis.db = db\n\t\tthis.nodeId = nodeId ?? generateUUIDv7()\n\t\tthis.ready = this.initialize()\n\t}\n\n\tgetVersionVector(): VersionVector {\n\t\tthis.assertOpen()\n\t\treturn new Map(this.versionVector)\n\t}\n\n\tgetNodeId(): string {\n\t\treturn this.nodeId\n\t}\n\n\tasync applyRemoteOperation(op: Operation): Promise<ApplyResult> {\n\t\tthis.assertOpen()\n\t\tawait this.ready\n\n\t\t// Content-addressed dedup check\n\t\tconst existing = await this.db\n\t\t\t.select({ id: pgOperations.id })\n\t\t\t.from(pgOperations)\n\t\t\t.where(eq(pgOperations.id, op.id))\n\t\t\t.limit(1)\n\n\t\tif (existing.length > 0) {\n\t\t\treturn 'duplicate'\n\t\t}\n\n\t\tconst now = Date.now()\n\t\tconst row = this.serializeOperation(op, now)\n\n\t\tawait this.db.transaction(async (tx) => {\n\t\t\t// Insert operation with dedup\n\t\t\tawait tx\n\t\t\t\t.insert(pgOperations)\n\t\t\t\t.values(row)\n\t\t\t\t.onConflictDoNothing({ target: pgOperations.id })\n\n\t\t\t// Upsert version vector: advance max sequence number with GREATEST\n\t\t\tawait tx\n\t\t\t\t.insert(pgSyncState)\n\t\t\t\t.values({\n\t\t\t\t\tnodeId: op.nodeId,\n\t\t\t\t\tmaxSequenceNumber: op.sequenceNumber,\n\t\t\t\t\tlastSeenAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoUpdate({\n\t\t\t\t\ttarget: pgSyncState.nodeId,\n\t\t\t\t\tset: {\n\t\t\t\t\t\tmaxSequenceNumber: sql`GREATEST(${pgSyncState.maxSequenceNumber}, ${op.sequenceNumber})`,\n\t\t\t\t\t\tlastSeenAt: sql`${now}`,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t})\n\n\t\t// Update in-memory version vector cache\n\t\tconst currentMax = this.versionVector.get(op.nodeId) ?? 0\n\t\tif (op.sequenceNumber > currentMax) {\n\t\t\tthis.versionVector.set(op.nodeId, op.sequenceNumber)\n\t\t}\n\n\t\treturn 'applied'\n\t}\n\n\tasync getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]> {\n\t\tthis.assertOpen()\n\t\tawait this.ready\n\n\t\tconst rows = await this.db\n\t\t\t.select()\n\t\t\t.from(pgOperations)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(pgOperations.nodeId, nodeId),\n\t\t\t\t\tbetween(pgOperations.sequenceNumber, fromSeq, toSeq),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.orderBy(asc(pgOperations.sequenceNumber))\n\n\t\treturn rows.map((row) => this.deserializeOperation(row))\n\t}\n\n\tasync getOperationCount(): Promise<number> {\n\t\tthis.assertOpen()\n\t\tawait this.ready\n\n\t\tconst result = await this.db.select({ value: count() }).from(pgOperations)\n\t\treturn result[0]?.value ?? 0\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.closed = true\n\t}\n\n\tprivate async initialize(): Promise<void> {\n\t\tawait this.ensureTables()\n\n\t\t// Hydrate in-memory version vector cache via Drizzle query\n\t\tconst rows = await this.db\n\t\t\t.select({\n\t\t\t\tnodeId: pgSyncState.nodeId,\n\t\t\t\tmaxSequenceNumber: pgSyncState.maxSequenceNumber,\n\t\t\t})\n\t\t\t.from(pgSyncState)\n\n\t\tfor (const row of rows) {\n\t\t\tthis.versionVector.set(row.nodeId, row.maxSequenceNumber)\n\t\t}\n\t}\n\n\t/**\n\t * Create tables if they don't exist.\n\t * Uses raw SQL via Drizzle's sql template — standard DDL practice without drizzle-kit.\n\t */\n\tprivate async ensureTables(): Promise<void> {\n\t\tawait this.db.execute(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS operations (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tnode_id TEXT NOT NULL,\n\t\t\t\ttype TEXT NOT NULL,\n\t\t\t\tcollection TEXT NOT NULL,\n\t\t\t\trecord_id TEXT NOT NULL,\n\t\t\t\tdata TEXT,\n\t\t\t\tprevious_data TEXT,\n\t\t\t\twall_time INTEGER NOT NULL,\n\t\t\t\tlogical INTEGER NOT NULL,\n\t\t\t\ttimestamp_node_id TEXT NOT NULL,\n\t\t\t\tsequence_number INTEGER NOT NULL,\n\t\t\t\tcausal_deps TEXT NOT NULL DEFAULT '[]',\n\t\t\t\tschema_version INTEGER NOT NULL,\n\t\t\t\treceived_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\n\t\tawait this.db.execute(\n\t\t\tsql`CREATE INDEX IF NOT EXISTS idx_node_seq ON operations (node_id, sequence_number)`,\n\t\t)\n\t\tawait this.db.execute(\n\t\t\tsql`CREATE INDEX IF NOT EXISTS idx_collection ON operations (collection)`,\n\t\t)\n\t\tawait this.db.execute(\n\t\t\tsql`CREATE INDEX IF NOT EXISTS idx_received ON operations (received_at)`,\n\t\t)\n\n\t\tawait this.db.execute(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS sync_state (\n\t\t\t\tnode_id TEXT PRIMARY KEY,\n\t\t\t\tmax_sequence_number INTEGER NOT NULL,\n\t\t\t\tlast_seen_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\t}\n\n\tprivate serializeOperation(\n\t\top: Operation,\n\t\treceivedAt: number,\n\t): typeof pgOperations.$inferInsert {\n\t\treturn {\n\t\t\tid: op.id,\n\t\t\tnodeId: op.nodeId,\n\t\t\ttype: op.type,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tdata: op.data !== null ? JSON.stringify(op.data) : null,\n\t\t\tpreviousData: op.previousData !== null ? JSON.stringify(op.previousData) : null,\n\t\t\twallTime: op.timestamp.wallTime,\n\t\t\tlogical: op.timestamp.logical,\n\t\t\ttimestampNodeId: op.timestamp.nodeId,\n\t\t\tsequenceNumber: op.sequenceNumber,\n\t\t\tcausalDeps: JSON.stringify(op.causalDeps),\n\t\t\tschemaVersion: op.schemaVersion,\n\t\t\treceivedAt,\n\t\t}\n\t}\n\n\tprivate deserializeOperation(row: typeof pgOperations.$inferSelect): Operation {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tnodeId: row.nodeId,\n\t\t\ttype: row.type as Operation['type'],\n\t\t\tcollection: row.collection,\n\t\t\trecordId: row.recordId,\n\t\t\tdata: row.data !== null ? JSON.parse(row.data) : null,\n\t\t\tpreviousData: row.previousData !== null ? JSON.parse(row.previousData) : null,\n\t\t\ttimestamp: {\n\t\t\t\twallTime: row.wallTime,\n\t\t\t\tlogical: row.logical,\n\t\t\t\tnodeId: row.timestampNodeId,\n\t\t\t},\n\t\t\tsequenceNumber: row.sequenceNumber,\n\t\t\tcausalDeps: JSON.parse(row.causalDeps),\n\t\t\tschemaVersion: row.schemaVersion,\n\t\t}\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('PostgresServerStore is closed')\n\t\t}\n\t}\n}\n\n/**\n * Creates a PostgresServerStore from a PostgreSQL connection string.\n *\n * Uses runtime dynamic imports so projects that do not use PostgreSQL\n * do not need to install `postgres`. Wraps the postgres client with\n * Drizzle ORM for typed query building.\n */\nexport async function createPostgresServerStore(options: {\n\tconnectionString: string\n\tnodeId?: string\n}): Promise<PostgresServerStore> {\n\tconst { postgresClient, drizzleFn } = await loadPostgresDeps()\n\tconst client = postgresClient(options.connectionString)\n\tconst db = drizzleFn(client)\n\n\treturn new PostgresServerStore(db, options.nodeId)\n}\n\nasync function loadPostgresDeps(): Promise<{\n\tpostgresClient: (connectionString: string) => unknown\n\tdrizzleFn: (client: unknown) => PostgresJsDatabase\n}> {\n\ttry {\n\t\tconst dynamicImport = new Function('specifier', 'return import(specifier)') as (\n\t\t\tspecifier: string,\n\t\t) => Promise<unknown>\n\n\t\tconst postgresMod = (await dynamicImport('postgres')) as { default: (cs: string) => unknown }\n\t\tconst drizzleMod = (await dynamicImport('drizzle-orm/postgres-js')) as {\n\t\t\tdrizzle: (client: unknown) => PostgresJsDatabase\n\t\t}\n\n\t\treturn {\n\t\t\tpostgresClient: postgresMod.default,\n\t\t\tdrizzleFn: drizzleMod.drizzle,\n\t\t}\n\t} catch {\n\t\tthrow new Error(\n\t\t\t'PostgreSQL backend requires the \"postgres\" package. Install it in your project dependencies.',\n\t\t)\n\t}\n}\n","import { index, integer, pgTable, text } from 'drizzle-orm/pg-core'\n\n/**\n * Drizzle schema for the Kora sync server's PostgreSQL database.\n *\n * Two tables:\n * - `pgOperations` — the append-only operation log (content-addressed by id)\n * - `pgSyncState` — tracks the max sequence number seen per node (version vector)\n *\n * Column structure mirrors the SQLite drizzle-schema.ts but uses pgTable.\n */\n\nexport const pgOperations = pgTable(\n\t'operations',\n\t{\n\t\tid: text('id').primaryKey(),\n\t\tnodeId: text('node_id').notNull(),\n\t\ttype: text('type').notNull(),\n\t\tcollection: text('collection').notNull(),\n\t\trecordId: text('record_id').notNull(),\n\t\tdata: text('data'), // JSON-serialized, null for deletes\n\t\tpreviousData: text('previous_data'), // JSON-serialized, null for insert/delete\n\t\twallTime: integer('wall_time').notNull(),\n\t\tlogical: integer('logical').notNull(),\n\t\ttimestampNodeId: text('timestamp_node_id').notNull(),\n\t\tsequenceNumber: integer('sequence_number').notNull(),\n\t\tcausalDeps: text('causal_deps').notNull().default('[]'), // JSON array of op IDs\n\t\tschemaVersion: integer('schema_version').notNull(),\n\t\treceivedAt: integer('received_at').notNull(),\n\t},\n\t(table) => ({\n\t\tnodeSeqIdx: index('idx_pg_node_seq').on(table.nodeId, table.sequenceNumber),\n\t\tcollectionIdx: index('idx_pg_collection').on(table.collection),\n\t\treceivedIdx: index('idx_pg_received').on(table.receivedAt),\n\t}),\n)\n\nexport const pgSyncState = pgTable('sync_state', {\n\tnodeId: text('node_id').primaryKey(),\n\tmaxSequenceNumber: integer('max_sequence_number').notNull(),\n\tlastSeenAt: integer('last_seen_at').notNull(),\n})\n","import { createRequire } from 'node:module'\nimport type { Operation, VersionVector } from '@korajs/core'\nimport { generateUUIDv7 } from '@korajs/core'\nimport type { ApplyResult } from '@korajs/sync'\nimport { and, asc, between, count, eq, sql } from 'drizzle-orm'\nimport type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'\nimport { operations, syncState } from './drizzle-schema'\nimport type { ServerStore } from './server-store'\n\n// better-sqlite3 is a native CJS addon that cannot be loaded via ESM import().\n// createRequire provides a CJS require() that works in both ESM and CJS contexts.\n// tsup's shims option ensures import.meta.url is available in CJS builds.\nconst esmRequire = createRequire(import.meta.url)\n\n/**\n * SQLite-backed server store using Drizzle ORM.\n * Persists operations and version vectors to a real database file,\n * surviving process restarts.\n */\nexport class SqliteServerStore implements ServerStore {\n\tprivate readonly nodeId: string\n\tprivate readonly db: BetterSQLite3Database\n\tprivate closed = false\n\n\tconstructor(db: BetterSQLite3Database, nodeId?: string) {\n\t\tthis.db = db\n\t\tthis.nodeId = nodeId ?? generateUUIDv7()\n\t\tthis.ensureTables()\n\t}\n\n\tgetVersionVector(): VersionVector {\n\t\tthis.assertOpen()\n\t\tconst rows = this.db.select().from(syncState).all()\n\t\tconst vv: VersionVector = new Map()\n\t\tfor (const row of rows) {\n\t\t\tvv.set(row.nodeId, row.maxSequenceNumber)\n\t\t}\n\t\treturn vv\n\t}\n\n\tgetNodeId(): string {\n\t\treturn this.nodeId\n\t}\n\n\tasync applyRemoteOperation(op: Operation): Promise<ApplyResult> {\n\t\tthis.assertOpen()\n\n\t\tconst now = Date.now()\n\t\tconst row = this.serializeOperation(op, now)\n\n\t\t// Use a transaction for atomicity: insert op + update version vector\n\t\tconst result = this.db.transaction((tx) => {\n\t\t\t// Content-addressed dedup via onConflictDoNothing\n\t\t\tconst insertResult = tx\n\t\t\t\t.insert(operations)\n\t\t\t\t.values(row)\n\t\t\t\t.onConflictDoNothing({ target: operations.id })\n\t\t\t\t.run()\n\n\t\t\tif (insertResult.changes === 0) {\n\t\t\t\treturn 'duplicate' as const\n\t\t\t}\n\n\t\t\t// Advance version vector: upsert with MAX to ensure monotonic progress\n\t\t\ttx.insert(syncState)\n\t\t\t\t.values({\n\t\t\t\t\tnodeId: op.nodeId,\n\t\t\t\t\tmaxSequenceNumber: op.sequenceNumber,\n\t\t\t\t\tlastSeenAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoUpdate({\n\t\t\t\t\ttarget: syncState.nodeId,\n\t\t\t\t\tset: {\n\t\t\t\t\t\tmaxSequenceNumber: sql`MAX(${syncState.maxSequenceNumber}, ${op.sequenceNumber})`,\n\t\t\t\t\t\tlastSeenAt: sql`${now}`,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\t.run()\n\n\t\t\treturn 'applied' as const\n\t\t})\n\n\t\treturn result\n\t}\n\n\tasync getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]> {\n\t\tthis.assertOpen()\n\n\t\tconst rows = this.db\n\t\t\t.select()\n\t\t\t.from(operations)\n\t\t\t.where(and(eq(operations.nodeId, nodeId), between(operations.sequenceNumber, fromSeq, toSeq)))\n\t\t\t.orderBy(asc(operations.sequenceNumber))\n\t\t\t.all()\n\n\t\treturn rows.map((row) => this.deserializeOperation(row))\n\t}\n\n\tasync getOperationCount(): Promise<number> {\n\t\tthis.assertOpen()\n\n\t\tconst result = this.db.select({ value: count() }).from(operations).all()\n\t\treturn result[0]?.value ?? 0\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.closed = true\n\t}\n\n\t/**\n\t * Create the operations and sync_state tables if they don't exist.\n\t * Uses raw SQL via Drizzle's sql template — standard practice for DDL without drizzle-kit.\n\t */\n\tprivate ensureTables(): void {\n\t\tthis.db.run(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS operations (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tnode_id TEXT NOT NULL,\n\t\t\t\ttype TEXT NOT NULL,\n\t\t\t\tcollection TEXT NOT NULL,\n\t\t\t\trecord_id TEXT NOT NULL,\n\t\t\t\tdata TEXT,\n\t\t\t\tprevious_data TEXT,\n\t\t\t\twall_time INTEGER NOT NULL,\n\t\t\t\tlogical INTEGER NOT NULL,\n\t\t\t\ttimestamp_node_id TEXT NOT NULL,\n\t\t\t\tsequence_number INTEGER NOT NULL,\n\t\t\t\tcausal_deps TEXT NOT NULL DEFAULT '[]',\n\t\t\t\tschema_version INTEGER NOT NULL,\n\t\t\t\treceived_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE INDEX IF NOT EXISTS idx_node_seq ON operations (node_id, sequence_number)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE INDEX IF NOT EXISTS idx_collection ON operations (collection)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE INDEX IF NOT EXISTS idx_received ON operations (received_at)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS sync_state (\n\t\t\t\tnode_id TEXT PRIMARY KEY,\n\t\t\t\tmax_sequence_number INTEGER NOT NULL,\n\t\t\t\tlast_seen_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\t}\n\n\tprivate serializeOperation(\n\t\top: Operation,\n\t\treceivedAt: number,\n\t): typeof operations.$inferInsert {\n\t\treturn {\n\t\t\tid: op.id,\n\t\t\tnodeId: op.nodeId,\n\t\t\ttype: op.type,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tdata: op.data !== null ? JSON.stringify(op.data) : null,\n\t\t\tpreviousData: op.previousData !== null ? JSON.stringify(op.previousData) : null,\n\t\t\twallTime: op.timestamp.wallTime,\n\t\t\tlogical: op.timestamp.logical,\n\t\t\ttimestampNodeId: op.timestamp.nodeId,\n\t\t\tsequenceNumber: op.sequenceNumber,\n\t\t\tcausalDeps: JSON.stringify(op.causalDeps),\n\t\t\tschemaVersion: op.schemaVersion,\n\t\t\treceivedAt,\n\t\t}\n\t}\n\n\tprivate deserializeOperation(row: typeof operations.$inferSelect): Operation {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tnodeId: row.nodeId,\n\t\t\ttype: row.type as Operation['type'],\n\t\t\tcollection: row.collection,\n\t\t\trecordId: row.recordId,\n\t\t\tdata: row.data !== null ? JSON.parse(row.data) : null,\n\t\t\tpreviousData: row.previousData !== null ? JSON.parse(row.previousData) : null,\n\t\t\ttimestamp: {\n\t\t\t\twallTime: row.wallTime,\n\t\t\t\tlogical: row.logical,\n\t\t\t\tnodeId: row.timestampNodeId,\n\t\t\t},\n\t\t\tsequenceNumber: row.sequenceNumber,\n\t\t\tcausalDeps: JSON.parse(row.causalDeps),\n\t\t\tschemaVersion: row.schemaVersion,\n\t\t}\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('SqliteServerStore is closed')\n\t\t}\n\t}\n}\n\n/**\n * Creates a SqliteServerStore with a file-backed or in-memory database.\n * Handles database creation, Drizzle wrapping, and table setup.\n *\n * @param options - Configuration options\n * @param options.filename - Path to SQLite database file. Defaults to ':memory:' for testing.\n * @param options.nodeId - Server node ID. Auto-generated if not provided.\n * @returns A ready-to-use SqliteServerStore\n *\n * @example\n * ```typescript\n * import { createSqliteServerStore } from '@korajs/server'\n *\n * const store = createSqliteServerStore({ filename: './kora-server.db' })\n * const server = createKoraServer({ store, port: 3001 })\n * ```\n */\nexport function createSqliteServerStore(options: {\n\tfilename?: string\n\tnodeId?: string\n}): SqliteServerStore {\n\t// better-sqlite3 is a native CJS addon — use esmRequire (from createRequire)\n\t// so this works in both ESM and CJS contexts.\n\tconst Database = esmRequire('better-sqlite3')\n\tconst { drizzle } = esmRequire('drizzle-orm/better-sqlite3')\n\n\tconst filename = options.filename ?? ':memory:'\n\tconst sqlite = new Database(filename)\n\n\t// Enable WAL mode for better concurrent read/write performance\n\tsqlite.pragma('journal_mode = WAL')\n\n\tconst db = drizzle(sqlite)\n\treturn new SqliteServerStore(db, options.nodeId)\n}\n","import { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'\n\n/**\n * Drizzle schema for the Kora sync server's SQLite database.\n *\n * Two tables:\n * - `operations` — the append-only operation log (content-addressed by id)\n * - `syncState` — tracks the max sequence number seen per node (version vector)\n */\n\nexport const operations = sqliteTable(\n\t'operations',\n\t{\n\t\tid: text('id').primaryKey(),\n\t\tnodeId: text('node_id').notNull(),\n\t\ttype: text('type').notNull(),\n\t\tcollection: text('collection').notNull(),\n\t\trecordId: text('record_id').notNull(),\n\t\tdata: text('data'), // JSON-serialized, null for deletes\n\t\tpreviousData: text('previous_data'), // JSON-serialized, null for insert/delete\n\t\twallTime: integer('wall_time').notNull(),\n\t\tlogical: integer('logical').notNull(),\n\t\ttimestampNodeId: text('timestamp_node_id').notNull(),\n\t\tsequenceNumber: integer('sequence_number').notNull(),\n\t\tcausalDeps: text('causal_deps').notNull().default('[]'), // JSON array of op IDs\n\t\tschemaVersion: integer('schema_version').notNull(),\n\t\treceivedAt: integer('received_at').notNull(),\n\t},\n\t(table) => ({\n\t\tnodeSeqIdx: index('idx_node_seq').on(table.nodeId, table.sequenceNumber),\n\t\tcollectionIdx: index('idx_collection').on(table.collection),\n\t\treceivedIdx: index('idx_received').on(table.receivedAt),\n\t}),\n)\n\nexport const syncState = sqliteTable('sync_state', {\n\tnodeId: text('node_id').primaryKey(),\n\tmaxSequenceNumber: integer('max_sequence_number').notNull(),\n\tlastSeenAt: integer('last_seen_at').notNull(),\n})\n","import { SyncError } from '@korajs/core'\nimport type { MessageSerializer } from '@korajs/sync'\nimport type {\n\tServerCloseHandler,\n\tServerErrorHandler,\n\tServerMessageHandler,\n\tServerTransport,\n} from './server-transport'\n\nexport interface HttpPollResponse {\n\tstatus: 200 | 204 | 304 | 410\n\tbody?: string | Uint8Array\n\theaders?: Record<string, string>\n}\n\ninterface QueuedMessage {\n\tetag: string\n\tcontentType: string\n\tpayload: string | Uint8Array\n}\n\n/**\n * Server-side transport for HTTP long-polling clients.\n *\n * Incoming client messages are pushed via POST, while outbound server\n * messages are pulled via GET long-poll requests.\n */\nexport class HttpServerTransport implements ServerTransport {\n\tprivate readonly serializer: MessageSerializer\n\n\tprivate messageHandler: ServerMessageHandler | null = null\n\tprivate closeHandler: ServerCloseHandler | null = null\n\tprivate errorHandler: ServerErrorHandler | null = null\n\n\tprivate connected = true\n\tprivate nextSequence = 1\n\tprivate readonly queue: QueuedMessage[] = []\n\n\tconstructor(serializer: MessageSerializer) {\n\t\tthis.serializer = serializer\n\t}\n\n\tsend(message: import('@korajs/sync').SyncMessage): void {\n\t\tif (!this.connected) return\n\n\t\tconst encoded = this.serializer.encode(message)\n\t\tconst isBinary = encoded instanceof Uint8Array\n\t\tthis.queue.push({\n\t\t\tetag: this.makeEtag(this.nextSequence++),\n\t\t\tcontentType: isBinary ? 'application/x-protobuf' : 'application/json',\n\t\t\tpayload: encoded,\n\t\t})\n\t}\n\n\tonMessage(handler: ServerMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t}\n\n\tonClose(handler: ServerCloseHandler): void {\n\t\tthis.closeHandler = handler\n\t}\n\n\tonError(handler: ServerErrorHandler): void {\n\t\tthis.errorHandler = handler\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.connected\n\t}\n\n\tclose(code = 1000, reason = 'transport closed'): void {\n\t\tif (!this.connected) return\n\t\tthis.connected = false\n\t\tthis.queue.length = 0\n\t\tthis.closeHandler?.(code, reason)\n\t}\n\n\treceive(payload: string | Uint8Array): void {\n\t\tif (!this.connected) {\n\t\t\tthrow new SyncError('HTTP server transport is closed')\n\t\t}\n\n\t\ttry {\n\t\t\tconst message = this.serializer.decode(payload)\n\t\t\tthis.messageHandler?.(message)\n\t\t} catch (error) {\n\t\t\tthis.errorHandler?.(error instanceof Error ? error : new Error(String(error)))\n\t\t}\n\t}\n\n\tpoll(ifNoneMatch?: string): HttpPollResponse {\n\t\tif (!this.connected) {\n\t\t\treturn { status: 410 }\n\t\t}\n\n\t\tconst next = this.queue[0]\n\t\tif (!next) {\n\t\t\treturn { status: 204 }\n\t\t}\n\n\t\tif (ifNoneMatch && ifNoneMatch === next.etag) {\n\t\t\treturn {\n\t\t\t\tstatus: 304,\n\t\t\t\theaders: { etag: next.etag },\n\t\t\t}\n\t\t}\n\n\t\tthis.queue.shift()\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\tbody: next.payload,\n\t\t\theaders: {\n\t\t\t\t'content-type': next.contentType,\n\t\t\t\tetag: next.etag,\n\t\t\t},\n\t\t}\n\t}\n\n\tprivate makeEtag(sequence: number): string {\n\t\treturn `W/\"${sequence}\"`\n\t}\n}\n","import { SyncError } from '@korajs/core'\nimport type { SyncMessage } from '@korajs/sync'\nimport { JsonMessageSerializer } from '@korajs/sync'\nimport type { MessageSerializer } from '@korajs/sync'\nimport type {\n\tServerCloseHandler,\n\tServerErrorHandler,\n\tServerMessageHandler,\n\tServerTransport,\n} from './server-transport'\n\n/** WebSocket ready states (mirrors ws constants) */\nconst WS_OPEN = 1\n\n/**\n * Minimal interface for a ws.WebSocket instance.\n * Allows dependency injection for testing without importing ws directly.\n */\nexport interface WsWebSocket {\n\treadyState: number\n\tsend(data: string | Uint8Array, callback?: (err?: Error) => void): void\n\tclose(code?: number, reason?: string): void\n\ton(event: string, listener: (...args: unknown[]) => void): void\n\tremoveAllListeners(): void\n}\n\n/**\n * Options for WsServerTransport.\n */\nexport interface WsServerTransportOptions {\n\t/** Message serializer. Defaults to JsonMessageSerializer. */\n\tserializer?: MessageSerializer\n}\n\n/**\n * Server-side transport wrapping a ws.WebSocket connection.\n * Created for each incoming client connection.\n */\nexport class WsServerTransport implements ServerTransport {\n\tprivate readonly ws: WsWebSocket\n\tprivate readonly serializer: MessageSerializer\n\tprivate messageHandler: ServerMessageHandler | null = null\n\tprivate closeHandler: ServerCloseHandler | null = null\n\tprivate errorHandler: ServerErrorHandler | null = null\n\n\tconstructor(ws: WsWebSocket, options?: WsServerTransportOptions) {\n\t\tthis.ws = ws\n\t\tthis.serializer = options?.serializer ?? new JsonMessageSerializer()\n\t\tthis.setupListeners()\n\t}\n\n\tsend(message: SyncMessage): void {\n\t\tif (this.ws.readyState !== WS_OPEN) {\n\t\t\tthrow new SyncError('Cannot send message: WebSocket is not open', {\n\t\t\t\treadyState: this.ws.readyState,\n\t\t\t\tmessageType: message.type,\n\t\t\t})\n\t\t}\n\n\t\tconst encoded = this.serializer.encode(message)\n\t\tthis.ws.send(encoded)\n\t}\n\n\tonMessage(handler: ServerMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t}\n\n\tonClose(handler: ServerCloseHandler): void {\n\t\tthis.closeHandler = handler\n\t}\n\n\tonError(handler: ServerErrorHandler): void {\n\t\tthis.errorHandler = handler\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.ws.readyState === WS_OPEN\n\t}\n\n\tclose(code?: number, reason?: string): void {\n\t\tthis.ws.close(code ?? 1000, reason ?? 'server closing')\n\t}\n\n\tprivate setupListeners(): void {\n\t\tthis.ws.on('message', (data: unknown) => {\n\t\t\ttry {\n\t\t\t\tif (\n\t\t\t\t\ttypeof data !== 'string' &&\n\t\t\t\t\t!(data instanceof Uint8Array) &&\n\t\t\t\t\t!(data instanceof ArrayBuffer)\n\t\t\t\t) {\n\t\t\t\t\tthrow new SyncError('Unsupported WebSocket payload type', {\n\t\t\t\t\t\tpayloadType: typeof data,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tconst decoded = this.serializer.decode(data)\n\t\t\t\tthis.messageHandler?.(decoded)\n\t\t\t} catch (err) {\n\t\t\t\tthis.errorHandler?.(err instanceof Error ? err : new Error(String(err)))\n\t\t\t}\n\t\t})\n\n\t\tthis.ws.on('close', (code: unknown, reason: unknown) => {\n\t\t\tthis.closeHandler?.(Number(code) || 1006, String(reason || 'connection closed'))\n\t\t})\n\n\t\tthis.ws.on('error', (err: unknown) => {\n\t\t\tthis.errorHandler?.(err instanceof Error ? err : new Error(String(err)))\n\t\t})\n\t}\n}\n","import type { KoraEventEmitter, Operation } from '@korajs/core'\nimport { SyncError, generateUUIDv7 } from '@korajs/core'\nimport { topologicalSort } from '@korajs/core/internal'\nimport type {\n\tHandshakeMessage,\n\tMessageSerializer,\n\tOperationBatchMessage,\n\tSyncMessage,\n\tWireFormat,\n} from '@korajs/sync'\nimport {\n\tNegotiatedMessageSerializer,\n\tversionVectorToWire,\n\twireToVersionVector,\n} from '@korajs/sync'\nimport type { ServerStore } from '../store/server-store'\nimport { operationMatchesScopes } from '../scopes/server-scope-filter'\nimport type { ServerTransport } from '../transport/server-transport'\nimport type { AuthContext, AuthProvider } from '../types'\n\nconst DEFAULT_BATCH_SIZE = 100\nconst DEFAULT_SCHEMA_VERSION = 1\n\n/**\n * Possible states for a client session.\n */\nexport type SessionState = 'connected' | 'authenticated' | 'syncing' | 'streaming' | 'closed'\n\n/**\n * Callback invoked when a session has new operations to relay to other sessions.\n */\nexport type RelayCallback = (sourceSessionId: string, operations: Operation[]) => void\n\n/**\n * Options for creating a ClientSession.\n */\nexport interface ClientSessionOptions {\n\t/** Unique session identifier */\n\tsessionId: string\n\t/** Transport for this client connection */\n\ttransport: ServerTransport\n\t/** Server-side operation store */\n\tstore: ServerStore\n\t/** Authentication provider (optional) */\n\tauth?: AuthProvider\n\t/** Message serializer */\n\tserializer?: MessageSerializer\n\t/** Event emitter for DevTools integration */\n\temitter?: KoraEventEmitter\n\t/** Max operations per sync batch */\n\tbatchSize?: number\n\t/** Schema version the server expects */\n\tschemaVersion?: number\n\t/** Called when this session has operations to relay to other sessions */\n\tonRelay?: RelayCallback\n\t/** Called when this session closes */\n\tonClose?: (sessionId: string) => void\n}\n\n/**\n * Handles the sync protocol for a single connected client.\n *\n * Lifecycle: connected → (authenticated) → syncing → streaming → closed\n *\n * The session:\n * 1. Receives a handshake from the client\n * 2. Authenticates if an AuthProvider is configured\n * 3. Sends back a HandshakeResponse with the server's version vector\n * 4. Computes and sends the server's delta to the client (paginated)\n * 5. Processes incoming operation batches from the client\n * 6. Transitions to streaming for real-time bidirectional sync\n * 7. Relays new operations to other sessions via the RelayCallback\n */\nexport class ClientSession {\n\tprivate state: SessionState = 'connected'\n\tprivate clientNodeId: string | null = null\n\tprivate authContext: AuthContext | null = null\n\n\tprivate readonly sessionId: string\n\tprivate readonly transport: ServerTransport\n\tprivate readonly store: ServerStore\n\tprivate readonly auth: AuthProvider | null\n\tprivate readonly serializer: MessageSerializer\n\tprivate readonly emitter: KoraEventEmitter | null\n\tprivate readonly batchSize: number\n\tprivate readonly schemaVersion: number\n\tprivate readonly onRelay: RelayCallback | null\n\tprivate readonly onClose: ((sessionId: string) => void) | null\n\n\tconstructor(options: ClientSessionOptions) {\n\t\tthis.sessionId = options.sessionId\n\t\tthis.transport = options.transport\n\t\tthis.store = options.store\n\t\tthis.auth = options.auth ?? null\n\t\tthis.serializer = options.serializer ?? new NegotiatedMessageSerializer('json')\n\t\tthis.emitter = options.emitter ?? null\n\t\tthis.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE\n\t\tthis.schemaVersion = options.schemaVersion ?? DEFAULT_SCHEMA_VERSION\n\t\tthis.onRelay = options.onRelay ?? null\n\t\tthis.onClose = options.onClose ?? null\n\t}\n\n\t/**\n\t * Start handling messages from the client transport.\n\t */\n\tstart(): void {\n\t\tthis.transport.onMessage((msg) => this.handleMessage(msg))\n\t\tthis.transport.onClose((_code, _reason) => this.handleTransportClose())\n\t\tthis.transport.onError((_err) => {\n\t\t\t// Transport errors during active session cause close\n\t\t\tif (this.state !== 'closed') {\n\t\t\t\tthis.handleTransportClose()\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Relay operations from another session to this client.\n\t * Only relays if the session is in streaming state and transport is connected.\n\t */\n\trelayOperations(operations: Operation[]): void {\n\t\tif (this.state !== 'streaming' || !this.transport.isConnected()) return\n\t\tif (operations.length === 0) return\n\n\t\tconst visibleOperations = operations.filter((op) =>\n\t\t\toperationMatchesScopes(op, this.authContext?.scopes),\n\t\t)\n\t\tif (visibleOperations.length === 0) return\n\n\t\tconst serializedOps = visibleOperations.map((op) => this.serializer.encodeOperation(op))\n\t\tconst msg: SyncMessage = {\n\t\t\ttype: 'operation-batch',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\toperations: serializedOps,\n\t\t\tisFinal: true,\n\t\t\tbatchIndex: 0,\n\t\t}\n\t\tthis.transport.send(msg)\n\t}\n\n\t/**\n\t * Close this session.\n\t */\n\tclose(reason?: string): void {\n\t\tif (this.state === 'closed') return\n\t\tthis.state = 'closed'\n\n\t\tif (this.transport.isConnected()) {\n\t\t\tthis.transport.close(1000, reason ?? 'session closed')\n\t\t}\n\n\t\tthis.onClose?.(this.sessionId)\n\t}\n\n\t// --- Getters ---\n\n\tgetState(): SessionState {\n\t\treturn this.state\n\t}\n\n\tgetSessionId(): string {\n\t\treturn this.sessionId\n\t}\n\n\tgetClientNodeId(): string | null {\n\t\treturn this.clientNodeId\n\t}\n\n\tgetAuthContext(): AuthContext | null {\n\t\treturn this.authContext\n\t}\n\n\tisStreaming(): boolean {\n\t\treturn this.state === 'streaming'\n\t}\n\n\t// --- Private protocol handlers ---\n\n\tprivate handleMessage(message: SyncMessage): void {\n\t\tswitch (message.type) {\n\t\t\tcase 'handshake':\n\t\t\t\tthis.handleHandshake(message)\n\t\t\t\tbreak\n\t\t\tcase 'operation-batch':\n\t\t\t\tthis.handleOperationBatch(message)\n\t\t\t\tbreak\n\t\t\t// Acknowledgments from clients are noted but no action needed on server\n\t\t\tcase 'acknowledgment':\n\t\t\t\tbreak\n\t\t\tcase 'error':\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\tprivate async handleHandshake(msg: HandshakeMessage): Promise<void> {\n\t\t// Only accept handshake in 'connected' state (prevent duplicate handshakes)\n\t\tif (this.state !== 'connected') {\n\t\t\tthis.sendError('DUPLICATE_HANDSHAKE', 'Handshake already completed', false)\n\t\t\treturn\n\t\t}\n\n\t\tthis.clientNodeId = msg.nodeId\n\n\t\t// Authenticate if provider is configured\n\t\tif (this.auth) {\n\t\t\tconst token = msg.authToken ?? ''\n\t\t\tconst context = await this.auth.authenticate(token)\n\t\t\tif (!context) {\n\t\t\t\tthis.sendError('AUTH_FAILED', 'Authentication failed', false)\n\t\t\t\tthis.close('authentication failed')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.authContext = context\n\t\t\tthis.state = 'authenticated'\n\t\t}\n\n\t\t// Send handshake response with server's version vector\n\t\tconst serverVector = this.store.getVersionVector()\n\t\tconst selectedWireFormat = selectWireFormat(msg.supportedWireFormats)\n\t\tthis.setSerializerWireFormat(selectedWireFormat)\n\t\tconst response: SyncMessage = {\n\t\t\ttype: 'handshake-response',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\tnodeId: this.store.getNodeId(),\n\t\t\tversionVector: versionVectorToWire(serverVector),\n\t\t\tschemaVersion: this.schemaVersion,\n\t\t\taccepted: true,\n\t\t\tselectedWireFormat,\n\t\t}\n\t\tthis.transport.send(response)\n\n\t\tthis.emitter?.emit({ type: 'sync:connected', nodeId: msg.nodeId })\n\n\t\t// Transition to syncing and send delta\n\t\tthis.state = 'syncing'\n\t\tconst clientVector = wireToVersionVector(msg.versionVector)\n\t\tawait this.sendDelta(clientVector)\n\n\t\t// Transition to streaming after delta is sent\n\t\tthis.state = 'streaming'\n\t}\n\n\tprivate async handleOperationBatch(msg: OperationBatchMessage): Promise<void> {\n\t\tconst operations = msg.operations.map((s) => this.serializer.decodeOperation(s))\n\t\tconst applied: Operation[] = []\n\n\t\tfor (const op of operations) {\n\t\t\tif (!operationMatchesScopes(op, this.authContext?.scopes)) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst result = await this.store.applyRemoteOperation(op)\n\t\t\tif (result === 'applied') {\n\t\t\t\tapplied.push(op)\n\t\t\t}\n\t\t}\n\n\t\tif (operations.length > 0) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:received',\n\t\t\t\toperations,\n\t\t\t\tbatchSize: operations.length,\n\t\t\t})\n\t\t}\n\n\t\t// Send acknowledgment\n\t\tconst lastOp = operations[operations.length - 1]\n\t\tconst ack: SyncMessage = {\n\t\t\ttype: 'acknowledgment',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\tacknowledgedMessageId: msg.messageId,\n\t\t\tlastSequenceNumber: lastOp ? lastOp.sequenceNumber : 0,\n\t\t}\n\t\tthis.transport.send(ack)\n\n\t\t// Relay only newly applied operations to other sessions\n\t\tif (applied.length > 0) {\n\t\t\tthis.onRelay?.(this.sessionId, applied)\n\t\t}\n\t}\n\n\tprivate async sendDelta(clientVector: Map<string, number>): Promise<void> {\n\t\tconst serverVector = this.store.getVersionVector()\n\t\tconst missing: Operation[] = []\n\n\t\tfor (const [nodeId, serverSeq] of serverVector) {\n\t\t\tconst clientSeq = clientVector.get(nodeId) ?? 0\n\t\t\tif (serverSeq > clientSeq) {\n\t\t\t\tconst ops = await this.store.getOperationRange(nodeId, clientSeq + 1, serverSeq)\n\t\t\t\tconst visible = ops.filter((op) => operationMatchesScopes(op, this.authContext?.scopes))\n\t\t\t\tmissing.push(...visible)\n\t\t\t}\n\t\t}\n\n\t\tif (missing.length === 0) {\n\t\t\t// Send empty final batch to signal delta is complete\n\t\t\tconst emptyBatch: SyncMessage = {\n\t\t\t\ttype: 'operation-batch',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\toperations: [],\n\t\t\t\tisFinal: true,\n\t\t\t\tbatchIndex: 0,\n\t\t\t}\n\t\t\tthis.transport.send(emptyBatch)\n\t\t\treturn\n\t\t}\n\n\t\t// Sort causally and paginate\n\t\tconst sorted = topologicalSort(missing)\n\t\tconst totalBatches = Math.ceil(sorted.length / this.batchSize)\n\n\t\tfor (let i = 0; i < totalBatches; i++) {\n\t\t\tconst start = i * this.batchSize\n\t\t\tconst batchOps = sorted.slice(start, start + this.batchSize)\n\t\t\tconst serializedOps = batchOps.map((op) => this.serializer.encodeOperation(op))\n\n\t\t\tconst batchMsg: SyncMessage = {\n\t\t\t\ttype: 'operation-batch',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\toperations: serializedOps,\n\t\t\t\tisFinal: i === totalBatches - 1,\n\t\t\t\tbatchIndex: i,\n\t\t\t}\n\t\t\tthis.transport.send(batchMsg)\n\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:sent',\n\t\t\t\toperations: batchOps,\n\t\t\t\tbatchSize: batchOps.length,\n\t\t\t})\n\t\t}\n\t}\n\n\tprivate sendError(code: string, message: string, retriable: boolean): void {\n\t\tconst errorMsg: SyncMessage = {\n\t\t\ttype: 'error',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\tcode,\n\t\t\tmessage,\n\t\t\tretriable,\n\t\t}\n\t\tthis.transport.send(errorMsg)\n\t}\n\n\tprivate setSerializerWireFormat(format: WireFormat): void {\n\t\tif (typeof this.serializer.setWireFormat === 'function') {\n\t\t\tthis.serializer.setWireFormat(format)\n\t\t}\n\t}\n\n\tprivate handleTransportClose(): void {\n\t\tif (this.state === 'closed') return\n\t\tthis.state = 'closed'\n\t\tthis.emitter?.emit({ type: 'sync:disconnected', reason: 'transport closed' })\n\t\tthis.onClose?.(this.sessionId)\n\t}\n}\n\nfunction selectWireFormat(supportedWireFormats?: WireFormat[]): WireFormat {\n\tif (supportedWireFormats?.includes('protobuf')) {\n\t\treturn 'protobuf'\n\t}\n\n\treturn 'json'\n}\n","import type { Operation } from '@korajs/core'\n\n/**\n * Per-collection scope map from auth context.\n */\nexport type ScopeMap = Record<string, Record<string, unknown>>\n\n/**\n * Returns true if an operation is visible to a session based on its scopes.\n *\n * Rules:\n * - No scopes configured => visible\n * - Collection missing from scope map => hidden\n * - All scoped field/value pairs must match the operation snapshot\n */\nexport function operationMatchesScopes(op: Operation, scopes: ScopeMap | undefined): boolean {\n\tif (!scopes) return true\n\n\tconst collectionScope = scopes[op.collection]\n\tif (!collectionScope) return false\n\n\tconst snapshot = buildSnapshot(op)\n\tif (!snapshot) return false\n\n\tfor (const [field, expected] of Object.entries(collectionScope)) {\n\t\tif (snapshot[field] !== expected) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\nfunction buildSnapshot(op: Operation): Record<string, unknown> | null {\n\tconst previous = asRecord(op.previousData)\n\tconst next = asRecord(op.data)\n\n\tif (!previous && !next) return null\n\n\treturn {\n\t\t...(previous ?? {}),\n\t\t...(next ?? {}),\n\t}\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) {\n\t\treturn null\n\t}\n\n\treturn value as Record<string, unknown>\n}\n","import type { KoraEventEmitter, Operation } from '@korajs/core'\nimport { SyncError, generateUUIDv7 } from '@korajs/core'\nimport type { MessageSerializer } from '@korajs/sync'\nimport { JsonMessageSerializer } from '@korajs/sync'\nimport { ClientSession } from '../session/client-session'\nimport type { ServerStore } from '../store/server-store'\nimport { HttpServerTransport } from '../transport/http-server-transport'\nimport type { ServerTransport } from '../transport/server-transport'\nimport { WsServerTransport } from '../transport/ws-server-transport'\nimport type {\n\tAuthProvider,\n\tHttpSyncRequest,\n\tHttpSyncResponse,\n\tKoraSyncServerConfig,\n\tServerStatus,\n} from '../types'\n\nconst DEFAULT_MAX_CONNECTIONS = 0 // unlimited\nconst DEFAULT_BATCH_SIZE = 100\nconst DEFAULT_SCHEMA_VERSION = 1\nconst DEFAULT_HOST = '0.0.0.0'\nconst DEFAULT_PATH = '/'\n\n/**\n * Minimal interface for a ws.WebSocketServer instance.\n * Allows dependency injection for testing without importing ws directly.\n */\nexport interface WsServerLike {\n\ton(event: string, listener: (...args: unknown[]) => void): void\n\tclose(callback?: (err?: Error) => void): void\n\taddress(): { port: number } | string | null\n}\n\n/**\n * Constructor type for creating a WebSocket server.\n */\nexport type WsServerConstructor = new (options: {\n\tport?: number\n\thost?: string\n\tpath?: string\n}) => WsServerLike\n\n/**\n * Self-hosted sync server. Accepts WebSocket connections from clients,\n * handles the sync protocol, stores operations, and relays changes\n * between connected clients.\n *\n * Two modes of operation:\n * 1. **Standalone**: Call `start()` with a port — creates its own WebSocket server.\n * 2. **Attach**: Call `handleConnection(transport)` — attach to an existing HTTP server.\n */\nexport class KoraSyncServer {\n\tprivate readonly store: ServerStore\n\tprivate readonly auth: AuthProvider | null\n\tprivate readonly serializer: MessageSerializer\n\tprivate readonly emitter: KoraEventEmitter | null\n\tprivate readonly maxConnections: number\n\tprivate readonly batchSize: number\n\tprivate readonly schemaVersion: number\n\tprivate readonly port: number | undefined\n\tprivate readonly host: string\n\tprivate readonly path: string\n\n\tprivate readonly sessions = new Map<string, ClientSession>()\n\tprivate readonly httpClients = new Map<string, { sessionId: string; transport: HttpServerTransport }>()\n\tprivate readonly httpSessionToClient = new Map<string, string>()\n\tprivate wsServer: WsServerLike | null = null\n\tprivate running = false\n\n\tconstructor(config: KoraSyncServerConfig) {\n\t\tthis.store = config.store\n\t\tthis.auth = config.auth ?? null\n\t\tthis.serializer = config.serializer ?? new JsonMessageSerializer()\n\t\tthis.emitter = config.emitter ?? null\n\t\tthis.maxConnections = config.maxConnections ?? DEFAULT_MAX_CONNECTIONS\n\t\tthis.batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE\n\t\tthis.schemaVersion = config.schemaVersion ?? DEFAULT_SCHEMA_VERSION\n\t\tthis.port = config.port\n\t\tthis.host = config.host ?? DEFAULT_HOST\n\t\tthis.path = config.path ?? DEFAULT_PATH\n\t}\n\n\t/**\n\t * Start the WebSocket server in standalone mode.\n\t *\n\t * @param wsServerImpl - Optional WebSocket server constructor for testing\n\t */\n\tasync start(wsServerImpl?: WsServerConstructor): Promise<void> {\n\t\tif (this.running) {\n\t\t\tthrow new SyncError('Server is already running', { port: this.port })\n\t\t}\n\n\t\tif (!wsServerImpl && this.port === undefined) {\n\t\t\tthrow new SyncError(\n\t\t\t\t'Port is required for standalone mode. Provide port in config or use handleConnection() for attach mode.',\n\t\t\t\t{},\n\t\t\t)\n\t\t}\n\n\t\tif (wsServerImpl) {\n\t\t\tthis.wsServer = new wsServerImpl({\n\t\t\t\tport: this.port,\n\t\t\t\thost: this.host,\n\t\t\t\tpath: this.path,\n\t\t\t})\n\t\t} else {\n\t\t\t// Dynamic import of ws — only needed in standalone mode\n\t\t\tconst { WebSocketServer } = await import('ws')\n\t\t\tthis.wsServer = new WebSocketServer({\n\t\t\t\tport: this.port,\n\t\t\t\thost: this.host,\n\t\t\t\tpath: this.path,\n\t\t\t})\n\t\t}\n\n\t\tthis.wsServer.on('connection', (ws: unknown) => {\n\t\t\tconst transport = new WsServerTransport(\n\t\t\t\tws as import('../transport/ws-server-transport').WsWebSocket,\n\t\t\t\t{\n\t\t\t\t\tserializer: this.serializer,\n\t\t\t\t},\n\t\t\t)\n\t\t\tthis.handleConnection(transport)\n\t\t})\n\n\t\tthis.running = true\n\t}\n\n\t/**\n\t * Stop the server. Closes all sessions and the WebSocket server.\n\t */\n\tasync stop(): Promise<void> {\n\t\t// Close all active sessions (works in both standalone and attach mode)\n\t\tfor (const session of this.sessions.values()) {\n\t\t\tsession.close('server shutting down')\n\t\t}\n\t\tthis.sessions.clear()\n\t\tthis.httpClients.clear()\n\t\tthis.httpSessionToClient.clear()\n\n\t\t// Close WebSocket server (standalone mode only)\n\t\tif (this.wsServer) {\n\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\tthis.wsServer?.close(() => resolve())\n\t\t\t})\n\t\t\tthis.wsServer = null\n\t\t}\n\n\t\tthis.running = false\n\t}\n\n\t/**\n\t * Handle one HTTP sync request for a long-polling client.\n\t *\n\t * A stable `clientId` identifies the logical connection across requests.\n\t */\n\tasync handleHttpRequest(request: HttpSyncRequest): Promise<HttpSyncResponse> {\n\t\tif (!request.clientId || request.clientId.trim().length === 0) {\n\t\t\treturn { status: 400 }\n\t\t}\n\n\t\tconst client = this.getOrCreateHttpClient(request.clientId)\n\n\t\tif (request.method === 'POST') {\n\t\t\tif (request.body === undefined) {\n\t\t\t\treturn { status: 400 }\n\t\t\t}\n\n\t\t\tconst payload = normalizeHttpBody(request.body, request.contentType)\n\t\t\tclient.transport.receive(payload)\n\t\t\treturn { status: 202 }\n\t\t}\n\n\t\tif (request.method === 'GET') {\n\t\t\tconst polled = client.transport.poll(request.ifNoneMatch)\n\t\t\treturn {\n\t\t\t\tstatus: polled.status,\n\t\t\t\tbody: polled.body,\n\t\t\t\theaders: polled.headers,\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tstatus: 405,\n\t\t\theaders: { allow: 'GET, POST' },\n\t\t}\n\t}\n\n\t/**\n\t * Handle an incoming client connection (attach mode).\n\t * Creates a new ClientSession for the transport.\n\t *\n\t * @param transport - The server transport for the new connection\n\t * @returns The session ID\n\t */\n\thandleConnection(transport: ServerTransport): string {\n\t\t// Check max connections\n\t\tif (this.maxConnections > 0 && this.sessions.size >= this.maxConnections) {\n\t\t\ttransport.send({\n\t\t\t\ttype: 'error',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\tcode: 'MAX_CONNECTIONS',\n\t\t\t\tmessage: `Server has reached maximum connections (${this.maxConnections})`,\n\t\t\t\tretriable: true,\n\t\t\t})\n\t\t\ttransport.close(4029, 'max connections reached')\n\t\t\tthrow new SyncError('Maximum connections reached', {\n\t\t\t\tcurrent: this.sessions.size,\n\t\t\t\tmax: this.maxConnections,\n\t\t\t})\n\t\t}\n\n\t\tconst sessionId = generateUUIDv7()\n\n\t\tconst session = new ClientSession({\n\t\t\tsessionId,\n\t\t\ttransport,\n\t\t\tstore: this.store,\n\t\t\tauth: this.auth ?? undefined,\n\t\t\tserializer: this.serializer,\n\t\t\temitter: this.emitter ?? undefined,\n\t\t\tbatchSize: this.batchSize,\n\t\t\tschemaVersion: this.schemaVersion,\n\t\t\tonRelay: (sourceSessionId, operations) => {\n\t\t\t\tthis.handleRelay(sourceSessionId, operations)\n\t\t\t},\n\t\t\tonClose: (sid) => {\n\t\t\t\tthis.handleSessionClose(sid)\n\t\t\t},\n\t\t})\n\n\t\tthis.sessions.set(sessionId, session)\n\t\tsession.start()\n\n\t\treturn sessionId\n\t}\n\n\t/**\n\t * Get the current server status.\n\t */\n\tasync getStatus(): Promise<ServerStatus> {\n\t\treturn {\n\t\t\trunning: this.running,\n\t\t\tconnectedClients: this.sessions.size,\n\t\t\tport: this.port ?? null,\n\t\t\ttotalOperations: await this.store.getOperationCount(),\n\t\t}\n\t}\n\n\t/**\n\t * Get the number of currently connected clients.\n\t */\n\tgetConnectionCount(): number {\n\t\treturn this.sessions.size\n\t}\n\n\t// --- Private ---\n\n\tprivate handleRelay(sourceSessionId: string, operations: Operation[]): void {\n\t\tfor (const [sessionId, session] of this.sessions) {\n\t\t\tif (sessionId === sourceSessionId) continue\n\t\t\tsession.relayOperations(operations)\n\t\t}\n\t}\n\n\tprivate handleSessionClose(sessionId: string): void {\n\t\tthis.sessions.delete(sessionId)\n\n\t\tconst clientId = this.httpSessionToClient.get(sessionId)\n\t\tif (clientId) {\n\t\t\tthis.httpSessionToClient.delete(sessionId)\n\t\t\tthis.httpClients.delete(clientId)\n\t\t}\n\t}\n\n\tprivate getOrCreateHttpClient(clientId: string): { sessionId: string; transport: HttpServerTransport } {\n\t\tconst existing = this.httpClients.get(clientId)\n\t\tif (existing) {\n\t\t\treturn existing\n\t\t}\n\n\t\tconst transport = new HttpServerTransport(this.serializer)\n\t\tconst sessionId = this.handleConnection(transport)\n\t\tconst client = { sessionId, transport }\n\n\t\tthis.httpClients.set(clientId, client)\n\t\tthis.httpSessionToClient.set(sessionId, clientId)\n\n\t\treturn client\n\t}\n}\n\nfunction normalizeHttpBody(body: string | Uint8Array, contentType?: string): string | Uint8Array {\n\tif (body instanceof Uint8Array) {\n\t\treturn body\n\t}\n\n\tif (contentType?.includes('application/x-protobuf')) {\n\t\treturn new TextEncoder().encode(body)\n\t}\n\n\treturn body\n}\n","import type { AuthContext, AuthProvider } from '../types'\n\n/**\n * Auth provider that accepts all connections.\n * Returns a default anonymous context for any token.\n * Useful for development and testing.\n */\nexport class NoAuthProvider implements AuthProvider {\n\tasync authenticate(_token: string): Promise<AuthContext> {\n\t\treturn { userId: 'anonymous' }\n\t}\n}\n","import type { AuthContext, AuthProvider } from '../types'\n\n/**\n * Options for creating a TokenAuthProvider.\n */\nexport interface TokenAuthProviderOptions {\n\t/**\n\t * Validate a token and return an AuthContext if valid, or null if rejected.\n\t * This is where you implement your auth logic (JWT verification, database lookup, etc.).\n\t */\n\tvalidate: (token: string) => Promise<AuthContext | null>\n}\n\n/**\n * Token-based auth provider that delegates validation to a user-provided function.\n *\n * @example\n * ```typescript\n * const auth = new TokenAuthProvider({\n * validate: async (token) => {\n * const user = await verifyJWT(token)\n * return user ? { userId: user.id } : null\n * }\n * })\n * ```\n */\nexport class TokenAuthProvider implements AuthProvider {\n\tprivate readonly validate: (token: string) => Promise<AuthContext | null>\n\n\tconstructor(options: TokenAuthProviderOptions) {\n\t\tthis.validate = options.validate\n\t}\n\n\tasync authenticate(token: string): Promise<AuthContext | null> {\n\t\treturn this.validate(token)\n\t}\n}\n","import type { KoraSyncServerConfig } from '../types'\nimport { KoraSyncServer } from './kora-sync-server'\n\n/**\n * Factory function to create a KoraSyncServer.\n *\n * @param config - Server configuration\n * @returns A new KoraSyncServer instance\n *\n * @example\n * ```typescript\n * const server = createKoraServer({\n * store: new MemoryServerStore(),\n * port: 3000,\n * })\n * await server.start()\n * ```\n */\nexport function createKoraServer(config: KoraSyncServerConfig): KoraSyncServer {\n\treturn new KoraSyncServer(config)\n}\n","import type { ServerStore } from '../store/server-store'\nimport type { KoraSyncServerConfig } from '../types'\nimport { KoraSyncServer } from './kora-sync-server'\nimport { WsServerTransport } from '../transport/ws-server-transport'\n\n/**\n * Configuration for the production server that serves both\n * static files and WebSocket sync on a single port.\n */\nexport interface ProductionServerConfig {\n\t/** Server-side operation store */\n\tstore: ServerStore\n\t/** Port to listen on. Defaults to 3001 or process.env.PORT. */\n\tport?: number\n\t/** Directory containing built static files. Defaults to './dist'. */\n\tstaticDir?: string\n\t/** WebSocket sync path. Defaults to '/kora-sync'. */\n\tsyncPath?: string\n\t/** Additional KoraSyncServer options */\n\tsyncOptions?: Omit<KoraSyncServerConfig, 'store' | 'port' | 'host' | 'path'>\n}\n\n/**\n * A production server handle returned by createProductionServer.\n */\nexport interface ProductionServer {\n\t/** Start listening. Returns the URL the server is available at. */\n\tstart(): Promise<string>\n\t/** Stop the server gracefully. */\n\tstop(): Promise<void>\n}\n\n// MIME types for static file serving\nconst MIME_TYPES: Record<string, string> = {\n\t'.html': 'text/html',\n\t'.js': 'text/javascript',\n\t'.css': 'text/css',\n\t'.json': 'application/json',\n\t'.png': 'image/png',\n\t'.jpg': 'image/jpeg',\n\t'.svg': 'image/svg+xml',\n\t'.ico': 'image/x-icon',\n\t'.wasm': 'application/wasm',\n\t'.woff': 'font/woff',\n\t'.woff2': 'font/woff2',\n\t'.map': 'application/json',\n}\n\n/**\n * Creates a production server that serves both static files and WebSocket sync\n * on a single port. This is the recommended way to deploy a Kora app — one port\n * means one tunnel (ngrok, cloudflared) handles everything.\n *\n * @param config - Production server configuration\n * @returns A ProductionServer instance\n *\n * @example\n * ```typescript\n * import { createProductionServer, createSqliteServerStore } from '@korajs/server'\n *\n * const server = createProductionServer({\n * store: createSqliteServerStore({ filename: './kora-server.db' }),\n * })\n *\n * const url = await server.start()\n * console.log(`App running at ${url}`)\n * ```\n */\nexport function createProductionServer(config: ProductionServerConfig): ProductionServer {\n\tconst port = config.port ?? (Number(process.env.PORT) || 3001)\n\tconst staticDir = config.staticDir ?? './dist'\n\tconst syncPath = config.syncPath ?? '/kora-sync'\n\n\tconst syncServer = new KoraSyncServer({\n\t\tstore: config.store,\n\t\t...config.syncOptions,\n\t})\n\n\tlet httpServer: import('node:http').Server | null = null\n\n\treturn {\n\t\tasync start(): Promise<string> {\n\t\t\tconst { createServer } = await import('node:http')\n\t\t\tconst { createReadStream, existsSync, statSync } = await import('node:fs')\n\t\t\tconst { extname, join, resolve } = await import('node:path')\n\t\t\tconst { WebSocketServer } = await import('ws')\n\n\t\t\tconst distDir = resolve(staticDir)\n\n\t\t\thttpServer = createServer((req, res) => {\n\t\t\t\t// COOP/COEP headers required for SharedArrayBuffer (OPFS persistence)\n\t\t\t\tres.setHeader('Cross-Origin-Opener-Policy', 'same-origin')\n\t\t\t\tres.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')\n\n\t\t\t\tconst url = new URL(req.url || '/', `http://${req.headers.host}`)\n\t\t\t\tlet filePath = join(distDir, url.pathname)\n\n\t\t\t\t// SPA fallback: serve index.html for non-file routes\n\t\t\t\tif (!extname(filePath)) {\n\t\t\t\t\tconst indexPath = join(filePath, 'index.html')\n\t\t\t\t\tif (existsSync(indexPath)) {\n\t\t\t\t\t\tfilePath = indexPath\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfilePath = join(distDir, 'index.html')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!existsSync(filePath)) {\n\t\t\t\t\tfilePath = join(distDir, 'index.html')\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tconst stat = statSync(filePath)\n\t\t\t\t\tif (stat.isDirectory()) {\n\t\t\t\t\t\tfilePath = join(filePath, 'index.html')\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\tfilePath = join(distDir, 'index.html')\n\t\t\t\t}\n\n\t\t\t\tif (!existsSync(filePath)) {\n\t\t\t\t\tres.writeHead(404)\n\t\t\t\t\tres.end('Not Found')\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst ext = extname(filePath)\n\t\t\t\tconst contentType = MIME_TYPES[ext] || 'application/octet-stream'\n\t\t\t\tres.writeHead(200, { 'Content-Type': contentType })\n\t\t\t\tcreateReadStream(filePath).pipe(res)\n\t\t\t})\n\n\t\t\tconst wss = new WebSocketServer({ noServer: true })\n\n\t\t\thttpServer.on('upgrade', (req, socket, head) => {\n\t\t\t\tconst url = new URL(req.url || '/', `http://${req.headers.host}`)\n\t\t\t\tif (url.pathname === syncPath) {\n\t\t\t\t\twss.handleUpgrade(req, socket, head, (ws) => {\n\t\t\t\t\t\tconst transport = new WsServerTransport(ws)\n\t\t\t\t\t\tsyncServer.handleConnection(transport)\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tsocket.destroy()\n\t\t\t\t}\n\t\t\t})\n\n\t\t\treturn new Promise<string>((resolve) => {\n\t\t\t\thttpServer!.listen(port, '0.0.0.0', () => {\n\t\t\t\t\tresolve(`http://localhost:${port}`)\n\t\t\t\t})\n\t\t\t})\n\t\t},\n\n\t\tasync stop(): Promise<void> {\n\t\t\tawait syncServer.stop()\n\t\t\tif (httpServer) {\n\t\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\t\thttpServer!.close(() => resolve())\n\t\t\t\t})\n\t\t\t\thttpServer = null\n\t\t\t}\n\t\t},\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,IAAM,mBAAmB,MACvB,OAAO,aAAa,cAChB,IAAI,IAAI,QAAQ,UAAU,EAAE,EAAE,OAC7B,SAAS,iBAAiB,SAAS,cAAc,QAAQ,YAAY,MAAM,WAC1E,SAAS,cAAc,MACvB,IAAI,IAAI,WAAW,SAAS,OAAO,EAAE;AAEtC,IAAM,gBAAgC,iCAAiB;;;ACX9D,kBAA+B;AAQxB,IAAM,oBAAN,MAA+C;AAAA,EACpC;AAAA,EACA,aAA0B,CAAC;AAAA,EAC3B,iBAAiB,oBAAI,IAAuB;AAAA,EAC5C,gBAAqC,oBAAI,IAAI;AAAA,EACtD,SAAS;AAAA,EAEjB,YAAY,QAAiB;AAC5B,SAAK,SAAS,cAAU,4BAAe;AAAA,EACxC;AAAA,EAEA,mBAAkC;AACjC,WAAO,IAAI,IAAI,KAAK,aAAa;AAAA,EAClC;AAAA,EAEA,YAAoB;AACnB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,MAAM,qBAAqB,IAAqC;AAC/D,SAAK,WAAW;AAGhB,QAAI,KAAK,eAAe,IAAI,GAAG,EAAE,GAAG;AACnC,aAAO;AAAA,IACR;AAEA,SAAK,WAAW,KAAK,EAAE;AACvB,SAAK,eAAe,IAAI,GAAG,IAAI,EAAE;AAGjC,UAAM,aAAa,KAAK,cAAc,IAAI,GAAG,MAAM,KAAK;AACxD,QAAI,GAAG,iBAAiB,YAAY;AACnC,WAAK,cAAc,IAAI,GAAG,QAAQ,GAAG,cAAc;AAAA,IACpD;AAEA,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,kBAAkB,QAAgB,SAAiB,OAAqC;AAC7F,SAAK,WAAW;AAEhB,WAAO,KAAK,WACV;AAAA,MACA,CAAC,OAAO,GAAG,WAAW,UAAU,GAAG,kBAAkB,WAAW,GAAG,kBAAkB;AAAA,IACtF,EACC,KAAK,CAAC,GAAG,MAAM,EAAE,iBAAiB,EAAE,cAAc;AAAA,EACrD;AAAA,EAEA,MAAM,oBAAqC;AAC1C,SAAK,WAAW;AAChB,WAAO,KAAK,WAAW;AAAA,EACxB;AAAA,EAEA,MAAM,QAAuB;AAC5B,SAAK,SAAS;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,mBAAgC;AAC/B,WAAO,CAAC,GAAG,KAAK,UAAU;AAAA,EAC3B;AAAA,EAEQ,aAAmB;AAC1B,QAAI,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC9C;AAAA,EACD;AACD;;;AChFA,IAAAA,eAA+B;AAE/B,yBAAkD;;;ACHlD,qBAA8C;AAYvC,IAAM,mBAAe;AAAA,EAC3B;AAAA,EACA;AAAA,IACC,QAAI,qBAAK,IAAI,EAAE,WAAW;AAAA,IAC1B,YAAQ,qBAAK,SAAS,EAAE,QAAQ;AAAA,IAChC,UAAM,qBAAK,MAAM,EAAE,QAAQ;AAAA,IAC3B,gBAAY,qBAAK,YAAY,EAAE,QAAQ;AAAA,IACvC,cAAU,qBAAK,WAAW,EAAE,QAAQ;AAAA,IACpC,UAAM,qBAAK,MAAM;AAAA;AAAA,IACjB,kBAAc,qBAAK,eAAe;AAAA;AAAA,IAClC,cAAU,wBAAQ,WAAW,EAAE,QAAQ;AAAA,IACvC,aAAS,wBAAQ,SAAS,EAAE,QAAQ;AAAA,IACpC,qBAAiB,qBAAK,mBAAmB,EAAE,QAAQ;AAAA,IACnD,oBAAgB,wBAAQ,iBAAiB,EAAE,QAAQ;AAAA,IACnD,gBAAY,qBAAK,aAAa,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA;AAAA,IACtD,mBAAe,wBAAQ,gBAAgB,EAAE,QAAQ;AAAA,IACjD,gBAAY,wBAAQ,aAAa,EAAE,QAAQ;AAAA,EAC5C;AAAA,EACA,CAAC,WAAW;AAAA,IACX,gBAAY,sBAAM,iBAAiB,EAAE,GAAG,MAAM,QAAQ,MAAM,cAAc;AAAA,IAC1E,mBAAe,sBAAM,mBAAmB,EAAE,GAAG,MAAM,UAAU;AAAA,IAC7D,iBAAa,sBAAM,iBAAiB,EAAE,GAAG,MAAM,UAAU;AAAA,EAC1D;AACD;AAEO,IAAM,kBAAc,wBAAQ,cAAc;AAAA,EAChD,YAAQ,qBAAK,SAAS,EAAE,WAAW;AAAA,EACnC,uBAAmB,wBAAQ,qBAAqB,EAAE,QAAQ;AAAA,EAC1D,gBAAY,wBAAQ,cAAc,EAAE,QAAQ;AAC7C,CAAC;;;AD5BM,IAAM,sBAAN,MAAiD;AAAA,EACtC;AAAA,EACA;AAAA,EACA,gBAA+B,oBAAI,IAAI;AAAA,EACvC;AAAA,EACT,SAAS;AAAA,EAEjB,YAAY,IAAwB,QAAiB;AACpD,SAAK,KAAK;AACV,SAAK,SAAS,cAAU,6BAAe;AACvC,SAAK,QAAQ,KAAK,WAAW;AAAA,EAC9B;AAAA,EAEA,mBAAkC;AACjC,SAAK,WAAW;AAChB,WAAO,IAAI,IAAI,KAAK,aAAa;AAAA,EAClC;AAAA,EAEA,YAAoB;AACnB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,MAAM,qBAAqB,IAAqC;AAC/D,SAAK,WAAW;AAChB,UAAM,KAAK;AAGX,UAAM,WAAW,MAAM,KAAK,GAC1B,OAAO,EAAE,IAAI,aAAa,GAAG,CAAC,EAC9B,KAAK,YAAY,EACjB,UAAM,uBAAG,aAAa,IAAI,GAAG,EAAE,CAAC,EAChC,MAAM,CAAC;AAET,QAAI,SAAS,SAAS,GAAG;AACxB,aAAO;AAAA,IACR;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,MAAM,KAAK,mBAAmB,IAAI,GAAG;AAE3C,UAAM,KAAK,GAAG,YAAY,OAAO,OAAO;AAEvC,YAAM,GACJ,OAAO,YAAY,EACnB,OAAO,GAAG,EACV,oBAAoB,EAAE,QAAQ,aAAa,GAAG,CAAC;AAGjD,YAAM,GACJ,OAAO,WAAW,EAClB,OAAO;AAAA,QACP,QAAQ,GAAG;AAAA,QACX,mBAAmB,GAAG;AAAA,QACtB,YAAY;AAAA,MACb,CAAC,EACA,mBAAmB;AAAA,QACnB,QAAQ,YAAY;AAAA,QACpB,KAAK;AAAA,UACJ,mBAAmB,kCAAe,YAAY,iBAAiB,KAAK,GAAG,cAAc;AAAA,UACrF,YAAY,yBAAM,GAAG;AAAA,QACtB;AAAA,MACD,CAAC;AAAA,IACH,CAAC;AAGD,UAAM,aAAa,KAAK,cAAc,IAAI,GAAG,MAAM,KAAK;AACxD,QAAI,GAAG,iBAAiB,YAAY;AACnC,WAAK,cAAc,IAAI,GAAG,QAAQ,GAAG,cAAc;AAAA,IACpD;AAEA,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,kBAAkB,QAAgB,SAAiB,OAAqC;AAC7F,SAAK,WAAW;AAChB,UAAM,KAAK;AAEX,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO,EACP,KAAK,YAAY,EACjB;AAAA,UACA;AAAA,YACC,uBAAG,aAAa,QAAQ,MAAM;AAAA,YAC9B,4BAAQ,aAAa,gBAAgB,SAAS,KAAK;AAAA,MACpD;AAAA,IACD,EACC,YAAQ,wBAAI,aAAa,cAAc,CAAC;AAE1C,WAAO,KAAK,IAAI,CAAC,QAAQ,KAAK,qBAAqB,GAAG,CAAC;AAAA,EACxD;AAAA,EAEA,MAAM,oBAAqC;AAC1C,SAAK,WAAW;AAChB,UAAM,KAAK;AAEX,UAAM,SAAS,MAAM,KAAK,GAAG,OAAO,EAAE,WAAO,0BAAM,EAAE,CAAC,EAAE,KAAK,YAAY;AACzE,WAAO,OAAO,CAAC,GAAG,SAAS;AAAA,EAC5B;AAAA,EAEA,MAAM,QAAuB;AAC5B,SAAK,SAAS;AAAA,EACf;AAAA,EAEA,MAAc,aAA4B;AACzC,UAAM,KAAK,aAAa;AAGxB,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO;AAAA,MACP,QAAQ,YAAY;AAAA,MACpB,mBAAmB,YAAY;AAAA,IAChC,CAAC,EACA,KAAK,WAAW;AAElB,eAAW,OAAO,MAAM;AACvB,WAAK,cAAc,IAAI,IAAI,QAAQ,IAAI,iBAAiB;AAAA,IACzD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,eAA8B;AAC3C,UAAM,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAiBrB;AAED,UAAM,KAAK,GAAG;AAAA,MACb;AAAA,IACD;AACA,UAAM,KAAK,GAAG;AAAA,MACb;AAAA,IACD;AACA,UAAM,KAAK,GAAG;AAAA,MACb;AAAA,IACD;AAEA,UAAM,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAMrB;AAAA,EACF;AAAA,EAEQ,mBACP,IACA,YACmC;AACnC,WAAO;AAAA,MACN,IAAI,GAAG;AAAA,MACP,QAAQ,GAAG;AAAA,MACX,MAAM,GAAG;AAAA,MACT,YAAY,GAAG;AAAA,MACf,UAAU,GAAG;AAAA,MACb,MAAM,GAAG,SAAS,OAAO,KAAK,UAAU,GAAG,IAAI,IAAI;AAAA,MACnD,cAAc,GAAG,iBAAiB,OAAO,KAAK,UAAU,GAAG,YAAY,IAAI;AAAA,MAC3E,UAAU,GAAG,UAAU;AAAA,MACvB,SAAS,GAAG,UAAU;AAAA,MACtB,iBAAiB,GAAG,UAAU;AAAA,MAC9B,gBAAgB,GAAG;AAAA,MACnB,YAAY,KAAK,UAAU,GAAG,UAAU;AAAA,MACxC,eAAe,GAAG;AAAA,MAClB;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,qBAAqB,KAAkD;AAC9E,WAAO;AAAA,MACN,IAAI,IAAI;AAAA,MACR,QAAQ,IAAI;AAAA,MACZ,MAAM,IAAI;AAAA,MACV,YAAY,IAAI;AAAA,MAChB,UAAU,IAAI;AAAA,MACd,MAAM,IAAI,SAAS,OAAO,KAAK,MAAM,IAAI,IAAI,IAAI;AAAA,MACjD,cAAc,IAAI,iBAAiB,OAAO,KAAK,MAAM,IAAI,YAAY,IAAI;AAAA,MACzE,WAAW;AAAA,QACV,UAAU,IAAI;AAAA,QACd,SAAS,IAAI;AAAA,QACb,QAAQ,IAAI;AAAA,MACb;AAAA,MACA,gBAAgB,IAAI;AAAA,MACpB,YAAY,KAAK,MAAM,IAAI,UAAU;AAAA,MACrC,eAAe,IAAI;AAAA,IACpB;AAAA,EACD;AAAA,EAEQ,aAAmB;AAC1B,QAAI,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,+BAA+B;AAAA,IAChD;AAAA,EACD;AACD;AASA,eAAsB,0BAA0B,SAGf;AAChC,QAAM,EAAE,gBAAgB,UAAU,IAAI,MAAM,iBAAiB;AAC7D,QAAM,SAAS,eAAe,QAAQ,gBAAgB;AACtD,QAAM,KAAK,UAAU,MAAM;AAE3B,SAAO,IAAI,oBAAoB,IAAI,QAAQ,MAAM;AAClD;AAEA,eAAe,mBAGZ;AACF,MAAI;AACH,UAAM,gBAAgB,IAAI,SAAS,aAAa,0BAA0B;AAI1E,UAAM,cAAe,MAAM,cAAc,UAAU;AACnD,UAAM,aAAc,MAAM,cAAc,yBAAyB;AAIjE,WAAO;AAAA,MACN,gBAAgB,YAAY;AAAA,MAC5B,WAAW,WAAW;AAAA,IACvB;AAAA,EACD,QAAQ;AACP,UAAM,IAAI;AAAA,MACT;AAAA,IACD;AAAA,EACD;AACD;;;AEzQA,yBAA8B;AAE9B,IAAAC,eAA+B;AAE/B,IAAAC,sBAAkD;;;ACJlD,yBAAkD;AAU3C,IAAM,iBAAa;AAAA,EACzB;AAAA,EACA;AAAA,IACC,QAAI,yBAAK,IAAI,EAAE,WAAW;AAAA,IAC1B,YAAQ,yBAAK,SAAS,EAAE,QAAQ;AAAA,IAChC,UAAM,yBAAK,MAAM,EAAE,QAAQ;AAAA,IAC3B,gBAAY,yBAAK,YAAY,EAAE,QAAQ;AAAA,IACvC,cAAU,yBAAK,WAAW,EAAE,QAAQ;AAAA,IACpC,UAAM,yBAAK,MAAM;AAAA;AAAA,IACjB,kBAAc,yBAAK,eAAe;AAAA;AAAA,IAClC,cAAU,4BAAQ,WAAW,EAAE,QAAQ;AAAA,IACvC,aAAS,4BAAQ,SAAS,EAAE,QAAQ;AAAA,IACpC,qBAAiB,yBAAK,mBAAmB,EAAE,QAAQ;AAAA,IACnD,oBAAgB,4BAAQ,iBAAiB,EAAE,QAAQ;AAAA,IACnD,gBAAY,yBAAK,aAAa,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA;AAAA,IACtD,mBAAe,4BAAQ,gBAAgB,EAAE,QAAQ;AAAA,IACjD,gBAAY,4BAAQ,aAAa,EAAE,QAAQ;AAAA,EAC5C;AAAA,EACA,CAAC,WAAW;AAAA,IACX,gBAAY,0BAAM,cAAc,EAAE,GAAG,MAAM,QAAQ,MAAM,cAAc;AAAA,IACvE,mBAAe,0BAAM,gBAAgB,EAAE,GAAG,MAAM,UAAU;AAAA,IAC1D,iBAAa,0BAAM,cAAc,EAAE,GAAG,MAAM,UAAU;AAAA,EACvD;AACD;AAEO,IAAM,gBAAY,gCAAY,cAAc;AAAA,EAClD,YAAQ,yBAAK,SAAS,EAAE,WAAW;AAAA,EACnC,uBAAmB,4BAAQ,qBAAqB,EAAE,QAAQ;AAAA,EAC1D,gBAAY,4BAAQ,cAAc,EAAE,QAAQ;AAC7C,CAAC;;;AD3BD,IAAM,iBAAa,kCAAc,aAAe;AAOzC,IAAM,oBAAN,MAA+C;AAAA,EACpC;AAAA,EACA;AAAA,EACT,SAAS;AAAA,EAEjB,YAAY,IAA2B,QAAiB;AACvD,SAAK,KAAK;AACV,SAAK,SAAS,cAAU,6BAAe;AACvC,SAAK,aAAa;AAAA,EACnB;AAAA,EAEA,mBAAkC;AACjC,SAAK,WAAW;AAChB,UAAM,OAAO,KAAK,GAAG,OAAO,EAAE,KAAK,SAAS,EAAE,IAAI;AAClD,UAAM,KAAoB,oBAAI,IAAI;AAClC,eAAW,OAAO,MAAM;AACvB,SAAG,IAAI,IAAI,QAAQ,IAAI,iBAAiB;AAAA,IACzC;AACA,WAAO;AAAA,EACR;AAAA,EAEA,YAAoB;AACnB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,MAAM,qBAAqB,IAAqC;AAC/D,SAAK,WAAW;AAEhB,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,MAAM,KAAK,mBAAmB,IAAI,GAAG;AAG3C,UAAM,SAAS,KAAK,GAAG,YAAY,CAAC,OAAO;AAE1C,YAAM,eAAe,GACnB,OAAO,UAAU,EACjB,OAAO,GAAG,EACV,oBAAoB,EAAE,QAAQ,WAAW,GAAG,CAAC,EAC7C,IAAI;AAEN,UAAI,aAAa,YAAY,GAAG;AAC/B,eAAO;AAAA,MACR;AAGA,SAAG,OAAO,SAAS,EACjB,OAAO;AAAA,QACP,QAAQ,GAAG;AAAA,QACX,mBAAmB,GAAG;AAAA,QACtB,YAAY;AAAA,MACb,CAAC,EACA,mBAAmB;AAAA,QACnB,QAAQ,UAAU;AAAA,QAClB,KAAK;AAAA,UACJ,mBAAmB,8BAAU,UAAU,iBAAiB,KAAK,GAAG,cAAc;AAAA,UAC9E,YAAY,0BAAM,GAAG;AAAA,QACtB;AAAA,MACD,CAAC,EACA,IAAI;AAEN,aAAO;AAAA,IACR,CAAC;AAED,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,kBAAkB,QAAgB,SAAiB,OAAqC;AAC7F,SAAK,WAAW;AAEhB,UAAM,OAAO,KAAK,GAChB,OAAO,EACP,KAAK,UAAU,EACf,UAAM,6BAAI,wBAAG,WAAW,QAAQ,MAAM,OAAG,6BAAQ,WAAW,gBAAgB,SAAS,KAAK,CAAC,CAAC,EAC5F,YAAQ,yBAAI,WAAW,cAAc,CAAC,EACtC,IAAI;AAEN,WAAO,KAAK,IAAI,CAAC,QAAQ,KAAK,qBAAqB,GAAG,CAAC;AAAA,EACxD;AAAA,EAEA,MAAM,oBAAqC;AAC1C,SAAK,WAAW;AAEhB,UAAM,SAAS,KAAK,GAAG,OAAO,EAAE,WAAO,2BAAM,EAAE,CAAC,EAAE,KAAK,UAAU,EAAE,IAAI;AACvE,WAAO,OAAO,CAAC,GAAG,SAAS;AAAA,EAC5B;AAAA,EAEA,MAAM,QAAuB;AAC5B,SAAK,SAAS;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,eAAqB;AAC5B,SAAK,GAAG,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAiBX;AAED,SAAK,GAAG,IAAI;AAAA;AAAA,GAEX;AAED,SAAK,GAAG,IAAI;AAAA;AAAA,GAEX;AAED,SAAK,GAAG,IAAI;AAAA;AAAA,GAEX;AAED,SAAK,GAAG,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAMX;AAAA,EACF;AAAA,EAEQ,mBACP,IACA,YACiC;AACjC,WAAO;AAAA,MACN,IAAI,GAAG;AAAA,MACP,QAAQ,GAAG;AAAA,MACX,MAAM,GAAG;AAAA,MACT,YAAY,GAAG;AAAA,MACf,UAAU,GAAG;AAAA,MACb,MAAM,GAAG,SAAS,OAAO,KAAK,UAAU,GAAG,IAAI,IAAI;AAAA,MACnD,cAAc,GAAG,iBAAiB,OAAO,KAAK,UAAU,GAAG,YAAY,IAAI;AAAA,MAC3E,UAAU,GAAG,UAAU;AAAA,MACvB,SAAS,GAAG,UAAU;AAAA,MACtB,iBAAiB,GAAG,UAAU;AAAA,MAC9B,gBAAgB,GAAG;AAAA,MACnB,YAAY,KAAK,UAAU,GAAG,UAAU;AAAA,MACxC,eAAe,GAAG;AAAA,MAClB;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,qBAAqB,KAAgD;AAC5E,WAAO;AAAA,MACN,IAAI,IAAI;AAAA,MACR,QAAQ,IAAI;AAAA,MACZ,MAAM,IAAI;AAAA,MACV,YAAY,IAAI;AAAA,MAChB,UAAU,IAAI;AAAA,MACd,MAAM,IAAI,SAAS,OAAO,KAAK,MAAM,IAAI,IAAI,IAAI;AAAA,MACjD,cAAc,IAAI,iBAAiB,OAAO,KAAK,MAAM,IAAI,YAAY,IAAI;AAAA,MACzE,WAAW;AAAA,QACV,UAAU,IAAI;AAAA,QACd,SAAS,IAAI;AAAA,QACb,QAAQ,IAAI;AAAA,MACb;AAAA,MACA,gBAAgB,IAAI;AAAA,MACpB,YAAY,KAAK,MAAM,IAAI,UAAU;AAAA,MACrC,eAAe,IAAI;AAAA,IACpB;AAAA,EACD;AAAA,EAEQ,aAAmB;AAC1B,QAAI,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC9C;AAAA,EACD;AACD;AAmBO,SAAS,wBAAwB,SAGlB;AAGrB,QAAM,WAAW,WAAW,gBAAgB;AAC5C,QAAM,EAAE,QAAQ,IAAI,WAAW,4BAA4B;AAE3D,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,SAAS,IAAI,SAAS,QAAQ;AAGpC,SAAO,OAAO,oBAAoB;AAElC,QAAM,KAAK,QAAQ,MAAM;AACzB,SAAO,IAAI,kBAAkB,IAAI,QAAQ,MAAM;AAChD;;;AE7OA,IAAAC,eAA0B;AA2BnB,IAAM,sBAAN,MAAqD;AAAA,EAC1C;AAAA,EAET,iBAA8C;AAAA,EAC9C,eAA0C;AAAA,EAC1C,eAA0C;AAAA,EAE1C,YAAY;AAAA,EACZ,eAAe;AAAA,EACN,QAAyB,CAAC;AAAA,EAE3C,YAAY,YAA+B;AAC1C,SAAK,aAAa;AAAA,EACnB;AAAA,EAEA,KAAK,SAAmD;AACvD,QAAI,CAAC,KAAK,UAAW;AAErB,UAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAC9C,UAAM,WAAW,mBAAmB;AACpC,SAAK,MAAM,KAAK;AAAA,MACf,MAAM,KAAK,SAAS,KAAK,cAAc;AAAA,MACvC,aAAa,WAAW,2BAA2B;AAAA,MACnD,SAAS;AAAA,IACV,CAAC;AAAA,EACF;AAAA,EAEA,UAAU,SAAqC;AAC9C,SAAK,iBAAiB;AAAA,EACvB;AAAA,EAEA,QAAQ,SAAmC;AAC1C,SAAK,eAAe;AAAA,EACrB;AAAA,EAEA,QAAQ,SAAmC;AAC1C,SAAK,eAAe;AAAA,EACrB;AAAA,EAEA,cAAuB;AACtB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,MAAM,OAAO,KAAM,SAAS,oBAA0B;AACrD,QAAI,CAAC,KAAK,UAAW;AACrB,SAAK,YAAY;AACjB,SAAK,MAAM,SAAS;AACpB,SAAK,eAAe,MAAM,MAAM;AAAA,EACjC;AAAA,EAEA,QAAQ,SAAoC;AAC3C,QAAI,CAAC,KAAK,WAAW;AACpB,YAAM,IAAI,uBAAU,iCAAiC;AAAA,IACtD;AAEA,QAAI;AACH,YAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAC9C,WAAK,iBAAiB,OAAO;AAAA,IAC9B,SAAS,OAAO;AACf,WAAK,eAAe,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IAC9E;AAAA,EACD;AAAA,EAEA,KAAK,aAAwC;AAC5C,QAAI,CAAC,KAAK,WAAW;AACpB,aAAO,EAAE,QAAQ,IAAI;AAAA,IACtB;AAEA,UAAM,OAAO,KAAK,MAAM,CAAC;AACzB,QAAI,CAAC,MAAM;AACV,aAAO,EAAE,QAAQ,IAAI;AAAA,IACtB;AAEA,QAAI,eAAe,gBAAgB,KAAK,MAAM;AAC7C,aAAO;AAAA,QACN,QAAQ;AAAA,QACR,SAAS,EAAE,MAAM,KAAK,KAAK;AAAA,MAC5B;AAAA,IACD;AAEA,SAAK,MAAM,MAAM;AACjB,WAAO;AAAA,MACN,QAAQ;AAAA,MACR,MAAM,KAAK;AAAA,MACX,SAAS;AAAA,QACR,gBAAgB,KAAK;AAAA,QACrB,MAAM,KAAK;AAAA,MACZ;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,SAAS,UAA0B;AAC1C,WAAO,MAAM,QAAQ;AAAA,EACtB;AACD;;;ACzHA,IAAAC,eAA0B;AAE1B,kBAAsC;AAUtC,IAAM,UAAU;AA0BT,IAAM,oBAAN,MAAmD;AAAA,EACxC;AAAA,EACA;AAAA,EACT,iBAA8C;AAAA,EAC9C,eAA0C;AAAA,EAC1C,eAA0C;AAAA,EAElD,YAAY,IAAiB,SAAoC;AAChE,SAAK,KAAK;AACV,SAAK,aAAa,SAAS,cAAc,IAAI,kCAAsB;AACnE,SAAK,eAAe;AAAA,EACrB;AAAA,EAEA,KAAK,SAA4B;AAChC,QAAI,KAAK,GAAG,eAAe,SAAS;AACnC,YAAM,IAAI,uBAAU,8CAA8C;AAAA,QACjE,YAAY,KAAK,GAAG;AAAA,QACpB,aAAa,QAAQ;AAAA,MACtB,CAAC;AAAA,IACF;AAEA,UAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAC9C,SAAK,GAAG,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,UAAU,SAAqC;AAC9C,SAAK,iBAAiB;AAAA,EACvB;AAAA,EAEA,QAAQ,SAAmC;AAC1C,SAAK,eAAe;AAAA,EACrB;AAAA,EAEA,QAAQ,SAAmC;AAC1C,SAAK,eAAe;AAAA,EACrB;AAAA,EAEA,cAAuB;AACtB,WAAO,KAAK,GAAG,eAAe;AAAA,EAC/B;AAAA,EAEA,MAAM,MAAe,QAAuB;AAC3C,SAAK,GAAG,MAAM,QAAQ,KAAM,UAAU,gBAAgB;AAAA,EACvD;AAAA,EAEQ,iBAAuB;AAC9B,SAAK,GAAG,GAAG,WAAW,CAAC,SAAkB;AACxC,UAAI;AACH,YACC,OAAO,SAAS,YAChB,EAAE,gBAAgB,eAClB,EAAE,gBAAgB,cACjB;AACD,gBAAM,IAAI,uBAAU,sCAAsC;AAAA,YACzD,aAAa,OAAO;AAAA,UACrB,CAAC;AAAA,QACF;AAEA,cAAM,UAAU,KAAK,WAAW,OAAO,IAAI;AAC3C,aAAK,iBAAiB,OAAO;AAAA,MAC9B,SAAS,KAAK;AACb,aAAK,eAAe,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,MACxE;AAAA,IACD,CAAC;AAED,SAAK,GAAG,GAAG,SAAS,CAAC,MAAe,WAAoB;AACvD,WAAK,eAAe,OAAO,IAAI,KAAK,MAAM,OAAO,UAAU,mBAAmB,CAAC;AAAA,IAChF,CAAC;AAED,SAAK,GAAG,GAAG,SAAS,CAAC,QAAiB;AACrC,WAAK,eAAe,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,IACxE,CAAC;AAAA,EACF;AACD;;;AC9GA,IAAAC,eAA0C;AAC1C,sBAAgC;AAQhC,IAAAC,eAIO;;;ACCA,SAAS,uBAAuB,IAAe,QAAuC;AAC5F,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,kBAAkB,OAAO,GAAG,UAAU;AAC5C,MAAI,CAAC,gBAAiB,QAAO;AAE7B,QAAM,WAAW,cAAc,EAAE;AACjC,MAAI,CAAC,SAAU,QAAO;AAEtB,aAAW,CAAC,OAAO,QAAQ,KAAK,OAAO,QAAQ,eAAe,GAAG;AAChE,QAAI,SAAS,KAAK,MAAM,UAAU;AACjC,aAAO;AAAA,IACR;AAAA,EACD;AAEA,SAAO;AACR;AAEA,SAAS,cAAc,IAA+C;AACrE,QAAM,WAAW,SAAS,GAAG,YAAY;AACzC,QAAM,OAAO,SAAS,GAAG,IAAI;AAE7B,MAAI,CAAC,YAAY,CAAC,KAAM,QAAO;AAE/B,SAAO;AAAA,IACN,GAAI,YAAY,CAAC;AAAA,IACjB,GAAI,QAAQ,CAAC;AAAA,EACd;AACD;AAEA,SAAS,SAAS,OAAgD;AACjE,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG;AACxE,WAAO;AAAA,EACR;AAEA,SAAO;AACR;;;AD/BA,IAAM,qBAAqB;AAC3B,IAAM,yBAAyB;AAoDxB,IAAM,gBAAN,MAAoB;AAAA,EAClB,QAAsB;AAAA,EACtB,eAA8B;AAAA,EAC9B,cAAkC;AAAA,EAEzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAA+B;AAC1C,SAAK,YAAY,QAAQ;AACzB,SAAK,YAAY,QAAQ;AACzB,SAAK,QAAQ,QAAQ;AACrB,SAAK,OAAO,QAAQ,QAAQ;AAC5B,SAAK,aAAa,QAAQ,cAAc,IAAI,yCAA4B,MAAM;AAC9E,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,gBAAgB,QAAQ,iBAAiB;AAC9C,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,UAAU,QAAQ,WAAW;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACb,SAAK,UAAU,UAAU,CAAC,QAAQ,KAAK,cAAc,GAAG,CAAC;AACzD,SAAK,UAAU,QAAQ,CAAC,OAAO,YAAY,KAAK,qBAAqB,CAAC;AACtE,SAAK,UAAU,QAAQ,CAAC,SAAS;AAEhC,UAAI,KAAK,UAAU,UAAU;AAC5B,aAAK,qBAAqB;AAAA,MAC3B;AAAA,IACD,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAgBC,aAA+B;AAC9C,QAAI,KAAK,UAAU,eAAe,CAAC,KAAK,UAAU,YAAY,EAAG;AACjE,QAAIA,YAAW,WAAW,EAAG;AAE7B,UAAM,oBAAoBA,YAAW;AAAA,MAAO,CAAC,OAC5C,uBAAuB,IAAI,KAAK,aAAa,MAAM;AAAA,IACpD;AACA,QAAI,kBAAkB,WAAW,EAAG;AAEpC,UAAM,gBAAgB,kBAAkB,IAAI,CAAC,OAAO,KAAK,WAAW,gBAAgB,EAAE,CAAC;AACvF,UAAM,MAAmB;AAAA,MACxB,MAAM;AAAA,MACN,eAAW,6BAAe;AAAA,MAC1B,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,YAAY;AAAA,IACb;AACA,SAAK,UAAU,KAAK,GAAG;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC5B,QAAI,KAAK,UAAU,SAAU;AAC7B,SAAK,QAAQ;AAEb,QAAI,KAAK,UAAU,YAAY,GAAG;AACjC,WAAK,UAAU,MAAM,KAAM,UAAU,gBAAgB;AAAA,IACtD;AAEA,SAAK,UAAU,KAAK,SAAS;AAAA,EAC9B;AAAA;AAAA,EAIA,WAAyB;AACxB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,eAAuB;AACtB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,kBAAiC;AAChC,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,iBAAqC;AACpC,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,cAAuB;AACtB,WAAO,KAAK,UAAU;AAAA,EACvB;AAAA;AAAA,EAIQ,cAAc,SAA4B;AACjD,YAAQ,QAAQ,MAAM;AAAA,MACrB,KAAK;AACJ,aAAK,gBAAgB,OAAO;AAC5B;AAAA,MACD,KAAK;AACJ,aAAK,qBAAqB,OAAO;AACjC;AAAA;AAAA,MAED,KAAK;AACJ;AAAA,MACD,KAAK;AACJ;AAAA,IACF;AAAA,EACD;AAAA,EAEA,MAAc,gBAAgB,KAAsC;AAEnE,QAAI,KAAK,UAAU,aAAa;AAC/B,WAAK,UAAU,uBAAuB,+BAA+B,KAAK;AAC1E;AAAA,IACD;AAEA,SAAK,eAAe,IAAI;AAGxB,QAAI,KAAK,MAAM;AACd,YAAM,QAAQ,IAAI,aAAa;AAC/B,YAAM,UAAU,MAAM,KAAK,KAAK,aAAa,KAAK;AAClD,UAAI,CAAC,SAAS;AACb,aAAK,UAAU,eAAe,yBAAyB,KAAK;AAC5D,aAAK,MAAM,uBAAuB;AAClC;AAAA,MACD;AACA,WAAK,cAAc;AACnB,WAAK,QAAQ;AAAA,IACd;AAGA,UAAM,eAAe,KAAK,MAAM,iBAAiB;AACjD,UAAM,qBAAqB,iBAAiB,IAAI,oBAAoB;AACpE,SAAK,wBAAwB,kBAAkB;AAC/C,UAAM,WAAwB;AAAA,MAC7B,MAAM;AAAA,MACN,eAAW,6BAAe;AAAA,MAC1B,QAAQ,KAAK,MAAM,UAAU;AAAA,MAC7B,mBAAe,kCAAoB,YAAY;AAAA,MAC/C,eAAe,KAAK;AAAA,MACpB,UAAU;AAAA,MACV;AAAA,IACD;AACA,SAAK,UAAU,KAAK,QAAQ;AAE5B,SAAK,SAAS,KAAK,EAAE,MAAM,kBAAkB,QAAQ,IAAI,OAAO,CAAC;AAGjE,SAAK,QAAQ;AACb,UAAM,mBAAe,kCAAoB,IAAI,aAAa;AAC1D,UAAM,KAAK,UAAU,YAAY;AAGjC,SAAK,QAAQ;AAAA,EACd;AAAA,EAEA,MAAc,qBAAqB,KAA2C;AAC7E,UAAMA,cAAa,IAAI,WAAW,IAAI,CAAC,MAAM,KAAK,WAAW,gBAAgB,CAAC,CAAC;AAC/E,UAAM,UAAuB,CAAC;AAE9B,eAAW,MAAMA,aAAY;AAC5B,UAAI,CAAC,uBAAuB,IAAI,KAAK,aAAa,MAAM,GAAG;AAC1D;AAAA,MACD;AAEA,YAAM,SAAS,MAAM,KAAK,MAAM,qBAAqB,EAAE;AACvD,UAAI,WAAW,WAAW;AACzB,gBAAQ,KAAK,EAAE;AAAA,MAChB;AAAA,IACD;AAEA,QAAIA,YAAW,SAAS,GAAG;AAC1B,WAAK,SAAS,KAAK;AAAA,QAClB,MAAM;AAAA,QACN,YAAAA;AAAA,QACA,WAAWA,YAAW;AAAA,MACvB,CAAC;AAAA,IACF;AAGA,UAAM,SAASA,YAAWA,YAAW,SAAS,CAAC;AAC/C,UAAM,MAAmB;AAAA,MACxB,MAAM;AAAA,MACN,eAAW,6BAAe;AAAA,MAC1B,uBAAuB,IAAI;AAAA,MAC3B,oBAAoB,SAAS,OAAO,iBAAiB;AAAA,IACtD;AACA,SAAK,UAAU,KAAK,GAAG;AAGvB,QAAI,QAAQ,SAAS,GAAG;AACvB,WAAK,UAAU,KAAK,WAAW,OAAO;AAAA,IACvC;AAAA,EACD;AAAA,EAEA,MAAc,UAAU,cAAkD;AACzE,UAAM,eAAe,KAAK,MAAM,iBAAiB;AACjD,UAAM,UAAuB,CAAC;AAE9B,eAAW,CAAC,QAAQ,SAAS,KAAK,cAAc;AAC/C,YAAM,YAAY,aAAa,IAAI,MAAM,KAAK;AAC9C,UAAI,YAAY,WAAW;AAC1B,cAAM,MAAM,MAAM,KAAK,MAAM,kBAAkB,QAAQ,YAAY,GAAG,SAAS;AAC/E,cAAM,UAAU,IAAI,OAAO,CAAC,OAAO,uBAAuB,IAAI,KAAK,aAAa,MAAM,CAAC;AACvF,gBAAQ,KAAK,GAAG,OAAO;AAAA,MACxB;AAAA,IACD;AAEA,QAAI,QAAQ,WAAW,GAAG;AAEzB,YAAM,aAA0B;AAAA,QAC/B,MAAM;AAAA,QACN,eAAW,6BAAe;AAAA,QAC1B,YAAY,CAAC;AAAA,QACb,SAAS;AAAA,QACT,YAAY;AAAA,MACb;AACA,WAAK,UAAU,KAAK,UAAU;AAC9B;AAAA,IACD;AAGA,UAAM,aAAS,iCAAgB,OAAO;AACtC,UAAM,eAAe,KAAK,KAAK,OAAO,SAAS,KAAK,SAAS;AAE7D,aAAS,IAAI,GAAG,IAAI,cAAc,KAAK;AACtC,YAAM,QAAQ,IAAI,KAAK;AACvB,YAAM,WAAW,OAAO,MAAM,OAAO,QAAQ,KAAK,SAAS;AAC3D,YAAM,gBAAgB,SAAS,IAAI,CAAC,OAAO,KAAK,WAAW,gBAAgB,EAAE,CAAC;AAE9E,YAAM,WAAwB;AAAA,QAC7B,MAAM;AAAA,QACN,eAAW,6BAAe;AAAA,QAC1B,YAAY;AAAA,QACZ,SAAS,MAAM,eAAe;AAAA,QAC9B,YAAY;AAAA,MACb;AACA,WAAK,UAAU,KAAK,QAAQ;AAE5B,WAAK,SAAS,KAAK;AAAA,QAClB,MAAM;AAAA,QACN,YAAY;AAAA,QACZ,WAAW,SAAS;AAAA,MACrB,CAAC;AAAA,IACF;AAAA,EACD;AAAA,EAEQ,UAAU,MAAc,SAAiB,WAA0B;AAC1E,UAAM,WAAwB;AAAA,MAC7B,MAAM;AAAA,MACN,eAAW,6BAAe;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,IACD;AACA,SAAK,UAAU,KAAK,QAAQ;AAAA,EAC7B;AAAA,EAEQ,wBAAwB,QAA0B;AACzD,QAAI,OAAO,KAAK,WAAW,kBAAkB,YAAY;AACxD,WAAK,WAAW,cAAc,MAAM;AAAA,IACrC;AAAA,EACD;AAAA,EAEQ,uBAA6B;AACpC,QAAI,KAAK,UAAU,SAAU;AAC7B,SAAK,QAAQ;AACb,SAAK,SAAS,KAAK,EAAE,MAAM,qBAAqB,QAAQ,mBAAmB,CAAC;AAC5E,SAAK,UAAU,KAAK,SAAS;AAAA,EAC9B;AACD;AAEA,SAAS,iBAAiB,sBAAiD;AAC1E,MAAI,sBAAsB,SAAS,UAAU,GAAG;AAC/C,WAAO;AAAA,EACR;AAEA,SAAO;AACR;;;AE3WA,IAAAC,eAA0C;AAE1C,IAAAC,eAAsC;AActC,IAAM,0BAA0B;AAChC,IAAMC,sBAAqB;AAC3B,IAAMC,0BAAyB;AAC/B,IAAM,eAAe;AACrB,IAAM,eAAe;AA8Bd,IAAM,iBAAN,MAAqB;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,WAAW,oBAAI,IAA2B;AAAA,EAC1C,cAAc,oBAAI,IAAmE;AAAA,EACrF,sBAAsB,oBAAI,IAAoB;AAAA,EACvD,WAAgC;AAAA,EAChC,UAAU;AAAA,EAElB,YAAY,QAA8B;AACzC,SAAK,QAAQ,OAAO;AACpB,SAAK,OAAO,OAAO,QAAQ;AAC3B,SAAK,aAAa,OAAO,cAAc,IAAI,mCAAsB;AACjE,SAAK,UAAU,OAAO,WAAW;AACjC,SAAK,iBAAiB,OAAO,kBAAkB;AAC/C,SAAK,YAAY,OAAO,aAAaD;AACrC,SAAK,gBAAgB,OAAO,iBAAiBC;AAC7C,SAAK,OAAO,OAAO;AACnB,SAAK,OAAO,OAAO,QAAQ;AAC3B,SAAK,OAAO,OAAO,QAAQ;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,MAAM,cAAmD;AAC9D,QAAI,KAAK,SAAS;AACjB,YAAM,IAAI,uBAAU,6BAA6B,EAAE,MAAM,KAAK,KAAK,CAAC;AAAA,IACrE;AAEA,QAAI,CAAC,gBAAgB,KAAK,SAAS,QAAW;AAC7C,YAAM,IAAI;AAAA,QACT;AAAA,QACA,CAAC;AAAA,MACF;AAAA,IACD;AAEA,QAAI,cAAc;AACjB,WAAK,WAAW,IAAI,aAAa;AAAA,QAChC,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,MACZ,CAAC;AAAA,IACF,OAAO;AAEN,YAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,IAAI;AAC7C,WAAK,WAAW,IAAI,gBAAgB;AAAA,QACnC,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,MACZ,CAAC;AAAA,IACF;AAEA,SAAK,SAAS,GAAG,cAAc,CAAC,OAAgB;AAC/C,YAAM,YAAY,IAAI;AAAA,QACrB;AAAA,QACA;AAAA,UACC,YAAY,KAAK;AAAA,QAClB;AAAA,MACD;AACA,WAAK,iBAAiB,SAAS;AAAA,IAChC,CAAC;AAED,SAAK,UAAU;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAsB;AAE3B,eAAW,WAAW,KAAK,SAAS,OAAO,GAAG;AAC7C,cAAQ,MAAM,sBAAsB;AAAA,IACrC;AACA,SAAK,SAAS,MAAM;AACpB,SAAK,YAAY,MAAM;AACvB,SAAK,oBAAoB,MAAM;AAG/B,QAAI,KAAK,UAAU;AAClB,YAAM,IAAI,QAAc,CAAC,YAAY;AACpC,aAAK,UAAU,MAAM,MAAM,QAAQ,CAAC;AAAA,MACrC,CAAC;AACD,WAAK,WAAW;AAAA,IACjB;AAEA,SAAK,UAAU;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,kBAAkB,SAAqD;AAC5E,QAAI,CAAC,QAAQ,YAAY,QAAQ,SAAS,KAAK,EAAE,WAAW,GAAG;AAC9D,aAAO,EAAE,QAAQ,IAAI;AAAA,IACtB;AAEA,UAAM,SAAS,KAAK,sBAAsB,QAAQ,QAAQ;AAE1D,QAAI,QAAQ,WAAW,QAAQ;AAC9B,UAAI,QAAQ,SAAS,QAAW;AAC/B,eAAO,EAAE,QAAQ,IAAI;AAAA,MACtB;AAEA,YAAM,UAAU,kBAAkB,QAAQ,MAAM,QAAQ,WAAW;AACnE,aAAO,UAAU,QAAQ,OAAO;AAChC,aAAO,EAAE,QAAQ,IAAI;AAAA,IACtB;AAEA,QAAI,QAAQ,WAAW,OAAO;AAC7B,YAAM,SAAS,OAAO,UAAU,KAAK,QAAQ,WAAW;AACxD,aAAO;AAAA,QACN,QAAQ,OAAO;AAAA,QACf,MAAM,OAAO;AAAA,QACb,SAAS,OAAO;AAAA,MACjB;AAAA,IACD;AAEA,WAAO;AAAA,MACN,QAAQ;AAAA,MACR,SAAS,EAAE,OAAO,YAAY;AAAA,IAC/B;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,iBAAiB,WAAoC;AAEpD,QAAI,KAAK,iBAAiB,KAAK,KAAK,SAAS,QAAQ,KAAK,gBAAgB;AACzE,gBAAU,KAAK;AAAA,QACd,MAAM;AAAA,QACN,eAAW,6BAAe;AAAA,QAC1B,MAAM;AAAA,QACN,SAAS,2CAA2C,KAAK,cAAc;AAAA,QACvE,WAAW;AAAA,MACZ,CAAC;AACD,gBAAU,MAAM,MAAM,yBAAyB;AAC/C,YAAM,IAAI,uBAAU,+BAA+B;AAAA,QAClD,SAAS,KAAK,SAAS;AAAA,QACvB,KAAK,KAAK;AAAA,MACX,CAAC;AAAA,IACF;AAEA,UAAM,gBAAY,6BAAe;AAEjC,UAAM,UAAU,IAAI,cAAc;AAAA,MACjC;AAAA,MACA;AAAA,MACA,OAAO,KAAK;AAAA,MACZ,MAAM,KAAK,QAAQ;AAAA,MACnB,YAAY,KAAK;AAAA,MACjB,SAAS,KAAK,WAAW;AAAA,MACzB,WAAW,KAAK;AAAA,MAChB,eAAe,KAAK;AAAA,MACpB,SAAS,CAAC,iBAAiBC,gBAAe;AACzC,aAAK,YAAY,iBAAiBA,WAAU;AAAA,MAC7C;AAAA,MACA,SAAS,CAAC,QAAQ;AACjB,aAAK,mBAAmB,GAAG;AAAA,MAC5B;AAAA,IACD,CAAC;AAED,SAAK,SAAS,IAAI,WAAW,OAAO;AACpC,YAAQ,MAAM;AAEd,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAmC;AACxC,WAAO;AAAA,MACN,SAAS,KAAK;AAAA,MACd,kBAAkB,KAAK,SAAS;AAAA,MAChC,MAAM,KAAK,QAAQ;AAAA,MACnB,iBAAiB,MAAM,KAAK,MAAM,kBAAkB;AAAA,IACrD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,qBAA6B;AAC5B,WAAO,KAAK,SAAS;AAAA,EACtB;AAAA;AAAA,EAIQ,YAAY,iBAAyBA,aAA+B;AAC3E,eAAW,CAAC,WAAW,OAAO,KAAK,KAAK,UAAU;AACjD,UAAI,cAAc,gBAAiB;AACnC,cAAQ,gBAAgBA,WAAU;AAAA,IACnC;AAAA,EACD;AAAA,EAEQ,mBAAmB,WAAyB;AACnD,SAAK,SAAS,OAAO,SAAS;AAE9B,UAAM,WAAW,KAAK,oBAAoB,IAAI,SAAS;AACvD,QAAI,UAAU;AACb,WAAK,oBAAoB,OAAO,SAAS;AACzC,WAAK,YAAY,OAAO,QAAQ;AAAA,IACjC;AAAA,EACD;AAAA,EAEQ,sBAAsB,UAAyE;AACtG,UAAM,WAAW,KAAK,YAAY,IAAI,QAAQ;AAC9C,QAAI,UAAU;AACb,aAAO;AAAA,IACR;AAEA,UAAM,YAAY,IAAI,oBAAoB,KAAK,UAAU;AACzD,UAAM,YAAY,KAAK,iBAAiB,SAAS;AACjD,UAAM,SAAS,EAAE,WAAW,UAAU;AAEtC,SAAK,YAAY,IAAI,UAAU,MAAM;AACrC,SAAK,oBAAoB,IAAI,WAAW,QAAQ;AAEhD,WAAO;AAAA,EACR;AACD;AAEA,SAAS,kBAAkB,MAA2B,aAA2C;AAChG,MAAI,gBAAgB,YAAY;AAC/B,WAAO;AAAA,EACR;AAEA,MAAI,aAAa,SAAS,wBAAwB,GAAG;AACpD,WAAO,IAAI,YAAY,EAAE,OAAO,IAAI;AAAA,EACrC;AAEA,SAAO;AACR;;;ACvSO,IAAM,iBAAN,MAA6C;AAAA,EACnD,MAAM,aAAa,QAAsC;AACxD,WAAO,EAAE,QAAQ,YAAY;AAAA,EAC9B;AACD;;;ACeO,IAAM,oBAAN,MAAgD;AAAA,EACrC;AAAA,EAEjB,YAAY,SAAmC;AAC9C,SAAK,WAAW,QAAQ;AAAA,EACzB;AAAA,EAEA,MAAM,aAAa,OAA4C;AAC9D,WAAO,KAAK,SAAS,KAAK;AAAA,EAC3B;AACD;;;AClBO,SAAS,iBAAiB,QAA8C;AAC9E,SAAO,IAAI,eAAe,MAAM;AACjC;;;ACaA,IAAM,aAAqC;AAAA,EAC1C,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAQ;AACT;AAsBO,SAAS,uBAAuB,QAAkD;AACxF,QAAM,OAAO,OAAO,SAAS,OAAO,QAAQ,IAAI,IAAI,KAAK;AACzD,QAAM,YAAY,OAAO,aAAa;AACtC,QAAM,WAAW,OAAO,YAAY;AAEpC,QAAM,aAAa,IAAI,eAAe;AAAA,IACrC,OAAO,OAAO;AAAA,IACd,GAAG,OAAO;AAAA,EACX,CAAC;AAED,MAAI,aAAgD;AAEpD,SAAO;AAAA,IACN,MAAM,QAAyB;AAC9B,YAAM,EAAE,aAAa,IAAI,MAAM,OAAO,MAAW;AACjD,YAAM,EAAE,kBAAkB,YAAY,SAAS,IAAI,MAAM,OAAO,IAAS;AACzE,YAAM,EAAE,SAAS,MAAM,QAAQ,IAAI,MAAM,OAAO,MAAW;AAC3D,YAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,IAAI;AAE7C,YAAM,UAAU,QAAQ,SAAS;AAEjC,mBAAa,aAAa,CAAC,KAAK,QAAQ;AAEvC,YAAI,UAAU,8BAA8B,aAAa;AACzD,YAAI,UAAU,gCAAgC,cAAc;AAE5D,cAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ,IAAI,EAAE;AAChE,YAAI,WAAW,KAAK,SAAS,IAAI,QAAQ;AAGzC,YAAI,CAAC,QAAQ,QAAQ,GAAG;AACvB,gBAAM,YAAY,KAAK,UAAU,YAAY;AAC7C,cAAI,WAAW,SAAS,GAAG;AAC1B,uBAAW;AAAA,UACZ,OAAO;AACN,uBAAW,KAAK,SAAS,YAAY;AAAA,UACtC;AAAA,QACD;AAEA,YAAI,CAAC,WAAW,QAAQ,GAAG;AAC1B,qBAAW,KAAK,SAAS,YAAY;AAAA,QACtC;AAEA,YAAI;AACH,gBAAM,OAAO,SAAS,QAAQ;AAC9B,cAAI,KAAK,YAAY,GAAG;AACvB,uBAAW,KAAK,UAAU,YAAY;AAAA,UACvC;AAAA,QACD,QAAQ;AACP,qBAAW,KAAK,SAAS,YAAY;AAAA,QACtC;AAEA,YAAI,CAAC,WAAW,QAAQ,GAAG;AAC1B,cAAI,UAAU,GAAG;AACjB,cAAI,IAAI,WAAW;AACnB;AAAA,QACD;AAEA,cAAM,MAAM,QAAQ,QAAQ;AAC5B,cAAM,cAAc,WAAW,GAAG,KAAK;AACvC,YAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,yBAAiB,QAAQ,EAAE,KAAK,GAAG;AAAA,MACpC,CAAC;AAED,YAAM,MAAM,IAAI,gBAAgB,EAAE,UAAU,KAAK,CAAC;AAElD,iBAAW,GAAG,WAAW,CAAC,KAAK,QAAQ,SAAS;AAC/C,cAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ,IAAI,EAAE;AAChE,YAAI,IAAI,aAAa,UAAU;AAC9B,cAAI,cAAc,KAAK,QAAQ,MAAM,CAAC,OAAO;AAC5C,kBAAM,YAAY,IAAI,kBAAkB,EAAE;AAC1C,uBAAW,iBAAiB,SAAS;AAAA,UACtC,CAAC;AAAA,QACF,OAAO;AACN,iBAAO,QAAQ;AAAA,QAChB;AAAA,MACD,CAAC;AAED,aAAO,IAAI,QAAgB,CAACC,aAAY;AACvC,mBAAY,OAAO,MAAM,WAAW,MAAM;AACzC,UAAAA,SAAQ,oBAAoB,IAAI,EAAE;AAAA,QACnC,CAAC;AAAA,MACF,CAAC;AAAA,IACF;AAAA,IAEA,MAAM,OAAsB;AAC3B,YAAM,WAAW,KAAK;AACtB,UAAI,YAAY;AACf,cAAM,IAAI,QAAc,CAAC,YAAY;AACpC,qBAAY,MAAM,MAAM,QAAQ,CAAC;AAAA,QAClC,CAAC;AACD,qBAAa;AAAA,MACd;AAAA,IACD;AAAA,EACD;AACD;","names":["import_core","import_core","import_drizzle_orm","import_core","import_core","import_core","import_sync","operations","import_core","import_sync","DEFAULT_BATCH_SIZE","DEFAULT_SCHEMA_VERSION","operations","resolve"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -344,6 +344,53 @@ declare class KoraSyncServer {
|
|
|
344
344
|
private getOrCreateHttpClient;
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Configuration for the production server that serves both
|
|
349
|
+
* static files and WebSocket sync on a single port.
|
|
350
|
+
*/
|
|
351
|
+
interface ProductionServerConfig {
|
|
352
|
+
/** Server-side operation store */
|
|
353
|
+
store: ServerStore;
|
|
354
|
+
/** Port to listen on. Defaults to 3001 or process.env.PORT. */
|
|
355
|
+
port?: number;
|
|
356
|
+
/** Directory containing built static files. Defaults to './dist'. */
|
|
357
|
+
staticDir?: string;
|
|
358
|
+
/** WebSocket sync path. Defaults to '/kora-sync'. */
|
|
359
|
+
syncPath?: string;
|
|
360
|
+
/** Additional KoraSyncServer options */
|
|
361
|
+
syncOptions?: Omit<KoraSyncServerConfig, 'store' | 'port' | 'host' | 'path'>;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* A production server handle returned by createProductionServer.
|
|
365
|
+
*/
|
|
366
|
+
interface ProductionServer {
|
|
367
|
+
/** Start listening. Returns the URL the server is available at. */
|
|
368
|
+
start(): Promise<string>;
|
|
369
|
+
/** Stop the server gracefully. */
|
|
370
|
+
stop(): Promise<void>;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Creates a production server that serves both static files and WebSocket sync
|
|
374
|
+
* on a single port. This is the recommended way to deploy a Kora app — one port
|
|
375
|
+
* means one tunnel (ngrok, cloudflared) handles everything.
|
|
376
|
+
*
|
|
377
|
+
* @param config - Production server configuration
|
|
378
|
+
* @returns A ProductionServer instance
|
|
379
|
+
*
|
|
380
|
+
* @example
|
|
381
|
+
* ```typescript
|
|
382
|
+
* import { createProductionServer, createSqliteServerStore } from '@korajs/server'
|
|
383
|
+
*
|
|
384
|
+
* const server = createProductionServer({
|
|
385
|
+
* store: createSqliteServerStore({ filename: './kora-server.db' }),
|
|
386
|
+
* })
|
|
387
|
+
*
|
|
388
|
+
* const url = await server.start()
|
|
389
|
+
* console.log(`App running at ${url}`)
|
|
390
|
+
* ```
|
|
391
|
+
*/
|
|
392
|
+
declare function createProductionServer(config: ProductionServerConfig): ProductionServer;
|
|
393
|
+
|
|
347
394
|
/**
|
|
348
395
|
* Options for creating a TokenAuthProvider.
|
|
349
396
|
*/
|
|
@@ -510,4 +557,4 @@ declare class NoAuthProvider implements AuthProvider {
|
|
|
510
557
|
*/
|
|
511
558
|
declare function createKoraServer(config: KoraSyncServerConfig): KoraSyncServer;
|
|
512
559
|
|
|
513
|
-
export { type AuthContext, type AuthProvider, ClientSession, type ClientSessionOptions, type HttpPollResponse, HttpServerTransport, type HttpSyncRequest, type HttpSyncResponse, KoraSyncServer, type KoraSyncServerConfig, MemoryServerStore, NoAuthProvider, PostgresServerStore, type RelayCallback, ServerCloseHandler, ServerErrorHandler, ServerMessageHandler, type ServerStatus, type ServerStore, ServerTransport, type SessionState, SqliteServerStore, TokenAuthProvider, type TokenAuthProviderOptions, type WsServerConstructor, type WsServerLike, WsServerTransport, type WsServerTransportOptions, type WsWebSocket, createKoraServer, createPostgresServerStore, createSqliteServerStore };
|
|
560
|
+
export { type AuthContext, type AuthProvider, ClientSession, type ClientSessionOptions, type HttpPollResponse, HttpServerTransport, type HttpSyncRequest, type HttpSyncResponse, KoraSyncServer, type KoraSyncServerConfig, MemoryServerStore, NoAuthProvider, PostgresServerStore, type ProductionServer, type ProductionServerConfig, type RelayCallback, ServerCloseHandler, ServerErrorHandler, ServerMessageHandler, type ServerStatus, type ServerStore, ServerTransport, type SessionState, SqliteServerStore, TokenAuthProvider, type TokenAuthProviderOptions, type WsServerConstructor, type WsServerLike, WsServerTransport, type WsServerTransportOptions, type WsWebSocket, createKoraServer, createPostgresServerStore, createProductionServer, createSqliteServerStore };
|
package/dist/index.d.ts
CHANGED
|
@@ -344,6 +344,53 @@ declare class KoraSyncServer {
|
|
|
344
344
|
private getOrCreateHttpClient;
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Configuration for the production server that serves both
|
|
349
|
+
* static files and WebSocket sync on a single port.
|
|
350
|
+
*/
|
|
351
|
+
interface ProductionServerConfig {
|
|
352
|
+
/** Server-side operation store */
|
|
353
|
+
store: ServerStore;
|
|
354
|
+
/** Port to listen on. Defaults to 3001 or process.env.PORT. */
|
|
355
|
+
port?: number;
|
|
356
|
+
/** Directory containing built static files. Defaults to './dist'. */
|
|
357
|
+
staticDir?: string;
|
|
358
|
+
/** WebSocket sync path. Defaults to '/kora-sync'. */
|
|
359
|
+
syncPath?: string;
|
|
360
|
+
/** Additional KoraSyncServer options */
|
|
361
|
+
syncOptions?: Omit<KoraSyncServerConfig, 'store' | 'port' | 'host' | 'path'>;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* A production server handle returned by createProductionServer.
|
|
365
|
+
*/
|
|
366
|
+
interface ProductionServer {
|
|
367
|
+
/** Start listening. Returns the URL the server is available at. */
|
|
368
|
+
start(): Promise<string>;
|
|
369
|
+
/** Stop the server gracefully. */
|
|
370
|
+
stop(): Promise<void>;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Creates a production server that serves both static files and WebSocket sync
|
|
374
|
+
* on a single port. This is the recommended way to deploy a Kora app — one port
|
|
375
|
+
* means one tunnel (ngrok, cloudflared) handles everything.
|
|
376
|
+
*
|
|
377
|
+
* @param config - Production server configuration
|
|
378
|
+
* @returns A ProductionServer instance
|
|
379
|
+
*
|
|
380
|
+
* @example
|
|
381
|
+
* ```typescript
|
|
382
|
+
* import { createProductionServer, createSqliteServerStore } from '@korajs/server'
|
|
383
|
+
*
|
|
384
|
+
* const server = createProductionServer({
|
|
385
|
+
* store: createSqliteServerStore({ filename: './kora-server.db' }),
|
|
386
|
+
* })
|
|
387
|
+
*
|
|
388
|
+
* const url = await server.start()
|
|
389
|
+
* console.log(`App running at ${url}`)
|
|
390
|
+
* ```
|
|
391
|
+
*/
|
|
392
|
+
declare function createProductionServer(config: ProductionServerConfig): ProductionServer;
|
|
393
|
+
|
|
347
394
|
/**
|
|
348
395
|
* Options for creating a TokenAuthProvider.
|
|
349
396
|
*/
|
|
@@ -510,4 +557,4 @@ declare class NoAuthProvider implements AuthProvider {
|
|
|
510
557
|
*/
|
|
511
558
|
declare function createKoraServer(config: KoraSyncServerConfig): KoraSyncServer;
|
|
512
559
|
|
|
513
|
-
export { type AuthContext, type AuthProvider, ClientSession, type ClientSessionOptions, type HttpPollResponse, HttpServerTransport, type HttpSyncRequest, type HttpSyncResponse, KoraSyncServer, type KoraSyncServerConfig, MemoryServerStore, NoAuthProvider, PostgresServerStore, type RelayCallback, ServerCloseHandler, ServerErrorHandler, ServerMessageHandler, type ServerStatus, type ServerStore, ServerTransport, type SessionState, SqliteServerStore, TokenAuthProvider, type TokenAuthProviderOptions, type WsServerConstructor, type WsServerLike, WsServerTransport, type WsServerTransportOptions, type WsWebSocket, createKoraServer, createPostgresServerStore, createSqliteServerStore };
|
|
560
|
+
export { type AuthContext, type AuthProvider, ClientSession, type ClientSessionOptions, type HttpPollResponse, HttpServerTransport, type HttpSyncRequest, type HttpSyncResponse, KoraSyncServer, type KoraSyncServerConfig, MemoryServerStore, NoAuthProvider, PostgresServerStore, type ProductionServer, type ProductionServerConfig, type RelayCallback, ServerCloseHandler, ServerErrorHandler, ServerMessageHandler, type ServerStatus, type ServerStore, ServerTransport, type SessionState, SqliteServerStore, TokenAuthProvider, type TokenAuthProviderOptions, type WsServerConstructor, type WsServerLike, WsServerTransport, type WsServerTransportOptions, type WsWebSocket, createKoraServer, createPostgresServerStore, createProductionServer, createSqliteServerStore };
|
package/dist/index.js
CHANGED
|
@@ -1142,6 +1142,101 @@ var TokenAuthProvider = class {
|
|
|
1142
1142
|
function createKoraServer(config) {
|
|
1143
1143
|
return new KoraSyncServer(config);
|
|
1144
1144
|
}
|
|
1145
|
+
|
|
1146
|
+
// src/server/production-server.ts
|
|
1147
|
+
var MIME_TYPES = {
|
|
1148
|
+
".html": "text/html",
|
|
1149
|
+
".js": "text/javascript",
|
|
1150
|
+
".css": "text/css",
|
|
1151
|
+
".json": "application/json",
|
|
1152
|
+
".png": "image/png",
|
|
1153
|
+
".jpg": "image/jpeg",
|
|
1154
|
+
".svg": "image/svg+xml",
|
|
1155
|
+
".ico": "image/x-icon",
|
|
1156
|
+
".wasm": "application/wasm",
|
|
1157
|
+
".woff": "font/woff",
|
|
1158
|
+
".woff2": "font/woff2",
|
|
1159
|
+
".map": "application/json"
|
|
1160
|
+
};
|
|
1161
|
+
function createProductionServer(config) {
|
|
1162
|
+
const port = config.port ?? (Number(process.env.PORT) || 3001);
|
|
1163
|
+
const staticDir = config.staticDir ?? "./dist";
|
|
1164
|
+
const syncPath = config.syncPath ?? "/kora-sync";
|
|
1165
|
+
const syncServer = new KoraSyncServer({
|
|
1166
|
+
store: config.store,
|
|
1167
|
+
...config.syncOptions
|
|
1168
|
+
});
|
|
1169
|
+
let httpServer = null;
|
|
1170
|
+
return {
|
|
1171
|
+
async start() {
|
|
1172
|
+
const { createServer } = await import("http");
|
|
1173
|
+
const { createReadStream, existsSync, statSync } = await import("fs");
|
|
1174
|
+
const { extname, join, resolve } = await import("path");
|
|
1175
|
+
const { WebSocketServer } = await import("ws");
|
|
1176
|
+
const distDir = resolve(staticDir);
|
|
1177
|
+
httpServer = createServer((req, res) => {
|
|
1178
|
+
res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
|
|
1179
|
+
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
|
|
1180
|
+
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
1181
|
+
let filePath = join(distDir, url.pathname);
|
|
1182
|
+
if (!extname(filePath)) {
|
|
1183
|
+
const indexPath = join(filePath, "index.html");
|
|
1184
|
+
if (existsSync(indexPath)) {
|
|
1185
|
+
filePath = indexPath;
|
|
1186
|
+
} else {
|
|
1187
|
+
filePath = join(distDir, "index.html");
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
if (!existsSync(filePath)) {
|
|
1191
|
+
filePath = join(distDir, "index.html");
|
|
1192
|
+
}
|
|
1193
|
+
try {
|
|
1194
|
+
const stat = statSync(filePath);
|
|
1195
|
+
if (stat.isDirectory()) {
|
|
1196
|
+
filePath = join(filePath, "index.html");
|
|
1197
|
+
}
|
|
1198
|
+
} catch {
|
|
1199
|
+
filePath = join(distDir, "index.html");
|
|
1200
|
+
}
|
|
1201
|
+
if (!existsSync(filePath)) {
|
|
1202
|
+
res.writeHead(404);
|
|
1203
|
+
res.end("Not Found");
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
const ext = extname(filePath);
|
|
1207
|
+
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
1208
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
1209
|
+
createReadStream(filePath).pipe(res);
|
|
1210
|
+
});
|
|
1211
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
1212
|
+
httpServer.on("upgrade", (req, socket, head) => {
|
|
1213
|
+
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
1214
|
+
if (url.pathname === syncPath) {
|
|
1215
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
1216
|
+
const transport = new WsServerTransport(ws);
|
|
1217
|
+
syncServer.handleConnection(transport);
|
|
1218
|
+
});
|
|
1219
|
+
} else {
|
|
1220
|
+
socket.destroy();
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
1223
|
+
return new Promise((resolve2) => {
|
|
1224
|
+
httpServer.listen(port, "0.0.0.0", () => {
|
|
1225
|
+
resolve2(`http://localhost:${port}`);
|
|
1226
|
+
});
|
|
1227
|
+
});
|
|
1228
|
+
},
|
|
1229
|
+
async stop() {
|
|
1230
|
+
await syncServer.stop();
|
|
1231
|
+
if (httpServer) {
|
|
1232
|
+
await new Promise((resolve) => {
|
|
1233
|
+
httpServer.close(() => resolve());
|
|
1234
|
+
});
|
|
1235
|
+
httpServer = null;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1145
1240
|
export {
|
|
1146
1241
|
ClientSession,
|
|
1147
1242
|
HttpServerTransport,
|
|
@@ -1154,6 +1249,7 @@ export {
|
|
|
1154
1249
|
WsServerTransport,
|
|
1155
1250
|
createKoraServer,
|
|
1156
1251
|
createPostgresServerStore,
|
|
1252
|
+
createProductionServer,
|
|
1157
1253
|
createSqliteServerStore
|
|
1158
1254
|
};
|
|
1159
1255
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store/memory-server-store.ts","../src/store/postgres-server-store.ts","../src/store/drizzle-pg-schema.ts","../src/store/sqlite-server-store.ts","../src/store/drizzle-schema.ts","../src/transport/http-server-transport.ts","../src/transport/ws-server-transport.ts","../src/session/client-session.ts","../src/scopes/server-scope-filter.ts","../src/server/kora-sync-server.ts","../src/auth/no-auth.ts","../src/auth/token-auth.ts","../src/server/create-server.ts"],"sourcesContent":["import type { Operation, VersionVector } from '@korajs/core'\nimport { generateUUIDv7 } from '@korajs/core'\nimport type { ApplyResult } from '@korajs/sync'\nimport type { ServerStore } from './server-store'\n\n/**\n * In-memory server store for testing and quick prototyping.\n * Not suitable for production — data does not survive process restart.\n */\nexport class MemoryServerStore implements ServerStore {\n\tprivate readonly nodeId: string\n\tprivate readonly operations: Operation[] = []\n\tprivate readonly operationIndex = new Map<string, Operation>()\n\tprivate readonly versionVector: Map<string, number> = new Map()\n\tprivate closed = false\n\n\tconstructor(nodeId?: string) {\n\t\tthis.nodeId = nodeId ?? generateUUIDv7()\n\t}\n\n\tgetVersionVector(): VersionVector {\n\t\treturn new Map(this.versionVector)\n\t}\n\n\tgetNodeId(): string {\n\t\treturn this.nodeId\n\t}\n\n\tasync applyRemoteOperation(op: Operation): Promise<ApplyResult> {\n\t\tthis.assertOpen()\n\n\t\t// Content-addressed dedup: same id = same content\n\t\tif (this.operationIndex.has(op.id)) {\n\t\t\treturn 'duplicate'\n\t\t}\n\n\t\tthis.operations.push(op)\n\t\tthis.operationIndex.set(op.id, op)\n\n\t\t// Advance version vector\n\t\tconst currentSeq = this.versionVector.get(op.nodeId) ?? 0\n\t\tif (op.sequenceNumber > currentSeq) {\n\t\t\tthis.versionVector.set(op.nodeId, op.sequenceNumber)\n\t\t}\n\n\t\treturn 'applied'\n\t}\n\n\tasync getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]> {\n\t\tthis.assertOpen()\n\n\t\treturn this.operations\n\t\t\t.filter(\n\t\t\t\t(op) => op.nodeId === nodeId && op.sequenceNumber >= fromSeq && op.sequenceNumber <= toSeq,\n\t\t\t)\n\t\t\t.sort((a, b) => a.sequenceNumber - b.sequenceNumber)\n\t}\n\n\tasync getOperationCount(): Promise<number> {\n\t\tthis.assertOpen()\n\t\treturn this.operations.length\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.closed = true\n\t}\n\n\t// --- Testing helpers (not on interface) ---\n\n\t/**\n\t * Get all stored operations (for test assertions).\n\t */\n\tgetAllOperations(): Operation[] {\n\t\treturn [...this.operations]\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('MemoryServerStore is closed')\n\t\t}\n\t}\n}\n","import type { Operation, VersionVector } from '@korajs/core'\nimport { generateUUIDv7 } from '@korajs/core'\nimport type { ApplyResult } from '@korajs/sync'\nimport { and, asc, between, count, eq, sql } from 'drizzle-orm'\nimport type { PostgresJsDatabase } from 'drizzle-orm/postgres-js'\nimport { pgOperations, pgSyncState } from './drizzle-pg-schema'\nimport type { ServerStore } from './server-store'\n\n/**\n * PostgreSQL-backed server store using Drizzle ORM.\n * All reads and writes go through Drizzle's typed query builder.\n * DDL stays as raw SQL via Drizzle's sql template (standard practice without drizzle-kit).\n */\nexport class PostgresServerStore implements ServerStore {\n\tprivate readonly nodeId: string\n\tprivate readonly db: PostgresJsDatabase\n\tprivate readonly versionVector: VersionVector = new Map()\n\tprivate readonly ready: Promise<void>\n\tprivate closed = false\n\n\tconstructor(db: PostgresJsDatabase, nodeId?: string) {\n\t\tthis.db = db\n\t\tthis.nodeId = nodeId ?? generateUUIDv7()\n\t\tthis.ready = this.initialize()\n\t}\n\n\tgetVersionVector(): VersionVector {\n\t\tthis.assertOpen()\n\t\treturn new Map(this.versionVector)\n\t}\n\n\tgetNodeId(): string {\n\t\treturn this.nodeId\n\t}\n\n\tasync applyRemoteOperation(op: Operation): Promise<ApplyResult> {\n\t\tthis.assertOpen()\n\t\tawait this.ready\n\n\t\t// Content-addressed dedup check\n\t\tconst existing = await this.db\n\t\t\t.select({ id: pgOperations.id })\n\t\t\t.from(pgOperations)\n\t\t\t.where(eq(pgOperations.id, op.id))\n\t\t\t.limit(1)\n\n\t\tif (existing.length > 0) {\n\t\t\treturn 'duplicate'\n\t\t}\n\n\t\tconst now = Date.now()\n\t\tconst row = this.serializeOperation(op, now)\n\n\t\tawait this.db.transaction(async (tx) => {\n\t\t\t// Insert operation with dedup\n\t\t\tawait tx\n\t\t\t\t.insert(pgOperations)\n\t\t\t\t.values(row)\n\t\t\t\t.onConflictDoNothing({ target: pgOperations.id })\n\n\t\t\t// Upsert version vector: advance max sequence number with GREATEST\n\t\t\tawait tx\n\t\t\t\t.insert(pgSyncState)\n\t\t\t\t.values({\n\t\t\t\t\tnodeId: op.nodeId,\n\t\t\t\t\tmaxSequenceNumber: op.sequenceNumber,\n\t\t\t\t\tlastSeenAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoUpdate({\n\t\t\t\t\ttarget: pgSyncState.nodeId,\n\t\t\t\t\tset: {\n\t\t\t\t\t\tmaxSequenceNumber: sql`GREATEST(${pgSyncState.maxSequenceNumber}, ${op.sequenceNumber})`,\n\t\t\t\t\t\tlastSeenAt: sql`${now}`,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t})\n\n\t\t// Update in-memory version vector cache\n\t\tconst currentMax = this.versionVector.get(op.nodeId) ?? 0\n\t\tif (op.sequenceNumber > currentMax) {\n\t\t\tthis.versionVector.set(op.nodeId, op.sequenceNumber)\n\t\t}\n\n\t\treturn 'applied'\n\t}\n\n\tasync getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]> {\n\t\tthis.assertOpen()\n\t\tawait this.ready\n\n\t\tconst rows = await this.db\n\t\t\t.select()\n\t\t\t.from(pgOperations)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(pgOperations.nodeId, nodeId),\n\t\t\t\t\tbetween(pgOperations.sequenceNumber, fromSeq, toSeq),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.orderBy(asc(pgOperations.sequenceNumber))\n\n\t\treturn rows.map((row) => this.deserializeOperation(row))\n\t}\n\n\tasync getOperationCount(): Promise<number> {\n\t\tthis.assertOpen()\n\t\tawait this.ready\n\n\t\tconst result = await this.db.select({ value: count() }).from(pgOperations)\n\t\treturn result[0]?.value ?? 0\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.closed = true\n\t}\n\n\tprivate async initialize(): Promise<void> {\n\t\tawait this.ensureTables()\n\n\t\t// Hydrate in-memory version vector cache via Drizzle query\n\t\tconst rows = await this.db\n\t\t\t.select({\n\t\t\t\tnodeId: pgSyncState.nodeId,\n\t\t\t\tmaxSequenceNumber: pgSyncState.maxSequenceNumber,\n\t\t\t})\n\t\t\t.from(pgSyncState)\n\n\t\tfor (const row of rows) {\n\t\t\tthis.versionVector.set(row.nodeId, row.maxSequenceNumber)\n\t\t}\n\t}\n\n\t/**\n\t * Create tables if they don't exist.\n\t * Uses raw SQL via Drizzle's sql template — standard DDL practice without drizzle-kit.\n\t */\n\tprivate async ensureTables(): Promise<void> {\n\t\tawait this.db.execute(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS operations (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tnode_id TEXT NOT NULL,\n\t\t\t\ttype TEXT NOT NULL,\n\t\t\t\tcollection TEXT NOT NULL,\n\t\t\t\trecord_id TEXT NOT NULL,\n\t\t\t\tdata TEXT,\n\t\t\t\tprevious_data TEXT,\n\t\t\t\twall_time INTEGER NOT NULL,\n\t\t\t\tlogical INTEGER NOT NULL,\n\t\t\t\ttimestamp_node_id TEXT NOT NULL,\n\t\t\t\tsequence_number INTEGER NOT NULL,\n\t\t\t\tcausal_deps TEXT NOT NULL DEFAULT '[]',\n\t\t\t\tschema_version INTEGER NOT NULL,\n\t\t\t\treceived_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\n\t\tawait this.db.execute(\n\t\t\tsql`CREATE INDEX IF NOT EXISTS idx_node_seq ON operations (node_id, sequence_number)`,\n\t\t)\n\t\tawait this.db.execute(\n\t\t\tsql`CREATE INDEX IF NOT EXISTS idx_collection ON operations (collection)`,\n\t\t)\n\t\tawait this.db.execute(\n\t\t\tsql`CREATE INDEX IF NOT EXISTS idx_received ON operations (received_at)`,\n\t\t)\n\n\t\tawait this.db.execute(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS sync_state (\n\t\t\t\tnode_id TEXT PRIMARY KEY,\n\t\t\t\tmax_sequence_number INTEGER NOT NULL,\n\t\t\t\tlast_seen_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\t}\n\n\tprivate serializeOperation(\n\t\top: Operation,\n\t\treceivedAt: number,\n\t): typeof pgOperations.$inferInsert {\n\t\treturn {\n\t\t\tid: op.id,\n\t\t\tnodeId: op.nodeId,\n\t\t\ttype: op.type,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tdata: op.data !== null ? JSON.stringify(op.data) : null,\n\t\t\tpreviousData: op.previousData !== null ? JSON.stringify(op.previousData) : null,\n\t\t\twallTime: op.timestamp.wallTime,\n\t\t\tlogical: op.timestamp.logical,\n\t\t\ttimestampNodeId: op.timestamp.nodeId,\n\t\t\tsequenceNumber: op.sequenceNumber,\n\t\t\tcausalDeps: JSON.stringify(op.causalDeps),\n\t\t\tschemaVersion: op.schemaVersion,\n\t\t\treceivedAt,\n\t\t}\n\t}\n\n\tprivate deserializeOperation(row: typeof pgOperations.$inferSelect): Operation {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tnodeId: row.nodeId,\n\t\t\ttype: row.type as Operation['type'],\n\t\t\tcollection: row.collection,\n\t\t\trecordId: row.recordId,\n\t\t\tdata: row.data !== null ? JSON.parse(row.data) : null,\n\t\t\tpreviousData: row.previousData !== null ? JSON.parse(row.previousData) : null,\n\t\t\ttimestamp: {\n\t\t\t\twallTime: row.wallTime,\n\t\t\t\tlogical: row.logical,\n\t\t\t\tnodeId: row.timestampNodeId,\n\t\t\t},\n\t\t\tsequenceNumber: row.sequenceNumber,\n\t\t\tcausalDeps: JSON.parse(row.causalDeps),\n\t\t\tschemaVersion: row.schemaVersion,\n\t\t}\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('PostgresServerStore is closed')\n\t\t}\n\t}\n}\n\n/**\n * Creates a PostgresServerStore from a PostgreSQL connection string.\n *\n * Uses runtime dynamic imports so projects that do not use PostgreSQL\n * do not need to install `postgres`. Wraps the postgres client with\n * Drizzle ORM for typed query building.\n */\nexport async function createPostgresServerStore(options: {\n\tconnectionString: string\n\tnodeId?: string\n}): Promise<PostgresServerStore> {\n\tconst { postgresClient, drizzleFn } = await loadPostgresDeps()\n\tconst client = postgresClient(options.connectionString)\n\tconst db = drizzleFn(client)\n\n\treturn new PostgresServerStore(db, options.nodeId)\n}\n\nasync function loadPostgresDeps(): Promise<{\n\tpostgresClient: (connectionString: string) => unknown\n\tdrizzleFn: (client: unknown) => PostgresJsDatabase\n}> {\n\ttry {\n\t\tconst dynamicImport = new Function('specifier', 'return import(specifier)') as (\n\t\t\tspecifier: string,\n\t\t) => Promise<unknown>\n\n\t\tconst postgresMod = (await dynamicImport('postgres')) as { default: (cs: string) => unknown }\n\t\tconst drizzleMod = (await dynamicImport('drizzle-orm/postgres-js')) as {\n\t\t\tdrizzle: (client: unknown) => PostgresJsDatabase\n\t\t}\n\n\t\treturn {\n\t\t\tpostgresClient: postgresMod.default,\n\t\t\tdrizzleFn: drizzleMod.drizzle,\n\t\t}\n\t} catch {\n\t\tthrow new Error(\n\t\t\t'PostgreSQL backend requires the \"postgres\" package. Install it in your project dependencies.',\n\t\t)\n\t}\n}\n","import { index, integer, pgTable, text } from 'drizzle-orm/pg-core'\n\n/**\n * Drizzle schema for the Kora sync server's PostgreSQL database.\n *\n * Two tables:\n * - `pgOperations` — the append-only operation log (content-addressed by id)\n * - `pgSyncState` — tracks the max sequence number seen per node (version vector)\n *\n * Column structure mirrors the SQLite drizzle-schema.ts but uses pgTable.\n */\n\nexport const pgOperations = pgTable(\n\t'operations',\n\t{\n\t\tid: text('id').primaryKey(),\n\t\tnodeId: text('node_id').notNull(),\n\t\ttype: text('type').notNull(),\n\t\tcollection: text('collection').notNull(),\n\t\trecordId: text('record_id').notNull(),\n\t\tdata: text('data'), // JSON-serialized, null for deletes\n\t\tpreviousData: text('previous_data'), // JSON-serialized, null for insert/delete\n\t\twallTime: integer('wall_time').notNull(),\n\t\tlogical: integer('logical').notNull(),\n\t\ttimestampNodeId: text('timestamp_node_id').notNull(),\n\t\tsequenceNumber: integer('sequence_number').notNull(),\n\t\tcausalDeps: text('causal_deps').notNull().default('[]'), // JSON array of op IDs\n\t\tschemaVersion: integer('schema_version').notNull(),\n\t\treceivedAt: integer('received_at').notNull(),\n\t},\n\t(table) => ({\n\t\tnodeSeqIdx: index('idx_pg_node_seq').on(table.nodeId, table.sequenceNumber),\n\t\tcollectionIdx: index('idx_pg_collection').on(table.collection),\n\t\treceivedIdx: index('idx_pg_received').on(table.receivedAt),\n\t}),\n)\n\nexport const pgSyncState = pgTable('sync_state', {\n\tnodeId: text('node_id').primaryKey(),\n\tmaxSequenceNumber: integer('max_sequence_number').notNull(),\n\tlastSeenAt: integer('last_seen_at').notNull(),\n})\n","import { createRequire } from 'node:module'\nimport type { Operation, VersionVector } from '@korajs/core'\nimport { generateUUIDv7 } from '@korajs/core'\nimport type { ApplyResult } from '@korajs/sync'\nimport { and, asc, between, count, eq, sql } from 'drizzle-orm'\nimport type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'\nimport { operations, syncState } from './drizzle-schema'\nimport type { ServerStore } from './server-store'\n\n// better-sqlite3 is a native CJS addon that cannot be loaded via ESM import().\n// createRequire provides a CJS require() that works in both ESM and CJS contexts.\n// tsup's shims option ensures import.meta.url is available in CJS builds.\nconst esmRequire = createRequire(import.meta.url)\n\n/**\n * SQLite-backed server store using Drizzle ORM.\n * Persists operations and version vectors to a real database file,\n * surviving process restarts.\n */\nexport class SqliteServerStore implements ServerStore {\n\tprivate readonly nodeId: string\n\tprivate readonly db: BetterSQLite3Database\n\tprivate closed = false\n\n\tconstructor(db: BetterSQLite3Database, nodeId?: string) {\n\t\tthis.db = db\n\t\tthis.nodeId = nodeId ?? generateUUIDv7()\n\t\tthis.ensureTables()\n\t}\n\n\tgetVersionVector(): VersionVector {\n\t\tthis.assertOpen()\n\t\tconst rows = this.db.select().from(syncState).all()\n\t\tconst vv: VersionVector = new Map()\n\t\tfor (const row of rows) {\n\t\t\tvv.set(row.nodeId, row.maxSequenceNumber)\n\t\t}\n\t\treturn vv\n\t}\n\n\tgetNodeId(): string {\n\t\treturn this.nodeId\n\t}\n\n\tasync applyRemoteOperation(op: Operation): Promise<ApplyResult> {\n\t\tthis.assertOpen()\n\n\t\tconst now = Date.now()\n\t\tconst row = this.serializeOperation(op, now)\n\n\t\t// Use a transaction for atomicity: insert op + update version vector\n\t\tconst result = this.db.transaction((tx) => {\n\t\t\t// Content-addressed dedup via onConflictDoNothing\n\t\t\tconst insertResult = tx\n\t\t\t\t.insert(operations)\n\t\t\t\t.values(row)\n\t\t\t\t.onConflictDoNothing({ target: operations.id })\n\t\t\t\t.run()\n\n\t\t\tif (insertResult.changes === 0) {\n\t\t\t\treturn 'duplicate' as const\n\t\t\t}\n\n\t\t\t// Advance version vector: upsert with MAX to ensure monotonic progress\n\t\t\ttx.insert(syncState)\n\t\t\t\t.values({\n\t\t\t\t\tnodeId: op.nodeId,\n\t\t\t\t\tmaxSequenceNumber: op.sequenceNumber,\n\t\t\t\t\tlastSeenAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoUpdate({\n\t\t\t\t\ttarget: syncState.nodeId,\n\t\t\t\t\tset: {\n\t\t\t\t\t\tmaxSequenceNumber: sql`MAX(${syncState.maxSequenceNumber}, ${op.sequenceNumber})`,\n\t\t\t\t\t\tlastSeenAt: sql`${now}`,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\t.run()\n\n\t\t\treturn 'applied' as const\n\t\t})\n\n\t\treturn result\n\t}\n\n\tasync getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]> {\n\t\tthis.assertOpen()\n\n\t\tconst rows = this.db\n\t\t\t.select()\n\t\t\t.from(operations)\n\t\t\t.where(and(eq(operations.nodeId, nodeId), between(operations.sequenceNumber, fromSeq, toSeq)))\n\t\t\t.orderBy(asc(operations.sequenceNumber))\n\t\t\t.all()\n\n\t\treturn rows.map((row) => this.deserializeOperation(row))\n\t}\n\n\tasync getOperationCount(): Promise<number> {\n\t\tthis.assertOpen()\n\n\t\tconst result = this.db.select({ value: count() }).from(operations).all()\n\t\treturn result[0]?.value ?? 0\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.closed = true\n\t}\n\n\t/**\n\t * Create the operations and sync_state tables if they don't exist.\n\t * Uses raw SQL via Drizzle's sql template — standard practice for DDL without drizzle-kit.\n\t */\n\tprivate ensureTables(): void {\n\t\tthis.db.run(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS operations (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tnode_id TEXT NOT NULL,\n\t\t\t\ttype TEXT NOT NULL,\n\t\t\t\tcollection TEXT NOT NULL,\n\t\t\t\trecord_id TEXT NOT NULL,\n\t\t\t\tdata TEXT,\n\t\t\t\tprevious_data TEXT,\n\t\t\t\twall_time INTEGER NOT NULL,\n\t\t\t\tlogical INTEGER NOT NULL,\n\t\t\t\ttimestamp_node_id TEXT NOT NULL,\n\t\t\t\tsequence_number INTEGER NOT NULL,\n\t\t\t\tcausal_deps TEXT NOT NULL DEFAULT '[]',\n\t\t\t\tschema_version INTEGER NOT NULL,\n\t\t\t\treceived_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE INDEX IF NOT EXISTS idx_node_seq ON operations (node_id, sequence_number)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE INDEX IF NOT EXISTS idx_collection ON operations (collection)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE INDEX IF NOT EXISTS idx_received ON operations (received_at)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS sync_state (\n\t\t\t\tnode_id TEXT PRIMARY KEY,\n\t\t\t\tmax_sequence_number INTEGER NOT NULL,\n\t\t\t\tlast_seen_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\t}\n\n\tprivate serializeOperation(\n\t\top: Operation,\n\t\treceivedAt: number,\n\t): typeof operations.$inferInsert {\n\t\treturn {\n\t\t\tid: op.id,\n\t\t\tnodeId: op.nodeId,\n\t\t\ttype: op.type,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tdata: op.data !== null ? JSON.stringify(op.data) : null,\n\t\t\tpreviousData: op.previousData !== null ? JSON.stringify(op.previousData) : null,\n\t\t\twallTime: op.timestamp.wallTime,\n\t\t\tlogical: op.timestamp.logical,\n\t\t\ttimestampNodeId: op.timestamp.nodeId,\n\t\t\tsequenceNumber: op.sequenceNumber,\n\t\t\tcausalDeps: JSON.stringify(op.causalDeps),\n\t\t\tschemaVersion: op.schemaVersion,\n\t\t\treceivedAt,\n\t\t}\n\t}\n\n\tprivate deserializeOperation(row: typeof operations.$inferSelect): Operation {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tnodeId: row.nodeId,\n\t\t\ttype: row.type as Operation['type'],\n\t\t\tcollection: row.collection,\n\t\t\trecordId: row.recordId,\n\t\t\tdata: row.data !== null ? JSON.parse(row.data) : null,\n\t\t\tpreviousData: row.previousData !== null ? JSON.parse(row.previousData) : null,\n\t\t\ttimestamp: {\n\t\t\t\twallTime: row.wallTime,\n\t\t\t\tlogical: row.logical,\n\t\t\t\tnodeId: row.timestampNodeId,\n\t\t\t},\n\t\t\tsequenceNumber: row.sequenceNumber,\n\t\t\tcausalDeps: JSON.parse(row.causalDeps),\n\t\t\tschemaVersion: row.schemaVersion,\n\t\t}\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('SqliteServerStore is closed')\n\t\t}\n\t}\n}\n\n/**\n * Creates a SqliteServerStore with a file-backed or in-memory database.\n * Handles database creation, Drizzle wrapping, and table setup.\n *\n * @param options - Configuration options\n * @param options.filename - Path to SQLite database file. Defaults to ':memory:' for testing.\n * @param options.nodeId - Server node ID. Auto-generated if not provided.\n * @returns A ready-to-use SqliteServerStore\n *\n * @example\n * ```typescript\n * import { createSqliteServerStore } from '@korajs/server'\n *\n * const store = createSqliteServerStore({ filename: './kora-server.db' })\n * const server = createKoraServer({ store, port: 3001 })\n * ```\n */\nexport function createSqliteServerStore(options: {\n\tfilename?: string\n\tnodeId?: string\n}): SqliteServerStore {\n\t// better-sqlite3 is a native CJS addon — use esmRequire (from createRequire)\n\t// so this works in both ESM and CJS contexts.\n\tconst Database = esmRequire('better-sqlite3')\n\tconst { drizzle } = esmRequire('drizzle-orm/better-sqlite3')\n\n\tconst filename = options.filename ?? ':memory:'\n\tconst sqlite = new Database(filename)\n\n\t// Enable WAL mode for better concurrent read/write performance\n\tsqlite.pragma('journal_mode = WAL')\n\n\tconst db = drizzle(sqlite)\n\treturn new SqliteServerStore(db, options.nodeId)\n}\n","import { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'\n\n/**\n * Drizzle schema for the Kora sync server's SQLite database.\n *\n * Two tables:\n * - `operations` — the append-only operation log (content-addressed by id)\n * - `syncState` — tracks the max sequence number seen per node (version vector)\n */\n\nexport const operations = sqliteTable(\n\t'operations',\n\t{\n\t\tid: text('id').primaryKey(),\n\t\tnodeId: text('node_id').notNull(),\n\t\ttype: text('type').notNull(),\n\t\tcollection: text('collection').notNull(),\n\t\trecordId: text('record_id').notNull(),\n\t\tdata: text('data'), // JSON-serialized, null for deletes\n\t\tpreviousData: text('previous_data'), // JSON-serialized, null for insert/delete\n\t\twallTime: integer('wall_time').notNull(),\n\t\tlogical: integer('logical').notNull(),\n\t\ttimestampNodeId: text('timestamp_node_id').notNull(),\n\t\tsequenceNumber: integer('sequence_number').notNull(),\n\t\tcausalDeps: text('causal_deps').notNull().default('[]'), // JSON array of op IDs\n\t\tschemaVersion: integer('schema_version').notNull(),\n\t\treceivedAt: integer('received_at').notNull(),\n\t},\n\t(table) => ({\n\t\tnodeSeqIdx: index('idx_node_seq').on(table.nodeId, table.sequenceNumber),\n\t\tcollectionIdx: index('idx_collection').on(table.collection),\n\t\treceivedIdx: index('idx_received').on(table.receivedAt),\n\t}),\n)\n\nexport const syncState = sqliteTable('sync_state', {\n\tnodeId: text('node_id').primaryKey(),\n\tmaxSequenceNumber: integer('max_sequence_number').notNull(),\n\tlastSeenAt: integer('last_seen_at').notNull(),\n})\n","import { SyncError } from '@korajs/core'\nimport type { MessageSerializer } from '@korajs/sync'\nimport type {\n\tServerCloseHandler,\n\tServerErrorHandler,\n\tServerMessageHandler,\n\tServerTransport,\n} from './server-transport'\n\nexport interface HttpPollResponse {\n\tstatus: 200 | 204 | 304 | 410\n\tbody?: string | Uint8Array\n\theaders?: Record<string, string>\n}\n\ninterface QueuedMessage {\n\tetag: string\n\tcontentType: string\n\tpayload: string | Uint8Array\n}\n\n/**\n * Server-side transport for HTTP long-polling clients.\n *\n * Incoming client messages are pushed via POST, while outbound server\n * messages are pulled via GET long-poll requests.\n */\nexport class HttpServerTransport implements ServerTransport {\n\tprivate readonly serializer: MessageSerializer\n\n\tprivate messageHandler: ServerMessageHandler | null = null\n\tprivate closeHandler: ServerCloseHandler | null = null\n\tprivate errorHandler: ServerErrorHandler | null = null\n\n\tprivate connected = true\n\tprivate nextSequence = 1\n\tprivate readonly queue: QueuedMessage[] = []\n\n\tconstructor(serializer: MessageSerializer) {\n\t\tthis.serializer = serializer\n\t}\n\n\tsend(message: import('@korajs/sync').SyncMessage): void {\n\t\tif (!this.connected) return\n\n\t\tconst encoded = this.serializer.encode(message)\n\t\tconst isBinary = encoded instanceof Uint8Array\n\t\tthis.queue.push({\n\t\t\tetag: this.makeEtag(this.nextSequence++),\n\t\t\tcontentType: isBinary ? 'application/x-protobuf' : 'application/json',\n\t\t\tpayload: encoded,\n\t\t})\n\t}\n\n\tonMessage(handler: ServerMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t}\n\n\tonClose(handler: ServerCloseHandler): void {\n\t\tthis.closeHandler = handler\n\t}\n\n\tonError(handler: ServerErrorHandler): void {\n\t\tthis.errorHandler = handler\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.connected\n\t}\n\n\tclose(code = 1000, reason = 'transport closed'): void {\n\t\tif (!this.connected) return\n\t\tthis.connected = false\n\t\tthis.queue.length = 0\n\t\tthis.closeHandler?.(code, reason)\n\t}\n\n\treceive(payload: string | Uint8Array): void {\n\t\tif (!this.connected) {\n\t\t\tthrow new SyncError('HTTP server transport is closed')\n\t\t}\n\n\t\ttry {\n\t\t\tconst message = this.serializer.decode(payload)\n\t\t\tthis.messageHandler?.(message)\n\t\t} catch (error) {\n\t\t\tthis.errorHandler?.(error instanceof Error ? error : new Error(String(error)))\n\t\t}\n\t}\n\n\tpoll(ifNoneMatch?: string): HttpPollResponse {\n\t\tif (!this.connected) {\n\t\t\treturn { status: 410 }\n\t\t}\n\n\t\tconst next = this.queue[0]\n\t\tif (!next) {\n\t\t\treturn { status: 204 }\n\t\t}\n\n\t\tif (ifNoneMatch && ifNoneMatch === next.etag) {\n\t\t\treturn {\n\t\t\t\tstatus: 304,\n\t\t\t\theaders: { etag: next.etag },\n\t\t\t}\n\t\t}\n\n\t\tthis.queue.shift()\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\tbody: next.payload,\n\t\t\theaders: {\n\t\t\t\t'content-type': next.contentType,\n\t\t\t\tetag: next.etag,\n\t\t\t},\n\t\t}\n\t}\n\n\tprivate makeEtag(sequence: number): string {\n\t\treturn `W/\"${sequence}\"`\n\t}\n}\n","import { SyncError } from '@korajs/core'\nimport type { SyncMessage } from '@korajs/sync'\nimport { JsonMessageSerializer } from '@korajs/sync'\nimport type { MessageSerializer } from '@korajs/sync'\nimport type {\n\tServerCloseHandler,\n\tServerErrorHandler,\n\tServerMessageHandler,\n\tServerTransport,\n} from './server-transport'\n\n/** WebSocket ready states (mirrors ws constants) */\nconst WS_OPEN = 1\n\n/**\n * Minimal interface for a ws.WebSocket instance.\n * Allows dependency injection for testing without importing ws directly.\n */\nexport interface WsWebSocket {\n\treadyState: number\n\tsend(data: string | Uint8Array, callback?: (err?: Error) => void): void\n\tclose(code?: number, reason?: string): void\n\ton(event: string, listener: (...args: unknown[]) => void): void\n\tremoveAllListeners(): void\n}\n\n/**\n * Options for WsServerTransport.\n */\nexport interface WsServerTransportOptions {\n\t/** Message serializer. Defaults to JsonMessageSerializer. */\n\tserializer?: MessageSerializer\n}\n\n/**\n * Server-side transport wrapping a ws.WebSocket connection.\n * Created for each incoming client connection.\n */\nexport class WsServerTransport implements ServerTransport {\n\tprivate readonly ws: WsWebSocket\n\tprivate readonly serializer: MessageSerializer\n\tprivate messageHandler: ServerMessageHandler | null = null\n\tprivate closeHandler: ServerCloseHandler | null = null\n\tprivate errorHandler: ServerErrorHandler | null = null\n\n\tconstructor(ws: WsWebSocket, options?: WsServerTransportOptions) {\n\t\tthis.ws = ws\n\t\tthis.serializer = options?.serializer ?? new JsonMessageSerializer()\n\t\tthis.setupListeners()\n\t}\n\n\tsend(message: SyncMessage): void {\n\t\tif (this.ws.readyState !== WS_OPEN) {\n\t\t\tthrow new SyncError('Cannot send message: WebSocket is not open', {\n\t\t\t\treadyState: this.ws.readyState,\n\t\t\t\tmessageType: message.type,\n\t\t\t})\n\t\t}\n\n\t\tconst encoded = this.serializer.encode(message)\n\t\tthis.ws.send(encoded)\n\t}\n\n\tonMessage(handler: ServerMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t}\n\n\tonClose(handler: ServerCloseHandler): void {\n\t\tthis.closeHandler = handler\n\t}\n\n\tonError(handler: ServerErrorHandler): void {\n\t\tthis.errorHandler = handler\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.ws.readyState === WS_OPEN\n\t}\n\n\tclose(code?: number, reason?: string): void {\n\t\tthis.ws.close(code ?? 1000, reason ?? 'server closing')\n\t}\n\n\tprivate setupListeners(): void {\n\t\tthis.ws.on('message', (data: unknown) => {\n\t\t\ttry {\n\t\t\t\tif (\n\t\t\t\t\ttypeof data !== 'string' &&\n\t\t\t\t\t!(data instanceof Uint8Array) &&\n\t\t\t\t\t!(data instanceof ArrayBuffer)\n\t\t\t\t) {\n\t\t\t\t\tthrow new SyncError('Unsupported WebSocket payload type', {\n\t\t\t\t\t\tpayloadType: typeof data,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tconst decoded = this.serializer.decode(data)\n\t\t\t\tthis.messageHandler?.(decoded)\n\t\t\t} catch (err) {\n\t\t\t\tthis.errorHandler?.(err instanceof Error ? err : new Error(String(err)))\n\t\t\t}\n\t\t})\n\n\t\tthis.ws.on('close', (code: unknown, reason: unknown) => {\n\t\t\tthis.closeHandler?.(Number(code) || 1006, String(reason || 'connection closed'))\n\t\t})\n\n\t\tthis.ws.on('error', (err: unknown) => {\n\t\t\tthis.errorHandler?.(err instanceof Error ? err : new Error(String(err)))\n\t\t})\n\t}\n}\n","import type { KoraEventEmitter, Operation } from '@korajs/core'\nimport { SyncError, generateUUIDv7 } from '@korajs/core'\nimport { topologicalSort } from '@korajs/core/internal'\nimport type {\n\tHandshakeMessage,\n\tMessageSerializer,\n\tOperationBatchMessage,\n\tSyncMessage,\n\tWireFormat,\n} from '@korajs/sync'\nimport {\n\tNegotiatedMessageSerializer,\n\tversionVectorToWire,\n\twireToVersionVector,\n} from '@korajs/sync'\nimport type { ServerStore } from '../store/server-store'\nimport { operationMatchesScopes } from '../scopes/server-scope-filter'\nimport type { ServerTransport } from '../transport/server-transport'\nimport type { AuthContext, AuthProvider } from '../types'\n\nconst DEFAULT_BATCH_SIZE = 100\nconst DEFAULT_SCHEMA_VERSION = 1\n\n/**\n * Possible states for a client session.\n */\nexport type SessionState = 'connected' | 'authenticated' | 'syncing' | 'streaming' | 'closed'\n\n/**\n * Callback invoked when a session has new operations to relay to other sessions.\n */\nexport type RelayCallback = (sourceSessionId: string, operations: Operation[]) => void\n\n/**\n * Options for creating a ClientSession.\n */\nexport interface ClientSessionOptions {\n\t/** Unique session identifier */\n\tsessionId: string\n\t/** Transport for this client connection */\n\ttransport: ServerTransport\n\t/** Server-side operation store */\n\tstore: ServerStore\n\t/** Authentication provider (optional) */\n\tauth?: AuthProvider\n\t/** Message serializer */\n\tserializer?: MessageSerializer\n\t/** Event emitter for DevTools integration */\n\temitter?: KoraEventEmitter\n\t/** Max operations per sync batch */\n\tbatchSize?: number\n\t/** Schema version the server expects */\n\tschemaVersion?: number\n\t/** Called when this session has operations to relay to other sessions */\n\tonRelay?: RelayCallback\n\t/** Called when this session closes */\n\tonClose?: (sessionId: string) => void\n}\n\n/**\n * Handles the sync protocol for a single connected client.\n *\n * Lifecycle: connected → (authenticated) → syncing → streaming → closed\n *\n * The session:\n * 1. Receives a handshake from the client\n * 2. Authenticates if an AuthProvider is configured\n * 3. Sends back a HandshakeResponse with the server's version vector\n * 4. Computes and sends the server's delta to the client (paginated)\n * 5. Processes incoming operation batches from the client\n * 6. Transitions to streaming for real-time bidirectional sync\n * 7. Relays new operations to other sessions via the RelayCallback\n */\nexport class ClientSession {\n\tprivate state: SessionState = 'connected'\n\tprivate clientNodeId: string | null = null\n\tprivate authContext: AuthContext | null = null\n\n\tprivate readonly sessionId: string\n\tprivate readonly transport: ServerTransport\n\tprivate readonly store: ServerStore\n\tprivate readonly auth: AuthProvider | null\n\tprivate readonly serializer: MessageSerializer\n\tprivate readonly emitter: KoraEventEmitter | null\n\tprivate readonly batchSize: number\n\tprivate readonly schemaVersion: number\n\tprivate readonly onRelay: RelayCallback | null\n\tprivate readonly onClose: ((sessionId: string) => void) | null\n\n\tconstructor(options: ClientSessionOptions) {\n\t\tthis.sessionId = options.sessionId\n\t\tthis.transport = options.transport\n\t\tthis.store = options.store\n\t\tthis.auth = options.auth ?? null\n\t\tthis.serializer = options.serializer ?? new NegotiatedMessageSerializer('json')\n\t\tthis.emitter = options.emitter ?? null\n\t\tthis.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE\n\t\tthis.schemaVersion = options.schemaVersion ?? DEFAULT_SCHEMA_VERSION\n\t\tthis.onRelay = options.onRelay ?? null\n\t\tthis.onClose = options.onClose ?? null\n\t}\n\n\t/**\n\t * Start handling messages from the client transport.\n\t */\n\tstart(): void {\n\t\tthis.transport.onMessage((msg) => this.handleMessage(msg))\n\t\tthis.transport.onClose((_code, _reason) => this.handleTransportClose())\n\t\tthis.transport.onError((_err) => {\n\t\t\t// Transport errors during active session cause close\n\t\t\tif (this.state !== 'closed') {\n\t\t\t\tthis.handleTransportClose()\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Relay operations from another session to this client.\n\t * Only relays if the session is in streaming state and transport is connected.\n\t */\n\trelayOperations(operations: Operation[]): void {\n\t\tif (this.state !== 'streaming' || !this.transport.isConnected()) return\n\t\tif (operations.length === 0) return\n\n\t\tconst visibleOperations = operations.filter((op) =>\n\t\t\toperationMatchesScopes(op, this.authContext?.scopes),\n\t\t)\n\t\tif (visibleOperations.length === 0) return\n\n\t\tconst serializedOps = visibleOperations.map((op) => this.serializer.encodeOperation(op))\n\t\tconst msg: SyncMessage = {\n\t\t\ttype: 'operation-batch',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\toperations: serializedOps,\n\t\t\tisFinal: true,\n\t\t\tbatchIndex: 0,\n\t\t}\n\t\tthis.transport.send(msg)\n\t}\n\n\t/**\n\t * Close this session.\n\t */\n\tclose(reason?: string): void {\n\t\tif (this.state === 'closed') return\n\t\tthis.state = 'closed'\n\n\t\tif (this.transport.isConnected()) {\n\t\t\tthis.transport.close(1000, reason ?? 'session closed')\n\t\t}\n\n\t\tthis.onClose?.(this.sessionId)\n\t}\n\n\t// --- Getters ---\n\n\tgetState(): SessionState {\n\t\treturn this.state\n\t}\n\n\tgetSessionId(): string {\n\t\treturn this.sessionId\n\t}\n\n\tgetClientNodeId(): string | null {\n\t\treturn this.clientNodeId\n\t}\n\n\tgetAuthContext(): AuthContext | null {\n\t\treturn this.authContext\n\t}\n\n\tisStreaming(): boolean {\n\t\treturn this.state === 'streaming'\n\t}\n\n\t// --- Private protocol handlers ---\n\n\tprivate handleMessage(message: SyncMessage): void {\n\t\tswitch (message.type) {\n\t\t\tcase 'handshake':\n\t\t\t\tthis.handleHandshake(message)\n\t\t\t\tbreak\n\t\t\tcase 'operation-batch':\n\t\t\t\tthis.handleOperationBatch(message)\n\t\t\t\tbreak\n\t\t\t// Acknowledgments from clients are noted but no action needed on server\n\t\t\tcase 'acknowledgment':\n\t\t\t\tbreak\n\t\t\tcase 'error':\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\tprivate async handleHandshake(msg: HandshakeMessage): Promise<void> {\n\t\t// Only accept handshake in 'connected' state (prevent duplicate handshakes)\n\t\tif (this.state !== 'connected') {\n\t\t\tthis.sendError('DUPLICATE_HANDSHAKE', 'Handshake already completed', false)\n\t\t\treturn\n\t\t}\n\n\t\tthis.clientNodeId = msg.nodeId\n\n\t\t// Authenticate if provider is configured\n\t\tif (this.auth) {\n\t\t\tconst token = msg.authToken ?? ''\n\t\t\tconst context = await this.auth.authenticate(token)\n\t\t\tif (!context) {\n\t\t\t\tthis.sendError('AUTH_FAILED', 'Authentication failed', false)\n\t\t\t\tthis.close('authentication failed')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.authContext = context\n\t\t\tthis.state = 'authenticated'\n\t\t}\n\n\t\t// Send handshake response with server's version vector\n\t\tconst serverVector = this.store.getVersionVector()\n\t\tconst selectedWireFormat = selectWireFormat(msg.supportedWireFormats)\n\t\tthis.setSerializerWireFormat(selectedWireFormat)\n\t\tconst response: SyncMessage = {\n\t\t\ttype: 'handshake-response',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\tnodeId: this.store.getNodeId(),\n\t\t\tversionVector: versionVectorToWire(serverVector),\n\t\t\tschemaVersion: this.schemaVersion,\n\t\t\taccepted: true,\n\t\t\tselectedWireFormat,\n\t\t}\n\t\tthis.transport.send(response)\n\n\t\tthis.emitter?.emit({ type: 'sync:connected', nodeId: msg.nodeId })\n\n\t\t// Transition to syncing and send delta\n\t\tthis.state = 'syncing'\n\t\tconst clientVector = wireToVersionVector(msg.versionVector)\n\t\tawait this.sendDelta(clientVector)\n\n\t\t// Transition to streaming after delta is sent\n\t\tthis.state = 'streaming'\n\t}\n\n\tprivate async handleOperationBatch(msg: OperationBatchMessage): Promise<void> {\n\t\tconst operations = msg.operations.map((s) => this.serializer.decodeOperation(s))\n\t\tconst applied: Operation[] = []\n\n\t\tfor (const op of operations) {\n\t\t\tif (!operationMatchesScopes(op, this.authContext?.scopes)) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst result = await this.store.applyRemoteOperation(op)\n\t\t\tif (result === 'applied') {\n\t\t\t\tapplied.push(op)\n\t\t\t}\n\t\t}\n\n\t\tif (operations.length > 0) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:received',\n\t\t\t\toperations,\n\t\t\t\tbatchSize: operations.length,\n\t\t\t})\n\t\t}\n\n\t\t// Send acknowledgment\n\t\tconst lastOp = operations[operations.length - 1]\n\t\tconst ack: SyncMessage = {\n\t\t\ttype: 'acknowledgment',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\tacknowledgedMessageId: msg.messageId,\n\t\t\tlastSequenceNumber: lastOp ? lastOp.sequenceNumber : 0,\n\t\t}\n\t\tthis.transport.send(ack)\n\n\t\t// Relay only newly applied operations to other sessions\n\t\tif (applied.length > 0) {\n\t\t\tthis.onRelay?.(this.sessionId, applied)\n\t\t}\n\t}\n\n\tprivate async sendDelta(clientVector: Map<string, number>): Promise<void> {\n\t\tconst serverVector = this.store.getVersionVector()\n\t\tconst missing: Operation[] = []\n\n\t\tfor (const [nodeId, serverSeq] of serverVector) {\n\t\t\tconst clientSeq = clientVector.get(nodeId) ?? 0\n\t\t\tif (serverSeq > clientSeq) {\n\t\t\t\tconst ops = await this.store.getOperationRange(nodeId, clientSeq + 1, serverSeq)\n\t\t\t\tconst visible = ops.filter((op) => operationMatchesScopes(op, this.authContext?.scopes))\n\t\t\t\tmissing.push(...visible)\n\t\t\t}\n\t\t}\n\n\t\tif (missing.length === 0) {\n\t\t\t// Send empty final batch to signal delta is complete\n\t\t\tconst emptyBatch: SyncMessage = {\n\t\t\t\ttype: 'operation-batch',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\toperations: [],\n\t\t\t\tisFinal: true,\n\t\t\t\tbatchIndex: 0,\n\t\t\t}\n\t\t\tthis.transport.send(emptyBatch)\n\t\t\treturn\n\t\t}\n\n\t\t// Sort causally and paginate\n\t\tconst sorted = topologicalSort(missing)\n\t\tconst totalBatches = Math.ceil(sorted.length / this.batchSize)\n\n\t\tfor (let i = 0; i < totalBatches; i++) {\n\t\t\tconst start = i * this.batchSize\n\t\t\tconst batchOps = sorted.slice(start, start + this.batchSize)\n\t\t\tconst serializedOps = batchOps.map((op) => this.serializer.encodeOperation(op))\n\n\t\t\tconst batchMsg: SyncMessage = {\n\t\t\t\ttype: 'operation-batch',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\toperations: serializedOps,\n\t\t\t\tisFinal: i === totalBatches - 1,\n\t\t\t\tbatchIndex: i,\n\t\t\t}\n\t\t\tthis.transport.send(batchMsg)\n\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:sent',\n\t\t\t\toperations: batchOps,\n\t\t\t\tbatchSize: batchOps.length,\n\t\t\t})\n\t\t}\n\t}\n\n\tprivate sendError(code: string, message: string, retriable: boolean): void {\n\t\tconst errorMsg: SyncMessage = {\n\t\t\ttype: 'error',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\tcode,\n\t\t\tmessage,\n\t\t\tretriable,\n\t\t}\n\t\tthis.transport.send(errorMsg)\n\t}\n\n\tprivate setSerializerWireFormat(format: WireFormat): void {\n\t\tif (typeof this.serializer.setWireFormat === 'function') {\n\t\t\tthis.serializer.setWireFormat(format)\n\t\t}\n\t}\n\n\tprivate handleTransportClose(): void {\n\t\tif (this.state === 'closed') return\n\t\tthis.state = 'closed'\n\t\tthis.emitter?.emit({ type: 'sync:disconnected', reason: 'transport closed' })\n\t\tthis.onClose?.(this.sessionId)\n\t}\n}\n\nfunction selectWireFormat(supportedWireFormats?: WireFormat[]): WireFormat {\n\tif (supportedWireFormats?.includes('protobuf')) {\n\t\treturn 'protobuf'\n\t}\n\n\treturn 'json'\n}\n","import type { Operation } from '@korajs/core'\n\n/**\n * Per-collection scope map from auth context.\n */\nexport type ScopeMap = Record<string, Record<string, unknown>>\n\n/**\n * Returns true if an operation is visible to a session based on its scopes.\n *\n * Rules:\n * - No scopes configured => visible\n * - Collection missing from scope map => hidden\n * - All scoped field/value pairs must match the operation snapshot\n */\nexport function operationMatchesScopes(op: Operation, scopes: ScopeMap | undefined): boolean {\n\tif (!scopes) return true\n\n\tconst collectionScope = scopes[op.collection]\n\tif (!collectionScope) return false\n\n\tconst snapshot = buildSnapshot(op)\n\tif (!snapshot) return false\n\n\tfor (const [field, expected] of Object.entries(collectionScope)) {\n\t\tif (snapshot[field] !== expected) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\nfunction buildSnapshot(op: Operation): Record<string, unknown> | null {\n\tconst previous = asRecord(op.previousData)\n\tconst next = asRecord(op.data)\n\n\tif (!previous && !next) return null\n\n\treturn {\n\t\t...(previous ?? {}),\n\t\t...(next ?? {}),\n\t}\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) {\n\t\treturn null\n\t}\n\n\treturn value as Record<string, unknown>\n}\n","import type { KoraEventEmitter, Operation } from '@korajs/core'\nimport { SyncError, generateUUIDv7 } from '@korajs/core'\nimport type { MessageSerializer } from '@korajs/sync'\nimport { JsonMessageSerializer } from '@korajs/sync'\nimport { ClientSession } from '../session/client-session'\nimport type { ServerStore } from '../store/server-store'\nimport { HttpServerTransport } from '../transport/http-server-transport'\nimport type { ServerTransport } from '../transport/server-transport'\nimport { WsServerTransport } from '../transport/ws-server-transport'\nimport type {\n\tAuthProvider,\n\tHttpSyncRequest,\n\tHttpSyncResponse,\n\tKoraSyncServerConfig,\n\tServerStatus,\n} from '../types'\n\nconst DEFAULT_MAX_CONNECTIONS = 0 // unlimited\nconst DEFAULT_BATCH_SIZE = 100\nconst DEFAULT_SCHEMA_VERSION = 1\nconst DEFAULT_HOST = '0.0.0.0'\nconst DEFAULT_PATH = '/'\n\n/**\n * Minimal interface for a ws.WebSocketServer instance.\n * Allows dependency injection for testing without importing ws directly.\n */\nexport interface WsServerLike {\n\ton(event: string, listener: (...args: unknown[]) => void): void\n\tclose(callback?: (err?: Error) => void): void\n\taddress(): { port: number } | string | null\n}\n\n/**\n * Constructor type for creating a WebSocket server.\n */\nexport type WsServerConstructor = new (options: {\n\tport?: number\n\thost?: string\n\tpath?: string\n}) => WsServerLike\n\n/**\n * Self-hosted sync server. Accepts WebSocket connections from clients,\n * handles the sync protocol, stores operations, and relays changes\n * between connected clients.\n *\n * Two modes of operation:\n * 1. **Standalone**: Call `start()` with a port — creates its own WebSocket server.\n * 2. **Attach**: Call `handleConnection(transport)` — attach to an existing HTTP server.\n */\nexport class KoraSyncServer {\n\tprivate readonly store: ServerStore\n\tprivate readonly auth: AuthProvider | null\n\tprivate readonly serializer: MessageSerializer\n\tprivate readonly emitter: KoraEventEmitter | null\n\tprivate readonly maxConnections: number\n\tprivate readonly batchSize: number\n\tprivate readonly schemaVersion: number\n\tprivate readonly port: number | undefined\n\tprivate readonly host: string\n\tprivate readonly path: string\n\n\tprivate readonly sessions = new Map<string, ClientSession>()\n\tprivate readonly httpClients = new Map<string, { sessionId: string; transport: HttpServerTransport }>()\n\tprivate readonly httpSessionToClient = new Map<string, string>()\n\tprivate wsServer: WsServerLike | null = null\n\tprivate running = false\n\n\tconstructor(config: KoraSyncServerConfig) {\n\t\tthis.store = config.store\n\t\tthis.auth = config.auth ?? null\n\t\tthis.serializer = config.serializer ?? new JsonMessageSerializer()\n\t\tthis.emitter = config.emitter ?? null\n\t\tthis.maxConnections = config.maxConnections ?? DEFAULT_MAX_CONNECTIONS\n\t\tthis.batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE\n\t\tthis.schemaVersion = config.schemaVersion ?? DEFAULT_SCHEMA_VERSION\n\t\tthis.port = config.port\n\t\tthis.host = config.host ?? DEFAULT_HOST\n\t\tthis.path = config.path ?? DEFAULT_PATH\n\t}\n\n\t/**\n\t * Start the WebSocket server in standalone mode.\n\t *\n\t * @param wsServerImpl - Optional WebSocket server constructor for testing\n\t */\n\tasync start(wsServerImpl?: WsServerConstructor): Promise<void> {\n\t\tif (this.running) {\n\t\t\tthrow new SyncError('Server is already running', { port: this.port })\n\t\t}\n\n\t\tif (!wsServerImpl && this.port === undefined) {\n\t\t\tthrow new SyncError(\n\t\t\t\t'Port is required for standalone mode. Provide port in config or use handleConnection() for attach mode.',\n\t\t\t\t{},\n\t\t\t)\n\t\t}\n\n\t\tif (wsServerImpl) {\n\t\t\tthis.wsServer = new wsServerImpl({\n\t\t\t\tport: this.port,\n\t\t\t\thost: this.host,\n\t\t\t\tpath: this.path,\n\t\t\t})\n\t\t} else {\n\t\t\t// Dynamic import of ws — only needed in standalone mode\n\t\t\tconst { WebSocketServer } = await import('ws')\n\t\t\tthis.wsServer = new WebSocketServer({\n\t\t\t\tport: this.port,\n\t\t\t\thost: this.host,\n\t\t\t\tpath: this.path,\n\t\t\t})\n\t\t}\n\n\t\tthis.wsServer.on('connection', (ws: unknown) => {\n\t\t\tconst transport = new WsServerTransport(\n\t\t\t\tws as import('../transport/ws-server-transport').WsWebSocket,\n\t\t\t\t{\n\t\t\t\t\tserializer: this.serializer,\n\t\t\t\t},\n\t\t\t)\n\t\t\tthis.handleConnection(transport)\n\t\t})\n\n\t\tthis.running = true\n\t}\n\n\t/**\n\t * Stop the server. Closes all sessions and the WebSocket server.\n\t */\n\tasync stop(): Promise<void> {\n\t\t// Close all active sessions (works in both standalone and attach mode)\n\t\tfor (const session of this.sessions.values()) {\n\t\t\tsession.close('server shutting down')\n\t\t}\n\t\tthis.sessions.clear()\n\t\tthis.httpClients.clear()\n\t\tthis.httpSessionToClient.clear()\n\n\t\t// Close WebSocket server (standalone mode only)\n\t\tif (this.wsServer) {\n\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\tthis.wsServer?.close(() => resolve())\n\t\t\t})\n\t\t\tthis.wsServer = null\n\t\t}\n\n\t\tthis.running = false\n\t}\n\n\t/**\n\t * Handle one HTTP sync request for a long-polling client.\n\t *\n\t * A stable `clientId` identifies the logical connection across requests.\n\t */\n\tasync handleHttpRequest(request: HttpSyncRequest): Promise<HttpSyncResponse> {\n\t\tif (!request.clientId || request.clientId.trim().length === 0) {\n\t\t\treturn { status: 400 }\n\t\t}\n\n\t\tconst client = this.getOrCreateHttpClient(request.clientId)\n\n\t\tif (request.method === 'POST') {\n\t\t\tif (request.body === undefined) {\n\t\t\t\treturn { status: 400 }\n\t\t\t}\n\n\t\t\tconst payload = normalizeHttpBody(request.body, request.contentType)\n\t\t\tclient.transport.receive(payload)\n\t\t\treturn { status: 202 }\n\t\t}\n\n\t\tif (request.method === 'GET') {\n\t\t\tconst polled = client.transport.poll(request.ifNoneMatch)\n\t\t\treturn {\n\t\t\t\tstatus: polled.status,\n\t\t\t\tbody: polled.body,\n\t\t\t\theaders: polled.headers,\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tstatus: 405,\n\t\t\theaders: { allow: 'GET, POST' },\n\t\t}\n\t}\n\n\t/**\n\t * Handle an incoming client connection (attach mode).\n\t * Creates a new ClientSession for the transport.\n\t *\n\t * @param transport - The server transport for the new connection\n\t * @returns The session ID\n\t */\n\thandleConnection(transport: ServerTransport): string {\n\t\t// Check max connections\n\t\tif (this.maxConnections > 0 && this.sessions.size >= this.maxConnections) {\n\t\t\ttransport.send({\n\t\t\t\ttype: 'error',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\tcode: 'MAX_CONNECTIONS',\n\t\t\t\tmessage: `Server has reached maximum connections (${this.maxConnections})`,\n\t\t\t\tretriable: true,\n\t\t\t})\n\t\t\ttransport.close(4029, 'max connections reached')\n\t\t\tthrow new SyncError('Maximum connections reached', {\n\t\t\t\tcurrent: this.sessions.size,\n\t\t\t\tmax: this.maxConnections,\n\t\t\t})\n\t\t}\n\n\t\tconst sessionId = generateUUIDv7()\n\n\t\tconst session = new ClientSession({\n\t\t\tsessionId,\n\t\t\ttransport,\n\t\t\tstore: this.store,\n\t\t\tauth: this.auth ?? undefined,\n\t\t\tserializer: this.serializer,\n\t\t\temitter: this.emitter ?? undefined,\n\t\t\tbatchSize: this.batchSize,\n\t\t\tschemaVersion: this.schemaVersion,\n\t\t\tonRelay: (sourceSessionId, operations) => {\n\t\t\t\tthis.handleRelay(sourceSessionId, operations)\n\t\t\t},\n\t\t\tonClose: (sid) => {\n\t\t\t\tthis.handleSessionClose(sid)\n\t\t\t},\n\t\t})\n\n\t\tthis.sessions.set(sessionId, session)\n\t\tsession.start()\n\n\t\treturn sessionId\n\t}\n\n\t/**\n\t * Get the current server status.\n\t */\n\tasync getStatus(): Promise<ServerStatus> {\n\t\treturn {\n\t\t\trunning: this.running,\n\t\t\tconnectedClients: this.sessions.size,\n\t\t\tport: this.port ?? null,\n\t\t\ttotalOperations: await this.store.getOperationCount(),\n\t\t}\n\t}\n\n\t/**\n\t * Get the number of currently connected clients.\n\t */\n\tgetConnectionCount(): number {\n\t\treturn this.sessions.size\n\t}\n\n\t// --- Private ---\n\n\tprivate handleRelay(sourceSessionId: string, operations: Operation[]): void {\n\t\tfor (const [sessionId, session] of this.sessions) {\n\t\t\tif (sessionId === sourceSessionId) continue\n\t\t\tsession.relayOperations(operations)\n\t\t}\n\t}\n\n\tprivate handleSessionClose(sessionId: string): void {\n\t\tthis.sessions.delete(sessionId)\n\n\t\tconst clientId = this.httpSessionToClient.get(sessionId)\n\t\tif (clientId) {\n\t\t\tthis.httpSessionToClient.delete(sessionId)\n\t\t\tthis.httpClients.delete(clientId)\n\t\t}\n\t}\n\n\tprivate getOrCreateHttpClient(clientId: string): { sessionId: string; transport: HttpServerTransport } {\n\t\tconst existing = this.httpClients.get(clientId)\n\t\tif (existing) {\n\t\t\treturn existing\n\t\t}\n\n\t\tconst transport = new HttpServerTransport(this.serializer)\n\t\tconst sessionId = this.handleConnection(transport)\n\t\tconst client = { sessionId, transport }\n\n\t\tthis.httpClients.set(clientId, client)\n\t\tthis.httpSessionToClient.set(sessionId, clientId)\n\n\t\treturn client\n\t}\n}\n\nfunction normalizeHttpBody(body: string | Uint8Array, contentType?: string): string | Uint8Array {\n\tif (body instanceof Uint8Array) {\n\t\treturn body\n\t}\n\n\tif (contentType?.includes('application/x-protobuf')) {\n\t\treturn new TextEncoder().encode(body)\n\t}\n\n\treturn body\n}\n","import type { AuthContext, AuthProvider } from '../types'\n\n/**\n * Auth provider that accepts all connections.\n * Returns a default anonymous context for any token.\n * Useful for development and testing.\n */\nexport class NoAuthProvider implements AuthProvider {\n\tasync authenticate(_token: string): Promise<AuthContext> {\n\t\treturn { userId: 'anonymous' }\n\t}\n}\n","import type { AuthContext, AuthProvider } from '../types'\n\n/**\n * Options for creating a TokenAuthProvider.\n */\nexport interface TokenAuthProviderOptions {\n\t/**\n\t * Validate a token and return an AuthContext if valid, or null if rejected.\n\t * This is where you implement your auth logic (JWT verification, database lookup, etc.).\n\t */\n\tvalidate: (token: string) => Promise<AuthContext | null>\n}\n\n/**\n * Token-based auth provider that delegates validation to a user-provided function.\n *\n * @example\n * ```typescript\n * const auth = new TokenAuthProvider({\n * validate: async (token) => {\n * const user = await verifyJWT(token)\n * return user ? { userId: user.id } : null\n * }\n * })\n * ```\n */\nexport class TokenAuthProvider implements AuthProvider {\n\tprivate readonly validate: (token: string) => Promise<AuthContext | null>\n\n\tconstructor(options: TokenAuthProviderOptions) {\n\t\tthis.validate = options.validate\n\t}\n\n\tasync authenticate(token: string): Promise<AuthContext | null> {\n\t\treturn this.validate(token)\n\t}\n}\n","import type { KoraSyncServerConfig } from '../types'\nimport { KoraSyncServer } from './kora-sync-server'\n\n/**\n * Factory function to create a KoraSyncServer.\n *\n * @param config - Server configuration\n * @returns A new KoraSyncServer instance\n *\n * @example\n * ```typescript\n * const server = createKoraServer({\n * store: new MemoryServerStore(),\n * port: 3000,\n * })\n * await server.start()\n * ```\n */\nexport function createKoraServer(config: KoraSyncServerConfig): KoraSyncServer {\n\treturn new KoraSyncServer(config)\n}\n"],"mappings":"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","names":["generateUUIDv7","generateUUIDv7","generateUUIDv7","and","asc","between","count","eq","sql","index","integer","text","generateUUIDv7","sql","and","eq","between","asc","count","SyncError","generateUUIDv7","operations","generateUUIDv7","SyncError","generateUUIDv7","JsonMessageSerializer","DEFAULT_BATCH_SIZE","DEFAULT_SCHEMA_VERSION","JsonMessageSerializer","SyncError","generateUUIDv7","operations"]}
|
|
1
|
+
{"version":3,"sources":["../src/store/memory-server-store.ts","../src/store/postgres-server-store.ts","../src/store/drizzle-pg-schema.ts","../src/store/sqlite-server-store.ts","../src/store/drizzle-schema.ts","../src/transport/http-server-transport.ts","../src/transport/ws-server-transport.ts","../src/session/client-session.ts","../src/scopes/server-scope-filter.ts","../src/server/kora-sync-server.ts","../src/auth/no-auth.ts","../src/auth/token-auth.ts","../src/server/create-server.ts","../src/server/production-server.ts"],"sourcesContent":["import type { Operation, VersionVector } from '@korajs/core'\nimport { generateUUIDv7 } from '@korajs/core'\nimport type { ApplyResult } from '@korajs/sync'\nimport type { ServerStore } from './server-store'\n\n/**\n * In-memory server store for testing and quick prototyping.\n * Not suitable for production — data does not survive process restart.\n */\nexport class MemoryServerStore implements ServerStore {\n\tprivate readonly nodeId: string\n\tprivate readonly operations: Operation[] = []\n\tprivate readonly operationIndex = new Map<string, Operation>()\n\tprivate readonly versionVector: Map<string, number> = new Map()\n\tprivate closed = false\n\n\tconstructor(nodeId?: string) {\n\t\tthis.nodeId = nodeId ?? generateUUIDv7()\n\t}\n\n\tgetVersionVector(): VersionVector {\n\t\treturn new Map(this.versionVector)\n\t}\n\n\tgetNodeId(): string {\n\t\treturn this.nodeId\n\t}\n\n\tasync applyRemoteOperation(op: Operation): Promise<ApplyResult> {\n\t\tthis.assertOpen()\n\n\t\t// Content-addressed dedup: same id = same content\n\t\tif (this.operationIndex.has(op.id)) {\n\t\t\treturn 'duplicate'\n\t\t}\n\n\t\tthis.operations.push(op)\n\t\tthis.operationIndex.set(op.id, op)\n\n\t\t// Advance version vector\n\t\tconst currentSeq = this.versionVector.get(op.nodeId) ?? 0\n\t\tif (op.sequenceNumber > currentSeq) {\n\t\t\tthis.versionVector.set(op.nodeId, op.sequenceNumber)\n\t\t}\n\n\t\treturn 'applied'\n\t}\n\n\tasync getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]> {\n\t\tthis.assertOpen()\n\n\t\treturn this.operations\n\t\t\t.filter(\n\t\t\t\t(op) => op.nodeId === nodeId && op.sequenceNumber >= fromSeq && op.sequenceNumber <= toSeq,\n\t\t\t)\n\t\t\t.sort((a, b) => a.sequenceNumber - b.sequenceNumber)\n\t}\n\n\tasync getOperationCount(): Promise<number> {\n\t\tthis.assertOpen()\n\t\treturn this.operations.length\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.closed = true\n\t}\n\n\t// --- Testing helpers (not on interface) ---\n\n\t/**\n\t * Get all stored operations (for test assertions).\n\t */\n\tgetAllOperations(): Operation[] {\n\t\treturn [...this.operations]\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('MemoryServerStore is closed')\n\t\t}\n\t}\n}\n","import type { Operation, VersionVector } from '@korajs/core'\nimport { generateUUIDv7 } from '@korajs/core'\nimport type { ApplyResult } from '@korajs/sync'\nimport { and, asc, between, count, eq, sql } from 'drizzle-orm'\nimport type { PostgresJsDatabase } from 'drizzle-orm/postgres-js'\nimport { pgOperations, pgSyncState } from './drizzle-pg-schema'\nimport type { ServerStore } from './server-store'\n\n/**\n * PostgreSQL-backed server store using Drizzle ORM.\n * All reads and writes go through Drizzle's typed query builder.\n * DDL stays as raw SQL via Drizzle's sql template (standard practice without drizzle-kit).\n */\nexport class PostgresServerStore implements ServerStore {\n\tprivate readonly nodeId: string\n\tprivate readonly db: PostgresJsDatabase\n\tprivate readonly versionVector: VersionVector = new Map()\n\tprivate readonly ready: Promise<void>\n\tprivate closed = false\n\n\tconstructor(db: PostgresJsDatabase, nodeId?: string) {\n\t\tthis.db = db\n\t\tthis.nodeId = nodeId ?? generateUUIDv7()\n\t\tthis.ready = this.initialize()\n\t}\n\n\tgetVersionVector(): VersionVector {\n\t\tthis.assertOpen()\n\t\treturn new Map(this.versionVector)\n\t}\n\n\tgetNodeId(): string {\n\t\treturn this.nodeId\n\t}\n\n\tasync applyRemoteOperation(op: Operation): Promise<ApplyResult> {\n\t\tthis.assertOpen()\n\t\tawait this.ready\n\n\t\t// Content-addressed dedup check\n\t\tconst existing = await this.db\n\t\t\t.select({ id: pgOperations.id })\n\t\t\t.from(pgOperations)\n\t\t\t.where(eq(pgOperations.id, op.id))\n\t\t\t.limit(1)\n\n\t\tif (existing.length > 0) {\n\t\t\treturn 'duplicate'\n\t\t}\n\n\t\tconst now = Date.now()\n\t\tconst row = this.serializeOperation(op, now)\n\n\t\tawait this.db.transaction(async (tx) => {\n\t\t\t// Insert operation with dedup\n\t\t\tawait tx\n\t\t\t\t.insert(pgOperations)\n\t\t\t\t.values(row)\n\t\t\t\t.onConflictDoNothing({ target: pgOperations.id })\n\n\t\t\t// Upsert version vector: advance max sequence number with GREATEST\n\t\t\tawait tx\n\t\t\t\t.insert(pgSyncState)\n\t\t\t\t.values({\n\t\t\t\t\tnodeId: op.nodeId,\n\t\t\t\t\tmaxSequenceNumber: op.sequenceNumber,\n\t\t\t\t\tlastSeenAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoUpdate({\n\t\t\t\t\ttarget: pgSyncState.nodeId,\n\t\t\t\t\tset: {\n\t\t\t\t\t\tmaxSequenceNumber: sql`GREATEST(${pgSyncState.maxSequenceNumber}, ${op.sequenceNumber})`,\n\t\t\t\t\t\tlastSeenAt: sql`${now}`,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t})\n\n\t\t// Update in-memory version vector cache\n\t\tconst currentMax = this.versionVector.get(op.nodeId) ?? 0\n\t\tif (op.sequenceNumber > currentMax) {\n\t\t\tthis.versionVector.set(op.nodeId, op.sequenceNumber)\n\t\t}\n\n\t\treturn 'applied'\n\t}\n\n\tasync getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]> {\n\t\tthis.assertOpen()\n\t\tawait this.ready\n\n\t\tconst rows = await this.db\n\t\t\t.select()\n\t\t\t.from(pgOperations)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(pgOperations.nodeId, nodeId),\n\t\t\t\t\tbetween(pgOperations.sequenceNumber, fromSeq, toSeq),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.orderBy(asc(pgOperations.sequenceNumber))\n\n\t\treturn rows.map((row) => this.deserializeOperation(row))\n\t}\n\n\tasync getOperationCount(): Promise<number> {\n\t\tthis.assertOpen()\n\t\tawait this.ready\n\n\t\tconst result = await this.db.select({ value: count() }).from(pgOperations)\n\t\treturn result[0]?.value ?? 0\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.closed = true\n\t}\n\n\tprivate async initialize(): Promise<void> {\n\t\tawait this.ensureTables()\n\n\t\t// Hydrate in-memory version vector cache via Drizzle query\n\t\tconst rows = await this.db\n\t\t\t.select({\n\t\t\t\tnodeId: pgSyncState.nodeId,\n\t\t\t\tmaxSequenceNumber: pgSyncState.maxSequenceNumber,\n\t\t\t})\n\t\t\t.from(pgSyncState)\n\n\t\tfor (const row of rows) {\n\t\t\tthis.versionVector.set(row.nodeId, row.maxSequenceNumber)\n\t\t}\n\t}\n\n\t/**\n\t * Create tables if they don't exist.\n\t * Uses raw SQL via Drizzle's sql template — standard DDL practice without drizzle-kit.\n\t */\n\tprivate async ensureTables(): Promise<void> {\n\t\tawait this.db.execute(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS operations (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tnode_id TEXT NOT NULL,\n\t\t\t\ttype TEXT NOT NULL,\n\t\t\t\tcollection TEXT NOT NULL,\n\t\t\t\trecord_id TEXT NOT NULL,\n\t\t\t\tdata TEXT,\n\t\t\t\tprevious_data TEXT,\n\t\t\t\twall_time INTEGER NOT NULL,\n\t\t\t\tlogical INTEGER NOT NULL,\n\t\t\t\ttimestamp_node_id TEXT NOT NULL,\n\t\t\t\tsequence_number INTEGER NOT NULL,\n\t\t\t\tcausal_deps TEXT NOT NULL DEFAULT '[]',\n\t\t\t\tschema_version INTEGER NOT NULL,\n\t\t\t\treceived_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\n\t\tawait this.db.execute(\n\t\t\tsql`CREATE INDEX IF NOT EXISTS idx_node_seq ON operations (node_id, sequence_number)`,\n\t\t)\n\t\tawait this.db.execute(\n\t\t\tsql`CREATE INDEX IF NOT EXISTS idx_collection ON operations (collection)`,\n\t\t)\n\t\tawait this.db.execute(\n\t\t\tsql`CREATE INDEX IF NOT EXISTS idx_received ON operations (received_at)`,\n\t\t)\n\n\t\tawait this.db.execute(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS sync_state (\n\t\t\t\tnode_id TEXT PRIMARY KEY,\n\t\t\t\tmax_sequence_number INTEGER NOT NULL,\n\t\t\t\tlast_seen_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\t}\n\n\tprivate serializeOperation(\n\t\top: Operation,\n\t\treceivedAt: number,\n\t): typeof pgOperations.$inferInsert {\n\t\treturn {\n\t\t\tid: op.id,\n\t\t\tnodeId: op.nodeId,\n\t\t\ttype: op.type,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tdata: op.data !== null ? JSON.stringify(op.data) : null,\n\t\t\tpreviousData: op.previousData !== null ? JSON.stringify(op.previousData) : null,\n\t\t\twallTime: op.timestamp.wallTime,\n\t\t\tlogical: op.timestamp.logical,\n\t\t\ttimestampNodeId: op.timestamp.nodeId,\n\t\t\tsequenceNumber: op.sequenceNumber,\n\t\t\tcausalDeps: JSON.stringify(op.causalDeps),\n\t\t\tschemaVersion: op.schemaVersion,\n\t\t\treceivedAt,\n\t\t}\n\t}\n\n\tprivate deserializeOperation(row: typeof pgOperations.$inferSelect): Operation {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tnodeId: row.nodeId,\n\t\t\ttype: row.type as Operation['type'],\n\t\t\tcollection: row.collection,\n\t\t\trecordId: row.recordId,\n\t\t\tdata: row.data !== null ? JSON.parse(row.data) : null,\n\t\t\tpreviousData: row.previousData !== null ? JSON.parse(row.previousData) : null,\n\t\t\ttimestamp: {\n\t\t\t\twallTime: row.wallTime,\n\t\t\t\tlogical: row.logical,\n\t\t\t\tnodeId: row.timestampNodeId,\n\t\t\t},\n\t\t\tsequenceNumber: row.sequenceNumber,\n\t\t\tcausalDeps: JSON.parse(row.causalDeps),\n\t\t\tschemaVersion: row.schemaVersion,\n\t\t}\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('PostgresServerStore is closed')\n\t\t}\n\t}\n}\n\n/**\n * Creates a PostgresServerStore from a PostgreSQL connection string.\n *\n * Uses runtime dynamic imports so projects that do not use PostgreSQL\n * do not need to install `postgres`. Wraps the postgres client with\n * Drizzle ORM for typed query building.\n */\nexport async function createPostgresServerStore(options: {\n\tconnectionString: string\n\tnodeId?: string\n}): Promise<PostgresServerStore> {\n\tconst { postgresClient, drizzleFn } = await loadPostgresDeps()\n\tconst client = postgresClient(options.connectionString)\n\tconst db = drizzleFn(client)\n\n\treturn new PostgresServerStore(db, options.nodeId)\n}\n\nasync function loadPostgresDeps(): Promise<{\n\tpostgresClient: (connectionString: string) => unknown\n\tdrizzleFn: (client: unknown) => PostgresJsDatabase\n}> {\n\ttry {\n\t\tconst dynamicImport = new Function('specifier', 'return import(specifier)') as (\n\t\t\tspecifier: string,\n\t\t) => Promise<unknown>\n\n\t\tconst postgresMod = (await dynamicImport('postgres')) as { default: (cs: string) => unknown }\n\t\tconst drizzleMod = (await dynamicImport('drizzle-orm/postgres-js')) as {\n\t\t\tdrizzle: (client: unknown) => PostgresJsDatabase\n\t\t}\n\n\t\treturn {\n\t\t\tpostgresClient: postgresMod.default,\n\t\t\tdrizzleFn: drizzleMod.drizzle,\n\t\t}\n\t} catch {\n\t\tthrow new Error(\n\t\t\t'PostgreSQL backend requires the \"postgres\" package. Install it in your project dependencies.',\n\t\t)\n\t}\n}\n","import { index, integer, pgTable, text } from 'drizzle-orm/pg-core'\n\n/**\n * Drizzle schema for the Kora sync server's PostgreSQL database.\n *\n * Two tables:\n * - `pgOperations` — the append-only operation log (content-addressed by id)\n * - `pgSyncState` — tracks the max sequence number seen per node (version vector)\n *\n * Column structure mirrors the SQLite drizzle-schema.ts but uses pgTable.\n */\n\nexport const pgOperations = pgTable(\n\t'operations',\n\t{\n\t\tid: text('id').primaryKey(),\n\t\tnodeId: text('node_id').notNull(),\n\t\ttype: text('type').notNull(),\n\t\tcollection: text('collection').notNull(),\n\t\trecordId: text('record_id').notNull(),\n\t\tdata: text('data'), // JSON-serialized, null for deletes\n\t\tpreviousData: text('previous_data'), // JSON-serialized, null for insert/delete\n\t\twallTime: integer('wall_time').notNull(),\n\t\tlogical: integer('logical').notNull(),\n\t\ttimestampNodeId: text('timestamp_node_id').notNull(),\n\t\tsequenceNumber: integer('sequence_number').notNull(),\n\t\tcausalDeps: text('causal_deps').notNull().default('[]'), // JSON array of op IDs\n\t\tschemaVersion: integer('schema_version').notNull(),\n\t\treceivedAt: integer('received_at').notNull(),\n\t},\n\t(table) => ({\n\t\tnodeSeqIdx: index('idx_pg_node_seq').on(table.nodeId, table.sequenceNumber),\n\t\tcollectionIdx: index('idx_pg_collection').on(table.collection),\n\t\treceivedIdx: index('idx_pg_received').on(table.receivedAt),\n\t}),\n)\n\nexport const pgSyncState = pgTable('sync_state', {\n\tnodeId: text('node_id').primaryKey(),\n\tmaxSequenceNumber: integer('max_sequence_number').notNull(),\n\tlastSeenAt: integer('last_seen_at').notNull(),\n})\n","import { createRequire } from 'node:module'\nimport type { Operation, VersionVector } from '@korajs/core'\nimport { generateUUIDv7 } from '@korajs/core'\nimport type { ApplyResult } from '@korajs/sync'\nimport { and, asc, between, count, eq, sql } from 'drizzle-orm'\nimport type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'\nimport { operations, syncState } from './drizzle-schema'\nimport type { ServerStore } from './server-store'\n\n// better-sqlite3 is a native CJS addon that cannot be loaded via ESM import().\n// createRequire provides a CJS require() that works in both ESM and CJS contexts.\n// tsup's shims option ensures import.meta.url is available in CJS builds.\nconst esmRequire = createRequire(import.meta.url)\n\n/**\n * SQLite-backed server store using Drizzle ORM.\n * Persists operations and version vectors to a real database file,\n * surviving process restarts.\n */\nexport class SqliteServerStore implements ServerStore {\n\tprivate readonly nodeId: string\n\tprivate readonly db: BetterSQLite3Database\n\tprivate closed = false\n\n\tconstructor(db: BetterSQLite3Database, nodeId?: string) {\n\t\tthis.db = db\n\t\tthis.nodeId = nodeId ?? generateUUIDv7()\n\t\tthis.ensureTables()\n\t}\n\n\tgetVersionVector(): VersionVector {\n\t\tthis.assertOpen()\n\t\tconst rows = this.db.select().from(syncState).all()\n\t\tconst vv: VersionVector = new Map()\n\t\tfor (const row of rows) {\n\t\t\tvv.set(row.nodeId, row.maxSequenceNumber)\n\t\t}\n\t\treturn vv\n\t}\n\n\tgetNodeId(): string {\n\t\treturn this.nodeId\n\t}\n\n\tasync applyRemoteOperation(op: Operation): Promise<ApplyResult> {\n\t\tthis.assertOpen()\n\n\t\tconst now = Date.now()\n\t\tconst row = this.serializeOperation(op, now)\n\n\t\t// Use a transaction for atomicity: insert op + update version vector\n\t\tconst result = this.db.transaction((tx) => {\n\t\t\t// Content-addressed dedup via onConflictDoNothing\n\t\t\tconst insertResult = tx\n\t\t\t\t.insert(operations)\n\t\t\t\t.values(row)\n\t\t\t\t.onConflictDoNothing({ target: operations.id })\n\t\t\t\t.run()\n\n\t\t\tif (insertResult.changes === 0) {\n\t\t\t\treturn 'duplicate' as const\n\t\t\t}\n\n\t\t\t// Advance version vector: upsert with MAX to ensure monotonic progress\n\t\t\ttx.insert(syncState)\n\t\t\t\t.values({\n\t\t\t\t\tnodeId: op.nodeId,\n\t\t\t\t\tmaxSequenceNumber: op.sequenceNumber,\n\t\t\t\t\tlastSeenAt: now,\n\t\t\t\t})\n\t\t\t\t.onConflictDoUpdate({\n\t\t\t\t\ttarget: syncState.nodeId,\n\t\t\t\t\tset: {\n\t\t\t\t\t\tmaxSequenceNumber: sql`MAX(${syncState.maxSequenceNumber}, ${op.sequenceNumber})`,\n\t\t\t\t\t\tlastSeenAt: sql`${now}`,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\t.run()\n\n\t\t\treturn 'applied' as const\n\t\t})\n\n\t\treturn result\n\t}\n\n\tasync getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]> {\n\t\tthis.assertOpen()\n\n\t\tconst rows = this.db\n\t\t\t.select()\n\t\t\t.from(operations)\n\t\t\t.where(and(eq(operations.nodeId, nodeId), between(operations.sequenceNumber, fromSeq, toSeq)))\n\t\t\t.orderBy(asc(operations.sequenceNumber))\n\t\t\t.all()\n\n\t\treturn rows.map((row) => this.deserializeOperation(row))\n\t}\n\n\tasync getOperationCount(): Promise<number> {\n\t\tthis.assertOpen()\n\n\t\tconst result = this.db.select({ value: count() }).from(operations).all()\n\t\treturn result[0]?.value ?? 0\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.closed = true\n\t}\n\n\t/**\n\t * Create the operations and sync_state tables if they don't exist.\n\t * Uses raw SQL via Drizzle's sql template — standard practice for DDL without drizzle-kit.\n\t */\n\tprivate ensureTables(): void {\n\t\tthis.db.run(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS operations (\n\t\t\t\tid TEXT PRIMARY KEY,\n\t\t\t\tnode_id TEXT NOT NULL,\n\t\t\t\ttype TEXT NOT NULL,\n\t\t\t\tcollection TEXT NOT NULL,\n\t\t\t\trecord_id TEXT NOT NULL,\n\t\t\t\tdata TEXT,\n\t\t\t\tprevious_data TEXT,\n\t\t\t\twall_time INTEGER NOT NULL,\n\t\t\t\tlogical INTEGER NOT NULL,\n\t\t\t\ttimestamp_node_id TEXT NOT NULL,\n\t\t\t\tsequence_number INTEGER NOT NULL,\n\t\t\t\tcausal_deps TEXT NOT NULL DEFAULT '[]',\n\t\t\t\tschema_version INTEGER NOT NULL,\n\t\t\t\treceived_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE INDEX IF NOT EXISTS idx_node_seq ON operations (node_id, sequence_number)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE INDEX IF NOT EXISTS idx_collection ON operations (collection)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE INDEX IF NOT EXISTS idx_received ON operations (received_at)\n\t\t`)\n\n\t\tthis.db.run(sql`\n\t\t\tCREATE TABLE IF NOT EXISTS sync_state (\n\t\t\t\tnode_id TEXT PRIMARY KEY,\n\t\t\t\tmax_sequence_number INTEGER NOT NULL,\n\t\t\t\tlast_seen_at INTEGER NOT NULL\n\t\t\t)\n\t\t`)\n\t}\n\n\tprivate serializeOperation(\n\t\top: Operation,\n\t\treceivedAt: number,\n\t): typeof operations.$inferInsert {\n\t\treturn {\n\t\t\tid: op.id,\n\t\t\tnodeId: op.nodeId,\n\t\t\ttype: op.type,\n\t\t\tcollection: op.collection,\n\t\t\trecordId: op.recordId,\n\t\t\tdata: op.data !== null ? JSON.stringify(op.data) : null,\n\t\t\tpreviousData: op.previousData !== null ? JSON.stringify(op.previousData) : null,\n\t\t\twallTime: op.timestamp.wallTime,\n\t\t\tlogical: op.timestamp.logical,\n\t\t\ttimestampNodeId: op.timestamp.nodeId,\n\t\t\tsequenceNumber: op.sequenceNumber,\n\t\t\tcausalDeps: JSON.stringify(op.causalDeps),\n\t\t\tschemaVersion: op.schemaVersion,\n\t\t\treceivedAt,\n\t\t}\n\t}\n\n\tprivate deserializeOperation(row: typeof operations.$inferSelect): Operation {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tnodeId: row.nodeId,\n\t\t\ttype: row.type as Operation['type'],\n\t\t\tcollection: row.collection,\n\t\t\trecordId: row.recordId,\n\t\t\tdata: row.data !== null ? JSON.parse(row.data) : null,\n\t\t\tpreviousData: row.previousData !== null ? JSON.parse(row.previousData) : null,\n\t\t\ttimestamp: {\n\t\t\t\twallTime: row.wallTime,\n\t\t\t\tlogical: row.logical,\n\t\t\t\tnodeId: row.timestampNodeId,\n\t\t\t},\n\t\t\tsequenceNumber: row.sequenceNumber,\n\t\t\tcausalDeps: JSON.parse(row.causalDeps),\n\t\t\tschemaVersion: row.schemaVersion,\n\t\t}\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('SqliteServerStore is closed')\n\t\t}\n\t}\n}\n\n/**\n * Creates a SqliteServerStore with a file-backed or in-memory database.\n * Handles database creation, Drizzle wrapping, and table setup.\n *\n * @param options - Configuration options\n * @param options.filename - Path to SQLite database file. Defaults to ':memory:' for testing.\n * @param options.nodeId - Server node ID. Auto-generated if not provided.\n * @returns A ready-to-use SqliteServerStore\n *\n * @example\n * ```typescript\n * import { createSqliteServerStore } from '@korajs/server'\n *\n * const store = createSqliteServerStore({ filename: './kora-server.db' })\n * const server = createKoraServer({ store, port: 3001 })\n * ```\n */\nexport function createSqliteServerStore(options: {\n\tfilename?: string\n\tnodeId?: string\n}): SqliteServerStore {\n\t// better-sqlite3 is a native CJS addon — use esmRequire (from createRequire)\n\t// so this works in both ESM and CJS contexts.\n\tconst Database = esmRequire('better-sqlite3')\n\tconst { drizzle } = esmRequire('drizzle-orm/better-sqlite3')\n\n\tconst filename = options.filename ?? ':memory:'\n\tconst sqlite = new Database(filename)\n\n\t// Enable WAL mode for better concurrent read/write performance\n\tsqlite.pragma('journal_mode = WAL')\n\n\tconst db = drizzle(sqlite)\n\treturn new SqliteServerStore(db, options.nodeId)\n}\n","import { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'\n\n/**\n * Drizzle schema for the Kora sync server's SQLite database.\n *\n * Two tables:\n * - `operations` — the append-only operation log (content-addressed by id)\n * - `syncState` — tracks the max sequence number seen per node (version vector)\n */\n\nexport const operations = sqliteTable(\n\t'operations',\n\t{\n\t\tid: text('id').primaryKey(),\n\t\tnodeId: text('node_id').notNull(),\n\t\ttype: text('type').notNull(),\n\t\tcollection: text('collection').notNull(),\n\t\trecordId: text('record_id').notNull(),\n\t\tdata: text('data'), // JSON-serialized, null for deletes\n\t\tpreviousData: text('previous_data'), // JSON-serialized, null for insert/delete\n\t\twallTime: integer('wall_time').notNull(),\n\t\tlogical: integer('logical').notNull(),\n\t\ttimestampNodeId: text('timestamp_node_id').notNull(),\n\t\tsequenceNumber: integer('sequence_number').notNull(),\n\t\tcausalDeps: text('causal_deps').notNull().default('[]'), // JSON array of op IDs\n\t\tschemaVersion: integer('schema_version').notNull(),\n\t\treceivedAt: integer('received_at').notNull(),\n\t},\n\t(table) => ({\n\t\tnodeSeqIdx: index('idx_node_seq').on(table.nodeId, table.sequenceNumber),\n\t\tcollectionIdx: index('idx_collection').on(table.collection),\n\t\treceivedIdx: index('idx_received').on(table.receivedAt),\n\t}),\n)\n\nexport const syncState = sqliteTable('sync_state', {\n\tnodeId: text('node_id').primaryKey(),\n\tmaxSequenceNumber: integer('max_sequence_number').notNull(),\n\tlastSeenAt: integer('last_seen_at').notNull(),\n})\n","import { SyncError } from '@korajs/core'\nimport type { MessageSerializer } from '@korajs/sync'\nimport type {\n\tServerCloseHandler,\n\tServerErrorHandler,\n\tServerMessageHandler,\n\tServerTransport,\n} from './server-transport'\n\nexport interface HttpPollResponse {\n\tstatus: 200 | 204 | 304 | 410\n\tbody?: string | Uint8Array\n\theaders?: Record<string, string>\n}\n\ninterface QueuedMessage {\n\tetag: string\n\tcontentType: string\n\tpayload: string | Uint8Array\n}\n\n/**\n * Server-side transport for HTTP long-polling clients.\n *\n * Incoming client messages are pushed via POST, while outbound server\n * messages are pulled via GET long-poll requests.\n */\nexport class HttpServerTransport implements ServerTransport {\n\tprivate readonly serializer: MessageSerializer\n\n\tprivate messageHandler: ServerMessageHandler | null = null\n\tprivate closeHandler: ServerCloseHandler | null = null\n\tprivate errorHandler: ServerErrorHandler | null = null\n\n\tprivate connected = true\n\tprivate nextSequence = 1\n\tprivate readonly queue: QueuedMessage[] = []\n\n\tconstructor(serializer: MessageSerializer) {\n\t\tthis.serializer = serializer\n\t}\n\n\tsend(message: import('@korajs/sync').SyncMessage): void {\n\t\tif (!this.connected) return\n\n\t\tconst encoded = this.serializer.encode(message)\n\t\tconst isBinary = encoded instanceof Uint8Array\n\t\tthis.queue.push({\n\t\t\tetag: this.makeEtag(this.nextSequence++),\n\t\t\tcontentType: isBinary ? 'application/x-protobuf' : 'application/json',\n\t\t\tpayload: encoded,\n\t\t})\n\t}\n\n\tonMessage(handler: ServerMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t}\n\n\tonClose(handler: ServerCloseHandler): void {\n\t\tthis.closeHandler = handler\n\t}\n\n\tonError(handler: ServerErrorHandler): void {\n\t\tthis.errorHandler = handler\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.connected\n\t}\n\n\tclose(code = 1000, reason = 'transport closed'): void {\n\t\tif (!this.connected) return\n\t\tthis.connected = false\n\t\tthis.queue.length = 0\n\t\tthis.closeHandler?.(code, reason)\n\t}\n\n\treceive(payload: string | Uint8Array): void {\n\t\tif (!this.connected) {\n\t\t\tthrow new SyncError('HTTP server transport is closed')\n\t\t}\n\n\t\ttry {\n\t\t\tconst message = this.serializer.decode(payload)\n\t\t\tthis.messageHandler?.(message)\n\t\t} catch (error) {\n\t\t\tthis.errorHandler?.(error instanceof Error ? error : new Error(String(error)))\n\t\t}\n\t}\n\n\tpoll(ifNoneMatch?: string): HttpPollResponse {\n\t\tif (!this.connected) {\n\t\t\treturn { status: 410 }\n\t\t}\n\n\t\tconst next = this.queue[0]\n\t\tif (!next) {\n\t\t\treturn { status: 204 }\n\t\t}\n\n\t\tif (ifNoneMatch && ifNoneMatch === next.etag) {\n\t\t\treturn {\n\t\t\t\tstatus: 304,\n\t\t\t\theaders: { etag: next.etag },\n\t\t\t}\n\t\t}\n\n\t\tthis.queue.shift()\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\tbody: next.payload,\n\t\t\theaders: {\n\t\t\t\t'content-type': next.contentType,\n\t\t\t\tetag: next.etag,\n\t\t\t},\n\t\t}\n\t}\n\n\tprivate makeEtag(sequence: number): string {\n\t\treturn `W/\"${sequence}\"`\n\t}\n}\n","import { SyncError } from '@korajs/core'\nimport type { SyncMessage } from '@korajs/sync'\nimport { JsonMessageSerializer } from '@korajs/sync'\nimport type { MessageSerializer } from '@korajs/sync'\nimport type {\n\tServerCloseHandler,\n\tServerErrorHandler,\n\tServerMessageHandler,\n\tServerTransport,\n} from './server-transport'\n\n/** WebSocket ready states (mirrors ws constants) */\nconst WS_OPEN = 1\n\n/**\n * Minimal interface for a ws.WebSocket instance.\n * Allows dependency injection for testing without importing ws directly.\n */\nexport interface WsWebSocket {\n\treadyState: number\n\tsend(data: string | Uint8Array, callback?: (err?: Error) => void): void\n\tclose(code?: number, reason?: string): void\n\ton(event: string, listener: (...args: unknown[]) => void): void\n\tremoveAllListeners(): void\n}\n\n/**\n * Options for WsServerTransport.\n */\nexport interface WsServerTransportOptions {\n\t/** Message serializer. Defaults to JsonMessageSerializer. */\n\tserializer?: MessageSerializer\n}\n\n/**\n * Server-side transport wrapping a ws.WebSocket connection.\n * Created for each incoming client connection.\n */\nexport class WsServerTransport implements ServerTransport {\n\tprivate readonly ws: WsWebSocket\n\tprivate readonly serializer: MessageSerializer\n\tprivate messageHandler: ServerMessageHandler | null = null\n\tprivate closeHandler: ServerCloseHandler | null = null\n\tprivate errorHandler: ServerErrorHandler | null = null\n\n\tconstructor(ws: WsWebSocket, options?: WsServerTransportOptions) {\n\t\tthis.ws = ws\n\t\tthis.serializer = options?.serializer ?? new JsonMessageSerializer()\n\t\tthis.setupListeners()\n\t}\n\n\tsend(message: SyncMessage): void {\n\t\tif (this.ws.readyState !== WS_OPEN) {\n\t\t\tthrow new SyncError('Cannot send message: WebSocket is not open', {\n\t\t\t\treadyState: this.ws.readyState,\n\t\t\t\tmessageType: message.type,\n\t\t\t})\n\t\t}\n\n\t\tconst encoded = this.serializer.encode(message)\n\t\tthis.ws.send(encoded)\n\t}\n\n\tonMessage(handler: ServerMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t}\n\n\tonClose(handler: ServerCloseHandler): void {\n\t\tthis.closeHandler = handler\n\t}\n\n\tonError(handler: ServerErrorHandler): void {\n\t\tthis.errorHandler = handler\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.ws.readyState === WS_OPEN\n\t}\n\n\tclose(code?: number, reason?: string): void {\n\t\tthis.ws.close(code ?? 1000, reason ?? 'server closing')\n\t}\n\n\tprivate setupListeners(): void {\n\t\tthis.ws.on('message', (data: unknown) => {\n\t\t\ttry {\n\t\t\t\tif (\n\t\t\t\t\ttypeof data !== 'string' &&\n\t\t\t\t\t!(data instanceof Uint8Array) &&\n\t\t\t\t\t!(data instanceof ArrayBuffer)\n\t\t\t\t) {\n\t\t\t\t\tthrow new SyncError('Unsupported WebSocket payload type', {\n\t\t\t\t\t\tpayloadType: typeof data,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tconst decoded = this.serializer.decode(data)\n\t\t\t\tthis.messageHandler?.(decoded)\n\t\t\t} catch (err) {\n\t\t\t\tthis.errorHandler?.(err instanceof Error ? err : new Error(String(err)))\n\t\t\t}\n\t\t})\n\n\t\tthis.ws.on('close', (code: unknown, reason: unknown) => {\n\t\t\tthis.closeHandler?.(Number(code) || 1006, String(reason || 'connection closed'))\n\t\t})\n\n\t\tthis.ws.on('error', (err: unknown) => {\n\t\t\tthis.errorHandler?.(err instanceof Error ? err : new Error(String(err)))\n\t\t})\n\t}\n}\n","import type { KoraEventEmitter, Operation } from '@korajs/core'\nimport { SyncError, generateUUIDv7 } from '@korajs/core'\nimport { topologicalSort } from '@korajs/core/internal'\nimport type {\n\tHandshakeMessage,\n\tMessageSerializer,\n\tOperationBatchMessage,\n\tSyncMessage,\n\tWireFormat,\n} from '@korajs/sync'\nimport {\n\tNegotiatedMessageSerializer,\n\tversionVectorToWire,\n\twireToVersionVector,\n} from '@korajs/sync'\nimport type { ServerStore } from '../store/server-store'\nimport { operationMatchesScopes } from '../scopes/server-scope-filter'\nimport type { ServerTransport } from '../transport/server-transport'\nimport type { AuthContext, AuthProvider } from '../types'\n\nconst DEFAULT_BATCH_SIZE = 100\nconst DEFAULT_SCHEMA_VERSION = 1\n\n/**\n * Possible states for a client session.\n */\nexport type SessionState = 'connected' | 'authenticated' | 'syncing' | 'streaming' | 'closed'\n\n/**\n * Callback invoked when a session has new operations to relay to other sessions.\n */\nexport type RelayCallback = (sourceSessionId: string, operations: Operation[]) => void\n\n/**\n * Options for creating a ClientSession.\n */\nexport interface ClientSessionOptions {\n\t/** Unique session identifier */\n\tsessionId: string\n\t/** Transport for this client connection */\n\ttransport: ServerTransport\n\t/** Server-side operation store */\n\tstore: ServerStore\n\t/** Authentication provider (optional) */\n\tauth?: AuthProvider\n\t/** Message serializer */\n\tserializer?: MessageSerializer\n\t/** Event emitter for DevTools integration */\n\temitter?: KoraEventEmitter\n\t/** Max operations per sync batch */\n\tbatchSize?: number\n\t/** Schema version the server expects */\n\tschemaVersion?: number\n\t/** Called when this session has operations to relay to other sessions */\n\tonRelay?: RelayCallback\n\t/** Called when this session closes */\n\tonClose?: (sessionId: string) => void\n}\n\n/**\n * Handles the sync protocol for a single connected client.\n *\n * Lifecycle: connected → (authenticated) → syncing → streaming → closed\n *\n * The session:\n * 1. Receives a handshake from the client\n * 2. Authenticates if an AuthProvider is configured\n * 3. Sends back a HandshakeResponse with the server's version vector\n * 4. Computes and sends the server's delta to the client (paginated)\n * 5. Processes incoming operation batches from the client\n * 6. Transitions to streaming for real-time bidirectional sync\n * 7. Relays new operations to other sessions via the RelayCallback\n */\nexport class ClientSession {\n\tprivate state: SessionState = 'connected'\n\tprivate clientNodeId: string | null = null\n\tprivate authContext: AuthContext | null = null\n\n\tprivate readonly sessionId: string\n\tprivate readonly transport: ServerTransport\n\tprivate readonly store: ServerStore\n\tprivate readonly auth: AuthProvider | null\n\tprivate readonly serializer: MessageSerializer\n\tprivate readonly emitter: KoraEventEmitter | null\n\tprivate readonly batchSize: number\n\tprivate readonly schemaVersion: number\n\tprivate readonly onRelay: RelayCallback | null\n\tprivate readonly onClose: ((sessionId: string) => void) | null\n\n\tconstructor(options: ClientSessionOptions) {\n\t\tthis.sessionId = options.sessionId\n\t\tthis.transport = options.transport\n\t\tthis.store = options.store\n\t\tthis.auth = options.auth ?? null\n\t\tthis.serializer = options.serializer ?? new NegotiatedMessageSerializer('json')\n\t\tthis.emitter = options.emitter ?? null\n\t\tthis.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE\n\t\tthis.schemaVersion = options.schemaVersion ?? DEFAULT_SCHEMA_VERSION\n\t\tthis.onRelay = options.onRelay ?? null\n\t\tthis.onClose = options.onClose ?? null\n\t}\n\n\t/**\n\t * Start handling messages from the client transport.\n\t */\n\tstart(): void {\n\t\tthis.transport.onMessage((msg) => this.handleMessage(msg))\n\t\tthis.transport.onClose((_code, _reason) => this.handleTransportClose())\n\t\tthis.transport.onError((_err) => {\n\t\t\t// Transport errors during active session cause close\n\t\t\tif (this.state !== 'closed') {\n\t\t\t\tthis.handleTransportClose()\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Relay operations from another session to this client.\n\t * Only relays if the session is in streaming state and transport is connected.\n\t */\n\trelayOperations(operations: Operation[]): void {\n\t\tif (this.state !== 'streaming' || !this.transport.isConnected()) return\n\t\tif (operations.length === 0) return\n\n\t\tconst visibleOperations = operations.filter((op) =>\n\t\t\toperationMatchesScopes(op, this.authContext?.scopes),\n\t\t)\n\t\tif (visibleOperations.length === 0) return\n\n\t\tconst serializedOps = visibleOperations.map((op) => this.serializer.encodeOperation(op))\n\t\tconst msg: SyncMessage = {\n\t\t\ttype: 'operation-batch',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\toperations: serializedOps,\n\t\t\tisFinal: true,\n\t\t\tbatchIndex: 0,\n\t\t}\n\t\tthis.transport.send(msg)\n\t}\n\n\t/**\n\t * Close this session.\n\t */\n\tclose(reason?: string): void {\n\t\tif (this.state === 'closed') return\n\t\tthis.state = 'closed'\n\n\t\tif (this.transport.isConnected()) {\n\t\t\tthis.transport.close(1000, reason ?? 'session closed')\n\t\t}\n\n\t\tthis.onClose?.(this.sessionId)\n\t}\n\n\t// --- Getters ---\n\n\tgetState(): SessionState {\n\t\treturn this.state\n\t}\n\n\tgetSessionId(): string {\n\t\treturn this.sessionId\n\t}\n\n\tgetClientNodeId(): string | null {\n\t\treturn this.clientNodeId\n\t}\n\n\tgetAuthContext(): AuthContext | null {\n\t\treturn this.authContext\n\t}\n\n\tisStreaming(): boolean {\n\t\treturn this.state === 'streaming'\n\t}\n\n\t// --- Private protocol handlers ---\n\n\tprivate handleMessage(message: SyncMessage): void {\n\t\tswitch (message.type) {\n\t\t\tcase 'handshake':\n\t\t\t\tthis.handleHandshake(message)\n\t\t\t\tbreak\n\t\t\tcase 'operation-batch':\n\t\t\t\tthis.handleOperationBatch(message)\n\t\t\t\tbreak\n\t\t\t// Acknowledgments from clients are noted but no action needed on server\n\t\t\tcase 'acknowledgment':\n\t\t\t\tbreak\n\t\t\tcase 'error':\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\tprivate async handleHandshake(msg: HandshakeMessage): Promise<void> {\n\t\t// Only accept handshake in 'connected' state (prevent duplicate handshakes)\n\t\tif (this.state !== 'connected') {\n\t\t\tthis.sendError('DUPLICATE_HANDSHAKE', 'Handshake already completed', false)\n\t\t\treturn\n\t\t}\n\n\t\tthis.clientNodeId = msg.nodeId\n\n\t\t// Authenticate if provider is configured\n\t\tif (this.auth) {\n\t\t\tconst token = msg.authToken ?? ''\n\t\t\tconst context = await this.auth.authenticate(token)\n\t\t\tif (!context) {\n\t\t\t\tthis.sendError('AUTH_FAILED', 'Authentication failed', false)\n\t\t\t\tthis.close('authentication failed')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.authContext = context\n\t\t\tthis.state = 'authenticated'\n\t\t}\n\n\t\t// Send handshake response with server's version vector\n\t\tconst serverVector = this.store.getVersionVector()\n\t\tconst selectedWireFormat = selectWireFormat(msg.supportedWireFormats)\n\t\tthis.setSerializerWireFormat(selectedWireFormat)\n\t\tconst response: SyncMessage = {\n\t\t\ttype: 'handshake-response',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\tnodeId: this.store.getNodeId(),\n\t\t\tversionVector: versionVectorToWire(serverVector),\n\t\t\tschemaVersion: this.schemaVersion,\n\t\t\taccepted: true,\n\t\t\tselectedWireFormat,\n\t\t}\n\t\tthis.transport.send(response)\n\n\t\tthis.emitter?.emit({ type: 'sync:connected', nodeId: msg.nodeId })\n\n\t\t// Transition to syncing and send delta\n\t\tthis.state = 'syncing'\n\t\tconst clientVector = wireToVersionVector(msg.versionVector)\n\t\tawait this.sendDelta(clientVector)\n\n\t\t// Transition to streaming after delta is sent\n\t\tthis.state = 'streaming'\n\t}\n\n\tprivate async handleOperationBatch(msg: OperationBatchMessage): Promise<void> {\n\t\tconst operations = msg.operations.map((s) => this.serializer.decodeOperation(s))\n\t\tconst applied: Operation[] = []\n\n\t\tfor (const op of operations) {\n\t\t\tif (!operationMatchesScopes(op, this.authContext?.scopes)) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst result = await this.store.applyRemoteOperation(op)\n\t\t\tif (result === 'applied') {\n\t\t\t\tapplied.push(op)\n\t\t\t}\n\t\t}\n\n\t\tif (operations.length > 0) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:received',\n\t\t\t\toperations,\n\t\t\t\tbatchSize: operations.length,\n\t\t\t})\n\t\t}\n\n\t\t// Send acknowledgment\n\t\tconst lastOp = operations[operations.length - 1]\n\t\tconst ack: SyncMessage = {\n\t\t\ttype: 'acknowledgment',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\tacknowledgedMessageId: msg.messageId,\n\t\t\tlastSequenceNumber: lastOp ? lastOp.sequenceNumber : 0,\n\t\t}\n\t\tthis.transport.send(ack)\n\n\t\t// Relay only newly applied operations to other sessions\n\t\tif (applied.length > 0) {\n\t\t\tthis.onRelay?.(this.sessionId, applied)\n\t\t}\n\t}\n\n\tprivate async sendDelta(clientVector: Map<string, number>): Promise<void> {\n\t\tconst serverVector = this.store.getVersionVector()\n\t\tconst missing: Operation[] = []\n\n\t\tfor (const [nodeId, serverSeq] of serverVector) {\n\t\t\tconst clientSeq = clientVector.get(nodeId) ?? 0\n\t\t\tif (serverSeq > clientSeq) {\n\t\t\t\tconst ops = await this.store.getOperationRange(nodeId, clientSeq + 1, serverSeq)\n\t\t\t\tconst visible = ops.filter((op) => operationMatchesScopes(op, this.authContext?.scopes))\n\t\t\t\tmissing.push(...visible)\n\t\t\t}\n\t\t}\n\n\t\tif (missing.length === 0) {\n\t\t\t// Send empty final batch to signal delta is complete\n\t\t\tconst emptyBatch: SyncMessage = {\n\t\t\t\ttype: 'operation-batch',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\toperations: [],\n\t\t\t\tisFinal: true,\n\t\t\t\tbatchIndex: 0,\n\t\t\t}\n\t\t\tthis.transport.send(emptyBatch)\n\t\t\treturn\n\t\t}\n\n\t\t// Sort causally and paginate\n\t\tconst sorted = topologicalSort(missing)\n\t\tconst totalBatches = Math.ceil(sorted.length / this.batchSize)\n\n\t\tfor (let i = 0; i < totalBatches; i++) {\n\t\t\tconst start = i * this.batchSize\n\t\t\tconst batchOps = sorted.slice(start, start + this.batchSize)\n\t\t\tconst serializedOps = batchOps.map((op) => this.serializer.encodeOperation(op))\n\n\t\t\tconst batchMsg: SyncMessage = {\n\t\t\t\ttype: 'operation-batch',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\toperations: serializedOps,\n\t\t\t\tisFinal: i === totalBatches - 1,\n\t\t\t\tbatchIndex: i,\n\t\t\t}\n\t\t\tthis.transport.send(batchMsg)\n\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'sync:sent',\n\t\t\t\toperations: batchOps,\n\t\t\t\tbatchSize: batchOps.length,\n\t\t\t})\n\t\t}\n\t}\n\n\tprivate sendError(code: string, message: string, retriable: boolean): void {\n\t\tconst errorMsg: SyncMessage = {\n\t\t\ttype: 'error',\n\t\t\tmessageId: generateUUIDv7(),\n\t\t\tcode,\n\t\t\tmessage,\n\t\t\tretriable,\n\t\t}\n\t\tthis.transport.send(errorMsg)\n\t}\n\n\tprivate setSerializerWireFormat(format: WireFormat): void {\n\t\tif (typeof this.serializer.setWireFormat === 'function') {\n\t\t\tthis.serializer.setWireFormat(format)\n\t\t}\n\t}\n\n\tprivate handleTransportClose(): void {\n\t\tif (this.state === 'closed') return\n\t\tthis.state = 'closed'\n\t\tthis.emitter?.emit({ type: 'sync:disconnected', reason: 'transport closed' })\n\t\tthis.onClose?.(this.sessionId)\n\t}\n}\n\nfunction selectWireFormat(supportedWireFormats?: WireFormat[]): WireFormat {\n\tif (supportedWireFormats?.includes('protobuf')) {\n\t\treturn 'protobuf'\n\t}\n\n\treturn 'json'\n}\n","import type { Operation } from '@korajs/core'\n\n/**\n * Per-collection scope map from auth context.\n */\nexport type ScopeMap = Record<string, Record<string, unknown>>\n\n/**\n * Returns true if an operation is visible to a session based on its scopes.\n *\n * Rules:\n * - No scopes configured => visible\n * - Collection missing from scope map => hidden\n * - All scoped field/value pairs must match the operation snapshot\n */\nexport function operationMatchesScopes(op: Operation, scopes: ScopeMap | undefined): boolean {\n\tif (!scopes) return true\n\n\tconst collectionScope = scopes[op.collection]\n\tif (!collectionScope) return false\n\n\tconst snapshot = buildSnapshot(op)\n\tif (!snapshot) return false\n\n\tfor (const [field, expected] of Object.entries(collectionScope)) {\n\t\tif (snapshot[field] !== expected) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\nfunction buildSnapshot(op: Operation): Record<string, unknown> | null {\n\tconst previous = asRecord(op.previousData)\n\tconst next = asRecord(op.data)\n\n\tif (!previous && !next) return null\n\n\treturn {\n\t\t...(previous ?? {}),\n\t\t...(next ?? {}),\n\t}\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) {\n\t\treturn null\n\t}\n\n\treturn value as Record<string, unknown>\n}\n","import type { KoraEventEmitter, Operation } from '@korajs/core'\nimport { SyncError, generateUUIDv7 } from '@korajs/core'\nimport type { MessageSerializer } from '@korajs/sync'\nimport { JsonMessageSerializer } from '@korajs/sync'\nimport { ClientSession } from '../session/client-session'\nimport type { ServerStore } from '../store/server-store'\nimport { HttpServerTransport } from '../transport/http-server-transport'\nimport type { ServerTransport } from '../transport/server-transport'\nimport { WsServerTransport } from '../transport/ws-server-transport'\nimport type {\n\tAuthProvider,\n\tHttpSyncRequest,\n\tHttpSyncResponse,\n\tKoraSyncServerConfig,\n\tServerStatus,\n} from '../types'\n\nconst DEFAULT_MAX_CONNECTIONS = 0 // unlimited\nconst DEFAULT_BATCH_SIZE = 100\nconst DEFAULT_SCHEMA_VERSION = 1\nconst DEFAULT_HOST = '0.0.0.0'\nconst DEFAULT_PATH = '/'\n\n/**\n * Minimal interface for a ws.WebSocketServer instance.\n * Allows dependency injection for testing without importing ws directly.\n */\nexport interface WsServerLike {\n\ton(event: string, listener: (...args: unknown[]) => void): void\n\tclose(callback?: (err?: Error) => void): void\n\taddress(): { port: number } | string | null\n}\n\n/**\n * Constructor type for creating a WebSocket server.\n */\nexport type WsServerConstructor = new (options: {\n\tport?: number\n\thost?: string\n\tpath?: string\n}) => WsServerLike\n\n/**\n * Self-hosted sync server. Accepts WebSocket connections from clients,\n * handles the sync protocol, stores operations, and relays changes\n * between connected clients.\n *\n * Two modes of operation:\n * 1. **Standalone**: Call `start()` with a port — creates its own WebSocket server.\n * 2. **Attach**: Call `handleConnection(transport)` — attach to an existing HTTP server.\n */\nexport class KoraSyncServer {\n\tprivate readonly store: ServerStore\n\tprivate readonly auth: AuthProvider | null\n\tprivate readonly serializer: MessageSerializer\n\tprivate readonly emitter: KoraEventEmitter | null\n\tprivate readonly maxConnections: number\n\tprivate readonly batchSize: number\n\tprivate readonly schemaVersion: number\n\tprivate readonly port: number | undefined\n\tprivate readonly host: string\n\tprivate readonly path: string\n\n\tprivate readonly sessions = new Map<string, ClientSession>()\n\tprivate readonly httpClients = new Map<string, { sessionId: string; transport: HttpServerTransport }>()\n\tprivate readonly httpSessionToClient = new Map<string, string>()\n\tprivate wsServer: WsServerLike | null = null\n\tprivate running = false\n\n\tconstructor(config: KoraSyncServerConfig) {\n\t\tthis.store = config.store\n\t\tthis.auth = config.auth ?? null\n\t\tthis.serializer = config.serializer ?? new JsonMessageSerializer()\n\t\tthis.emitter = config.emitter ?? null\n\t\tthis.maxConnections = config.maxConnections ?? DEFAULT_MAX_CONNECTIONS\n\t\tthis.batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE\n\t\tthis.schemaVersion = config.schemaVersion ?? DEFAULT_SCHEMA_VERSION\n\t\tthis.port = config.port\n\t\tthis.host = config.host ?? DEFAULT_HOST\n\t\tthis.path = config.path ?? DEFAULT_PATH\n\t}\n\n\t/**\n\t * Start the WebSocket server in standalone mode.\n\t *\n\t * @param wsServerImpl - Optional WebSocket server constructor for testing\n\t */\n\tasync start(wsServerImpl?: WsServerConstructor): Promise<void> {\n\t\tif (this.running) {\n\t\t\tthrow new SyncError('Server is already running', { port: this.port })\n\t\t}\n\n\t\tif (!wsServerImpl && this.port === undefined) {\n\t\t\tthrow new SyncError(\n\t\t\t\t'Port is required for standalone mode. Provide port in config or use handleConnection() for attach mode.',\n\t\t\t\t{},\n\t\t\t)\n\t\t}\n\n\t\tif (wsServerImpl) {\n\t\t\tthis.wsServer = new wsServerImpl({\n\t\t\t\tport: this.port,\n\t\t\t\thost: this.host,\n\t\t\t\tpath: this.path,\n\t\t\t})\n\t\t} else {\n\t\t\t// Dynamic import of ws — only needed in standalone mode\n\t\t\tconst { WebSocketServer } = await import('ws')\n\t\t\tthis.wsServer = new WebSocketServer({\n\t\t\t\tport: this.port,\n\t\t\t\thost: this.host,\n\t\t\t\tpath: this.path,\n\t\t\t})\n\t\t}\n\n\t\tthis.wsServer.on('connection', (ws: unknown) => {\n\t\t\tconst transport = new WsServerTransport(\n\t\t\t\tws as import('../transport/ws-server-transport').WsWebSocket,\n\t\t\t\t{\n\t\t\t\t\tserializer: this.serializer,\n\t\t\t\t},\n\t\t\t)\n\t\t\tthis.handleConnection(transport)\n\t\t})\n\n\t\tthis.running = true\n\t}\n\n\t/**\n\t * Stop the server. Closes all sessions and the WebSocket server.\n\t */\n\tasync stop(): Promise<void> {\n\t\t// Close all active sessions (works in both standalone and attach mode)\n\t\tfor (const session of this.sessions.values()) {\n\t\t\tsession.close('server shutting down')\n\t\t}\n\t\tthis.sessions.clear()\n\t\tthis.httpClients.clear()\n\t\tthis.httpSessionToClient.clear()\n\n\t\t// Close WebSocket server (standalone mode only)\n\t\tif (this.wsServer) {\n\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\tthis.wsServer?.close(() => resolve())\n\t\t\t})\n\t\t\tthis.wsServer = null\n\t\t}\n\n\t\tthis.running = false\n\t}\n\n\t/**\n\t * Handle one HTTP sync request for a long-polling client.\n\t *\n\t * A stable `clientId` identifies the logical connection across requests.\n\t */\n\tasync handleHttpRequest(request: HttpSyncRequest): Promise<HttpSyncResponse> {\n\t\tif (!request.clientId || request.clientId.trim().length === 0) {\n\t\t\treturn { status: 400 }\n\t\t}\n\n\t\tconst client = this.getOrCreateHttpClient(request.clientId)\n\n\t\tif (request.method === 'POST') {\n\t\t\tif (request.body === undefined) {\n\t\t\t\treturn { status: 400 }\n\t\t\t}\n\n\t\t\tconst payload = normalizeHttpBody(request.body, request.contentType)\n\t\t\tclient.transport.receive(payload)\n\t\t\treturn { status: 202 }\n\t\t}\n\n\t\tif (request.method === 'GET') {\n\t\t\tconst polled = client.transport.poll(request.ifNoneMatch)\n\t\t\treturn {\n\t\t\t\tstatus: polled.status,\n\t\t\t\tbody: polled.body,\n\t\t\t\theaders: polled.headers,\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tstatus: 405,\n\t\t\theaders: { allow: 'GET, POST' },\n\t\t}\n\t}\n\n\t/**\n\t * Handle an incoming client connection (attach mode).\n\t * Creates a new ClientSession for the transport.\n\t *\n\t * @param transport - The server transport for the new connection\n\t * @returns The session ID\n\t */\n\thandleConnection(transport: ServerTransport): string {\n\t\t// Check max connections\n\t\tif (this.maxConnections > 0 && this.sessions.size >= this.maxConnections) {\n\t\t\ttransport.send({\n\t\t\t\ttype: 'error',\n\t\t\t\tmessageId: generateUUIDv7(),\n\t\t\t\tcode: 'MAX_CONNECTIONS',\n\t\t\t\tmessage: `Server has reached maximum connections (${this.maxConnections})`,\n\t\t\t\tretriable: true,\n\t\t\t})\n\t\t\ttransport.close(4029, 'max connections reached')\n\t\t\tthrow new SyncError('Maximum connections reached', {\n\t\t\t\tcurrent: this.sessions.size,\n\t\t\t\tmax: this.maxConnections,\n\t\t\t})\n\t\t}\n\n\t\tconst sessionId = generateUUIDv7()\n\n\t\tconst session = new ClientSession({\n\t\t\tsessionId,\n\t\t\ttransport,\n\t\t\tstore: this.store,\n\t\t\tauth: this.auth ?? undefined,\n\t\t\tserializer: this.serializer,\n\t\t\temitter: this.emitter ?? undefined,\n\t\t\tbatchSize: this.batchSize,\n\t\t\tschemaVersion: this.schemaVersion,\n\t\t\tonRelay: (sourceSessionId, operations) => {\n\t\t\t\tthis.handleRelay(sourceSessionId, operations)\n\t\t\t},\n\t\t\tonClose: (sid) => {\n\t\t\t\tthis.handleSessionClose(sid)\n\t\t\t},\n\t\t})\n\n\t\tthis.sessions.set(sessionId, session)\n\t\tsession.start()\n\n\t\treturn sessionId\n\t}\n\n\t/**\n\t * Get the current server status.\n\t */\n\tasync getStatus(): Promise<ServerStatus> {\n\t\treturn {\n\t\t\trunning: this.running,\n\t\t\tconnectedClients: this.sessions.size,\n\t\t\tport: this.port ?? null,\n\t\t\ttotalOperations: await this.store.getOperationCount(),\n\t\t}\n\t}\n\n\t/**\n\t * Get the number of currently connected clients.\n\t */\n\tgetConnectionCount(): number {\n\t\treturn this.sessions.size\n\t}\n\n\t// --- Private ---\n\n\tprivate handleRelay(sourceSessionId: string, operations: Operation[]): void {\n\t\tfor (const [sessionId, session] of this.sessions) {\n\t\t\tif (sessionId === sourceSessionId) continue\n\t\t\tsession.relayOperations(operations)\n\t\t}\n\t}\n\n\tprivate handleSessionClose(sessionId: string): void {\n\t\tthis.sessions.delete(sessionId)\n\n\t\tconst clientId = this.httpSessionToClient.get(sessionId)\n\t\tif (clientId) {\n\t\t\tthis.httpSessionToClient.delete(sessionId)\n\t\t\tthis.httpClients.delete(clientId)\n\t\t}\n\t}\n\n\tprivate getOrCreateHttpClient(clientId: string): { sessionId: string; transport: HttpServerTransport } {\n\t\tconst existing = this.httpClients.get(clientId)\n\t\tif (existing) {\n\t\t\treturn existing\n\t\t}\n\n\t\tconst transport = new HttpServerTransport(this.serializer)\n\t\tconst sessionId = this.handleConnection(transport)\n\t\tconst client = { sessionId, transport }\n\n\t\tthis.httpClients.set(clientId, client)\n\t\tthis.httpSessionToClient.set(sessionId, clientId)\n\n\t\treturn client\n\t}\n}\n\nfunction normalizeHttpBody(body: string | Uint8Array, contentType?: string): string | Uint8Array {\n\tif (body instanceof Uint8Array) {\n\t\treturn body\n\t}\n\n\tif (contentType?.includes('application/x-protobuf')) {\n\t\treturn new TextEncoder().encode(body)\n\t}\n\n\treturn body\n}\n","import type { AuthContext, AuthProvider } from '../types'\n\n/**\n * Auth provider that accepts all connections.\n * Returns a default anonymous context for any token.\n * Useful for development and testing.\n */\nexport class NoAuthProvider implements AuthProvider {\n\tasync authenticate(_token: string): Promise<AuthContext> {\n\t\treturn { userId: 'anonymous' }\n\t}\n}\n","import type { AuthContext, AuthProvider } from '../types'\n\n/**\n * Options for creating a TokenAuthProvider.\n */\nexport interface TokenAuthProviderOptions {\n\t/**\n\t * Validate a token and return an AuthContext if valid, or null if rejected.\n\t * This is where you implement your auth logic (JWT verification, database lookup, etc.).\n\t */\n\tvalidate: (token: string) => Promise<AuthContext | null>\n}\n\n/**\n * Token-based auth provider that delegates validation to a user-provided function.\n *\n * @example\n * ```typescript\n * const auth = new TokenAuthProvider({\n * validate: async (token) => {\n * const user = await verifyJWT(token)\n * return user ? { userId: user.id } : null\n * }\n * })\n * ```\n */\nexport class TokenAuthProvider implements AuthProvider {\n\tprivate readonly validate: (token: string) => Promise<AuthContext | null>\n\n\tconstructor(options: TokenAuthProviderOptions) {\n\t\tthis.validate = options.validate\n\t}\n\n\tasync authenticate(token: string): Promise<AuthContext | null> {\n\t\treturn this.validate(token)\n\t}\n}\n","import type { KoraSyncServerConfig } from '../types'\nimport { KoraSyncServer } from './kora-sync-server'\n\n/**\n * Factory function to create a KoraSyncServer.\n *\n * @param config - Server configuration\n * @returns A new KoraSyncServer instance\n *\n * @example\n * ```typescript\n * const server = createKoraServer({\n * store: new MemoryServerStore(),\n * port: 3000,\n * })\n * await server.start()\n * ```\n */\nexport function createKoraServer(config: KoraSyncServerConfig): KoraSyncServer {\n\treturn new KoraSyncServer(config)\n}\n","import type { ServerStore } from '../store/server-store'\nimport type { KoraSyncServerConfig } from '../types'\nimport { KoraSyncServer } from './kora-sync-server'\nimport { WsServerTransport } from '../transport/ws-server-transport'\n\n/**\n * Configuration for the production server that serves both\n * static files and WebSocket sync on a single port.\n */\nexport interface ProductionServerConfig {\n\t/** Server-side operation store */\n\tstore: ServerStore\n\t/** Port to listen on. Defaults to 3001 or process.env.PORT. */\n\tport?: number\n\t/** Directory containing built static files. Defaults to './dist'. */\n\tstaticDir?: string\n\t/** WebSocket sync path. Defaults to '/kora-sync'. */\n\tsyncPath?: string\n\t/** Additional KoraSyncServer options */\n\tsyncOptions?: Omit<KoraSyncServerConfig, 'store' | 'port' | 'host' | 'path'>\n}\n\n/**\n * A production server handle returned by createProductionServer.\n */\nexport interface ProductionServer {\n\t/** Start listening. Returns the URL the server is available at. */\n\tstart(): Promise<string>\n\t/** Stop the server gracefully. */\n\tstop(): Promise<void>\n}\n\n// MIME types for static file serving\nconst MIME_TYPES: Record<string, string> = {\n\t'.html': 'text/html',\n\t'.js': 'text/javascript',\n\t'.css': 'text/css',\n\t'.json': 'application/json',\n\t'.png': 'image/png',\n\t'.jpg': 'image/jpeg',\n\t'.svg': 'image/svg+xml',\n\t'.ico': 'image/x-icon',\n\t'.wasm': 'application/wasm',\n\t'.woff': 'font/woff',\n\t'.woff2': 'font/woff2',\n\t'.map': 'application/json',\n}\n\n/**\n * Creates a production server that serves both static files and WebSocket sync\n * on a single port. This is the recommended way to deploy a Kora app — one port\n * means one tunnel (ngrok, cloudflared) handles everything.\n *\n * @param config - Production server configuration\n * @returns A ProductionServer instance\n *\n * @example\n * ```typescript\n * import { createProductionServer, createSqliteServerStore } from '@korajs/server'\n *\n * const server = createProductionServer({\n * store: createSqliteServerStore({ filename: './kora-server.db' }),\n * })\n *\n * const url = await server.start()\n * console.log(`App running at ${url}`)\n * ```\n */\nexport function createProductionServer(config: ProductionServerConfig): ProductionServer {\n\tconst port = config.port ?? (Number(process.env.PORT) || 3001)\n\tconst staticDir = config.staticDir ?? './dist'\n\tconst syncPath = config.syncPath ?? '/kora-sync'\n\n\tconst syncServer = new KoraSyncServer({\n\t\tstore: config.store,\n\t\t...config.syncOptions,\n\t})\n\n\tlet httpServer: import('node:http').Server | null = null\n\n\treturn {\n\t\tasync start(): Promise<string> {\n\t\t\tconst { createServer } = await import('node:http')\n\t\t\tconst { createReadStream, existsSync, statSync } = await import('node:fs')\n\t\t\tconst { extname, join, resolve } = await import('node:path')\n\t\t\tconst { WebSocketServer } = await import('ws')\n\n\t\t\tconst distDir = resolve(staticDir)\n\n\t\t\thttpServer = createServer((req, res) => {\n\t\t\t\t// COOP/COEP headers required for SharedArrayBuffer (OPFS persistence)\n\t\t\t\tres.setHeader('Cross-Origin-Opener-Policy', 'same-origin')\n\t\t\t\tres.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')\n\n\t\t\t\tconst url = new URL(req.url || '/', `http://${req.headers.host}`)\n\t\t\t\tlet filePath = join(distDir, url.pathname)\n\n\t\t\t\t// SPA fallback: serve index.html for non-file routes\n\t\t\t\tif (!extname(filePath)) {\n\t\t\t\t\tconst indexPath = join(filePath, 'index.html')\n\t\t\t\t\tif (existsSync(indexPath)) {\n\t\t\t\t\t\tfilePath = indexPath\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfilePath = join(distDir, 'index.html')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!existsSync(filePath)) {\n\t\t\t\t\tfilePath = join(distDir, 'index.html')\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tconst stat = statSync(filePath)\n\t\t\t\t\tif (stat.isDirectory()) {\n\t\t\t\t\t\tfilePath = join(filePath, 'index.html')\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\tfilePath = join(distDir, 'index.html')\n\t\t\t\t}\n\n\t\t\t\tif (!existsSync(filePath)) {\n\t\t\t\t\tres.writeHead(404)\n\t\t\t\t\tres.end('Not Found')\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst ext = extname(filePath)\n\t\t\t\tconst contentType = MIME_TYPES[ext] || 'application/octet-stream'\n\t\t\t\tres.writeHead(200, { 'Content-Type': contentType })\n\t\t\t\tcreateReadStream(filePath).pipe(res)\n\t\t\t})\n\n\t\t\tconst wss = new WebSocketServer({ noServer: true })\n\n\t\t\thttpServer.on('upgrade', (req, socket, head) => {\n\t\t\t\tconst url = new URL(req.url || '/', `http://${req.headers.host}`)\n\t\t\t\tif (url.pathname === syncPath) {\n\t\t\t\t\twss.handleUpgrade(req, socket, head, (ws) => {\n\t\t\t\t\t\tconst transport = new WsServerTransport(ws)\n\t\t\t\t\t\tsyncServer.handleConnection(transport)\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tsocket.destroy()\n\t\t\t\t}\n\t\t\t})\n\n\t\t\treturn new Promise<string>((resolve) => {\n\t\t\t\thttpServer!.listen(port, '0.0.0.0', () => {\n\t\t\t\t\tresolve(`http://localhost:${port}`)\n\t\t\t\t})\n\t\t\t})\n\t\t},\n\n\t\tasync stop(): Promise<void> {\n\t\t\tawait syncServer.stop()\n\t\t\tif (httpServer) {\n\t\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\t\thttpServer!.close(() => resolve())\n\t\t\t\t})\n\t\t\t\thttpServer = null\n\t\t\t}\n\t\t},\n\t}\n}\n"],"mappings":";AACA,SAAS,sBAAsB;AAQxB,IAAM,oBAAN,MAA+C;AAAA,EACpC;AAAA,EACA,aAA0B,CAAC;AAAA,EAC3B,iBAAiB,oBAAI,IAAuB;AAAA,EAC5C,gBAAqC,oBAAI,IAAI;AAAA,EACtD,SAAS;AAAA,EAEjB,YAAY,QAAiB;AAC5B,SAAK,SAAS,UAAU,eAAe;AAAA,EACxC;AAAA,EAEA,mBAAkC;AACjC,WAAO,IAAI,IAAI,KAAK,aAAa;AAAA,EAClC;AAAA,EAEA,YAAoB;AACnB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,MAAM,qBAAqB,IAAqC;AAC/D,SAAK,WAAW;AAGhB,QAAI,KAAK,eAAe,IAAI,GAAG,EAAE,GAAG;AACnC,aAAO;AAAA,IACR;AAEA,SAAK,WAAW,KAAK,EAAE;AACvB,SAAK,eAAe,IAAI,GAAG,IAAI,EAAE;AAGjC,UAAM,aAAa,KAAK,cAAc,IAAI,GAAG,MAAM,KAAK;AACxD,QAAI,GAAG,iBAAiB,YAAY;AACnC,WAAK,cAAc,IAAI,GAAG,QAAQ,GAAG,cAAc;AAAA,IACpD;AAEA,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,kBAAkB,QAAgB,SAAiB,OAAqC;AAC7F,SAAK,WAAW;AAEhB,WAAO,KAAK,WACV;AAAA,MACA,CAAC,OAAO,GAAG,WAAW,UAAU,GAAG,kBAAkB,WAAW,GAAG,kBAAkB;AAAA,IACtF,EACC,KAAK,CAAC,GAAG,MAAM,EAAE,iBAAiB,EAAE,cAAc;AAAA,EACrD;AAAA,EAEA,MAAM,oBAAqC;AAC1C,SAAK,WAAW;AAChB,WAAO,KAAK,WAAW;AAAA,EACxB;AAAA,EAEA,MAAM,QAAuB;AAC5B,SAAK,SAAS;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,mBAAgC;AAC/B,WAAO,CAAC,GAAG,KAAK,UAAU;AAAA,EAC3B;AAAA,EAEQ,aAAmB;AAC1B,QAAI,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC9C;AAAA,EACD;AACD;;;AChFA,SAAS,kBAAAA,uBAAsB;AAE/B,SAAS,KAAK,KAAK,SAAS,OAAO,IAAI,WAAW;;;ACHlD,SAAS,OAAO,SAAS,SAAS,YAAY;AAYvC,IAAM,eAAe;AAAA,EAC3B;AAAA,EACA;AAAA,IACC,IAAI,KAAK,IAAI,EAAE,WAAW;AAAA,IAC1B,QAAQ,KAAK,SAAS,EAAE,QAAQ;AAAA,IAChC,MAAM,KAAK,MAAM,EAAE,QAAQ;AAAA,IAC3B,YAAY,KAAK,YAAY,EAAE,QAAQ;AAAA,IACvC,UAAU,KAAK,WAAW,EAAE,QAAQ;AAAA,IACpC,MAAM,KAAK,MAAM;AAAA;AAAA,IACjB,cAAc,KAAK,eAAe;AAAA;AAAA,IAClC,UAAU,QAAQ,WAAW,EAAE,QAAQ;AAAA,IACvC,SAAS,QAAQ,SAAS,EAAE,QAAQ;AAAA,IACpC,iBAAiB,KAAK,mBAAmB,EAAE,QAAQ;AAAA,IACnD,gBAAgB,QAAQ,iBAAiB,EAAE,QAAQ;AAAA,IACnD,YAAY,KAAK,aAAa,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA;AAAA,IACtD,eAAe,QAAQ,gBAAgB,EAAE,QAAQ;AAAA,IACjD,YAAY,QAAQ,aAAa,EAAE,QAAQ;AAAA,EAC5C;AAAA,EACA,CAAC,WAAW;AAAA,IACX,YAAY,MAAM,iBAAiB,EAAE,GAAG,MAAM,QAAQ,MAAM,cAAc;AAAA,IAC1E,eAAe,MAAM,mBAAmB,EAAE,GAAG,MAAM,UAAU;AAAA,IAC7D,aAAa,MAAM,iBAAiB,EAAE,GAAG,MAAM,UAAU;AAAA,EAC1D;AACD;AAEO,IAAM,cAAc,QAAQ,cAAc;AAAA,EAChD,QAAQ,KAAK,SAAS,EAAE,WAAW;AAAA,EACnC,mBAAmB,QAAQ,qBAAqB,EAAE,QAAQ;AAAA,EAC1D,YAAY,QAAQ,cAAc,EAAE,QAAQ;AAC7C,CAAC;;;AD5BM,IAAM,sBAAN,MAAiD;AAAA,EACtC;AAAA,EACA;AAAA,EACA,gBAA+B,oBAAI,IAAI;AAAA,EACvC;AAAA,EACT,SAAS;AAAA,EAEjB,YAAY,IAAwB,QAAiB;AACpD,SAAK,KAAK;AACV,SAAK,SAAS,UAAUC,gBAAe;AACvC,SAAK,QAAQ,KAAK,WAAW;AAAA,EAC9B;AAAA,EAEA,mBAAkC;AACjC,SAAK,WAAW;AAChB,WAAO,IAAI,IAAI,KAAK,aAAa;AAAA,EAClC;AAAA,EAEA,YAAoB;AACnB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,MAAM,qBAAqB,IAAqC;AAC/D,SAAK,WAAW;AAChB,UAAM,KAAK;AAGX,UAAM,WAAW,MAAM,KAAK,GAC1B,OAAO,EAAE,IAAI,aAAa,GAAG,CAAC,EAC9B,KAAK,YAAY,EACjB,MAAM,GAAG,aAAa,IAAI,GAAG,EAAE,CAAC,EAChC,MAAM,CAAC;AAET,QAAI,SAAS,SAAS,GAAG;AACxB,aAAO;AAAA,IACR;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,MAAM,KAAK,mBAAmB,IAAI,GAAG;AAE3C,UAAM,KAAK,GAAG,YAAY,OAAO,OAAO;AAEvC,YAAM,GACJ,OAAO,YAAY,EACnB,OAAO,GAAG,EACV,oBAAoB,EAAE,QAAQ,aAAa,GAAG,CAAC;AAGjD,YAAM,GACJ,OAAO,WAAW,EAClB,OAAO;AAAA,QACP,QAAQ,GAAG;AAAA,QACX,mBAAmB,GAAG;AAAA,QACtB,YAAY;AAAA,MACb,CAAC,EACA,mBAAmB;AAAA,QACnB,QAAQ,YAAY;AAAA,QACpB,KAAK;AAAA,UACJ,mBAAmB,eAAe,YAAY,iBAAiB,KAAK,GAAG,cAAc;AAAA,UACrF,YAAY,MAAM,GAAG;AAAA,QACtB;AAAA,MACD,CAAC;AAAA,IACH,CAAC;AAGD,UAAM,aAAa,KAAK,cAAc,IAAI,GAAG,MAAM,KAAK;AACxD,QAAI,GAAG,iBAAiB,YAAY;AACnC,WAAK,cAAc,IAAI,GAAG,QAAQ,GAAG,cAAc;AAAA,IACpD;AAEA,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,kBAAkB,QAAgB,SAAiB,OAAqC;AAC7F,SAAK,WAAW;AAChB,UAAM,KAAK;AAEX,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO,EACP,KAAK,YAAY,EACjB;AAAA,MACA;AAAA,QACC,GAAG,aAAa,QAAQ,MAAM;AAAA,QAC9B,QAAQ,aAAa,gBAAgB,SAAS,KAAK;AAAA,MACpD;AAAA,IACD,EACC,QAAQ,IAAI,aAAa,cAAc,CAAC;AAE1C,WAAO,KAAK,IAAI,CAAC,QAAQ,KAAK,qBAAqB,GAAG,CAAC;AAAA,EACxD;AAAA,EAEA,MAAM,oBAAqC;AAC1C,SAAK,WAAW;AAChB,UAAM,KAAK;AAEX,UAAM,SAAS,MAAM,KAAK,GAAG,OAAO,EAAE,OAAO,MAAM,EAAE,CAAC,EAAE,KAAK,YAAY;AACzE,WAAO,OAAO,CAAC,GAAG,SAAS;AAAA,EAC5B;AAAA,EAEA,MAAM,QAAuB;AAC5B,SAAK,SAAS;AAAA,EACf;AAAA,EAEA,MAAc,aAA4B;AACzC,UAAM,KAAK,aAAa;AAGxB,UAAM,OAAO,MAAM,KAAK,GACtB,OAAO;AAAA,MACP,QAAQ,YAAY;AAAA,MACpB,mBAAmB,YAAY;AAAA,IAChC,CAAC,EACA,KAAK,WAAW;AAElB,eAAW,OAAO,MAAM;AACvB,WAAK,cAAc,IAAI,IAAI,QAAQ,IAAI,iBAAiB;AAAA,IACzD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,eAA8B;AAC3C,UAAM,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAiBrB;AAED,UAAM,KAAK,GAAG;AAAA,MACb;AAAA,IACD;AACA,UAAM,KAAK,GAAG;AAAA,MACb;AAAA,IACD;AACA,UAAM,KAAK,GAAG;AAAA,MACb;AAAA,IACD;AAEA,UAAM,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAMrB;AAAA,EACF;AAAA,EAEQ,mBACP,IACA,YACmC;AACnC,WAAO;AAAA,MACN,IAAI,GAAG;AAAA,MACP,QAAQ,GAAG;AAAA,MACX,MAAM,GAAG;AAAA,MACT,YAAY,GAAG;AAAA,MACf,UAAU,GAAG;AAAA,MACb,MAAM,GAAG,SAAS,OAAO,KAAK,UAAU,GAAG,IAAI,IAAI;AAAA,MACnD,cAAc,GAAG,iBAAiB,OAAO,KAAK,UAAU,GAAG,YAAY,IAAI;AAAA,MAC3E,UAAU,GAAG,UAAU;AAAA,MACvB,SAAS,GAAG,UAAU;AAAA,MACtB,iBAAiB,GAAG,UAAU;AAAA,MAC9B,gBAAgB,GAAG;AAAA,MACnB,YAAY,KAAK,UAAU,GAAG,UAAU;AAAA,MACxC,eAAe,GAAG;AAAA,MAClB;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,qBAAqB,KAAkD;AAC9E,WAAO;AAAA,MACN,IAAI,IAAI;AAAA,MACR,QAAQ,IAAI;AAAA,MACZ,MAAM,IAAI;AAAA,MACV,YAAY,IAAI;AAAA,MAChB,UAAU,IAAI;AAAA,MACd,MAAM,IAAI,SAAS,OAAO,KAAK,MAAM,IAAI,IAAI,IAAI;AAAA,MACjD,cAAc,IAAI,iBAAiB,OAAO,KAAK,MAAM,IAAI,YAAY,IAAI;AAAA,MACzE,WAAW;AAAA,QACV,UAAU,IAAI;AAAA,QACd,SAAS,IAAI;AAAA,QACb,QAAQ,IAAI;AAAA,MACb;AAAA,MACA,gBAAgB,IAAI;AAAA,MACpB,YAAY,KAAK,MAAM,IAAI,UAAU;AAAA,MACrC,eAAe,IAAI;AAAA,IACpB;AAAA,EACD;AAAA,EAEQ,aAAmB;AAC1B,QAAI,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,+BAA+B;AAAA,IAChD;AAAA,EACD;AACD;AASA,eAAsB,0BAA0B,SAGf;AAChC,QAAM,EAAE,gBAAgB,UAAU,IAAI,MAAM,iBAAiB;AAC7D,QAAM,SAAS,eAAe,QAAQ,gBAAgB;AACtD,QAAM,KAAK,UAAU,MAAM;AAE3B,SAAO,IAAI,oBAAoB,IAAI,QAAQ,MAAM;AAClD;AAEA,eAAe,mBAGZ;AACF,MAAI;AACH,UAAM,gBAAgB,IAAI,SAAS,aAAa,0BAA0B;AAI1E,UAAM,cAAe,MAAM,cAAc,UAAU;AACnD,UAAM,aAAc,MAAM,cAAc,yBAAyB;AAIjE,WAAO;AAAA,MACN,gBAAgB,YAAY;AAAA,MAC5B,WAAW,WAAW;AAAA,IACvB;AAAA,EACD,QAAQ;AACP,UAAM,IAAI;AAAA,MACT;AAAA,IACD;AAAA,EACD;AACD;;;AEzQA,SAAS,qBAAqB;AAE9B,SAAS,kBAAAC,uBAAsB;AAE/B,SAAS,OAAAC,MAAK,OAAAC,MAAK,WAAAC,UAAS,SAAAC,QAAO,MAAAC,KAAI,OAAAC,YAAW;;;ACJlD,SAAS,SAAAC,QAAO,WAAAC,UAAS,aAAa,QAAAC,aAAY;AAU3C,IAAM,aAAa;AAAA,EACzB;AAAA,EACA;AAAA,IACC,IAAIA,MAAK,IAAI,EAAE,WAAW;AAAA,IAC1B,QAAQA,MAAK,SAAS,EAAE,QAAQ;AAAA,IAChC,MAAMA,MAAK,MAAM,EAAE,QAAQ;AAAA,IAC3B,YAAYA,MAAK,YAAY,EAAE,QAAQ;AAAA,IACvC,UAAUA,MAAK,WAAW,EAAE,QAAQ;AAAA,IACpC,MAAMA,MAAK,MAAM;AAAA;AAAA,IACjB,cAAcA,MAAK,eAAe;AAAA;AAAA,IAClC,UAAUD,SAAQ,WAAW,EAAE,QAAQ;AAAA,IACvC,SAASA,SAAQ,SAAS,EAAE,QAAQ;AAAA,IACpC,iBAAiBC,MAAK,mBAAmB,EAAE,QAAQ;AAAA,IACnD,gBAAgBD,SAAQ,iBAAiB,EAAE,QAAQ;AAAA,IACnD,YAAYC,MAAK,aAAa,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA;AAAA,IACtD,eAAeD,SAAQ,gBAAgB,EAAE,QAAQ;AAAA,IACjD,YAAYA,SAAQ,aAAa,EAAE,QAAQ;AAAA,EAC5C;AAAA,EACA,CAAC,WAAW;AAAA,IACX,YAAYD,OAAM,cAAc,EAAE,GAAG,MAAM,QAAQ,MAAM,cAAc;AAAA,IACvE,eAAeA,OAAM,gBAAgB,EAAE,GAAG,MAAM,UAAU;AAAA,IAC1D,aAAaA,OAAM,cAAc,EAAE,GAAG,MAAM,UAAU;AAAA,EACvD;AACD;AAEO,IAAM,YAAY,YAAY,cAAc;AAAA,EAClD,QAAQE,MAAK,SAAS,EAAE,WAAW;AAAA,EACnC,mBAAmBD,SAAQ,qBAAqB,EAAE,QAAQ;AAAA,EAC1D,YAAYA,SAAQ,cAAc,EAAE,QAAQ;AAC7C,CAAC;;;AD3BD,IAAM,aAAa,cAAc,YAAY,GAAG;AAOzC,IAAM,oBAAN,MAA+C;AAAA,EACpC;AAAA,EACA;AAAA,EACT,SAAS;AAAA,EAEjB,YAAY,IAA2B,QAAiB;AACvD,SAAK,KAAK;AACV,SAAK,SAAS,UAAUE,gBAAe;AACvC,SAAK,aAAa;AAAA,EACnB;AAAA,EAEA,mBAAkC;AACjC,SAAK,WAAW;AAChB,UAAM,OAAO,KAAK,GAAG,OAAO,EAAE,KAAK,SAAS,EAAE,IAAI;AAClD,UAAM,KAAoB,oBAAI,IAAI;AAClC,eAAW,OAAO,MAAM;AACvB,SAAG,IAAI,IAAI,QAAQ,IAAI,iBAAiB;AAAA,IACzC;AACA,WAAO;AAAA,EACR;AAAA,EAEA,YAAoB;AACnB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,MAAM,qBAAqB,IAAqC;AAC/D,SAAK,WAAW;AAEhB,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,MAAM,KAAK,mBAAmB,IAAI,GAAG;AAG3C,UAAM,SAAS,KAAK,GAAG,YAAY,CAAC,OAAO;AAE1C,YAAM,eAAe,GACnB,OAAO,UAAU,EACjB,OAAO,GAAG,EACV,oBAAoB,EAAE,QAAQ,WAAW,GAAG,CAAC,EAC7C,IAAI;AAEN,UAAI,aAAa,YAAY,GAAG;AAC/B,eAAO;AAAA,MACR;AAGA,SAAG,OAAO,SAAS,EACjB,OAAO;AAAA,QACP,QAAQ,GAAG;AAAA,QACX,mBAAmB,GAAG;AAAA,QACtB,YAAY;AAAA,MACb,CAAC,EACA,mBAAmB;AAAA,QACnB,QAAQ,UAAU;AAAA,QAClB,KAAK;AAAA,UACJ,mBAAmBC,WAAU,UAAU,iBAAiB,KAAK,GAAG,cAAc;AAAA,UAC9E,YAAYA,OAAM,GAAG;AAAA,QACtB;AAAA,MACD,CAAC,EACA,IAAI;AAEN,aAAO;AAAA,IACR,CAAC;AAED,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,kBAAkB,QAAgB,SAAiB,OAAqC;AAC7F,SAAK,WAAW;AAEhB,UAAM,OAAO,KAAK,GAChB,OAAO,EACP,KAAK,UAAU,EACf,MAAMC,KAAIC,IAAG,WAAW,QAAQ,MAAM,GAAGC,SAAQ,WAAW,gBAAgB,SAAS,KAAK,CAAC,CAAC,EAC5F,QAAQC,KAAI,WAAW,cAAc,CAAC,EACtC,IAAI;AAEN,WAAO,KAAK,IAAI,CAAC,QAAQ,KAAK,qBAAqB,GAAG,CAAC;AAAA,EACxD;AAAA,EAEA,MAAM,oBAAqC;AAC1C,SAAK,WAAW;AAEhB,UAAM,SAAS,KAAK,GAAG,OAAO,EAAE,OAAOC,OAAM,EAAE,CAAC,EAAE,KAAK,UAAU,EAAE,IAAI;AACvE,WAAO,OAAO,CAAC,GAAG,SAAS;AAAA,EAC5B;AAAA,EAEA,MAAM,QAAuB;AAC5B,SAAK,SAAS;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,eAAqB;AAC5B,SAAK,GAAG,IAAIL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAiBX;AAED,SAAK,GAAG,IAAIA;AAAA;AAAA,GAEX;AAED,SAAK,GAAG,IAAIA;AAAA;AAAA,GAEX;AAED,SAAK,GAAG,IAAIA;AAAA;AAAA,GAEX;AAED,SAAK,GAAG,IAAIA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAMX;AAAA,EACF;AAAA,EAEQ,mBACP,IACA,YACiC;AACjC,WAAO;AAAA,MACN,IAAI,GAAG;AAAA,MACP,QAAQ,GAAG;AAAA,MACX,MAAM,GAAG;AAAA,MACT,YAAY,GAAG;AAAA,MACf,UAAU,GAAG;AAAA,MACb,MAAM,GAAG,SAAS,OAAO,KAAK,UAAU,GAAG,IAAI,IAAI;AAAA,MACnD,cAAc,GAAG,iBAAiB,OAAO,KAAK,UAAU,GAAG,YAAY,IAAI;AAAA,MAC3E,UAAU,GAAG,UAAU;AAAA,MACvB,SAAS,GAAG,UAAU;AAAA,MACtB,iBAAiB,GAAG,UAAU;AAAA,MAC9B,gBAAgB,GAAG;AAAA,MACnB,YAAY,KAAK,UAAU,GAAG,UAAU;AAAA,MACxC,eAAe,GAAG;AAAA,MAClB;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,qBAAqB,KAAgD;AAC5E,WAAO;AAAA,MACN,IAAI,IAAI;AAAA,MACR,QAAQ,IAAI;AAAA,MACZ,MAAM,IAAI;AAAA,MACV,YAAY,IAAI;AAAA,MAChB,UAAU,IAAI;AAAA,MACd,MAAM,IAAI,SAAS,OAAO,KAAK,MAAM,IAAI,IAAI,IAAI;AAAA,MACjD,cAAc,IAAI,iBAAiB,OAAO,KAAK,MAAM,IAAI,YAAY,IAAI;AAAA,MACzE,WAAW;AAAA,QACV,UAAU,IAAI;AAAA,QACd,SAAS,IAAI;AAAA,QACb,QAAQ,IAAI;AAAA,MACb;AAAA,MACA,gBAAgB,IAAI;AAAA,MACpB,YAAY,KAAK,MAAM,IAAI,UAAU;AAAA,MACrC,eAAe,IAAI;AAAA,IACpB;AAAA,EACD;AAAA,EAEQ,aAAmB;AAC1B,QAAI,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC9C;AAAA,EACD;AACD;AAmBO,SAAS,wBAAwB,SAGlB;AAGrB,QAAM,WAAW,WAAW,gBAAgB;AAC5C,QAAM,EAAE,QAAQ,IAAI,WAAW,4BAA4B;AAE3D,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,SAAS,IAAI,SAAS,QAAQ;AAGpC,SAAO,OAAO,oBAAoB;AAElC,QAAM,KAAK,QAAQ,MAAM;AACzB,SAAO,IAAI,kBAAkB,IAAI,QAAQ,MAAM;AAChD;;;AE7OA,SAAS,iBAAiB;AA2BnB,IAAM,sBAAN,MAAqD;AAAA,EAC1C;AAAA,EAET,iBAA8C;AAAA,EAC9C,eAA0C;AAAA,EAC1C,eAA0C;AAAA,EAE1C,YAAY;AAAA,EACZ,eAAe;AAAA,EACN,QAAyB,CAAC;AAAA,EAE3C,YAAY,YAA+B;AAC1C,SAAK,aAAa;AAAA,EACnB;AAAA,EAEA,KAAK,SAAmD;AACvD,QAAI,CAAC,KAAK,UAAW;AAErB,UAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAC9C,UAAM,WAAW,mBAAmB;AACpC,SAAK,MAAM,KAAK;AAAA,MACf,MAAM,KAAK,SAAS,KAAK,cAAc;AAAA,MACvC,aAAa,WAAW,2BAA2B;AAAA,MACnD,SAAS;AAAA,IACV,CAAC;AAAA,EACF;AAAA,EAEA,UAAU,SAAqC;AAC9C,SAAK,iBAAiB;AAAA,EACvB;AAAA,EAEA,QAAQ,SAAmC;AAC1C,SAAK,eAAe;AAAA,EACrB;AAAA,EAEA,QAAQ,SAAmC;AAC1C,SAAK,eAAe;AAAA,EACrB;AAAA,EAEA,cAAuB;AACtB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,MAAM,OAAO,KAAM,SAAS,oBAA0B;AACrD,QAAI,CAAC,KAAK,UAAW;AACrB,SAAK,YAAY;AACjB,SAAK,MAAM,SAAS;AACpB,SAAK,eAAe,MAAM,MAAM;AAAA,EACjC;AAAA,EAEA,QAAQ,SAAoC;AAC3C,QAAI,CAAC,KAAK,WAAW;AACpB,YAAM,IAAI,UAAU,iCAAiC;AAAA,IACtD;AAEA,QAAI;AACH,YAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAC9C,WAAK,iBAAiB,OAAO;AAAA,IAC9B,SAAS,OAAO;AACf,WAAK,eAAe,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IAC9E;AAAA,EACD;AAAA,EAEA,KAAK,aAAwC;AAC5C,QAAI,CAAC,KAAK,WAAW;AACpB,aAAO,EAAE,QAAQ,IAAI;AAAA,IACtB;AAEA,UAAM,OAAO,KAAK,MAAM,CAAC;AACzB,QAAI,CAAC,MAAM;AACV,aAAO,EAAE,QAAQ,IAAI;AAAA,IACtB;AAEA,QAAI,eAAe,gBAAgB,KAAK,MAAM;AAC7C,aAAO;AAAA,QACN,QAAQ;AAAA,QACR,SAAS,EAAE,MAAM,KAAK,KAAK;AAAA,MAC5B;AAAA,IACD;AAEA,SAAK,MAAM,MAAM;AACjB,WAAO;AAAA,MACN,QAAQ;AAAA,MACR,MAAM,KAAK;AAAA,MACX,SAAS;AAAA,QACR,gBAAgB,KAAK;AAAA,QACrB,MAAM,KAAK;AAAA,MACZ;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,SAAS,UAA0B;AAC1C,WAAO,MAAM,QAAQ;AAAA,EACtB;AACD;;;ACzHA,SAAS,aAAAM,kBAAiB;AAE1B,SAAS,6BAA6B;AAUtC,IAAM,UAAU;AA0BT,IAAM,oBAAN,MAAmD;AAAA,EACxC;AAAA,EACA;AAAA,EACT,iBAA8C;AAAA,EAC9C,eAA0C;AAAA,EAC1C,eAA0C;AAAA,EAElD,YAAY,IAAiB,SAAoC;AAChE,SAAK,KAAK;AACV,SAAK,aAAa,SAAS,cAAc,IAAI,sBAAsB;AACnE,SAAK,eAAe;AAAA,EACrB;AAAA,EAEA,KAAK,SAA4B;AAChC,QAAI,KAAK,GAAG,eAAe,SAAS;AACnC,YAAM,IAAIA,WAAU,8CAA8C;AAAA,QACjE,YAAY,KAAK,GAAG;AAAA,QACpB,aAAa,QAAQ;AAAA,MACtB,CAAC;AAAA,IACF;AAEA,UAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAC9C,SAAK,GAAG,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,UAAU,SAAqC;AAC9C,SAAK,iBAAiB;AAAA,EACvB;AAAA,EAEA,QAAQ,SAAmC;AAC1C,SAAK,eAAe;AAAA,EACrB;AAAA,EAEA,QAAQ,SAAmC;AAC1C,SAAK,eAAe;AAAA,EACrB;AAAA,EAEA,cAAuB;AACtB,WAAO,KAAK,GAAG,eAAe;AAAA,EAC/B;AAAA,EAEA,MAAM,MAAe,QAAuB;AAC3C,SAAK,GAAG,MAAM,QAAQ,KAAM,UAAU,gBAAgB;AAAA,EACvD;AAAA,EAEQ,iBAAuB;AAC9B,SAAK,GAAG,GAAG,WAAW,CAAC,SAAkB;AACxC,UAAI;AACH,YACC,OAAO,SAAS,YAChB,EAAE,gBAAgB,eAClB,EAAE,gBAAgB,cACjB;AACD,gBAAM,IAAIA,WAAU,sCAAsC;AAAA,YACzD,aAAa,OAAO;AAAA,UACrB,CAAC;AAAA,QACF;AAEA,cAAM,UAAU,KAAK,WAAW,OAAO,IAAI;AAC3C,aAAK,iBAAiB,OAAO;AAAA,MAC9B,SAAS,KAAK;AACb,aAAK,eAAe,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,MACxE;AAAA,IACD,CAAC;AAED,SAAK,GAAG,GAAG,SAAS,CAAC,MAAe,WAAoB;AACvD,WAAK,eAAe,OAAO,IAAI,KAAK,MAAM,OAAO,UAAU,mBAAmB,CAAC;AAAA,IAChF,CAAC;AAED,SAAK,GAAG,GAAG,SAAS,CAAC,QAAiB;AACrC,WAAK,eAAe,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,IACxE,CAAC;AAAA,EACF;AACD;;;AC9GA,SAAoB,kBAAAC,uBAAsB;AAC1C,SAAS,uBAAuB;AAQhC;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,OACM;;;ACCA,SAAS,uBAAuB,IAAe,QAAuC;AAC5F,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,kBAAkB,OAAO,GAAG,UAAU;AAC5C,MAAI,CAAC,gBAAiB,QAAO;AAE7B,QAAM,WAAW,cAAc,EAAE;AACjC,MAAI,CAAC,SAAU,QAAO;AAEtB,aAAW,CAAC,OAAO,QAAQ,KAAK,OAAO,QAAQ,eAAe,GAAG;AAChE,QAAI,SAAS,KAAK,MAAM,UAAU;AACjC,aAAO;AAAA,IACR;AAAA,EACD;AAEA,SAAO;AACR;AAEA,SAAS,cAAc,IAA+C;AACrE,QAAM,WAAW,SAAS,GAAG,YAAY;AACzC,QAAM,OAAO,SAAS,GAAG,IAAI;AAE7B,MAAI,CAAC,YAAY,CAAC,KAAM,QAAO;AAE/B,SAAO;AAAA,IACN,GAAI,YAAY,CAAC;AAAA,IACjB,GAAI,QAAQ,CAAC;AAAA,EACd;AACD;AAEA,SAAS,SAAS,OAAgD;AACjE,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG;AACxE,WAAO;AAAA,EACR;AAEA,SAAO;AACR;;;AD/BA,IAAM,qBAAqB;AAC3B,IAAM,yBAAyB;AAoDxB,IAAM,gBAAN,MAAoB;AAAA,EAClB,QAAsB;AAAA,EACtB,eAA8B;AAAA,EAC9B,cAAkC;AAAA,EAEzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAA+B;AAC1C,SAAK,YAAY,QAAQ;AACzB,SAAK,YAAY,QAAQ;AACzB,SAAK,QAAQ,QAAQ;AACrB,SAAK,OAAO,QAAQ,QAAQ;AAC5B,SAAK,aAAa,QAAQ,cAAc,IAAI,4BAA4B,MAAM;AAC9E,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,gBAAgB,QAAQ,iBAAiB;AAC9C,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,UAAU,QAAQ,WAAW;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACb,SAAK,UAAU,UAAU,CAAC,QAAQ,KAAK,cAAc,GAAG,CAAC;AACzD,SAAK,UAAU,QAAQ,CAAC,OAAO,YAAY,KAAK,qBAAqB,CAAC;AACtE,SAAK,UAAU,QAAQ,CAAC,SAAS;AAEhC,UAAI,KAAK,UAAU,UAAU;AAC5B,aAAK,qBAAqB;AAAA,MAC3B;AAAA,IACD,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAgBC,aAA+B;AAC9C,QAAI,KAAK,UAAU,eAAe,CAAC,KAAK,UAAU,YAAY,EAAG;AACjE,QAAIA,YAAW,WAAW,EAAG;AAE7B,UAAM,oBAAoBA,YAAW;AAAA,MAAO,CAAC,OAC5C,uBAAuB,IAAI,KAAK,aAAa,MAAM;AAAA,IACpD;AACA,QAAI,kBAAkB,WAAW,EAAG;AAEpC,UAAM,gBAAgB,kBAAkB,IAAI,CAAC,OAAO,KAAK,WAAW,gBAAgB,EAAE,CAAC;AACvF,UAAM,MAAmB;AAAA,MACxB,MAAM;AAAA,MACN,WAAWC,gBAAe;AAAA,MAC1B,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,YAAY;AAAA,IACb;AACA,SAAK,UAAU,KAAK,GAAG;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC5B,QAAI,KAAK,UAAU,SAAU;AAC7B,SAAK,QAAQ;AAEb,QAAI,KAAK,UAAU,YAAY,GAAG;AACjC,WAAK,UAAU,MAAM,KAAM,UAAU,gBAAgB;AAAA,IACtD;AAEA,SAAK,UAAU,KAAK,SAAS;AAAA,EAC9B;AAAA;AAAA,EAIA,WAAyB;AACxB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,eAAuB;AACtB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,kBAAiC;AAChC,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,iBAAqC;AACpC,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,cAAuB;AACtB,WAAO,KAAK,UAAU;AAAA,EACvB;AAAA;AAAA,EAIQ,cAAc,SAA4B;AACjD,YAAQ,QAAQ,MAAM;AAAA,MACrB,KAAK;AACJ,aAAK,gBAAgB,OAAO;AAC5B;AAAA,MACD,KAAK;AACJ,aAAK,qBAAqB,OAAO;AACjC;AAAA;AAAA,MAED,KAAK;AACJ;AAAA,MACD,KAAK;AACJ;AAAA,IACF;AAAA,EACD;AAAA,EAEA,MAAc,gBAAgB,KAAsC;AAEnE,QAAI,KAAK,UAAU,aAAa;AAC/B,WAAK,UAAU,uBAAuB,+BAA+B,KAAK;AAC1E;AAAA,IACD;AAEA,SAAK,eAAe,IAAI;AAGxB,QAAI,KAAK,MAAM;AACd,YAAM,QAAQ,IAAI,aAAa;AAC/B,YAAM,UAAU,MAAM,KAAK,KAAK,aAAa,KAAK;AAClD,UAAI,CAAC,SAAS;AACb,aAAK,UAAU,eAAe,yBAAyB,KAAK;AAC5D,aAAK,MAAM,uBAAuB;AAClC;AAAA,MACD;AACA,WAAK,cAAc;AACnB,WAAK,QAAQ;AAAA,IACd;AAGA,UAAM,eAAe,KAAK,MAAM,iBAAiB;AACjD,UAAM,qBAAqB,iBAAiB,IAAI,oBAAoB;AACpE,SAAK,wBAAwB,kBAAkB;AAC/C,UAAM,WAAwB;AAAA,MAC7B,MAAM;AAAA,MACN,WAAWA,gBAAe;AAAA,MAC1B,QAAQ,KAAK,MAAM,UAAU;AAAA,MAC7B,eAAe,oBAAoB,YAAY;AAAA,MAC/C,eAAe,KAAK;AAAA,MACpB,UAAU;AAAA,MACV;AAAA,IACD;AACA,SAAK,UAAU,KAAK,QAAQ;AAE5B,SAAK,SAAS,KAAK,EAAE,MAAM,kBAAkB,QAAQ,IAAI,OAAO,CAAC;AAGjE,SAAK,QAAQ;AACb,UAAM,eAAe,oBAAoB,IAAI,aAAa;AAC1D,UAAM,KAAK,UAAU,YAAY;AAGjC,SAAK,QAAQ;AAAA,EACd;AAAA,EAEA,MAAc,qBAAqB,KAA2C;AAC7E,UAAMD,cAAa,IAAI,WAAW,IAAI,CAAC,MAAM,KAAK,WAAW,gBAAgB,CAAC,CAAC;AAC/E,UAAM,UAAuB,CAAC;AAE9B,eAAW,MAAMA,aAAY;AAC5B,UAAI,CAAC,uBAAuB,IAAI,KAAK,aAAa,MAAM,GAAG;AAC1D;AAAA,MACD;AAEA,YAAM,SAAS,MAAM,KAAK,MAAM,qBAAqB,EAAE;AACvD,UAAI,WAAW,WAAW;AACzB,gBAAQ,KAAK,EAAE;AAAA,MAChB;AAAA,IACD;AAEA,QAAIA,YAAW,SAAS,GAAG;AAC1B,WAAK,SAAS,KAAK;AAAA,QAClB,MAAM;AAAA,QACN,YAAAA;AAAA,QACA,WAAWA,YAAW;AAAA,MACvB,CAAC;AAAA,IACF;AAGA,UAAM,SAASA,YAAWA,YAAW,SAAS,CAAC;AAC/C,UAAM,MAAmB;AAAA,MACxB,MAAM;AAAA,MACN,WAAWC,gBAAe;AAAA,MAC1B,uBAAuB,IAAI;AAAA,MAC3B,oBAAoB,SAAS,OAAO,iBAAiB;AAAA,IACtD;AACA,SAAK,UAAU,KAAK,GAAG;AAGvB,QAAI,QAAQ,SAAS,GAAG;AACvB,WAAK,UAAU,KAAK,WAAW,OAAO;AAAA,IACvC;AAAA,EACD;AAAA,EAEA,MAAc,UAAU,cAAkD;AACzE,UAAM,eAAe,KAAK,MAAM,iBAAiB;AACjD,UAAM,UAAuB,CAAC;AAE9B,eAAW,CAAC,QAAQ,SAAS,KAAK,cAAc;AAC/C,YAAM,YAAY,aAAa,IAAI,MAAM,KAAK;AAC9C,UAAI,YAAY,WAAW;AAC1B,cAAM,MAAM,MAAM,KAAK,MAAM,kBAAkB,QAAQ,YAAY,GAAG,SAAS;AAC/E,cAAM,UAAU,IAAI,OAAO,CAAC,OAAO,uBAAuB,IAAI,KAAK,aAAa,MAAM,CAAC;AACvF,gBAAQ,KAAK,GAAG,OAAO;AAAA,MACxB;AAAA,IACD;AAEA,QAAI,QAAQ,WAAW,GAAG;AAEzB,YAAM,aAA0B;AAAA,QAC/B,MAAM;AAAA,QACN,WAAWA,gBAAe;AAAA,QAC1B,YAAY,CAAC;AAAA,QACb,SAAS;AAAA,QACT,YAAY;AAAA,MACb;AACA,WAAK,UAAU,KAAK,UAAU;AAC9B;AAAA,IACD;AAGA,UAAM,SAAS,gBAAgB,OAAO;AACtC,UAAM,eAAe,KAAK,KAAK,OAAO,SAAS,KAAK,SAAS;AAE7D,aAAS,IAAI,GAAG,IAAI,cAAc,KAAK;AACtC,YAAM,QAAQ,IAAI,KAAK;AACvB,YAAM,WAAW,OAAO,MAAM,OAAO,QAAQ,KAAK,SAAS;AAC3D,YAAM,gBAAgB,SAAS,IAAI,CAAC,OAAO,KAAK,WAAW,gBAAgB,EAAE,CAAC;AAE9E,YAAM,WAAwB;AAAA,QAC7B,MAAM;AAAA,QACN,WAAWA,gBAAe;AAAA,QAC1B,YAAY;AAAA,QACZ,SAAS,MAAM,eAAe;AAAA,QAC9B,YAAY;AAAA,MACb;AACA,WAAK,UAAU,KAAK,QAAQ;AAE5B,WAAK,SAAS,KAAK;AAAA,QAClB,MAAM;AAAA,QACN,YAAY;AAAA,QACZ,WAAW,SAAS;AAAA,MACrB,CAAC;AAAA,IACF;AAAA,EACD;AAAA,EAEQ,UAAU,MAAc,SAAiB,WAA0B;AAC1E,UAAM,WAAwB;AAAA,MAC7B,MAAM;AAAA,MACN,WAAWA,gBAAe;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,IACD;AACA,SAAK,UAAU,KAAK,QAAQ;AAAA,EAC7B;AAAA,EAEQ,wBAAwB,QAA0B;AACzD,QAAI,OAAO,KAAK,WAAW,kBAAkB,YAAY;AACxD,WAAK,WAAW,cAAc,MAAM;AAAA,IACrC;AAAA,EACD;AAAA,EAEQ,uBAA6B;AACpC,QAAI,KAAK,UAAU,SAAU;AAC7B,SAAK,QAAQ;AACb,SAAK,SAAS,KAAK,EAAE,MAAM,qBAAqB,QAAQ,mBAAmB,CAAC;AAC5E,SAAK,UAAU,KAAK,SAAS;AAAA,EAC9B;AACD;AAEA,SAAS,iBAAiB,sBAAiD;AAC1E,MAAI,sBAAsB,SAAS,UAAU,GAAG;AAC/C,WAAO;AAAA,EACR;AAEA,SAAO;AACR;;;AE3WA,SAAS,aAAAC,YAAW,kBAAAC,uBAAsB;AAE1C,SAAS,yBAAAC,8BAA6B;AActC,IAAM,0BAA0B;AAChC,IAAMC,sBAAqB;AAC3B,IAAMC,0BAAyB;AAC/B,IAAM,eAAe;AACrB,IAAM,eAAe;AA8Bd,IAAM,iBAAN,MAAqB;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,WAAW,oBAAI,IAA2B;AAAA,EAC1C,cAAc,oBAAI,IAAmE;AAAA,EACrF,sBAAsB,oBAAI,IAAoB;AAAA,EACvD,WAAgC;AAAA,EAChC,UAAU;AAAA,EAElB,YAAY,QAA8B;AACzC,SAAK,QAAQ,OAAO;AACpB,SAAK,OAAO,OAAO,QAAQ;AAC3B,SAAK,aAAa,OAAO,cAAc,IAAIC,uBAAsB;AACjE,SAAK,UAAU,OAAO,WAAW;AACjC,SAAK,iBAAiB,OAAO,kBAAkB;AAC/C,SAAK,YAAY,OAAO,aAAaF;AACrC,SAAK,gBAAgB,OAAO,iBAAiBC;AAC7C,SAAK,OAAO,OAAO;AACnB,SAAK,OAAO,OAAO,QAAQ;AAC3B,SAAK,OAAO,OAAO,QAAQ;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,MAAM,cAAmD;AAC9D,QAAI,KAAK,SAAS;AACjB,YAAM,IAAIE,WAAU,6BAA6B,EAAE,MAAM,KAAK,KAAK,CAAC;AAAA,IACrE;AAEA,QAAI,CAAC,gBAAgB,KAAK,SAAS,QAAW;AAC7C,YAAM,IAAIA;AAAA,QACT;AAAA,QACA,CAAC;AAAA,MACF;AAAA,IACD;AAEA,QAAI,cAAc;AACjB,WAAK,WAAW,IAAI,aAAa;AAAA,QAChC,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,MACZ,CAAC;AAAA,IACF,OAAO;AAEN,YAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,IAAI;AAC7C,WAAK,WAAW,IAAI,gBAAgB;AAAA,QACnC,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,MACZ,CAAC;AAAA,IACF;AAEA,SAAK,SAAS,GAAG,cAAc,CAAC,OAAgB;AAC/C,YAAM,YAAY,IAAI;AAAA,QACrB;AAAA,QACA;AAAA,UACC,YAAY,KAAK;AAAA,QAClB;AAAA,MACD;AACA,WAAK,iBAAiB,SAAS;AAAA,IAChC,CAAC;AAED,SAAK,UAAU;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAsB;AAE3B,eAAW,WAAW,KAAK,SAAS,OAAO,GAAG;AAC7C,cAAQ,MAAM,sBAAsB;AAAA,IACrC;AACA,SAAK,SAAS,MAAM;AACpB,SAAK,YAAY,MAAM;AACvB,SAAK,oBAAoB,MAAM;AAG/B,QAAI,KAAK,UAAU;AAClB,YAAM,IAAI,QAAc,CAAC,YAAY;AACpC,aAAK,UAAU,MAAM,MAAM,QAAQ,CAAC;AAAA,MACrC,CAAC;AACD,WAAK,WAAW;AAAA,IACjB;AAEA,SAAK,UAAU;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,kBAAkB,SAAqD;AAC5E,QAAI,CAAC,QAAQ,YAAY,QAAQ,SAAS,KAAK,EAAE,WAAW,GAAG;AAC9D,aAAO,EAAE,QAAQ,IAAI;AAAA,IACtB;AAEA,UAAM,SAAS,KAAK,sBAAsB,QAAQ,QAAQ;AAE1D,QAAI,QAAQ,WAAW,QAAQ;AAC9B,UAAI,QAAQ,SAAS,QAAW;AAC/B,eAAO,EAAE,QAAQ,IAAI;AAAA,MACtB;AAEA,YAAM,UAAU,kBAAkB,QAAQ,MAAM,QAAQ,WAAW;AACnE,aAAO,UAAU,QAAQ,OAAO;AAChC,aAAO,EAAE,QAAQ,IAAI;AAAA,IACtB;AAEA,QAAI,QAAQ,WAAW,OAAO;AAC7B,YAAM,SAAS,OAAO,UAAU,KAAK,QAAQ,WAAW;AACxD,aAAO;AAAA,QACN,QAAQ,OAAO;AAAA,QACf,MAAM,OAAO;AAAA,QACb,SAAS,OAAO;AAAA,MACjB;AAAA,IACD;AAEA,WAAO;AAAA,MACN,QAAQ;AAAA,MACR,SAAS,EAAE,OAAO,YAAY;AAAA,IAC/B;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,iBAAiB,WAAoC;AAEpD,QAAI,KAAK,iBAAiB,KAAK,KAAK,SAAS,QAAQ,KAAK,gBAAgB;AACzE,gBAAU,KAAK;AAAA,QACd,MAAM;AAAA,QACN,WAAWC,gBAAe;AAAA,QAC1B,MAAM;AAAA,QACN,SAAS,2CAA2C,KAAK,cAAc;AAAA,QACvE,WAAW;AAAA,MACZ,CAAC;AACD,gBAAU,MAAM,MAAM,yBAAyB;AAC/C,YAAM,IAAID,WAAU,+BAA+B;AAAA,QAClD,SAAS,KAAK,SAAS;AAAA,QACvB,KAAK,KAAK;AAAA,MACX,CAAC;AAAA,IACF;AAEA,UAAM,YAAYC,gBAAe;AAEjC,UAAM,UAAU,IAAI,cAAc;AAAA,MACjC;AAAA,MACA;AAAA,MACA,OAAO,KAAK;AAAA,MACZ,MAAM,KAAK,QAAQ;AAAA,MACnB,YAAY,KAAK;AAAA,MACjB,SAAS,KAAK,WAAW;AAAA,MACzB,WAAW,KAAK;AAAA,MAChB,eAAe,KAAK;AAAA,MACpB,SAAS,CAAC,iBAAiBC,gBAAe;AACzC,aAAK,YAAY,iBAAiBA,WAAU;AAAA,MAC7C;AAAA,MACA,SAAS,CAAC,QAAQ;AACjB,aAAK,mBAAmB,GAAG;AAAA,MAC5B;AAAA,IACD,CAAC;AAED,SAAK,SAAS,IAAI,WAAW,OAAO;AACpC,YAAQ,MAAM;AAEd,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAmC;AACxC,WAAO;AAAA,MACN,SAAS,KAAK;AAAA,MACd,kBAAkB,KAAK,SAAS;AAAA,MAChC,MAAM,KAAK,QAAQ;AAAA,MACnB,iBAAiB,MAAM,KAAK,MAAM,kBAAkB;AAAA,IACrD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,qBAA6B;AAC5B,WAAO,KAAK,SAAS;AAAA,EACtB;AAAA;AAAA,EAIQ,YAAY,iBAAyBA,aAA+B;AAC3E,eAAW,CAAC,WAAW,OAAO,KAAK,KAAK,UAAU;AACjD,UAAI,cAAc,gBAAiB;AACnC,cAAQ,gBAAgBA,WAAU;AAAA,IACnC;AAAA,EACD;AAAA,EAEQ,mBAAmB,WAAyB;AACnD,SAAK,SAAS,OAAO,SAAS;AAE9B,UAAM,WAAW,KAAK,oBAAoB,IAAI,SAAS;AACvD,QAAI,UAAU;AACb,WAAK,oBAAoB,OAAO,SAAS;AACzC,WAAK,YAAY,OAAO,QAAQ;AAAA,IACjC;AAAA,EACD;AAAA,EAEQ,sBAAsB,UAAyE;AACtG,UAAM,WAAW,KAAK,YAAY,IAAI,QAAQ;AAC9C,QAAI,UAAU;AACb,aAAO;AAAA,IACR;AAEA,UAAM,YAAY,IAAI,oBAAoB,KAAK,UAAU;AACzD,UAAM,YAAY,KAAK,iBAAiB,SAAS;AACjD,UAAM,SAAS,EAAE,WAAW,UAAU;AAEtC,SAAK,YAAY,IAAI,UAAU,MAAM;AACrC,SAAK,oBAAoB,IAAI,WAAW,QAAQ;AAEhD,WAAO;AAAA,EACR;AACD;AAEA,SAAS,kBAAkB,MAA2B,aAA2C;AAChG,MAAI,gBAAgB,YAAY;AAC/B,WAAO;AAAA,EACR;AAEA,MAAI,aAAa,SAAS,wBAAwB,GAAG;AACpD,WAAO,IAAI,YAAY,EAAE,OAAO,IAAI;AAAA,EACrC;AAEA,SAAO;AACR;;;ACvSO,IAAM,iBAAN,MAA6C;AAAA,EACnD,MAAM,aAAa,QAAsC;AACxD,WAAO,EAAE,QAAQ,YAAY;AAAA,EAC9B;AACD;;;ACeO,IAAM,oBAAN,MAAgD;AAAA,EACrC;AAAA,EAEjB,YAAY,SAAmC;AAC9C,SAAK,WAAW,QAAQ;AAAA,EACzB;AAAA,EAEA,MAAM,aAAa,OAA4C;AAC9D,WAAO,KAAK,SAAS,KAAK;AAAA,EAC3B;AACD;;;AClBO,SAAS,iBAAiB,QAA8C;AAC9E,SAAO,IAAI,eAAe,MAAM;AACjC;;;ACaA,IAAM,aAAqC;AAAA,EAC1C,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAQ;AACT;AAsBO,SAAS,uBAAuB,QAAkD;AACxF,QAAM,OAAO,OAAO,SAAS,OAAO,QAAQ,IAAI,IAAI,KAAK;AACzD,QAAM,YAAY,OAAO,aAAa;AACtC,QAAM,WAAW,OAAO,YAAY;AAEpC,QAAM,aAAa,IAAI,eAAe;AAAA,IACrC,OAAO,OAAO;AAAA,IACd,GAAG,OAAO;AAAA,EACX,CAAC;AAED,MAAI,aAAgD;AAEpD,SAAO;AAAA,IACN,MAAM,QAAyB;AAC9B,YAAM,EAAE,aAAa,IAAI,MAAM,OAAO,MAAW;AACjD,YAAM,EAAE,kBAAkB,YAAY,SAAS,IAAI,MAAM,OAAO,IAAS;AACzE,YAAM,EAAE,SAAS,MAAM,QAAQ,IAAI,MAAM,OAAO,MAAW;AAC3D,YAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,IAAI;AAE7C,YAAM,UAAU,QAAQ,SAAS;AAEjC,mBAAa,aAAa,CAAC,KAAK,QAAQ;AAEvC,YAAI,UAAU,8BAA8B,aAAa;AACzD,YAAI,UAAU,gCAAgC,cAAc;AAE5D,cAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ,IAAI,EAAE;AAChE,YAAI,WAAW,KAAK,SAAS,IAAI,QAAQ;AAGzC,YAAI,CAAC,QAAQ,QAAQ,GAAG;AACvB,gBAAM,YAAY,KAAK,UAAU,YAAY;AAC7C,cAAI,WAAW,SAAS,GAAG;AAC1B,uBAAW;AAAA,UACZ,OAAO;AACN,uBAAW,KAAK,SAAS,YAAY;AAAA,UACtC;AAAA,QACD;AAEA,YAAI,CAAC,WAAW,QAAQ,GAAG;AAC1B,qBAAW,KAAK,SAAS,YAAY;AAAA,QACtC;AAEA,YAAI;AACH,gBAAM,OAAO,SAAS,QAAQ;AAC9B,cAAI,KAAK,YAAY,GAAG;AACvB,uBAAW,KAAK,UAAU,YAAY;AAAA,UACvC;AAAA,QACD,QAAQ;AACP,qBAAW,KAAK,SAAS,YAAY;AAAA,QACtC;AAEA,YAAI,CAAC,WAAW,QAAQ,GAAG;AAC1B,cAAI,UAAU,GAAG;AACjB,cAAI,IAAI,WAAW;AACnB;AAAA,QACD;AAEA,cAAM,MAAM,QAAQ,QAAQ;AAC5B,cAAM,cAAc,WAAW,GAAG,KAAK;AACvC,YAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,yBAAiB,QAAQ,EAAE,KAAK,GAAG;AAAA,MACpC,CAAC;AAED,YAAM,MAAM,IAAI,gBAAgB,EAAE,UAAU,KAAK,CAAC;AAElD,iBAAW,GAAG,WAAW,CAAC,KAAK,QAAQ,SAAS;AAC/C,cAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ,IAAI,EAAE;AAChE,YAAI,IAAI,aAAa,UAAU;AAC9B,cAAI,cAAc,KAAK,QAAQ,MAAM,CAAC,OAAO;AAC5C,kBAAM,YAAY,IAAI,kBAAkB,EAAE;AAC1C,uBAAW,iBAAiB,SAAS;AAAA,UACtC,CAAC;AAAA,QACF,OAAO;AACN,iBAAO,QAAQ;AAAA,QAChB;AAAA,MACD,CAAC;AAED,aAAO,IAAI,QAAgB,CAACC,aAAY;AACvC,mBAAY,OAAO,MAAM,WAAW,MAAM;AACzC,UAAAA,SAAQ,oBAAoB,IAAI,EAAE;AAAA,QACnC,CAAC;AAAA,MACF,CAAC;AAAA,IACF;AAAA,IAEA,MAAM,OAAsB;AAC3B,YAAM,WAAW,KAAK;AACtB,UAAI,YAAY;AACf,cAAM,IAAI,QAAc,CAAC,YAAY;AACpC,qBAAY,MAAM,MAAM,QAAQ,CAAC;AAAA,QAClC,CAAC;AACD,qBAAa;AAAA,MACd;AAAA,IACD;AAAA,EACD;AACD;","names":["generateUUIDv7","generateUUIDv7","generateUUIDv7","and","asc","between","count","eq","sql","index","integer","text","generateUUIDv7","sql","and","eq","between","asc","count","SyncError","generateUUIDv7","operations","generateUUIDv7","SyncError","generateUUIDv7","JsonMessageSerializer","DEFAULT_BATCH_SIZE","DEFAULT_SCHEMA_VERSION","JsonMessageSerializer","SyncError","generateUUIDv7","operations","resolve"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@korajs/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Kora.js self-hosted sync server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"better-sqlite3": "^12.8.0",
|
|
36
36
|
"drizzle-orm": "^0.45.2",
|
|
37
37
|
"ws": "^8.18.0",
|
|
38
|
-
"@korajs/
|
|
39
|
-
"@korajs/
|
|
38
|
+
"@korajs/sync": "0.1.5",
|
|
39
|
+
"@korajs/core": "0.1.3"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"postgres": "^3.4.0"
|