nmtjs 0.15.0-beta.1 → 0.15.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/_exports/application.js +1 -0
  2. package/dist/_exports/common.js +1 -0
  3. package/dist/_exports/contract.js +2 -0
  4. package/dist/_exports/core.js +1 -0
  5. package/dist/_exports/gateway.js +1 -0
  6. package/dist/_exports/http-transport/bun.js +1 -0
  7. package/dist/_exports/http-transport/deno.js +1 -0
  8. package/dist/_exports/http-transport/node.js +1 -0
  9. package/dist/_exports/http-transport.js +1 -0
  10. package/dist/_exports/index.js +39 -0
  11. package/dist/_exports/json-format.js +1 -0
  12. package/dist/_exports/protocol/client.js +1 -0
  13. package/dist/_exports/protocol/server.js +1 -0
  14. package/dist/_exports/protocol.js +1 -0
  15. package/dist/_exports/runtime/types.js +1 -0
  16. package/dist/_exports/runtime.js +1 -0
  17. package/dist/_exports/type.js +2 -0
  18. package/dist/_exports/ws-transport/bun.js +1 -0
  19. package/dist/_exports/ws-transport/deno.js +1 -0
  20. package/dist/_exports/ws-transport/node.js +1 -0
  21. package/dist/_exports/ws-transport.js +1 -0
  22. package/dist/cli.js +121 -0
  23. package/dist/command.js +30 -0
  24. package/dist/config.js +13 -0
  25. package/dist/entrypoints/cli.js +11 -0
  26. package/dist/entrypoints/main.js +85 -0
  27. package/dist/entrypoints/thread.js +52 -0
  28. package/dist/entrypoints/worker.js +41 -0
  29. package/dist/resolver.js +14 -0
  30. package/dist/typings.js +17 -0
  31. package/dist/vite/builder.js +109 -0
  32. package/dist/vite/config.js +21 -0
  33. package/dist/vite/plugins.js +17 -0
  34. package/dist/vite/runners/worker.js +32 -0
  35. package/dist/vite/server.js +17 -0
  36. package/dist/vite/servers/main.js +22 -0
  37. package/dist/vite/servers/worker.js +78 -0
  38. package/package.json +13 -13
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/runtime/application';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/common';
@@ -0,0 +1,2 @@
1
+ export * from '@nmtjs/contract';
2
+ export { default } from '@nmtjs/contract';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/core';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/gateway';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/http-transport/bun';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/http-transport/deno';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/http-transport/node';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/http-transport';
@@ -0,0 +1,39 @@
1
+ import { CoreInjectables, createConsolePrettyDestination, createFactoryInjectable, createLazyInjectable, createOptionalInjectable, createValueInjectable, } from '@nmtjs/core';
2
+ import { createTransport, GatewayInjectables } from '@nmtjs/gateway';
3
+ import { createContractProcedure, createContractRouter, createFilter, createGuard, createHook, createJob, createMiddleware, createPlugin, createProcedure, createRouter, createStep, defineApplication, RuntimeInjectables, } from '@nmtjs/runtime';
4
+ export const neemata = {
5
+ app: defineApplication,
6
+ injectables: {
7
+ ...CoreInjectables,
8
+ ...GatewayInjectables,
9
+ ...RuntimeInjectables,
10
+ },
11
+ transport: createTransport,
12
+ plugin: createPlugin,
13
+ logging: {
14
+ console:
15
+ // TODO: TSC wants it
16
+ createConsolePrettyDestination,
17
+ },
18
+ optional: createOptionalInjectable,
19
+ value: createValueInjectable,
20
+ lazy: createLazyInjectable,
21
+ factory: createFactoryInjectable,
22
+ router: createRouter,
23
+ contractRouter: createContractRouter,
24
+ procedure: createProcedure,
25
+ contractProcedure: createContractProcedure,
26
+ middleware: createMiddleware,
27
+ guard: createGuard,
28
+ filter: createFilter,
29
+ job: createJob,
30
+ step: createStep,
31
+ hook: createHook,
32
+ };
33
+ export { c } from '@nmtjs/contract';
34
+ export { Scope } from '@nmtjs/core';
35
+ export { ErrorCode, ProtocolBlob } from '@nmtjs/protocol';
36
+ export { t } from '@nmtjs/type';
37
+ export { ApiError, defineApplication, LifecycleHook } from 'nmtjs/runtime';
38
+ export { neemata as n };
39
+ export default neemata;
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/json-format/server';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/protocol/client';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/protocol/server';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/protocol';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/runtime/types';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/runtime';
@@ -0,0 +1,2 @@
1
+ export * from '@nmtjs/type';
2
+ export { default } from '@nmtjs/type';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/ws-transport/bun';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/ws-transport/deno';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/ws-transport/node';
@@ -0,0 +1 @@
1
+ export * from '@nmtjs/ws-transport';
package/dist/cli.js ADDED
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node --enable-source-maps
2
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
3
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
4
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
5
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
6
+ });
7
+ }
8
+ return path;
9
+ };
10
+ import { once } from 'node:events';
11
+ import { relative, resolve } from 'node:path';
12
+ import process from 'node:process';
13
+ import { defineCommand, runMain } from 'citty';
14
+ import { config as dotenv } from 'dotenv';
15
+ import { resolver } from "./resolver.js";
16
+ import { generateTypings } from "./typings.js";
17
+ import { createBuilder } from "./vite/builder.js";
18
+ import { baseViteConfigOptions } from "./vite/config.js";
19
+ import { createMainServer } from "./vite/servers/main.js";
20
+ // import { createMainRunner } from './vite/runner.ts'
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
+ };
30
+ let config;
31
+ let viteConfigOptions;
32
+ let applicationImports;
33
+ const mainCommand = defineCommand({
34
+ meta: { description: 'Neemata CLI' },
35
+ args: { ...commonArgs },
36
+ async setup(ctx) {
37
+ const configPath = resolve(ctx.args.config);
38
+ config = await import(__rewriteRelativeImportExtension(configPath)).then((m) => m.default);
39
+ for (const env of config.env) {
40
+ if (typeof env === 'string') {
41
+ const { error } = dotenv({ path: env });
42
+ if (error)
43
+ console.warn(error);
44
+ }
45
+ else if (typeof env === 'object') {
46
+ for (const key in env) {
47
+ process.env[key] = env[key];
48
+ }
49
+ }
50
+ }
51
+ // const applicationEntryPaths: Record<string, string> = {}
52
+ applicationImports = {};
53
+ const currentPkg = resolver.sync(process.cwd(), './package.json');
54
+ for (const [appName, appSpecifier] of Object.entries(config.applications)) {
55
+ const resolution = resolver.sync(process.cwd(), appSpecifier);
56
+ if (resolution.error)
57
+ throw new Error(`Failed to resolve application path for ${appName}: ${resolution.error}`);
58
+ if (!resolution.path)
59
+ throw new Error(`Failed to resolve application path for ${appName}: no path found`);
60
+ const specifier = resolution.packageJsonPath === currentPkg.path
61
+ ? relative(resolve('.neemata'), resolution.path)
62
+ : appSpecifier;
63
+ applicationImports[appName] = { path: resolution.path, specifier };
64
+ }
65
+ viteConfigOptions = {
66
+ applicationImports,
67
+ serverEntryPath: resolve(config.serverPath),
68
+ ...baseViteConfigOptions,
69
+ configPath,
70
+ };
71
+ },
72
+ subCommands: {
73
+ prepare: defineCommand({
74
+ async run(ctx) {
75
+ await generateTypings(applicationImports);
76
+ },
77
+ }),
78
+ dev: defineCommand({
79
+ async run(ctx) {
80
+ const { runner } = await createMainServer(viteConfigOptions, 'development', config);
81
+ await runner.import(viteConfigOptions.entrypointMainPath);
82
+ await once(process, 'beforeExit');
83
+ },
84
+ }),
85
+ preview: defineCommand({
86
+ async run(ctx) {
87
+ const { runner } = await createMainServer(viteConfigOptions, 'production', config);
88
+ await runner.import(viteConfigOptions.entrypointMainPath);
89
+ await once(process, 'beforeExit');
90
+ },
91
+ }),
92
+ build: defineCommand({
93
+ async run(ctx) {
94
+ const builder = await createBuilder(viteConfigOptions, config);
95
+ await builder.build();
96
+ },
97
+ }),
98
+ // command: defineCommand({
99
+ // async run(ctx) {
100
+ // const runner = await createRunner(
101
+ // viteConfigOptions,
102
+ // 'production',
103
+ // config,
104
+ // )
105
+ // const workerModule = await runner.import<
106
+ // typeof import('./entrypoints/worker.ts')
107
+ // >(import.meta.resolve('./entrypoints/worker.js'))
108
+ // const commandModule = await runner.import<
109
+ // typeof import('./command.ts')
110
+ // >(import.meta.resolve('./command.js'))
111
+ // const worker = await workerModule.default({
112
+ // applicationWorkerData: undefined,
113
+ // type: ApplicationType.Command,
114
+ // workerType: ApplicationWorkerType.Command,
115
+ // })
116
+ // await runMain(commandModule.default(worker), { rawArgs: ctx.rawArgs })
117
+ // },
118
+ // }),
119
+ },
120
+ });
121
+ runMain(mainCommand);
@@ -0,0 +1,30 @@
1
+ // import type { ApplicationWorker } from '@nmtjs/runtime/worker'
2
+ // import { typeToString } from '@nmtjs/type'
3
+ // import { defineCommand } from 'citty'
4
+ export {};
5
+ // export default (worker: ApplicationWorker) =>
6
+ // defineCommand({
7
+ // meta: { description: 'Application CLI' },
8
+ // subCommands: {
9
+ // list: defineCommand({
10
+ // async run(ctx) {
11
+ // worker.app.initializeCore()
12
+ // const commands = Array.from(worker.app.registry.commands).map(
13
+ // ([name, command]) => ({
14
+ // command: name,
15
+ // args: typeToString(command.args),
16
+ // kwargs: typeToString(command.kwargs),
17
+ // }),
18
+ // )
19
+ // console.table(commands, ['command', 'args', 'kwargs'])
20
+ // },
21
+ // }),
22
+ // execute: defineCommand({
23
+ // async run(ctx) {
24
+ // const { _: positionals, ...kwargs } = ctx.args
25
+ // const [commandName, ...args] = positionals
26
+ // await worker.runCommand(commandName, args, kwargs)
27
+ // },
28
+ // }),
29
+ // },
30
+ // })
package/dist/config.js ADDED
@@ -0,0 +1,13 @@
1
+ export function defineConfig(config = {}) {
2
+ return {
3
+ serverPath: './src/server.ts',
4
+ externalDependencies: 'prod',
5
+ timeout: 10000,
6
+ env: [],
7
+ plugins: [],
8
+ ...config,
9
+ // @ts-expect-error
10
+ applications: config.applications || {},
11
+ build: { outDir: './dist', minify: true, ...config.build },
12
+ };
13
+ }
@@ -0,0 +1,11 @@
1
+ // // import { ApplicationType, ApplicationWorkerType } from '@nmtjs/application'
2
+ // import { runMain } from 'citty'
3
+ export {};
4
+ // import command from '../command.ts'
5
+ // import createWorker from './worker.ts'
6
+ // const worker = await createWorker({
7
+ // applicationWorkerData: undefined,
8
+ // // type: ApplicationType.Command,
9
+ // // workerType: ApplicationWorkerType.Command,
10
+ // })
11
+ // runMain(command(worker))
@@ -0,0 +1,85 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import EventEmitter from 'node:events';
10
+ import { fileURLToPath } from 'node:url';
11
+ import { ApplicationServer, isServerConfig } from '@nmtjs/runtime';
12
+ class InvalidServerConfigError extends Error {
13
+ constructor() {
14
+ super(`Server config file does not have a default export, or it not a valid application. Please, make sure the application is defined using defineApplication().`);
15
+ this.name = 'InvalidServerConfigError';
16
+ }
17
+ }
18
+ const _ext = new URL(import.meta.url).pathname.endsWith('.ts') ? '.ts' : '.js';
19
+ const _vite = __VITE_CONFIG__ ? JSON.parse(__VITE_CONFIG__) : undefined;
20
+ const applicationsConfig = __APPLICATIONS_CONFIG__
21
+ ? JSON.parse(__APPLICATIONS_CONFIG__)
22
+ : {};
23
+ let _viteServerEvents;
24
+ let _viteWorkerServer;
25
+ let server;
26
+ if (import.meta.env.DEV && import.meta.hot) {
27
+ import.meta.hot.accept('#server', async (module) => {
28
+ await server.stop();
29
+ await createServer(module?.default);
30
+ });
31
+ }
32
+ if (_vite) {
33
+ const { createWorkerServer } = await import("../vite/servers/worker.js");
34
+ const neemataConfig = await import(__rewriteRelativeImportExtension(
35
+ /* @vite-ignore */
36
+ _vite.options.configPath)).then((m) => m.default);
37
+ _viteServerEvents = new EventEmitter();
38
+ _viteWorkerServer = await createWorkerServer(_vite.options, _vite.mode, neemataConfig, _viteServerEvents);
39
+ }
40
+ async function createServer(config) {
41
+ if (!isServerConfig(config))
42
+ throw new InvalidServerConfigError();
43
+ server = new ApplicationServer(config, applicationsConfig, {
44
+ path: fileURLToPath(import.meta.resolve(`./thread${_ext}`)),
45
+ workerData: { vite: _vite?.mode },
46
+ worker: _viteServerEvents
47
+ ? (worker) => {
48
+ _viteServerEvents.emit('worker', worker);
49
+ }
50
+ : undefined,
51
+ });
52
+ await server.start();
53
+ }
54
+ let isTerminating = false;
55
+ async function handleTermination() {
56
+ if (isTerminating)
57
+ return;
58
+ isTerminating = true;
59
+ await server?.stop();
60
+ _viteWorkerServer?.close();
61
+ process.exit(0);
62
+ }
63
+ function handleUnexpectedError(error) {
64
+ console.error(new Error('Unexpected Error:', { cause: error }));
65
+ }
66
+ process.once('SIGTERM', handleTermination);
67
+ process.once('SIGINT', handleTermination);
68
+ process.on('uncaughtException', handleUnexpectedError);
69
+ process.on('unhandledRejection', handleUnexpectedError);
70
+ await createServer(await import(
71
+ // @ts-expect-error
72
+ '#server').then((m) => m.default));
73
+ const { format } = Intl.NumberFormat('en', {
74
+ notation: 'compact',
75
+ maximumFractionDigits: 2,
76
+ unit: 'byte',
77
+ });
78
+ const printMem = () => {
79
+ globalThis.gc?.();
80
+ // print memory usage every 10 seconds
81
+ const memoryUsage = process.memoryUsage();
82
+ console.log(`Memory Usage: RSS=${format(memoryUsage.rss)}, HeapTotal=${format(memoryUsage.heapTotal)}, HeapUsed=${format(memoryUsage.heapUsed)}, External=${format(memoryUsage.external)}, ArrayBuffers=${format(memoryUsage.arrayBuffers)}`);
83
+ };
84
+ // printMem()
85
+ // setInterval(printMem, 5000)
@@ -0,0 +1,52 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { fileURLToPath } from 'node:url';
10
+ import { workerData as _workerData } from 'node:worker_threads';
11
+ const workerData = _workerData;
12
+ const ext = new URL(import.meta.url).pathname.endsWith('.ts') ? '.ts' : '.js';
13
+ const workerPath = fileURLToPath(import.meta.resolve(`./worker${ext}`));
14
+ process.on('uncaughtException', (error) => {
15
+ console.error(new Error('Uncaught Exception:', { cause: error }));
16
+ });
17
+ process.on('unhandledRejection', (error) => {
18
+ console.error(new Error('Unhandled Promise Rejection:', { cause: error }));
19
+ });
20
+ process.on('beforeExit', (code) => {
21
+ runner?.close();
22
+ });
23
+ let runner;
24
+ let workerModule;
25
+ try {
26
+ if (workerData.vite) {
27
+ const { createModuleRunner } = (await import("../vite/runners/worker.js"));
28
+ runner = createModuleRunner(workerData.vite);
29
+ workerModule = await runner.import(workerPath);
30
+ }
31
+ else {
32
+ runner = undefined;
33
+ workerModule = await import(__rewriteRelativeImportExtension(
34
+ /* @vite-ignore */
35
+ workerPath));
36
+ }
37
+ const runtime = await workerModule.run(workerData.runtime);
38
+ workerData.port.on('message', async (msg) => {
39
+ if (msg.type === 'stop') {
40
+ await runtime.stop();
41
+ process.exit(0);
42
+ }
43
+ });
44
+ const hosts = (await runtime?.start()) || undefined;
45
+ workerData.port.postMessage({
46
+ type: 'ready',
47
+ data: { hosts },
48
+ });
49
+ }
50
+ catch (error) {
51
+ console.error(new Error('Worker thread error:', { cause: error }));
52
+ }
@@ -0,0 +1,41 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { workerData } from 'node:worker_threads';
10
+ import { ApplicationWorkerRuntime, isApplicationConfig, JobWorkerRuntime, } from '@nmtjs/runtime';
11
+ export async function run(options) {
12
+ const serverConfig = await import(
13
+ // @ts-expect-error
14
+ '#server').then((m) => m.default);
15
+ if (options.type === 'application') {
16
+ globalThis._hotAccept = (module) => {
17
+ if (module) {
18
+ if (!isApplicationConfig(module.default))
19
+ throw new Error('Invalid application config');
20
+ runtime.reload(module.default);
21
+ }
22
+ };
23
+ const { name, path, transportsData } = options;
24
+ const appConfig = await import(__rewriteRelativeImportExtension(
25
+ /* @vite-ignore */
26
+ path)).then((m) => m.default);
27
+ const runtime = new ApplicationWorkerRuntime(serverConfig, { name, path, transports: transportsData }, appConfig);
28
+ return runtime;
29
+ }
30
+ else if (options.type === 'jobs') {
31
+ const { jobWorkerQueue } = options;
32
+ const runtime = new JobWorkerRuntime(serverConfig, {
33
+ queueName: jobWorkerQueue,
34
+ port: workerData.port,
35
+ });
36
+ return runtime;
37
+ }
38
+ else {
39
+ throw new Error(`Unknown runtime type: ${workerData.runtime.type}`);
40
+ }
41
+ }
@@ -0,0 +1,14 @@
1
+ import { ResolverFactory } from 'oxc-resolver';
2
+ const fallback = {};
3
+ try {
4
+ // oxc-resolver fails to resolve uWebSockets.js for some reason
5
+ const mdl = 'uWebSockets.js';
6
+ const path = import.meta.resolve(mdl);
7
+ fallback[mdl] = [path];
8
+ }
9
+ catch { }
10
+ export const resolver = new ResolverFactory({
11
+ tsconfig: 'auto',
12
+ extensions: ['.ts', '.js', '.mjs', '.mts', '.json', '.node'],
13
+ fallback,
14
+ });
@@ -0,0 +1,17 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ import dedent from 'dedent';
4
+ export async function generateTypings(applicationImports) {
5
+ await mkdir('.neemata', { recursive: true }).catch(() => { });
6
+ await writeFile(resolve('.neemata', 'types.d.ts'), dedent `
7
+ /// <reference types="@nmtjs/runtime/types" />
8
+
9
+ declare module '@nmtjs/runtime/types' {
10
+ interface Applications {
11
+ ${Object.entries(applicationImports)
12
+ .map(([appName, { specifier }]) => `'${appName}': typeof import('${specifier}').default`)
13
+ .join('\n')}
14
+ }
15
+ }
16
+ `);
17
+ }
@@ -0,0 +1,109 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { isBuiltin } from 'node:module';
10
+ import { resolve } from 'node:path';
11
+ import { build as viteBuild } from 'vite';
12
+ // import pkgJson from '../../package.json' with { type: 'json' }
13
+ import { createConfig } from "./config.js";
14
+ import { buildPlugins } from "./plugins.js";
15
+ export async function createBuilder(configOptions, neemataConfig) {
16
+ const config = createConfig(configOptions);
17
+ async function build() {
18
+ const packageJson = await import(__rewriteRelativeImportExtension(resolve('./package.json')), {
19
+ with: { type: 'json' },
20
+ }).then((mod) => mod.default);
21
+ // techinically it's possible to do the same with rolldown directly,
22
+ // but vite handles a lot of things, like defines substitutions, etc.
23
+ // also, since during dev the code is processed via vite anyway,
24
+ // using vite for build as well ensures consistency between dev and prod
25
+ return await viteBuild({
26
+ appType: 'custom',
27
+ clearScreen: false,
28
+ resolve: { alias: config.alias },
29
+ ssr: { noExternal: true },
30
+ plugins: [...buildPlugins, ...neemataConfig.plugins],
31
+ build: {
32
+ lib: { entry: config.entries, formats: ['es'] },
33
+ ssr: true,
34
+ target: 'node20',
35
+ sourcemap: true,
36
+ outDir: resolve(neemataConfig.build.outDir),
37
+ minify: neemataConfig.build.minify,
38
+ emptyOutDir: true,
39
+ rolldownOptions: {
40
+ platform: 'node',
41
+ external: (id) => {
42
+ if (neemataConfig.externalDependencies === 'all')
43
+ return true;
44
+ if (isBuiltin(id) ||
45
+ id.includes('vite/server') ||
46
+ id.endsWith('.node'))
47
+ return true;
48
+ if (neemataConfig.externalDependencies === 'prod') {
49
+ const prodDeps = Object.keys(packageJson.dependencies ?? {});
50
+ if (prodDeps.includes(id))
51
+ return true;
52
+ }
53
+ if (Array.isArray(neemataConfig.externalDependencies)) {
54
+ for (const dep of neemataConfig.externalDependencies) {
55
+ if (typeof dep === 'string' && dep === id)
56
+ return true;
57
+ if (dep instanceof RegExp && dep.test(id))
58
+ return true;
59
+ }
60
+ }
61
+ return false;
62
+ },
63
+ transform: {
64
+ define: {
65
+ __VITE_CONFIG__: '""',
66
+ __APPLICATIONS_CONFIG__: JSON.stringify(JSON.stringify(Object.fromEntries(Object.keys(configOptions.applicationImports).map((appName) => [appName, `./application.${appName}.js`])))),
67
+ __dirname: 'new URL(".", import.meta.url).pathname',
68
+ __filename: 'new URL(import.meta.url).pathname',
69
+ },
70
+ },
71
+ output: {
72
+ entryFileNames: '[name].js',
73
+ chunkFileNames: 'chunks/[name]-[hash].js',
74
+ advancedChunks: {
75
+ groups: [
76
+ {
77
+ name: 'ioredis',
78
+ test: /node_modules[\\/](@ioredis|ioredis|redis)/,
79
+ priority: 4,
80
+ },
81
+ {
82
+ name: 'bullmq',
83
+ test: /node_modules[\\/]bullmq/,
84
+ priority: 2,
85
+ },
86
+ { name: 'zod', test: /node_modules[\\/]zod/, priority: 2 },
87
+ { name: 'pino', test: /node_modules[\\/]pino/, priority: 2 },
88
+ {
89
+ name: '@nmtjs-runtime',
90
+ test: /node_modules[\\/](@nmtjs[\\/]runtime)/,
91
+ priority: 2,
92
+ },
93
+ {
94
+ name: '@nmtjs-common',
95
+ test: /node_modules[\\/]@nmtjs[\\/](?=[^runtime|nmtjs])/,
96
+ priority: 1,
97
+ },
98
+ { name: 'vendor', test: /node_modules/, priority: 0 },
99
+ ],
100
+ },
101
+ minify: neemataConfig.build.minify,
102
+ },
103
+ },
104
+ chunkSizeWarningLimit: 10_000,
105
+ },
106
+ });
107
+ }
108
+ return { build };
109
+ }
@@ -0,0 +1,21 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ const ext = new URL(import.meta.url).pathname.endsWith('.ts') ? '.ts' : '.js';
3
+ export const baseViteConfigOptions = {
4
+ entrypointMainPath: fileURLToPath(import.meta.resolve(`../entrypoints/main${ext}`)),
5
+ entrypointWorkerPath: fileURLToPath(import.meta.resolve(`../entrypoints/worker${ext}`)),
6
+ entrypointThreadPath: fileURLToPath(import.meta.resolve(`../entrypoints/thread${ext}`)),
7
+ // entrypointCLIPath: fileURLToPath(import.meta.resolve('../entrypoints/cli')),
8
+ };
9
+ export function createConfig(options) {
10
+ const alias = { '#server': options.serverEntryPath };
11
+ const entries = {
12
+ server: options.serverEntryPath,
13
+ main: options.entrypointMainPath,
14
+ thread: options.entrypointThreadPath,
15
+ worker: options.entrypointWorkerPath,
16
+ };
17
+ for (const [name, { path }] of Object.entries(options.applicationImports)) {
18
+ entries[`application.${name}`] = path;
19
+ }
20
+ return { alias, entries };
21
+ }
@@ -0,0 +1,17 @@
1
+ import { dirname, join } from 'node:path';
2
+ import { viteStaticCopy } from 'vite-plugin-static-copy';
3
+ import { resolver } from "../resolver.js";
4
+ const targets = [];
5
+ try {
6
+ const { path } = resolver.sync(process.cwd(), '@nmtjs/proxy');
7
+ if (path) {
8
+ targets.push({
9
+ src: join(dirname(path), `neemata-proxy.${process.platform}-${process.arch}*.node`),
10
+ dest: './chunks/',
11
+ });
12
+ }
13
+ }
14
+ catch { }
15
+ export const buildPlugins = [
16
+ ...(targets.length ? viteStaticCopy({ targets }) : []),
17
+ ];
@@ -0,0 +1,32 @@
1
+ import { BroadcastChannel, isInternalThread, isMainThread, parentPort, threadId, } from 'node:worker_threads';
2
+ import { noopFn } from '@nmtjs/common';
3
+ import { createNodeImportMeta, ESModulesEvaluator, ModuleRunner, } from 'vite/module-runner';
4
+ export function createBroadcastChannel(threadId) {
5
+ return new BroadcastChannel(`nmtjs:vite:${threadId}`);
6
+ }
7
+ export function createModuleRunner(mode = 'development') {
8
+ if (isMainThread || isInternalThread)
9
+ throw new Error('Module runner can only be created inside worker threads.');
10
+ const channel = createBroadcastChannel(threadId);
11
+ const transport = {
12
+ connect({ onMessage, onDisconnection }) {
13
+ // @ts-expect-error
14
+ channel.onmessage = (event) => {
15
+ onMessage(event.data);
16
+ };
17
+ parentPort.on('close', onDisconnection);
18
+ },
19
+ send(data) {
20
+ channel.postMessage(data);
21
+ },
22
+ timeout: 5000,
23
+ };
24
+ const runner = new ModuleRunner({
25
+ transport,
26
+ createImportMeta: createNodeImportMeta,
27
+ hmr: mode === 'development'
28
+ ? { logger: { debug: noopFn, error: console.error } }
29
+ : false,
30
+ }, new ESModulesEvaluator());
31
+ return runner;
32
+ }
@@ -0,0 +1,17 @@
1
+ import { createServer as createViteServer } from 'vite';
2
+ export function createServer(options, config, dev = {}) {
3
+ return createViteServer({
4
+ ...config,
5
+ server: { middlewareMode: true, ws: false },
6
+ environments: {
7
+ neemata: {
8
+ consumer: 'server',
9
+ dev,
10
+ define: {
11
+ __VITE_CONFIG__: JSON.stringify(JSON.stringify({ options, mode: config.mode })),
12
+ __APPLICATIONS_CONFIG__: JSON.stringify(JSON.stringify(Object.fromEntries(Object.entries(options.applicationImports).map(([appName, { path }]) => [appName, path])))),
13
+ },
14
+ },
15
+ },
16
+ });
17
+ }
@@ -0,0 +1,22 @@
1
+ import { noopFn } from '@nmtjs/common';
2
+ import { createServerModuleRunner } from 'vite';
3
+ import { createConfig } from "../config.js";
4
+ import { buildPlugins } from "../plugins.js";
5
+ import { createServer } from "../server.js";
6
+ export async function createMainServer(options, mode, neemataConfig) {
7
+ const config = createConfig(options);
8
+ const server = await createServer(options, {
9
+ appType: 'custom',
10
+ clearScreen: false,
11
+ resolve: { alias: config.alias },
12
+ mode,
13
+ plugins: [...buildPlugins, ...neemataConfig.plugins],
14
+ });
15
+ const environment = server.environments.neemata;
16
+ const runner = createServerModuleRunner(environment, {
17
+ hmr: mode === 'development'
18
+ ? { logger: { debug: noopFn, error: console.error } }
19
+ : false,
20
+ });
21
+ return { server, runner };
22
+ }
@@ -0,0 +1,78 @@
1
+ import { DevEnvironment } from 'vite';
2
+ import { createConfig } from "../config.js";
3
+ import { buildPlugins } from "../plugins.js";
4
+ import { createBroadcastChannel } from "../runners/worker.js";
5
+ import { createServer } from "../server.js";
6
+ export async function createWorkerServer(options, mode, neemataConfig, events) {
7
+ const config = createConfig(options);
8
+ const applicationEntries = Object.values(options.applicationImports).map((v) => v.path);
9
+ const _injectHmr = `\n\nif(import.meta.hot) { import.meta.hot.accept(globalThis._hotAccept) }`;
10
+ const server = await createServer(options, {
11
+ appType: 'custom',
12
+ clearScreen: false,
13
+ resolve: { alias: config.alias },
14
+ mode,
15
+ plugins: [
16
+ ...buildPlugins,
17
+ ...neemataConfig.plugins,
18
+ mode === 'development'
19
+ ? [
20
+ {
21
+ name: 'neemata-worker-application-hmr',
22
+ transform(code, id, options) {
23
+ if (applicationEntries.includes(id)) {
24
+ return code + _injectHmr;
25
+ }
26
+ },
27
+ },
28
+ ]
29
+ : [],
30
+ ],
31
+ }, {
32
+ createEnvironment: async (name, config, context) => {
33
+ const channels = new Map();
34
+ const clients = new Map();
35
+ const handlers = new Map();
36
+ events.on('worker', (worker) => {
37
+ const channel = createBroadcastChannel(worker.threadId);
38
+ channel.onmessage = (event) => {
39
+ const value = event.data;
40
+ const handler = handlers.get(value.event);
41
+ if (handler)
42
+ handler(value.data, client);
43
+ };
44
+ channels.set(worker.threadId, channel);
45
+ const client = {
46
+ send: (payload) => {
47
+ channel.postMessage(payload);
48
+ },
49
+ };
50
+ clients.set(channel, client);
51
+ worker.on('exit', () => {
52
+ const handler = handlers.get('vite:client:disconnect');
53
+ if (handler)
54
+ handler(undefined, client);
55
+ });
56
+ });
57
+ const transport = {
58
+ send(data) {
59
+ for (const channel of channels.values()) {
60
+ channel.postMessage(data);
61
+ }
62
+ },
63
+ on(event, handler) {
64
+ handlers.set(event, handler);
65
+ },
66
+ off(event) {
67
+ handlers.delete(event);
68
+ },
69
+ };
70
+ const environment = new DevEnvironment(name, config, {
71
+ hot: mode === 'development',
72
+ transport,
73
+ });
74
+ return environment;
75
+ },
76
+ });
77
+ return server;
78
+ }
package/package.json CHANGED
@@ -16,22 +16,22 @@
16
16
  "oxc-resolver": "^11.13.2",
17
17
  "vite": "npm:rolldown-vite@7.2.10",
18
18
  "vite-plugin-static-copy": "^3.1.4",
19
- "@nmtjs/core": "0.15.0-beta.1",
20
- "@nmtjs/gateway": "0.15.0-beta.1",
21
- "@nmtjs/contract": "0.15.0-beta.1",
22
- "@nmtjs/json-format": "0.15.0-beta.1",
23
- "@nmtjs/protocol": "0.15.0-beta.1",
24
- "@nmtjs/type": "0.15.0-beta.1",
25
- "@nmtjs/http-transport": "0.15.0-beta.1",
26
- "@nmtjs/ws-transport": "0.15.0-beta.1",
27
- "@nmtjs/common": "0.15.0-beta.1",
28
- "@nmtjs/runtime": "0.15.0-beta.1"
19
+ "@nmtjs/contract": "0.15.0-beta.2",
20
+ "@nmtjs/core": "0.15.0-beta.2",
21
+ "@nmtjs/gateway": "0.15.0-beta.2",
22
+ "@nmtjs/common": "0.15.0-beta.2",
23
+ "@nmtjs/protocol": "0.15.0-beta.2",
24
+ "@nmtjs/json-format": "0.15.0-beta.2",
25
+ "@nmtjs/type": "0.15.0-beta.2",
26
+ "@nmtjs/runtime": "0.15.0-beta.2",
27
+ "@nmtjs/ws-transport": "0.15.0-beta.2",
28
+ "@nmtjs/http-transport": "0.15.0-beta.2"
29
29
  },
30
30
  "devDependencies": {
31
- "@nmtjs/proxy": "0.15.0-beta.1"
31
+ "@nmtjs/proxy": "0.15.0-beta.2"
32
32
  },
33
33
  "peerDependencies": {
34
- "@nmtjs/proxy": "0.15.0-beta.1"
34
+ "@nmtjs/proxy": "0.15.0-beta.2"
35
35
  },
36
36
  "peerDependenciesMeta": {
37
37
  "@nmtjs/proxy": {
@@ -43,7 +43,7 @@
43
43
  "LICENSE.md",
44
44
  "README.md"
45
45
  ],
46
- "version": "0.15.0-beta.1",
46
+ "version": "0.15.0-beta.2",
47
47
  "scripts": {
48
48
  "clean-build": "rm -rf ./dist",
49
49
  "build": "tsc",