@idevconn/create-icore 0.5.0 → 0.5.1

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 (39) hide show
  1. package/dist/cli.js +84 -25
  2. package/dist/index.cjs +81 -24
  3. package/dist/index.d.cts +7 -1
  4. package/dist/index.d.ts +7 -1
  5. package/dist/index.js +80 -24
  6. package/package.json +1 -1
  7. package/templates/apps/api/.env.example +14 -0
  8. package/templates/apps/microservices/auth/package.json +1 -1
  9. package/templates/apps/microservices/auth/src/app/app.module.ts +17 -30
  10. package/templates/apps/microservices/auth/src/main.ts +6 -23
  11. package/templates/apps/microservices/jobs/src/app/redis-connection.ts +35 -0
  12. package/templates/apps/microservices/jobs/src/app/workers/cleanup.worker.ts +2 -1
  13. package/templates/apps/microservices/jobs/src/app/workers/email.worker.ts +2 -1
  14. package/templates/apps/microservices/jobs/src/app/workers/image-process.worker.ts +2 -1
  15. package/templates/apps/microservices/notes/src/app/app.module.ts +22 -27
  16. package/templates/apps/microservices/notes/src/main.ts +6 -23
  17. package/templates/apps/microservices/payment/src/app/app.module.ts +6 -4
  18. package/templates/apps/microservices/payment/src/main.ts +6 -23
  19. package/templates/apps/microservices/upload/package.json +1 -1
  20. package/templates/apps/microservices/upload/src/app/app.module.ts +18 -30
  21. package/templates/apps/microservices/upload/src/main.ts +6 -23
  22. package/templates/libs/firebase-admin/README.md +11 -0
  23. package/templates/libs/firebase-admin/eslint.config.mjs +24 -0
  24. package/templates/libs/firebase-admin/package.json +12 -0
  25. package/templates/libs/firebase-admin/project.json +19 -0
  26. package/templates/libs/firebase-admin/src/index.ts +1 -0
  27. package/templates/libs/firebase-admin/src/lib/__tests__/firebase-admin.unit.test.ts +105 -0
  28. package/templates/libs/firebase-admin/src/lib/firebase-admin.ts +70 -0
  29. package/templates/libs/firebase-admin/tsconfig.json +23 -0
  30. package/templates/libs/firebase-admin/tsconfig.lib.json +23 -0
  31. package/templates/libs/firebase-admin/tsconfig.spec.json +22 -0
  32. package/templates/libs/firebase-admin/vitest.config.mts +21 -0
  33. package/templates/libs/jobs-client/src/lib/jobs-client.service.ts +14 -2
  34. package/templates/libs/shared/src/__tests__/bootstrap.unit.test.ts +92 -0
  35. package/templates/libs/shared/src/__tests__/transport.unit.test.ts +14 -2
  36. package/templates/libs/shared/src/bootstrap.ts +79 -0
  37. package/templates/libs/shared/src/index.ts +1 -0
  38. package/templates/libs/shared/src/transport.ts +25 -3
  39. package/templates/tsconfig.base.json +2 -1
@@ -0,0 +1,79 @@
1
+ import { formatEnvBanner } from './env';
2
+
3
+ interface MicroserviceLike {
4
+ listen: () => Promise<unknown>;
5
+ close: () => Promise<void>;
6
+ }
7
+
8
+ interface LoggerLike {
9
+ log: (msg: string) => void;
10
+ warn: (msg: string) => void;
11
+ error: (msg: string, trace?: unknown) => void;
12
+ }
13
+
14
+ const RETRY_DELAY_MS = 3000;
15
+
16
+ function delay(ms: number): Promise<void> {
17
+ return new Promise((resolve) => setTimeout(resolve, ms));
18
+ }
19
+
20
+ /**
21
+ * Boots a microservice and binds its transport, surviving a broker that isn't
22
+ * up yet.
23
+ *
24
+ * NestJS `ServerRedis`/`ServerNats` REJECT `app.listen()` on the *initial*
25
+ * connect failure — the ioredis retryStrategy / NATS reconnect only governs
26
+ * re-connection after a first successful connect. So without this helper a MS
27
+ * would `process.exit(1)` whenever Redis/NATS is down on boot, even though the
28
+ * transport options ask for infinite retries.
29
+ *
30
+ * Behaviour (honours the "never crash on missing infra" rule):
31
+ * - tcp transport, or NODE_ENV=production → fail fast: `process.exit(1)`.
32
+ * - redis/nats in dev → log a boxed banner and retry `listen()` forever, so
33
+ * the service idles until the broker appears, then binds automatically.
34
+ *
35
+ * `createApp` must construct a *fresh* app each call: a failed listen closes
36
+ * the transport's clients, so the next attempt needs a new instance.
37
+ */
38
+ export async function bootstrapMicroservice(
39
+ prefix: string,
40
+ createApp: () => Promise<MicroserviceLike>,
41
+ logger: LoggerLike,
42
+ ): Promise<void> {
43
+ const transport = (process.env[`${prefix}_TRANSPORT`] ?? 'tcp').toLowerCase();
44
+ const isProd = process.env['NODE_ENV'] === 'production';
45
+ const failFast = transport === 'tcp' || isProd;
46
+
47
+ for (let attempt = 1; ; attempt++) {
48
+ let app: MicroserviceLike | undefined;
49
+ try {
50
+ app = await createApp();
51
+ await app.listen();
52
+ logger.log(`${prefix} microservice listening — transport=${transport}`);
53
+ return;
54
+ } catch (err) {
55
+ if (app) await app.close().catch(() => undefined);
56
+ const reason = err instanceof Error ? err.message : String(err);
57
+
58
+ if (failFast) {
59
+ logger.error(
60
+ `${prefix} microservice bootstrap failed`,
61
+ err instanceof Error ? err.stack : err,
62
+ );
63
+ process.exit(1);
64
+ }
65
+
66
+ logger.warn(
67
+ formatEnvBanner({
68
+ service: `${prefix} microservice`,
69
+ provider: transport,
70
+ missing: [],
71
+ envPath: `the service .env (${prefix}_${transport.toUpperCase()}_URL)`,
72
+ reason: `${reason} — retry ${attempt} in ${RETRY_DELAY_MS / 1000}s; idling until the ${transport} broker is reachable`,
73
+ headline: `⚠ ${prefix} microservice — ${transport} broker unreachable (idling, not crashing)`,
74
+ }),
75
+ );
76
+ await delay(RETRY_DELAY_MS);
77
+ }
78
+ }
79
+ }
@@ -1,4 +1,5 @@
1
1
  export * from './env';
2
+ export * from './bootstrap';
2
3
  export * from './abilities';
3
4
  export * from './jobs';
4
5
  export * from './strategies';
@@ -67,15 +67,37 @@ export function buildTransport(prefix: string): ClientOptions {
67
67
  // ioredis accepts a connection URL string; the NestJS RedisOptions type
68
68
  // exposes host/port fields but passes options directly to ioredis which
69
69
  // also accepts a url field at runtime.
70
+ //
71
+ // retryAttempts/retryDelay are mandatory for resilience: NestJS
72
+ // ServerRedis builds its ioredis retryStrategy from them, and when
73
+ // retryAttempts is unset it logs "retry attempts not specified", stops
74
+ // reconnecting, and `app.listen()` REJECTS → the MS process exits. With
75
+ // an effectively-infinite retry the initial connect stays pending and
76
+ // keeps reconnecting, so a not-yet-up Redis never crashes the service —
77
+ // it idles and attaches once Redis is reachable.
70
78
  return {
71
79
  transport: Transport.REDIS,
72
- options: { url: required(`${prefix}_REDIS_URL`) },
80
+ options: {
81
+ url: required(`${prefix}_REDIS_URL`),
82
+ retryAttempts: Number.POSITIVE_INFINITY,
83
+ retryDelay: 2000,
84
+ },
73
85
  } as unknown as ClientOptions;
74
86
  case 'nats':
87
+ // reconnect/maxReconnectAttempts: -1 retries forever once connected, so a
88
+ // dropped broker re-attaches instead of giving up. The *initial* connect
89
+ // is intentionally allowed to reject when NATS is down on boot —
90
+ // bootstrapMicroservice() catches that, logs a banner, and retries, giving
91
+ // the same visible behaviour as the Redis transport above.
75
92
  return {
76
93
  transport: Transport.NATS,
77
- options: { servers: required(`${prefix}_NATS_URL`).split(',') },
78
- };
94
+ options: {
95
+ servers: required(`${prefix}_NATS_URL`).split(','),
96
+ reconnect: true,
97
+ maxReconnectAttempts: -1,
98
+ reconnectTimeWait: 2000,
99
+ },
100
+ } as unknown as ClientOptions;
79
101
  default:
80
102
  throw new Error(`Unknown transport: ${kind}`);
81
103
  }
@@ -29,7 +29,8 @@
29
29
  "@icore/payment-client": ["./libs/payment-client/src/index.ts"],
30
30
  "@icore/notes-client": ["./libs/notes-client/src/index.ts"],
31
31
  "@icore/jobs-client": ["./libs/jobs-client/src/index.ts"],
32
- "@icore/vite-plugins": ["./libs/vite-plugins/src/index.d.mts"]
32
+ "@icore/vite-plugins": ["./libs/vite-plugins/src/index.d.mts"],
33
+ "@icore/firebase-admin": ["./libs/firebase-admin/src/index.ts"]
33
34
  }
34
35
  },
35
36
  "exclude": ["node_modules", "dist", ".nx"]