@vlasky/zongji 0.5.8 → 0.6.0
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/.claude/settings.local.json +16 -0
- package/.mcp.json +17 -0
- package/.tap/processinfo/84de694d-068b-4ac7-b4d8-f835c2122b2d.json +892 -0
- package/.tap/processinfo/a1e9627e-66eb-412c-aaa5-079a2eb9f385.json +891 -0
- package/.tap/processinfo/abc34c6f-f4aa-4405-8f9a-947048be1df2.json +892 -0
- package/.tap/processinfo/affe6c5b-a34d-41b9-bb73-4b14d35138de.json +891 -0
- package/.tap/processinfo/bf82449d-cdeb-478b-9506-b939a9506bf2.json +883 -0
- package/.tap/processinfo/dd98c508-7067-4342-bdf5-63664b630701.json +892 -0
- package/.tap/test-results/test_await_end.js.tap +19 -0
- package/.tap/test-results/test_await_end2.js.tap +27 -0
- package/.tap/test-results/test_debug_init.js.tap +23 -0
- package/.tap/test-results/test_debug_init2.js.tap +35 -0
- package/.tap/test-results/test_invalid_host.js.tap +34 -0
- package/.tap/test-results/test_minimal.js.tap +26 -0
- package/.tap/test-results/test_queryseq.js.tap +14 -0
- package/.tap/test-results/test_timing.js.tap +17 -0
- package/.tap/test-results/test_unref.js.tap +15 -0
- package/CHANGELOG.md +55 -0
- package/CLAUDE.md +99 -0
- package/GTID.md +54 -0
- package/README.md +44 -21
- package/TODO.md +43 -0
- package/docker-compose.yml +14 -13
- package/docker-test.sh +7 -7
- package/eslint.config.js +35 -0
- package/example.js +1 -1
- package/index.d.ts +314 -0
- package/index.js +444 -272
- package/jsconfig.json +15 -0
- package/lib/binlog_event.js +296 -217
- package/lib/code_map.js +7 -4
- package/lib/common.js +46 -24
- package/lib/datetime_decode.js +147 -34
- package/lib/json_decode.js +12 -6
- package/lib/packet/binlog.js +3 -3
- package/lib/packet/combinlog.js +22 -20
- package/lib/packet/index.js +50 -50
- package/lib/reader.js +215 -109
- package/lib/rows_event.js +98 -102
- package/lib/sequence/binlog.js +142 -39
- package/notes.md +77 -0
- package/package.json +17 -11
- package/scripts/start-mysql.sh +28 -0
- package/workerthreads.md +173 -0
- package/.eslintrc +0 -19
package/docker-test.sh
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
MYSQL_HOSTS="
|
|
2
|
+
MYSQL_HOSTS="mysql57 mysql80 mysql84"
|
|
3
3
|
|
|
4
4
|
for hostname in ${MYSQL_HOSTS}; do
|
|
5
|
-
echo $hostname + node
|
|
6
|
-
docker run -it --network=zongji_default -e MYSQL_HOST=$hostname -w /build -v $PWD:/build node:
|
|
7
|
-
echo $hostname + node
|
|
8
|
-
docker run -it --network=zongji_default -e MYSQL_HOST=$hostname -w /build -v $PWD:/build node:
|
|
9
|
-
echo $hostname + node
|
|
10
|
-
docker run -it --network=zongji_default -e MYSQL_HOST=$hostname -w /build -v $PWD:/build node:
|
|
5
|
+
echo $hostname + node 18
|
|
6
|
+
docker run -it --network=zongji_default -e MYSQL_HOST=$hostname -w /build -v $PWD:/build node:18 npm test
|
|
7
|
+
echo $hostname + node 20
|
|
8
|
+
docker run -it --network=zongji_default -e MYSQL_HOST=$hostname -w /build -v $PWD:/build node:20 npm test
|
|
9
|
+
echo $hostname + node 22
|
|
10
|
+
docker run -it --network=zongji_default -e MYSQL_HOST=$hostname -w /build -v $PWD:/build node:22 npm test
|
|
11
11
|
done
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import js from '@eslint/js';
|
|
2
|
+
|
|
3
|
+
export default [
|
|
4
|
+
{ ignores: ['.tap/'] },
|
|
5
|
+
js.configs.recommended,
|
|
6
|
+
{
|
|
7
|
+
languageOptions: {
|
|
8
|
+
ecmaVersion: 2022,
|
|
9
|
+
sourceType: 'module',
|
|
10
|
+
globals: {
|
|
11
|
+
Buffer: 'readonly',
|
|
12
|
+
clearImmediate: 'readonly',
|
|
13
|
+
clearInterval: 'readonly',
|
|
14
|
+
clearTimeout: 'readonly',
|
|
15
|
+
console: 'readonly',
|
|
16
|
+
process: 'readonly',
|
|
17
|
+
setImmediate: 'readonly',
|
|
18
|
+
setInterval: 'readonly',
|
|
19
|
+
setTimeout: 'readonly',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
rules: {
|
|
23
|
+
'comma-dangle': ['warn', 'only-multiline'],
|
|
24
|
+
'eol-last': ['error'],
|
|
25
|
+
'keyword-spacing': ['error', { before: true }],
|
|
26
|
+
'no-console': 'off',
|
|
27
|
+
'no-trailing-spaces': ['error', { skipBlankLines: true }],
|
|
28
|
+
'no-unused-vars': 'warn',
|
|
29
|
+
'no-var': 'warn',
|
|
30
|
+
'quotes': ['warn', 'single', 'avoid-escape'],
|
|
31
|
+
'semi': ['error', 'always'],
|
|
32
|
+
'space-before-blocks': 'error',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
];
|
package/example.js
CHANGED
package/index.d.ts
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { Connection, Pool, ConnectionOptions, PoolOptions } from 'mysql2';
|
|
3
|
+
|
|
4
|
+
// Configuration options for ZongJi
|
|
5
|
+
export interface ZongJiOptions {
|
|
6
|
+
/** Unique server ID for the binlog connection (default: 1) */
|
|
7
|
+
serverId?: number;
|
|
8
|
+
/** If true, start reading from the end of the binlog */
|
|
9
|
+
startAtEnd?: boolean;
|
|
10
|
+
/** Binlog filename to start from */
|
|
11
|
+
filename?: string;
|
|
12
|
+
/** Binlog position to start from */
|
|
13
|
+
position?: number;
|
|
14
|
+
/** If true, use non-blocking mode */
|
|
15
|
+
nonBlock?: boolean;
|
|
16
|
+
/** List of event types to include (e.g., ['tablemap', 'writerows']) */
|
|
17
|
+
includeEvents?: string[];
|
|
18
|
+
/** List of event types to exclude */
|
|
19
|
+
excludeEvents?: string[];
|
|
20
|
+
/** Schema/table filter for inclusion. Use `true` for all tables in a schema, or array of table names */
|
|
21
|
+
includeSchema?: Record<string, string[] | true>;
|
|
22
|
+
/** Schema/table filter for exclusion */
|
|
23
|
+
excludeSchema?: Record<string, string[] | true>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Column metadata from INFORMATION_SCHEMA
|
|
27
|
+
export interface ColumnSchema {
|
|
28
|
+
COLUMN_NAME: string;
|
|
29
|
+
COLLATION_NAME: string | null;
|
|
30
|
+
CHARACTER_SET_NAME: string | null;
|
|
31
|
+
COLUMN_COMMENT: string;
|
|
32
|
+
COLUMN_TYPE: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Column information from TableMap event
|
|
36
|
+
export interface ColumnInfo {
|
|
37
|
+
name: string;
|
|
38
|
+
charset: string | null;
|
|
39
|
+
type: number;
|
|
40
|
+
metadata: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Table metadata cached by ZongJi
|
|
44
|
+
export interface TableMapEntry {
|
|
45
|
+
columnSchemas: ColumnSchema[];
|
|
46
|
+
parentSchema: string;
|
|
47
|
+
tableName: string;
|
|
48
|
+
columns?: ColumnInfo[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Base binlog event class
|
|
52
|
+
export interface BinlogEvent {
|
|
53
|
+
/** Timestamp of the event in milliseconds */
|
|
54
|
+
timestamp: number;
|
|
55
|
+
/** Position in the binlog after this event */
|
|
56
|
+
nextPosition: number;
|
|
57
|
+
/** Size of the event in bytes */
|
|
58
|
+
size: number;
|
|
59
|
+
/** Get the lowercase event name */
|
|
60
|
+
getEventName(): string;
|
|
61
|
+
/** Get the event class name */
|
|
62
|
+
getTypeName(): string;
|
|
63
|
+
/** Print event details to console */
|
|
64
|
+
dump(): void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Rotate event - indicates binlog file change
|
|
68
|
+
export interface RotateEvent extends BinlogEvent {
|
|
69
|
+
/** Position in the new binlog file */
|
|
70
|
+
position: number;
|
|
71
|
+
/** Name of the new binlog file */
|
|
72
|
+
binlogName: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Format Description event
|
|
76
|
+
export interface FormatEvent extends BinlogEvent {}
|
|
77
|
+
|
|
78
|
+
// GTID event
|
|
79
|
+
export interface GtidEvent extends BinlogEvent {
|
|
80
|
+
/** GTID flags */
|
|
81
|
+
flags: number;
|
|
82
|
+
/** Source UUID */
|
|
83
|
+
sid: string;
|
|
84
|
+
/** Group number */
|
|
85
|
+
gno: number;
|
|
86
|
+
/** Full GTID string (sid:gno) */
|
|
87
|
+
gtid: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Anonymous GTID event
|
|
91
|
+
export interface AnonymousGtidEvent extends BinlogEvent {
|
|
92
|
+
/** GTID flags */
|
|
93
|
+
flags: number;
|
|
94
|
+
/** Source UUID */
|
|
95
|
+
sid: string;
|
|
96
|
+
/** Group number */
|
|
97
|
+
gno: number;
|
|
98
|
+
/** Full GTID string (sid:gno) */
|
|
99
|
+
gtid: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Previous GTIDs event
|
|
103
|
+
export interface GtidInterval {
|
|
104
|
+
start: number;
|
|
105
|
+
end: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface GtidSidEntry {
|
|
109
|
+
sid: string;
|
|
110
|
+
intervals: GtidInterval[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface PreviousGtidsEvent extends BinlogEvent {
|
|
114
|
+
/** Array of SID entries with their intervals */
|
|
115
|
+
sids: GtidSidEntry[];
|
|
116
|
+
/** GTID set as a formatted string */
|
|
117
|
+
gtidSet: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// XID (commit) event
|
|
121
|
+
export interface XidEvent extends BinlogEvent {
|
|
122
|
+
/** Transaction ID for 2PC */
|
|
123
|
+
xid: number | string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Query event
|
|
127
|
+
export interface QueryEvent extends BinlogEvent {
|
|
128
|
+
/** Slave proxy ID */
|
|
129
|
+
slaveProxyId: number;
|
|
130
|
+
/** Time in seconds the query took to execute */
|
|
131
|
+
executionTime: number;
|
|
132
|
+
/** Length of the schema name */
|
|
133
|
+
schemaLength: number;
|
|
134
|
+
/** Error code (0 for success) */
|
|
135
|
+
errorCode: number;
|
|
136
|
+
/** Length of status variables */
|
|
137
|
+
statusVarsLength: number;
|
|
138
|
+
/** Status variables */
|
|
139
|
+
statusVars: string;
|
|
140
|
+
/** Database/schema name */
|
|
141
|
+
schema: string;
|
|
142
|
+
/** The SQL query */
|
|
143
|
+
query: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// IntVar event (for statement-based replication)
|
|
147
|
+
export interface IntVarEvent extends BinlogEvent {
|
|
148
|
+
/** Variable type: 1=LAST_INSERT_ID, 2=INSERT_ID */
|
|
149
|
+
type: number;
|
|
150
|
+
/** The integer value */
|
|
151
|
+
value: number | string;
|
|
152
|
+
/** Get the variable type name */
|
|
153
|
+
getIntTypeName(): string;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// TableMap event
|
|
157
|
+
export interface TableMapEvent extends BinlogEvent {
|
|
158
|
+
/** Internal table ID */
|
|
159
|
+
tableId: number;
|
|
160
|
+
/** Table flags */
|
|
161
|
+
flags: number;
|
|
162
|
+
/** Database/schema name */
|
|
163
|
+
schemaName: string;
|
|
164
|
+
/** Table name */
|
|
165
|
+
tableName: string;
|
|
166
|
+
/** Number of columns */
|
|
167
|
+
columnCount: number;
|
|
168
|
+
/** Array of MySQL type codes for each column */
|
|
169
|
+
columnTypes: number[];
|
|
170
|
+
/** Column metadata */
|
|
171
|
+
columnsMetadata: Record<string, unknown>[];
|
|
172
|
+
/** Reference to the tableMap cache */
|
|
173
|
+
tableMap: Record<number, TableMapEntry>;
|
|
174
|
+
/** Update column info after fetching from INFORMATION_SCHEMA */
|
|
175
|
+
updateColumnInfo(): void;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Row data type - key is column name, value is column value
|
|
179
|
+
export type RowData = Record<string, unknown>;
|
|
180
|
+
|
|
181
|
+
// Update row with before/after values
|
|
182
|
+
export interface UpdateRowData {
|
|
183
|
+
before: RowData;
|
|
184
|
+
after: RowData;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Base rows event
|
|
188
|
+
export interface RowsEvent extends BinlogEvent {
|
|
189
|
+
/** Internal table ID */
|
|
190
|
+
tableId: number;
|
|
191
|
+
/** Event flags */
|
|
192
|
+
flags: number;
|
|
193
|
+
/** Number of columns */
|
|
194
|
+
numberOfColumns: number;
|
|
195
|
+
/** Reference to the tableMap cache */
|
|
196
|
+
tableMap: Record<number, TableMapEntry>;
|
|
197
|
+
/** Whether checksum is enabled */
|
|
198
|
+
useChecksum: boolean;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// WriteRows event (INSERT)
|
|
202
|
+
export interface WriteRowsEvent extends RowsEvent {
|
|
203
|
+
/** Array of inserted rows */
|
|
204
|
+
rows: RowData[];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// DeleteRows event (DELETE)
|
|
208
|
+
export interface DeleteRowsEvent extends RowsEvent {
|
|
209
|
+
/** Array of deleted rows */
|
|
210
|
+
rows: RowData[];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// UpdateRows event (UPDATE)
|
|
214
|
+
export interface UpdateRowsEvent extends RowsEvent {
|
|
215
|
+
/** Array of updated rows with before/after values */
|
|
216
|
+
rows: UpdateRowData[];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Unknown event type
|
|
220
|
+
export interface UnknownEvent extends BinlogEvent {}
|
|
221
|
+
|
|
222
|
+
// Union type of all event types
|
|
223
|
+
export type AnyBinlogEvent =
|
|
224
|
+
| RotateEvent
|
|
225
|
+
| FormatEvent
|
|
226
|
+
| GtidEvent
|
|
227
|
+
| AnonymousGtidEvent
|
|
228
|
+
| PreviousGtidsEvent
|
|
229
|
+
| XidEvent
|
|
230
|
+
| QueryEvent
|
|
231
|
+
| IntVarEvent
|
|
232
|
+
| TableMapEvent
|
|
233
|
+
| WriteRowsEvent
|
|
234
|
+
| DeleteRowsEvent
|
|
235
|
+
| UpdateRowsEvent
|
|
236
|
+
| UnknownEvent;
|
|
237
|
+
|
|
238
|
+
// Connection options - can be mysql2 options, Connection, or Pool
|
|
239
|
+
export type ZongJiDsn = string | ConnectionOptions | PoolOptions | Connection | Pool;
|
|
240
|
+
|
|
241
|
+
// Main ZongJi class
|
|
242
|
+
declare class ZongJi extends EventEmitter {
|
|
243
|
+
/** Cached table metadata */
|
|
244
|
+
tableMap: Record<number, TableMapEntry>;
|
|
245
|
+
/** Whether ZongJi is ready to receive events */
|
|
246
|
+
ready: boolean;
|
|
247
|
+
/** Whether ZongJi has been stopped */
|
|
248
|
+
stopped: boolean;
|
|
249
|
+
/** Whether binlog checksum is enabled */
|
|
250
|
+
useChecksum: boolean;
|
|
251
|
+
/** Current binlog options */
|
|
252
|
+
options: {
|
|
253
|
+
serverId?: number;
|
|
254
|
+
filename?: string;
|
|
255
|
+
position?: number;
|
|
256
|
+
startAtEnd?: boolean;
|
|
257
|
+
};
|
|
258
|
+
/** Current filter settings */
|
|
259
|
+
filters: {
|
|
260
|
+
includeEvents?: string[];
|
|
261
|
+
excludeEvents?: string[];
|
|
262
|
+
includeSchema?: Record<string, string[] | true>;
|
|
263
|
+
excludeSchema?: Record<string, string[] | true>;
|
|
264
|
+
};
|
|
265
|
+
/** The control connection (for metadata queries) */
|
|
266
|
+
ctrlConnection: Connection | null;
|
|
267
|
+
/** The binlog streaming connection */
|
|
268
|
+
connection: Connection | null;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Create a new ZongJi instance
|
|
272
|
+
* @param dsn - Connection string, options object, or existing Connection/Pool
|
|
273
|
+
*/
|
|
274
|
+
constructor(dsn: ZongJiDsn);
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Start listening to the binlog
|
|
278
|
+
* @param options - Configuration options
|
|
279
|
+
*/
|
|
280
|
+
start(options?: ZongJiOptions): void;
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Stop listening to the binlog
|
|
284
|
+
*/
|
|
285
|
+
stop(): void;
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Get current option value(s)
|
|
289
|
+
* @param name - Option name or array of names
|
|
290
|
+
*/
|
|
291
|
+
get(name: string): unknown;
|
|
292
|
+
get(name: string[]): Record<string, unknown>;
|
|
293
|
+
|
|
294
|
+
// Event emitter overloads for type safety
|
|
295
|
+
on(event: 'binlog', listener: (event: AnyBinlogEvent) => void): this;
|
|
296
|
+
on(event: 'ready', listener: () => void): this;
|
|
297
|
+
on(event: 'stopped', listener: () => void): this;
|
|
298
|
+
on(event: 'error', listener: (error: Error) => void): this;
|
|
299
|
+
on(event: string, listener: (...args: unknown[]) => void): this;
|
|
300
|
+
|
|
301
|
+
once(event: 'binlog', listener: (event: AnyBinlogEvent) => void): this;
|
|
302
|
+
once(event: 'ready', listener: () => void): this;
|
|
303
|
+
once(event: 'stopped', listener: () => void): this;
|
|
304
|
+
once(event: 'error', listener: (error: Error) => void): this;
|
|
305
|
+
once(event: string, listener: (...args: unknown[]) => void): this;
|
|
306
|
+
|
|
307
|
+
emit(event: 'binlog', binlogEvent: AnyBinlogEvent): boolean;
|
|
308
|
+
emit(event: 'ready'): boolean;
|
|
309
|
+
emit(event: 'stopped'): boolean;
|
|
310
|
+
emit(event: 'error', error: Error): boolean;
|
|
311
|
+
emit(event: string, ...args: unknown[]): boolean;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export default ZongJi;
|