@risingwave/wavelet-server 0.2.4 → 0.2.5

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 (72) hide show
  1. package/dist/__tests__/cursor-parsing.test.d.ts +2 -0
  2. package/dist/__tests__/cursor-parsing.test.d.ts.map +1 -0
  3. package/dist/__tests__/cursor-parsing.test.js +64 -0
  4. package/dist/__tests__/cursor-parsing.test.js.map +1 -0
  5. package/dist/__tests__/http-api.test.d.ts +2 -0
  6. package/dist/__tests__/http-api.test.d.ts.map +1 -0
  7. package/dist/__tests__/http-api.test.js +135 -0
  8. package/dist/__tests__/http-api.test.js.map +1 -0
  9. package/dist/__tests__/integration.test.d.ts +2 -0
  10. package/dist/__tests__/integration.test.d.ts.map +1 -0
  11. package/dist/__tests__/integration.test.js +229 -0
  12. package/dist/__tests__/integration.test.js.map +1 -0
  13. package/dist/__tests__/jwt.test.d.ts +2 -0
  14. package/dist/__tests__/jwt.test.d.ts.map +1 -0
  15. package/dist/__tests__/jwt.test.js +86 -0
  16. package/dist/__tests__/jwt.test.js.map +1 -0
  17. package/dist/__tests__/ws-fanout.test.d.ts +2 -0
  18. package/dist/__tests__/ws-fanout.test.d.ts.map +1 -0
  19. package/dist/__tests__/ws-fanout.test.js +127 -0
  20. package/dist/__tests__/ws-fanout.test.js.map +1 -0
  21. package/dist/config-loader.d.ts +3 -0
  22. package/dist/config-loader.d.ts.map +1 -0
  23. package/dist/config-loader.js +25 -0
  24. package/dist/config-loader.js.map +1 -0
  25. package/dist/cursor-manager.d.ts +54 -0
  26. package/dist/cursor-manager.d.ts.map +1 -0
  27. package/dist/cursor-manager.js +263 -0
  28. package/dist/cursor-manager.js.map +1 -0
  29. package/dist/ddl-manager.d.ts +33 -0
  30. package/dist/ddl-manager.d.ts.map +1 -0
  31. package/dist/ddl-manager.js +364 -0
  32. package/dist/ddl-manager.js.map +1 -0
  33. package/dist/http-api.d.ts +21 -0
  34. package/dist/http-api.d.ts.map +1 -0
  35. package/dist/http-api.js +242 -0
  36. package/dist/http-api.js.map +1 -0
  37. package/dist/index.d.ts +5 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +32 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/jwt.d.ts +16 -0
  42. package/dist/jwt.d.ts.map +1 -0
  43. package/dist/jwt.js +87 -0
  44. package/dist/jwt.js.map +1 -0
  45. package/dist/server.d.ts +24 -0
  46. package/dist/server.d.ts.map +1 -0
  47. package/dist/server.js +82 -0
  48. package/dist/server.js.map +1 -0
  49. package/dist/webhook.d.ts +9 -0
  50. package/dist/webhook.d.ts.map +1 -0
  51. package/dist/webhook.js +63 -0
  52. package/dist/webhook.js.map +1 -0
  53. package/dist/ws-fanout.d.ts +24 -0
  54. package/dist/ws-fanout.d.ts.map +1 -0
  55. package/dist/ws-fanout.js +198 -0
  56. package/dist/ws-fanout.js.map +1 -0
  57. package/package.json +7 -3
  58. package/src/__tests__/cursor-parsing.test.ts +0 -68
  59. package/src/__tests__/http-api.test.ts +0 -130
  60. package/src/__tests__/integration.test.ts +0 -249
  61. package/src/__tests__/jwt.test.ts +0 -62
  62. package/src/__tests__/ws-fanout.test.ts +0 -143
  63. package/src/config-loader.ts +0 -28
  64. package/src/cursor-manager.ts +0 -311
  65. package/src/ddl-manager.ts +0 -408
  66. package/src/http-api.ts +0 -278
  67. package/src/index.ts +0 -31
  68. package/src/jwt.ts +0 -56
  69. package/src/server.ts +0 -89
  70. package/src/webhook.ts +0 -67
  71. package/src/ws-fanout.ts +0 -245
  72. package/tsconfig.json +0 -8
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_events_1 = require("node:events");
4
+ const vitest_1 = require("vitest");
5
+ const ws_1 = require("ws");
6
+ const ws_fanout_js_1 = require("../ws-fanout.js");
7
+ class MockSocket extends node_events_1.EventEmitter {
8
+ readyState = ws_1.WebSocket.OPEN;
9
+ sent = [];
10
+ send(message) {
11
+ this.sent.push(message);
12
+ }
13
+ ping() { }
14
+ close() {
15
+ this.readyState = ws_1.WebSocket.CLOSED;
16
+ this.emit('close');
17
+ }
18
+ }
19
+ function deferred() {
20
+ let resolve;
21
+ let reject;
22
+ const promise = new Promise((res, rej) => {
23
+ resolve = res;
24
+ reject = rej;
25
+ });
26
+ return { promise, resolve, reject };
27
+ }
28
+ (0, vitest_1.describe)('WebSocketFanout', () => {
29
+ (0, vitest_1.it)('drops queued shared diffs that are already covered by bootstrap', async () => {
30
+ const bootstrapDeferred = deferred();
31
+ const cursorManager = {
32
+ bootstrap: vitest_1.vi.fn().mockReturnValue(bootstrapDeferred.promise),
33
+ };
34
+ const jwt = {
35
+ isConfigured: () => false,
36
+ };
37
+ const fanout = new ws_fanout_js_1.WebSocketFanout(cursorManager, jwt, {
38
+ leaderboard: {},
39
+ });
40
+ const ws = new MockSocket();
41
+ const req = {
42
+ url: '/subscribe/leaderboard',
43
+ headers: { host: 'localhost' },
44
+ };
45
+ const connectionPromise = fanout.handleConnection(ws, req);
46
+ fanout.broadcast('leaderboard', {
47
+ cursor: '150',
48
+ inserted: [{ player_id: 'alice', score: 10 }],
49
+ updated: [],
50
+ deleted: [],
51
+ });
52
+ fanout.broadcast('leaderboard', {
53
+ cursor: '300',
54
+ inserted: [{ player_id: 'bob', score: 20 }],
55
+ updated: [],
56
+ deleted: [],
57
+ });
58
+ bootstrapDeferred.resolve({
59
+ snapshotRows: [{ player_id: 'alice', score: 10 }],
60
+ diffs: [{
61
+ cursor: '200',
62
+ inserted: [],
63
+ updated: [{ player_id: 'alice', score: 15 }],
64
+ deleted: [],
65
+ }],
66
+ lastCursor: '200',
67
+ });
68
+ await connectionPromise;
69
+ const messages = ws.sent.map((message) => JSON.parse(message));
70
+ (0, vitest_1.expect)(messages).toEqual([
71
+ { type: 'connected', query: 'leaderboard' },
72
+ {
73
+ type: 'snapshot',
74
+ query: 'leaderboard',
75
+ rows: [{ player_id: 'alice', score: 10 }],
76
+ },
77
+ {
78
+ type: 'diff',
79
+ query: 'leaderboard',
80
+ cursor: '200',
81
+ inserted: [],
82
+ updated: [{ player_id: 'alice', score: 15 }],
83
+ deleted: [],
84
+ },
85
+ {
86
+ type: 'diff',
87
+ query: 'leaderboard',
88
+ cursor: '300',
89
+ inserted: [{ player_id: 'bob', score: 20 }],
90
+ updated: [],
91
+ deleted: [],
92
+ },
93
+ ]);
94
+ });
95
+ (0, vitest_1.it)('filters snapshot rows with the same claim rule as diffs', async () => {
96
+ const cursorManager = {
97
+ bootstrap: vitest_1.vi.fn().mockResolvedValue({
98
+ snapshotRows: [
99
+ { user_id: 'u1', total: 10 },
100
+ { user_id: 'u2', total: 20 },
101
+ ],
102
+ diffs: [],
103
+ lastCursor: null,
104
+ }),
105
+ };
106
+ const jwt = {
107
+ isConfigured: () => true,
108
+ verify: vitest_1.vi.fn().mockResolvedValue({ user_id: 'u1' }),
109
+ };
110
+ const fanout = new ws_fanout_js_1.WebSocketFanout(cursorManager, jwt, {
111
+ totals: { filterBy: 'user_id' },
112
+ });
113
+ const ws = new MockSocket();
114
+ const req = {
115
+ url: '/subscribe/totals?token=test-token',
116
+ headers: { host: 'localhost' },
117
+ };
118
+ await fanout.handleConnection(ws, req);
119
+ const snapshotMessage = JSON.parse(ws.sent[1]);
120
+ (0, vitest_1.expect)(snapshotMessage).toEqual({
121
+ type: 'snapshot',
122
+ query: 'totals',
123
+ rows: [{ user_id: 'u1', total: 10 }],
124
+ });
125
+ });
126
+ });
127
+ //# sourceMappingURL=ws-fanout.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-fanout.test.js","sourceRoot":"","sources":["../../src/__tests__/ws-fanout.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { WaveletConfig } from '@risingwave/wavelet';
2
+ export declare function loadConfig(configPath: string): Promise<WaveletConfig>;
3
+ //# sourceMappingURL=config-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../src/config-loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAExD,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAuB3E"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadConfig = loadConfig;
4
+ const node_url_1 = require("node:url");
5
+ const node_path_1 = require("node:path");
6
+ async function loadConfig(configPath) {
7
+ const abs = (0, node_path_1.resolve)(configPath);
8
+ const mod = await import((0, node_url_1.pathToFileURL)(abs).href);
9
+ // Handle various module formats:
10
+ // - ESM: mod.default is the config
11
+ // - CJS interop: mod.default.default is the config
12
+ // - Plain object: mod itself is the config
13
+ let config = mod.default ?? mod;
14
+ if (config && typeof config === 'object' && 'default' in config) {
15
+ config = config.default;
16
+ }
17
+ if (!config || !config.database) {
18
+ throw new Error(`wavelet.config.ts must export a config with a 'database' field.\n` +
19
+ `Example:\n` +
20
+ ` import { defineConfig } from '@risingwave/wavelet'\n` +
21
+ ` export default defineConfig({ database: 'postgres://...' })`);
22
+ }
23
+ return config;
24
+ }
25
+ //# sourceMappingURL=config-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../src/config-loader.ts"],"names":[],"mappings":";;AAIA,gCAuBC;AA3BD,uCAAwC;AACxC,yCAAmC;AAG5B,KAAK,UAAU,UAAU,CAAC,UAAkB;IACjD,MAAM,GAAG,GAAG,IAAA,mBAAO,EAAC,UAAU,CAAC,CAAA;IAC/B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAA,wBAAa,EAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAA;IAEjD,iCAAiC;IACjC,mCAAmC;IACnC,mDAAmD;IACnD,2CAA2C;IAC3C,IAAI,MAAM,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAA;IAC/B,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;QAChE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAA;IACzB,CAAC;IAED,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,mEAAmE;YACnE,YAAY;YACZ,wDAAwD;YACxD,+DAA+D,CAChE,CAAA;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,54 @@
1
+ import type { SqlFragment, QueryDef } from '@risingwave/wavelet';
2
+ export interface DiffRow {
3
+ op: 'insert' | 'update_insert' | 'update_delete' | 'delete';
4
+ row: Record<string, unknown>;
5
+ rw_timestamp: string;
6
+ }
7
+ export interface ViewDiff {
8
+ cursor: string;
9
+ inserted: Record<string, unknown>[];
10
+ updated: Record<string, unknown>[];
11
+ deleted: Record<string, unknown>[];
12
+ }
13
+ export interface BootstrapResult {
14
+ snapshotRows: Record<string, unknown>[];
15
+ diffs: ViewDiff[];
16
+ lastCursor: string | null;
17
+ }
18
+ type DiffCallback = (queryName: string, diff: ViewDiff) => void;
19
+ /**
20
+ * Manages persistent subscription cursors against RisingWave.
21
+ *
22
+ * Each query gets its own dedicated pg connection and a persistent cursor.
23
+ * Uses blocking FETCH (WITH timeout) so there is no polling interval -
24
+ * diffs are dispatched as soon as RisingWave produces them.
25
+ */
26
+ export declare class CursorManager {
27
+ private connectionString;
28
+ private queries;
29
+ private client;
30
+ private queryConnections;
31
+ private cursorNames;
32
+ private subscriptions;
33
+ private running;
34
+ constructor(connectionString: string, queries: Record<string, QueryDef | SqlFragment>);
35
+ initialize(): Promise<void>;
36
+ /**
37
+ * Start listening for diffs on all queries.
38
+ * Each query runs its own async loop with blocking FETCH.
39
+ * No polling interval - FETCH blocks until data arrives or timeout.
40
+ */
41
+ startPolling(callback: DiffCallback): void;
42
+ stopPolling(): void;
43
+ private listenLoop;
44
+ parseDiffs(rows: any[]): ViewDiff;
45
+ parseDiffBatches(rows: any[]): ViewDiff[];
46
+ bootstrap(queryName: string): Promise<BootstrapResult>;
47
+ query(sql: string): Promise<any[]>;
48
+ execute(sql: string): Promise<void>;
49
+ close(): Promise<void>;
50
+ private stripSubscriptionMetadata;
51
+ private normalizeCursor;
52
+ }
53
+ export {};
54
+ //# sourceMappingURL=cursor-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-manager.d.ts","sourceRoot":"","sources":["../src/cursor-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAIhE,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,QAAQ,GAAG,eAAe,GAAG,eAAe,GAAG,QAAQ,CAAA;IAC3D,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC5B,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IAClC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IACvC,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,KAAK,YAAY,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAA;AAE/D;;;;;;GAMG;AACH,qBAAa,aAAa;IAWtB,OAAO,CAAC,gBAAgB;IACxB,OAAO,CAAC,OAAO;IAVjB,OAAO,CAAC,MAAM,CAA2C;IAGzD,OAAO,CAAC,gBAAgB,CAAsD;IAC9E,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,OAAO,CAAQ;gBAGb,gBAAgB,EAAE,MAAM,EACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,WAAW,CAAC;IAGnD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAqCjC;;;;OAIG;IACH,YAAY,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI;IAQ1C,WAAW,IAAI,IAAI;YAIL,UAAU;IA6CxB,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,QAAQ;IAqCjC,gBAAgB,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,QAAQ,EAAE;IAyBnC,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAyDtD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAMlC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB5B,OAAO,CAAC,yBAAyB;IAKjC,OAAO,CAAC,eAAe;CAKxB"}
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CursorManager = void 0;
7
+ const pg_1 = __importDefault(require("pg"));
8
+ const { Client } = pg_1.default;
9
+ /**
10
+ * Manages persistent subscription cursors against RisingWave.
11
+ *
12
+ * Each query gets its own dedicated pg connection and a persistent cursor.
13
+ * Uses blocking FETCH (WITH timeout) so there is no polling interval -
14
+ * diffs are dispatched as soon as RisingWave produces them.
15
+ */
16
+ class CursorManager {
17
+ connectionString;
18
+ queries;
19
+ // Shared connection for DDL (CREATE SUBSCRIPTION) and ad-hoc queries
20
+ client = null;
21
+ // Per-query dedicated connections for blocking FETCH
22
+ queryConnections = new Map();
23
+ cursorNames = new Map();
24
+ subscriptions = new Map();
25
+ running = false;
26
+ constructor(connectionString, queries) {
27
+ this.connectionString = connectionString;
28
+ this.queries = queries;
29
+ }
30
+ async initialize() {
31
+ this.client = new Client({ connectionString: this.connectionString });
32
+ await this.client.connect();
33
+ console.log('Connected to RisingWave');
34
+ for (const [queryName] of Object.entries(this.queries)) {
35
+ const subName = `wavelet_sub_${queryName}`;
36
+ // Create subscription if not exists (idempotent)
37
+ try {
38
+ await this.client.query(`CREATE SUBSCRIPTION ${subName} FROM ${queryName} WITH (retention = '24h')`);
39
+ console.log(`Created subscription: ${subName}`);
40
+ }
41
+ catch (err) {
42
+ if (err.message?.includes('exists')) {
43
+ console.log(`Subscription exists: ${subName}`);
44
+ }
45
+ else {
46
+ throw err;
47
+ }
48
+ }
49
+ this.subscriptions.set(queryName, subName);
50
+ // Create dedicated connection and persistent cursor for this query
51
+ const conn = new Client({ connectionString: this.connectionString });
52
+ await conn.connect();
53
+ const cursorName = `wavelet_cur_${queryName}`;
54
+ await conn.query(`DECLARE ${cursorName} SUBSCRIPTION CURSOR FOR ${subName}`);
55
+ console.log(`Opened persistent cursor: ${cursorName}`);
56
+ this.queryConnections.set(queryName, conn);
57
+ this.cursorNames.set(queryName, cursorName);
58
+ }
59
+ }
60
+ /**
61
+ * Start listening for diffs on all queries.
62
+ * Each query runs its own async loop with blocking FETCH.
63
+ * No polling interval - FETCH blocks until data arrives or timeout.
64
+ */
65
+ startPolling(callback) {
66
+ this.running = true;
67
+ for (const [queryName] of this.subscriptions.entries()) {
68
+ this.listenLoop(queryName, callback);
69
+ }
70
+ }
71
+ stopPolling() {
72
+ this.running = false;
73
+ }
74
+ async listenLoop(queryName, callback) {
75
+ const conn = this.queryConnections.get(queryName);
76
+ const cursorName = this.cursorNames.get(queryName);
77
+ if (!conn || !cursorName)
78
+ return;
79
+ while (this.running) {
80
+ try {
81
+ // Blocking FETCH: waits up to 5s for new data, returns immediately when data arrives
82
+ const result = await conn.query(`FETCH NEXT FROM ${cursorName} WITH (timeout = '5s')`);
83
+ if (result.rows.length === 0)
84
+ continue;
85
+ // Got at least one row. Drain any remaining rows with timeout.
86
+ const allRows = [...result.rows];
87
+ let more = true;
88
+ while (more) {
89
+ const batch = await conn.query(`FETCH 100 FROM ${cursorName} WITH (timeout = '1s')`);
90
+ if (batch.rows.length > 0) {
91
+ allRows.push(...batch.rows);
92
+ }
93
+ else {
94
+ more = false;
95
+ }
96
+ }
97
+ const diffs = this.parseDiffBatches(allRows);
98
+ for (const diff of diffs) {
99
+ if (diff.inserted.length === 0 && diff.updated.length === 0 && diff.deleted.length === 0) {
100
+ continue;
101
+ }
102
+ callback(queryName, diff);
103
+ }
104
+ }
105
+ catch (err) {
106
+ if (!this.running)
107
+ return;
108
+ console.error(`[cursor-manager] Error fetching ${queryName}:`, err.message);
109
+ // Back off on error, then retry
110
+ await new Promise(r => setTimeout(r, 1000));
111
+ }
112
+ }
113
+ }
114
+ parseDiffs(rows) {
115
+ const diff = {
116
+ cursor: '',
117
+ inserted: [],
118
+ updated: [],
119
+ deleted: [],
120
+ };
121
+ for (const row of rows) {
122
+ const { op, rw_timestamp, ...data } = row;
123
+ diff.cursor = rw_timestamp ?? diff.cursor;
124
+ // RisingWave returns op as a string: "Insert", "Delete", "UpdateInsert", "UpdateDelete"
125
+ const opStr = String(op);
126
+ switch (opStr) {
127
+ case 'Insert':
128
+ case '1':
129
+ diff.inserted.push(data);
130
+ break;
131
+ case 'Delete':
132
+ case '2':
133
+ diff.deleted.push(data);
134
+ break;
135
+ case 'UpdateDelete':
136
+ case '3':
137
+ diff.deleted.push(data);
138
+ break;
139
+ case 'UpdateInsert':
140
+ case '4':
141
+ diff.updated.push(data);
142
+ break;
143
+ }
144
+ }
145
+ return diff;
146
+ }
147
+ parseDiffBatches(rows) {
148
+ const diffs = [];
149
+ let currentRows = [];
150
+ let currentCursor = null;
151
+ for (const row of rows) {
152
+ const cursor = this.normalizeCursor(row.rw_timestamp);
153
+ if (!cursor)
154
+ continue;
155
+ if (currentCursor !== null && cursor !== currentCursor) {
156
+ diffs.push(this.parseDiffs(currentRows));
157
+ currentRows = [];
158
+ }
159
+ currentCursor = cursor;
160
+ currentRows.push(row);
161
+ }
162
+ if (currentRows.length > 0) {
163
+ diffs.push(this.parseDiffs(currentRows));
164
+ }
165
+ return diffs;
166
+ }
167
+ async bootstrap(queryName) {
168
+ const subName = this.subscriptions.get(queryName);
169
+ if (!subName) {
170
+ throw new Error(`Subscription for query '${queryName}' is not initialized. Start the server before accepting WebSocket clients.`);
171
+ }
172
+ const conn = new Client({ connectionString: this.connectionString });
173
+ await conn.connect();
174
+ const cursorName = `wavelet_boot_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
175
+ try {
176
+ await conn.query(`DECLARE ${cursorName} SUBSCRIPTION CURSOR FOR ${subName} FULL`);
177
+ const snapshotRows = [];
178
+ const incrementalRows = [];
179
+ let readingSnapshot = true;
180
+ while (readingSnapshot) {
181
+ const result = await conn.query(`FETCH 1000 FROM ${cursorName}`);
182
+ if (result.rows.length === 0)
183
+ break;
184
+ let firstIncrementalIndex = result.rows.findIndex((row) => this.normalizeCursor(row.rw_timestamp) !== null);
185
+ if (firstIncrementalIndex === -1)
186
+ firstIncrementalIndex = result.rows.length;
187
+ for (const row of result.rows.slice(0, firstIncrementalIndex)) {
188
+ snapshotRows.push(this.stripSubscriptionMetadata(row));
189
+ }
190
+ if (firstIncrementalIndex < result.rows.length) {
191
+ incrementalRows.push(...result.rows.slice(firstIncrementalIndex));
192
+ readingSnapshot = false;
193
+ }
194
+ }
195
+ while (true) {
196
+ const result = await conn.query(`FETCH 1000 FROM ${cursorName}`);
197
+ if (result.rows.length === 0)
198
+ break;
199
+ incrementalRows.push(...result.rows);
200
+ }
201
+ const diffs = this.parseDiffBatches(incrementalRows);
202
+ const lastCursor = diffs.length > 0 ? diffs[diffs.length - 1].cursor : null;
203
+ return { snapshotRows, diffs, lastCursor };
204
+ }
205
+ finally {
206
+ try {
207
+ await conn.query(`CLOSE ${cursorName}`);
208
+ }
209
+ catch { }
210
+ try {
211
+ await conn.end();
212
+ }
213
+ catch { }
214
+ }
215
+ }
216
+ async query(sql) {
217
+ if (!this.client)
218
+ throw new Error('Not connected');
219
+ const result = await this.client.query(sql);
220
+ return result.rows;
221
+ }
222
+ async execute(sql) {
223
+ if (!this.client)
224
+ throw new Error('Not connected');
225
+ await this.client.query(sql);
226
+ }
227
+ async close() {
228
+ this.running = false;
229
+ // Close per-query connections
230
+ for (const [queryName, conn] of this.queryConnections) {
231
+ const cursorName = this.cursorNames.get(queryName);
232
+ try {
233
+ if (cursorName)
234
+ await conn.query(`CLOSE ${cursorName}`);
235
+ }
236
+ catch { }
237
+ try {
238
+ await conn.end();
239
+ }
240
+ catch { }
241
+ }
242
+ this.queryConnections.clear();
243
+ this.cursorNames.clear();
244
+ // Close shared connection
245
+ try {
246
+ await this.client?.end();
247
+ }
248
+ catch { }
249
+ this.client = null;
250
+ }
251
+ stripSubscriptionMetadata(row) {
252
+ const { op: _op, rw_timestamp: _rwTimestamp, ...data } = row;
253
+ return data;
254
+ }
255
+ normalizeCursor(value) {
256
+ if (value === null || value === undefined)
257
+ return null;
258
+ const cursor = String(value).trim();
259
+ return cursor === '' ? null : cursor;
260
+ }
261
+ }
262
+ exports.CursorManager = CursorManager;
263
+ //# sourceMappingURL=cursor-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-manager.js","sourceRoot":"","sources":["../src/cursor-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { WaveletConfig } from '@risingwave/wavelet';
2
+ export interface DdlAction {
3
+ type: 'create' | 'update' | 'delete' | 'unchanged';
4
+ resource: 'event' | 'source' | 'query' | 'subscription';
5
+ name: string;
6
+ detail?: string;
7
+ }
8
+ export declare class DdlManager {
9
+ private connectionString;
10
+ private client;
11
+ constructor(connectionString: string);
12
+ connect(): Promise<void>;
13
+ close(): Promise<void>;
14
+ /**
15
+ * Sync all events, queries, and subscriptions to match the config.
16
+ * Returns a list of actions taken.
17
+ * Idempotent - safe to call multiple times.
18
+ */
19
+ sync(config: WaveletConfig): Promise<DdlAction[]>;
20
+ private getExistingTables;
21
+ private getExistingMaterializedViews;
22
+ private getExistingSources;
23
+ private getExistingSubscriptions;
24
+ private tableHasDependentMVs;
25
+ private createTable;
26
+ private dropTable;
27
+ private createMaterializedView;
28
+ private dropMaterializedView;
29
+ private createSubscription;
30
+ private dropSubscription;
31
+ private createCdcSource;
32
+ }
33
+ //# sourceMappingURL=ddl-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ddl-manager.d.ts","sourceRoot":"","sources":["../src/ddl-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAsD,MAAM,qBAAqB,CAAA;AAI5G,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAA;IAClD,QAAQ,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,cAAc,CAAA;IACvD,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAkCD,qBAAa,UAAU;IAGT,OAAO,CAAC,gBAAgB;IAFpC,OAAO,CAAC,MAAM,CAA2C;gBAErC,gBAAgB,EAAE,MAAM;IAEtC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B;;;;OAIG;IACG,IAAI,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;YA8IzC,iBAAiB;YAOjB,4BAA4B;YAW5B,kBAAkB;YAYlB,wBAAwB;YAOxB,oBAAoB;YAUpB,WAAW;YAcX,SAAS;YAaT,sBAAsB;YAatB,oBAAoB;YAapB,kBAAkB;YAelB,gBAAgB;YAahB,eAAe;CAmD9B"}