nmtjs 0.15.0-beta.2 → 0.15.0-beta.21

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 (254) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cli.js +3 -2
  3. package/dist/cli.js.map +1 -0
  4. package/dist/config.d.ts +51 -0
  5. package/dist/config.js +1 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/entrypoints/cli.d.ts +1 -0
  8. package/dist/entrypoints/cli.js +1 -0
  9. package/dist/entrypoints/cli.js.map +1 -0
  10. package/dist/entrypoints/main.d.ts +5 -0
  11. package/dist/entrypoints/main.js +83 -15
  12. package/dist/entrypoints/main.js.map +1 -0
  13. package/dist/entrypoints/thread.d.ts +14 -0
  14. package/dist/entrypoints/thread.js +130 -24
  15. package/dist/entrypoints/thread.js.map +1 -0
  16. package/dist/entrypoints/worker.d.ts +3 -0
  17. package/dist/entrypoints/worker.js +4 -3
  18. package/dist/entrypoints/worker.js.map +1 -0
  19. package/dist/index.d.ts +69 -0
  20. package/dist/{_exports/index.js → index.js} +9 -5
  21. package/dist/index.js.map +1 -0
  22. package/dist/resolver.d.ts +2 -0
  23. package/dist/resolver.js +1 -0
  24. package/dist/resolver.js.map +1 -0
  25. package/dist/runtime/application/api/api.d.ts +49 -0
  26. package/dist/runtime/application/api/api.js +193 -0
  27. package/dist/runtime/application/api/api.js.map +1 -0
  28. package/dist/runtime/application/api/constants.d.ts +14 -0
  29. package/dist/runtime/application/api/constants.js +8 -0
  30. package/dist/runtime/application/api/constants.js.map +1 -0
  31. package/dist/runtime/application/api/filters.d.ts +14 -0
  32. package/dist/runtime/application/api/filters.js +11 -0
  33. package/dist/runtime/application/api/filters.js.map +1 -0
  34. package/dist/runtime/application/api/guards.d.ts +13 -0
  35. package/dist/runtime/application/api/guards.js +8 -0
  36. package/dist/runtime/application/api/guards.js.map +1 -0
  37. package/dist/runtime/application/api/index.d.ts +8 -0
  38. package/dist/runtime/application/api/index.js +9 -0
  39. package/dist/runtime/application/api/index.js.map +1 -0
  40. package/dist/runtime/application/api/middlewares.d.ts +14 -0
  41. package/dist/runtime/application/api/middlewares.js +12 -0
  42. package/dist/runtime/application/api/middlewares.js.map +1 -0
  43. package/dist/runtime/application/api/procedure.d.ts +67 -0
  44. package/dist/runtime/application/api/procedure.js +50 -0
  45. package/dist/runtime/application/api/procedure.js.map +1 -0
  46. package/dist/runtime/application/api/router.d.ts +71 -0
  47. package/dist/runtime/application/api/router.js +51 -0
  48. package/dist/runtime/application/api/router.js.map +1 -0
  49. package/dist/runtime/application/api/types.d.ts +32 -0
  50. package/dist/runtime/application/api/types.js +2 -0
  51. package/dist/runtime/application/api/types.js.map +1 -0
  52. package/dist/runtime/application/config.d.ts +26 -0
  53. package/dist/runtime/application/config.js +21 -0
  54. package/dist/runtime/application/config.js.map +1 -0
  55. package/dist/runtime/application/constants.d.ts +2 -0
  56. package/dist/runtime/application/constants.js +2 -0
  57. package/dist/runtime/application/constants.js.map +1 -0
  58. package/dist/runtime/application/hook.d.ts +19 -0
  59. package/dist/runtime/application/hook.js +11 -0
  60. package/dist/runtime/application/hook.js.map +1 -0
  61. package/dist/runtime/application/hooks.d.ts +3 -0
  62. package/dist/runtime/application/hooks.js +4 -0
  63. package/dist/runtime/application/hooks.js.map +1 -0
  64. package/dist/runtime/application/index.d.ts +5 -0
  65. package/dist/runtime/application/index.js +6 -0
  66. package/dist/runtime/application/index.js.map +1 -0
  67. package/dist/runtime/constants.d.ts +8 -0
  68. package/dist/runtime/constants.js +5 -0
  69. package/dist/runtime/constants.js.map +1 -0
  70. package/dist/runtime/core/hooks.d.ts +4 -0
  71. package/dist/runtime/core/hooks.js +4 -0
  72. package/dist/runtime/core/hooks.js.map +1 -0
  73. package/dist/runtime/core/plugin.d.ts +8 -0
  74. package/dist/runtime/core/plugin.js +4 -0
  75. package/dist/runtime/core/plugin.js.map +1 -0
  76. package/dist/runtime/core/runtime.d.ts +27 -0
  77. package/dist/runtime/core/runtime.js +81 -0
  78. package/dist/runtime/core/runtime.js.map +1 -0
  79. package/dist/runtime/enums.d.ts +21 -0
  80. package/dist/runtime/enums.js +26 -0
  81. package/dist/runtime/enums.js.map +1 -0
  82. package/dist/runtime/index.d.ts +21 -0
  83. package/dist/runtime/index.js +22 -0
  84. package/dist/runtime/index.js.map +1 -0
  85. package/dist/runtime/injectables.d.ts +23 -0
  86. package/dist/runtime/injectables.js +20 -0
  87. package/dist/runtime/injectables.js.map +1 -0
  88. package/dist/runtime/jobs/job.d.ts +132 -0
  89. package/dist/runtime/jobs/job.js +68 -0
  90. package/dist/runtime/jobs/job.js.map +1 -0
  91. package/dist/runtime/jobs/manager.d.ts +113 -0
  92. package/dist/runtime/jobs/manager.js +210 -0
  93. package/dist/runtime/jobs/manager.js.map +1 -0
  94. package/dist/runtime/jobs/router.d.ts +266 -0
  95. package/dist/runtime/jobs/router.js +432 -0
  96. package/dist/runtime/jobs/router.js.map +1 -0
  97. package/dist/runtime/jobs/runner.d.ts +64 -0
  98. package/dist/runtime/jobs/runner.js +256 -0
  99. package/dist/runtime/jobs/runner.js.map +1 -0
  100. package/dist/runtime/jobs/step.d.ts +23 -0
  101. package/dist/runtime/jobs/step.js +18 -0
  102. package/dist/runtime/jobs/step.js.map +1 -0
  103. package/dist/runtime/jobs/ui.d.ts +3 -0
  104. package/dist/runtime/jobs/ui.js +17 -0
  105. package/dist/runtime/jobs/ui.js.map +1 -0
  106. package/dist/runtime/pubsub/manager.d.ts +48 -0
  107. package/dist/runtime/pubsub/manager.js +119 -0
  108. package/dist/runtime/pubsub/manager.js.map +1 -0
  109. package/dist/runtime/pubsub/redis.d.ts +16 -0
  110. package/dist/runtime/pubsub/redis.js +98 -0
  111. package/dist/runtime/pubsub/redis.js.map +1 -0
  112. package/dist/runtime/scheduler/index.d.ts +22 -0
  113. package/dist/runtime/scheduler/index.js +20 -0
  114. package/dist/runtime/scheduler/index.js.map +1 -0
  115. package/dist/runtime/server/applications.d.ts +52 -0
  116. package/dist/runtime/server/applications.js +133 -0
  117. package/dist/runtime/server/applications.js.map +1 -0
  118. package/dist/runtime/server/config.d.ts +121 -0
  119. package/dist/runtime/server/config.js +33 -0
  120. package/dist/runtime/server/config.js.map +1 -0
  121. package/dist/runtime/server/jobs.d.ts +41 -0
  122. package/dist/runtime/server/jobs.js +181 -0
  123. package/dist/runtime/server/jobs.js.map +1 -0
  124. package/dist/runtime/server/pool.d.ts +54 -0
  125. package/dist/runtime/server/pool.js +194 -0
  126. package/dist/runtime/server/pool.js.map +1 -0
  127. package/dist/runtime/server/proxy.d.ts +21 -0
  128. package/dist/runtime/server/proxy.js +79 -0
  129. package/dist/runtime/server/proxy.js.map +1 -0
  130. package/dist/runtime/server/server.d.ts +53 -0
  131. package/dist/runtime/server/server.js +90 -0
  132. package/dist/runtime/server/server.js.map +1 -0
  133. package/dist/runtime/store/index.d.ts +3 -0
  134. package/dist/runtime/store/index.js +23 -0
  135. package/dist/runtime/store/index.js.map +1 -0
  136. package/dist/runtime/types.d.ts +103 -0
  137. package/dist/runtime/types.js +2 -0
  138. package/dist/runtime/types.js.map +1 -0
  139. package/dist/runtime/workers/application.d.ts +47 -0
  140. package/dist/runtime/workers/application.js +162 -0
  141. package/dist/runtime/workers/application.js.map +1 -0
  142. package/dist/runtime/workers/base.d.ts +16 -0
  143. package/dist/runtime/workers/base.js +46 -0
  144. package/dist/runtime/workers/base.js.map +1 -0
  145. package/dist/runtime/workers/cli.d.ts +1 -0
  146. package/dist/runtime/workers/cli.js +2 -0
  147. package/dist/runtime/workers/cli.js.map +1 -0
  148. package/dist/runtime/workers/job.d.ts +20 -0
  149. package/dist/runtime/workers/job.js +172 -0
  150. package/dist/runtime/workers/job.js.map +1 -0
  151. package/dist/typings.d.ts +5 -0
  152. package/dist/typings.js +4 -3
  153. package/dist/typings.js.map +1 -0
  154. package/dist/vite/builder.d.ts +5 -0
  155. package/dist/vite/builder.js +5 -1
  156. package/dist/vite/builder.js.map +1 -0
  157. package/dist/vite/config.d.ts +28 -0
  158. package/dist/vite/config.js +1 -0
  159. package/dist/vite/config.js.map +1 -0
  160. package/dist/vite/plugins.d.ts +2 -0
  161. package/dist/vite/plugins.js +1 -0
  162. package/dist/vite/plugins.js.map +1 -0
  163. package/dist/vite/runners/worker.d.ts +4 -0
  164. package/dist/vite/runners/worker.js +1 -0
  165. package/dist/vite/runners/worker.js.map +1 -0
  166. package/dist/vite/server.d.ts +3 -0
  167. package/dist/vite/server.js +6 -1
  168. package/dist/vite/server.js.map +1 -0
  169. package/dist/vite/servers/main.d.ts +8 -0
  170. package/dist/vite/servers/main.js +1 -0
  171. package/dist/vite/servers/main.js.map +1 -0
  172. package/dist/vite/servers/worker.d.ts +11 -0
  173. package/dist/vite/servers/worker.js +28 -0
  174. package/dist/vite/servers/worker.js.map +1 -0
  175. package/package.json +31 -18
  176. package/src/cli.ts +144 -0
  177. package/src/config.ts +64 -0
  178. package/src/entrypoints/cli.ts +13 -0
  179. package/src/entrypoints/main.ts +200 -0
  180. package/src/entrypoints/thread.ts +184 -0
  181. package/src/entrypoints/worker.ts +48 -0
  182. package/src/index.ts +82 -0
  183. package/src/resolver.ts +16 -0
  184. package/src/runtime/application/api/api.ts +265 -0
  185. package/src/runtime/application/api/constants.ts +22 -0
  186. package/src/runtime/application/api/filters.ts +39 -0
  187. package/src/runtime/application/api/guards.ts +29 -0
  188. package/src/runtime/application/api/index.ts +8 -0
  189. package/src/runtime/application/api/middlewares.ts +37 -0
  190. package/src/runtime/application/api/procedure.ts +229 -0
  191. package/src/runtime/application/api/router.ts +193 -0
  192. package/src/runtime/application/api/types.ts +124 -0
  193. package/src/runtime/application/config.ts +69 -0
  194. package/src/runtime/application/constants.ts +4 -0
  195. package/src/runtime/application/hook.ts +51 -0
  196. package/src/runtime/application/hooks.ts +3 -0
  197. package/src/runtime/application/index.ts +5 -0
  198. package/src/runtime/constants.ts +13 -0
  199. package/src/runtime/core/hooks.ts +5 -0
  200. package/src/runtime/core/plugin.ts +13 -0
  201. package/src/runtime/core/runtime.ts +109 -0
  202. package/src/runtime/enums.ts +24 -0
  203. package/src/runtime/index.ts +21 -0
  204. package/src/runtime/injectables.ts +61 -0
  205. package/src/runtime/jobs/job.ts +370 -0
  206. package/src/runtime/jobs/manager.ts +348 -0
  207. package/src/runtime/jobs/router.ts +896 -0
  208. package/src/runtime/jobs/runner.ts +320 -0
  209. package/src/runtime/jobs/step.ts +66 -0
  210. package/src/runtime/jobs/ui.ts +21 -0
  211. package/src/runtime/pubsub/manager.ts +211 -0
  212. package/src/runtime/pubsub/redis.ts +108 -0
  213. package/src/runtime/scheduler/index.ts +39 -0
  214. package/src/runtime/server/applications.ts +210 -0
  215. package/src/runtime/server/config.ts +158 -0
  216. package/src/runtime/server/jobs.ts +250 -0
  217. package/src/runtime/server/pool.ts +260 -0
  218. package/src/runtime/server/proxy.ts +118 -0
  219. package/src/runtime/server/server.ts +155 -0
  220. package/src/runtime/store/index.ts +30 -0
  221. package/src/runtime/types.ts +93 -0
  222. package/src/runtime/workers/application.ts +209 -0
  223. package/src/runtime/workers/base.ts +68 -0
  224. package/src/runtime/workers/cli.ts +0 -0
  225. package/src/runtime/workers/job.ts +153 -0
  226. package/src/typings.ts +30 -0
  227. package/src/vite/builder.ts +122 -0
  228. package/src/vite/config.ts +45 -0
  229. package/src/vite/plugins.ts +26 -0
  230. package/src/vite/runners/worker.ts +57 -0
  231. package/src/vite/server.ts +39 -0
  232. package/src/vite/servers/main.ts +34 -0
  233. package/src/vite/servers/worker.ts +143 -0
  234. package/dist/_exports/application.js +0 -1
  235. package/dist/_exports/common.js +0 -1
  236. package/dist/_exports/contract.js +0 -2
  237. package/dist/_exports/core.js +0 -1
  238. package/dist/_exports/gateway.js +0 -1
  239. package/dist/_exports/http-transport/bun.js +0 -1
  240. package/dist/_exports/http-transport/deno.js +0 -1
  241. package/dist/_exports/http-transport/node.js +0 -1
  242. package/dist/_exports/http-transport.js +0 -1
  243. package/dist/_exports/json-format.js +0 -1
  244. package/dist/_exports/protocol/client.js +0 -1
  245. package/dist/_exports/protocol/server.js +0 -1
  246. package/dist/_exports/protocol.js +0 -1
  247. package/dist/_exports/runtime/types.js +0 -1
  248. package/dist/_exports/runtime.js +0 -1
  249. package/dist/_exports/type.js +0 -2
  250. package/dist/_exports/ws-transport/bun.js +0 -1
  251. package/dist/_exports/ws-transport/deno.js +0 -1
  252. package/dist/_exports/ws-transport/node.js +0 -1
  253. package/dist/_exports/ws-transport.js +0 -1
  254. package/dist/command.js +0 -30
package/src/cli.ts ADDED
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node --enable-source-maps
2
+
3
+ import { once } from 'node:events'
4
+ import { relative, resolve } from 'node:path'
5
+ import process from 'node:process'
6
+
7
+ import type { ArgDef } from 'citty'
8
+ import { defineCommand, runMain } from 'citty'
9
+ import { config as dotenv } from 'dotenv'
10
+
11
+ import type { NeemataConfig } from './config.ts'
12
+ import type { ViteConfigOptions } from './vite/config.ts'
13
+ import { resolver } from './resolver.ts'
14
+ import { generateTypings } from './typings.ts'
15
+ import { createBuilder } from './vite/builder.ts'
16
+ import { baseViteConfigOptions } from './vite/config.ts'
17
+ import { createMainServer } from './vite/servers/main.ts'
18
+
19
+ // import { createMainRunner } from './vite/runner.ts'
20
+
21
+ const commonArgs = {
22
+ config: {
23
+ type: 'string',
24
+ alias: 'c',
25
+ default: './neemata.config.ts',
26
+ description: 'Path to Neemata config file',
27
+ required: false,
28
+ },
29
+ } satisfies Record<string, ArgDef>
30
+
31
+ let config: NeemataConfig
32
+ let viteConfigOptions: ViteConfigOptions
33
+ let applicationImports: Record<
34
+ string,
35
+ { path: string; specifier: string; type: 'neemata' | 'custom' }
36
+ >
37
+
38
+ const mainCommand = defineCommand({
39
+ meta: { description: 'Neemata CLI' },
40
+ args: { ...commonArgs },
41
+ async setup(ctx) {
42
+ const configPath = resolve(ctx.args.config as string)
43
+ config = await import(configPath).then((m) => m.default)
44
+
45
+ for (const env of config.env) {
46
+ if (typeof env === 'string') {
47
+ const { error } = dotenv({ path: env })
48
+ if (error) console.warn(error)
49
+ } else if (typeof env === 'object') {
50
+ for (const key in env) {
51
+ process.env[key] = env[key]
52
+ }
53
+ }
54
+ }
55
+
56
+ // const applicationEntryPaths: Record<string, string> = {}
57
+ applicationImports = {}
58
+ const currentPkg = resolver.sync(process.cwd(), './package.json')
59
+
60
+ for (const [appName, { specifier: appSpecifier, type }] of Object.entries(
61
+ config.applications,
62
+ )) {
63
+ const resolution = resolver.sync(process.cwd(), appSpecifier)
64
+ if (resolution.error)
65
+ throw new Error(
66
+ `Failed to resolve application path for ${appName}: ${resolution.error}`,
67
+ )
68
+ if (!resolution.path)
69
+ throw new Error(
70
+ `Failed to resolve application path for ${appName}: no path found`,
71
+ )
72
+ const specifier =
73
+ resolution.packageJsonPath === currentPkg.path
74
+ ? relative(resolve('.neemata'), resolution.path)
75
+ : appSpecifier
76
+ applicationImports[appName] = { path: resolution.path, specifier, type }
77
+ }
78
+
79
+ viteConfigOptions = {
80
+ applicationImports,
81
+ serverEntryPath: resolve(config.serverPath),
82
+ ...baseViteConfigOptions,
83
+ configPath,
84
+ }
85
+ },
86
+ subCommands: {
87
+ prepare: defineCommand({
88
+ async run(ctx) {
89
+ await generateTypings(applicationImports)
90
+ },
91
+ }),
92
+ dev: defineCommand({
93
+ async run(ctx) {
94
+ const { runner } = await createMainServer(
95
+ viteConfigOptions,
96
+ 'development',
97
+ config,
98
+ )
99
+ await runner.import(viteConfigOptions.entrypointMainPath)
100
+ await once(process, 'beforeExit')
101
+ },
102
+ }),
103
+ preview: defineCommand({
104
+ async run(ctx) {
105
+ const { runner } = await createMainServer(
106
+ viteConfigOptions,
107
+ 'production',
108
+ config,
109
+ )
110
+ await runner.import(viteConfigOptions.entrypointMainPath)
111
+ await once(process, 'beforeExit')
112
+ },
113
+ }),
114
+ build: defineCommand({
115
+ async run(ctx) {
116
+ const builder = await createBuilder(viteConfigOptions, config)
117
+ await builder.build()
118
+ },
119
+ }),
120
+ // command: defineCommand({
121
+ // async run(ctx) {
122
+ // const runner = await createRunner(
123
+ // viteConfigOptions,
124
+ // 'production',
125
+ // config,
126
+ // )
127
+ // const workerModule = await runner.import<
128
+ // typeof import('./entrypoints/worker.ts')
129
+ // >(import.meta.resolve('./entrypoints/worker.js'))
130
+ // const commandModule = await runner.import<
131
+ // typeof import('./command.ts')
132
+ // >(import.meta.resolve('./command.js'))
133
+ // const worker = await workerModule.default({
134
+ // applicationWorkerData: undefined,
135
+ // type: ApplicationType.Command,
136
+ // workerType: ApplicationWorkerType.Command,
137
+ // })
138
+ // await runMain(commandModule.default(worker), { rawArgs: ctx.rawArgs })
139
+ // },
140
+ // }),
141
+ },
142
+ })
143
+
144
+ runMain(mainCommand)
package/src/config.ts ADDED
@@ -0,0 +1,64 @@
1
+ import type { Plugin as VitePlugin } from 'vite'
2
+
3
+ export type ExtrernalDependency = string | RegExp
4
+
5
+ type DeepPartial<T> = {
6
+ [P in keyof T]?: T[P] extends Record<any, unknown> ? DeepPartial<T[P]> : T[P]
7
+ }
8
+
9
+ export interface NeemataConfig {
10
+ /**
11
+ * Path to application entry point
12
+ */
13
+ applications: {
14
+ /**
15
+ * Application name
16
+ */
17
+ [appName: string]: { type: 'neemata' | 'custom'; specifier: string }
18
+ }
19
+ /**
20
+ * Path to server entry point
21
+ */
22
+ serverPath: string
23
+ /**
24
+ * External dependencies to exclude from application bundle
25
+ *
26
+ * 'prod' - exclude production dependencies from package.json
27
+ *
28
+ * 'all' - exclude all dependencies from package.json
29
+ *
30
+ * ExtrernalDependency[] - array of package names or regular expressions to match package names
31
+ */
32
+ externalDependencies: 'prod' | 'all' | ExtrernalDependency[]
33
+ /**
34
+ * Timeout in milliseconds for graceful shutdown of application workers
35
+ */
36
+ timeout: number
37
+
38
+ /**
39
+ * Environment variables to set for application workers
40
+ *
41
+ * Strings are paths to .env files
42
+ * Records are key-value pairs to set directly
43
+ */
44
+ env: (Record<string, string> | string)[]
45
+
46
+ build: { outDir: string; minify: boolean }
47
+ plugins: VitePlugin[]
48
+ }
49
+
50
+ export function defineConfig(
51
+ config: DeepPartial<NeemataConfig> = {},
52
+ ): NeemataConfig {
53
+ return {
54
+ serverPath: './src/server.ts',
55
+ externalDependencies: 'prod',
56
+ timeout: 10000,
57
+ env: [],
58
+ plugins: [],
59
+ ...config,
60
+ // @ts-expect-error
61
+ applications: config.applications || {},
62
+ build: { outDir: './dist', minify: true, ...config.build },
63
+ }
64
+ }
@@ -0,0 +1,13 @@
1
+ // // import { ApplicationType, ApplicationWorkerType } from '@nmtjs/application'
2
+ // import { runMain } from 'citty'
3
+
4
+ // import command from '../command.ts'
5
+ // import createWorker from './worker.ts'
6
+
7
+ // const worker = await createWorker({
8
+ // applicationWorkerData: undefined,
9
+ // // type: ApplicationType.Command,
10
+ // // workerType: ApplicationWorkerType.Command,
11
+ // })
12
+
13
+ // runMain(command(worker))
@@ -0,0 +1,200 @@
1
+ import type { Worker } from 'node:worker_threads'
2
+ import EventEmitter from 'node:events'
3
+ import { fileURLToPath } from 'node:url'
4
+
5
+ import type {
6
+ ApplicationWorkerErrorEvent,
7
+ ApplicationWorkerReadyEvent,
8
+ ServerConfig,
9
+ } from 'nmtjs/runtime'
10
+ import type { ViteDevServer } from 'vite'
11
+ import { ApplicationServer, isServerConfig } from 'nmtjs/runtime'
12
+
13
+ import type { WorkerServerEventMap as BaseWorkerServerEventMap } from '../vite/servers/worker.ts'
14
+
15
+ declare global {
16
+ const __VITE_CONFIG__: string
17
+ const __APPLICATIONS_CONFIG__: string
18
+ }
19
+
20
+ class InvalidServerConfigError extends Error {
21
+ constructor() {
22
+ super(
23
+ `Server config file does not have a default export, or it is not a valid server config. Please, make sure the server config is defined using defineServer().`,
24
+ )
25
+ this.name = 'InvalidServerConfigError'
26
+ }
27
+ }
28
+
29
+ const _ext = new URL(import.meta.url).pathname.endsWith('.ts') ? '.ts' : '.js'
30
+ const _vite = __VITE_CONFIG__ ? JSON.parse(__VITE_CONFIG__) : undefined
31
+ const applicationsConfig: Record<
32
+ string,
33
+ { type: 'neemata' | 'custom'; specifier: string }
34
+ > = __APPLICATIONS_CONFIG__ ? JSON.parse(__APPLICATIONS_CONFIG__) : {}
35
+
36
+ type WorkerEventMap = BaseWorkerServerEventMap & {
37
+ 'worker-error': [ApplicationWorkerErrorEvent]
38
+ 'worker-ready': [ApplicationWorkerReadyEvent]
39
+ }
40
+
41
+ let _viteServerEvents: EventEmitter<WorkerEventMap> | undefined
42
+ let _viteWorkerServer: ViteDevServer | undefined
43
+
44
+ let server: ApplicationServer | undefined
45
+ let hasActiveWorkerError = false
46
+ const isDev = _vite?.mode === 'development'
47
+
48
+ if (import.meta.env.DEV && import.meta.hot) {
49
+ import.meta.hot.accept('#server', async (module) => {
50
+ await shutdownServer()
51
+ await bootWithHandling(module?.default)
52
+ })
53
+ }
54
+
55
+ if (_vite) {
56
+ const { createWorkerServer } = await import('../vite/servers/worker.ts')
57
+ const neemataConfig = await import(
58
+ /* @vite-ignore */
59
+ _vite.options.configPath
60
+ ).then((m) => m.default as import('../config.ts').NeemataConfig)
61
+ _viteServerEvents = new EventEmitter<WorkerEventMap>()
62
+ _viteServerEvents.on('worker-error', handleWorkerError)
63
+ _viteServerEvents.on('worker-ready', handleWorkerReady)
64
+ _viteWorkerServer = await createWorkerServer(
65
+ _vite.options,
66
+ _vite.mode,
67
+ neemataConfig,
68
+ _viteServerEvents,
69
+ )
70
+ }
71
+
72
+ async function bootServer(configValue: ServerConfig | undefined) {
73
+ if (!isServerConfig(configValue)) throw new InvalidServerConfigError()
74
+ const workerConfig = {
75
+ path: fileURLToPath(import.meta.resolve(`./thread${_ext}`)),
76
+ workerData: { vite: _vite?.mode },
77
+ worker: _viteServerEvents
78
+ ? (worker: Worker) => {
79
+ _viteServerEvents.emit('worker', worker)
80
+ }
81
+ : undefined,
82
+ events: _viteServerEvents,
83
+ }
84
+ const appServer = new ApplicationServer(
85
+ configValue,
86
+ applicationsConfig,
87
+ workerConfig,
88
+ )
89
+
90
+ try {
91
+ await appServer.start()
92
+ server = appServer
93
+ clearWorkerErrorOverlay()
94
+ } catch (error) {
95
+ await appServer.stop().catch(() => {})
96
+ throw error
97
+ }
98
+ }
99
+
100
+ async function bootWithHandling(configValue: ServerConfig | undefined) {
101
+ try {
102
+ await bootServer(configValue)
103
+ } catch (error) {
104
+ handleStartupError(error)
105
+ if (!isDev) throw error
106
+ }
107
+ }
108
+
109
+ let isTerminating = false
110
+
111
+ async function handleTermination() {
112
+ if (isTerminating) return
113
+ isTerminating = true
114
+ await shutdownServer()
115
+ _viteWorkerServer?.close()
116
+ process.exit(0)
117
+ }
118
+
119
+ function handleUnexpectedError(error: Error) {
120
+ console.error(new Error('Unexpected Error:', { cause: error }))
121
+ }
122
+
123
+ async function shutdownServer() {
124
+ if (!server) return
125
+ try {
126
+ await server.stop()
127
+ } catch (error) {
128
+ console.error(
129
+ new Error('Failed to stop application server', { cause: error as Error }),
130
+ )
131
+ } finally {
132
+ server = undefined
133
+ }
134
+ }
135
+
136
+ function handleWorkerError(event: ApplicationWorkerErrorEvent) {
137
+ hasActiveWorkerError = true
138
+ console.error(
139
+ new Error(`Worker ${event.application} thread ${event.threadId} error`, {
140
+ cause: event.error,
141
+ }),
142
+ )
143
+ }
144
+
145
+ function handleWorkerReady(_: ApplicationWorkerReadyEvent) {
146
+ clearWorkerErrorOverlay()
147
+ }
148
+
149
+ function handleStartupError(error: unknown) {
150
+ const normalized = error instanceof Error ? error : new Error(String(error))
151
+ if (_viteServerEvents) {
152
+ _viteServerEvents.emit('worker-error', {
153
+ application: 'server',
154
+ threadId: -1,
155
+ error: normalized,
156
+ } as ApplicationWorkerErrorEvent)
157
+ } else {
158
+ hasActiveWorkerError = true
159
+ console.error(
160
+ new Error('Failed to start application server', { cause: normalized }),
161
+ )
162
+ }
163
+ }
164
+
165
+ function clearWorkerErrorOverlay() {
166
+ if (!hasActiveWorkerError) return
167
+ hasActiveWorkerError = false
168
+ }
169
+
170
+ process.once('SIGTERM', handleTermination)
171
+ process.once('SIGINT', handleTermination)
172
+ process.on('uncaughtException', handleUnexpectedError)
173
+ process.on('unhandledRejection', handleUnexpectedError)
174
+
175
+ await bootWithHandling(
176
+ await import(
177
+ // @ts-expect-error
178
+ '#server'
179
+ ).then((m) => m.default),
180
+ ).catch(() => {
181
+ if (!isDev) process.exit(1)
182
+ })
183
+
184
+ const { format } = Intl.NumberFormat('en', {
185
+ notation: 'compact',
186
+ maximumFractionDigits: 2,
187
+ unit: 'byte',
188
+ })
189
+
190
+ const printMem = () => {
191
+ globalThis.gc?.()
192
+ // print memory usage every 10 seconds
193
+ const memoryUsage = process.memoryUsage()
194
+ console.log(
195
+ `Memory Usage: RSS=${format(memoryUsage.rss)}, HeapTotal=${format(memoryUsage.heapTotal)}, HeapUsed=${format(memoryUsage.heapUsed)}, External=${format(memoryUsage.external)}, ArrayBuffers=${format(memoryUsage.arrayBuffers)}`,
196
+ )
197
+ }
198
+ void printMem
199
+ // printMem()
200
+ // setInterval(printMem, 5000)
@@ -0,0 +1,184 @@
1
+ import type { MessagePort } from 'node:worker_threads'
2
+ import { fileURLToPath } from 'node:url'
3
+ import { workerData as _workerData } from 'node:worker_threads'
4
+
5
+ import type {
6
+ ThreadErrorMessage,
7
+ ThreadPortMessage,
8
+ WorkerThreadErrorOrigin,
9
+ } from 'nmtjs/runtime'
10
+ import type { ModuleRunner } from 'vite/module-runner'
11
+
12
+ export type RunWorkerOptions = {
13
+ port: MessagePort
14
+ runtime:
15
+ | { type: 'application'; name: string; path: string; transportsData: any }
16
+ | { type: 'jobs'; jobWorkerPool: string }
17
+ vite?: 'development' | 'production'
18
+ }
19
+
20
+ const workerData = _workerData as RunWorkerOptions
21
+
22
+ const ext = new URL(import.meta.url).pathname.endsWith('.ts') ? '.ts' : '.js'
23
+ const workerPath = fileURLToPath(import.meta.resolve(`./worker${ext}`))
24
+
25
+ type WorkerModule = typeof import('./worker.ts')
26
+ type WorkerRuntime = Awaited<ReturnType<WorkerModule['run']>>
27
+
28
+ const kReportedError = Symbol.for('nmtjs.worker.reported-error')
29
+
30
+ let runner: ModuleRunner | undefined
31
+ let workerModule: WorkerModule
32
+ let runtime: WorkerRuntime | undefined
33
+ let runtimeStarted = false
34
+
35
+ process.on('uncaughtException', (error) => {
36
+ reportError(error, 'runtime', { fatal: true })
37
+ })
38
+
39
+ process.on('unhandledRejection', (error) => {
40
+ reportError(error, 'runtime', { fatal: true })
41
+ })
42
+
43
+ process.on('exit', () => {
44
+ void cleanup()
45
+ })
46
+
47
+ async function cleanup() {
48
+ await stopRuntime()
49
+ await closeRunner()
50
+ }
51
+
52
+ async function closeRunner() {
53
+ if (!runner) return
54
+ try {
55
+ await runner.close()
56
+ } catch (error) {
57
+ reportError(error, 'runtime', { fatal: false })
58
+ } finally {
59
+ runner = undefined
60
+ }
61
+ }
62
+
63
+ async function stopRuntime() {
64
+ if (!runtime) return
65
+ try {
66
+ if (runtimeStarted) {
67
+ await runtime.stop()
68
+ }
69
+ } catch (error) {
70
+ reportError(error, 'runtime', { fatal: false })
71
+ } finally {
72
+ runtimeStarted = false
73
+ }
74
+ }
75
+
76
+ function normalizeError(value: unknown): Error {
77
+ if (value instanceof Error) return value
78
+ if (typeof value === 'string') return new Error(value)
79
+ if (value && typeof value === 'object') {
80
+ try {
81
+ return new Error(JSON.stringify(value))
82
+ } catch {}
83
+ }
84
+ return new Error(String(value))
85
+ }
86
+
87
+ function serializeError(
88
+ error: Error,
89
+ origin: WorkerThreadErrorOrigin,
90
+ fatal: boolean,
91
+ ): ThreadErrorMessage {
92
+ return {
93
+ message: error.message,
94
+ name: error.name,
95
+ stack: error.stack,
96
+ origin,
97
+ fatal,
98
+ }
99
+ }
100
+
101
+ function reportError(
102
+ value: unknown,
103
+ origin: WorkerThreadErrorOrigin,
104
+ options: { fatal?: boolean } = {},
105
+ ): Error {
106
+ const fatal = options.fatal ?? origin !== 'runtime'
107
+ const error = normalizeError(value)
108
+ if (!(error as any)[kReportedError]) {
109
+ try {
110
+ workerData.port.postMessage({
111
+ type: 'error',
112
+ data: serializeError(error, origin, fatal),
113
+ } satisfies ThreadPortMessage)
114
+ } catch {}
115
+ console.error(new Error(`Worker thread ${origin} error`, { cause: error }))
116
+ ;(error as any)[kReportedError] = true
117
+ }
118
+ return error
119
+ }
120
+
121
+ async function loadWorkerModule() {
122
+ try {
123
+ if (workerData.vite) {
124
+ const { createModuleRunner } = (await import(
125
+ '../vite/runners/worker.ts'
126
+ )) as typeof import('../vite/runners/worker.ts')
127
+
128
+ runner = createModuleRunner(workerData.vite)
129
+ workerModule = await runner.import(workerPath)
130
+ } else {
131
+ runner = undefined
132
+ workerModule = await import(
133
+ /* @vite-ignore */
134
+ workerPath
135
+ )
136
+ }
137
+ } catch (error) {
138
+ throw reportError(error, 'bootstrap')
139
+ }
140
+ }
141
+
142
+ async function initializeRuntime() {
143
+ try {
144
+ runtime = await workerModule.run(workerData.runtime)
145
+ } catch (error) {
146
+ throw reportError(error, 'bootstrap')
147
+ }
148
+
149
+ workerData.port.on('message', async (msg) => {
150
+ if (msg.type === 'stop') {
151
+ await cleanup()
152
+ process.exit(0)
153
+ }
154
+ })
155
+ }
156
+
157
+ async function startRuntime() {
158
+ if (!runtime) return
159
+ try {
160
+ const hosts = (await runtime.start()) || undefined
161
+ runtimeStarted = true
162
+ workerData.port.postMessage({
163
+ type: 'ready',
164
+ data: { hosts },
165
+ } satisfies ThreadPortMessage)
166
+ } catch (error) {
167
+ throw reportError(error, 'start')
168
+ }
169
+ }
170
+
171
+ async function main() {
172
+ await loadWorkerModule()
173
+ await initializeRuntime()
174
+ await startRuntime()
175
+ }
176
+
177
+ main().catch(async (error) => {
178
+ const normalized = error instanceof Error ? error : normalizeError(error)
179
+ if (!(normalized as any)[kReportedError]) {
180
+ reportError(normalized, 'bootstrap')
181
+ }
182
+ await cleanup()
183
+ process.exit(1)
184
+ })
@@ -0,0 +1,48 @@
1
+ import { workerData } from 'node:worker_threads'
2
+
3
+ import type { ServerConfig } from 'nmtjs/runtime'
4
+ import {
5
+ ApplicationWorkerRuntime,
6
+ isApplicationConfig,
7
+ JobWorkerRuntime,
8
+ } from 'nmtjs/runtime'
9
+
10
+ import type { RunWorkerOptions } from './thread.ts'
11
+
12
+ export async function run(options: RunWorkerOptions['runtime']) {
13
+ const serverConfig: ServerConfig = await import(
14
+ // @ts-expect-error
15
+ '#server'
16
+ ).then((m) => m.default)
17
+ if (options.type === 'application') {
18
+ globalThis._hotAccept = (module: any) => {
19
+ if (module) {
20
+ if (!isApplicationConfig(module.default))
21
+ throw new Error('Invalid application config')
22
+ runtime.reload(module.default)
23
+ }
24
+ }
25
+
26
+ const { name, path, transportsData } = options
27
+ const appConfig = await import(
28
+ /* @vite-ignore */
29
+ path
30
+ ).then((m) => m.default)
31
+
32
+ const runtime = new ApplicationWorkerRuntime(
33
+ serverConfig,
34
+ { name, path, transports: transportsData },
35
+ appConfig,
36
+ )
37
+ return runtime
38
+ } else if (options.type === 'jobs') {
39
+ const { jobWorkerPool } = options
40
+ const runtime = new JobWorkerRuntime(serverConfig, {
41
+ poolName: jobWorkerPool,
42
+ port: workerData.port,
43
+ })
44
+ return runtime
45
+ } else {
46
+ throw new Error(`Unknown runtime type: ${(workerData.runtime as any).type}`)
47
+ }
48
+ }