@open-rlb/nestjs-amqp 2.1.0 → 2.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 (198) hide show
  1. package/package.json +6 -5
  2. package/schematics/add-auth-provider/index.d.ts +3 -0
  3. package/schematics/add-auth-provider/index.js +60 -0
  4. package/schematics/add-auth-provider/index.js.map +1 -0
  5. package/schematics/add-auth-provider/index.spec.ts +83 -0
  6. package/schematics/add-auth-provider/index.ts +83 -0
  7. package/schematics/add-auth-provider/schema.d.ts +18 -0
  8. package/schematics/add-auth-provider/schema.js +3 -0
  9. package/schematics/add-auth-provider/schema.js.map +1 -0
  10. package/schematics/add-auth-provider/schema.json +59 -0
  11. package/schematics/add-auth-provider/schema.ts +35 -0
  12. package/schematics/add-exchange/index.d.ts +3 -0
  13. package/schematics/add-exchange/index.js +49 -0
  14. package/schematics/add-exchange/index.js.map +1 -0
  15. package/schematics/add-exchange/index.spec.ts +65 -0
  16. package/schematics/add-exchange/index.ts +65 -0
  17. package/schematics/add-exchange/schema.d.ts +14 -0
  18. package/schematics/add-exchange/schema.js +3 -0
  19. package/schematics/add-exchange/schema.js.map +1 -0
  20. package/schematics/add-exchange/schema.json +50 -0
  21. package/schematics/add-exchange/schema.ts +26 -0
  22. package/schematics/add-exchange-binding/index.d.ts +3 -0
  23. package/schematics/add-exchange-binding/index.js +53 -0
  24. package/schematics/add-exchange-binding/index.js.map +1 -0
  25. package/schematics/add-exchange-binding/index.spec.ts +73 -0
  26. package/schematics/add-exchange-binding/index.ts +64 -0
  27. package/schematics/add-exchange-binding/schema.d.ts +8 -0
  28. package/schematics/add-exchange-binding/schema.js +3 -0
  29. package/schematics/add-exchange-binding/schema.js.map +1 -0
  30. package/schematics/add-exchange-binding/schema.json +36 -0
  31. package/schematics/add-exchange-binding/schema.ts +14 -0
  32. package/schematics/add-gateway/index.d.ts +4 -0
  33. package/schematics/add-gateway/index.js +105 -0
  34. package/schematics/add-gateway/index.js.map +1 -0
  35. package/schematics/add-gateway/index.spec.ts +163 -0
  36. package/schematics/add-gateway/index.ts +141 -0
  37. package/schematics/add-gateway/paths.d.ts +12 -0
  38. package/schematics/add-gateway/paths.js +87 -0
  39. package/schematics/add-gateway/paths.js.map +1 -0
  40. package/schematics/add-gateway/paths.ts +103 -0
  41. package/schematics/add-gateway/schema.d.ts +13 -0
  42. package/schematics/add-gateway/schema.js +3 -0
  43. package/schematics/add-gateway/schema.js.map +1 -0
  44. package/schematics/add-gateway/schema.json +63 -0
  45. package/schematics/add-gateway/schema.ts +24 -0
  46. package/schematics/add-gateway/wiring.d.ts +14 -0
  47. package/schematics/add-gateway/wiring.js +102 -0
  48. package/schematics/add-gateway/wiring.js.map +1 -0
  49. package/schematics/add-gateway/wiring.ts +115 -0
  50. package/schematics/add-metrics-topic/index.d.ts +3 -0
  51. package/schematics/add-metrics-topic/index.js +31 -0
  52. package/schematics/add-metrics-topic/index.js.map +1 -0
  53. package/schematics/add-metrics-topic/index.spec.ts +79 -0
  54. package/schematics/add-metrics-topic/index.ts +49 -0
  55. package/schematics/add-metrics-topic/schema.d.ts +10 -0
  56. package/schematics/add-metrics-topic/schema.js +3 -0
  57. package/schematics/add-metrics-topic/schema.js.map +1 -0
  58. package/schematics/add-metrics-topic/schema.json +48 -0
  59. package/schematics/add-metrics-topic/schema.ts +18 -0
  60. package/schematics/add-queue/index.d.ts +3 -0
  61. package/schematics/add-queue/index.js +48 -0
  62. package/schematics/add-queue/index.js.map +1 -0
  63. package/schematics/add-queue/index.spec.ts +75 -0
  64. package/schematics/add-queue/index.ts +74 -0
  65. package/schematics/add-queue/schema.d.ts +17 -0
  66. package/schematics/add-queue/schema.js +3 -0
  67. package/schematics/add-queue/schema.js.map +1 -0
  68. package/schematics/add-queue/schema.json +54 -0
  69. package/schematics/add-queue/schema.ts +32 -0
  70. package/schematics/add-reply-queue/index.d.ts +3 -0
  71. package/schematics/add-reply-queue/index.js +26 -0
  72. package/schematics/add-reply-queue/index.js.map +1 -0
  73. package/schematics/add-reply-queue/index.spec.ts +69 -0
  74. package/schematics/add-reply-queue/index.ts +33 -0
  75. package/schematics/add-reply-queue/schema.d.ts +6 -0
  76. package/schematics/add-reply-queue/schema.js +3 -0
  77. package/schematics/add-reply-queue/schema.js.map +1 -0
  78. package/schematics/add-reply-queue/schema.json +28 -0
  79. package/schematics/add-reply-queue/schema.ts +10 -0
  80. package/schematics/add-route/index.d.ts +3 -0
  81. package/schematics/add-route/index.js +57 -0
  82. package/schematics/add-route/index.js.map +1 -0
  83. package/schematics/add-route/index.spec.ts +75 -0
  84. package/schematics/add-route/index.ts +74 -0
  85. package/schematics/add-route/schema.d.ts +22 -0
  86. package/schematics/add-route/schema.js +3 -0
  87. package/schematics/add-route/schema.js.map +1 -0
  88. package/schematics/add-route/schema.json +74 -0
  89. package/schematics/add-route/schema.ts +43 -0
  90. package/schematics/add-topic/index.d.ts +3 -0
  91. package/schematics/add-topic/index.js +69 -0
  92. package/schematics/add-topic/index.js.map +1 -0
  93. package/schematics/add-topic/index.spec.ts +100 -0
  94. package/schematics/add-topic/index.ts +104 -0
  95. package/schematics/add-topic/schema.d.ts +18 -0
  96. package/schematics/add-topic/schema.js +3 -0
  97. package/schematics/add-topic/schema.js.map +1 -0
  98. package/schematics/add-topic/schema.json +63 -0
  99. package/schematics/add-topic/schema.ts +34 -0
  100. package/schematics/add-ws-event/index.d.ts +3 -0
  101. package/schematics/add-ws-event/index.js +53 -0
  102. package/schematics/add-ws-event/index.js.map +1 -0
  103. package/schematics/add-ws-event/index.spec.ts +85 -0
  104. package/schematics/add-ws-event/index.ts +72 -0
  105. package/schematics/add-ws-event/schema.d.ts +17 -0
  106. package/schematics/add-ws-event/schema.js +3 -0
  107. package/schematics/add-ws-event/schema.js.map +1 -0
  108. package/schematics/add-ws-event/schema.json +53 -0
  109. package/schematics/add-ws-event/schema.ts +33 -0
  110. package/schematics/collection.json +81 -1
  111. package/schematics/configure-broker/index.d.ts +3 -0
  112. package/schematics/configure-broker/index.js +41 -0
  113. package/schematics/configure-broker/index.js.map +1 -0
  114. package/schematics/configure-broker/index.spec.ts +62 -0
  115. package/schematics/configure-broker/index.ts +51 -0
  116. package/schematics/configure-broker/schema.d.ts +11 -0
  117. package/schematics/configure-broker/schema.js +3 -0
  118. package/schematics/configure-broker/schema.js.map +1 -0
  119. package/schematics/configure-broker/schema.json +47 -0
  120. package/schematics/configure-broker/schema.ts +20 -0
  121. package/schematics/enable-load-config/index.d.ts +3 -0
  122. package/schematics/enable-load-config/index.js +32 -0
  123. package/schematics/enable-load-config/index.js.map +1 -0
  124. package/schematics/enable-load-config/index.spec.ts +61 -0
  125. package/schematics/enable-load-config/index.ts +39 -0
  126. package/schematics/enable-load-config/schema.d.ts +8 -0
  127. package/schematics/enable-load-config/schema.js +3 -0
  128. package/schematics/enable-load-config/schema.js.map +1 -0
  129. package/schematics/enable-load-config/schema.json +36 -0
  130. package/schematics/enable-load-config/schema.ts +14 -0
  131. package/schematics/enable-retry/index.d.ts +3 -0
  132. package/schematics/enable-retry/index.js +51 -0
  133. package/schematics/enable-retry/index.js.map +1 -0
  134. package/schematics/enable-retry/index.spec.ts +73 -0
  135. package/schematics/enable-retry/index.ts +62 -0
  136. package/schematics/enable-retry/schema.d.ts +13 -0
  137. package/schematics/enable-retry/schema.js +3 -0
  138. package/schematics/enable-retry/schema.js.map +1 -0
  139. package/schematics/enable-retry/schema.json +64 -0
  140. package/schematics/enable-retry/schema.ts +24 -0
  141. package/schematics/enable-route-discovery/index.d.ts +3 -0
  142. package/schematics/enable-route-discovery/index.js +36 -0
  143. package/schematics/enable-route-discovery/index.js.map +1 -0
  144. package/schematics/enable-route-discovery/index.spec.ts +55 -0
  145. package/schematics/enable-route-discovery/index.ts +48 -0
  146. package/schematics/enable-route-discovery/schema.d.ts +9 -0
  147. package/schematics/enable-route-discovery/schema.js +3 -0
  148. package/schematics/enable-route-discovery/schema.js.map +1 -0
  149. package/schematics/enable-route-discovery/schema.json +44 -0
  150. package/schematics/enable-route-discovery/schema.ts +16 -0
  151. package/schematics/harden-gateway/index.d.ts +3 -0
  152. package/schematics/harden-gateway/index.js +93 -0
  153. package/schematics/harden-gateway/index.js.map +1 -0
  154. package/schematics/harden-gateway/index.spec.ts +105 -0
  155. package/schematics/harden-gateway/index.ts +108 -0
  156. package/schematics/harden-gateway/schema.d.ts +11 -0
  157. package/schematics/harden-gateway/schema.js +3 -0
  158. package/schematics/harden-gateway/schema.js.map +1 -0
  159. package/schematics/harden-gateway/schema.json +47 -0
  160. package/schematics/harden-gateway/schema.ts +20 -0
  161. package/schematics/nest-add/index.js +55 -832
  162. package/schematics/nest-add/index.js.map +1 -1
  163. package/schematics/nest-add/index.spec.ts +73 -0
  164. package/schematics/nest-add/index.ts +76 -917
  165. package/schematics/nest-add/init.schema.d.ts +1 -0
  166. package/schematics/nest-add/init.schema.ts +3 -0
  167. package/schematics/nest-add/schema.json +4 -43
  168. package/schematics/set-connection-name/index.d.ts +3 -0
  169. package/schematics/set-connection-name/index.js +34 -0
  170. package/schematics/set-connection-name/index.js.map +1 -0
  171. package/schematics/set-connection-name/index.spec.ts +48 -0
  172. package/schematics/set-connection-name/index.ts +42 -0
  173. package/schematics/set-connection-name/schema.d.ts +5 -0
  174. package/schematics/set-connection-name/schema.js +3 -0
  175. package/schematics/set-connection-name/schema.js.map +1 -0
  176. package/schematics/set-connection-name/schema.json +24 -0
  177. package/schematics/set-connection-name/schema.ts +8 -0
  178. package/schematics/utils/broker-yaml.util.d.ts +53 -0
  179. package/schematics/utils/broker-yaml.util.js +91 -0
  180. package/schematics/utils/broker-yaml.util.js.map +1 -0
  181. package/schematics/utils/broker-yaml.util.ts +172 -0
  182. package/schematics/utils/nest-wiring.util.d.ts +30 -0
  183. package/schematics/utils/nest-wiring.util.js +170 -0
  184. package/schematics/utils/nest-wiring.util.js.map +1 -0
  185. package/schematics/utils/nest-wiring.util.ts +211 -0
  186. package/schematics/utils/schematic-prompt.util.d.ts +7 -0
  187. package/schematics/utils/schematic-prompt.util.js +57 -0
  188. package/schematics/utils/schematic-prompt.util.js.map +1 -0
  189. package/schematics/utils/schematic-prompt.util.ts +82 -0
  190. package/schematics/utils/yaml-config.util.d.ts +22 -0
  191. package/schematics/utils/yaml-config.util.js +119 -0
  192. package/schematics/utils/yaml-config.util.js.map +1 -0
  193. package/schematics/utils/yaml-config.util.ts +167 -0
  194. /package/schematics/{nest-add → add-gateway}/files/acl/src/cache/in-memory-acl-store.ts +0 -0
  195. /package/schematics/{nest-add → add-gateway}/files/acl/src/modules/database/repository/acl.repository.ts +0 -0
  196. /package/schematics/{nest-add → add-gateway}/files/db-core/src/modules/database/repository/in-memory-collection.ts +0 -0
  197. /package/schematics/{nest-add → add-gateway}/files/gateway-admin/src/modules/database/repository/gateway.repository.ts +0 -0
  198. /package/schematics/{nest-add → add-gateway}/files/gateway-admin/src/modules/database/repository/route-sync.repository.ts +0 -0
@@ -2,862 +2,85 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.main = main;
4
4
  const schematics_1 = require("@angular-devkit/schematics");
5
- const jsonc_parser_1 = require("jsonc-parser");
6
5
  const path_1 = require("path");
7
- const formatting_1 = require("../utils/formatting");
8
- const name_parser_1 = require("../utils/name.parser");
9
- const source_root_helpers_1 = require("../utils/source-root.helpers");
10
- function defaultNames(project) {
11
- return {
12
- exchange: 'rlb',
13
- aclQueue: 'rlb-acl',
14
- adminQueue: 'rlb-gateway-admin',
15
- controlTopic: 'rlb-gateway-control',
16
- routeExchange: 'rlb-route-discovery',
17
- routeQueue: 'rlb-route-sync',
18
- serviceName: project || 'my-service',
19
- };
20
- }
21
- async function resolveSelections(o, context) {
22
- const project = (o.project || 'my-service').toString();
23
- const d = defaultNames(project);
24
- const flagsProvided = o.gatewayConfig !== undefined || (Array.isArray(o.features) && o.features.length > 0);
25
- const canPrompt = !!process.stdout && !!process.stdout.isTTY && !process.env.CI && !flagsProvided;
26
- let prompts;
27
- if (canPrompt) {
28
- try {
29
- prompts = require('@inquirer/prompts');
30
- }
31
- catch {
32
- context.logger.warn('[nest-add] @inquirer/prompts not found; falling back to flags/defaults (non-interactive).');
33
- prompts = undefined;
34
- }
35
- }
36
- if (!prompts) {
37
- const features = new Set((o.features || []).map((f) => String(f).trim()));
38
- const gatewayConfig = o.gatewayConfig === true;
39
- return {
40
- gatewayConfig,
41
- acl: gatewayConfig && features.has('acl'),
42
- admin: gatewayConfig && features.has('gateway-admin'),
43
- routeReception: gatewayConfig && features.has('route-reception'),
44
- autoPublish: !gatewayConfig && features.has('auto-config-publish'),
45
- skills: o.skills !== false,
46
- names: {
47
- exchange: o.exchange || d.exchange,
48
- aclQueue: o.aclQueue || d.aclQueue,
49
- adminQueue: o.adminQueue || d.adminQueue,
50
- controlTopic: o.controlTopic || d.controlTopic,
51
- routeExchange: o.routeExchange || d.routeExchange,
52
- routeQueue: o.routeQueue || d.routeQueue,
53
- serviceName: o.serviceName || d.serviceName,
54
- },
55
- };
56
- }
57
- const { confirm, checkbox, input } = prompts;
58
- const names = { ...d };
59
- let acl = false, admin = false, routeReception = false, autoPublish = false;
60
- const gatewayConfig = await confirm({
61
- message: 'Create a gateway (HTTP/WebSocket) configuration?',
62
- default: false,
63
- });
64
- if (gatewayConfig) {
65
- const picked = await checkbox({
66
- message: 'Select gateway features to include',
67
- choices: [
68
- { name: 'ACL — role-based authorization + management', value: 'acl' },
69
- { name: 'Gateway admin + auth — DB-managed routes, auth-providers, metrics', value: 'gateway-admin' },
70
- { name: 'Route reception — apply routes auto-published by microservices', value: 'route-reception' },
71
- ],
72
- });
73
- acl = picked.includes('acl');
74
- admin = picked.includes('gateway-admin');
75
- routeReception = picked.includes('route-reception');
76
- if (acl || admin || routeReception) {
77
- names.exchange = await input({ message: 'Main AMQP exchange name', default: d.exchange });
78
- }
79
- if (acl) {
80
- names.aclQueue = await input({ message: 'Queue backing the rlb-acl topic', default: d.aclQueue });
81
- }
82
- if (admin || routeReception) {
83
- names.adminQueue = await input({ message: 'Queue backing the rlb-gateway-admin topic', default: d.adminQueue });
84
- names.controlTopic = await input({ message: 'Broadcast control/reload topic name', default: d.controlTopic });
85
- }
86
- if (routeReception) {
87
- names.routeExchange = await input({ message: 'Route-discovery exchange (must match the publishers)', default: d.routeExchange });
88
- names.routeQueue = await input({ message: 'Route-sync queue', default: d.routeQueue });
89
- }
90
- }
91
- else {
92
- const picked = await checkbox({
93
- message: 'Select microservice features to include',
94
- choices: [
95
- { name: 'Auto-send config at startup — publish this service’s @BrokerHTTP routes to the gateway on boot', value: 'auto-config-publish' },
96
- ],
6
+ const nest_wiring_util_1 = require("../utils/nest-wiring.util");
7
+ const schematic_prompt_util_1 = require("../utils/schematic-prompt.util");
8
+ const yaml_config_util_1 = require("../utils/yaml-config.util");
9
+ function main(options) {
10
+ return async (tree, context) => {
11
+ const prompts = (0, schematic_prompt_util_1.loadPrompts)(context, options.skills !== undefined);
12
+ const skills = options.skills ?? (await (0, schematic_prompt_util_1.askConfirm)(prompts, 'Add the Claude skills / project docs into .claude/skills?', true));
13
+ ensureBaseConfig(tree, options.config, context);
14
+ (0, nest_wiring_util_1.createConfigLoader)(tree);
15
+ const modulePath = (0, nest_wiring_util_1.wireAppModule)(tree, {
16
+ libSymbols: (0, nest_wiring_util_1.coreLibSymbols)(),
17
+ importLines: (0, nest_wiring_util_1.coreImportLines)(),
18
+ entries: [(0, nest_wiring_util_1.configModuleEntry)(), (0, nest_wiring_util_1.brokerModuleEntry)()],
97
19
  });
98
- autoPublish = picked.includes('auto-config-publish');
99
- if (autoPublish) {
100
- names.serviceName = await input({ message: 'Service name (route ownership + AMQP connection_name)', default: d.serviceName });
101
- names.routeExchange = await input({ message: 'Route-discovery exchange', default: d.routeExchange });
102
- names.routeQueue = await input({ message: 'Route-sync queue', default: d.routeQueue });
20
+ if (!modulePath) {
21
+ context.logger.warn('[nest-add] app.module.ts not found — wire ConfigModule + BrokerModule.forRootAsync manually.');
103
22
  }
104
- }
105
- const skills = await confirm({ message: 'Copy the Claude skills into .claude/skills?', default: true });
106
- return { gatewayConfig, acl, admin, routeReception, autoPublish, skills, names };
23
+ (0, nest_wiring_util_1.addDeps)(tree, { '@nestjs/config': '^4.0.4', 'js-yaml': '^4.1.0' });
24
+ context.logger.info('[nest-add] core ready. Use `add-gateway`, `add-topic`, `add-route`, `enable-retry`, to add features.');
25
+ return skills ? copySkills() : (0, schematics_1.noop)();
26
+ };
107
27
  }
108
- function buildConfigYaml(sel) {
109
- const n = sel.names;
110
- const anyAdmin = sel.admin || sel.routeReception;
111
- const routeDiscoveryPub = sel.autoPublish ? `
112
- # Route auto-discovery (publisher side): announce this service's @BrokerHTTP routes on boot.
113
- # serviceName also fills the AMQP connection_name (none is set explicitly below).
114
- routeDiscovery:
115
- serviceName: "${n.serviceName}"
116
- publishOnBoot: true
117
- exchange: ${n.routeExchange}
118
- queue: ${n.routeQueue}` : '';
119
- const clientProps = sel.autoPublish ? '' : `
120
- clientProperties:
121
- connection_name: "<APP_NAME>"`;
122
- const exchanges = [];
123
- if (sel.acl || anyAdmin) {
124
- exchanges.push(` - name: ${n.exchange}
125
- type: "direct"
126
- createExchangeIfNotExists: true
127
- options:
128
- durable: true`);
129
- }
130
- exchanges.push(` - name: example.fanout
131
- type: "fanout"
132
- createExchangeIfNotExists: true
133
- options:
134
- durable: true
135
- autoDelete: false
136
- internal: false`);
137
- const queues = [];
138
- if (sel.acl) {
139
- queues.push(` - name: ${n.aclQueue}
140
- exchange: ${n.exchange}
141
- routingKey: ${n.aclQueue}
142
- createQueueIfNotExists: true
143
- options:
144
- durable: true`);
145
- }
146
- if (anyAdmin) {
147
- queues.push(` - name: ${n.adminQueue}
148
- exchange: ${n.exchange}
149
- routingKey: ${n.adminQueue}
150
- createQueueIfNotExists: true
151
- options:
152
- durable: true`);
153
- }
154
- queues.push(` - name: example.queue
155
- exchange: example.fanout
156
- routingKey: example.queue
157
- createQueueIfNotExists: true
158
- options:
159
- durable: true
160
- autoDelete: false
161
- exclusive: false`);
162
- const topics = [];
163
- if (sel.acl) {
164
- topics.push(` # Fixed topic name (decorator-bound in the lib): the ACL handlers bind to 'rlb-acl'.
165
- - name: rlb-acl
166
- mode: rpc
167
- queue: ${n.aclQueue}
168
- exchange: ${n.exchange}
169
- routingKey: ${n.aclQueue}`);
170
- }
171
- if (anyAdmin) {
172
- topics.push(` # Fixed topic name (decorator-bound): the gateway-admin handlers bind to 'rlb-gateway-admin'.
173
- - name: rlb-gateway-admin
174
- mode: rpc
175
- queue: ${n.adminQueue}
176
- exchange: ${n.exchange}
177
- routingKey: ${n.adminQueue}
178
- # Broadcast control topic: the gateway rebuilds its routes at runtime on a 'gw-reload'.
179
- - name: ${n.controlTopic}
180
- mode: broadcast
181
- exchange: ${n.exchange}
182
- routingKey: ${n.controlTopic}`);
183
- }
184
- topics.push(` - name: example.topic
185
- exchange: example.fanout
186
- routingKey: "example.topic"
187
- mode: event`);
188
- let yaml = `app:
28
+ function ensureBaseConfig(tree, preferred, context) {
29
+ const configPath = (0, yaml_config_util_1.findConfigYaml)(tree, preferred);
30
+ if (!tree.exists(configPath)) {
31
+ tree.create(configPath, BASE_CONFIG_YAML);
32
+ context.logger.info(`[nest-add] created ${configPath}.`);
33
+ return;
34
+ }
35
+ const { doc } = (0, yaml_config_util_1.readConfigDoc)(tree, configPath);
36
+ if (!doc.hasIn(['app']))
37
+ (0, yaml_config_util_1.setIn)(doc, ['app'], { port: 80, host: '0.0.0.0', environment: 'development' });
38
+ if (!doc.hasIn(['broker']))
39
+ (0, yaml_config_util_1.setIn)(doc, ['broker'], { name: 'rabbitmq', uri: '<AMQP_URI>' });
40
+ if (!doc.hasIn(['topics']))
41
+ (0, yaml_config_util_1.setIn)(doc, ['topics'], []);
42
+ (0, yaml_config_util_1.writeConfigDoc)(tree, configPath, doc);
43
+ }
44
+ const BASE_CONFIG_YAML = `app:
189
45
  port: 80
190
46
  host: 0.0.0.0
191
47
  environment: "development"
192
48
 
193
- auth-providers: []
194
-
195
49
  broker:
196
50
  name: "rabbitmq"
197
51
  uri: "<AMQP_URI>"
198
- defaultSubscribeErrorBehavior: "ack"
199
- defaultPublishErrorBehavior: "reject"${routeDiscoveryPub}
200
52
  connectionManagerOptions:
201
53
  heartbeatIntervalInSeconds: 60
202
54
  reconnectTimeInSeconds: 60
203
- connectionOptions:${clientProps}
55
+ connectionOptions:
56
+ clientProperties:
57
+ # Logical name — the library appends -<hostname>-<pid> per instance.
58
+ connection_name: "<APP_NAME>"
204
59
  credentials:
205
60
  mechanism: PLAIN
206
61
  username: "<AMQP_USERNAME>"
207
62
  password: "<AMQP_PASSWORD>"
208
63
  exchanges:
209
- ${exchanges.join('\n')}
64
+ - name: example.fanout
65
+ type: "fanout"
66
+ createExchangeIfNotExists: true
67
+ options:
68
+ durable: true
210
69
  queues:
211
- ${queues.join('\n')}
70
+ - name: example.queue
71
+ exchange: example.fanout
72
+ routingKey: example.queue
73
+ createQueueIfNotExists: true
74
+ options:
75
+ durable: true
212
76
 
213
77
  topics:
214
- ${topics.join('\n')}
78
+ - name: example.topic
79
+ exchange: example.fanout
80
+ routingKey: "example.topic"
81
+ mode: event
215
82
  `;
216
- if (sel.gatewayConfig) {
217
- yaml += '\n' + buildGatewayBlock(sel);
218
- }
219
- return yaml;
220
- }
221
- function buildGatewayBlock(sel) {
222
- const n = sel.names;
223
- const anyAdmin = sel.admin || sel.routeReception;
224
- const paths = [];
225
- if (sel.acl)
226
- paths.push(ACL_PATHS);
227
- if (sel.admin)
228
- paths.push(adminPaths(n.controlTopic));
229
- paths.push(` - name: example-path
230
- method: POST
231
- dataSource: body
232
- path: /example
233
- topic: example.topic
234
- action: example-action
235
- mode: event`);
236
- let block = `gateway:
237
- events: []
238
- ws:
239
- heartbeatIntervalMs: 30000
240
- # Auth is declared per-event (events[].auth / requireAuth / actions / scopeClaim).
241
- paths:
242
- ${paths.join('\n')}`;
243
- if (anyAdmin) {
244
- block += `
245
- # Load DB-managed routes at boot AND on every runtime reload (ordered static-before-param).
246
- loadConfig:
247
- paths:
248
- topic: rlb-gateway-admin
249
- action: gw-path-export
250
- # Broadcast topic that triggers a runtime route rebuild (see topics: ${n.controlTopic}).
251
- reloadTopic: ${n.controlTopic}
252
- # Per-request metrics auto-emitted to the gateway-admin metrics handler.
253
- metrics:
254
- topic: rlb-gateway-admin
255
- action: gw-metrics-track`;
256
- }
257
- return block + '\n';
258
- }
259
- const ACL_PATHS = ` # --- ACL management: actions (name is the key — PUT upserts, GET lists, DELETE by name) ---
260
- - name: acl-action-list
261
- method: GET
262
- path: /acl/actions
263
- dataSource: query
264
- topic: rlb-acl
265
- action: acl-action-list
266
- mode: rpc
267
- - name: acl-action-get
268
- method: GET
269
- path: /acl/actions/get
270
- dataSource: query
271
- topic: rlb-acl
272
- action: acl-action-get
273
- mode: rpc
274
- - name: acl-action-upsert
275
- method: PUT
276
- path: /acl/actions
277
- dataSource: body
278
- topic: rlb-acl
279
- action: acl-action-update
280
- mode: rpc
281
- - name: acl-action-delete
282
- method: DELETE
283
- path: /acl/actions
284
- dataSource: body
285
- topic: rlb-acl
286
- action: acl-action-delete
287
- mode: rpc
288
- - name: acl-action-search # query: ?q=&limit=
289
- method: GET
290
- path: /acl/actions/search
291
- dataSource: query
292
- topic: rlb-acl
293
- action: acl-action-search
294
- mode: rpc
295
- # --- ACL management: roles ---
296
- - name: acl-role-list
297
- method: GET
298
- path: /acl/roles
299
- dataSource: query
300
- topic: rlb-acl
301
- action: acl-role-list
302
- mode: rpc
303
- - name: acl-role-get
304
- method: GET
305
- path: /acl/roles/get
306
- dataSource: query
307
- topic: rlb-acl
308
- action: acl-role-get
309
- mode: rpc
310
- - name: acl-role-upsert
311
- method: PUT
312
- path: /acl/roles
313
- dataSource: body
314
- topic: rlb-acl
315
- action: acl-role-update
316
- mode: rpc
317
- - name: acl-role-delete
318
- method: DELETE
319
- path: /acl/roles
320
- dataSource: body
321
- topic: rlb-acl
322
- action: acl-role-delete
323
- mode: rpc
324
- - name: acl-role-search # query: ?q=&limit=
325
- method: GET
326
- path: /acl/roles/search
327
- dataSource: query
328
- topic: rlb-acl
329
- action: acl-role-search
330
- mode: rpc
331
- # --- ACL grants (per-user; resourceId/companyId optional) ---
332
- - name: acl-grant
333
- method: POST
334
- path: /acl/grants
335
- dataSource: body
336
- topic: rlb-acl
337
- action: acl-grant
338
- mode: rpc
339
- - name: acl-revoke
340
- method: DELETE
341
- path: /acl/grants
342
- dataSource: body
343
- topic: rlb-acl
344
- action: acl-revoke
345
- mode: rpc
346
- - name: acl-grant-search # access search; query: ?q=&limit=
347
- method: GET
348
- path: /acl/grants/search
349
- dataSource: query
350
- topic: rlb-acl
351
- action: acl-grant-search
352
- mode: rpc
353
- # --- ACL check (GET → 200 with true/false) ---
354
- # checkAction(userId, {companyId?, resourceId?}, action): true if the user holds the action
355
- # via any role on the EXACT (companyId, resourceId). companyId/resourceId are optional;
356
- # when both are absent the check matches resource-less grants only (no wildcard).
357
- - name: acl-check-action
358
- method: GET
359
- path: /acl/check
360
- dataSource: query
361
- topic: rlb-acl
362
- action: acl-check-action
363
- mode: rpc
364
- # Lists the caller's accessible resources. Add an 'auth: <provider>' line once you declare an auth-provider.
365
- - name: acl-list-resources-by-user
366
- method: GET
367
- path: /acl/resources
368
- dataSource: query
369
- topic: rlb-acl
370
- action: acl-list-resources-by-user
371
- mode: rpc`;
372
- function adminPaths(controlTopic) {
373
- return ` # --- gateway-admin: health + DB routes + auth-providers + metrics ---
374
- - name: health
375
- method: GET
376
- path: /health
377
- dataSource: query
378
- topic: rlb-gateway-admin
379
- action: gw-health
380
- mode: rpc
381
- - name: gw-path-create
382
- method: POST
383
- path: /admin/paths
384
- dataSource: body
385
- topic: rlb-gateway-admin
386
- action: gw-path-create
387
- mode: rpc
388
- - name: gw-path-list
389
- method: GET
390
- path: /admin/paths
391
- dataSource: query
392
- topic: rlb-gateway-admin
393
- action: gw-path-list
394
- mode: rpc
395
- - name: gw-path-search # query: ?q=&limit=
396
- method: GET
397
- path: /admin/paths/search
398
- dataSource: query
399
- topic: rlb-gateway-admin
400
- action: gw-path-search
401
- mode: rpc
402
- - name: gw-path-export
403
- method: GET
404
- path: /admin/paths/export
405
- dataSource: query
406
- topic: rlb-gateway-admin
407
- action: gw-path-export
408
- mode: rpc
409
- - name: gw-path-update
410
- method: PUT
411
- path: /admin/paths
412
- dataSource: body
413
- topic: rlb-gateway-admin
414
- action: gw-path-update
415
- mode: rpc
416
- - name: gw-path-get
417
- method: GET
418
- path: /admin/paths/get
419
- dataSource: query
420
- topic: rlb-gateway-admin
421
- action: gw-path-get
422
- mode: rpc
423
- - name: gw-path-delete
424
- method: DELETE
425
- path: /admin/paths
426
- dataSource: body
427
- topic: rlb-gateway-admin
428
- action: gw-path-delete
429
- mode: rpc
430
- # Route-change journal (who changed what): actor=system for auto-discovery, userId for manual CRUD.
431
- - name: gw-route-log-list
432
- method: GET
433
- path: /admin/route-log
434
- dataSource: query
435
- topic: rlb-gateway-admin
436
- action: gw-route-log-list
437
- mode: rpc
438
- - name: gw-route-log-search # filtered+paginated; query: ?actor=&service=&event=&routeKey=&from=&to=&page=&limit=
439
- method: GET
440
- path: /admin/route-log/search
441
- dataSource: query
442
- topic: rlb-gateway-admin
443
- action: gw-route-log-search
444
- mode: rpc
445
- - name: gw-auth-list
446
- method: GET
447
- path: /admin/auth
448
- dataSource: query
449
- topic: rlb-gateway-admin
450
- action: gw-auth-list
451
- mode: rpc
452
- - name: gw-auth-search # query: ?q=&limit=
453
- method: GET
454
- path: /admin/auth/search
455
- dataSource: query
456
- topic: rlb-gateway-admin
457
- action: gw-auth-search
458
- mode: rpc
459
- - name: gw-auth-upsert
460
- method: PUT
461
- path: /admin/auth
462
- dataSource: body
463
- topic: rlb-gateway-admin
464
- action: gw-auth-update
465
- mode: rpc
466
- - name: gw-auth-get
467
- method: GET
468
- path: /admin/auth/get
469
- dataSource: query
470
- topic: rlb-gateway-admin
471
- action: gw-auth-get
472
- mode: rpc
473
- - name: gw-auth-delete
474
- method: DELETE
475
- path: /admin/auth
476
- dataSource: body
477
- topic: rlb-gateway-admin
478
- action: gw-auth-delete
479
- mode: rpc
480
- - name: gw-metrics-get
481
- method: GET
482
- path: /admin/metrics
483
- dataSource: query
484
- topic: rlb-gateway-admin
485
- action: gw-metrics-get
486
- mode: rpc
487
- - name: gw-metrics-series
488
- method: GET
489
- path: /admin/metrics/series
490
- dataSource: query
491
- topic: rlb-gateway-admin
492
- action: gw-metrics-series
493
- mode: rpc
494
- - name: gw-metrics-points
495
- method: GET
496
- path: /admin/metrics/points
497
- dataSource: query
498
- topic: rlb-gateway-admin
499
- action: gw-metrics-points
500
- mode: rpc
501
- - name: gw-metrics-summary # dashboard overview; query: ?from=&to=&method=&route=&name=&topN=
502
- method: GET
503
- path: /admin/metrics/summary
504
- dataSource: query
505
- topic: rlb-gateway-admin
506
- action: gw-metrics-summary
507
- mode: rpc
508
- - name: gw-metrics-prometheus # Prometheus text exposition (returned as text/plain)
509
- method: GET
510
- path: /admin/metrics/prometheus
511
- dataSource: query
512
- topic: rlb-gateway-admin
513
- action: gw-metrics-prometheus
514
- mode: rpc
515
- headers:
516
- Content-Type: text/plain; version=0.0.4
517
- - name: gw-metrics-rollups # long-term downsampled aggregates; query: ?from=&to=&method=&route=
518
- method: GET
519
- path: /admin/metrics/rollups
520
- dataSource: query
521
- topic: rlb-gateway-admin
522
- action: gw-metrics-rollups
523
- mode: rpc
524
- - name: gw-metrics-track
525
- method: POST
526
- path: /admin/metrics/track
527
- dataSource: body
528
- topic: rlb-gateway-admin
529
- action: gw-metrics-track
530
- mode: event
531
- - name: gw-reload
532
- method: POST
533
- path: /admin/reload
534
- dataSource: body
535
- topic: ${controlTopic}
536
- action: gw-reload
537
- mode: event
538
- - name: gw-auth-reload # deliberate: reload DB auth-providers into RAM (separate from gw-reload)
539
- method: POST
540
- path: /admin/auth/reload
541
- dataSource: body
542
- topic: ${controlTopic}
543
- action: gw-auth-reload
544
- mode: event`;
545
- }
546
- function buildImportStatements(sel) {
547
- const libSymbols = ['AppConfig', 'BrokerModule', 'BrokerTopic', 'RabbitMQConfig'];
548
- if (sel.gatewayConfig)
549
- libSymbols.push('GatewayConfig', 'HandlerAuthConfig', 'ProxyModule');
550
- if (sel.acl)
551
- libSymbols.push('AclActionRepository', 'AclGrantRepository', 'AclModule', 'AclRoleRepository', 'AclService', 'RLB_ACL_CACHE_STORE', 'RLB_GTW_ACL_ROLE_SERVICE');
552
- if (sel.admin || sel.routeReception)
553
- libSymbols.push('AuthProviderRepository', 'GatewayAdminModule', 'HttpMetricRepository', 'HttpPathRepository', 'RouteSyncLogRepository');
554
- if (sel.admin && sel.gatewayConfig)
555
- libSymbols.push('RLB_GTW_AUTH_PROVIDER_SOURCE');
556
- const lib = `import { ${[...new Set(libSymbols)].sort().join(', ')} } from '@open-rlb/nestjs-amqp';`;
557
- const lines = [lib, `import { ConfigModule, ConfigService } from '@nestjs/config';`, `import yamlConfig from './config/config.loader';`];
558
- if (sel.gatewayConfig)
559
- lines.push(`import { HttpModule } from '@nestjs/axios';`);
560
- if (sel.acl) {
561
- lines.push(`import { InMemoryAclActionRepository, InMemoryAclGrantRepository, InMemoryAclRoleRepository } from './modules/database/repository/acl.repository';`);
562
- lines.push(`import { InMemoryAclStore } from './cache/in-memory-acl-store';`);
563
- }
564
- if (sel.admin || sel.routeReception) {
565
- lines.push(`import { InMemoryAuthProviderRepository, InMemoryHttpMetricRepository, InMemoryHttpPathRepository } from './modules/database/repository/gateway.repository';`);
566
- lines.push(`import { InMemoryRouteSyncLogRepository } from './modules/database/repository/route-sync.repository';`);
567
- }
568
- return lines.join('\n');
569
- }
570
- function brokerForRootAsync() {
571
- return `BrokerModule.forRootAsync({
572
- imports: [ConfigModule],
573
- inject: [ConfigService],
574
- useFactory: async (configService: ConfigService) => ({
575
- options: configService.get<RabbitMQConfig>('broker')!,
576
- topics: configService.get<BrokerTopic[]>('topics')!,
577
- appOptions: configService.get<AppConfig>('app'),
578
- })
579
- })`;
580
- }
581
- function proxyForRootAsync(sel) {
582
- const provs = [];
583
- if (sel.acl)
584
- provs.push(` // Action-gated paths resolve the caller's identity via AclService (in-process, no broker hop).
585
- { provide: RLB_GTW_ACL_ROLE_SERVICE, useExisting: AclService },`);
586
- if (sel.admin)
587
- provs.push(` // DB auth-provider source for the runtime registry; activated by a deliberate gw-auth-reload.
588
- { provide: RLB_GTW_AUTH_PROVIDER_SOURCE, useExisting: InMemoryAuthProviderRepository },`);
589
- const providers = provs.length ? `[\n${provs.join('\n')}\n ]` : `[]`;
590
- return `ProxyModule.forRootAsync({
591
- imports: [ConfigModule],
592
- inject: [ConfigService],
593
- useFactory: (configService: ConfigService) => ({
594
- authOptions: configService.get<HandlerAuthConfig[]>('auth-providers'),
595
- gatewayOptions: configService.get<GatewayConfig>('gateway'),
596
- }),
597
- providers: ${providers},
598
- })`;
599
- }
600
- function aclForRoot() {
601
- return `AclModule.forRoot(
602
- [
603
- InMemoryAclActionRepository,
604
- { provide: AclActionRepository, useExisting: InMemoryAclActionRepository },
605
- InMemoryAclRoleRepository,
606
- { provide: AclRoleRepository, useExisting: InMemoryAclRoleRepository },
607
- InMemoryAclGrantRepository,
608
- { provide: AclGrantRepository, useExisting: InMemoryAclGrantRepository },
609
- InMemoryAclStore,
610
- { provide: RLB_ACL_CACHE_STORE, useExisting: InMemoryAclStore },
611
- ],
612
- { cache: { ramTtlMs: 30000, l2TtlSec: 600 } },
613
- )`;
614
- }
615
- function gatewayAdminForRoot(sel) {
616
- const options = sel.routeReception
617
- ? `,
618
- {
619
- // Consumer-side route-discovery — names MUST match the publishers' broker.routeDiscovery.
620
- routeDiscovery: { exchange: '${sel.names.routeExchange}', queue: '${sel.names.routeQueue}' },
621
- }`
622
- : '';
623
- return `GatewayAdminModule.forRoot(
624
- [
625
- InMemoryHttpPathRepository,
626
- { provide: HttpPathRepository, useExisting: InMemoryHttpPathRepository },
627
- InMemoryAuthProviderRepository,
628
- { provide: AuthProviderRepository, useExisting: InMemoryAuthProviderRepository },
629
- InMemoryHttpMetricRepository,
630
- { provide: HttpMetricRepository, useExisting: InMemoryHttpMetricRepository },
631
- InMemoryRouteSyncLogRepository,
632
- { provide: RouteSyncLogRepository, useExisting: InMemoryRouteSyncLogRepository },
633
- ]${options},
634
- )`;
635
- }
636
- function buildModuleEntries(sel) {
637
- const entries = [];
638
- entries.push(`ConfigModule.forRoot({ isGlobal: true, load: [yamlConfig] })`);
639
- entries.push(brokerForRootAsync());
640
- if (sel.gatewayConfig) {
641
- entries.push('HttpModule');
642
- entries.push(proxyForRootAsync(sel));
643
- }
644
- if (sel.acl)
645
- entries.push(aclForRoot());
646
- if (sel.admin || sel.routeReception)
647
- entries.push(gatewayAdminForRoot(sel));
648
- return entries;
649
- }
650
- function main(options) {
651
- const project = (options.project || 'my-service').toString();
652
- options = transform(options);
653
- return async (tree, context) => {
654
- const sel = await resolveSelections({ ...options, project }, context);
655
- const anyRepo = sel.acl || sel.admin || sel.routeReception;
656
- return (0, schematics_1.branchAndMerge)((0, schematics_1.chain)([
657
- (0, source_root_helpers_1.mergeSourceRoot)(options),
658
- addModulesToAppModule(sel),
659
- createConfigLoader(),
660
- updateConfigYaml(sel),
661
- sel.gatewayConfig ? configureMainForGateway() : (0, schematics_1.noop)(),
662
- anyRepo ? copyAsset('db-core') : (0, schematics_1.noop)(),
663
- sel.acl ? copyAsset('acl') : (0, schematics_1.noop)(),
664
- (sel.admin || sel.routeReception) ? copyAsset('gateway-admin') : (0, schematics_1.noop)(),
665
- sel.skills ? copySkills() : (0, schematics_1.noop)(),
666
- updatePackageJson(sel),
667
- ]));
668
- };
669
- }
670
- function transform(source) {
671
- const target = Object.assign({}, source);
672
- target.metadata = 'providers';
673
- target.type = 'module';
674
- target.language = 'ts';
675
- const location = new name_parser_1.NameParser().parse({ ...target, name: 'init' });
676
- target.name = '';
677
- target.path = (0, formatting_1.normalizeToKebabOrSnakeCase)(location.path);
678
- target.specFileSuffix = (0, formatting_1.normalizeToKebabOrSnakeCase)(source.specFileSuffix || 'spec');
679
- return target;
680
- }
681
- function copyAsset(name) {
682
- return (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)(`./files/${name}`), [(0, schematics_1.move)((0, path_1.normalize)('.'))]), schematics_1.MergeStrategy.Overwrite);
683
- }
684
83
  function copySkills() {
685
84
  return (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)('./files/skills'), [(0, schematics_1.move)((0, path_1.normalize)('.claude/skills'))]), schematics_1.MergeStrategy.Overwrite);
686
85
  }
687
- function createConfigLoader() {
688
- return (tree) => {
689
- const path = 'src/config/config.loader.ts';
690
- if (tree.exists(path))
691
- return tree;
692
- tree.create(path, `import { readFileSync } from 'fs';
693
- import * as yaml from 'js-yaml';
694
- import { join } from 'path';
695
-
696
- const YAML_CONFIG_FILENAME = 'config/config.yaml';
697
-
698
- export default () =>
699
- yaml.load(readFileSync(join(process.cwd(), YAML_CONFIG_FILENAME), 'utf8')) as Record<string, any>;
700
- `);
701
- return tree;
702
- };
703
- }
704
- function updateConfigYaml(sel) {
705
- return (tree) => {
706
- const CONFIG_PATH = 'config/config.yaml';
707
- const block = buildConfigYaml(sel);
708
- if (!tree.exists(CONFIG_PATH)) {
709
- tree.create(CONFIG_PATH, block);
710
- return tree;
711
- }
712
- const existing = tree.read(CONFIG_PATH).toString('utf-8');
713
- const SECTION_KEYS = ['app:', 'auth-providers:', 'broker:', 'topics:', ...(sel.gatewayConfig ? ['gateway:'] : [])];
714
- let toAppend = '';
715
- for (const key of SECTION_KEYS) {
716
- if (!existing.includes(key)) {
717
- const section = extractYamlSection(block, key);
718
- if (section)
719
- toAppend += '\n' + section + '\n';
720
- }
721
- }
722
- if (toAppend.length > 0) {
723
- tree.overwrite(CONFIG_PATH, existing.trimEnd() + '\n' + toAppend);
724
- }
725
- return tree;
726
- };
727
- }
728
- function extractYamlSection(yaml, sectionKey) {
729
- const lines = yaml.split('\n');
730
- const startIdx = lines.findIndex((l) => l.startsWith(sectionKey));
731
- if (startIdx === -1)
732
- return '';
733
- const endIdx = lines.findIndex((l, i) => i > startIdx && l.length > 0 && !l.startsWith(' ') && !l.startsWith('#'));
734
- const sectionLines = endIdx === -1 ? lines.slice(startIdx) : lines.slice(startIdx, endIdx);
735
- return sectionLines.join('\n').trimEnd();
736
- }
737
- function addModulesToAppModule(sel) {
738
- return (tree) => {
739
- const candidatePaths = ['/src/app.module.ts', '/app/app.module.ts', 'src/app.module.ts', 'app/app.module.ts'];
740
- let modulePath = candidatePaths.find((p) => tree.exists(p)) || findFileInTree(tree, 'app.module.ts');
741
- if (!modulePath) {
742
- console.warn('[nest-add] app.module.ts not found: AppModule wiring skipped.');
743
- return tree;
744
- }
745
- const raw = tree.read(modulePath);
746
- if (!raw)
747
- return tree;
748
- let content = raw.toString('utf-8');
749
- if (content.includes('BrokerModule.forRootAsync'))
750
- return tree;
751
- if (!content.includes('@open-rlb/nestjs-amqp')) {
752
- const pos = findLastImportEndIndex(content);
753
- content = content.slice(0, pos) + '\n' + buildImportStatements(sel) + content.slice(pos);
754
- }
755
- const entries = buildModuleEntries(sel);
756
- for (let i = entries.length - 1; i >= 0; i--) {
757
- content = insertIntoImportsArray(content, entries[i]);
758
- }
759
- tree.overwrite(modulePath, content);
760
- return tree;
761
- };
762
- }
763
- function configureMainForGateway() {
764
- return (tree) => {
765
- const candidatePaths = ['/src/main.ts', '/app/main.ts', 'src/main.ts', 'app/main.ts'];
766
- const mainPath = candidatePaths.find((p) => tree.exists(p)) || findFileInTree(tree, 'main.ts');
767
- if (!mainPath) {
768
- console.warn('[nest-add] main.ts not found: enable rawBody + WsAdapter manually.');
769
- return tree;
770
- }
771
- let content = tree.read(mainPath).toString('utf-8');
772
- if (content.includes('WsAdapter'))
773
- return tree;
774
- const pos = findLastImportEndIndex(content);
775
- content = content.slice(0, pos) + "\nimport { WsAdapter } from '@nestjs/platform-ws';" + content.slice(pos);
776
- const m = content.match(/const\s+(\w+)\s*=\s*await\s+NestFactory\.create\(\s*([A-Za-z0-9_]+)\s*(,\s*\{[^}]*\})?\s*\)\s*;?/);
777
- if (m) {
778
- const appVar = m[1];
779
- const moduleArg = m[2];
780
- const replacement = `const ${appVar} = await NestFactory.create(${moduleArg}, { rawBody: true });\n ${appVar}.useWebSocketAdapter(new WsAdapter(${appVar}));\n ${appVar}.enableShutdownHooks();`;
781
- content = content.replace(m[0], replacement);
782
- }
783
- else {
784
- console.warn('[nest-add] NestFactory.create() not found in main.ts: add { rawBody: true } + useWebSocketAdapter manually.');
785
- }
786
- tree.overwrite(mainPath, content);
787
- return tree;
788
- };
789
- }
790
- function insertIntoImportsArray(source, moduleEntry) {
791
- const match = /imports\s*:\s*\[/.exec(source);
792
- if (!match)
793
- return source;
794
- const openBracketPos = source.indexOf('[', match.index);
795
- let depth = 0;
796
- let closeBracketPos = -1;
797
- for (let i = openBracketPos; i < source.length; i++) {
798
- if (source[i] === '[')
799
- depth++;
800
- else if (source[i] === ']') {
801
- depth--;
802
- if (depth === 0) {
803
- closeBracketPos = i;
804
- break;
805
- }
806
- }
807
- }
808
- if (closeBracketPos === -1)
809
- return source;
810
- const arrayContent = source.slice(openBracketPos + 1, closeBracketPos).trim();
811
- const newArrayContent = arrayContent.length === 0
812
- ? `\n ${moduleEntry},\n `
813
- : `\n ${moduleEntry},\n ${arrayContent}\n `;
814
- return source.slice(0, openBracketPos + 1) + newArrayContent + source.slice(closeBracketPos);
815
- }
816
- function findLastImportEndIndex(source) {
817
- const importRegex = /^import\s+.+from\s+['"][^'"]+['"];?\s*$/gm;
818
- let lastEnd = 0;
819
- let match;
820
- while ((match = importRegex.exec(source)) !== null) {
821
- lastEnd = match.index + match[0].length;
822
- }
823
- return lastEnd;
824
- }
825
- function findFileInTree(tree, fileName) {
826
- let found;
827
- tree.visit((path) => {
828
- if (!found && path.endsWith(`/${fileName}`))
829
- found = path;
830
- });
831
- return found;
832
- }
833
- function updatePackageJson(sel) {
834
- return (host) => {
835
- if (!host.exists('package.json'))
836
- return host;
837
- return updateJsonFile(host, 'package.json', (packageJson) => {
838
- packageJson.dependencies = packageJson.dependencies || {};
839
- const add = (name, version) => {
840
- if (!packageJson.dependencies[name])
841
- packageJson.dependencies[name] = version;
842
- };
843
- add('@nestjs/config', '^4.0.4');
844
- add('js-yaml', '^4.1.0');
845
- if (sel.gatewayConfig) {
846
- add('@nestjs/axios', '^4.0.1');
847
- add('@nestjs/platform-ws', '^11.0.1');
848
- add('@nestjs/websockets', '^11.0.1');
849
- add('ws', '^8.21.0');
850
- }
851
- });
852
- };
853
- }
854
- function updateJsonFile(host, path, callback) {
855
- const source = host.read(path);
856
- if (source) {
857
- const json = (0, jsonc_parser_1.parse)(source.toString('utf-8'));
858
- callback(json);
859
- host.overwrite(path, JSON.stringify(json, null, 2));
860
- }
861
- return host;
862
- }
863
86
  //# sourceMappingURL=index.js.map