nmtjs 0.16.0-beta.9 → 0.16.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 (161) hide show
  1. package/dist/cli.js +2 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/entrypoints/worker.js +9 -9
  4. package/dist/entrypoints/worker.js.map +1 -1
  5. package/dist/index.d.ts +58 -15
  6. package/dist/index.js +5 -2
  7. package/dist/index.js.map +1 -1
  8. package/dist/runtime/application/index.d.ts +1 -5
  9. package/dist/runtime/application/index.js +1 -5
  10. package/dist/runtime/application/index.js.map +1 -1
  11. package/dist/runtime/enums.d.ts +1 -12
  12. package/dist/runtime/enums.js +1 -14
  13. package/dist/runtime/enums.js.map +1 -1
  14. package/dist/runtime/hooks.d.ts +1 -4
  15. package/dist/runtime/hooks.js +1 -3
  16. package/dist/runtime/hooks.js.map +1 -1
  17. package/dist/runtime/index.d.ts +1 -3
  18. package/dist/runtime/index.js +1 -3
  19. package/dist/runtime/index.js.map +1 -1
  20. package/dist/runtime/injectables.d.ts +48 -7
  21. package/dist/runtime/injectables.js +19 -3
  22. package/dist/runtime/injectables.js.map +1 -1
  23. package/dist/runtime/jobs/job.d.ts +1 -2
  24. package/dist/runtime/jobs/job.js.map +1 -1
  25. package/dist/runtime/jobs/router.d.ts +2 -5
  26. package/dist/runtime/jobs/router.js +2 -3
  27. package/dist/runtime/jobs/router.js.map +1 -1
  28. package/dist/runtime/jobs/runner.d.ts +1 -1
  29. package/dist/runtime/jobs/runner.js +1 -1
  30. package/dist/runtime/jobs/runner.js.map +1 -1
  31. package/dist/runtime/jobs/types.d.ts +1 -1
  32. package/dist/runtime/metrics/metric.d.ts +2 -2
  33. package/dist/runtime/metrics/metric.js +1 -1
  34. package/dist/runtime/metrics/metric.js.map +1 -1
  35. package/dist/runtime/metrics/registry.d.ts +3 -3
  36. package/dist/runtime/metrics/registry.js +1 -1
  37. package/dist/runtime/metrics/registry.js.map +1 -1
  38. package/dist/runtime/metrics/server.js +1 -1
  39. package/dist/runtime/metrics/server.js.map +1 -1
  40. package/dist/runtime/plugin.d.ts +2 -8
  41. package/dist/runtime/plugin.js +1 -3
  42. package/dist/runtime/plugin.js.map +1 -1
  43. package/dist/runtime/runtime.d.ts +2 -2
  44. package/dist/runtime/runtime.js +1 -2
  45. package/dist/runtime/runtime.js.map +1 -1
  46. package/dist/runtime/server/applications.js +3 -0
  47. package/dist/runtime/server/applications.js.map +1 -1
  48. package/dist/runtime/server/config.d.ts +15 -12
  49. package/dist/runtime/server/config.js.map +1 -1
  50. package/dist/runtime/server/jobs.d.ts +6 -7
  51. package/dist/runtime/server/jobs.js +44 -30
  52. package/dist/runtime/server/jobs.js.map +1 -1
  53. package/dist/runtime/subscription/manager.d.ts +3 -3
  54. package/dist/runtime/subscription/manager.js +8 -8
  55. package/dist/runtime/subscription/manager.js.map +1 -1
  56. package/dist/runtime/subscription/redis.d.ts +1 -1
  57. package/dist/runtime/subscription/redis.js +16 -17
  58. package/dist/runtime/subscription/redis.js.map +1 -1
  59. package/dist/runtime/types.d.ts +3 -11
  60. package/dist/runtime/workers/application.d.ts +15 -33
  61. package/dist/runtime/workers/application.js +46 -139
  62. package/dist/runtime/workers/application.js.map +1 -1
  63. package/dist/runtime/workers/base.d.ts +0 -2
  64. package/dist/runtime/workers/base.js +3 -8
  65. package/dist/runtime/workers/base.js.map +1 -1
  66. package/dist/runtime/workers/job.js +2 -1
  67. package/dist/runtime/workers/job.js.map +1 -1
  68. package/dist/vite/server.js +1 -1
  69. package/dist/vite/server.js.map +1 -1
  70. package/package.json +19 -14
  71. package/src/cli.ts +2 -3
  72. package/src/entrypoints/worker.ts +11 -9
  73. package/src/index.ts +12 -5
  74. package/src/runtime/application/index.ts +1 -5
  75. package/src/runtime/enums.ts +2 -14
  76. package/src/runtime/hooks.ts +1 -5
  77. package/src/runtime/index.ts +2 -3
  78. package/src/runtime/injectables.ts +35 -6
  79. package/src/runtime/jobs/job.ts +1 -2
  80. package/src/runtime/jobs/router.ts +12 -15
  81. package/src/runtime/jobs/runner.ts +2 -2
  82. package/src/runtime/jobs/types.ts +1 -1
  83. package/src/runtime/metrics/metric.ts +2 -2
  84. package/src/runtime/metrics/registry.ts +5 -1
  85. package/src/runtime/metrics/server.ts +1 -1
  86. package/src/runtime/plugin.ts +2 -13
  87. package/src/runtime/runtime.ts +2 -4
  88. package/src/runtime/server/applications.ts +6 -0
  89. package/src/runtime/server/config.ts +27 -21
  90. package/src/runtime/server/jobs.ts +58 -34
  91. package/src/runtime/subscription/manager.ts +11 -14
  92. package/src/runtime/subscription/redis.ts +25 -23
  93. package/src/runtime/types.ts +3 -12
  94. package/src/runtime/workers/application.ts +76 -160
  95. package/src/runtime/workers/base.ts +9 -15
  96. package/src/runtime/workers/job.ts +3 -6
  97. package/src/vite/server.ts +1 -1
  98. package/dist/runtime/application/api/api.d.ts +0 -55
  99. package/dist/runtime/application/api/api.js +0 -252
  100. package/dist/runtime/application/api/api.js.map +0 -1
  101. package/dist/runtime/application/api/config.d.ts +0 -14
  102. package/dist/runtime/application/api/config.js +0 -6
  103. package/dist/runtime/application/api/config.js.map +0 -1
  104. package/dist/runtime/application/api/constants.d.ts +0 -14
  105. package/dist/runtime/application/api/constants.js +0 -8
  106. package/dist/runtime/application/api/constants.js.map +0 -1
  107. package/dist/runtime/application/api/filters.d.ts +0 -14
  108. package/dist/runtime/application/api/filters.js +0 -11
  109. package/dist/runtime/application/api/filters.js.map +0 -1
  110. package/dist/runtime/application/api/guards.d.ts +0 -15
  111. package/dist/runtime/application/api/guards.js +0 -8
  112. package/dist/runtime/application/api/guards.js.map +0 -1
  113. package/dist/runtime/application/api/index.d.ts +0 -23
  114. package/dist/runtime/application/api/index.js +0 -17
  115. package/dist/runtime/application/api/index.js.map +0 -1
  116. package/dist/runtime/application/api/logging.d.ts +0 -14
  117. package/dist/runtime/application/api/logging.js +0 -77
  118. package/dist/runtime/application/api/logging.js.map +0 -1
  119. package/dist/runtime/application/api/meta.d.ts +0 -21
  120. package/dist/runtime/application/api/meta.js +0 -2
  121. package/dist/runtime/application/api/meta.js.map +0 -1
  122. package/dist/runtime/application/api/middlewares.d.ts +0 -14
  123. package/dist/runtime/application/api/middlewares.js +0 -12
  124. package/dist/runtime/application/api/middlewares.js.map +0 -1
  125. package/dist/runtime/application/api/procedure.d.ts +0 -71
  126. package/dist/runtime/application/api/procedure.js +0 -41
  127. package/dist/runtime/application/api/procedure.js.map +0 -1
  128. package/dist/runtime/application/api/router.d.ts +0 -93
  129. package/dist/runtime/application/api/router.js +0 -55
  130. package/dist/runtime/application/api/router.js.map +0 -1
  131. package/dist/runtime/application/api/types.d.ts +0 -38
  132. package/dist/runtime/application/api/types.js +0 -2
  133. package/dist/runtime/application/api/types.js.map +0 -1
  134. package/dist/runtime/application/config.d.ts +0 -28
  135. package/dist/runtime/application/config.js +0 -25
  136. package/dist/runtime/application/config.js.map +0 -1
  137. package/dist/runtime/application/constants.d.ts +0 -2
  138. package/dist/runtime/application/constants.js +0 -2
  139. package/dist/runtime/application/constants.js.map +0 -1
  140. package/dist/runtime/application/hook.d.ts +0 -19
  141. package/dist/runtime/application/hook.js +0 -11
  142. package/dist/runtime/application/hook.js.map +0 -1
  143. package/dist/runtime/application/hooks.d.ts +0 -3
  144. package/dist/runtime/application/hooks.js +0 -4
  145. package/dist/runtime/application/hooks.js.map +0 -1
  146. package/src/runtime/application/api/api.ts +0 -406
  147. package/src/runtime/application/api/config.ts +0 -18
  148. package/src/runtime/application/api/constants.ts +0 -22
  149. package/src/runtime/application/api/filters.ts +0 -39
  150. package/src/runtime/application/api/guards.ts +0 -33
  151. package/src/runtime/application/api/index.ts +0 -69
  152. package/src/runtime/application/api/logging.ts +0 -110
  153. package/src/runtime/application/api/meta.ts +0 -37
  154. package/src/runtime/application/api/middlewares.ts +0 -37
  155. package/src/runtime/application/api/procedure.ts +0 -224
  156. package/src/runtime/application/api/router.ts +0 -247
  157. package/src/runtime/application/api/types.ts +0 -138
  158. package/src/runtime/application/config.ts +0 -82
  159. package/src/runtime/application/constants.ts +0 -4
  160. package/src/runtime/application/hook.ts +0 -51
  161. package/src/runtime/application/hooks.ts +0 -3
@@ -14,10 +14,12 @@ import type {
14
14
  WorkerPoolConfig,
15
15
  WorkerPoolFactory,
16
16
  } from './worker-pool.ts'
17
- import { JobWorkerPool, WorkerType } from '../enums.ts'
17
+ import { WorkerType } from '../enums.ts'
18
18
  import { getJobQueueName } from '../jobs/manager.ts'
19
19
  import { JobRunnersPool } from './worker-pool.ts'
20
20
 
21
+ type JobsPoolConfig = Exclude<ServerConfig['jobs'], undefined>['pools']
22
+
21
23
  function enrichBullMqErrorStack(error: unknown): Error {
22
24
  const normalized =
23
25
  error instanceof Error
@@ -31,6 +33,38 @@ function enrichBullMqErrorStack(error: unknown): Error {
31
33
  return normalized
32
34
  }
33
35
 
36
+ function getJobsByPool(jobs: Iterable<AnyJob>): Map<string, AnyJob[]> {
37
+ const jobsByPool = new Map<string, AnyJob[]>()
38
+
39
+ for (const job of jobs) {
40
+ const poolJobs = jobsByPool.get(job.options.pool)
41
+ if (poolJobs) {
42
+ poolJobs.push(job)
43
+ } else {
44
+ jobsByPool.set(job.options.pool, [job])
45
+ }
46
+ }
47
+
48
+ return jobsByPool
49
+ }
50
+
51
+ function assertActiveJobPoolsConfigured(
52
+ jobsByPool: Map<string, AnyJob[]>,
53
+ pools: JobsPoolConfig,
54
+ ) {
55
+ const missingPoolMessages = [...jobsByPool]
56
+ .filter(([poolName]) => !pools[poolName])
57
+ .flatMap(([poolName, jobs]) =>
58
+ jobs.map((job) => `${job.name} -> ${poolName}`),
59
+ )
60
+
61
+ if (missingPoolMessages.length > 0) {
62
+ throw new Error(
63
+ `Invalid jobs pool configuration: missing pool config for jobs: ${missingPoolMessages.join(', ')}`,
64
+ )
65
+ }
66
+ }
67
+
34
68
  /**
35
69
  * ApplicationServerJobs manages job worker pools and BullMQ queue workers.
36
70
  *
@@ -38,7 +72,7 @@ function enrichBullMqErrorStack(error: unknown): Error {
38
72
  * - Uses JobRunnersPool (extends WorkerPool) for worker management
39
73
  * - Uses ManagedWorker for restart logic and state tracking
40
74
  * - Integrates with ErrorPolicy for restart decisions
41
- * - Proper health tracking per job pool type
75
+ * - Proper health tracking per job pool
42
76
  */
43
77
  export class ApplicationServerJobs {
44
78
  /**
@@ -49,10 +83,10 @@ export class ApplicationServerJobs {
49
83
  jobs: Map<string, AnyJob>
50
84
 
51
85
  /**
52
- * Shared resource pools by pool type (Io, Compute).
53
- * All jobs of a given pool type share the same pool for resource management.
86
+ * Shared resource pools by configured pool name.
87
+ * All jobs using the same pool name share the same pool for resource management.
54
88
  */
55
- protected pools = new Map<JobWorkerPool, JobRunnersPool>()
89
+ protected pools = new Map<string, JobRunnersPool>()
56
90
 
57
91
  constructor(
58
92
  readonly params: {
@@ -86,16 +120,16 @@ export class ApplicationServerJobs {
86
120
  return
87
121
  }
88
122
 
89
- // Step 1: Initialize shared resource pools (Io, Compute)
90
- const poolTypes = Object.values(JobWorkerPool)
123
+ const jobsByPool = getJobsByPool(this.jobs.values())
124
+ assertActiveJobPoolsConfigured(jobsByPool, jobsConfig.pools)
91
125
 
92
- for (const poolType of poolTypes) {
93
- const poolConfig = jobsConfig.pools[poolType]
94
- if (!poolConfig) continue
126
+ // Step 1: Initialize shared resource pools used by active jobs
127
+ for (const poolName of jobsByPool.keys()) {
128
+ const poolConfig = jobsConfig.pools[poolName]!
95
129
 
96
- // Create a JobRunnersPool for this pool type
130
+ // Create a JobRunnersPool for this pool
97
131
  const config: WorkerPoolConfig = {
98
- name: `job-pool-${poolType}`,
132
+ name: `job-pool-${poolName}`,
99
133
  workerType: WorkerType.Job,
100
134
  path: workerConfig.path,
101
135
  workerData: { ...workerConfig.workerData },
@@ -111,15 +145,15 @@ export class ApplicationServerJobs {
111
145
 
112
146
  // Add workers to the pool
113
147
  for (let i = 0; i < poolConfig.threads; i++) {
114
- pool.add({ runtime: { type: 'jobs', jobWorkerPool: poolType } }, i)
148
+ pool.add({ runtime: { type: 'jobs', jobWorkerPool: poolName } }, i)
115
149
  }
116
150
 
117
151
  await pool.startAll()
118
- this.pools.set(poolType, pool)
152
+ this.pools.set(poolName, pool)
119
153
 
120
154
  logger.info(
121
155
  {
122
- pool: poolType,
156
+ pool: poolName,
123
157
  threads: poolConfig.threads,
124
158
  jobsPerThread: poolConfig.jobs,
125
159
  },
@@ -129,28 +163,18 @@ export class ApplicationServerJobs {
129
163
 
130
164
  // Step 2: Create a dedicated BullMQ Worker for each job
131
165
  // Calculate how many jobs use each pool for fair concurrency distribution
132
- const jobsPerPool = new Map<JobWorkerPool, number>()
133
- for (const job of this.jobs.values()) {
134
- const count = jobsPerPool.get(job.options.pool) ?? 0
135
- jobsPerPool.set(job.options.pool, count + 1)
136
- }
137
-
138
166
  for (const job of this.jobs.values()) {
139
167
  const queueName = getJobQueueName(job)
140
- const poolType = job.options.pool
141
- const pool = this.pools.get(poolType)
168
+ const poolName = job.options.pool
169
+ const pool = this.pools.get(poolName)
142
170
 
143
171
  if (!pool) {
144
- logger.warn(
145
- { job: job.name, pool: poolType },
146
- 'No pool configured for job, skipping worker creation',
147
- )
148
- continue
172
+ throw new Error(`Job "${job.name}" pool "${poolName}" is not started`)
149
173
  }
150
174
 
151
- const poolConfig = jobsConfig.pools[poolType]
175
+ const poolConfig = jobsConfig.pools[poolName]!
152
176
  const poolCapacity = poolConfig.threads * poolConfig.jobs
153
- const jobCountInPool = jobsPerPool.get(poolType) ?? 1
177
+ const jobCountInPool = jobsByPool.get(poolName)?.length ?? 1
154
178
  // Use job-specific concurrency if provided, otherwise distribute pool capacity evenly
155
179
  const defaultConcurrency = Math.max(
156
180
  1,
@@ -192,7 +216,7 @@ export class ApplicationServerJobs {
192
216
 
193
217
  this.queueWorkers.add(queueWorker)
194
218
  logger.info(
195
- { job: job.name, queue: queueName, pool: poolType, concurrency },
219
+ { job: job.name, queue: queueName, pool: poolName, concurrency },
196
220
  'Job queue worker started',
197
221
  )
198
222
  }
@@ -234,9 +258,9 @@ export class ApplicationServerJobs {
234
258
  }
235
259
 
236
260
  /**
237
- * Get a job pool by type.
261
+ * Get a job pool by name.
238
262
  */
239
- getPool(poolType: JobWorkerPool): JobRunnersPool | undefined {
240
- return this.pools.get(poolType)
263
+ getPool(poolName: string): JobRunnersPool | undefined {
264
+ return this.pools.get(poolName)
241
265
  }
242
266
  }
@@ -7,12 +7,10 @@ import type {
7
7
  TAnyEventContract,
8
8
  TAnySubscriptionContract,
9
9
  } from '@nmtjs/contract'
10
- import type { Container, Logger } from '@nmtjs/core'
10
+ import type { Logger } from '@nmtjs/core'
11
11
  import type { t } from '@nmtjs/type'
12
12
  import { isAbortError } from '@nmtjs/common'
13
13
 
14
- import { subscriptionAdapter } from '../injectables.ts'
15
-
16
14
  export type SubscriptionAdapterEvent = { channel: string; payload: any }
17
15
 
18
16
  export interface SubscriptionAdapterType {
@@ -74,7 +72,7 @@ export type PublishFn = <
74
72
 
75
73
  export type SubscriptionManagerOptions = {
76
74
  logger: Logger
77
- container: Container
75
+ adapter: SubscriptionAdapterType
78
76
  }
79
77
 
80
78
  export class SubscriptionManager {
@@ -85,13 +83,7 @@ export class SubscriptionManager {
85
83
  this.logger = options.logger.child({ component: SubscriptionManager.name })
86
84
  }
87
85
 
88
- protected get adapter() {
89
- return this.options.container.resolve(subscriptionAdapter)
90
- }
91
-
92
86
  subscribe: SubscribeFn = async (subscription, events, options, signal) => {
93
- const adapter = await this.adapter
94
-
95
87
  const eventKeys =
96
88
  Object.keys(events).length === 0
97
89
  ? Object.keys(subscription.events)
@@ -118,7 +110,7 @@ export class SubscriptionManager {
118
110
  'Reusing pubsub channel stream',
119
111
  )
120
112
  } else {
121
- const iterable = adapter.subscribe(channel, signal)
113
+ const iterable = this.options.adapter.subscribe(channel, signal)
122
114
  const stream = this.createEventStream(iterable)
123
115
  stream.on('close', () => {
124
116
  this.subscriptions.delete(channel)
@@ -173,15 +165,13 @@ export class SubscriptionManager {
173
165
  }
174
166
 
175
167
  publish: PublishFn = async (event, options, data) => {
176
- const adapter = await this.adapter
177
-
178
168
  const channel = getChannelName(event, options)
179
169
 
180
170
  this.logger.trace({ channel, event: event.name }, 'Publishing pubsub event')
181
171
 
182
172
  try {
183
173
  const payload = event.payload.encode(data)
184
- const published = await adapter.publish(channel, payload)
174
+ const published = await this.options.adapter.publish(channel, payload)
185
175
 
186
176
  if (published) {
187
177
  this.logger.trace(
@@ -205,6 +195,13 @@ export class SubscriptionManager {
205
195
  }
206
196
  }
207
197
 
198
+ async dispose(): Promise<void> {
199
+ for (const { stream } of this.subscriptions.values()) {
200
+ stream.destroy()
201
+ }
202
+ this.subscriptions.clear()
203
+ }
204
+
208
205
  private createEventStream(
209
206
  iterable: AsyncGenerator<SubscriptionAdapterEvent>,
210
207
  ): Readable {
@@ -1,10 +1,14 @@
1
1
  import EventEmitter, { on } from 'node:events'
2
2
 
3
+ import type { RuntimePlugin } from '@nmtjs/application'
3
4
  import type { Logger } from '@nmtjs/core'
4
5
  import { isAbortError } from '@nmtjs/common'
5
- import { createFactoryInjectable } from '@nmtjs/core'
6
+ import {
7
+ CoreInjectables,
8
+ createFactoryInjectable,
9
+ provision,
10
+ } from '@nmtjs/core'
6
11
 
7
- import type { RuntimePlugin } from '../plugin.ts'
8
12
  import type { Store } from '../types.ts'
9
13
  import type {
10
14
  SubscriptionAdapterEvent,
@@ -130,28 +134,26 @@ export class RedisSubscriptionAdapter implements SubscriptionAdapterType {
130
134
  }
131
135
 
132
136
  export const RedisSubscriptionAdapterPlugin = (): RuntimePlugin => {
133
- return {
134
- name: 'redis-subscription-adapter',
135
- hooks: {
136
- 'lifecycle:beforeInitialize': async (ctx) => {
137
- const adapter = await ctx.container.resolve(
138
- createFactoryInjectable({
139
- dependencies: { config: storeConfig },
140
- factory: async ({ config }) => {
141
- const connection = await createStoreClient(config)
142
- const adapter = new RedisSubscriptionAdapter(
143
- connection,
144
- ctx.logger,
145
- )
146
- await adapter.initialize()
147
- return { adapter, connection }
148
- },
149
- pick: ({ adapter }) => adapter,
150
- dispose: ({ connection }) => connection.quit(),
151
- }),
152
- )
153
- ctx.container.provide(subscriptionAdapter, adapter)
137
+ const adapterFactory = createFactoryInjectable(
138
+ {
139
+ dependencies: { config: storeConfig, logger: CoreInjectables.logger },
140
+ factory: async ({ config, logger }) => {
141
+ const connection = await createStoreClient(config)
142
+ const adapter = new RedisSubscriptionAdapter(connection, logger)
143
+ await adapter.initialize()
144
+ return { adapter, connection }
145
+ },
146
+ pick: ({ adapter }) => adapter,
147
+ dispose: async ({ adapter, connection }) => {
148
+ await adapter.dispose()
149
+ await connection.quit()
154
150
  },
155
151
  },
152
+ 'RedisSubscriptionAdapter',
153
+ )
154
+
155
+ return {
156
+ name: 'redis-subscription-adapter',
157
+ injections: [provision(subscriptionAdapter, adapterFactory)],
156
158
  }
157
159
  }
@@ -1,11 +1,9 @@
1
- import type { HookTypes } from '@nmtjs/core'
1
+ import type { ApplicationHostDefinition } from '@nmtjs/application'
2
2
  import type { ProxyableTransportType } from '@nmtjs/gateway'
3
3
  import type { Redis, RedisOptions } from 'ioredis'
4
4
  import type { Redis as Valkey, RedisOptions as ValkeyOptions } from 'iovalkey'
5
5
 
6
- import type { ApplicationConfig } from './application/config.ts'
7
- import type { LifecycleHook, StoreType } from './enums.ts'
8
- import type { BaseRuntime } from './runtime.ts'
6
+ import type { StoreType } from './enums.ts'
9
7
 
10
8
  export type WorkerThreadErrorOrigin = 'bootstrap' | 'start' | 'runtime'
11
9
 
@@ -66,15 +64,8 @@ export type JobTaskResultTypes = {
66
64
  queue_job_not_found: {}
67
65
  }
68
66
 
69
- export interface LifecycleHookTypes extends HookTypes {
70
- [LifecycleHook.BeforeInitialize]: (runtime: BaseRuntime) => any
71
- [LifecycleHook.AfterInitialize]: (runtime: BaseRuntime) => any
72
- [LifecycleHook.BeforeDispose]: (runtime: BaseRuntime) => any
73
- [LifecycleHook.AfterDispose]: (runtime: BaseRuntime) => any
74
- }
75
-
76
67
  export type ApplicationDefinitionType =
77
- | { type: 'neemata'; definition: ApplicationConfig<any, any> }
68
+ | { type: 'neemata'; definition: ApplicationHostDefinition }
78
69
  | { type: 'custom'; definition: any }
79
70
 
80
71
  export interface Applications
@@ -1,217 +1,133 @@
1
- import type { Dependant } from '@nmtjs/core'
2
- import type { GatewayOptions, Transport } from '@nmtjs/gateway'
3
- import { Gateway } from '@nmtjs/gateway'
1
+ import type {
2
+ ApplicationHost,
3
+ ApplicationHostDefinition,
4
+ ApplicationHostOptions,
5
+ ApplicationResolvedProcedure,
6
+ ApplicationTransport,
7
+ NeemataApplication,
8
+ TransportOptionsOf,
9
+ } from '@nmtjs/application'
10
+ import type { Gateway, GatewayOptions } from '@nmtjs/gateway'
11
+ import { createApplicationHost, LifecycleHook } from '@nmtjs/application'
4
12
  import { JsonFormat } from '@nmtjs/json-format/server'
5
13
  import { MsgpackFormat } from '@nmtjs/msgpack-format/server'
6
14
  import { ProtocolFormats } from '@nmtjs/protocol/server'
7
15
 
8
- import type { ApplicationConfig } from '../application/config.ts'
9
- import type {
10
- AnyFilter,
11
- AnyGuard,
12
- AnyMiddleware,
13
- AnyProcedure,
14
- AnyRouter,
15
- kDefaultProcedure as kDefaultProcedureKey,
16
- } from '../application/index.ts'
17
- import type { ServerConfig } from '../server/config.ts'
18
- import { ApplicationApi } from '../application/api/api.ts'
19
- import { ApplicationHooks } from '../application/hooks.ts'
20
- import {
21
- isProcedure,
22
- isRootRouter,
23
- isRouter,
24
- kDefaultProcedure,
25
- kRootRouter,
26
- } from '../application/index.ts'
27
- import { LifecycleHook, WorkerType } from '../enums.ts'
16
+ import type { ServerApplicationConfig, ServerConfig } from '../server/config.ts'
17
+ import { WorkerType } from '../enums.ts'
28
18
  import { BaseWorkerRuntime } from './base.ts'
29
19
 
30
20
  export interface ApplicationWorkerRuntimeOptions {
31
21
  name: string
32
22
  path: string
33
- transports: { [key: string]: any }
23
+ transports: ServerApplicationConfig['threads'][number]
34
24
  }
35
25
 
36
26
  export class ApplicationWorkerRuntime extends BaseWorkerRuntime {
37
- api!: ApplicationApi
38
- applicationHooks!: ApplicationHooks
39
- gateway!: Gateway
40
- transports!: GatewayOptions['transports']
41
-
42
- routers = new Map<string | kRootRouter, AnyRouter>()
43
- procedures = new Map<
44
- string | kDefaultProcedureKey,
45
- { procedure: AnyProcedure; path: AnyRouter[] }
46
- >()
47
- filters = new Set<AnyFilter>()
48
- middlewares = new Set<AnyMiddleware>()
49
- guards = new Set<AnyGuard>()
27
+ host!: ApplicationHost
50
28
 
51
29
  constructor(
52
30
  readonly config: ServerConfig,
53
31
  readonly runtimeOptions: ApplicationWorkerRuntimeOptions,
54
- protected appConfig: ApplicationConfig,
32
+ protected hostDefinition: ApplicationHostDefinition,
55
33
  ) {
56
34
  super(
57
35
  config,
58
36
  {
59
37
  logger: config.logger,
60
38
  name: `Worker ${runtimeOptions.name}`,
61
- plugins: appConfig.plugins,
39
+ plugins: [],
62
40
  },
63
41
  WorkerType.Application,
64
42
  )
43
+ }
65
44
 
66
- this.applicationHooks = new ApplicationHooks()
45
+ get application(): NeemataApplication {
46
+ return this.host.application
47
+ }
67
48
 
68
- this.api = new ApplicationApi({
69
- timeout: this.appConfig.api.timeout,
70
- container: this.container,
71
- logger: this.logger,
72
- meta: this.appConfig.meta,
73
- filters: this.filters,
74
- middlewares: this.middlewares,
75
- guards: this.guards,
76
- procedures: this.procedures,
77
- })
49
+ get api() {
50
+ return this.application.api
78
51
  }
79
52
 
80
- async start() {
81
- await this.initialize()
53
+ get applicationHooks() {
54
+ return this.application.applicationHooks
55
+ }
82
56
 
83
- this.transports = {}
57
+ get gateway(): Gateway<ApplicationResolvedProcedure> {
58
+ return this.host.gateway
59
+ }
84
60
 
85
- for (const key in this.runtimeOptions.transports) {
86
- const options = this.runtimeOptions.transports[key]
87
- const { factory, proxyable } = this.appConfig.transports[key] as Transport
88
- this.transports[key] = { transport: await factory(options), proxyable }
89
- }
61
+ get transports(): GatewayOptions<ApplicationResolvedProcedure>['transports'] {
62
+ return this.host.transports
63
+ }
90
64
 
91
- this.gateway = new Gateway({
92
- ...this.appConfig.gateway,
65
+ async start() {
66
+ await this.initialize()
67
+ this.host = createApplicationHost(this.hostDefinition.application, {
68
+ name: this.runtimeOptions.name,
93
69
  logger: this.logger,
94
70
  container: this.container,
95
- hooks: this.lifecycleHooks,
96
71
  formats: new ProtocolFormats([new JsonFormat(), new MsgpackFormat()]),
97
- transports: this.transports,
98
- api: this.api,
99
- identity: this.appConfig.identity,
100
- })
101
-
102
- return await this.gateway.start().finally(async () => {
103
- await this.lifecycleHooks.callHook(LifecycleHook.Start)
72
+ ...this.resolveHostOptions(),
104
73
  })
74
+ return this.host.start()
105
75
  }
106
76
 
107
77
  async stop() {
108
- await this.gateway.stop()
78
+ await this.host.stop()
109
79
  await this.dispose()
110
80
  await this.lifecycleHooks.callHook(LifecycleHook.Stop)
111
81
  }
112
82
 
113
- async reload(appConfig: ApplicationConfig): Promise<void> {
114
- await this.dispose()
115
- this.appConfig = appConfig
116
- this.plugins = appConfig.plugins
117
- await this.initialize()
118
- this.gateway.options.identity =
119
- this.appConfig.identity ?? this.gateway.options.identity
120
- await this.gateway.reload()
83
+ async reload(hostDefinition: ApplicationHostDefinition): Promise<void> {
84
+ this.hostDefinition = hostDefinition
85
+ await this.host.reloadApplication(hostDefinition.application)
121
86
  }
122
87
 
123
- async initialize(): Promise<void> {
124
- this.registerApi()
125
- this.lifecycleHooks.addHooks(this.appConfig.lifecycleHooks)
126
- await super.initialize()
127
- }
88
+ protected *_dependents() {}
128
89
 
129
- protected async _initialize(): Promise<void> {
130
- await super._initialize()
90
+ protected resolveHostOptions(): Pick<
91
+ ApplicationHostOptions,
92
+ 'transports' | 'gateway' | 'identity'
93
+ > {
94
+ const config = this.config.applications[
95
+ this.runtimeOptions.name
96
+ ] as ServerApplicationConfig
131
97
 
132
- for (const hook of this.appConfig.hooks) {
133
- this.applicationHooks.hook(hook.name, async (...args: any[]) => {
134
- const ctx = await this.container.createContext(hook.dependencies)
135
- await hook.handler(ctx, ...args)
136
- })
98
+ if (!config) {
99
+ throw new Error(
100
+ `Missing server application config: ${this.runtimeOptions.name}`,
101
+ )
137
102
  }
138
- }
139
103
 
140
- protected async _dispose(): Promise<void> {
141
- this.applicationHooks.removeAllHooks()
142
- await super._dispose()
143
- this.lifecycleHooks.removeHooks(this.appConfig.lifecycleHooks)
144
- this.filters.clear()
145
- this.middlewares.clear()
146
- this.guards.clear()
147
- this.routers.clear()
148
- this.procedures.clear()
149
- }
150
-
151
- protected *_dependents(): Generator<Dependant> {
152
- yield* this.appConfig.filters
153
- yield* this.appConfig.guards
154
- yield* this.appConfig.middlewares
155
- yield* this.appConfig.meta
156
- yield* this.appConfig.hooks
157
- for (const router of this.routers.values()) {
158
- yield* router.meta
159
- }
160
- for (const { procedure } of this.procedures.values()) {
161
- yield procedure
162
- yield* procedure.meta
163
- yield* procedure.guards
164
- yield* procedure.middlewares
104
+ return {
105
+ transports: createHostTransportConfig(
106
+ this.hostDefinition.transports,
107
+ this.runtimeOptions.transports,
108
+ ),
109
+ gateway: this.hostDefinition.gateway,
110
+ identity: this.hostDefinition.identity,
165
111
  }
166
112
  }
113
+ }
167
114
 
168
- protected registerApi() {
169
- const { router, filters, guards, middlewares } = this.appConfig
170
-
171
- if (this.routers.has(kRootRouter)) {
172
- throw new Error('Root router already registered')
173
- }
174
-
175
- if (!isRootRouter(router)) {
176
- throw new Error('Root router must be a root router')
177
- }
178
-
179
- this.routers.set(kRootRouter, router)
180
- this.registerRouter(router, [])
181
-
182
- if (router.default) {
183
- if (!isProcedure(router.default)) {
184
- throw new Error('Root router default must be a procedure')
185
- }
186
- this.procedures.set(kDefaultProcedure, {
187
- procedure: router.default,
188
- path: [router],
189
- })
115
+ function createHostTransportConfig<
116
+ Transports extends Record<string, ApplicationTransport>,
117
+ >(
118
+ transports: Transports,
119
+ options: { [K in keyof Transports]: TransportOptionsOf<Transports[K]> },
120
+ ) {
121
+ const config = {} as {
122
+ [K in keyof Transports]: {
123
+ transport: Transports[K]
124
+ options: TransportOptionsOf<Transports[K]>
190
125
  }
191
-
192
- for (const filter of filters) this.filters.add(filter)
193
- for (const middleware of middlewares) this.middlewares.add(middleware)
194
- for (const guard of guards) this.guards.add(guard)
195
126
  }
196
127
 
197
- protected registerRouter(router: AnyRouter, path: AnyRouter[] = []) {
198
- for (const route of Object.values(router.routes)) {
199
- if (isRouter(route)) {
200
- const name = route.contract.name
201
- if (!name) throw new Error('Nested routers must have a name')
202
- if (this.routers.has(name)) {
203
- throw new Error(`Router ${String(name)} already registered`)
204
- }
205
- this.routers.set(name, route)
206
- this.registerRouter(route, [...path, router])
207
- } else if (isProcedure(route)) {
208
- const name = route.contract.name
209
- if (!name) throw new Error('Procedures must have a name')
210
- if (this.procedures.has(name)) {
211
- throw new Error(`Procedure ${name} already registered`)
212
- }
213
- this.procedures.set(name, { procedure: route, path: [...path, router] })
214
- }
215
- }
128
+ for (const key in transports) {
129
+ config[key] = { transport: transports[key], options: options[key] }
216
130
  }
131
+
132
+ return config
217
133
  }