@wabot-dev/framework 0.9.0 → 0.9.2

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 (56) hide show
  1. package/dist/src/addon/async/in-memory/InMemoryCronJobRepository.js +45 -0
  2. package/dist/src/addon/async/in-memory/InMemoryJobRepository.js +75 -0
  3. package/dist/src/addon/async/in-memory/index.js +2 -0
  4. package/dist/src/addon/async/pg/PgJobRepository.js +1 -0
  5. package/dist/src/addon/async/pg/PgTransactionAdapter.js +25 -0
  6. package/dist/src/addon/async/pg/index.js +3 -0
  7. package/dist/src/addon/chat-bot/{ram/RamChatMemory.js → in-memory/InMemoryChatMemory.js} +2 -2
  8. package/dist/src/addon/chat-bot/{ram/RamChatRepository.js → in-memory/InMemoryChatRepository.js} +6 -6
  9. package/dist/src/addon/chat-bot/in-memory/index.js +2 -0
  10. package/dist/src/addon/chat-bot/pg/index.js +2 -0
  11. package/dist/src/addon/chat-controller/socket/@socket.js +3 -1
  12. package/dist/src/addon/chat-controller/socket/SocketChannelConfig.js +4 -4
  13. package/dist/src/addon/chat-controller/whatsapp/WhatsAppSender.js +2 -72
  14. package/dist/src/addon/chat-controller/whatsapp/{WhatsAppReceiver.js → cloud-api/WhatsAppApiReceiver.js} +2 -2
  15. package/dist/src/addon/chat-controller/whatsapp/cloud-api/WhatsAppApiSender.js +67 -0
  16. package/dist/src/addon/chat-controller/whatsapp/cloud-api/WhatsAppReceiverByCloudApi.js +5 -7
  17. package/dist/src/addon/chat-controller/whatsapp/wasender/@wasender.js +23 -0
  18. package/dist/src/addon/chat-controller/{wasender/WhatsAppByWasenderChannel.js → whatsapp/wasender/WasenderChannel.js} +20 -20
  19. package/dist/src/addon/chat-controller/{wasender/WhatsAppByWasenderChannelConfig.js → whatsapp/wasender/WasenderChannelConfig.js} +3 -3
  20. package/dist/src/addon/chat-controller/whatsapp/wasender/WasenderChannelName.js +3 -0
  21. package/dist/src/addon/chat-controller/{wasender/WhatsAppReceiverByWasender.js → whatsapp/wasender/WasenderReceiver.js} +7 -7
  22. package/dist/src/addon/chat-controller/{wasender/WhatsAppSenderByWasender.js → whatsapp/wasender/WasenderSender.js} +8 -5
  23. package/dist/src/addon/chat-controller/{wasender → whatsapp/wasender}/WasenderWebhookController.js +4 -4
  24. package/dist/src/addon/lock/InMemoryLockKey.js +45 -0
  25. package/dist/src/addon/lock/InMemoryLocker.js +15 -0
  26. package/dist/src/addon/lock/index.js +2 -0
  27. package/dist/src/core/config/resolver.js +19 -1
  28. package/dist/src/feature/async/{@cron.js → @cronHandler.js} +2 -2
  29. package/dist/src/feature/async/@transaction.js +22 -0
  30. package/dist/src/feature/async/AsyncMetadataStore.js +6 -0
  31. package/dist/src/feature/async/TransactionMetadataStore.js +28 -0
  32. package/dist/src/feature/chat-controller/metadata/ControllerMetadataStore.js +3 -0
  33. package/dist/src/feature/pg/index.js +13 -0
  34. package/dist/src/feature/project-runner/ProjectRunner.js +253 -0
  35. package/dist/src/feature/rest-controller/metadata/RestControllerMetadataStore.js +3 -0
  36. package/dist/src/feature/socket-controller/metadata/SocketControllerMetadataStore.js +3 -0
  37. package/dist/src/index.d.ts +195 -261
  38. package/dist/src/index.js +23 -29
  39. package/package.json +6 -6
  40. package/dist/src/addon/chat-controller/wasender/@whatsAppByWasender.js +0 -20
  41. package/dist/src/addon/chat-controller/wasender/whatsAppByWasenderChannelName.js +0 -3
  42. package/dist/src/addon/chat-controller/whatsapp/@whatsApp.js +0 -20
  43. package/dist/src/addon/chat-controller/whatsapp/EnvWhatsAppRepository.js +0 -49
  44. package/dist/src/addon/chat-controller/whatsapp/PgWhatsAppRepository.js +0 -48
  45. package/dist/src/addon/chat-controller/whatsapp/WhatsApp.js +0 -30
  46. package/dist/src/addon/chat-controller/whatsapp/WhatsAppChannel.js +0 -58
  47. package/dist/src/addon/chat-controller/whatsapp/WhatsAppChannelConfig.js +0 -8
  48. package/dist/src/addon/chat-controller/whatsapp/WhatsAppRepository.js +0 -10
  49. package/dist/src/addon/chat-controller/whatsapp/cloud-api/WhatsAppSenderByCloudApi.js +0 -133
  50. package/dist/src/addon/chat-controller/whatsapp/proxy/WhatsAppProxyContracts.js +0 -5
  51. package/dist/src/addon/chat-controller/whatsapp/proxy/WhatsAppReceiverByWabotProxy.js +0 -67
  52. package/dist/src/addon/chat-controller/whatsapp/proxy/WhatsAppSenderByWabotProxy.js +0 -63
  53. package/dist/src/addon/chat-controller/whatsapp/proxy/WhatsAppWabotProxyConnection.js +0 -45
  54. package/dist/src/core/config/decorators.js +0 -22
  55. /package/dist/src/addon/chat-controller/whatsapp/{whatsAppChannelName.js → cloud-api/whatsAppChannelName.js} +0 -0
  56. /package/dist/src/addon/chat-controller/{wasender → whatsapp/wasender}/extractNumberFromWasenderKey.js +0 -0
@@ -0,0 +1,45 @@
1
+ class InMemoryLockKey {
2
+ key;
3
+ static locks = new Map();
4
+ constructor(key) {
5
+ this.key = key;
6
+ }
7
+ async run(fn) {
8
+ let release;
9
+ const lock = new Promise((r) => {
10
+ release = r;
11
+ });
12
+ const prev = InMemoryLockKey.locks.get(this.key) ?? Promise.resolve();
13
+ InMemoryLockKey.locks.set(this.key, lock);
14
+ try {
15
+ await prev;
16
+ return await fn();
17
+ }
18
+ finally {
19
+ if (InMemoryLockKey.locks.get(this.key) === lock) {
20
+ InMemoryLockKey.locks.delete(this.key);
21
+ }
22
+ release();
23
+ }
24
+ }
25
+ async tryRun(fn) {
26
+ if (InMemoryLockKey.locks.has(this.key))
27
+ return undefined;
28
+ let release;
29
+ const lock = new Promise((r) => {
30
+ release = r;
31
+ });
32
+ InMemoryLockKey.locks.set(this.key, lock);
33
+ try {
34
+ return await fn();
35
+ }
36
+ finally {
37
+ if (InMemoryLockKey.locks.get(this.key) === lock) {
38
+ InMemoryLockKey.locks.delete(this.key);
39
+ }
40
+ release();
41
+ }
42
+ }
43
+ }
44
+
45
+ export { InMemoryLockKey };
@@ -0,0 +1,15 @@
1
+ import { __decorate } from 'tslib';
2
+ import { singleton } from '../../core/injection/index.js';
3
+ import { InMemoryLockKey } from './InMemoryLockKey.js';
4
+
5
+ let InMemoryLocker = class InMemoryLocker {
6
+ withKey(key) {
7
+ const resolved = typeof key === 'object' ? key.lockerKey() : key;
8
+ return new InMemoryLockKey(resolved);
9
+ }
10
+ };
11
+ InMemoryLocker = __decorate([
12
+ singleton()
13
+ ], InMemoryLocker);
14
+
15
+ export { InMemoryLocker };
@@ -0,0 +1,2 @@
1
+ export { InMemoryLockKey } from './InMemoryLockKey.js';
2
+ export { InMemoryLocker } from './InMemoryLocker.js';
@@ -1,3 +1,21 @@
1
+ function resolveConfigReferences(config) {
2
+ const resolved = {};
3
+ for (const [key, value] of Object.entries(config)) {
4
+ if (isConfigReference(value)) {
5
+ resolved[key] = ConfigResolver.resolve(value);
6
+ }
7
+ else {
8
+ resolved[key] = value;
9
+ }
10
+ }
11
+ return resolved;
12
+ }
13
+ function isConfigReference(value) {
14
+ return (typeof value === 'object' &&
15
+ value !== null &&
16
+ '__isConfigReference' in value &&
17
+ value.__isConfigReference === true);
18
+ }
1
19
  class ConfigResolver {
2
20
  static resolve(reference) {
3
21
  const envValue = this.loadFromEnv(reference.path);
@@ -67,4 +85,4 @@ class ConfigResolver {
67
85
  }
68
86
  }
69
87
 
70
- export { ConfigResolver };
88
+ export { ConfigResolver, resolveConfigReferences };
@@ -1,7 +1,7 @@
1
1
  import { AsyncMetadataStore } from './AsyncMetadataStore.js';
2
2
  import { container, singleton } from '../../core/injection/index.js';
3
3
 
4
- function cron(config) {
4
+ function cronHandler(config) {
5
5
  return function (target) {
6
6
  const metadataStore = container.resolve(AsyncMetadataStore);
7
7
  metadataStore.registerCron(target, {
@@ -14,4 +14,4 @@ function cron(config) {
14
14
  };
15
15
  }
16
16
 
17
- export { cron };
17
+ export { cronHandler };
@@ -0,0 +1,22 @@
1
+ import { container } from '../../core/injection/index.js';
2
+ import { TransactionMetadataStore } from './TransactionMetadataStore.js';
3
+
4
+ function transaction(dbNames) {
5
+ return function (_target, _propertyKey, descriptor) {
6
+ const originalMethod = descriptor.value;
7
+ const store = container.resolve(TransactionMetadataStore);
8
+ descriptor.value = function (...args) {
9
+ const adapters = dbNames ? store.requireAdapters(dbNames) : store.getAllAdapters();
10
+ return runInTransactions(adapters, () => originalMethod.apply(this, args));
11
+ };
12
+ return descriptor;
13
+ };
14
+ }
15
+ function runInTransactions(adapters, fn) {
16
+ if (adapters.length === 0)
17
+ return fn();
18
+ const composed = adapters.reduceRight((next, adapter) => () => adapter.run(next), fn);
19
+ return composed();
20
+ }
21
+
22
+ export { transaction };
@@ -52,6 +52,12 @@ let AsyncMetadataStore = class AsyncMetadataStore {
52
52
  getCommandForCommandName(commandName) {
53
53
  return this.commandsMap.get(commandName) ?? null;
54
54
  }
55
+ getAllCommandHandlers() {
56
+ return Array.from(this.handlersInverseMap.keys());
57
+ }
58
+ getAllCronHandlers() {
59
+ return Array.from(this.cronsMap.keys());
60
+ }
55
61
  };
56
62
  AsyncMetadataStore = __decorate([
57
63
  singleton()
@@ -0,0 +1,28 @@
1
+ import { __decorate } from 'tslib';
2
+ import { singleton } from '../../core/injection/index.js';
3
+
4
+ let TransactionMetadataStore = class TransactionMetadataStore {
5
+ adapters = new Map();
6
+ registerAdapter(dbName, adapter) {
7
+ this.adapters.set(dbName, adapter);
8
+ }
9
+ requireAdapter(dbName) {
10
+ const adapter = this.adapters.get(dbName);
11
+ if (!adapter) {
12
+ throw new Error(`No transaction adapter registered for "${dbName}". ` +
13
+ `Register one with container.resolve(TransactionMetadataStore).registerAdapter("${dbName}", adapter)`);
14
+ }
15
+ return adapter;
16
+ }
17
+ requireAdapters(dbNames) {
18
+ return dbNames.map((name) => this.requireAdapter(name));
19
+ }
20
+ getAllAdapters() {
21
+ return Array.from(this.adapters.values());
22
+ }
23
+ };
24
+ TransactionMetadataStore = __decorate([
25
+ singleton()
26
+ ], TransactionMetadataStore);
27
+
28
+ export { TransactionMetadataStore };
@@ -20,6 +20,9 @@ let ControllerMetadataStore = class ControllerMetadataStore {
20
20
  saveChatControllerMetadata(controllerMetadata) {
21
21
  this.chatControllers.set(controllerMetadata.controllerConstructor, controllerMetadata);
22
22
  }
23
+ getAllChatControllerConstructors() {
24
+ return Array.from(this.chatControllers.keys());
25
+ }
23
26
  getChatControllerMetadata(controllerConstructor) {
24
27
  const mainMetadata = this.chatControllers.get(controllerConstructor);
25
28
  if (!mainMetadata)
@@ -0,0 +1,13 @@
1
+ export { PgCrudRepository } from './PgCrudRepository.js';
2
+ export { PgLocker } from './PgLocker.js';
3
+ export { PgLockKey } from './PgLockKey.js';
4
+ export { PgRepositoryBase } from './PgRepositoryBase.js';
5
+ export { getClientMap, pgStorage } from './pgStorage.js';
6
+ export { pgJsonRepository } from './query/@pgJsonRepository.js';
7
+ export { query } from './query/@query.js';
8
+ export { PgJsonRepository } from './query/PgJsonRepository.js';
9
+ export { PgRepositoryMetadataStore } from './query/PgRepositoryMetadataStore.js';
10
+ export { buildQuerySql } from './query/buildQuerySql.js';
11
+ export { parseQueryMethodName } from './query/parseQueryMethodName.js';
12
+ export { getPgClient, withPgClient } from './withPgClient.js';
13
+ export { withPgTransaction } from './withPgTransaction.js';
@@ -0,0 +1,253 @@
1
+ import { readdir } from 'node:fs/promises';
2
+ import { resolve, join } from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { container } from '../../core/injection/index.js';
5
+ import { Logger } from '../../core/logger/Logger.js';
6
+ import { Locker } from '../../core/lock/Locker.js';
7
+ import '../chat-bot/ChatAdapterRegistry.js';
8
+ import '../chat-bot/ChatBot.js';
9
+ import '../chat-bot/ChatOperator.js';
10
+ import { ChatRepository } from '../chat-bot/ChatRepository.js';
11
+ import '../chat-bot/UnionChatAdapter.js';
12
+ import '../chat-bot/metadata/ChatAdapterMetadataStore.js';
13
+ import 'uuid';
14
+ import '../chat-bot/metadata/ChatBotMetadataStore.js';
15
+ import { runChatAdapters } from '../chat-bot/runChatAdapters.js';
16
+ import '../../core/error/setupErrorHandlers.js';
17
+ import { ControllerMetadataStore } from '../chat-controller/metadata/ControllerMetadataStore.js';
18
+ import '../chat-controller/ChatResolver.js';
19
+ import { runChatControllers } from '../chat-controller/runChatControllers.js';
20
+ import { RestControllerMetadataStore } from '../rest-controller/metadata/RestControllerMetadataStore.js';
21
+ import { runRestControllers } from '../rest-controller/runRestControllers.js';
22
+ import { AsyncMetadataStore } from '../async/AsyncMetadataStore.js';
23
+ import { TransactionMetadataStore } from '../async/TransactionMetadataStore.js';
24
+ import '../async/Async.js';
25
+ import '../../_virtual/index.js';
26
+ import { CronJobRepository } from '../async/CronJobRepository.js';
27
+ import { JobRepository } from '../async/JobRepository.js';
28
+ import '../async/JobRunner.js';
29
+ import { runCommandHandlers } from '../async/runCommandHandlers.js';
30
+ import { runCronHandlers } from '../async/runCronHandlers.js';
31
+ import { SocketControllerMetadataStore } from '../socket-controller/metadata/SocketControllerMetadataStore.js';
32
+ import { runSocketControllers } from '../socket-controller/runSocketControllers.js';
33
+
34
+ const logger = new Logger('wabot:project-runner');
35
+ const TEST_FILE_PATTERNS = /\.(test|spec|unit|integration|e2e|multiprocess)\.(ts|js)$/;
36
+ const DEFAULT_CHAT_ADAPTERS = [
37
+ ['../../addon/chat-bot/openia', 'OpenaiChatAdapter'],
38
+ ['../../addon/chat-bot/openrouter', 'OpenRouterChatAdapter'],
39
+ ['../../addon/chat-bot/anthropic', 'AnthropicChatAdapter'],
40
+ ['../../addon/chat-bot/google', 'GoogleChatAdapter'],
41
+ ];
42
+ class ProjectRunner {
43
+ directories;
44
+ chatAdapters;
45
+ connectionString;
46
+ isPg;
47
+ pool = null;
48
+ constructor(config = {}) {
49
+ this.directories = config.directories ?? ['src'];
50
+ this.chatAdapters = config.chatAdapters;
51
+ this.connectionString = this.resolveConnectionString(config.connectionString);
52
+ this.isPg = this.connectionString != null && isPostgresUrl(this.connectionString);
53
+ }
54
+ async run() {
55
+ const [, files] = await Promise.all([
56
+ this.isPg ? this.initPool() : Promise.resolve(),
57
+ this.scanDirectories(),
58
+ ]);
59
+ await this.importFiles(files);
60
+ const components = this.discoverComponents();
61
+ await this.registerAdapters(components);
62
+ await this.startComponents(components);
63
+ }
64
+ resolveConnectionString(configValue) {
65
+ const cs = configValue ?? process.env.DATABASE_URL ?? null;
66
+ if (cs && !isPostgresUrl(cs)) {
67
+ logger.warn(`connectionString "${cs}" does not match a known scheme (postgres://, postgresql://); falling back to in-memory adapters`);
68
+ }
69
+ return cs;
70
+ }
71
+ async initPool() {
72
+ const { Pool } = await import('pg');
73
+ this.pool = new Pool({ connectionString: this.connectionString });
74
+ container.registerInstance(Pool, this.pool);
75
+ }
76
+ async scanDirectories() {
77
+ const seen = new Set();
78
+ const roots = this.directories
79
+ .map((d) => resolve(d))
80
+ .filter((d) => {
81
+ if (seen.has(d))
82
+ return false;
83
+ seen.add(d);
84
+ return true;
85
+ });
86
+ const results = await Promise.all(roots.map((dir) => scanDir(dir).catch((err) => {
87
+ logger.warn(`Could not read directory ${dir}: ${err.message}`);
88
+ return [];
89
+ })));
90
+ return results.flat();
91
+ }
92
+ async importFiles(files) {
93
+ if (files.length === 0) {
94
+ logger.info('No files to import');
95
+ return;
96
+ }
97
+ const results = await Promise.allSettled(files.map((file) => import(pathToFileURL(file).href)));
98
+ let imported = 0;
99
+ let failed = 0;
100
+ for (let i = 0; i < results.length; i++) {
101
+ const result = results[i];
102
+ if (result.status === 'fulfilled') {
103
+ imported++;
104
+ }
105
+ else {
106
+ failed++;
107
+ const reason = result.reason;
108
+ logger.error(`Failed to import ${files[i]}: ${reason.message}`);
109
+ }
110
+ }
111
+ if (failed > 0) {
112
+ logger.warn(`Imported ${imported}/${files.length} files (${failed} failed)`);
113
+ }
114
+ else {
115
+ logger.info(`Imported ${imported}/${files.length} files`);
116
+ }
117
+ }
118
+ discoverComponents() {
119
+ const asyncStore = container.resolve(AsyncMetadataStore);
120
+ return {
121
+ chatControllers: container
122
+ .resolve(ControllerMetadataStore)
123
+ .getAllChatControllerConstructors(),
124
+ restControllers: container
125
+ .resolve(RestControllerMetadataStore)
126
+ .getAllRestControllerConstructors(),
127
+ commandHandlers: asyncStore.getAllCommandHandlers(),
128
+ cronHandlers: asyncStore.getAllCronHandlers(),
129
+ socketControllers: container
130
+ .resolve(SocketControllerMetadataStore)
131
+ .getAllSocketControllerConstructors(),
132
+ };
133
+ }
134
+ registerAdapters(components) {
135
+ return this.isPg
136
+ ? this.registerPostgresAdapters(components)
137
+ : this.registerMemoryAdapters(components);
138
+ }
139
+ async registerMemoryAdapters(components) {
140
+ const needsJobs = components.commandHandlers.length > 0 || components.cronHandlers.length > 0;
141
+ const [chatBotMod, lockMod, asyncMod] = await Promise.all([
142
+ import('../../addon/chat-bot/in-memory/index.js'),
143
+ import('../../addon/lock/index.js'),
144
+ needsJobs ? import('../../addon/async/in-memory/index.js') : Promise.resolve(null),
145
+ ]);
146
+ container.registerType(ChatRepository, chatBotMod.InMemoryChatRepository);
147
+ container.registerType(Locker, lockMod.InMemoryLocker);
148
+ if (asyncMod) {
149
+ container.registerType(JobRepository, asyncMod.InMemoryJobRepository);
150
+ if (components.cronHandlers.length > 0) {
151
+ container.registerType(CronJobRepository, asyncMod.InMemoryCronJobRepository);
152
+ }
153
+ }
154
+ logger.info('Configured with in-memory adapters');
155
+ }
156
+ async registerPostgresAdapters(components) {
157
+ if (!this.pool) {
158
+ throw new Error('Postgres pool was not initialized');
159
+ }
160
+ const [chatBotMod, pgMod, asyncMod] = await Promise.all([
161
+ import('../../addon/chat-bot/pg/index.js'),
162
+ import('../pg/index.js'),
163
+ import('../../addon/async/pg/index.js'),
164
+ ]);
165
+ container.registerType(ChatRepository, chatBotMod.PgChatRepository);
166
+ container.registerType(Locker, pgMod.PgLocker);
167
+ const transactionStore = container.resolve(TransactionMetadataStore);
168
+ transactionStore.registerAdapter('default', new asyncMod.PgTransactionAdapter(this.pool));
169
+ const hasCommandHandlers = components.commandHandlers.length > 0;
170
+ const hasCronHandlers = components.cronHandlers.length > 0;
171
+ if (hasCommandHandlers || hasCronHandlers) {
172
+ container.registerType(JobRepository, asyncMod.PgJobRepository);
173
+ }
174
+ if (hasCronHandlers) {
175
+ container.registerType(CronJobRepository, asyncMod.PgCronJobRepository);
176
+ }
177
+ logger.info('Configured with PostgreSQL adapters');
178
+ }
179
+ async startComponents(components) {
180
+ const chatAdapters = this.chatAdapters ?? (await this.resolveDefaultChatAdapters());
181
+ if (chatAdapters.length > 0) {
182
+ runChatAdapters(chatAdapters);
183
+ }
184
+ if (components.chatControllers.length > 0) {
185
+ logger.info(`Starting ${components.chatControllers.length} chat controller(s)`);
186
+ runChatControllers(components.chatControllers);
187
+ }
188
+ if (components.restControllers.length > 0) {
189
+ logger.info(`Starting ${components.restControllers.length} REST controller(s)`);
190
+ runRestControllers(components.restControllers);
191
+ }
192
+ if (components.commandHandlers.length > 0) {
193
+ logger.info(`Starting ${components.commandHandlers.length} command handler(s)`);
194
+ runCommandHandlers(components.commandHandlers);
195
+ }
196
+ if (components.cronHandlers.length > 0) {
197
+ logger.info(`Starting ${components.cronHandlers.length} cron handler(s)`);
198
+ runCronHandlers(components.cronHandlers);
199
+ }
200
+ if (components.socketControllers.length > 0) {
201
+ logger.info(`Starting ${components.socketControllers.length} socket controller(s)`);
202
+ runSocketControllers(components.socketControllers);
203
+ }
204
+ }
205
+ async resolveDefaultChatAdapters() {
206
+ const results = await Promise.all(DEFAULT_CHAT_ADAPTERS.map(async ([path, name]) => {
207
+ try {
208
+ const mod = await import(path);
209
+ const adapter = mod[name];
210
+ if (!adapter) {
211
+ logger.warn(`Skipping ${name}: module loaded but no '${name}' export found`);
212
+ return null;
213
+ }
214
+ return adapter;
215
+ }
216
+ catch {
217
+ logger.warn(`Skipping ${name}: missing peer dependency`);
218
+ return null;
219
+ }
220
+ }));
221
+ return results.filter((a) => a != null);
222
+ }
223
+ }
224
+ function run(config) {
225
+ return new ProjectRunner(config).run();
226
+ }
227
+ function isPostgresUrl(cs) {
228
+ return cs.startsWith('postgres://') || cs.startsWith('postgresql://');
229
+ }
230
+ async function scanDir(dir) {
231
+ const entries = await readdir(dir, { withFileTypes: true });
232
+ const subResults = await Promise.all(entries.map(async (entry) => {
233
+ const name = entry.name;
234
+ if (entry.isDirectory()) {
235
+ if (name.startsWith('__'))
236
+ return [];
237
+ return scanDir(join(dir, name));
238
+ }
239
+ if (!entry.isFile())
240
+ return [];
241
+ if (!(name.endsWith('.ts') || name.endsWith('.js')))
242
+ return [];
243
+ if (name.endsWith('.d.ts'))
244
+ return [];
245
+ const fullPath = join(dir, name);
246
+ if (TEST_FILE_PATTERNS.test(fullPath))
247
+ return [];
248
+ return [fullPath];
249
+ }));
250
+ return subResults.flat();
251
+ }
252
+
253
+ export { ProjectRunner, run };
@@ -35,6 +35,9 @@ let RestControllerMetadataStore = class RestControllerMetadataStore {
35
35
  }
36
36
  methodMiddlewares.unshift(middlewareMetadata);
37
37
  }
38
+ getAllRestControllerConstructors() {
39
+ return Array.from(this.restControllers.keys());
40
+ }
38
41
  getControllerEndPointsInfo(controllerConstructor) {
39
42
  const controller = this.restControllers.get(controllerConstructor);
40
43
  if (!controller) {
@@ -22,6 +22,9 @@ let SocketControllerMetadataStore = class SocketControllerMetadataStore {
22
22
  }
23
23
  controllerMiddlewares.unshift(handshakeMetadata);
24
24
  }
25
+ getAllSocketControllerConstructors() {
26
+ return Array.from(this.socketControllers.keys());
27
+ }
25
28
  getSocketControllerInfo(controllerConstructor) {
26
29
  const controller = this.socketControllers.get(controllerConstructor);
27
30
  if (!controller) {