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.
Files changed (70) hide show
  1. package/README.md +93 -0
  2. package/dist/adapters/cassandra.cjs +117 -0
  3. package/dist/adapters/cassandra.cjs.map +1 -0
  4. package/dist/adapters/cassandra.d.cts +37 -0
  5. package/dist/adapters/cassandra.d.ts +37 -0
  6. package/dist/adapters/cassandra.js +90 -0
  7. package/dist/adapters/cassandra.js.map +1 -0
  8. package/dist/adapters/dynamodb.cjs +180 -0
  9. package/dist/adapters/dynamodb.cjs.map +1 -0
  10. package/dist/adapters/dynamodb.d.cts +48 -0
  11. package/dist/adapters/dynamodb.d.ts +48 -0
  12. package/dist/adapters/dynamodb.js +153 -0
  13. package/dist/adapters/dynamodb.js.map +1 -0
  14. package/dist/adapters/elasticsearch.cjs +120 -0
  15. package/dist/adapters/elasticsearch.cjs.map +1 -0
  16. package/dist/adapters/elasticsearch.d.cts +43 -0
  17. package/dist/adapters/elasticsearch.d.ts +43 -0
  18. package/dist/adapters/elasticsearch.js +93 -0
  19. package/dist/adapters/elasticsearch.js.map +1 -0
  20. package/dist/adapters/mongodb.cjs +159 -0
  21. package/dist/adapters/mongodb.cjs.map +1 -0
  22. package/dist/adapters/mongodb.d.cts +54 -0
  23. package/dist/adapters/mongodb.d.ts +54 -0
  24. package/dist/adapters/mongodb.js +132 -0
  25. package/dist/adapters/mongodb.js.map +1 -0
  26. package/dist/adapters/mysql.cjs +159 -0
  27. package/dist/adapters/mysql.cjs.map +1 -0
  28. package/dist/adapters/mysql.d.cts +63 -0
  29. package/dist/adapters/mysql.d.ts +63 -0
  30. package/dist/adapters/mysql.js +132 -0
  31. package/dist/adapters/mysql.js.map +1 -0
  32. package/dist/adapters/opensearch.cjs +120 -0
  33. package/dist/adapters/opensearch.cjs.map +1 -0
  34. package/dist/adapters/opensearch.d.cts +2 -0
  35. package/dist/adapters/opensearch.d.ts +2 -0
  36. package/dist/adapters/opensearch.js +93 -0
  37. package/dist/adapters/opensearch.js.map +1 -0
  38. package/dist/adapters/postgres.cjs +271 -0
  39. package/dist/adapters/postgres.cjs.map +1 -0
  40. package/dist/adapters/postgres.d.cts +81 -0
  41. package/dist/adapters/postgres.d.ts +81 -0
  42. package/dist/adapters/postgres.js +244 -0
  43. package/dist/adapters/postgres.js.map +1 -0
  44. package/dist/adapters/redis.cjs +153 -0
  45. package/dist/adapters/redis.cjs.map +1 -0
  46. package/dist/adapters/redis.d.cts +40 -0
  47. package/dist/adapters/redis.d.ts +40 -0
  48. package/dist/adapters/redis.js +126 -0
  49. package/dist/adapters/redis.js.map +1 -0
  50. package/dist/adapters/snowflake.cjs +117 -0
  51. package/dist/adapters/snowflake.cjs.map +1 -0
  52. package/dist/adapters/snowflake.d.cts +37 -0
  53. package/dist/adapters/snowflake.d.ts +37 -0
  54. package/dist/adapters/snowflake.js +90 -0
  55. package/dist/adapters/snowflake.js.map +1 -0
  56. package/dist/client/index.cjs +484 -0
  57. package/dist/client/index.cjs.map +1 -0
  58. package/dist/client/index.d.cts +174 -0
  59. package/dist/client/index.d.ts +174 -0
  60. package/dist/client/index.js +455 -0
  61. package/dist/client/index.js.map +1 -0
  62. package/dist/index.cjs +935 -0
  63. package/dist/index.cjs.map +1 -0
  64. package/dist/index.d.cts +190 -0
  65. package/dist/index.d.ts +190 -0
  66. package/dist/index.js +890 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/types-tPDla8AE.d.cts +75 -0
  69. package/dist/types-tPDla8AE.d.ts +75 -0
  70. package/package.json +157 -0
@@ -0,0 +1,159 @@
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/mongodb.ts
21
+ var mongodb_exports = {};
22
+ __export(mongodb_exports, {
23
+ MongoDbAdapter: () => MongoDbAdapter
24
+ });
25
+ module.exports = __toCommonJS(mongodb_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/mongodb/mongodb-adapter.ts
40
+ var MongoDbAdapter = class {
41
+ db;
42
+ watchOptions;
43
+ onError;
44
+ collections = /* @__PURE__ */ new Map();
45
+ connected = false;
46
+ constructor(options) {
47
+ this.db = options.db;
48
+ this.watchOptions = {
49
+ fullDocument: "updateLookup",
50
+ fullDocumentBeforeChange: "whenAvailable",
51
+ ...options.watchOptions
52
+ };
53
+ this.onError = options.onError;
54
+ }
55
+ async connect() {
56
+ this.connected = true;
57
+ for (const collection of this.collections.keys()) {
58
+ this.ensureCollectionStream(collection);
59
+ }
60
+ }
61
+ async disconnect() {
62
+ this.connected = false;
63
+ await Promise.all(
64
+ Array.from(this.collections.values(), async (state) => {
65
+ await state.stream.close();
66
+ })
67
+ );
68
+ }
69
+ onChange(collection, callback) {
70
+ const state = this.collections.get(collection);
71
+ if (state) {
72
+ state.listeners.add(callback);
73
+ } else {
74
+ this.collections.set(collection, {
75
+ stream: null,
76
+ listeners: /* @__PURE__ */ new Set([callback])
77
+ });
78
+ }
79
+ this.ensureCollectionStream(collection);
80
+ return () => {
81
+ const current = this.collections.get(collection);
82
+ if (!current) return;
83
+ current.listeners.delete(callback);
84
+ if (current.listeners.size === 0) {
85
+ void current.stream?.close();
86
+ this.collections.delete(collection);
87
+ }
88
+ };
89
+ }
90
+ ensureCollectionStream(collection) {
91
+ if (!this.connected) return;
92
+ const state = this.collections.get(collection);
93
+ if (!state || state.stream) return;
94
+ try {
95
+ const stream = this.db.collection(collection).watch([], this.watchOptions);
96
+ stream.on("change", (change) => {
97
+ this.handleChange(collection, change);
98
+ });
99
+ stream.on("error", (error) => {
100
+ this.onError?.(error, { collection });
101
+ });
102
+ state.stream = stream;
103
+ } catch (error) {
104
+ throw new ReactiveApiError(
105
+ "MONGODB_STREAM_FAILED",
106
+ `Failed to open MongoDB change stream for "${collection}": ${errorMessage(error)}`
107
+ );
108
+ }
109
+ }
110
+ handleChange(collection, change) {
111
+ const event = mapMongoChangeToEvent(collection, change);
112
+ if (!event) return;
113
+ const state = this.collections.get(collection);
114
+ if (!state) return;
115
+ for (const listener of state.listeners) {
116
+ listener(event);
117
+ }
118
+ }
119
+ };
120
+ function mapMongoChangeToEvent(collection, change) {
121
+ const timestamp = Date.now();
122
+ switch (change.operationType) {
123
+ case "insert":
124
+ return {
125
+ table: collection,
126
+ operation: "INSERT",
127
+ newRow: change.fullDocument ?? null,
128
+ oldRow: null,
129
+ timestamp
130
+ };
131
+ case "replace":
132
+ case "update":
133
+ return {
134
+ table: collection,
135
+ operation: "UPDATE",
136
+ newRow: change.fullDocument ?? null,
137
+ oldRow: change.fullDocumentBeforeChange ?? null,
138
+ timestamp
139
+ };
140
+ case "delete":
141
+ return {
142
+ table: collection,
143
+ operation: "DELETE",
144
+ newRow: null,
145
+ oldRow: change.fullDocumentBeforeChange ?? (change.documentKey ? { _id: change.documentKey._id } : null),
146
+ timestamp
147
+ };
148
+ default:
149
+ return null;
150
+ }
151
+ }
152
+ function errorMessage(error) {
153
+ return error instanceof Error ? error.message : String(error);
154
+ }
155
+ // Annotate the CommonJS export names for ESM import in node:
156
+ 0 && (module.exports = {
157
+ MongoDbAdapter
158
+ });
159
+ //# sourceMappingURL=mongodb.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/mongodb.ts","../../src/core/errors.ts","../../src/adapters/mongodb/mongodb-adapter.ts"],"sourcesContent":["export { MongoDbAdapter } from './mongodb/mongodb-adapter.js'\nexport type {\n MongoAdapterOptions,\n MongoChangeStreamDocument,\n MongoChangeStreamLike,\n MongoCollectionLike,\n MongoDatabaseLike,\n} from './mongodb/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 MongoAdapterOptions,\n MongoChangeStreamDocument,\n MongoCollectionState,\n} from './types.js'\n\n/**\n * MongoDB adapter for RouteFlow.\n *\n * The adapter expects a MongoDB driver's `Db`-compatible object and opens\n * one Change Stream per watched collection.\n */\nexport class MongoDbAdapter implements DatabaseAdapter {\n private readonly db: MongoAdapterOptions['db']\n private readonly watchOptions: Record<string, unknown>\n private readonly onError?: MongoAdapterOptions['onError']\n private readonly collections: Map<string, MongoCollectionState> = new Map()\n private connected = false\n\n constructor(options: MongoAdapterOptions) {\n this.db = options.db\n this.watchOptions = {\n fullDocument: 'updateLookup',\n fullDocumentBeforeChange: 'whenAvailable',\n ...options.watchOptions,\n }\n this.onError = options.onError\n }\n\n async connect(): Promise<void> {\n this.connected = true\n\n for (const collection of this.collections.keys()) {\n this.ensureCollectionStream(collection)\n }\n }\n\n async disconnect(): Promise<void> {\n this.connected = false\n\n await Promise.all(\n Array.from(this.collections.values(), async (state) => {\n await state.stream.close()\n }),\n )\n }\n\n onChange(collection: string, callback: (event: ChangeEvent) => void): () => void {\n const state = this.collections.get(collection)\n if (state) {\n state.listeners.add(callback)\n } else {\n this.collections.set(collection, {\n stream: null as never,\n listeners: new Set([callback]),\n })\n }\n\n this.ensureCollectionStream(collection)\n\n return () => {\n const current = this.collections.get(collection)\n if (!current) return\n\n current.listeners.delete(callback)\n\n if (current.listeners.size === 0) {\n void current.stream?.close()\n this.collections.delete(collection)\n }\n }\n }\n\n private ensureCollectionStream(collection: string): void {\n if (!this.connected) return\n\n const state = this.collections.get(collection)\n if (!state || state.stream) return\n\n try {\n const stream = this.db.collection(collection).watch([], this.watchOptions)\n stream.on('change', (change) => {\n this.handleChange(collection, change)\n })\n stream.on('error', (error) => {\n this.onError?.(error, { collection })\n })\n state.stream = stream\n } catch (error) {\n throw new ReactiveApiError(\n 'MONGODB_STREAM_FAILED',\n `Failed to open MongoDB change stream for \"${collection}\": ${errorMessage(error)}`,\n )\n }\n }\n\n private handleChange(\n collection: string,\n change: MongoChangeStreamDocument<Record<string, unknown>>,\n ): void {\n const event = mapMongoChangeToEvent(collection, change)\n if (!event) return\n\n const state = this.collections.get(collection)\n if (!state) return\n\n for (const listener of state.listeners) {\n listener(event)\n }\n }\n}\n\nfunction mapMongoChangeToEvent(\n collection: string,\n change: MongoChangeStreamDocument<Record<string, unknown>>,\n): ChangeEvent | null {\n const timestamp = Date.now()\n\n switch (change.operationType) {\n case 'insert':\n return {\n table: collection,\n operation: 'INSERT',\n newRow: change.fullDocument ?? null,\n oldRow: null,\n timestamp,\n }\n case 'replace':\n case 'update':\n return {\n table: collection,\n operation: 'UPDATE',\n newRow: change.fullDocument ?? null,\n oldRow: change.fullDocumentBeforeChange ?? null,\n timestamp,\n }\n case 'delete':\n return {\n table: collection,\n operation: 'DELETE',\n newRow: null,\n oldRow:\n change.fullDocumentBeforeChange ??\n (change.documentKey ? { _id: change.documentKey._id } : null),\n timestamp,\n }\n default:\n return null\n }\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,54 @@
1
+ import { D as DatabaseAdapter, C as ChangeEvent } from '../types-tPDla8AE.cjs';
2
+
3
+ interface MongoChangeStreamDocument<TSchema = Record<string, unknown>> {
4
+ operationType: 'insert' | 'update' | 'replace' | 'delete';
5
+ fullDocument?: TSchema | null;
6
+ fullDocumentBeforeChange?: TSchema | null;
7
+ documentKey?: {
8
+ _id?: unknown;
9
+ };
10
+ ns?: {
11
+ db?: string;
12
+ coll?: string;
13
+ };
14
+ }
15
+ interface MongoChangeStreamLike<TSchema = Record<string, unknown>> {
16
+ on(event: 'change', listener: (change: MongoChangeStreamDocument<TSchema>) => void): this;
17
+ on(event: 'error', listener: (error: Error) => void): this;
18
+ close(): Promise<void> | void;
19
+ }
20
+ interface MongoCollectionLike<TSchema = Record<string, unknown>> {
21
+ watch(pipeline?: Array<Record<string, unknown>>, options?: Record<string, unknown>): MongoChangeStreamLike<TSchema>;
22
+ }
23
+ interface MongoDatabaseLike {
24
+ collection<TSchema = Record<string, unknown>>(name: string): MongoCollectionLike<TSchema>;
25
+ }
26
+ interface MongoAdapterOptions {
27
+ db: MongoDatabaseLike;
28
+ watchOptions?: Record<string, unknown>;
29
+ onError?: (error: unknown, context: {
30
+ collection: string;
31
+ }) => void;
32
+ }
33
+
34
+ /**
35
+ * MongoDB adapter for RouteFlow.
36
+ *
37
+ * The adapter expects a MongoDB driver's `Db`-compatible object and opens
38
+ * one Change Stream per watched collection.
39
+ */
40
+ declare class MongoDbAdapter implements DatabaseAdapter {
41
+ private readonly db;
42
+ private readonly watchOptions;
43
+ private readonly onError?;
44
+ private readonly collections;
45
+ private connected;
46
+ constructor(options: MongoAdapterOptions);
47
+ connect(): Promise<void>;
48
+ disconnect(): Promise<void>;
49
+ onChange(collection: string, callback: (event: ChangeEvent) => void): () => void;
50
+ private ensureCollectionStream;
51
+ private handleChange;
52
+ }
53
+
54
+ export { type MongoAdapterOptions, type MongoChangeStreamDocument, type MongoChangeStreamLike, type MongoCollectionLike, type MongoDatabaseLike, MongoDbAdapter };
@@ -0,0 +1,54 @@
1
+ import { D as DatabaseAdapter, C as ChangeEvent } from '../types-tPDla8AE.js';
2
+
3
+ interface MongoChangeStreamDocument<TSchema = Record<string, unknown>> {
4
+ operationType: 'insert' | 'update' | 'replace' | 'delete';
5
+ fullDocument?: TSchema | null;
6
+ fullDocumentBeforeChange?: TSchema | null;
7
+ documentKey?: {
8
+ _id?: unknown;
9
+ };
10
+ ns?: {
11
+ db?: string;
12
+ coll?: string;
13
+ };
14
+ }
15
+ interface MongoChangeStreamLike<TSchema = Record<string, unknown>> {
16
+ on(event: 'change', listener: (change: MongoChangeStreamDocument<TSchema>) => void): this;
17
+ on(event: 'error', listener: (error: Error) => void): this;
18
+ close(): Promise<void> | void;
19
+ }
20
+ interface MongoCollectionLike<TSchema = Record<string, unknown>> {
21
+ watch(pipeline?: Array<Record<string, unknown>>, options?: Record<string, unknown>): MongoChangeStreamLike<TSchema>;
22
+ }
23
+ interface MongoDatabaseLike {
24
+ collection<TSchema = Record<string, unknown>>(name: string): MongoCollectionLike<TSchema>;
25
+ }
26
+ interface MongoAdapterOptions {
27
+ db: MongoDatabaseLike;
28
+ watchOptions?: Record<string, unknown>;
29
+ onError?: (error: unknown, context: {
30
+ collection: string;
31
+ }) => void;
32
+ }
33
+
34
+ /**
35
+ * MongoDB adapter for RouteFlow.
36
+ *
37
+ * The adapter expects a MongoDB driver's `Db`-compatible object and opens
38
+ * one Change Stream per watched collection.
39
+ */
40
+ declare class MongoDbAdapter implements DatabaseAdapter {
41
+ private readonly db;
42
+ private readonly watchOptions;
43
+ private readonly onError?;
44
+ private readonly collections;
45
+ private connected;
46
+ constructor(options: MongoAdapterOptions);
47
+ connect(): Promise<void>;
48
+ disconnect(): Promise<void>;
49
+ onChange(collection: string, callback: (event: ChangeEvent) => void): () => void;
50
+ private ensureCollectionStream;
51
+ private handleChange;
52
+ }
53
+
54
+ export { type MongoAdapterOptions, type MongoChangeStreamDocument, type MongoChangeStreamLike, type MongoCollectionLike, type MongoDatabaseLike, MongoDbAdapter };
@@ -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/mongodb/mongodb-adapter.ts
14
+ var MongoDbAdapter = class {
15
+ db;
16
+ watchOptions;
17
+ onError;
18
+ collections = /* @__PURE__ */ new Map();
19
+ connected = false;
20
+ constructor(options) {
21
+ this.db = options.db;
22
+ this.watchOptions = {
23
+ fullDocument: "updateLookup",
24
+ fullDocumentBeforeChange: "whenAvailable",
25
+ ...options.watchOptions
26
+ };
27
+ this.onError = options.onError;
28
+ }
29
+ async connect() {
30
+ this.connected = true;
31
+ for (const collection of this.collections.keys()) {
32
+ this.ensureCollectionStream(collection);
33
+ }
34
+ }
35
+ async disconnect() {
36
+ this.connected = false;
37
+ await Promise.all(
38
+ Array.from(this.collections.values(), async (state) => {
39
+ await state.stream.close();
40
+ })
41
+ );
42
+ }
43
+ onChange(collection, callback) {
44
+ const state = this.collections.get(collection);
45
+ if (state) {
46
+ state.listeners.add(callback);
47
+ } else {
48
+ this.collections.set(collection, {
49
+ stream: null,
50
+ listeners: /* @__PURE__ */ new Set([callback])
51
+ });
52
+ }
53
+ this.ensureCollectionStream(collection);
54
+ return () => {
55
+ const current = this.collections.get(collection);
56
+ if (!current) return;
57
+ current.listeners.delete(callback);
58
+ if (current.listeners.size === 0) {
59
+ void current.stream?.close();
60
+ this.collections.delete(collection);
61
+ }
62
+ };
63
+ }
64
+ ensureCollectionStream(collection) {
65
+ if (!this.connected) return;
66
+ const state = this.collections.get(collection);
67
+ if (!state || state.stream) return;
68
+ try {
69
+ const stream = this.db.collection(collection).watch([], this.watchOptions);
70
+ stream.on("change", (change) => {
71
+ this.handleChange(collection, change);
72
+ });
73
+ stream.on("error", (error) => {
74
+ this.onError?.(error, { collection });
75
+ });
76
+ state.stream = stream;
77
+ } catch (error) {
78
+ throw new ReactiveApiError(
79
+ "MONGODB_STREAM_FAILED",
80
+ `Failed to open MongoDB change stream for "${collection}": ${errorMessage(error)}`
81
+ );
82
+ }
83
+ }
84
+ handleChange(collection, change) {
85
+ const event = mapMongoChangeToEvent(collection, change);
86
+ if (!event) return;
87
+ const state = this.collections.get(collection);
88
+ if (!state) return;
89
+ for (const listener of state.listeners) {
90
+ listener(event);
91
+ }
92
+ }
93
+ };
94
+ function mapMongoChangeToEvent(collection, change) {
95
+ const timestamp = Date.now();
96
+ switch (change.operationType) {
97
+ case "insert":
98
+ return {
99
+ table: collection,
100
+ operation: "INSERT",
101
+ newRow: change.fullDocument ?? null,
102
+ oldRow: null,
103
+ timestamp
104
+ };
105
+ case "replace":
106
+ case "update":
107
+ return {
108
+ table: collection,
109
+ operation: "UPDATE",
110
+ newRow: change.fullDocument ?? null,
111
+ oldRow: change.fullDocumentBeforeChange ?? null,
112
+ timestamp
113
+ };
114
+ case "delete":
115
+ return {
116
+ table: collection,
117
+ operation: "DELETE",
118
+ newRow: null,
119
+ oldRow: change.fullDocumentBeforeChange ?? (change.documentKey ? { _id: change.documentKey._id } : null),
120
+ timestamp
121
+ };
122
+ default:
123
+ return null;
124
+ }
125
+ }
126
+ function errorMessage(error) {
127
+ return error instanceof Error ? error.message : String(error);
128
+ }
129
+ export {
130
+ MongoDbAdapter
131
+ };
132
+ //# sourceMappingURL=mongodb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/errors.ts","../../src/adapters/mongodb/mongodb-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 MongoAdapterOptions,\n MongoChangeStreamDocument,\n MongoCollectionState,\n} from './types.js'\n\n/**\n * MongoDB adapter for RouteFlow.\n *\n * The adapter expects a MongoDB driver's `Db`-compatible object and opens\n * one Change Stream per watched collection.\n */\nexport class MongoDbAdapter implements DatabaseAdapter {\n private readonly db: MongoAdapterOptions['db']\n private readonly watchOptions: Record<string, unknown>\n private readonly onError?: MongoAdapterOptions['onError']\n private readonly collections: Map<string, MongoCollectionState> = new Map()\n private connected = false\n\n constructor(options: MongoAdapterOptions) {\n this.db = options.db\n this.watchOptions = {\n fullDocument: 'updateLookup',\n fullDocumentBeforeChange: 'whenAvailable',\n ...options.watchOptions,\n }\n this.onError = options.onError\n }\n\n async connect(): Promise<void> {\n this.connected = true\n\n for (const collection of this.collections.keys()) {\n this.ensureCollectionStream(collection)\n }\n }\n\n async disconnect(): Promise<void> {\n this.connected = false\n\n await Promise.all(\n Array.from(this.collections.values(), async (state) => {\n await state.stream.close()\n }),\n )\n }\n\n onChange(collection: string, callback: (event: ChangeEvent) => void): () => void {\n const state = this.collections.get(collection)\n if (state) {\n state.listeners.add(callback)\n } else {\n this.collections.set(collection, {\n stream: null as never,\n listeners: new Set([callback]),\n })\n }\n\n this.ensureCollectionStream(collection)\n\n return () => {\n const current = this.collections.get(collection)\n if (!current) return\n\n current.listeners.delete(callback)\n\n if (current.listeners.size === 0) {\n void current.stream?.close()\n this.collections.delete(collection)\n }\n }\n }\n\n private ensureCollectionStream(collection: string): void {\n if (!this.connected) return\n\n const state = this.collections.get(collection)\n if (!state || state.stream) return\n\n try {\n const stream = this.db.collection(collection).watch([], this.watchOptions)\n stream.on('change', (change) => {\n this.handleChange(collection, change)\n })\n stream.on('error', (error) => {\n this.onError?.(error, { collection })\n })\n state.stream = stream\n } catch (error) {\n throw new ReactiveApiError(\n 'MONGODB_STREAM_FAILED',\n `Failed to open MongoDB change stream for \"${collection}\": ${errorMessage(error)}`,\n )\n }\n }\n\n private handleChange(\n collection: string,\n change: MongoChangeStreamDocument<Record<string, unknown>>,\n ): void {\n const event = mapMongoChangeToEvent(collection, change)\n if (!event) return\n\n const state = this.collections.get(collection)\n if (!state) return\n\n for (const listener of state.listeners) {\n listener(event)\n }\n }\n}\n\nfunction mapMongoChangeToEvent(\n collection: string,\n change: MongoChangeStreamDocument<Record<string, unknown>>,\n): ChangeEvent | null {\n const timestamp = Date.now()\n\n switch (change.operationType) {\n case 'insert':\n return {\n table: collection,\n operation: 'INSERT',\n newRow: change.fullDocument ?? null,\n oldRow: null,\n timestamp,\n }\n case 'replace':\n case 'update':\n return {\n table: collection,\n operation: 'UPDATE',\n newRow: change.fullDocument ?? null,\n oldRow: change.fullDocumentBeforeChange ?? null,\n timestamp,\n }\n case 'delete':\n return {\n table: collection,\n operation: 'DELETE',\n newRow: null,\n oldRow:\n change.fullDocumentBeforeChange ??\n (change.documentKey ? { _id: change.documentKey._id } : null),\n timestamp,\n }\n default:\n return null\n }\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,159 @@
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/mysql.ts
21
+ var mysql_exports = {};
22
+ __export(mysql_exports, {
23
+ MySqlAdapter: () => MySqlAdapter
24
+ });
25
+ module.exports = __toCommonJS(mysql_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/mysql/mysql-adapter.ts
40
+ var MySqlAdapter = class {
41
+ source;
42
+ schema;
43
+ startOptions;
44
+ onError;
45
+ listeners = /* @__PURE__ */ new Map();
46
+ handleBinlogBound = (event) => {
47
+ this.handleBinlog(event);
48
+ };
49
+ handleErrorBound = (error) => {
50
+ this.onError?.(error);
51
+ };
52
+ connected = false;
53
+ constructor(options) {
54
+ this.source = options.source;
55
+ this.schema = options.schema;
56
+ this.startOptions = options.startOptions;
57
+ this.onError = options.onError;
58
+ }
59
+ async connect() {
60
+ if (this.connected) return;
61
+ this.source.on("binlog", this.handleBinlogBound);
62
+ this.source.on("error", this.handleErrorBound);
63
+ try {
64
+ await this.source.start?.(this.startOptions);
65
+ } catch (error) {
66
+ throw new ReactiveApiError(
67
+ "MYSQL_BINLOG_START_FAILED",
68
+ `Failed to start MySQL binlog source: ${errorMessage(error)}`
69
+ );
70
+ }
71
+ this.connected = true;
72
+ }
73
+ async disconnect() {
74
+ if (!this.connected) return;
75
+ removeListener(this.source, "binlog", this.handleBinlogBound);
76
+ removeListener(this.source, "error", this.handleErrorBound);
77
+ await this.source.stop?.();
78
+ this.listeners.clear();
79
+ this.connected = false;
80
+ }
81
+ onChange(table, callback) {
82
+ if (!this.listeners.has(table)) {
83
+ this.listeners.set(table, /* @__PURE__ */ new Set());
84
+ }
85
+ this.listeners.get(table).add(callback);
86
+ return () => {
87
+ const callbacks = this.listeners.get(table);
88
+ if (!callbacks) return;
89
+ callbacks.delete(callback);
90
+ if (callbacks.size === 0) {
91
+ this.listeners.delete(table);
92
+ }
93
+ };
94
+ }
95
+ handleBinlog(event) {
96
+ const changes = normaliseBinlogEvent(event);
97
+ if (changes.length === 0) return;
98
+ for (const change of changes) {
99
+ if (this.schema && readSchemaName(event) !== this.schema) continue;
100
+ const callbacks = this.listeners.get(change.table);
101
+ if (!callbacks) continue;
102
+ const fluxEvent = {
103
+ ...change,
104
+ timestamp: Date.now()
105
+ };
106
+ for (const callback of callbacks) {
107
+ callback(fluxEvent);
108
+ }
109
+ }
110
+ }
111
+ };
112
+ function normaliseBinlogEvent(event) {
113
+ const typeName = event.getTypeName?.().toLowerCase() ?? "";
114
+ const table = event.tableMap?.tableName;
115
+ if (!table) return [];
116
+ if (typeName.includes("write")) {
117
+ return event.rows.map((row) => ({
118
+ table,
119
+ operation: "INSERT",
120
+ newRow: row,
121
+ oldRow: null
122
+ }));
123
+ }
124
+ if (typeName.includes("delete")) {
125
+ return event.rows.map((row) => ({
126
+ table,
127
+ operation: "DELETE",
128
+ newRow: null,
129
+ oldRow: row
130
+ }));
131
+ }
132
+ if (typeName.includes("update")) {
133
+ return event.rows.map((row) => ({
134
+ table,
135
+ operation: "UPDATE",
136
+ newRow: row.after,
137
+ oldRow: row.before
138
+ }));
139
+ }
140
+ return [];
141
+ }
142
+ function readSchemaName(event) {
143
+ return event.tableMap?.parentSchema ?? event.tableMap?.schemaName;
144
+ }
145
+ function removeListener(source, event, listener) {
146
+ if (source.off) {
147
+ source.off(event, listener);
148
+ return;
149
+ }
150
+ source.removeListener?.(event, listener);
151
+ }
152
+ function errorMessage(error) {
153
+ return error instanceof Error ? error.message : String(error);
154
+ }
155
+ // Annotate the CommonJS export names for ESM import in node:
156
+ 0 && (module.exports = {
157
+ MySqlAdapter
158
+ });
159
+ //# sourceMappingURL=mysql.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/mysql.ts","../../src/core/errors.ts","../../src/adapters/mysql/mysql-adapter.ts"],"sourcesContent":["export { MySqlAdapter } from './mysql/mysql-adapter.js'\nexport type { MySqlAdapterOptions } from './mysql/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 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,63 @@
1
+ import { D as DatabaseAdapter, C as ChangeEvent } from '../types-tPDla8AE.cjs';
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 };