@spear340000/core 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapter/memory-adapter.ts","../../src/adapter/polling-adapter.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ChangeEvent, DatabaseAdapter } from '../types.js'\n\n/**\n * In-memory database adapter for testing and local development.\n * No real database connection is needed — changes are triggered manually via `emit()`.\n *\n * @example\n * ```ts\n * const adapter = new MemoryAdapter()\n * await adapter.connect()\n *\n * adapter.onChange('orders', (event) => console.log(event))\n *\n * adapter.emit('orders', { operation: 'INSERT', newRow: { id: 1 }, oldRow: null })\n * ```\n */\nexport class MemoryAdapter implements DatabaseAdapter {\n private readonly listeners: Map<string, Set<(event: ChangeEvent) => void>> = new Map()\n private connected = false\n\n /** No-op — MemoryAdapter requires no real connection. */\n async connect(): Promise<void> {\n this.connected = true\n }\n\n /** No-op — clears all listeners on disconnect. */\n async disconnect(): Promise<void> {\n this.listeners.clear()\n this.connected = false\n }\n\n /**\n * Register a listener for changes on a specific table.\n * @returns An unsubscribe function.\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 this.listeners.get(table)!.add(callback)\n\n return () => {\n this.listeners.get(table)?.delete(callback)\n }\n }\n\n /**\n * Manually emit a change event on a table.\n * Useful in tests and examples to simulate DB changes without a real database.\n *\n * @param table - Table name to emit the event on\n * @param event - Change event data (table and timestamp are filled in automatically)\n */\n emit(table: string, event: Omit<ChangeEvent, 'table' | 'timestamp'>): void {\n const fullEvent: ChangeEvent = {\n ...event,\n table,\n timestamp: Date.now(),\n }\n\n const callbacks = this.listeners.get(table)\n if (!callbacks) return\n\n for (const cb of callbacks) {\n cb(fullEvent)\n }\n }\n\n /** Returns true if connect() has been called and disconnect() has not. */\n get isConnected(): boolean {\n return this.connected\n }\n}\n","import type { ChangeEvent, DatabaseAdapter } from '../types.js'\n\nexport interface PollingReadResult<T = unknown, TCursor = unknown> {\n events: Array<Omit<ChangeEvent<T>, 'table' | 'timestamp'> & Partial<Pick<ChangeEvent<T>, 'table' | 'timestamp'>>>\n cursor?: TCursor\n}\n\nexport interface PollingReadContext<TCursor = unknown> {\n cursor: TCursor | undefined\n table: string\n}\n\nexport interface PollingAdapterOptions<TCursor = unknown> {\n intervalMs?: number\n now?: () => number\n onError?: (error: unknown, context: { table: string }) => void\n readChanges: (\n context: PollingReadContext<TCursor>,\n ) => Promise<PollingReadResult<unknown, TCursor>>\n}\n\n/**\n * Generic polling-based adapter for databases without a native RouteFlow adapter yet.\n * It works with any backend as long as callers can periodically read a change feed.\n */\nexport class PollingAdapter<TCursor = unknown> implements DatabaseAdapter {\n private readonly listeners: Map<string, Set<(event: ChangeEvent) => void>> = new Map()\n private readonly cursors: Map<string, TCursor | undefined> = new Map()\n private readonly timers: Map<string, ReturnType<typeof setTimeout>> = new Map()\n private readonly activeTables: Set<string> = new Set()\n private readonly intervalMs: number\n private readonly now: () => number\n private readonly readChanges: PollingAdapterOptions<TCursor>['readChanges']\n private readonly onError?: PollingAdapterOptions<TCursor>['onError']\n private connected = false\n\n constructor(options: PollingAdapterOptions<TCursor>) {\n this.intervalMs = options.intervalMs ?? 1_000\n this.now = options.now ?? (() => Date.now())\n this.readChanges = options.readChanges\n this.onError = options.onError\n }\n\n async connect(): Promise<void> {\n this.connected = true\n\n for (const table of this.listeners.keys()) {\n this.ensurePolling(table)\n }\n }\n\n async disconnect(): Promise<void> {\n this.connected = false\n\n for (const timer of this.timers.values()) {\n clearTimeout(timer)\n }\n\n this.timers.clear()\n this.activeTables.clear()\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 this.ensurePolling(table)\n\n return () => {\n const callbacks = this.listeners.get(table)\n if (!callbacks) return\n\n callbacks.delete(callback)\n\n if (callbacks.size === 0) {\n this.listeners.delete(table)\n this.stopPolling(table)\n }\n }\n }\n\n private ensurePolling(table: string): void {\n if (!this.connected || this.activeTables.has(table)) return\n\n this.activeTables.add(table)\n void this.poll(table)\n }\n\n private stopPolling(table: string): void {\n const timer = this.timers.get(table)\n if (timer) clearTimeout(timer)\n\n this.timers.delete(table)\n this.activeTables.delete(table)\n this.cursors.delete(table)\n }\n\n private scheduleNext(table: string): void {\n if (!this.connected || !this.listeners.has(table)) {\n this.stopPolling(table)\n return\n }\n\n const timer = setTimeout(() => {\n void this.poll(table)\n }, this.intervalMs)\n\n this.timers.set(table, timer)\n }\n\n private async poll(table: string): Promise<void> {\n if (!this.connected || !this.listeners.has(table)) {\n this.stopPolling(table)\n return\n }\n\n try {\n const result = await this.readChanges({\n table,\n cursor: this.cursors.get(table),\n })\n\n this.cursors.set(table, result.cursor)\n\n for (const event of result.events) {\n this.emit(table, event)\n }\n } catch (error) {\n this.onError?.(error, { table })\n } finally {\n this.activeTables.delete(table)\n this.scheduleNext(table)\n }\n }\n\n private emit(\n defaultTable: string,\n event: Omit<ChangeEvent, 'table' | 'timestamp'> & Partial<Pick<ChangeEvent, 'table' | 'timestamp'>>,\n ): void {\n const fullEvent: ChangeEvent = {\n ...event,\n table: event.table ?? defaultTable,\n timestamp: event.timestamp ?? this.now(),\n }\n\n const callbacks = this.listeners.get(fullEvent.table)\n if (!callbacks) return\n\n for (const callback of callbacks) {\n callback(fullEvent)\n }\n }\n}\n"]}
@@ -0,0 +1,166 @@
1
+ import { __name } from '../chunk-PAWJFY3S.mjs';
2
+
3
+ // src/adapter/memory-adapter.ts
4
+ var MemoryAdapter = class {
5
+ static {
6
+ __name(this, "MemoryAdapter");
7
+ }
8
+ listeners = /* @__PURE__ */ new Map();
9
+ connected = false;
10
+ /** No-op — MemoryAdapter requires no real connection. */
11
+ async connect() {
12
+ this.connected = true;
13
+ }
14
+ /** No-op — clears all listeners on disconnect. */
15
+ async disconnect() {
16
+ this.listeners.clear();
17
+ this.connected = false;
18
+ }
19
+ /**
20
+ * Register a listener for changes on a specific table.
21
+ * @returns An unsubscribe function.
22
+ */
23
+ onChange(table, callback) {
24
+ if (!this.listeners.has(table)) {
25
+ this.listeners.set(table, /* @__PURE__ */ new Set());
26
+ }
27
+ this.listeners.get(table).add(callback);
28
+ return () => {
29
+ this.listeners.get(table)?.delete(callback);
30
+ };
31
+ }
32
+ /**
33
+ * Manually emit a change event on a table.
34
+ * Useful in tests and examples to simulate DB changes without a real database.
35
+ *
36
+ * @param table - Table name to emit the event on
37
+ * @param event - Change event data (table and timestamp are filled in automatically)
38
+ */
39
+ emit(table, event) {
40
+ const fullEvent = {
41
+ ...event,
42
+ table,
43
+ timestamp: Date.now()
44
+ };
45
+ const callbacks = this.listeners.get(table);
46
+ if (!callbacks) return;
47
+ for (const cb of callbacks) {
48
+ cb(fullEvent);
49
+ }
50
+ }
51
+ /** Returns true if connect() has been called and disconnect() has not. */
52
+ get isConnected() {
53
+ return this.connected;
54
+ }
55
+ };
56
+
57
+ // src/adapter/polling-adapter.ts
58
+ var PollingAdapter = class {
59
+ static {
60
+ __name(this, "PollingAdapter");
61
+ }
62
+ listeners = /* @__PURE__ */ new Map();
63
+ cursors = /* @__PURE__ */ new Map();
64
+ timers = /* @__PURE__ */ new Map();
65
+ activeTables = /* @__PURE__ */ new Set();
66
+ intervalMs;
67
+ now;
68
+ readChanges;
69
+ onError;
70
+ connected = false;
71
+ constructor(options) {
72
+ this.intervalMs = options.intervalMs ?? 1e3;
73
+ this.now = options.now ?? (() => Date.now());
74
+ this.readChanges = options.readChanges;
75
+ this.onError = options.onError;
76
+ }
77
+ async connect() {
78
+ this.connected = true;
79
+ for (const table of this.listeners.keys()) {
80
+ this.ensurePolling(table);
81
+ }
82
+ }
83
+ async disconnect() {
84
+ this.connected = false;
85
+ for (const timer of this.timers.values()) {
86
+ clearTimeout(timer);
87
+ }
88
+ this.timers.clear();
89
+ this.activeTables.clear();
90
+ }
91
+ onChange(table, callback) {
92
+ if (!this.listeners.has(table)) {
93
+ this.listeners.set(table, /* @__PURE__ */ new Set());
94
+ }
95
+ this.listeners.get(table).add(callback);
96
+ this.ensurePolling(table);
97
+ return () => {
98
+ const callbacks = this.listeners.get(table);
99
+ if (!callbacks) return;
100
+ callbacks.delete(callback);
101
+ if (callbacks.size === 0) {
102
+ this.listeners.delete(table);
103
+ this.stopPolling(table);
104
+ }
105
+ };
106
+ }
107
+ ensurePolling(table) {
108
+ if (!this.connected || this.activeTables.has(table)) return;
109
+ this.activeTables.add(table);
110
+ void this.poll(table);
111
+ }
112
+ stopPolling(table) {
113
+ const timer = this.timers.get(table);
114
+ if (timer) clearTimeout(timer);
115
+ this.timers.delete(table);
116
+ this.activeTables.delete(table);
117
+ this.cursors.delete(table);
118
+ }
119
+ scheduleNext(table) {
120
+ if (!this.connected || !this.listeners.has(table)) {
121
+ this.stopPolling(table);
122
+ return;
123
+ }
124
+ const timer = setTimeout(() => {
125
+ void this.poll(table);
126
+ }, this.intervalMs);
127
+ this.timers.set(table, timer);
128
+ }
129
+ async poll(table) {
130
+ if (!this.connected || !this.listeners.has(table)) {
131
+ this.stopPolling(table);
132
+ return;
133
+ }
134
+ try {
135
+ const result = await this.readChanges({
136
+ table,
137
+ cursor: this.cursors.get(table)
138
+ });
139
+ this.cursors.set(table, result.cursor);
140
+ for (const event of result.events) {
141
+ this.emit(table, event);
142
+ }
143
+ } catch (error) {
144
+ this.onError?.(error, { table });
145
+ } finally {
146
+ this.activeTables.delete(table);
147
+ this.scheduleNext(table);
148
+ }
149
+ }
150
+ emit(defaultTable, event) {
151
+ const fullEvent = {
152
+ ...event,
153
+ table: event.table ?? defaultTable,
154
+ timestamp: event.timestamp ?? this.now()
155
+ };
156
+ const callbacks = this.listeners.get(fullEvent.table);
157
+ if (!callbacks) return;
158
+ for (const callback of callbacks) {
159
+ callback(fullEvent);
160
+ }
161
+ }
162
+ };
163
+
164
+ export { MemoryAdapter, PollingAdapter };
165
+ //# sourceMappingURL=index.mjs.map
166
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapter/memory-adapter.ts","../../src/adapter/polling-adapter.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { ChangeEvent, DatabaseAdapter } from '../types.js'\n\n/**\n * In-memory database adapter for testing and local development.\n * No real database connection is needed — changes are triggered manually via `emit()`.\n *\n * @example\n * ```ts\n * const adapter = new MemoryAdapter()\n * await adapter.connect()\n *\n * adapter.onChange('orders', (event) => console.log(event))\n *\n * adapter.emit('orders', { operation: 'INSERT', newRow: { id: 1 }, oldRow: null })\n * ```\n */\nexport class MemoryAdapter implements DatabaseAdapter {\n private readonly listeners: Map<string, Set<(event: ChangeEvent) => void>> = new Map()\n private connected = false\n\n /** No-op — MemoryAdapter requires no real connection. */\n async connect(): Promise<void> {\n this.connected = true\n }\n\n /** No-op — clears all listeners on disconnect. */\n async disconnect(): Promise<void> {\n this.listeners.clear()\n this.connected = false\n }\n\n /**\n * Register a listener for changes on a specific table.\n * @returns An unsubscribe function.\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 this.listeners.get(table)!.add(callback)\n\n return () => {\n this.listeners.get(table)?.delete(callback)\n }\n }\n\n /**\n * Manually emit a change event on a table.\n * Useful in tests and examples to simulate DB changes without a real database.\n *\n * @param table - Table name to emit the event on\n * @param event - Change event data (table and timestamp are filled in automatically)\n */\n emit(table: string, event: Omit<ChangeEvent, 'table' | 'timestamp'>): void {\n const fullEvent: ChangeEvent = {\n ...event,\n table,\n timestamp: Date.now(),\n }\n\n const callbacks = this.listeners.get(table)\n if (!callbacks) return\n\n for (const cb of callbacks) {\n cb(fullEvent)\n }\n }\n\n /** Returns true if connect() has been called and disconnect() has not. */\n get isConnected(): boolean {\n return this.connected\n }\n}\n","import type { ChangeEvent, DatabaseAdapter } from '../types.js'\n\nexport interface PollingReadResult<T = unknown, TCursor = unknown> {\n events: Array<Omit<ChangeEvent<T>, 'table' | 'timestamp'> & Partial<Pick<ChangeEvent<T>, 'table' | 'timestamp'>>>\n cursor?: TCursor\n}\n\nexport interface PollingReadContext<TCursor = unknown> {\n cursor: TCursor | undefined\n table: string\n}\n\nexport interface PollingAdapterOptions<TCursor = unknown> {\n intervalMs?: number\n now?: () => number\n onError?: (error: unknown, context: { table: string }) => void\n readChanges: (\n context: PollingReadContext<TCursor>,\n ) => Promise<PollingReadResult<unknown, TCursor>>\n}\n\n/**\n * Generic polling-based adapter for databases without a native RouteFlow adapter yet.\n * It works with any backend as long as callers can periodically read a change feed.\n */\nexport class PollingAdapter<TCursor = unknown> implements DatabaseAdapter {\n private readonly listeners: Map<string, Set<(event: ChangeEvent) => void>> = new Map()\n private readonly cursors: Map<string, TCursor | undefined> = new Map()\n private readonly timers: Map<string, ReturnType<typeof setTimeout>> = new Map()\n private readonly activeTables: Set<string> = new Set()\n private readonly intervalMs: number\n private readonly now: () => number\n private readonly readChanges: PollingAdapterOptions<TCursor>['readChanges']\n private readonly onError?: PollingAdapterOptions<TCursor>['onError']\n private connected = false\n\n constructor(options: PollingAdapterOptions<TCursor>) {\n this.intervalMs = options.intervalMs ?? 1_000\n this.now = options.now ?? (() => Date.now())\n this.readChanges = options.readChanges\n this.onError = options.onError\n }\n\n async connect(): Promise<void> {\n this.connected = true\n\n for (const table of this.listeners.keys()) {\n this.ensurePolling(table)\n }\n }\n\n async disconnect(): Promise<void> {\n this.connected = false\n\n for (const timer of this.timers.values()) {\n clearTimeout(timer)\n }\n\n this.timers.clear()\n this.activeTables.clear()\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 this.ensurePolling(table)\n\n return () => {\n const callbacks = this.listeners.get(table)\n if (!callbacks) return\n\n callbacks.delete(callback)\n\n if (callbacks.size === 0) {\n this.listeners.delete(table)\n this.stopPolling(table)\n }\n }\n }\n\n private ensurePolling(table: string): void {\n if (!this.connected || this.activeTables.has(table)) return\n\n this.activeTables.add(table)\n void this.poll(table)\n }\n\n private stopPolling(table: string): void {\n const timer = this.timers.get(table)\n if (timer) clearTimeout(timer)\n\n this.timers.delete(table)\n this.activeTables.delete(table)\n this.cursors.delete(table)\n }\n\n private scheduleNext(table: string): void {\n if (!this.connected || !this.listeners.has(table)) {\n this.stopPolling(table)\n return\n }\n\n const timer = setTimeout(() => {\n void this.poll(table)\n }, this.intervalMs)\n\n this.timers.set(table, timer)\n }\n\n private async poll(table: string): Promise<void> {\n if (!this.connected || !this.listeners.has(table)) {\n this.stopPolling(table)\n return\n }\n\n try {\n const result = await this.readChanges({\n table,\n cursor: this.cursors.get(table),\n })\n\n this.cursors.set(table, result.cursor)\n\n for (const event of result.events) {\n this.emit(table, event)\n }\n } catch (error) {\n this.onError?.(error, { table })\n } finally {\n this.activeTables.delete(table)\n this.scheduleNext(table)\n }\n }\n\n private emit(\n defaultTable: string,\n event: Omit<ChangeEvent, 'table' | 'timestamp'> & Partial<Pick<ChangeEvent, 'table' | 'timestamp'>>,\n ): void {\n const fullEvent: ChangeEvent = {\n ...event,\n table: event.table ?? defaultTable,\n timestamp: event.timestamp ?? this.now(),\n }\n\n const callbacks = this.listeners.get(fullEvent.table)\n if (!callbacks) return\n\n for (const callback of callbacks) {\n callback(fullEvent)\n }\n }\n}\n"]}
@@ -0,0 +1,6 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ export { __name };
5
+ //# sourceMappingURL=chunk-PAWJFY3S.mjs.map
6
+ //# sourceMappingURL=chunk-PAWJFY3S.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-PAWJFY3S.mjs"}
@@ -0,0 +1,111 @@
1
+ import { FastifyInstance } from 'fastify';
2
+ import { H as HttpMethod, R as ReactiveOptions, A as AppOptions } from './types-tPDla8AE.mjs';
3
+ export { C as ChangeEvent, a as Context, D as DatabaseAdapter } from './types-tPDla8AE.mjs';
4
+
5
+ /**
6
+ * Registers a class method as an HTTP endpoint.
7
+ *
8
+ * @param method - HTTP verb
9
+ * @param path - Route path, may include Fastify-style params (e.g. '/users/:id')
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * @Route('GET', '/users/:id')
14
+ * async getUser(ctx: Context) { ... }
15
+ * ```
16
+ */
17
+ declare function Route(method: HttpMethod, path: string): (target: object, propertyKey: string) => void;
18
+
19
+ /**
20
+ * Marks a route handler as reactive — when the watched table(s) change,
21
+ * the handler is re-executed and the result is pushed to all subscribed clients.
22
+ *
23
+ * Must be used together with @Route.
24
+ *
25
+ * @param options - Reactive configuration (watch, filter, debounce)
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * @Reactive({ watch: 'orders', filter: (event, ctx) => event.newRow?.userId === ctx.params.userId })
30
+ * @Route('GET', '/orders/:userId/live')
31
+ * async getLiveOrders(ctx: Context) { ... }
32
+ * ```
33
+ */
34
+ declare function Reactive(options: ReactiveOptions): (target: object, propertyKey: string) => void;
35
+
36
+ /**
37
+ * Base error class for all RouteFlow errors.
38
+ * Always use this instead of plain `Error` throughout the framework.
39
+ */
40
+ declare class ReactiveApiError extends Error {
41
+ /** Machine-readable error code (e.g. 'ADAPTER_NOT_CONNECTED', 'INVALID_ROUTE') */
42
+ readonly code: string;
43
+ constructor(code: string, message: string);
44
+ }
45
+
46
+ type DatabaseCategory = 'rdbms' | 'nosql' | 'time-series' | 'search-engine' | 'cloud-data-warehouse' | 'in-memory' | 'newsql';
47
+ type DatabaseSupportMode = 'native-adapter' | 'polling-adapter' | 'external-cdc-bridge';
48
+ type DatabaseSupportTier = 'official' | 'experimental';
49
+ type DatabaseKey = 'postgresql' | 'mysql' | 'mariadb' | 'oracle-db' | 'ms-sql-server' | 'sqlite' | 'mongodb' | 'redis' | 'cassandra' | 'dynamodb' | 'neo4j' | 'elasticsearch' | 'hbase' | 'couchdb' | 'influxdb' | 'timescaledb' | 'prometheus' | 'opensearch' | 'solr' | 'snowflake' | 'bigquery' | 'redshift' | 'azure-synapse' | 'memcached' | 'voltdb' | 'cockroachdb' | 'tidb' | 'spanner';
50
+ interface DatabaseSupportDescriptor {
51
+ key: DatabaseKey;
52
+ name: string;
53
+ aliases: string[];
54
+ categories: DatabaseCategory[];
55
+ supportedModes: DatabaseSupportMode[];
56
+ tier: DatabaseSupportTier;
57
+ }
58
+ declare const SUPPORTED_DATABASES: readonly DatabaseSupportDescriptor[];
59
+ declare function getDatabaseSupport(name: string): DatabaseSupportDescriptor | undefined;
60
+ declare function listSupportedDatabases(category?: DatabaseCategory): readonly DatabaseSupportDescriptor[];
61
+ declare function listOfficialDatabases(category?: DatabaseCategory): readonly DatabaseSupportDescriptor[];
62
+
63
+ /**
64
+ * Main application class. Use `createApp()` to instantiate.
65
+ */
66
+ declare class ReactiveApp {
67
+ private readonly fastify;
68
+ private readonly engine;
69
+ private transport;
70
+ private readonly options;
71
+ /** Collected route patterns for reactive endpoints */
72
+ private readonly reactivePatterns;
73
+ constructor(options: AppOptions);
74
+ /**
75
+ * Register a controller class. Scans its methods for @Route and @Reactive
76
+ * decorators and registers HTTP routes and reactive endpoints accordingly.
77
+ *
78
+ * @param ControllerClass - A class constructor whose methods may be decorated
79
+ * with @Route and/or @Reactive.
80
+ */
81
+ register(ControllerClass: new () => object): this;
82
+ /**
83
+ * Access the underlying Fastify instance for supplemental routes such as
84
+ * health checks, static assets, or demo pages.
85
+ */
86
+ getFastify(): FastifyInstance;
87
+ /**
88
+ * Start the HTTP server.
89
+ * Connects the database adapter before accepting connections.
90
+ *
91
+ * @param port - Override the port set in AppOptions
92
+ */
93
+ listen(port?: number): Promise<void>;
94
+ /**
95
+ * Gracefully shut down the server and disconnect from the database.
96
+ */
97
+ close(): Promise<void>;
98
+ }
99
+ /**
100
+ * Create a new RouteFlow application.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * const app = createApp({ adapter: new MemoryAdapter(), port: 3000 })
105
+ * app.register(MyController)
106
+ * await app.listen()
107
+ * ```
108
+ */
109
+ declare function createApp(options: AppOptions): ReactiveApp;
110
+
111
+ export { AppOptions, type DatabaseCategory, type DatabaseKey, type DatabaseSupportDescriptor, type DatabaseSupportMode, type DatabaseSupportTier, HttpMethod, Reactive, ReactiveApiError, ReactiveApp, ReactiveOptions, Route, SUPPORTED_DATABASES, createApp, getDatabaseSupport, listOfficialDatabases, listSupportedDatabases };
@@ -0,0 +1,111 @@
1
+ import { FastifyInstance } from 'fastify';
2
+ import { H as HttpMethod, R as ReactiveOptions, A as AppOptions } from './types-tPDla8AE.js';
3
+ export { C as ChangeEvent, a as Context, D as DatabaseAdapter } from './types-tPDla8AE.js';
4
+
5
+ /**
6
+ * Registers a class method as an HTTP endpoint.
7
+ *
8
+ * @param method - HTTP verb
9
+ * @param path - Route path, may include Fastify-style params (e.g. '/users/:id')
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * @Route('GET', '/users/:id')
14
+ * async getUser(ctx: Context) { ... }
15
+ * ```
16
+ */
17
+ declare function Route(method: HttpMethod, path: string): (target: object, propertyKey: string) => void;
18
+
19
+ /**
20
+ * Marks a route handler as reactive — when the watched table(s) change,
21
+ * the handler is re-executed and the result is pushed to all subscribed clients.
22
+ *
23
+ * Must be used together with @Route.
24
+ *
25
+ * @param options - Reactive configuration (watch, filter, debounce)
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * @Reactive({ watch: 'orders', filter: (event, ctx) => event.newRow?.userId === ctx.params.userId })
30
+ * @Route('GET', '/orders/:userId/live')
31
+ * async getLiveOrders(ctx: Context) { ... }
32
+ * ```
33
+ */
34
+ declare function Reactive(options: ReactiveOptions): (target: object, propertyKey: string) => void;
35
+
36
+ /**
37
+ * Base error class for all RouteFlow errors.
38
+ * Always use this instead of plain `Error` throughout the framework.
39
+ */
40
+ declare class ReactiveApiError extends Error {
41
+ /** Machine-readable error code (e.g. 'ADAPTER_NOT_CONNECTED', 'INVALID_ROUTE') */
42
+ readonly code: string;
43
+ constructor(code: string, message: string);
44
+ }
45
+
46
+ type DatabaseCategory = 'rdbms' | 'nosql' | 'time-series' | 'search-engine' | 'cloud-data-warehouse' | 'in-memory' | 'newsql';
47
+ type DatabaseSupportMode = 'native-adapter' | 'polling-adapter' | 'external-cdc-bridge';
48
+ type DatabaseSupportTier = 'official' | 'experimental';
49
+ type DatabaseKey = 'postgresql' | 'mysql' | 'mariadb' | 'oracle-db' | 'ms-sql-server' | 'sqlite' | 'mongodb' | 'redis' | 'cassandra' | 'dynamodb' | 'neo4j' | 'elasticsearch' | 'hbase' | 'couchdb' | 'influxdb' | 'timescaledb' | 'prometheus' | 'opensearch' | 'solr' | 'snowflake' | 'bigquery' | 'redshift' | 'azure-synapse' | 'memcached' | 'voltdb' | 'cockroachdb' | 'tidb' | 'spanner';
50
+ interface DatabaseSupportDescriptor {
51
+ key: DatabaseKey;
52
+ name: string;
53
+ aliases: string[];
54
+ categories: DatabaseCategory[];
55
+ supportedModes: DatabaseSupportMode[];
56
+ tier: DatabaseSupportTier;
57
+ }
58
+ declare const SUPPORTED_DATABASES: readonly DatabaseSupportDescriptor[];
59
+ declare function getDatabaseSupport(name: string): DatabaseSupportDescriptor | undefined;
60
+ declare function listSupportedDatabases(category?: DatabaseCategory): readonly DatabaseSupportDescriptor[];
61
+ declare function listOfficialDatabases(category?: DatabaseCategory): readonly DatabaseSupportDescriptor[];
62
+
63
+ /**
64
+ * Main application class. Use `createApp()` to instantiate.
65
+ */
66
+ declare class ReactiveApp {
67
+ private readonly fastify;
68
+ private readonly engine;
69
+ private transport;
70
+ private readonly options;
71
+ /** Collected route patterns for reactive endpoints */
72
+ private readonly reactivePatterns;
73
+ constructor(options: AppOptions);
74
+ /**
75
+ * Register a controller class. Scans its methods for @Route and @Reactive
76
+ * decorators and registers HTTP routes and reactive endpoints accordingly.
77
+ *
78
+ * @param ControllerClass - A class constructor whose methods may be decorated
79
+ * with @Route and/or @Reactive.
80
+ */
81
+ register(ControllerClass: new () => object): this;
82
+ /**
83
+ * Access the underlying Fastify instance for supplemental routes such as
84
+ * health checks, static assets, or demo pages.
85
+ */
86
+ getFastify(): FastifyInstance;
87
+ /**
88
+ * Start the HTTP server.
89
+ * Connects the database adapter before accepting connections.
90
+ *
91
+ * @param port - Override the port set in AppOptions
92
+ */
93
+ listen(port?: number): Promise<void>;
94
+ /**
95
+ * Gracefully shut down the server and disconnect from the database.
96
+ */
97
+ close(): Promise<void>;
98
+ }
99
+ /**
100
+ * Create a new RouteFlow application.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * const app = createApp({ adapter: new MemoryAdapter(), port: 3000 })
105
+ * app.register(MyController)
106
+ * await app.listen()
107
+ * ```
108
+ */
109
+ declare function createApp(options: AppOptions): ReactiveApp;
110
+
111
+ export { AppOptions, type DatabaseCategory, type DatabaseKey, type DatabaseSupportDescriptor, type DatabaseSupportMode, type DatabaseSupportTier, HttpMethod, Reactive, ReactiveApiError, ReactiveApp, ReactiveOptions, Route, SUPPORTED_DATABASES, createApp, getDatabaseSupport, listOfficialDatabases, listSupportedDatabases };