qhttpx 1.8.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 (197) hide show
  1. package/.eslintrc.json +22 -0
  2. package/.github/workflows/ci.yml +32 -0
  3. package/.github/workflows/npm-publish.yml +37 -0
  4. package/.github/workflows/release.yml +21 -0
  5. package/.prettierrc +7 -0
  6. package/CHANGELOG.md +145 -0
  7. package/LICENSE +21 -0
  8. package/README.md +343 -0
  9. package/dist/package.json +61 -0
  10. package/dist/src/benchmarks/compare-frameworks.js +119 -0
  11. package/dist/src/benchmarks/quantam-users.js +56 -0
  12. package/dist/src/benchmarks/simple-json.js +58 -0
  13. package/dist/src/benchmarks/ultra-mode.js +122 -0
  14. package/dist/src/cli/index.js +200 -0
  15. package/dist/src/client/index.js +72 -0
  16. package/dist/src/core/batch.js +97 -0
  17. package/dist/src/core/body-parser.js +121 -0
  18. package/dist/src/core/buffer-pool.js +70 -0
  19. package/dist/src/core/config.js +50 -0
  20. package/dist/src/core/fusion.js +183 -0
  21. package/dist/src/core/logger.js +49 -0
  22. package/dist/src/core/metrics.js +111 -0
  23. package/dist/src/core/resources.js +25 -0
  24. package/dist/src/core/scheduler.js +85 -0
  25. package/dist/src/core/scope.js +68 -0
  26. package/dist/src/core/serializer.js +44 -0
  27. package/dist/src/core/server.js +905 -0
  28. package/dist/src/core/stream.js +71 -0
  29. package/dist/src/core/tasks.js +87 -0
  30. package/dist/src/core/types.js +19 -0
  31. package/dist/src/core/websocket.js +86 -0
  32. package/dist/src/core/worker-queue.js +73 -0
  33. package/dist/src/database/adapters/memory.js +90 -0
  34. package/dist/src/database/adapters/mongo.js +141 -0
  35. package/dist/src/database/adapters/postgres.js +111 -0
  36. package/dist/src/database/adapters/sqlite.js +42 -0
  37. package/dist/src/database/coalescer.js +134 -0
  38. package/dist/src/database/manager.js +87 -0
  39. package/dist/src/database/types.js +2 -0
  40. package/dist/src/index.js +61 -0
  41. package/dist/src/middleware/compression.js +133 -0
  42. package/dist/src/middleware/cors.js +66 -0
  43. package/dist/src/middleware/presets.js +33 -0
  44. package/dist/src/middleware/rate-limit.js +77 -0
  45. package/dist/src/middleware/security.js +69 -0
  46. package/dist/src/middleware/static.js +191 -0
  47. package/dist/src/openapi/generator.js +149 -0
  48. package/dist/src/router/radix-router.js +89 -0
  49. package/dist/src/router/radix-tree.js +81 -0
  50. package/dist/src/router/router.js +146 -0
  51. package/dist/src/testing/index.js +84 -0
  52. package/dist/src/utils/cookies.js +59 -0
  53. package/dist/src/utils/logger.js +45 -0
  54. package/dist/src/utils/signals.js +31 -0
  55. package/dist/src/utils/sse.js +32 -0
  56. package/dist/src/validation/index.js +19 -0
  57. package/dist/src/validation/simple.js +102 -0
  58. package/dist/src/validation/types.js +12 -0
  59. package/dist/src/validation/zod.js +18 -0
  60. package/dist/src/views/index.js +17 -0
  61. package/dist/src/views/types.js +2 -0
  62. package/dist/tests/adapters.test.js +106 -0
  63. package/dist/tests/batch.test.js +117 -0
  64. package/dist/tests/body-parser.test.js +52 -0
  65. package/dist/tests/compression-sse.test.js +87 -0
  66. package/dist/tests/cookies.test.js +63 -0
  67. package/dist/tests/cors.test.js +55 -0
  68. package/dist/tests/database.test.js +80 -0
  69. package/dist/tests/dx.test.js +64 -0
  70. package/dist/tests/ecosystem.test.js +133 -0
  71. package/dist/tests/features.test.js +47 -0
  72. package/dist/tests/fusion.test.js +92 -0
  73. package/dist/tests/http-basic.test.js +124 -0
  74. package/dist/tests/logger.test.js +33 -0
  75. package/dist/tests/middleware.test.js +109 -0
  76. package/dist/tests/observability.test.js +59 -0
  77. package/dist/tests/openapi.test.js +64 -0
  78. package/dist/tests/plugin.test.js +65 -0
  79. package/dist/tests/plugins.test.js +71 -0
  80. package/dist/tests/rate-limit.test.js +77 -0
  81. package/dist/tests/resources.test.js +44 -0
  82. package/dist/tests/scheduler.test.js +46 -0
  83. package/dist/tests/schema-routes.test.js +77 -0
  84. package/dist/tests/security.test.js +83 -0
  85. package/dist/tests/server-db.test.js +72 -0
  86. package/dist/tests/smoke.test.js +10 -0
  87. package/dist/tests/sqlite-fusion.test.js +92 -0
  88. package/dist/tests/static.test.js +102 -0
  89. package/dist/tests/stream.test.js +44 -0
  90. package/dist/tests/task-metrics.test.js +53 -0
  91. package/dist/tests/tasks.test.js +62 -0
  92. package/dist/tests/testing.test.js +47 -0
  93. package/dist/tests/validation.test.js +107 -0
  94. package/dist/tests/websocket.test.js +146 -0
  95. package/dist/vitest.config.js +9 -0
  96. package/docs/AEGIS.md +76 -0
  97. package/docs/BENCHMARKS.md +36 -0
  98. package/docs/CAPABILITIES.md +70 -0
  99. package/docs/CLI.md +43 -0
  100. package/docs/DATABASE.md +142 -0
  101. package/docs/ECOSYSTEM.md +146 -0
  102. package/docs/NEXT_STEPS.md +99 -0
  103. package/docs/OPENAPI.md +99 -0
  104. package/docs/PLUGINS.md +59 -0
  105. package/docs/REAL_WORLD_EXAMPLES.md +109 -0
  106. package/docs/ROADMAP.md +366 -0
  107. package/docs/VALIDATION.md +136 -0
  108. package/eslint.config.cjs +26 -0
  109. package/examples/api-server.ts +254 -0
  110. package/package.json +61 -0
  111. package/src/benchmarks/compare-frameworks.ts +149 -0
  112. package/src/benchmarks/quantam-users.ts +70 -0
  113. package/src/benchmarks/simple-json.ts +71 -0
  114. package/src/benchmarks/ultra-mode.ts +159 -0
  115. package/src/cli/index.ts +214 -0
  116. package/src/client/index.ts +93 -0
  117. package/src/core/batch.ts +110 -0
  118. package/src/core/body-parser.ts +151 -0
  119. package/src/core/buffer-pool.ts +96 -0
  120. package/src/core/config.ts +60 -0
  121. package/src/core/fusion.ts +210 -0
  122. package/src/core/logger.ts +70 -0
  123. package/src/core/metrics.ts +166 -0
  124. package/src/core/resources.ts +38 -0
  125. package/src/core/scheduler.ts +126 -0
  126. package/src/core/scope.ts +87 -0
  127. package/src/core/serializer.ts +41 -0
  128. package/src/core/server.ts +1113 -0
  129. package/src/core/stream.ts +111 -0
  130. package/src/core/tasks.ts +138 -0
  131. package/src/core/types.ts +178 -0
  132. package/src/core/websocket.ts +112 -0
  133. package/src/core/worker-queue.ts +90 -0
  134. package/src/database/adapters/memory.ts +99 -0
  135. package/src/database/adapters/mongo.ts +116 -0
  136. package/src/database/adapters/postgres.ts +86 -0
  137. package/src/database/adapters/sqlite.ts +44 -0
  138. package/src/database/coalescer.ts +153 -0
  139. package/src/database/manager.ts +97 -0
  140. package/src/database/types.ts +24 -0
  141. package/src/index.ts +42 -0
  142. package/src/middleware/compression.ts +147 -0
  143. package/src/middleware/cors.ts +98 -0
  144. package/src/middleware/presets.ts +50 -0
  145. package/src/middleware/rate-limit.ts +106 -0
  146. package/src/middleware/security.ts +109 -0
  147. package/src/middleware/static.ts +216 -0
  148. package/src/openapi/generator.ts +167 -0
  149. package/src/router/radix-router.ts +119 -0
  150. package/src/router/radix-tree.ts +106 -0
  151. package/src/router/router.ts +190 -0
  152. package/src/testing/index.ts +104 -0
  153. package/src/utils/cookies.ts +67 -0
  154. package/src/utils/logger.ts +59 -0
  155. package/src/utils/signals.ts +45 -0
  156. package/src/utils/sse.ts +41 -0
  157. package/src/validation/index.ts +3 -0
  158. package/src/validation/simple.ts +93 -0
  159. package/src/validation/types.ts +38 -0
  160. package/src/validation/zod.ts +14 -0
  161. package/src/views/index.ts +1 -0
  162. package/src/views/types.ts +4 -0
  163. package/tests/adapters.test.ts +120 -0
  164. package/tests/batch.test.ts +139 -0
  165. package/tests/body-parser.test.ts +83 -0
  166. package/tests/compression-sse.test.ts +98 -0
  167. package/tests/cookies.test.ts +74 -0
  168. package/tests/cors.test.ts +79 -0
  169. package/tests/database.test.ts +90 -0
  170. package/tests/dx.test.ts +78 -0
  171. package/tests/ecosystem.test.ts +156 -0
  172. package/tests/features.test.ts +51 -0
  173. package/tests/fusion.test.ts +121 -0
  174. package/tests/http-basic.test.ts +161 -0
  175. package/tests/logger.test.ts +48 -0
  176. package/tests/middleware.test.ts +137 -0
  177. package/tests/observability.test.ts +91 -0
  178. package/tests/openapi.test.ts +74 -0
  179. package/tests/plugin.test.ts +85 -0
  180. package/tests/plugins.test.ts +93 -0
  181. package/tests/rate-limit.test.ts +97 -0
  182. package/tests/resources.test.ts +64 -0
  183. package/tests/scheduler.test.ts +71 -0
  184. package/tests/schema-routes.test.ts +89 -0
  185. package/tests/security.test.ts +128 -0
  186. package/tests/server-db.test.ts +72 -0
  187. package/tests/smoke.test.ts +9 -0
  188. package/tests/sqlite-fusion.test.ts +106 -0
  189. package/tests/static.test.ts +111 -0
  190. package/tests/stream.test.ts +58 -0
  191. package/tests/task-metrics.test.ts +78 -0
  192. package/tests/tasks.test.ts +90 -0
  193. package/tests/testing.test.ts +53 -0
  194. package/tests/validation.test.ts +126 -0
  195. package/tests/websocket.test.ts +132 -0
  196. package/tsconfig.json +16 -0
  197. package/vitest.config.ts +9 -0
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSseStream = createSseStream;
4
+ exports.sendStream = sendStream;
5
+ function createSseStream(ctx, options = {}) {
6
+ const res = ctx.res;
7
+ if (!res.headersSent) {
8
+ res.statusCode = 200;
9
+ res.setHeader('content-type', 'text/event-stream; charset=utf-8');
10
+ res.setHeader('cache-control', 'no-cache');
11
+ res.setHeader('connection', 'keep-alive');
12
+ }
13
+ const anyRes = res;
14
+ if (anyRes.flushHeaders) {
15
+ anyRes.flushHeaders();
16
+ }
17
+ if (typeof options.retryMs === 'number') {
18
+ res.write(`retry: ${options.retryMs}\n\n`);
19
+ }
20
+ const send = (data, event) => {
21
+ if (res.writableEnded) {
22
+ return;
23
+ }
24
+ const payload = typeof data === 'string' ? data : JSON.stringify(data);
25
+ let chunk = '';
26
+ if (event) {
27
+ chunk += `event: ${event}\n`;
28
+ }
29
+ chunk += `data: ${payload}\n\n`;
30
+ res.write(chunk);
31
+ if (anyRes.flush) {
32
+ anyRes.flush();
33
+ }
34
+ };
35
+ const close = () => {
36
+ if (!res.writableEnded) {
37
+ res.end();
38
+ }
39
+ };
40
+ return { send, close };
41
+ }
42
+ function sendStream(ctx, stream, options = {}) {
43
+ const res = ctx.res;
44
+ if (!res.headersSent) {
45
+ if (options.status !== undefined) {
46
+ res.statusCode = options.status;
47
+ }
48
+ if (options.contentType) {
49
+ res.setHeader('content-type', options.contentType);
50
+ }
51
+ }
52
+ return new Promise((resolve, reject) => {
53
+ stream.on('error', (err) => {
54
+ if (!res.headersSent) {
55
+ res.statusCode = 500;
56
+ res.setHeader('content-type', 'text/plain; charset=utf-8');
57
+ }
58
+ if (!res.writableEnded) {
59
+ res.end('Internal Server Error');
60
+ }
61
+ reject(err);
62
+ });
63
+ stream.on('end', () => {
64
+ if (!res.writableEnded) {
65
+ res.end();
66
+ }
67
+ resolve();
68
+ });
69
+ stream.pipe(res, { end: false });
70
+ });
71
+ }
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TaskEngine = void 0;
4
+ class TaskEngine {
5
+ constructor(scheduler) {
6
+ this.tasks = new Map();
7
+ this.registeredTasksCount = 0;
8
+ this.totalEnqueued = 0;
9
+ this.totalCompleted = 0;
10
+ this.totalFailed = 0;
11
+ this.totalOverloaded = 0;
12
+ this.totalRetried = 0;
13
+ this.scheduler = scheduler;
14
+ }
15
+ register(name, handler, options = {}) {
16
+ if (!this.tasks.has(name)) {
17
+ this.registeredTasksCount += 1;
18
+ }
19
+ this.tasks.set(name, {
20
+ name,
21
+ handler,
22
+ options,
23
+ });
24
+ }
25
+ async enqueue(name, payload) {
26
+ const def = this.tasks.get(name);
27
+ if (!def) {
28
+ throw new Error(`Task "${name}" is not registered`);
29
+ }
30
+ this.totalEnqueued += 1;
31
+ await this.executeWithRetry(def, payload);
32
+ }
33
+ getMetrics() {
34
+ return {
35
+ registeredTasks: this.registeredTasksCount,
36
+ totalEnqueued: this.totalEnqueued,
37
+ totalCompleted: this.totalCompleted,
38
+ totalFailed: this.totalFailed,
39
+ totalOverloaded: this.totalOverloaded,
40
+ totalRetried: this.totalRetried,
41
+ };
42
+ }
43
+ async executeWithRetry(def, payload) {
44
+ const maxRetries = def.options.maxRetries ?? 0;
45
+ const backoffMs = def.options.backoffMs ?? 0;
46
+ let attempt = 0;
47
+ for (;;) {
48
+ let overloaded = false;
49
+ let error;
50
+ await this.scheduler.run(async () => {
51
+ try {
52
+ await def.handler(payload);
53
+ }
54
+ catch (err) {
55
+ error = err;
56
+ }
57
+ }, {
58
+ onOverloaded: () => {
59
+ overloaded = true;
60
+ },
61
+ });
62
+ if (!overloaded && !error) {
63
+ this.totalCompleted += 1;
64
+ return;
65
+ }
66
+ if (attempt >= maxRetries) {
67
+ if (error) {
68
+ this.totalFailed += 1;
69
+ throw error;
70
+ }
71
+ if (overloaded) {
72
+ this.totalOverloaded += 1;
73
+ throw new Error(`Task "${def.name}" overloaded`);
74
+ }
75
+ return;
76
+ }
77
+ attempt += 1;
78
+ this.totalRetried += 1;
79
+ if (backoffMs > 0) {
80
+ await new Promise((resolve) => {
81
+ setTimeout(resolve, backoffMs);
82
+ });
83
+ }
84
+ }
85
+ }
86
+ }
87
+ exports.TaskEngine = TaskEngine;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpError = exports.RoutePriority = void 0;
4
+ var RoutePriority;
5
+ (function (RoutePriority) {
6
+ RoutePriority["CRITICAL"] = "critical";
7
+ RoutePriority["STANDARD"] = "standard";
8
+ RoutePriority["BEST_EFFORT"] = "best-effort";
9
+ })(RoutePriority || (exports.RoutePriority = RoutePriority = {}));
10
+ class HttpError extends Error {
11
+ constructor(status, message, options = {}) {
12
+ super(message ?? 'HTTP Error');
13
+ this.status = status;
14
+ this.code = options.code;
15
+ this.details = options.details;
16
+ Object.setPrototypeOf(this, new.target.prototype);
17
+ }
18
+ }
19
+ exports.HttpError = HttpError;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebSocketManager = void 0;
4
+ const url_1 = require("url");
5
+ const ws_1 = require("ws");
6
+ class WebSocketManager {
7
+ constructor(requestIdGenerator) {
8
+ this.requestIdGenerator = requestIdGenerator;
9
+ this.handlers = [];
10
+ this.rooms = new Map();
11
+ this.wss = new ws_1.WebSocketServer({ noServer: true });
12
+ }
13
+ register(path, handler) {
14
+ this.handlers.push({ path, handler });
15
+ }
16
+ async handleUpgrade(req, socket, head) {
17
+ const rawUrl = req.url || '/';
18
+ const host = req.headers.host || 'localhost';
19
+ const urlObj = new url_1.URL(rawUrl, `http://${host}`);
20
+ const path = urlObj.pathname;
21
+ const handlerEntry = this.handlers.find((entry) => entry.path === path);
22
+ if (!handlerEntry) {
23
+ socket.destroy();
24
+ return;
25
+ }
26
+ this.wss.handleUpgrade(req, socket, head, (ws) => {
27
+ const qws = ws;
28
+ qws.id = this.requestIdGenerator();
29
+ qws.join = (room) => this.join(room, qws);
30
+ qws.leave = (room) => this.leave(room, qws);
31
+ qws.on('close', () => {
32
+ this.leaveAll(qws);
33
+ });
34
+ handlerEntry.handler(qws, req);
35
+ });
36
+ }
37
+ join(room, ws) {
38
+ if (!this.rooms.has(room)) {
39
+ this.rooms.set(room, new Set());
40
+ }
41
+ this.rooms.get(room).add(ws);
42
+ }
43
+ leave(room, ws) {
44
+ const set = this.rooms.get(room);
45
+ if (set) {
46
+ set.delete(ws);
47
+ if (set.size === 0) {
48
+ this.rooms.delete(room);
49
+ }
50
+ }
51
+ }
52
+ leaveAll(ws) {
53
+ for (const [room, set] of this.rooms) {
54
+ if (set.has(ws)) {
55
+ set.delete(ws);
56
+ if (set.size === 0) {
57
+ this.rooms.delete(room);
58
+ }
59
+ }
60
+ }
61
+ }
62
+ to(room) {
63
+ return {
64
+ emit: (data) => {
65
+ const set = this.rooms.get(room);
66
+ if (set) {
67
+ const payload = typeof data === 'string' ? data : JSON.stringify(data);
68
+ for (const client of set) {
69
+ if (client.readyState === ws_1.WebSocket.OPEN) {
70
+ client.send(payload);
71
+ }
72
+ }
73
+ }
74
+ },
75
+ };
76
+ }
77
+ broadcast(data) {
78
+ const payload = typeof data === 'string' ? data : JSON.stringify(data);
79
+ for (const client of this.wss.clients) {
80
+ if (client.readyState === ws_1.WebSocket.OPEN) {
81
+ client.send(payload);
82
+ }
83
+ }
84
+ }
85
+ }
86
+ exports.WebSocketManager = WebSocketManager;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ /**
3
+ * Lock-free (or lock-minimal) work queue for per-worker task distribution.
4
+ * Uses a simple ring buffer for high throughput.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.WorkerQueue = void 0;
8
+ class WorkerQueue {
9
+ constructor(capacity = 1024) {
10
+ this.writeIndex = 0;
11
+ this.readIndex = 0;
12
+ this.size = 0;
13
+ if (capacity <= 0 || !Number.isInteger(capacity)) {
14
+ throw new Error('Capacity must be a positive integer');
15
+ }
16
+ // Ensure capacity is a power of 2 for efficient modulo with bitmask
17
+ this.capacity = Math.pow(2, Math.ceil(Math.log2(capacity)));
18
+ this.buffer = new Array(this.capacity);
19
+ }
20
+ /**
21
+ * Enqueue a work item. Returns true if successful, false if queue is full.
22
+ */
23
+ enqueue(item) {
24
+ if (this.size >= this.capacity) {
25
+ return false;
26
+ }
27
+ this.buffer[this.writeIndex] = item;
28
+ this.writeIndex = (this.writeIndex + 1) & (this.capacity - 1);
29
+ this.size += 1;
30
+ return true;
31
+ }
32
+ /**
33
+ * Dequeue a work item. Returns undefined if queue is empty.
34
+ */
35
+ dequeue() {
36
+ if (this.size <= 0) {
37
+ return undefined;
38
+ }
39
+ const item = this.buffer[this.readIndex];
40
+ this.buffer[this.readIndex] = undefined;
41
+ this.readIndex = (this.readIndex + 1) & (this.capacity - 1);
42
+ this.size -= 1;
43
+ return item;
44
+ }
45
+ /**
46
+ * Peek at the next item without removing it.
47
+ */
48
+ peek() {
49
+ if (this.size <= 0) {
50
+ return undefined;
51
+ }
52
+ return this.buffer[this.readIndex];
53
+ }
54
+ /**
55
+ * Check if the queue is empty.
56
+ */
57
+ isEmpty() {
58
+ return this.size === 0;
59
+ }
60
+ /**
61
+ * Get the current size of the queue.
62
+ */
63
+ getSize() {
64
+ return this.size;
65
+ }
66
+ /**
67
+ * Get the capacity of the queue.
68
+ */
69
+ getCapacity() {
70
+ return this.capacity;
71
+ }
72
+ }
73
+ exports.WorkerQueue = WorkerQueue;
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryAdapter = void 0;
4
+ class MemoryAdapter {
5
+ constructor(config) {
6
+ this.config = config;
7
+ this.connected = false;
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ this.collections = new Map();
10
+ }
11
+ async connect() {
12
+ // Simulate connection delay
13
+ await new Promise(resolve => setTimeout(resolve, 100));
14
+ this.connected = true;
15
+ }
16
+ async disconnect() {
17
+ this.connected = false;
18
+ this.collections.clear();
19
+ }
20
+ isConnected() {
21
+ return this.connected;
22
+ }
23
+ /**
24
+ * Execute a query against the in-memory database
25
+ * @param query Object specifying collection, action, and parameters
26
+ * @example
27
+ * // Find
28
+ * query({ collection: 'users', action: 'find', filter: { id: 1 } })
29
+ * // Insert
30
+ * query({ collection: 'users', action: 'insert', data: { name: 'Alice' } })
31
+ */
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ async query(query) {
34
+ if (!this.connected) {
35
+ throw new Error('Database not connected');
36
+ }
37
+ const { collection, action, data, filter } = query;
38
+ if (!collection || !action) {
39
+ throw new Error('Query must specify collection and action');
40
+ }
41
+ if (!this.collections.has(collection)) {
42
+ this.collections.set(collection, []);
43
+ }
44
+ const store = this.collections.get(collection);
45
+ switch (action) {
46
+ case 'find':
47
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
+ if (!filter)
49
+ return store;
50
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
+ return store.filter(item => this.matches(item, filter));
52
+ case 'findOne':
53
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ if (!filter)
55
+ return store[0];
56
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
+ return store.find(item => this.matches(item, filter));
58
+ case 'insert':
59
+ const newItem = { id: Date.now(), ...data };
60
+ store.push(newItem);
61
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
+ return newItem;
63
+ case 'update':
64
+ let updatedCount = 0;
65
+ store.forEach((item, index) => {
66
+ if (this.matches(item, filter)) {
67
+ store[index] = { ...item, ...data };
68
+ updatedCount++;
69
+ }
70
+ });
71
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
+ return updatedCount;
73
+ case 'delete':
74
+ const initialLength = store.length;
75
+ const newStore = store.filter(item => !this.matches(item, filter));
76
+ this.collections.set(collection, newStore);
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+ return (initialLength - newStore.length);
79
+ default:
80
+ throw new Error(`Unknown action: ${action}`);
81
+ }
82
+ }
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ matches(item, filter) {
85
+ if (!filter)
86
+ return true;
87
+ return Object.entries(filter).every(([key, value]) => item[key] === value);
88
+ }
89
+ }
90
+ exports.MemoryAdapter = MemoryAdapter;
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.MongoAdapter = void 0;
37
+ class MongoAdapter {
38
+ constructor(config) {
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ this.client = null;
41
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
+ this.db = null;
43
+ this.config = config;
44
+ }
45
+ async connect() {
46
+ if (this.client)
47
+ return;
48
+ try {
49
+ const mongodb = await Promise.resolve().then(() => __importStar(require('mongodb')));
50
+ const { MongoClient } = mongodb.default || mongodb;
51
+ const url = this.config.url || `mongodb://${this.config.host || 'localhost'}:${this.config.port || 27017}`;
52
+ this.client = new MongoClient(url, {
53
+ auth: this.config.username ? {
54
+ username: this.config.username,
55
+ password: this.config.password
56
+ } : undefined,
57
+ ...this.config.options
58
+ });
59
+ await this.client.connect();
60
+ this.db = this.client.db(this.config.database);
61
+ }
62
+ catch (err) {
63
+ if (err.message.includes('Cannot find module')) {
64
+ throw new Error('MongoDB driver not found. Please run: npm install mongodb');
65
+ }
66
+ throw err;
67
+ }
68
+ }
69
+ async disconnect() {
70
+ if (this.client) {
71
+ await this.client.close();
72
+ this.client = null;
73
+ this.db = null;
74
+ }
75
+ }
76
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
+ async query(query, params) {
78
+ if (!this.db) {
79
+ throw new Error('Database not connected');
80
+ }
81
+ // MongoDB "query" is usually a collection.find() or similar.
82
+ // We need a convention here.
83
+ // Convention: query is a JSON object with { collection: 'name', action: 'find', filter: {}, ... }
84
+ // OR: query is just the collection name, and params[0] is the operation?
85
+ // Let's go with a structured object for maximum flexibility if passed as object.
86
+ // If query is string, maybe it's just collection name?
87
+ // Let's support a simple object-based API:
88
+ // db.query({ collection: 'users', action: 'find', filter: { id: 1 } })
89
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
90
+ let op = query;
91
+ if (typeof query === 'string') {
92
+ // Fallback or simple format?
93
+ // Maybe "users.find" ?
94
+ const parts = query.split('.');
95
+ if (parts.length >= 2) {
96
+ op = {
97
+ collection: parts[0],
98
+ action: parts[1],
99
+ filter: params?.[0]
100
+ };
101
+ }
102
+ else {
103
+ throw new Error('Invalid MongoDB query format. Use object or "collection.action" string.');
104
+ }
105
+ }
106
+ const collection = this.db.collection(op.collection);
107
+ switch (op.action) {
108
+ case 'find':
109
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
+ const cursor = collection.find(op.filter || {});
111
+ if (op.limit)
112
+ cursor.limit(op.limit);
113
+ if (op.skip)
114
+ cursor.skip(op.skip);
115
+ return (await cursor.toArray());
116
+ case 'findOne':
117
+ return (await collection.findOne(op.filter || {}));
118
+ case 'insertOne':
119
+ return (await collection.insertOne(op.doc || params?.[0]));
120
+ case 'insertMany':
121
+ return (await collection.insertMany(op.docs || params?.[0]));
122
+ case 'updateOne':
123
+ return (await collection.updateOne(op.filter, op.update || params?.[1]));
124
+ case 'updateMany':
125
+ return (await collection.updateMany(op.filter, op.update || params?.[1]));
126
+ case 'deleteOne':
127
+ return (await collection.deleteOne(op.filter));
128
+ case 'deleteMany':
129
+ return (await collection.deleteMany(op.filter));
130
+ case 'aggregate':
131
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
132
+ return (await collection.aggregate((op.pipeline || params)).toArray());
133
+ default:
134
+ throw new Error(`Unsupported MongoDB action: ${op.action}`);
135
+ }
136
+ }
137
+ isConnected() {
138
+ return this.client !== null;
139
+ }
140
+ }
141
+ exports.MongoAdapter = MongoAdapter;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PostgresAdapter = void 0;
37
+ class PostgresAdapter {
38
+ constructor(config) {
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ this.pool = null;
41
+ this.config = config;
42
+ }
43
+ async connect() {
44
+ if (this.pool)
45
+ return;
46
+ try {
47
+ // Dynamic import
48
+ const pg = await Promise.resolve().then(() => __importStar(require('pg')));
49
+ const { Pool } = pg.default || pg;
50
+ this.pool = new Pool({
51
+ host: this.config.host,
52
+ port: this.config.port || 5432,
53
+ user: this.config.username,
54
+ password: this.config.password,
55
+ database: this.config.database,
56
+ // Postgres-specific options can be passed via config.options
57
+ ...this.config.options
58
+ });
59
+ // Verify connection
60
+ const client = await this.pool.connect();
61
+ client.release();
62
+ }
63
+ catch (err) {
64
+ if (err.message.includes('Cannot find module')) {
65
+ throw new Error('PostgreSQL driver not found. Please run: npm install pg');
66
+ }
67
+ throw err;
68
+ }
69
+ }
70
+ async disconnect() {
71
+ if (this.pool) {
72
+ await this.pool.end();
73
+ this.pool = null;
74
+ }
75
+ }
76
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
+ async query(sql, params) {
78
+ if (!this.pool) {
79
+ throw new Error('Database not connected');
80
+ }
81
+ try {
82
+ // pg supports parameterized queries using $1, $2, etc.
83
+ // But our Coalescer might be sending '?' if it's generic.
84
+ // If the user writes 'SELECT * FROM users WHERE id = $1', that's fine.
85
+ // If they write '?', we might need to convert it?
86
+ // For now, we assume the user provides SQL compatible with the underlying driver.
87
+ // Or we could implement a simple '?' -> '$n' converter if we want to standardize.
88
+ // Standardizing is better for the generic "engine" feel.
89
+ if (sql.includes('?')) {
90
+ let i = 1;
91
+ sql = sql.replace(/\?/g, () => `$${i++}`);
92
+ }
93
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
94
+ const result = await this.pool.query(sql, params);
95
+ // Return rows for SELECT, or result meta for others?
96
+ // Our interface implies T.
97
+ // Usually users expect rows array for SELECT.
98
+ if (result.command === 'SELECT' || result.command === 'INSERT' && result.rows.length > 0) {
99
+ return result.rows;
100
+ }
101
+ return result;
102
+ }
103
+ catch (err) {
104
+ throw err;
105
+ }
106
+ }
107
+ isConnected() {
108
+ return this.pool !== null && !this.pool.ended;
109
+ }
110
+ }
111
+ exports.PostgresAdapter = PostgresAdapter;