@syncular/testkit 0.0.0 → 0.0.2-136

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.
Files changed (48) hide show
  1. package/dist/assertions.d.ts +61 -0
  2. package/dist/assertions.d.ts.map +1 -0
  3. package/dist/assertions.js +239 -0
  4. package/dist/assertions.js.map +1 -0
  5. package/dist/faults.d.ts +40 -0
  6. package/dist/faults.d.ts.map +1 -0
  7. package/dist/faults.js +136 -0
  8. package/dist/faults.js.map +1 -0
  9. package/dist/fixtures.d.ts +100 -0
  10. package/dist/fixtures.d.ts.map +1 -0
  11. package/dist/fixtures.js +541 -0
  12. package/dist/fixtures.js.map +1 -0
  13. package/dist/hono-node-server.d.ts +10 -0
  14. package/dist/hono-node-server.d.ts.map +1 -0
  15. package/dist/hono-node-server.js +63 -0
  16. package/dist/hono-node-server.js.map +1 -0
  17. package/dist/http-fixtures.d.ts +57 -0
  18. package/dist/http-fixtures.d.ts.map +1 -0
  19. package/dist/http-fixtures.js +107 -0
  20. package/dist/http-fixtures.js.map +1 -0
  21. package/dist/index.d.ts +10 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +10 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/project-scoped-tasks.d.ts +40 -0
  26. package/dist/project-scoped-tasks.d.ts.map +1 -0
  27. package/dist/project-scoped-tasks.js +245 -0
  28. package/dist/project-scoped-tasks.js.map +1 -0
  29. package/dist/runtime-process.d.ts +11 -0
  30. package/dist/runtime-process.d.ts.map +1 -0
  31. package/dist/runtime-process.js +92 -0
  32. package/dist/runtime-process.js.map +1 -0
  33. package/dist/sync-http.d.ts +48 -0
  34. package/dist/sync-http.d.ts.map +1 -0
  35. package/dist/sync-http.js +30 -0
  36. package/dist/sync-http.js.map +1 -0
  37. package/dist/sync-response.d.ts +7 -0
  38. package/dist/sync-response.d.ts.map +1 -0
  39. package/dist/sync-response.js +19 -0
  40. package/dist/sync-response.js.map +1 -0
  41. package/package.json +12 -12
  42. package/src/faults.ts +0 -3
  43. package/src/fixtures.ts +0 -3
  44. package/src/index.ts +3 -0
  45. package/src/project-scoped-tasks.ts +51 -1
  46. package/src/runtime-process.ts +133 -0
  47. package/src/sync-http.ts +100 -0
  48. package/src/sync-response.ts +45 -0
@@ -0,0 +1,57 @@
1
+ import { ClientTableRegistry, enqueueOutboxCommit, type SyncClientDb, type SyncOnceOptions, type SyncOnceResult, type SyncPullOnceOptions, type SyncPullResponse, type SyncPushOnceOptions, type SyncPushOnceResult } from '@syncular/client';
2
+ import type { SyncTransport } from '@syncular/core';
3
+ import { type ServerSyncDialect, type ServerTableHandler, type SyncCoreDb } from '@syncular/server';
4
+ import { type CreateSyncRoutesOptions } from '@syncular/server-hono';
5
+ import { Hono } from 'hono';
6
+ import type { Kysely } from 'kysely';
7
+ import { createNodeHonoServer } from './hono-node-server';
8
+ export type HttpServerDialect = 'sqlite' | 'pglite';
9
+ export type HttpClientDialect = 'bun-sqlite' | 'pglite';
10
+ export interface HttpServerFixture<DB extends SyncCoreDb> {
11
+ db: Kysely<DB>;
12
+ dialect: ServerSyncDialect;
13
+ app: Hono;
14
+ httpServer: ReturnType<typeof createNodeHonoServer>;
15
+ baseUrl: string;
16
+ destroy: () => Promise<void>;
17
+ }
18
+ export interface CreateHttpServerFixtureOptions<DB extends SyncCoreDb> {
19
+ serverDialect: HttpServerDialect;
20
+ createTables: (db: Kysely<DB>) => Promise<void>;
21
+ handlers: ServerTableHandler<DB>[];
22
+ authenticate: CreateSyncRoutesOptions<DB>['authenticate'];
23
+ sync?: CreateSyncRoutesOptions<DB>['sync'];
24
+ routePath?: string;
25
+ cors?: boolean;
26
+ corsAllowMethods?: string;
27
+ corsAllowHeaders?: string;
28
+ corsMaxAgeSeconds?: number;
29
+ }
30
+ export interface HttpClientFixture<DB extends SyncClientDb> {
31
+ db: Kysely<DB>;
32
+ transport: SyncTransport;
33
+ handlers: ClientTableRegistry<DB>;
34
+ actorId: string;
35
+ clientId: string;
36
+ enqueue: (args: Parameters<typeof enqueueOutboxCommit<DB>>[1]) => Promise<{
37
+ id: string;
38
+ clientCommitId: string;
39
+ }>;
40
+ push: (options?: Omit<SyncPushOnceOptions, 'clientId' | 'actorId'>) => Promise<SyncPushOnceResult>;
41
+ pull: (options: Omit<SyncPullOnceOptions, 'clientId' | 'actorId'>) => Promise<SyncPullResponse>;
42
+ syncOnce: (options: Omit<SyncOnceOptions, 'clientId' | 'actorId'>) => Promise<SyncOnceResult>;
43
+ destroy: () => Promise<void>;
44
+ }
45
+ export interface CreateHttpClientFixtureOptions<DB extends SyncClientDb> {
46
+ clientDialect: HttpClientDialect;
47
+ baseUrl: string;
48
+ actorId: string;
49
+ clientId: string;
50
+ createTables: (db: Kysely<DB>) => Promise<void>;
51
+ registerHandlers: (handlers: ClientTableRegistry<DB>) => void;
52
+ fetch?: typeof globalThis.fetch;
53
+ getHeaders?: () => Record<string, string>;
54
+ }
55
+ export declare function createHttpServerFixture<DB extends SyncCoreDb>(options: CreateHttpServerFixtureOptions<DB>): Promise<HttpServerFixture<DB>>;
56
+ export declare function createHttpClientFixture<DB extends SyncClientDb>(options: CreateHttpClientFixtureOptions<DB>): Promise<HttpClientFixture<DB>>;
57
+ //# sourceMappingURL=http-fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-fixtures.d.ts","sourceRoot":"","sources":["../src/http-fixtures.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,107 @@
1
+ import { ClientTableRegistry, enqueueOutboxCommit, ensureClientSyncSchema, syncOnce, syncPullOnce, syncPushOnce, } from '@syncular/client';
2
+ import { createBunSqliteDb } from '@syncular/dialect-bun-sqlite';
3
+ import { createPgliteDb } from '@syncular/dialect-pglite';
4
+ import { ensureSyncSchema, } from '@syncular/server';
5
+ import { createPostgresServerDialect } from '@syncular/server-dialect-postgres';
6
+ import { createSqliteServerDialect } from '@syncular/server-dialect-sqlite';
7
+ import { createSyncRoutes, } from '@syncular/server-hono';
8
+ import { createHttpTransport } from '@syncular/transport-http';
9
+ import { Hono } from 'hono';
10
+ import { createNodeHonoServer } from './hono-node-server.js';
11
+ export async function createHttpServerFixture(options) {
12
+ const db = options.serverDialect === 'pglite'
13
+ ? createPgliteDb()
14
+ : createBunSqliteDb({ path: ':memory:' });
15
+ const dialect = options.serverDialect === 'pglite'
16
+ ? createPostgresServerDialect()
17
+ : createSqliteServerDialect();
18
+ await ensureSyncSchema(db, dialect);
19
+ if (dialect.ensureConsoleSchema) {
20
+ await dialect.ensureConsoleSchema(db);
21
+ }
22
+ await options.createTables(db);
23
+ const app = new Hono();
24
+ const routePath = options.routePath ?? '/sync';
25
+ const syncRoutes = createSyncRoutes({
26
+ db,
27
+ dialect,
28
+ handlers: options.handlers,
29
+ authenticate: options.authenticate,
30
+ sync: options.sync,
31
+ });
32
+ app.route(routePath, syncRoutes);
33
+ const httpServer = createNodeHonoServer(app, {
34
+ cors: options.cors,
35
+ corsAllowMethods: options.corsAllowMethods,
36
+ corsAllowHeaders: options.corsAllowHeaders,
37
+ corsMaxAgeSeconds: options.corsMaxAgeSeconds,
38
+ });
39
+ await new Promise((resolve) => {
40
+ httpServer.listen(0, resolve);
41
+ });
42
+ const address = httpServer.address();
43
+ const port = typeof address === 'object' && address ? address.port : 0;
44
+ return {
45
+ db,
46
+ dialect,
47
+ app,
48
+ httpServer,
49
+ baseUrl: `http://localhost:${port}`,
50
+ destroy: async () => {
51
+ await new Promise((resolve, reject) => {
52
+ httpServer.close((err) => {
53
+ if (err) {
54
+ reject(err);
55
+ return;
56
+ }
57
+ resolve();
58
+ });
59
+ });
60
+ await db.destroy();
61
+ },
62
+ };
63
+ }
64
+ export async function createHttpClientFixture(options) {
65
+ const db = options.clientDialect === 'pglite'
66
+ ? createPgliteDb()
67
+ : createBunSqliteDb({ path: ':memory:' });
68
+ await ensureClientSyncSchema(db);
69
+ await options.createTables(db);
70
+ const handlers = new ClientTableRegistry();
71
+ options.registerHandlers(handlers);
72
+ const transport = createHttpTransport({
73
+ baseUrl: options.baseUrl,
74
+ getHeaders: options.getHeaders ??
75
+ (() => ({
76
+ 'x-actor-id': options.actorId,
77
+ })),
78
+ ...(options.fetch ? { fetch: options.fetch } : {}),
79
+ });
80
+ return {
81
+ db,
82
+ transport,
83
+ handlers,
84
+ actorId: options.actorId,
85
+ clientId: options.clientId,
86
+ enqueue: (args) => enqueueOutboxCommit(db, args),
87
+ push: (pushOptions) => syncPushOnce(db, transport, {
88
+ clientId: options.clientId,
89
+ actorId: options.actorId,
90
+ plugins: pushOptions?.plugins,
91
+ }),
92
+ pull: (pullOptions) => syncPullOnce(db, transport, handlers, {
93
+ ...pullOptions,
94
+ clientId: options.clientId,
95
+ actorId: options.actorId,
96
+ }),
97
+ syncOnce: (syncOptions) => syncOnce(db, transport, handlers, {
98
+ ...syncOptions,
99
+ clientId: options.clientId,
100
+ actorId: options.actorId,
101
+ }),
102
+ destroy: async () => {
103
+ await db.destroy();
104
+ },
105
+ };
106
+ }
107
+ //# sourceMappingURL=http-fixtures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-fixtures.js","sourceRoot":"","sources":["../src/http-fixtures.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export * from './assertions';
2
+ export * from './faults';
3
+ export * from './fixtures';
4
+ export * from './hono-node-server';
5
+ export * from './http-fixtures';
6
+ export * from './project-scoped-tasks';
7
+ export * from './runtime-process';
8
+ export * from './sync-http';
9
+ export * from './sync-response';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ export * from './assertions.js';
2
+ export * from './faults.js';
3
+ export * from './fixtures.js';
4
+ export * from './hono-node-server.js';
5
+ export * from './http-fixtures.js';
6
+ export * from './project-scoped-tasks.js';
7
+ export * from './runtime-process.js';
8
+ export * from './sync-http.js';
9
+ export * from './sync-response.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { type SyncOperation, type SyncSubscriptionRequest } from '@syncular/core';
2
+ import type { ServerTableHandler, SyncCoreDb } from '@syncular/server';
3
+ import type { Kysely } from 'kysely';
4
+ export interface ProjectScopedTasksRow {
5
+ id: string;
6
+ title: string;
7
+ completed: number;
8
+ user_id: string;
9
+ project_id: string;
10
+ server_version: number;
11
+ }
12
+ export interface ProjectScopedTasksDb extends SyncCoreDb {
13
+ tasks: ProjectScopedTasksRow;
14
+ }
15
+ export declare const PROJECT_SCOPED_TASKS_DDL = "\n CREATE TABLE IF NOT EXISTS tasks (\n id TEXT PRIMARY KEY,\n title TEXT NOT NULL DEFAULT '',\n completed INTEGER NOT NULL DEFAULT 0,\n user_id TEXT NOT NULL,\n project_id TEXT NOT NULL,\n server_version INTEGER NOT NULL DEFAULT 1\n )\n";
16
+ export interface ProjectScopedTasksHandlerOptions {
17
+ projectScopeCount?: number;
18
+ }
19
+ export declare function ensureProjectScopedTasksTable<DB extends SyncCoreDb & {
20
+ tasks: ProjectScopedTasksRow;
21
+ }>(db: Kysely<DB>): Promise<void>;
22
+ export declare function createProjectScopedTasksHandler<DB extends SyncCoreDb & {
23
+ tasks: ProjectScopedTasksRow;
24
+ }>(options?: ProjectScopedTasksHandlerOptions): ServerTableHandler<DB>;
25
+ export interface CreateProjectScopedTasksSubscriptionOptions {
26
+ id?: string;
27
+ userId: string;
28
+ projectId?: string;
29
+ cursor?: number;
30
+ }
31
+ export declare function createProjectScopedTasksSubscription(options: CreateProjectScopedTasksSubscriptionOptions): SyncSubscriptionRequest;
32
+ export interface CreateProjectScopedTaskUpsertOperationOptions {
33
+ taskId: string;
34
+ title: string;
35
+ completed?: number;
36
+ projectId?: string;
37
+ baseVersion?: number | null;
38
+ }
39
+ export declare function createProjectScopedTaskUpsertOperation(options: CreateProjectScopedTaskUpsertOperationOptions): SyncOperation;
40
+ //# sourceMappingURL=project-scoped-tasks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-scoped-tasks.d.ts","sourceRoot":"","sources":["../src/project-scoped-tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,uBAAuB,EAC7B,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAGV,kBAAkB,EAClB,UAAU,EACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGrC,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAqB,SAAQ,UAAU;IACtD,KAAK,EAAE,qBAAqB,CAAC;CAC9B;AAED,eAAO,MAAM,wBAAwB,sQASpC,CAAC;AAEF,MAAM,WAAW,gCAAgC;IAC/C,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAoBD,wBAAsB,6BAA6B,CACjD,EAAE,SAAS,UAAU,GAAG;IAAE,KAAK,EAAE,qBAAqB,CAAA;CAAE,EACxD,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/B;AAED,wBAAgB,+BAA+B,CAC7C,EAAE,SAAS,UAAU,GAAG;IAAE,KAAK,EAAE,qBAAqB,CAAA;CAAE,EACxD,OAAO,GAAE,gCAAqC,GAAG,kBAAkB,CAAC,EAAE,CAAC,CA0OxE;AAED,MAAM,WAAW,2CAA2C;IAC1D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,oCAAoC,CAClD,OAAO,EAAE,2CAA2C,GACnD,uBAAuB,CAWzB;AAED,MAAM,WAAW,6CAA6C;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,wBAAgB,sCAAsC,CACpD,OAAO,EAAE,6CAA6C,GACrD,aAAa,CAYf"}
@@ -0,0 +1,245 @@
1
+ import { isRecord, } from '@syncular/core';
2
+ import { sql } from 'kysely';
3
+ export const PROJECT_SCOPED_TASKS_DDL = `
4
+ CREATE TABLE IF NOT EXISTS tasks (
5
+ id TEXT PRIMARY KEY,
6
+ title TEXT NOT NULL DEFAULT '',
7
+ completed INTEGER NOT NULL DEFAULT 0,
8
+ user_id TEXT NOT NULL,
9
+ project_id TEXT NOT NULL,
10
+ server_version INTEGER NOT NULL DEFAULT 1
11
+ )
12
+ `;
13
+ function parseProjectScopedTaskPayload(payload) {
14
+ if (!isRecord(payload)) {
15
+ return {};
16
+ }
17
+ return {
18
+ title: typeof payload.title === 'string' ? payload.title : undefined,
19
+ completed: typeof payload.completed === 'number' ? payload.completed : undefined,
20
+ project_id: typeof payload.project_id === 'string' ? payload.project_id : undefined,
21
+ };
22
+ }
23
+ export async function ensureProjectScopedTasksTable(db) {
24
+ await sql.raw(PROJECT_SCOPED_TASKS_DDL).execute(db);
25
+ }
26
+ export function createProjectScopedTasksHandler(options = {}) {
27
+ const projectScopeCount = Math.max(1, options.projectScopeCount ?? 100);
28
+ return {
29
+ table: 'tasks',
30
+ scopePatterns: ['user:{user_id}:project:{project_id}'],
31
+ async resolveScopes(ctx) {
32
+ return {
33
+ user_id: ctx.actorId,
34
+ project_id: Array.from({ length: projectScopeCount }, (_, index) => `p${index}`),
35
+ };
36
+ },
37
+ extractScopes(row) {
38
+ return {
39
+ user_id: String(row.user_id ?? ''),
40
+ project_id: String(row.project_id ?? ''),
41
+ };
42
+ },
43
+ async snapshot(ctx) {
44
+ const userIdValue = ctx.scopeValues.user_id;
45
+ const projectIdValue = ctx.scopeValues.project_id;
46
+ const userId = Array.isArray(userIdValue) ? userIdValue[0] : userIdValue;
47
+ const projectId = Array.isArray(projectIdValue)
48
+ ? projectIdValue[0]
49
+ : projectIdValue;
50
+ if (!userId || userId !== ctx.actorId) {
51
+ return { rows: [], nextCursor: null };
52
+ }
53
+ if (!projectId) {
54
+ return { rows: [], nextCursor: null };
55
+ }
56
+ const pageSize = Math.max(1, Math.min(10_000, ctx.limit));
57
+ const cursor = ctx.cursor;
58
+ const cursorFilter = cursor && cursor.length > 0
59
+ ? sql `and ${sql.ref('id')} > ${sql.val(cursor)}`
60
+ : sql ``;
61
+ const result = await sql `
62
+ select id, title, completed, user_id, project_id, server_version
63
+ from tasks
64
+ where user_id = ${sql.val(userId)}
65
+ and project_id = ${sql.val(projectId)}
66
+ ${cursorFilter}
67
+ order by id asc
68
+ limit ${sql.val(pageSize + 1)}
69
+ `.execute(ctx.db);
70
+ const rows = result.rows;
71
+ const hasMore = rows.length > pageSize;
72
+ const pageRows = hasMore ? rows.slice(0, pageSize) : rows;
73
+ const nextCursor = hasMore
74
+ ? (pageRows[pageRows.length - 1]?.id ?? null)
75
+ : null;
76
+ return {
77
+ rows: pageRows,
78
+ nextCursor: typeof nextCursor === 'string' && nextCursor.length > 0
79
+ ? nextCursor
80
+ : null,
81
+ };
82
+ },
83
+ async applyOperation(ctx, op, opIndex) {
84
+ if (op.table !== 'tasks') {
85
+ return {
86
+ result: {
87
+ opIndex,
88
+ status: 'error',
89
+ error: `UNKNOWN_TABLE:${op.table}`,
90
+ code: 'UNKNOWN_TABLE',
91
+ retriable: false,
92
+ },
93
+ emittedChanges: [],
94
+ };
95
+ }
96
+ if (op.op === 'delete') {
97
+ const existingResult = await sql `
98
+ select id, project_id from tasks
99
+ where id = ${sql.val(op.row_id)} and user_id = ${sql.val(ctx.actorId)}
100
+ limit 1
101
+ `.execute(ctx.trx);
102
+ const existing = existingResult.rows[0];
103
+ if (!existing) {
104
+ return { result: { opIndex, status: 'applied' }, emittedChanges: [] };
105
+ }
106
+ await sql `
107
+ delete from tasks
108
+ where id = ${sql.val(op.row_id)} and user_id = ${sql.val(ctx.actorId)}
109
+ `.execute(ctx.trx);
110
+ const emitted = {
111
+ table: 'tasks',
112
+ row_id: op.row_id,
113
+ op: 'delete',
114
+ row_json: null,
115
+ row_version: null,
116
+ scopes: { user_id: ctx.actorId, project_id: existing.project_id },
117
+ };
118
+ return {
119
+ result: { opIndex, status: 'applied' },
120
+ emittedChanges: [emitted],
121
+ };
122
+ }
123
+ const payload = parseProjectScopedTaskPayload(op.payload);
124
+ const existingResult = await sql `
125
+ select id, title, completed, project_id, server_version
126
+ from tasks
127
+ where id = ${sql.val(op.row_id)} and user_id = ${sql.val(ctx.actorId)}
128
+ limit 1
129
+ `.execute(ctx.trx);
130
+ const existing = existingResult.rows[0];
131
+ if (existing &&
132
+ op.base_version != null &&
133
+ existing.server_version !== op.base_version) {
134
+ return {
135
+ result: {
136
+ opIndex,
137
+ status: 'conflict',
138
+ message: `Version conflict: server=${existing.server_version}, base=${op.base_version}`,
139
+ server_version: existing.server_version,
140
+ server_row: {
141
+ id: existing.id,
142
+ title: existing.title,
143
+ completed: existing.completed,
144
+ user_id: ctx.actorId,
145
+ project_id: existing.project_id,
146
+ server_version: existing.server_version,
147
+ },
148
+ },
149
+ emittedChanges: [],
150
+ };
151
+ }
152
+ const projectId = payload.project_id ?? existing?.project_id;
153
+ if (!projectId) {
154
+ return {
155
+ result: {
156
+ opIndex,
157
+ status: 'error',
158
+ error: 'MISSING_PROJECT_ID',
159
+ code: 'INVALID_REQUEST',
160
+ retriable: false,
161
+ },
162
+ emittedChanges: [],
163
+ };
164
+ }
165
+ if (existing) {
166
+ const nextVersion = existing.server_version + 1;
167
+ await sql `
168
+ update tasks set
169
+ title = ${sql.val(payload.title ?? existing.title)},
170
+ completed = ${sql.val(payload.completed ?? existing.completed)},
171
+ server_version = ${sql.val(nextVersion)}
172
+ where id = ${sql.val(op.row_id)} and user_id = ${sql.val(ctx.actorId)}
173
+ `.execute(ctx.trx);
174
+ }
175
+ else {
176
+ await sql `
177
+ insert into tasks (id, title, completed, user_id, project_id, server_version)
178
+ values (
179
+ ${sql.val(op.row_id)},
180
+ ${sql.val(payload.title ?? '')},
181
+ ${sql.val(payload.completed ?? 0)},
182
+ ${sql.val(ctx.actorId)},
183
+ ${sql.val(projectId)},
184
+ ${sql.val(1)}
185
+ )
186
+ `.execute(ctx.trx);
187
+ }
188
+ const updatedResult = await sql `
189
+ select id, title, completed, user_id, project_id, server_version
190
+ from tasks
191
+ where id = ${sql.val(op.row_id)} and user_id = ${sql.val(ctx.actorId)}
192
+ limit 1
193
+ `.execute(ctx.trx);
194
+ const updated = updatedResult.rows[0];
195
+ if (!updated) {
196
+ throw new Error('TASKS_ROW_NOT_FOUND');
197
+ }
198
+ const emitted = {
199
+ table: 'tasks',
200
+ row_id: op.row_id,
201
+ op: 'upsert',
202
+ row_json: {
203
+ id: updated.id,
204
+ title: updated.title,
205
+ completed: updated.completed,
206
+ user_id: updated.user_id,
207
+ project_id: updated.project_id,
208
+ server_version: updated.server_version,
209
+ },
210
+ row_version: updated.server_version,
211
+ scopes: { user_id: ctx.actorId, project_id: updated.project_id },
212
+ };
213
+ return {
214
+ result: { opIndex, status: 'applied' },
215
+ emittedChanges: [emitted],
216
+ };
217
+ },
218
+ };
219
+ }
220
+ export function createProjectScopedTasksSubscription(options) {
221
+ return {
222
+ id: options.id ?? 'sub-tasks',
223
+ table: 'tasks',
224
+ scopes: {
225
+ user_id: options.userId,
226
+ project_id: options.projectId ?? 'p0',
227
+ },
228
+ cursor: options.cursor ?? 0,
229
+ bootstrapState: null,
230
+ };
231
+ }
232
+ export function createProjectScopedTaskUpsertOperation(options) {
233
+ return {
234
+ table: 'tasks',
235
+ row_id: options.taskId,
236
+ op: 'upsert',
237
+ payload: {
238
+ title: options.title,
239
+ completed: options.completed ?? 0,
240
+ project_id: options.projectId ?? 'p0',
241
+ },
242
+ base_version: options.baseVersion ?? null,
243
+ };
244
+ }
245
+ //# sourceMappingURL=project-scoped-tasks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-scoped-tasks.js","sourceRoot":"","sources":["../src/project-scoped-tasks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { ChildProcess } from 'node:child_process';
2
+ export interface WaitForJsonPortOptions {
3
+ timeoutMs?: number;
4
+ processName?: string;
5
+ }
6
+ export declare function waitForJsonPortFromStdout(process: ChildProcess, options?: WaitForJsonPortOptions): Promise<number>;
7
+ export interface StopChildProcessOptions {
8
+ gracePeriodMs?: number;
9
+ }
10
+ export declare function stopChildProcess(process: ChildProcess, options?: StopChildProcessOptions): Promise<void>;
11
+ //# sourceMappingURL=runtime-process.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-process.d.ts","sourceRoot":"","sources":["../src/runtime-process.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,YAAY,EACrB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,MAAM,CAAC,CAiFjB;AAED,MAAM,WAAW,uBAAuB;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,YAAY,EACrB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CAgCf"}
@@ -0,0 +1,92 @@
1
+ export async function waitForJsonPortFromStdout(process, options = {}) {
2
+ const timeoutMs = options.timeoutMs ?? 30_000;
3
+ const processName = options.processName ?? 'Child process';
4
+ return new Promise((resolve, reject) => {
5
+ const stdout = process.stdout;
6
+ const stderr = process.stderr;
7
+ if (!stdout || !stderr) {
8
+ reject(new Error(`${processName} has no stdout/stderr pipes`));
9
+ return;
10
+ }
11
+ let stdoutBuffer = '';
12
+ let stderrBuffer = '';
13
+ const cleanup = () => {
14
+ clearTimeout(timeout);
15
+ stdout.off('data', onStdoutData);
16
+ stderr.off('data', onStderrData);
17
+ process.off('exit', onExit);
18
+ };
19
+ const resolvePort = (port) => {
20
+ cleanup();
21
+ resolve(port);
22
+ };
23
+ const rejectWith = (error) => {
24
+ cleanup();
25
+ reject(error);
26
+ };
27
+ const onStdoutData = (chunk) => {
28
+ stdoutBuffer += chunk.toString();
29
+ const lines = stdoutBuffer.split('\n');
30
+ stdoutBuffer = lines.pop() ?? '';
31
+ for (const line of lines) {
32
+ const trimmed = line.trim();
33
+ if (!trimmed) {
34
+ continue;
35
+ }
36
+ try {
37
+ const parsed = JSON.parse(trimmed);
38
+ if (parsed.port && parsed.port > 0) {
39
+ resolvePort(parsed.port);
40
+ return;
41
+ }
42
+ }
43
+ catch {
44
+ // keep reading until a valid JSON line is emitted
45
+ }
46
+ }
47
+ };
48
+ const onStderrData = (chunk) => {
49
+ stderrBuffer += chunk.toString();
50
+ };
51
+ const onExit = (code, signal) => {
52
+ rejectWith(new Error(`${processName} exited before reporting port (code=${String(code)} signal=${String(signal)})\nstderr: ${stderrBuffer}`));
53
+ };
54
+ const timeout = setTimeout(() => {
55
+ rejectWith(new Error(`${processName} startup timed out after ${timeoutMs}ms\nstderr: ${stderrBuffer}`));
56
+ }, timeoutMs);
57
+ stdout.on('data', onStdoutData);
58
+ stderr.on('data', onStderrData);
59
+ process.on('exit', onExit);
60
+ });
61
+ }
62
+ export async function stopChildProcess(process, options = {}) {
63
+ if (process.exitCode != null) {
64
+ return;
65
+ }
66
+ const gracePeriodMs = options.gracePeriodMs ?? 5000;
67
+ try {
68
+ process.kill('SIGTERM');
69
+ }
70
+ catch {
71
+ return;
72
+ }
73
+ await new Promise((resolve) => {
74
+ const onExit = () => {
75
+ clearTimeout(timeout);
76
+ process.off('exit', onExit);
77
+ resolve();
78
+ };
79
+ const timeout = setTimeout(() => {
80
+ try {
81
+ process.kill('SIGKILL');
82
+ }
83
+ catch {
84
+ // ignore
85
+ }
86
+ process.off('exit', onExit);
87
+ resolve();
88
+ }, gracePeriodMs);
89
+ process.on('exit', onExit);
90
+ });
91
+ }
92
+ //# sourceMappingURL=runtime-process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-process.js","sourceRoot":"","sources":["../src/runtime-process.ts"],"names":[],"mappings":"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"}