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
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # routeflow
2
+
3
+ RouteFlow — REST API with real-time database push subscriptions
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install routeflow
9
+ ```
10
+
11
+ For PostgreSQL:
12
+ ```bash
13
+ npm install routeflow pg
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ ### Server
19
+
20
+ ```typescript
21
+ import 'reflect-metadata'
22
+ import { createApp, MemoryAdapter, Route, Reactive } from 'routeflow'
23
+ import type { Context } from 'routeflow'
24
+
25
+ const adapter = new MemoryAdapter()
26
+ const items = [{ id: 1, name: 'Apple' }]
27
+
28
+ class ItemController {
29
+ @Route('GET', '/items')
30
+ async listItems(_ctx: Context) {
31
+ return items
32
+ }
33
+
34
+ @Reactive({ watch: 'items' })
35
+ @Route('GET', '/items/live')
36
+ async listLiveItems(_ctx: Context) {
37
+ return items
38
+ }
39
+ }
40
+
41
+ const app = createApp({ adapter, port: 3000 })
42
+ app.register(ItemController)
43
+ await app.listen()
44
+ ```
45
+
46
+ ### Client
47
+
48
+ ```typescript
49
+ import { createClient } from 'routeflow/client'
50
+
51
+ const client = createClient('http://localhost:3000')
52
+
53
+ const snapshot = await client.get('/items')
54
+
55
+ const unsubscribe = client.subscribe('/items/live', (items) => {
56
+ console.log('live update', items)
57
+ })
58
+ ```
59
+
60
+ ### Database Adapters
61
+
62
+ ```typescript
63
+ // PostgreSQL
64
+ import { PostgresAdapter } from 'routeflow/adapters/postgres'
65
+
66
+ // MongoDB
67
+ import { MongoDbAdapter } from 'routeflow/adapters/mongodb'
68
+
69
+ // MySQL
70
+ import { MySqlAdapter } from 'routeflow/adapters/mysql'
71
+
72
+ // Redis
73
+ import { RedisAdapter } from 'routeflow/adapters/redis'
74
+
75
+ // DynamoDB
76
+ import { DynamoDbAdapter } from 'routeflow/adapters/dynamodb'
77
+
78
+ // Elasticsearch
79
+ import { ElasticsearchAdapter } from 'routeflow/adapters/elasticsearch'
80
+
81
+ // OpenSearch
82
+ import { OpenSearchAdapter } from 'routeflow/adapters/opensearch'
83
+
84
+ // Snowflake
85
+ import { SnowflakeAdapter } from 'routeflow/adapters/snowflake'
86
+
87
+ // Cassandra
88
+ import { CassandraAdapter } from 'routeflow/adapters/cassandra'
89
+ ```
90
+
91
+ ## License
92
+
93
+ MIT
@@ -0,0 +1,117 @@
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/cassandra.ts
21
+ var cassandra_exports = {};
22
+ __export(cassandra_exports, {
23
+ CassandraAdapter: () => CassandraAdapter
24
+ });
25
+ module.exports = __toCommonJS(cassandra_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/cassandra/cassandra-adapter.ts
40
+ var CassandraAdapter = class {
41
+ source;
42
+ onError;
43
+ listeners = /* @__PURE__ */ new Map();
44
+ handleChangeBound = (event) => {
45
+ this.handleChange(event);
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
+ "CASSANDRA_SOURCE_START_FAILED",
64
+ `Failed to start Cassandra CDC 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(table, callback) {
78
+ if (!this.listeners.has(table)) {
79
+ this.listeners.set(table, /* @__PURE__ */ new Set());
80
+ }
81
+ this.listeners.get(table).add(callback);
82
+ return () => {
83
+ const callbacks = this.listeners.get(table);
84
+ if (!callbacks) return;
85
+ callbacks.delete(callback);
86
+ if (callbacks.size === 0) {
87
+ this.listeners.delete(table);
88
+ }
89
+ };
90
+ }
91
+ handleChange(change) {
92
+ const callbacks = this.listeners.get(change.table);
93
+ if (!callbacks) return;
94
+ const event = {
95
+ ...change,
96
+ timestamp: change.timestamp ?? Date.now()
97
+ };
98
+ for (const callback of callbacks) {
99
+ callback(event);
100
+ }
101
+ }
102
+ };
103
+ function removeListener(source, event, listener) {
104
+ if (source.off) {
105
+ source.off(event, listener);
106
+ return;
107
+ }
108
+ source.removeListener?.(event, listener);
109
+ }
110
+ function errorMessage(error) {
111
+ return error instanceof Error ? error.message : String(error);
112
+ }
113
+ // Annotate the CommonJS export names for ESM import in node:
114
+ 0 && (module.exports = {
115
+ CassandraAdapter
116
+ });
117
+ //# sourceMappingURL=cassandra.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/cassandra.ts","../../src/core/errors.ts","../../src/adapters/cassandra/cassandra-adapter.ts"],"sourcesContent":["export { CassandraAdapter } from './cassandra/cassandra-adapter.js'\nexport type { CassandraAdapterOptions } from './cassandra/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 CassandraAdapterOptions,\n CassandraCdcEvent,\n CassandraCdcSource,\n} from './types.js'\n\nexport class CassandraAdapter implements DatabaseAdapter {\n private readonly source: CassandraCdcSource\n private readonly onError?: CassandraAdapterOptions['onError']\n private readonly listeners: Map<string, Set<(event: ChangeEvent) => void>> = new Map()\n private readonly handleChangeBound = (event: CassandraCdcEvent) => {\n this.handleChange(event)\n }\n private readonly handleErrorBound = (error: Error) => {\n this.onError?.(error)\n }\n private connected = false\n\n constructor(options: CassandraAdapterOptions) {\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 'CASSANDRA_SOURCE_START_FAILED',\n `Failed to start Cassandra CDC 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(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 handleChange(change: CassandraCdcEvent): void {\n const callbacks = this.listeners.get(change.table)\n if (!callbacks) return\n\n const event: ChangeEvent = {\n ...change,\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: CassandraCdcSource,\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;;;ACPO,IAAM,mBAAN,MAAkD;AAAA,EACtC;AAAA,EACA;AAAA,EACA,YAA4D,oBAAI,IAAI;AAAA,EACpE,oBAAoB,CAAC,UAA6B;AACjE,SAAK,aAAa,KAAK;AAAA,EACzB;AAAA,EACiB,mBAAmB,CAAC,UAAiB;AACpD,SAAK,UAAU,KAAK;AAAA,EACtB;AAAA,EACQ,YAAY;AAAA,EAEpB,YAAY,SAAkC;AAC5C,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,yCAAyC,aAAa,KAAK,CAAC;AAAA,MAC9D;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,QAAiC;AACpD,UAAM,YAAY,KAAK,UAAU,IAAI,OAAO,KAAK;AACjD,QAAI,CAAC,UAAW;AAEhB,UAAM,QAAqB;AAAA,MACzB,GAAG;AAAA,MACH,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,37 @@
1
+ import { C as ChangeEvent, D as DatabaseAdapter } from '../types-tPDla8AE.cjs';
2
+
3
+ interface CassandraCdcEvent<T = unknown> {
4
+ table: string;
5
+ operation: ChangeEvent<T>['operation'];
6
+ newRow: T | null;
7
+ oldRow: T | null;
8
+ timestamp?: number;
9
+ }
10
+ interface CassandraCdcSource {
11
+ on(event: 'change', listener: (event: CassandraCdcEvent) => void): void;
12
+ on(event: 'error', listener: (error: Error) => void): void;
13
+ off?(event: 'change' | 'error', listener: (...args: any[]) => void): void;
14
+ removeListener?(event: 'change' | 'error', listener: (...args: any[]) => void): void;
15
+ start?(): Promise<void> | void;
16
+ stop?(): Promise<void> | void;
17
+ }
18
+ interface CassandraAdapterOptions {
19
+ source: CassandraCdcSource;
20
+ onError?: (error: unknown) => void;
21
+ }
22
+
23
+ declare class CassandraAdapter implements DatabaseAdapter {
24
+ private readonly source;
25
+ private readonly onError?;
26
+ private readonly listeners;
27
+ private readonly handleChangeBound;
28
+ private readonly handleErrorBound;
29
+ private connected;
30
+ constructor(options: CassandraAdapterOptions);
31
+ connect(): Promise<void>;
32
+ disconnect(): Promise<void>;
33
+ onChange(table: string, callback: (event: ChangeEvent) => void): () => void;
34
+ private handleChange;
35
+ }
36
+
37
+ export { CassandraAdapter, type CassandraAdapterOptions };
@@ -0,0 +1,37 @@
1
+ import { C as ChangeEvent, D as DatabaseAdapter } from '../types-tPDla8AE.js';
2
+
3
+ interface CassandraCdcEvent<T = unknown> {
4
+ table: string;
5
+ operation: ChangeEvent<T>['operation'];
6
+ newRow: T | null;
7
+ oldRow: T | null;
8
+ timestamp?: number;
9
+ }
10
+ interface CassandraCdcSource {
11
+ on(event: 'change', listener: (event: CassandraCdcEvent) => void): void;
12
+ on(event: 'error', listener: (error: Error) => void): void;
13
+ off?(event: 'change' | 'error', listener: (...args: any[]) => void): void;
14
+ removeListener?(event: 'change' | 'error', listener: (...args: any[]) => void): void;
15
+ start?(): Promise<void> | void;
16
+ stop?(): Promise<void> | void;
17
+ }
18
+ interface CassandraAdapterOptions {
19
+ source: CassandraCdcSource;
20
+ onError?: (error: unknown) => void;
21
+ }
22
+
23
+ declare class CassandraAdapter implements DatabaseAdapter {
24
+ private readonly source;
25
+ private readonly onError?;
26
+ private readonly listeners;
27
+ private readonly handleChangeBound;
28
+ private readonly handleErrorBound;
29
+ private connected;
30
+ constructor(options: CassandraAdapterOptions);
31
+ connect(): Promise<void>;
32
+ disconnect(): Promise<void>;
33
+ onChange(table: string, callback: (event: ChangeEvent) => void): () => void;
34
+ private handleChange;
35
+ }
36
+
37
+ export { CassandraAdapter, type CassandraAdapterOptions };
@@ -0,0 +1,90 @@
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/cassandra/cassandra-adapter.ts
14
+ var CassandraAdapter = class {
15
+ source;
16
+ onError;
17
+ listeners = /* @__PURE__ */ new Map();
18
+ handleChangeBound = (event) => {
19
+ this.handleChange(event);
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
+ "CASSANDRA_SOURCE_START_FAILED",
38
+ `Failed to start Cassandra CDC 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(table, callback) {
52
+ if (!this.listeners.has(table)) {
53
+ this.listeners.set(table, /* @__PURE__ */ new Set());
54
+ }
55
+ this.listeners.get(table).add(callback);
56
+ return () => {
57
+ const callbacks = this.listeners.get(table);
58
+ if (!callbacks) return;
59
+ callbacks.delete(callback);
60
+ if (callbacks.size === 0) {
61
+ this.listeners.delete(table);
62
+ }
63
+ };
64
+ }
65
+ handleChange(change) {
66
+ const callbacks = this.listeners.get(change.table);
67
+ if (!callbacks) return;
68
+ const event = {
69
+ ...change,
70
+ timestamp: change.timestamp ?? Date.now()
71
+ };
72
+ for (const callback of callbacks) {
73
+ callback(event);
74
+ }
75
+ }
76
+ };
77
+ function removeListener(source, event, listener) {
78
+ if (source.off) {
79
+ source.off(event, listener);
80
+ return;
81
+ }
82
+ source.removeListener?.(event, listener);
83
+ }
84
+ function errorMessage(error) {
85
+ return error instanceof Error ? error.message : String(error);
86
+ }
87
+ export {
88
+ CassandraAdapter
89
+ };
90
+ //# sourceMappingURL=cassandra.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/errors.ts","../../src/adapters/cassandra/cassandra-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 CassandraAdapterOptions,\n CassandraCdcEvent,\n CassandraCdcSource,\n} from './types.js'\n\nexport class CassandraAdapter implements DatabaseAdapter {\n private readonly source: CassandraCdcSource\n private readonly onError?: CassandraAdapterOptions['onError']\n private readonly listeners: Map<string, Set<(event: ChangeEvent) => void>> = new Map()\n private readonly handleChangeBound = (event: CassandraCdcEvent) => {\n this.handleChange(event)\n }\n private readonly handleErrorBound = (error: Error) => {\n this.onError?.(error)\n }\n private connected = false\n\n constructor(options: CassandraAdapterOptions) {\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 'CASSANDRA_SOURCE_START_FAILED',\n `Failed to start Cassandra CDC 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(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 handleChange(change: CassandraCdcEvent): void {\n const callbacks = this.listeners.get(change.table)\n if (!callbacks) return\n\n const event: ChangeEvent = {\n ...change,\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: CassandraCdcSource,\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;;;ACPO,IAAM,mBAAN,MAAkD;AAAA,EACtC;AAAA,EACA;AAAA,EACA,YAA4D,oBAAI,IAAI;AAAA,EACpE,oBAAoB,CAAC,UAA6B;AACjE,SAAK,aAAa,KAAK;AAAA,EACzB;AAAA,EACiB,mBAAmB,CAAC,UAAiB;AACpD,SAAK,UAAU,KAAK;AAAA,EACtB;AAAA,EACQ,YAAY;AAAA,EAEpB,YAAY,SAAkC;AAC5C,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,yCAAyC,aAAa,KAAK,CAAC;AAAA,MAC9D;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,QAAiC;AACpD,UAAM,YAAY,KAAK,UAAU,IAAI,OAAO,KAAK;AACjD,QAAI,CAAC,UAAW;AAEhB,UAAM,QAAqB;AAAA,MACzB,GAAG;AAAA,MACH,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,180 @@
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/dynamodb.ts
21
+ var dynamodb_exports = {};
22
+ __export(dynamodb_exports, {
23
+ DynamoDbAdapter: () => DynamoDbAdapter
24
+ });
25
+ module.exports = __toCommonJS(dynamodb_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/dynamodb/dynamodb-adapter.ts
40
+ var DynamoDbAdapter = class {
41
+ source;
42
+ unmarshall;
43
+ onError;
44
+ listeners = /* @__PURE__ */ new Map();
45
+ handleRecordBound = (record) => {
46
+ this.handleRecord(record);
47
+ };
48
+ handleErrorBound = (error) => {
49
+ this.onError?.(error);
50
+ };
51
+ connected = false;
52
+ constructor(options) {
53
+ this.source = options.source;
54
+ this.unmarshall = options.unmarshall ?? defaultUnmarshall;
55
+ this.onError = options.onError;
56
+ }
57
+ async connect() {
58
+ if (this.connected) return;
59
+ this.source.on("record", this.handleRecordBound);
60
+ this.source.on("error", this.handleErrorBound);
61
+ try {
62
+ await this.source.start?.();
63
+ } catch (error) {
64
+ throw new ReactiveApiError(
65
+ "DYNAMODB_STREAM_START_FAILED",
66
+ `Failed to start DynamoDB stream source: ${errorMessage(error)}`
67
+ );
68
+ }
69
+ this.connected = true;
70
+ }
71
+ async disconnect() {
72
+ if (!this.connected) return;
73
+ removeListener(this.source, "record", this.handleRecordBound);
74
+ removeListener(this.source, "error", this.handleErrorBound);
75
+ await this.source.stop?.();
76
+ this.listeners.clear();
77
+ this.connected = false;
78
+ }
79
+ onChange(table, callback) {
80
+ if (!this.listeners.has(table)) {
81
+ this.listeners.set(table, /* @__PURE__ */ new Set());
82
+ }
83
+ this.listeners.get(table).add(callback);
84
+ return () => {
85
+ const callbacks = this.listeners.get(table);
86
+ if (!callbacks) return;
87
+ callbacks.delete(callback);
88
+ if (callbacks.size === 0) {
89
+ this.listeners.delete(table);
90
+ }
91
+ };
92
+ }
93
+ handleRecord(record) {
94
+ const normalised = normaliseRecord(record, this.unmarshall);
95
+ if (!normalised) return;
96
+ const callbacks = this.listeners.get(normalised.table);
97
+ if (!callbacks) return;
98
+ const event = {
99
+ ...normalised,
100
+ timestamp: Date.now()
101
+ };
102
+ for (const callback of callbacks) {
103
+ callback(event);
104
+ }
105
+ }
106
+ };
107
+ function normaliseRecord(record, unmarshall) {
108
+ const table = extractTableName(record.eventSourceARN);
109
+ if (!table || !record.eventName) return null;
110
+ if (record.eventName === "INSERT") {
111
+ return {
112
+ table,
113
+ operation: "INSERT",
114
+ newRow: unmarshall(record.dynamodb?.NewImage),
115
+ oldRow: null
116
+ };
117
+ }
118
+ if (record.eventName === "MODIFY") {
119
+ return {
120
+ table,
121
+ operation: "UPDATE",
122
+ newRow: unmarshall(record.dynamodb?.NewImage),
123
+ oldRow: unmarshall(record.dynamodb?.OldImage)
124
+ };
125
+ }
126
+ if (record.eventName === "REMOVE") {
127
+ return {
128
+ table,
129
+ operation: "DELETE",
130
+ newRow: null,
131
+ oldRow: unmarshall(record.dynamodb?.OldImage)
132
+ };
133
+ }
134
+ return null;
135
+ }
136
+ function extractTableName(arn) {
137
+ if (!arn) return null;
138
+ const match = arn.match(/table\/([^/]+)/);
139
+ return match?.[1] ?? null;
140
+ }
141
+ function defaultUnmarshall(image) {
142
+ if (!image) return null;
143
+ const result = {};
144
+ for (const [key, value] of Object.entries(image)) {
145
+ result[key] = decodeAttributeValue(value);
146
+ }
147
+ return result;
148
+ }
149
+ function decodeAttributeValue(value) {
150
+ if (typeof value !== "object" || value === null) return value;
151
+ const record = value;
152
+ if ("S" in record) return record["S"];
153
+ if ("N" in record) return Number(record["N"]);
154
+ if ("BOOL" in record) return record["BOOL"];
155
+ if ("NULL" in record) return null;
156
+ if ("M" in record && typeof record["M"] === "object" && record["M"] !== null) {
157
+ return defaultUnmarshall(record["M"]);
158
+ }
159
+ if ("L" in record && Array.isArray(record["L"])) {
160
+ return record["L"].map((item) => decodeAttributeValue(item));
161
+ }
162
+ if ("SS" in record) return record["SS"];
163
+ if ("NS" in record) return Array.isArray(record["NS"]) ? record["NS"].map(Number) : record["NS"];
164
+ return value;
165
+ }
166
+ function removeListener(source, event, listener) {
167
+ if (source.off) {
168
+ source.off(event, listener);
169
+ return;
170
+ }
171
+ source.removeListener?.(event, listener);
172
+ }
173
+ function errorMessage(error) {
174
+ return error instanceof Error ? error.message : String(error);
175
+ }
176
+ // Annotate the CommonJS export names for ESM import in node:
177
+ 0 && (module.exports = {
178
+ DynamoDbAdapter
179
+ });
180
+ //# sourceMappingURL=dynamodb.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/dynamodb.ts","../../src/core/errors.ts","../../src/adapters/dynamodb/dynamodb-adapter.ts"],"sourcesContent":["export { DynamoDbAdapter } from './dynamodb/dynamodb-adapter.js'\nexport type { DynamoDbAdapterOptions } from './dynamodb/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 DynamoDbAdapterOptions,\n DynamoDbAttributeMap,\n DynamoDbStreamRecord,\n DynamoDbStreamSource,\n NormalisedDynamoDbRecord,\n} from './types.js'\n\n/**\n * DynamoDB adapter for RouteFlow.\n *\n * Consumes DynamoDB Streams-style records from an external source.\n */\nexport class DynamoDbAdapter implements DatabaseAdapter {\n private readonly source: DynamoDbStreamSource\n private readonly unmarshall: NonNullable<DynamoDbAdapterOptions['unmarshall']>\n private readonly onError?: DynamoDbAdapterOptions['onError']\n private readonly listeners: Map<string, Set<(event: ChangeEvent) => void>> = new Map()\n private readonly handleRecordBound = (record: DynamoDbStreamRecord) => {\n this.handleRecord(record)\n }\n private readonly handleErrorBound = (error: Error) => {\n this.onError?.(error)\n }\n private connected = false\n\n constructor(options: DynamoDbAdapterOptions) {\n this.source = options.source\n this.unmarshall = options.unmarshall ?? defaultUnmarshall\n this.onError = options.onError\n }\n\n async connect(): Promise<void> {\n if (this.connected) return\n\n this.source.on('record', this.handleRecordBound)\n this.source.on('error', this.handleErrorBound)\n\n try {\n await this.source.start?.()\n } catch (error) {\n throw new ReactiveApiError(\n 'DYNAMODB_STREAM_START_FAILED',\n `Failed to start DynamoDB stream 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, 'record', this.handleRecordBound)\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 handleRecord(record: DynamoDbStreamRecord): void {\n const normalised = normaliseRecord(record, this.unmarshall)\n if (!normalised) return\n\n const callbacks = this.listeners.get(normalised.table)\n if (!callbacks) return\n\n const event: ChangeEvent = {\n ...normalised,\n timestamp: Date.now(),\n }\n\n for (const callback of callbacks) {\n callback(event)\n }\n }\n}\n\nfunction normaliseRecord(\n record: DynamoDbStreamRecord,\n unmarshall: (image: DynamoDbAttributeMap | undefined) => Record<string, unknown> | null,\n): NormalisedDynamoDbRecord | null {\n const table = extractTableName(record.eventSourceARN)\n if (!table || !record.eventName) return null\n\n if (record.eventName === 'INSERT') {\n return {\n table,\n operation: 'INSERT',\n newRow: unmarshall(record.dynamodb?.NewImage),\n oldRow: null,\n }\n }\n\n if (record.eventName === 'MODIFY') {\n return {\n table,\n operation: 'UPDATE',\n newRow: unmarshall(record.dynamodb?.NewImage),\n oldRow: unmarshall(record.dynamodb?.OldImage),\n }\n }\n\n if (record.eventName === 'REMOVE') {\n return {\n table,\n operation: 'DELETE',\n newRow: null,\n oldRow: unmarshall(record.dynamodb?.OldImage),\n }\n }\n\n return null\n}\n\nfunction extractTableName(arn: string | undefined): string | null {\n if (!arn) return null\n const match = arn.match(/table\\/([^/]+)/)\n return match?.[1] ?? null\n}\n\nfunction defaultUnmarshall(\n image: DynamoDbAttributeMap | undefined,\n): Record<string, unknown> | null {\n if (!image) return null\n\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(image)) {\n result[key] = decodeAttributeValue(value)\n }\n return result\n}\n\nfunction decodeAttributeValue(value: unknown): unknown {\n if (typeof value !== 'object' || value === null) return value\n\n const record = value as Record<string, unknown>\n if ('S' in record) return record['S']\n if ('N' in record) return Number(record['N'])\n if ('BOOL' in record) return record['BOOL']\n if ('NULL' in record) return null\n if ('M' in record && typeof record['M'] === 'object' && record['M'] !== null) {\n return defaultUnmarshall(record['M'] as DynamoDbAttributeMap)\n }\n if ('L' in record && Array.isArray(record['L'])) {\n return record['L'].map((item) => decodeAttributeValue(item))\n }\n if ('SS' in record) return record['SS']\n if ('NS' in record) return Array.isArray(record['NS']) ? record['NS'].map(Number) : record['NS']\n\n return value\n}\n\nfunction removeListener(\n source: DynamoDbStreamSource,\n event: 'record' | '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,48 @@
1
+ import { D as DatabaseAdapter, C as ChangeEvent } from '../types-tPDla8AE.cjs';
2
+
3
+ interface DynamoDbAttributeMap {
4
+ [key: string]: unknown;
5
+ }
6
+ interface DynamoDbStreamRecord {
7
+ eventName?: 'INSERT' | 'MODIFY' | 'REMOVE';
8
+ dynamodb?: {
9
+ NewImage?: DynamoDbAttributeMap;
10
+ OldImage?: DynamoDbAttributeMap;
11
+ };
12
+ eventSourceARN?: string;
13
+ }
14
+ interface DynamoDbStreamSource {
15
+ on(event: 'record', listener: (record: DynamoDbStreamRecord) => void): void;
16
+ on(event: 'error', listener: (error: Error) => void): void;
17
+ off?(event: 'record' | 'error', listener: (...args: any[]) => void): void;
18
+ removeListener?(event: 'record' | 'error', listener: (...args: any[]) => void): void;
19
+ start?(): Promise<void> | void;
20
+ stop?(): Promise<void> | void;
21
+ }
22
+ interface DynamoDbAdapterOptions {
23
+ source: DynamoDbStreamSource;
24
+ unmarshall?: (image: DynamoDbAttributeMap | undefined) => Record<string, unknown> | null;
25
+ onError?: (error: unknown) => void;
26
+ }
27
+
28
+ /**
29
+ * DynamoDB adapter for RouteFlow.
30
+ *
31
+ * Consumes DynamoDB Streams-style records from an external source.
32
+ */
33
+ declare class DynamoDbAdapter implements DatabaseAdapter {
34
+ private readonly source;
35
+ private readonly unmarshall;
36
+ private readonly onError?;
37
+ private readonly listeners;
38
+ private readonly handleRecordBound;
39
+ private readonly handleErrorBound;
40
+ private connected;
41
+ constructor(options: DynamoDbAdapterOptions);
42
+ connect(): Promise<void>;
43
+ disconnect(): Promise<void>;
44
+ onChange(table: string, callback: (event: ChangeEvent) => void): () => void;
45
+ private handleRecord;
46
+ }
47
+
48
+ export { DynamoDbAdapter, type DynamoDbAdapterOptions };