routeflow-api 0.2.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/README.md +93 -0
- package/dist/adapters/cassandra.cjs +117 -0
- package/dist/adapters/cassandra.cjs.map +1 -0
- package/dist/adapters/cassandra.d.cts +37 -0
- package/dist/adapters/cassandra.d.ts +37 -0
- package/dist/adapters/cassandra.js +90 -0
- package/dist/adapters/cassandra.js.map +1 -0
- package/dist/adapters/dynamodb.cjs +180 -0
- package/dist/adapters/dynamodb.cjs.map +1 -0
- package/dist/adapters/dynamodb.d.cts +48 -0
- package/dist/adapters/dynamodb.d.ts +48 -0
- package/dist/adapters/dynamodb.js +153 -0
- package/dist/adapters/dynamodb.js.map +1 -0
- package/dist/adapters/elasticsearch.cjs +120 -0
- package/dist/adapters/elasticsearch.cjs.map +1 -0
- package/dist/adapters/elasticsearch.d.cts +43 -0
- package/dist/adapters/elasticsearch.d.ts +43 -0
- package/dist/adapters/elasticsearch.js +93 -0
- package/dist/adapters/elasticsearch.js.map +1 -0
- package/dist/adapters/mongodb.cjs +159 -0
- package/dist/adapters/mongodb.cjs.map +1 -0
- package/dist/adapters/mongodb.d.cts +54 -0
- package/dist/adapters/mongodb.d.ts +54 -0
- package/dist/adapters/mongodb.js +132 -0
- package/dist/adapters/mongodb.js.map +1 -0
- package/dist/adapters/mysql.cjs +159 -0
- package/dist/adapters/mysql.cjs.map +1 -0
- package/dist/adapters/mysql.d.cts +63 -0
- package/dist/adapters/mysql.d.ts +63 -0
- package/dist/adapters/mysql.js +132 -0
- package/dist/adapters/mysql.js.map +1 -0
- package/dist/adapters/opensearch.cjs +120 -0
- package/dist/adapters/opensearch.cjs.map +1 -0
- package/dist/adapters/opensearch.d.cts +2 -0
- package/dist/adapters/opensearch.d.ts +2 -0
- package/dist/adapters/opensearch.js +93 -0
- package/dist/adapters/opensearch.js.map +1 -0
- package/dist/adapters/postgres.cjs +271 -0
- package/dist/adapters/postgres.cjs.map +1 -0
- package/dist/adapters/postgres.d.cts +81 -0
- package/dist/adapters/postgres.d.ts +81 -0
- package/dist/adapters/postgres.js +244 -0
- package/dist/adapters/postgres.js.map +1 -0
- package/dist/adapters/redis.cjs +153 -0
- package/dist/adapters/redis.cjs.map +1 -0
- package/dist/adapters/redis.d.cts +40 -0
- package/dist/adapters/redis.d.ts +40 -0
- package/dist/adapters/redis.js +126 -0
- package/dist/adapters/redis.js.map +1 -0
- package/dist/adapters/snowflake.cjs +117 -0
- package/dist/adapters/snowflake.cjs.map +1 -0
- package/dist/adapters/snowflake.d.cts +37 -0
- package/dist/adapters/snowflake.d.ts +37 -0
- package/dist/adapters/snowflake.js +90 -0
- package/dist/adapters/snowflake.js.map +1 -0
- package/dist/client/index.cjs +484 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +174 -0
- package/dist/client/index.d.ts +174 -0
- package/dist/client/index.js +455 -0
- package/dist/client/index.js.map +1 -0
- package/dist/index.cjs +935 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +190 -0
- package/dist/index.d.ts +190 -0
- package/dist/index.js +890 -0
- package/dist/index.js.map +1 -0
- package/dist/types-tPDla8AE.d.cts +75 -0
- package/dist/types-tPDla8AE.d.ts +75 -0
- package/package.json +157 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { D as DatabaseAdapter, C as ChangeEvent } from '../types-tPDla8AE.js';
|
|
2
|
+
|
|
3
|
+
interface MySqlBinlogTableMap {
|
|
4
|
+
parentSchema?: string;
|
|
5
|
+
schemaName?: string;
|
|
6
|
+
tableName?: string;
|
|
7
|
+
}
|
|
8
|
+
interface MySqlWriteRowsEvent {
|
|
9
|
+
getTypeName?: () => string;
|
|
10
|
+
tableMap?: MySqlBinlogTableMap;
|
|
11
|
+
rows: Array<Record<string, unknown>>;
|
|
12
|
+
}
|
|
13
|
+
interface MySqlDeleteRowsEvent {
|
|
14
|
+
getTypeName?: () => string;
|
|
15
|
+
tableMap?: MySqlBinlogTableMap;
|
|
16
|
+
rows: Array<Record<string, unknown>>;
|
|
17
|
+
}
|
|
18
|
+
interface MySqlUpdateRowsEvent {
|
|
19
|
+
getTypeName?: () => string;
|
|
20
|
+
tableMap?: MySqlBinlogTableMap;
|
|
21
|
+
rows: Array<{
|
|
22
|
+
before: Record<string, unknown>;
|
|
23
|
+
after: Record<string, unknown>;
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
26
|
+
type MySqlBinlogEvent = MySqlWriteRowsEvent | MySqlDeleteRowsEvent | MySqlUpdateRowsEvent;
|
|
27
|
+
interface MySqlBinlogSource {
|
|
28
|
+
on(event: 'binlog', listener: (event: MySqlBinlogEvent) => void): void;
|
|
29
|
+
on(event: 'error', listener: (error: Error) => void): void;
|
|
30
|
+
off?(event: 'binlog' | 'error', listener: (...args: any[]) => void): void;
|
|
31
|
+
removeListener?(event: 'binlog' | 'error', listener: (...args: any[]) => void): void;
|
|
32
|
+
start?(options?: Record<string, unknown>): Promise<void> | void;
|
|
33
|
+
stop?(): Promise<void> | void;
|
|
34
|
+
}
|
|
35
|
+
interface MySqlAdapterOptions {
|
|
36
|
+
source: MySqlBinlogSource;
|
|
37
|
+
schema?: string;
|
|
38
|
+
startOptions?: Record<string, unknown>;
|
|
39
|
+
onError?: (error: unknown) => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* MySQL adapter for RouteFlow.
|
|
44
|
+
*
|
|
45
|
+
* It consumes a binlog event source compatible with libraries such as ZongJi.
|
|
46
|
+
*/
|
|
47
|
+
declare class MySqlAdapter implements DatabaseAdapter {
|
|
48
|
+
private readonly source;
|
|
49
|
+
private readonly schema?;
|
|
50
|
+
private readonly startOptions?;
|
|
51
|
+
private readonly onError?;
|
|
52
|
+
private readonly listeners;
|
|
53
|
+
private readonly handleBinlogBound;
|
|
54
|
+
private readonly handleErrorBound;
|
|
55
|
+
private connected;
|
|
56
|
+
constructor(options: MySqlAdapterOptions);
|
|
57
|
+
connect(): Promise<void>;
|
|
58
|
+
disconnect(): Promise<void>;
|
|
59
|
+
onChange(table: string, callback: (event: ChangeEvent) => void): () => void;
|
|
60
|
+
private handleBinlog;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { MySqlAdapter, type MySqlAdapterOptions };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// src/core/errors.ts
|
|
2
|
+
var ReactiveApiError = class extends Error {
|
|
3
|
+
/** Machine-readable error code (e.g. 'ADAPTER_NOT_CONNECTED', 'INVALID_ROUTE') */
|
|
4
|
+
code;
|
|
5
|
+
constructor(code, message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "ReactiveApiError";
|
|
8
|
+
this.code = code;
|
|
9
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/adapters/mysql/mysql-adapter.ts
|
|
14
|
+
var MySqlAdapter = class {
|
|
15
|
+
source;
|
|
16
|
+
schema;
|
|
17
|
+
startOptions;
|
|
18
|
+
onError;
|
|
19
|
+
listeners = /* @__PURE__ */ new Map();
|
|
20
|
+
handleBinlogBound = (event) => {
|
|
21
|
+
this.handleBinlog(event);
|
|
22
|
+
};
|
|
23
|
+
handleErrorBound = (error) => {
|
|
24
|
+
this.onError?.(error);
|
|
25
|
+
};
|
|
26
|
+
connected = false;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.source = options.source;
|
|
29
|
+
this.schema = options.schema;
|
|
30
|
+
this.startOptions = options.startOptions;
|
|
31
|
+
this.onError = options.onError;
|
|
32
|
+
}
|
|
33
|
+
async connect() {
|
|
34
|
+
if (this.connected) return;
|
|
35
|
+
this.source.on("binlog", this.handleBinlogBound);
|
|
36
|
+
this.source.on("error", this.handleErrorBound);
|
|
37
|
+
try {
|
|
38
|
+
await this.source.start?.(this.startOptions);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
throw new ReactiveApiError(
|
|
41
|
+
"MYSQL_BINLOG_START_FAILED",
|
|
42
|
+
`Failed to start MySQL binlog source: ${errorMessage(error)}`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
this.connected = true;
|
|
46
|
+
}
|
|
47
|
+
async disconnect() {
|
|
48
|
+
if (!this.connected) return;
|
|
49
|
+
removeListener(this.source, "binlog", this.handleBinlogBound);
|
|
50
|
+
removeListener(this.source, "error", this.handleErrorBound);
|
|
51
|
+
await this.source.stop?.();
|
|
52
|
+
this.listeners.clear();
|
|
53
|
+
this.connected = false;
|
|
54
|
+
}
|
|
55
|
+
onChange(table, callback) {
|
|
56
|
+
if (!this.listeners.has(table)) {
|
|
57
|
+
this.listeners.set(table, /* @__PURE__ */ new Set());
|
|
58
|
+
}
|
|
59
|
+
this.listeners.get(table).add(callback);
|
|
60
|
+
return () => {
|
|
61
|
+
const callbacks = this.listeners.get(table);
|
|
62
|
+
if (!callbacks) return;
|
|
63
|
+
callbacks.delete(callback);
|
|
64
|
+
if (callbacks.size === 0) {
|
|
65
|
+
this.listeners.delete(table);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
handleBinlog(event) {
|
|
70
|
+
const changes = normaliseBinlogEvent(event);
|
|
71
|
+
if (changes.length === 0) return;
|
|
72
|
+
for (const change of changes) {
|
|
73
|
+
if (this.schema && readSchemaName(event) !== this.schema) continue;
|
|
74
|
+
const callbacks = this.listeners.get(change.table);
|
|
75
|
+
if (!callbacks) continue;
|
|
76
|
+
const fluxEvent = {
|
|
77
|
+
...change,
|
|
78
|
+
timestamp: Date.now()
|
|
79
|
+
};
|
|
80
|
+
for (const callback of callbacks) {
|
|
81
|
+
callback(fluxEvent);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
function normaliseBinlogEvent(event) {
|
|
87
|
+
const typeName = event.getTypeName?.().toLowerCase() ?? "";
|
|
88
|
+
const table = event.tableMap?.tableName;
|
|
89
|
+
if (!table) return [];
|
|
90
|
+
if (typeName.includes("write")) {
|
|
91
|
+
return event.rows.map((row) => ({
|
|
92
|
+
table,
|
|
93
|
+
operation: "INSERT",
|
|
94
|
+
newRow: row,
|
|
95
|
+
oldRow: null
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
if (typeName.includes("delete")) {
|
|
99
|
+
return event.rows.map((row) => ({
|
|
100
|
+
table,
|
|
101
|
+
operation: "DELETE",
|
|
102
|
+
newRow: null,
|
|
103
|
+
oldRow: row
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
if (typeName.includes("update")) {
|
|
107
|
+
return event.rows.map((row) => ({
|
|
108
|
+
table,
|
|
109
|
+
operation: "UPDATE",
|
|
110
|
+
newRow: row.after,
|
|
111
|
+
oldRow: row.before
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
function readSchemaName(event) {
|
|
117
|
+
return event.tableMap?.parentSchema ?? event.tableMap?.schemaName;
|
|
118
|
+
}
|
|
119
|
+
function removeListener(source, event, listener) {
|
|
120
|
+
if (source.off) {
|
|
121
|
+
source.off(event, listener);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
source.removeListener?.(event, listener);
|
|
125
|
+
}
|
|
126
|
+
function errorMessage(error) {
|
|
127
|
+
return error instanceof Error ? error.message : String(error);
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
MySqlAdapter
|
|
131
|
+
};
|
|
132
|
+
//# sourceMappingURL=mysql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/errors.ts","../../src/adapters/mysql/mysql-adapter.ts"],"sourcesContent":["/**\n * Base error class for all RouteFlow errors.\n * Always use this instead of plain `Error` throughout the framework.\n */\nexport class ReactiveApiError extends Error {\n /** Machine-readable error code (e.g. 'ADAPTER_NOT_CONNECTED', 'INVALID_ROUTE') */\n readonly code: string\n\n constructor(code: string, message: string) {\n super(message)\n this.name = 'ReactiveApiError'\n this.code = code\n // Restore prototype chain (required when extending built-ins in TS)\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n","import type { ChangeEvent, DatabaseAdapter } from '../../core/types.js'\nimport { ReactiveApiError } from '../../core/errors.js'\nimport type {\n MySqlAdapterOptions,\n MySqlBinlogEvent,\n MySqlBinlogSource,\n NormalisedMySqlChange,\n} from './types.js'\n\n/**\n * MySQL adapter for RouteFlow.\n *\n * It consumes a binlog event source compatible with libraries such as ZongJi.\n */\nexport class MySqlAdapter implements DatabaseAdapter {\n private readonly source: MySqlBinlogSource\n private readonly schema?: string\n private readonly startOptions?: Record<string, unknown>\n private readonly onError?: (error: unknown) => void\n private readonly listeners: Map<string, Set<(event: ChangeEvent) => void>> = new Map()\n private readonly handleBinlogBound = (event: MySqlBinlogEvent) => {\n this.handleBinlog(event)\n }\n private readonly handleErrorBound = (error: Error) => {\n this.onError?.(error)\n }\n private connected = false\n\n constructor(options: MySqlAdapterOptions) {\n this.source = options.source\n this.schema = options.schema\n this.startOptions = options.startOptions\n this.onError = options.onError\n }\n\n async connect(): Promise<void> {\n if (this.connected) return\n\n this.source.on('binlog', this.handleBinlogBound)\n this.source.on('error', this.handleErrorBound)\n\n try {\n await this.source.start?.(this.startOptions)\n } catch (error) {\n throw new ReactiveApiError(\n 'MYSQL_BINLOG_START_FAILED',\n `Failed to start MySQL binlog source: ${errorMessage(error)}`,\n )\n }\n\n this.connected = true\n }\n\n async disconnect(): Promise<void> {\n if (!this.connected) return\n\n removeListener(this.source, 'binlog', this.handleBinlogBound)\n removeListener(this.source, 'error', this.handleErrorBound)\n await this.source.stop?.()\n this.listeners.clear()\n this.connected = false\n }\n\n onChange(table: string, callback: (event: ChangeEvent) => void): () => void {\n if (!this.listeners.has(table)) {\n this.listeners.set(table, new Set())\n }\n\n this.listeners.get(table)!.add(callback)\n\n return () => {\n const callbacks = this.listeners.get(table)\n if (!callbacks) return\n\n callbacks.delete(callback)\n if (callbacks.size === 0) {\n this.listeners.delete(table)\n }\n }\n }\n\n private handleBinlog(event: MySqlBinlogEvent): void {\n const changes = normaliseBinlogEvent(event)\n if (changes.length === 0) return\n\n for (const change of changes) {\n if (this.schema && readSchemaName(event) !== this.schema) continue\n\n const callbacks = this.listeners.get(change.table)\n if (!callbacks) continue\n\n const fluxEvent: ChangeEvent = {\n ...change,\n timestamp: Date.now(),\n }\n\n for (const callback of callbacks) {\n callback(fluxEvent)\n }\n }\n }\n}\n\nfunction normaliseBinlogEvent(event: MySqlBinlogEvent): NormalisedMySqlChange[] {\n const typeName = event.getTypeName?.().toLowerCase() ?? ''\n const table = event.tableMap?.tableName\n if (!table) return []\n\n if (typeName.includes('write')) {\n return event.rows.map((row) => ({\n table,\n operation: 'INSERT',\n newRow: row,\n oldRow: null,\n }))\n }\n\n if (typeName.includes('delete')) {\n return event.rows.map((row) => ({\n table,\n operation: 'DELETE',\n newRow: null,\n oldRow: row,\n }))\n }\n\n if (typeName.includes('update')) {\n return event.rows.map((row) => ({\n table,\n operation: 'UPDATE',\n newRow: row.after,\n oldRow: row.before,\n }))\n }\n\n return []\n}\n\nfunction readSchemaName(event: MySqlBinlogEvent): string | undefined {\n return event.tableMap?.parentSchema ?? event.tableMap?.schemaName\n}\n\nfunction removeListener(\n source: MySqlBinlogSource,\n event: 'binlog' | 'error',\n listener: (...args: any[]) => void,\n): void {\n if (source.off) {\n source.off(event, listener)\n return\n }\n\n source.removeListener?.(event, listener)\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/adapters/opensearch.ts
|
|
21
|
+
var opensearch_exports = {};
|
|
22
|
+
__export(opensearch_exports, {
|
|
23
|
+
OpenSearchAdapter: () => ElasticsearchAdapter
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(opensearch_exports);
|
|
26
|
+
|
|
27
|
+
// src/core/errors.ts
|
|
28
|
+
var ReactiveApiError = class extends Error {
|
|
29
|
+
/** Machine-readable error code (e.g. 'ADAPTER_NOT_CONNECTED', 'INVALID_ROUTE') */
|
|
30
|
+
code;
|
|
31
|
+
constructor(code, message) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "ReactiveApiError";
|
|
34
|
+
this.code = code;
|
|
35
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// src/adapters/elasticsearch/elasticsearch-adapter.ts
|
|
40
|
+
var ElasticsearchAdapter = class {
|
|
41
|
+
source;
|
|
42
|
+
onError;
|
|
43
|
+
listeners = /* @__PURE__ */ new Map();
|
|
44
|
+
handleChangeBound = (change) => {
|
|
45
|
+
this.handleChange(change);
|
|
46
|
+
};
|
|
47
|
+
handleErrorBound = (error) => {
|
|
48
|
+
this.onError?.(error);
|
|
49
|
+
};
|
|
50
|
+
connected = false;
|
|
51
|
+
constructor(options) {
|
|
52
|
+
this.source = options.source;
|
|
53
|
+
this.onError = options.onError;
|
|
54
|
+
}
|
|
55
|
+
async connect() {
|
|
56
|
+
if (this.connected) return;
|
|
57
|
+
this.source.on("change", this.handleChangeBound);
|
|
58
|
+
this.source.on("error", this.handleErrorBound);
|
|
59
|
+
try {
|
|
60
|
+
await this.source.start?.();
|
|
61
|
+
} catch (error) {
|
|
62
|
+
throw new ReactiveApiError(
|
|
63
|
+
"ELASTICSEARCH_SOURCE_START_FAILED",
|
|
64
|
+
`Failed to start Elasticsearch change source: ${errorMessage(error)}`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
this.connected = true;
|
|
68
|
+
}
|
|
69
|
+
async disconnect() {
|
|
70
|
+
if (!this.connected) return;
|
|
71
|
+
removeListener(this.source, "change", this.handleChangeBound);
|
|
72
|
+
removeListener(this.source, "error", this.handleErrorBound);
|
|
73
|
+
await this.source.stop?.();
|
|
74
|
+
this.listeners.clear();
|
|
75
|
+
this.connected = false;
|
|
76
|
+
}
|
|
77
|
+
onChange(index, callback) {
|
|
78
|
+
if (!this.listeners.has(index)) {
|
|
79
|
+
this.listeners.set(index, /* @__PURE__ */ new Set());
|
|
80
|
+
}
|
|
81
|
+
this.listeners.get(index).add(callback);
|
|
82
|
+
return () => {
|
|
83
|
+
const callbacks = this.listeners.get(index);
|
|
84
|
+
if (!callbacks) return;
|
|
85
|
+
callbacks.delete(callback);
|
|
86
|
+
if (callbacks.size === 0) {
|
|
87
|
+
this.listeners.delete(index);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
handleChange(change) {
|
|
92
|
+
const callbacks = this.listeners.get(change.index);
|
|
93
|
+
if (!callbacks) return;
|
|
94
|
+
const event = {
|
|
95
|
+
table: change.index,
|
|
96
|
+
operation: change.operation,
|
|
97
|
+
newRow: change.newDocument,
|
|
98
|
+
oldRow: change.oldDocument,
|
|
99
|
+
timestamp: change.timestamp ?? Date.now()
|
|
100
|
+
};
|
|
101
|
+
for (const callback of callbacks) {
|
|
102
|
+
callback(event);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
function removeListener(source, event, listener) {
|
|
107
|
+
if (source.off) {
|
|
108
|
+
source.off(event, listener);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
source.removeListener?.(event, listener);
|
|
112
|
+
}
|
|
113
|
+
function errorMessage(error) {
|
|
114
|
+
return error instanceof Error ? error.message : String(error);
|
|
115
|
+
}
|
|
116
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
117
|
+
0 && (module.exports = {
|
|
118
|
+
OpenSearchAdapter
|
|
119
|
+
});
|
|
120
|
+
//# sourceMappingURL=opensearch.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/opensearch.ts","../../src/core/errors.ts","../../src/adapters/elasticsearch/elasticsearch-adapter.ts"],"sourcesContent":["export { ElasticsearchAdapter as OpenSearchAdapter } from './elasticsearch/elasticsearch-adapter.js'\nexport type {\n ElasticsearchAdapterOptions as OpenSearchAdapterOptions,\n ElasticsearchChangeSource as OpenSearchChangeSource,\n ElasticsearchSourceEvent as OpenSearchSourceEvent,\n} from './elasticsearch/types.js'\n","/**\n * Base error class for all RouteFlow errors.\n * Always use this instead of plain `Error` throughout the framework.\n */\nexport class ReactiveApiError extends Error {\n /** Machine-readable error code (e.g. 'ADAPTER_NOT_CONNECTED', 'INVALID_ROUTE') */\n readonly code: string\n\n constructor(code: string, message: string) {\n super(message)\n this.name = 'ReactiveApiError'\n this.code = code\n // Restore prototype chain (required when extending built-ins in TS)\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n","import type { ChangeEvent, DatabaseAdapter } from '../../core/types.js'\nimport { ReactiveApiError } from '../../core/errors.js'\nimport type {\n ElasticsearchAdapterOptions,\n ElasticsearchChangeSource,\n ElasticsearchSourceEvent,\n} from './types.js'\n\n/**\n * Elasticsearch adapter for RouteFlow.\n *\n * Elasticsearch does not provide a built-in change stream, so this adapter\n * consumes an external source that emits index-level change events.\n */\nexport class ElasticsearchAdapter implements DatabaseAdapter {\n private readonly source: ElasticsearchChangeSource\n private readonly onError?: ElasticsearchAdapterOptions['onError']\n private readonly listeners: Map<string, Set<(event: ChangeEvent) => void>> = new Map()\n private readonly handleChangeBound = (change: ElasticsearchSourceEvent) => {\n this.handleChange(change)\n }\n private readonly handleErrorBound = (error: Error) => {\n this.onError?.(error)\n }\n private connected = false\n\n constructor(options: ElasticsearchAdapterOptions) {\n this.source = options.source\n this.onError = options.onError\n }\n\n async connect(): Promise<void> {\n if (this.connected) return\n\n this.source.on('change', this.handleChangeBound)\n this.source.on('error', this.handleErrorBound)\n\n try {\n await this.source.start?.()\n } catch (error) {\n throw new ReactiveApiError(\n 'ELASTICSEARCH_SOURCE_START_FAILED',\n `Failed to start Elasticsearch change source: ${errorMessage(error)}`,\n )\n }\n\n this.connected = true\n }\n\n async disconnect(): Promise<void> {\n if (!this.connected) return\n\n removeListener(this.source, 'change', this.handleChangeBound)\n removeListener(this.source, 'error', this.handleErrorBound)\n await this.source.stop?.()\n this.listeners.clear()\n this.connected = false\n }\n\n onChange(index: string, callback: (event: ChangeEvent) => void): () => void {\n if (!this.listeners.has(index)) {\n this.listeners.set(index, new Set())\n }\n\n this.listeners.get(index)!.add(callback)\n\n return () => {\n const callbacks = this.listeners.get(index)\n if (!callbacks) return\n\n callbacks.delete(callback)\n if (callbacks.size === 0) {\n this.listeners.delete(index)\n }\n }\n }\n\n private handleChange(change: ElasticsearchSourceEvent): void {\n const callbacks = this.listeners.get(change.index)\n if (!callbacks) return\n\n const event: ChangeEvent = {\n table: change.index,\n operation: change.operation,\n newRow: change.newDocument,\n oldRow: change.oldDocument,\n timestamp: change.timestamp ?? Date.now(),\n }\n\n for (const callback of callbacks) {\n callback(event)\n }\n }\n}\n\nfunction removeListener(\n source: ElasticsearchChangeSource,\n event: 'change' | 'error',\n listener: (...args: any[]) => void,\n): void {\n if (source.off) {\n source.off(event, listener)\n return\n }\n\n source.removeListener?.(event, listener)\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACIO,IAAM,mBAAN,cAA+B,MAAM;AAAA;AAAA,EAEjC;AAAA,EAET,YAAY,MAAc,SAAiB;AACzC,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AAEZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;;;ACDO,IAAM,uBAAN,MAAsD;AAAA,EAC1C;AAAA,EACA;AAAA,EACA,YAA4D,oBAAI,IAAI;AAAA,EACpE,oBAAoB,CAAC,WAAqC;AACzE,SAAK,aAAa,MAAM;AAAA,EAC1B;AAAA,EACiB,mBAAmB,CAAC,UAAiB;AACpD,SAAK,UAAU,KAAK;AAAA,EACtB;AAAA,EACQ,YAAY;AAAA,EAEpB,YAAY,SAAsC;AAChD,SAAK,SAAS,QAAQ;AACtB,SAAK,UAAU,QAAQ;AAAA,EACzB;AAAA,EAEA,MAAM,UAAyB;AAC7B,QAAI,KAAK,UAAW;AAEpB,SAAK,OAAO,GAAG,UAAU,KAAK,iBAAiB;AAC/C,SAAK,OAAO,GAAG,SAAS,KAAK,gBAAgB;AAE7C,QAAI;AACF,YAAM,KAAK,OAAO,QAAQ;AAAA,IAC5B,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR;AAAA,QACA,gDAAgD,aAAa,KAAK,CAAC;AAAA,MACrE;AAAA,IACF;AAEA,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,aAA4B;AAChC,QAAI,CAAC,KAAK,UAAW;AAErB,mBAAe,KAAK,QAAQ,UAAU,KAAK,iBAAiB;AAC5D,mBAAe,KAAK,QAAQ,SAAS,KAAK,gBAAgB;AAC1D,UAAM,KAAK,OAAO,OAAO;AACzB,SAAK,UAAU,MAAM;AACrB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,SAAS,OAAe,UAAoD;AAC1E,QAAI,CAAC,KAAK,UAAU,IAAI,KAAK,GAAG;AAC9B,WAAK,UAAU,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IACrC;AAEA,SAAK,UAAU,IAAI,KAAK,EAAG,IAAI,QAAQ;AAEvC,WAAO,MAAM;AACX,YAAM,YAAY,KAAK,UAAU,IAAI,KAAK;AAC1C,UAAI,CAAC,UAAW;AAEhB,gBAAU,OAAO,QAAQ;AACzB,UAAI,UAAU,SAAS,GAAG;AACxB,aAAK,UAAU,OAAO,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,aAAa,QAAwC;AAC3D,UAAM,YAAY,KAAK,UAAU,IAAI,OAAO,KAAK;AACjD,QAAI,CAAC,UAAW;AAEhB,UAAM,QAAqB;AAAA,MACzB,OAAO,OAAO;AAAA,MACd,WAAW,OAAO;AAAA,MAClB,QAAQ,OAAO;AAAA,MACf,QAAQ,OAAO;AAAA,MACf,WAAW,OAAO,aAAa,KAAK,IAAI;AAAA,IAC1C;AAEA,eAAW,YAAY,WAAW;AAChC,eAAS,KAAK;AAAA,IAChB;AAAA,EACF;AACF;AAEA,SAAS,eACP,QACA,OACA,UACM;AACN,MAAI,OAAO,KAAK;AACd,WAAO,IAAI,OAAO,QAAQ;AAC1B;AAAA,EACF;AAEA,SAAO,iBAAiB,OAAO,QAAQ;AACzC;AAEA,SAAS,aAAa,OAAwB;AAC5C,SAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;","names":[]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// src/core/errors.ts
|
|
2
|
+
var ReactiveApiError = class extends Error {
|
|
3
|
+
/** Machine-readable error code (e.g. 'ADAPTER_NOT_CONNECTED', 'INVALID_ROUTE') */
|
|
4
|
+
code;
|
|
5
|
+
constructor(code, message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "ReactiveApiError";
|
|
8
|
+
this.code = code;
|
|
9
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/adapters/elasticsearch/elasticsearch-adapter.ts
|
|
14
|
+
var ElasticsearchAdapter = class {
|
|
15
|
+
source;
|
|
16
|
+
onError;
|
|
17
|
+
listeners = /* @__PURE__ */ new Map();
|
|
18
|
+
handleChangeBound = (change) => {
|
|
19
|
+
this.handleChange(change);
|
|
20
|
+
};
|
|
21
|
+
handleErrorBound = (error) => {
|
|
22
|
+
this.onError?.(error);
|
|
23
|
+
};
|
|
24
|
+
connected = false;
|
|
25
|
+
constructor(options) {
|
|
26
|
+
this.source = options.source;
|
|
27
|
+
this.onError = options.onError;
|
|
28
|
+
}
|
|
29
|
+
async connect() {
|
|
30
|
+
if (this.connected) return;
|
|
31
|
+
this.source.on("change", this.handleChangeBound);
|
|
32
|
+
this.source.on("error", this.handleErrorBound);
|
|
33
|
+
try {
|
|
34
|
+
await this.source.start?.();
|
|
35
|
+
} catch (error) {
|
|
36
|
+
throw new ReactiveApiError(
|
|
37
|
+
"ELASTICSEARCH_SOURCE_START_FAILED",
|
|
38
|
+
`Failed to start Elasticsearch change source: ${errorMessage(error)}`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
this.connected = true;
|
|
42
|
+
}
|
|
43
|
+
async disconnect() {
|
|
44
|
+
if (!this.connected) return;
|
|
45
|
+
removeListener(this.source, "change", this.handleChangeBound);
|
|
46
|
+
removeListener(this.source, "error", this.handleErrorBound);
|
|
47
|
+
await this.source.stop?.();
|
|
48
|
+
this.listeners.clear();
|
|
49
|
+
this.connected = false;
|
|
50
|
+
}
|
|
51
|
+
onChange(index, callback) {
|
|
52
|
+
if (!this.listeners.has(index)) {
|
|
53
|
+
this.listeners.set(index, /* @__PURE__ */ new Set());
|
|
54
|
+
}
|
|
55
|
+
this.listeners.get(index).add(callback);
|
|
56
|
+
return () => {
|
|
57
|
+
const callbacks = this.listeners.get(index);
|
|
58
|
+
if (!callbacks) return;
|
|
59
|
+
callbacks.delete(callback);
|
|
60
|
+
if (callbacks.size === 0) {
|
|
61
|
+
this.listeners.delete(index);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
handleChange(change) {
|
|
66
|
+
const callbacks = this.listeners.get(change.index);
|
|
67
|
+
if (!callbacks) return;
|
|
68
|
+
const event = {
|
|
69
|
+
table: change.index,
|
|
70
|
+
operation: change.operation,
|
|
71
|
+
newRow: change.newDocument,
|
|
72
|
+
oldRow: change.oldDocument,
|
|
73
|
+
timestamp: change.timestamp ?? Date.now()
|
|
74
|
+
};
|
|
75
|
+
for (const callback of callbacks) {
|
|
76
|
+
callback(event);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
function removeListener(source, event, listener) {
|
|
81
|
+
if (source.off) {
|
|
82
|
+
source.off(event, listener);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
source.removeListener?.(event, listener);
|
|
86
|
+
}
|
|
87
|
+
function errorMessage(error) {
|
|
88
|
+
return error instanceof Error ? error.message : String(error);
|
|
89
|
+
}
|
|
90
|
+
export {
|
|
91
|
+
ElasticsearchAdapter as OpenSearchAdapter
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=opensearch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/errors.ts","../../src/adapters/elasticsearch/elasticsearch-adapter.ts"],"sourcesContent":["/**\n * Base error class for all RouteFlow errors.\n * Always use this instead of plain `Error` throughout the framework.\n */\nexport class ReactiveApiError extends Error {\n /** Machine-readable error code (e.g. 'ADAPTER_NOT_CONNECTED', 'INVALID_ROUTE') */\n readonly code: string\n\n constructor(code: string, message: string) {\n super(message)\n this.name = 'ReactiveApiError'\n this.code = code\n // Restore prototype chain (required when extending built-ins in TS)\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n","import type { ChangeEvent, DatabaseAdapter } from '../../core/types.js'\nimport { ReactiveApiError } from '../../core/errors.js'\nimport type {\n ElasticsearchAdapterOptions,\n ElasticsearchChangeSource,\n ElasticsearchSourceEvent,\n} from './types.js'\n\n/**\n * Elasticsearch adapter for RouteFlow.\n *\n * Elasticsearch does not provide a built-in change stream, so this adapter\n * consumes an external source that emits index-level change events.\n */\nexport class ElasticsearchAdapter implements DatabaseAdapter {\n private readonly source: ElasticsearchChangeSource\n private readonly onError?: ElasticsearchAdapterOptions['onError']\n private readonly listeners: Map<string, Set<(event: ChangeEvent) => void>> = new Map()\n private readonly handleChangeBound = (change: ElasticsearchSourceEvent) => {\n this.handleChange(change)\n }\n private readonly handleErrorBound = (error: Error) => {\n this.onError?.(error)\n }\n private connected = false\n\n constructor(options: ElasticsearchAdapterOptions) {\n this.source = options.source\n this.onError = options.onError\n }\n\n async connect(): Promise<void> {\n if (this.connected) return\n\n this.source.on('change', this.handleChangeBound)\n this.source.on('error', this.handleErrorBound)\n\n try {\n await this.source.start?.()\n } catch (error) {\n throw new ReactiveApiError(\n 'ELASTICSEARCH_SOURCE_START_FAILED',\n `Failed to start Elasticsearch change source: ${errorMessage(error)}`,\n )\n }\n\n this.connected = true\n }\n\n async disconnect(): Promise<void> {\n if (!this.connected) return\n\n removeListener(this.source, 'change', this.handleChangeBound)\n removeListener(this.source, 'error', this.handleErrorBound)\n await this.source.stop?.()\n this.listeners.clear()\n this.connected = false\n }\n\n onChange(index: string, callback: (event: ChangeEvent) => void): () => void {\n if (!this.listeners.has(index)) {\n this.listeners.set(index, new Set())\n }\n\n this.listeners.get(index)!.add(callback)\n\n return () => {\n const callbacks = this.listeners.get(index)\n if (!callbacks) return\n\n callbacks.delete(callback)\n if (callbacks.size === 0) {\n this.listeners.delete(index)\n }\n }\n }\n\n private handleChange(change: ElasticsearchSourceEvent): void {\n const callbacks = this.listeners.get(change.index)\n if (!callbacks) return\n\n const event: ChangeEvent = {\n table: change.index,\n operation: change.operation,\n newRow: change.newDocument,\n oldRow: change.oldDocument,\n timestamp: change.timestamp ?? Date.now(),\n }\n\n for (const callback of callbacks) {\n callback(event)\n }\n }\n}\n\nfunction removeListener(\n source: ElasticsearchChangeSource,\n event: 'change' | 'error',\n listener: (...args: any[]) => void,\n): void {\n if (source.off) {\n source.off(event, listener)\n return\n }\n\n source.removeListener?.(event, listener)\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n"],"mappings":";AAIO,IAAM,mBAAN,cAA+B,MAAM;AAAA;AAAA,EAEjC;AAAA,EAET,YAAY,MAAc,SAAiB;AACzC,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AAEZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;;;ACDO,IAAM,uBAAN,MAAsD;AAAA,EAC1C;AAAA,EACA;AAAA,EACA,YAA4D,oBAAI,IAAI;AAAA,EACpE,oBAAoB,CAAC,WAAqC;AACzE,SAAK,aAAa,MAAM;AAAA,EAC1B;AAAA,EACiB,mBAAmB,CAAC,UAAiB;AACpD,SAAK,UAAU,KAAK;AAAA,EACtB;AAAA,EACQ,YAAY;AAAA,EAEpB,YAAY,SAAsC;AAChD,SAAK,SAAS,QAAQ;AACtB,SAAK,UAAU,QAAQ;AAAA,EACzB;AAAA,EAEA,MAAM,UAAyB;AAC7B,QAAI,KAAK,UAAW;AAEpB,SAAK,OAAO,GAAG,UAAU,KAAK,iBAAiB;AAC/C,SAAK,OAAO,GAAG,SAAS,KAAK,gBAAgB;AAE7C,QAAI;AACF,YAAM,KAAK,OAAO,QAAQ;AAAA,IAC5B,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR;AAAA,QACA,gDAAgD,aAAa,KAAK,CAAC;AAAA,MACrE;AAAA,IACF;AAEA,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,aAA4B;AAChC,QAAI,CAAC,KAAK,UAAW;AAErB,mBAAe,KAAK,QAAQ,UAAU,KAAK,iBAAiB;AAC5D,mBAAe,KAAK,QAAQ,SAAS,KAAK,gBAAgB;AAC1D,UAAM,KAAK,OAAO,OAAO;AACzB,SAAK,UAAU,MAAM;AACrB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,SAAS,OAAe,UAAoD;AAC1E,QAAI,CAAC,KAAK,UAAU,IAAI,KAAK,GAAG;AAC9B,WAAK,UAAU,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IACrC;AAEA,SAAK,UAAU,IAAI,KAAK,EAAG,IAAI,QAAQ;AAEvC,WAAO,MAAM;AACX,YAAM,YAAY,KAAK,UAAU,IAAI,KAAK;AAC1C,UAAI,CAAC,UAAW;AAEhB,gBAAU,OAAO,QAAQ;AACzB,UAAI,UAAU,SAAS,GAAG;AACxB,aAAK,UAAU,OAAO,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,aAAa,QAAwC;AAC3D,UAAM,YAAY,KAAK,UAAU,IAAI,OAAO,KAAK;AACjD,QAAI,CAAC,UAAW;AAEhB,UAAM,QAAqB;AAAA,MACzB,OAAO,OAAO;AAAA,MACd,WAAW,OAAO;AAAA,MAClB,QAAQ,OAAO;AAAA,MACf,QAAQ,OAAO;AAAA,MACf,WAAW,OAAO,aAAa,KAAK,IAAI;AAAA,IAC1C;AAEA,eAAW,YAAY,WAAW;AAChC,eAAS,KAAK;AAAA,IAChB;AAAA,EACF;AACF;AAEA,SAAS,eACP,QACA,OACA,UACM;AACN,MAAI,OAAO,KAAK;AACd,WAAO,IAAI,OAAO,QAAQ;AAC1B;AAAA,EACF;AAEA,SAAO,iBAAiB,OAAO,QAAQ;AACzC;AAEA,SAAS,aAAa,OAAwB;AAC5C,SAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;","names":[]}
|