nmtjs 0.15.0-beta.3 → 0.15.0-beta.4
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.
- package/dist/_exports/index.d.ts +9 -5
- package/dist/_exports/index.js +7 -5
- package/dist/_exports/index.js.map +1 -1
- package/dist/vite/server.js +1 -0
- package/dist/vite/server.js.map +1 -1
- package/package.json +14 -13
- package/src/_exports/application.ts +1 -0
- package/src/_exports/common.ts +1 -0
- package/src/_exports/contract.ts +2 -0
- package/src/_exports/core.ts +1 -0
- package/src/_exports/gateway.ts +1 -0
- package/src/_exports/http-transport/bun.ts +1 -0
- package/src/_exports/http-transport/deno.ts +1 -0
- package/src/_exports/http-transport/node.ts +1 -0
- package/src/_exports/http-transport.ts +1 -0
- package/src/_exports/index.ts +77 -0
- package/src/_exports/json-format.ts +1 -0
- package/src/_exports/protocol/client.ts +1 -0
- package/src/_exports/protocol/server.ts +1 -0
- package/src/_exports/protocol.ts +1 -0
- package/src/_exports/runtime/types.ts +1 -0
- package/src/_exports/runtime.ts +1 -0
- package/src/_exports/type.ts +2 -0
- package/src/_exports/ws-transport/bun.ts +1 -0
- package/src/_exports/ws-transport/deno.ts +1 -0
- package/src/_exports/ws-transport/node.ts +1 -0
- package/src/_exports/ws-transport.ts +1 -0
- package/src/cli.ts +139 -0
- package/src/command.ts +30 -0
- package/src/config.ts +64 -0
- package/src/entrypoints/cli.ts +13 -0
- package/src/entrypoints/main.ts +111 -0
- package/src/entrypoints/thread.ts +69 -0
- package/src/entrypoints/worker.ts +48 -0
- package/src/resolver.ts +16 -0
- package/src/typings.ts +27 -0
- package/src/vite/builder.ts +119 -0
- package/src/vite/config.ts +42 -0
- package/src/vite/plugins.ts +26 -0
- package/src/vite/runners/worker.ts +57 -0
- package/src/vite/server.ts +36 -0
- package/src/vite/servers/main.ts +34 -0
- package/src/vite/servers/worker.ts +107 -0
package/dist/_exports/index.d.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import { createConsolePrettyDestination, createFactoryInjectable, createLazyInjectable,
|
|
1
|
+
import { createConsolePrettyDestination, createFactoryInjectable, createLazyInjectable, createValueInjectable } from '@nmtjs/core';
|
|
2
2
|
import { createTransport } from '@nmtjs/gateway';
|
|
3
|
-
import { createContractProcedure, createContractRouter, createFilter, createGuard, createHook, createJob, createMiddleware, createPlugin, createProcedure, createRouter, createStep, defineApplication } from '@nmtjs/runtime';
|
|
3
|
+
import { createContractProcedure, createContractRouter, createFilter, createGuard, createHook, createJob, createMiddleware, createPlugin, createProcedure, createRootRouter, createRouter, createStep, defineApplication, defineServer } from '@nmtjs/runtime';
|
|
4
4
|
export declare const neemata: {
|
|
5
5
|
app: typeof defineApplication;
|
|
6
|
+
server: typeof defineServer;
|
|
6
7
|
injectables: {
|
|
7
8
|
pubSubAdapter: import("@nmtjs/core").LazyInjectable<import("@nmtjs/runtime").PubSubAdapterType, import("@nmtjs/core").Scope.Global>;
|
|
8
9
|
pubSubPublish: import("@nmtjs/core").LazyInjectable<import("@nmtjs/runtime").PubSubPublish, import("@nmtjs/core").Scope.Global>;
|
|
9
10
|
pubSubSubscribe: import("@nmtjs/core").LazyInjectable<import("@nmtjs/runtime").PubSubSubscribe, import("@nmtjs/core").Scope.Global>;
|
|
10
11
|
jobManager: import("@nmtjs/core").LazyInjectable<import("@nmtjs/runtime").JobManagerInstance, import("@nmtjs/core").Scope.Global>;
|
|
11
12
|
storeConfig: import("@nmtjs/core").LazyInjectable<import("@nmtjs/runtime").ServerStoreConfig, import("@nmtjs/core").Scope.Global>;
|
|
13
|
+
workerType: import("@nmtjs/core").LazyInjectable<import("@nmtjs/runtime").WorkerType, import("@nmtjs/core").Scope.Global>;
|
|
14
|
+
jobWorkerQueue: import("@nmtjs/core").LazyInjectable<import("@nmtjs/runtime").JobWorkerQueue, import("@nmtjs/core").Scope.Global>;
|
|
12
15
|
connection: import("@nmtjs/core").LazyInjectable<import("@nmtjs/gateway").GatewayConnection, import("@nmtjs/core").Scope.Connection>;
|
|
13
16
|
connectionId: import("@nmtjs/core").LazyInjectable<string, import("@nmtjs/core").Scope.Connection>;
|
|
14
17
|
connectionData: import("@nmtjs/core").LazyInjectable<unknown, import("@nmtjs/core").Scope.Connection>;
|
|
@@ -37,10 +40,10 @@ export declare const neemata: {
|
|
|
37
40
|
logging: {
|
|
38
41
|
console: typeof createConsolePrettyDestination;
|
|
39
42
|
};
|
|
40
|
-
optional: typeof createOptionalInjectable;
|
|
41
43
|
value: typeof createValueInjectable;
|
|
42
44
|
lazy: typeof createLazyInjectable;
|
|
43
45
|
factory: typeof createFactoryInjectable;
|
|
46
|
+
rootRouter: typeof createRootRouter;
|
|
44
47
|
router: typeof createRouter;
|
|
45
48
|
contractRouter: typeof createContractRouter;
|
|
46
49
|
procedure: typeof createProcedure;
|
|
@@ -54,8 +57,9 @@ export declare const neemata: {
|
|
|
54
57
|
};
|
|
55
58
|
export { c } from '@nmtjs/contract';
|
|
56
59
|
export { Scope } from '@nmtjs/core';
|
|
57
|
-
export {
|
|
60
|
+
export { type ConnectionIdentityType, GatewayHook, ProxyableTransportType, } from '@nmtjs/gateway';
|
|
61
|
+
export { ConnectionType, ErrorCode, ProtocolBlob } from '@nmtjs/protocol';
|
|
58
62
|
export { t } from '@nmtjs/type';
|
|
59
|
-
export { ApiError, defineApplication, LifecycleHook } from 'nmtjs/runtime';
|
|
63
|
+
export { ApiError, defineApplication, JobWorkerQueue, LifecycleHook, StoreType, WorkerType, } from 'nmtjs/runtime';
|
|
60
64
|
export { neemata as n };
|
|
61
65
|
export default neemata;
|
package/dist/_exports/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { CoreInjectables, createConsolePrettyDestination, createFactoryInjectable, createLazyInjectable,
|
|
1
|
+
import { CoreInjectables, createConsolePrettyDestination, createFactoryInjectable, createLazyInjectable, createValueInjectable, } from '@nmtjs/core';
|
|
2
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';
|
|
3
|
+
import { createContractProcedure, createContractRouter, createFilter, createGuard, createHook, createJob, createMiddleware, createPlugin, createProcedure, createRootRouter, createRouter, createStep, defineApplication, defineServer, RuntimeInjectables, } from '@nmtjs/runtime';
|
|
4
4
|
export const neemata = {
|
|
5
5
|
app: defineApplication,
|
|
6
|
+
server: defineServer,
|
|
6
7
|
injectables: {
|
|
7
8
|
...CoreInjectables,
|
|
8
9
|
...GatewayInjectables,
|
|
@@ -15,10 +16,10 @@ export const neemata = {
|
|
|
15
16
|
// TODO: TSC wants it
|
|
16
17
|
createConsolePrettyDestination,
|
|
17
18
|
},
|
|
18
|
-
optional: createOptionalInjectable,
|
|
19
19
|
value: createValueInjectable,
|
|
20
20
|
lazy: createLazyInjectable,
|
|
21
21
|
factory: createFactoryInjectable,
|
|
22
|
+
rootRouter: createRootRouter,
|
|
22
23
|
router: createRouter,
|
|
23
24
|
contractRouter: createContractRouter,
|
|
24
25
|
procedure: createProcedure,
|
|
@@ -32,9 +33,10 @@ export const neemata = {
|
|
|
32
33
|
};
|
|
33
34
|
export { c } from '@nmtjs/contract';
|
|
34
35
|
export { Scope } from '@nmtjs/core';
|
|
35
|
-
export {
|
|
36
|
+
export { GatewayHook, ProxyableTransportType, } from '@nmtjs/gateway';
|
|
37
|
+
export { ConnectionType, ErrorCode, ProtocolBlob } from '@nmtjs/protocol';
|
|
36
38
|
export { t } from '@nmtjs/type';
|
|
37
|
-
export { ApiError, defineApplication, LifecycleHook } from 'nmtjs/runtime';
|
|
39
|
+
export { ApiError, defineApplication, JobWorkerQueue, LifecycleHook, StoreType, WorkerType, } from 'nmtjs/runtime';
|
|
38
40
|
export { neemata as n };
|
|
39
41
|
export default neemata;
|
|
40
42
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/_exports/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,8BAA8B,EAC9B,uBAAuB,EACvB,oBAAoB,EACpB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/_exports/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,8BAA8B,EAC9B,uBAAuB,EACvB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,GACnB,MAAM,gBAAgB,CAAA;AAEvB,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,GAAG,EAAE,iBAAiB;IACtB,MAAM,EAAE,YAAY;IACpB,WAAW,EAAE;QACX,GAAG,eAAe;QAClB,GAAG,kBAAkB;QACrB,GAAI,kBAAgD;KACrD;IACD,SAAS,EAAE,eAAe;IAC1B,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE;QACP,OAAO;QACL,qBAAqB;QACrB,8BAAuE;KAC1E;IACD,KAAK,EAAE,qBAAqB;IAC5B,IAAI,EAAE,oBAAoB;IAC1B,OAAO,EAAE,uBAAuB;IAChC,UAAU,EAAE,gBAAgB;IAC5B,MAAM,EAAE,YAAY;IACpB,cAAc,EAAE,oBAAoB;IACpC,SAAS,EAAE,eAAe;IAC1B,iBAAiB,EAAE,uBAAuB;IAC1C,UAAU,EAAE,gBAAgB;IAC5B,KAAK,EAAE,WAAW;IAClB,MAAM,EAAE,YAAY;IACpB,GAAG,EAAE,SAAS;IACd,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE,UAAU;CACjB,CAAA;AAED,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAA;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAEL,WAAW,EACX,sBAAsB,GACvB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACzE,OAAO,EAAE,CAAC,EAAE,MAAM,aAAa,CAAA;AAC/B,OAAO,EACL,QAAQ,EACR,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,SAAS,EACT,UAAU,GACX,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,OAAO,IAAI,CAAC,EAAE,CAAA;AACvB,eAAe,OAAO,CAAA"}
|
package/dist/vite/server.js
CHANGED
package/dist/vite/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/vite/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,MAAM,CAAA;AAIvD,MAAM,UAAU,YAAY,CAC1B,OAA0B,EAC1B,MAAkB,EAClB,MAA6B,EAAE;IAE/B,OAAO,gBAAgB,CAAC;QACtB,GAAG,MAAM;QACT,MAAM,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE;QAC3C,YAAY,EAAE;YACZ,OAAO,EAAE;gBACP,QAAQ,EAAE,QAAQ;gBAClB,GAAG;gBACH,MAAM,EAAE;oBACN,eAAe,EAAE,IAAI,CAAC,SAAS,CAC7B,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAC/C;oBACD,uBAAuB,EAAE,IAAI,CAAC,SAAS,CACrC,IAAI,CAAC,SAAS,CACZ,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAC5C,CAAC,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CACzC,CACF,CACF,CACF;iBACF;aACF;SACF;KACF,CAAC,CAAA;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/vite/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,MAAM,CAAA;AAIvD,MAAM,UAAU,YAAY,CAC1B,OAA0B,EAC1B,MAAkB,EAClB,MAA6B,EAAE;IAE/B,OAAO,gBAAgB,CAAC;QACtB,GAAG,MAAM;QACT,MAAM,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE;QAC3C,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE;QACnD,YAAY,EAAE;YACZ,OAAO,EAAE;gBACP,QAAQ,EAAE,QAAQ;gBAClB,GAAG;gBACH,MAAM,EAAE;oBACN,eAAe,EAAE,IAAI,CAAC,SAAS,CAC7B,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAC/C;oBACD,uBAAuB,EAAE,IAAI,CAAC,SAAS,CACrC,IAAI,CAAC,SAAS,CACZ,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAC5C,CAAC,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CACzC,CACF,CACF,CACF;iBACF;aACF;SACF;KACF,CAAC,CAAA;AACJ,CAAC"}
|
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/common": "0.15.0-beta.
|
|
20
|
-
"@nmtjs/
|
|
21
|
-
"@nmtjs/
|
|
22
|
-
"@nmtjs/
|
|
23
|
-
"@nmtjs/
|
|
24
|
-
"@nmtjs/
|
|
25
|
-
"@nmtjs/core": "0.15.0-beta.
|
|
26
|
-
"@nmtjs/type": "0.15.0-beta.
|
|
27
|
-
"@nmtjs/ws-transport": "0.15.0-beta.
|
|
28
|
-
"@nmtjs/http-transport": "0.15.0-beta.
|
|
19
|
+
"@nmtjs/common": "0.15.0-beta.4",
|
|
20
|
+
"@nmtjs/gateway": "0.15.0-beta.4",
|
|
21
|
+
"@nmtjs/json-format": "0.15.0-beta.4",
|
|
22
|
+
"@nmtjs/contract": "0.15.0-beta.4",
|
|
23
|
+
"@nmtjs/runtime": "0.15.0-beta.4",
|
|
24
|
+
"@nmtjs/protocol": "0.15.0-beta.4",
|
|
25
|
+
"@nmtjs/core": "0.15.0-beta.4",
|
|
26
|
+
"@nmtjs/type": "0.15.0-beta.4",
|
|
27
|
+
"@nmtjs/ws-transport": "0.15.0-beta.4",
|
|
28
|
+
"@nmtjs/http-transport": "0.15.0-beta.4"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@nmtjs/proxy": "0.15.0-beta.
|
|
31
|
+
"@nmtjs/proxy": "0.15.0-beta.4"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@nmtjs/proxy": "0.15.0-beta.
|
|
34
|
+
"@nmtjs/proxy": "0.15.0-beta.4"
|
|
35
35
|
},
|
|
36
36
|
"peerDependenciesMeta": {
|
|
37
37
|
"@nmtjs/proxy": {
|
|
@@ -40,10 +40,11 @@
|
|
|
40
40
|
},
|
|
41
41
|
"files": [
|
|
42
42
|
"dist",
|
|
43
|
+
"src",
|
|
43
44
|
"LICENSE.md",
|
|
44
45
|
"README.md"
|
|
45
46
|
],
|
|
46
|
-
"version": "0.15.0-beta.
|
|
47
|
+
"version": "0.15.0-beta.4",
|
|
47
48
|
"scripts": {
|
|
48
49
|
"clean-build": "rm -rf ./dist",
|
|
49
50
|
"build": "tsc --declaration --sourcemap",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@nmtjs/runtime/application'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@nmtjs/common'
|
|
@@ -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,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CoreInjectables,
|
|
3
|
+
createConsolePrettyDestination,
|
|
4
|
+
createFactoryInjectable,
|
|
5
|
+
createLazyInjectable,
|
|
6
|
+
createValueInjectable,
|
|
7
|
+
} from '@nmtjs/core'
|
|
8
|
+
import { createTransport, GatewayInjectables } from '@nmtjs/gateway'
|
|
9
|
+
import {
|
|
10
|
+
createContractProcedure,
|
|
11
|
+
createContractRouter,
|
|
12
|
+
createFilter,
|
|
13
|
+
createGuard,
|
|
14
|
+
createHook,
|
|
15
|
+
createJob,
|
|
16
|
+
createMiddleware,
|
|
17
|
+
createPlugin,
|
|
18
|
+
createProcedure,
|
|
19
|
+
createRootRouter,
|
|
20
|
+
createRouter,
|
|
21
|
+
createStep,
|
|
22
|
+
defineApplication,
|
|
23
|
+
defineServer,
|
|
24
|
+
RuntimeInjectables,
|
|
25
|
+
} from '@nmtjs/runtime'
|
|
26
|
+
|
|
27
|
+
export const neemata = {
|
|
28
|
+
app: defineApplication,
|
|
29
|
+
server: defineServer,
|
|
30
|
+
injectables: {
|
|
31
|
+
...CoreInjectables,
|
|
32
|
+
...GatewayInjectables,
|
|
33
|
+
...(RuntimeInjectables as typeof RuntimeInjectables),
|
|
34
|
+
},
|
|
35
|
+
transport: createTransport,
|
|
36
|
+
plugin: createPlugin,
|
|
37
|
+
logging: {
|
|
38
|
+
console:
|
|
39
|
+
// TODO: TSC wants it
|
|
40
|
+
createConsolePrettyDestination as typeof createConsolePrettyDestination,
|
|
41
|
+
},
|
|
42
|
+
value: createValueInjectable,
|
|
43
|
+
lazy: createLazyInjectable,
|
|
44
|
+
factory: createFactoryInjectable,
|
|
45
|
+
rootRouter: createRootRouter,
|
|
46
|
+
router: createRouter,
|
|
47
|
+
contractRouter: createContractRouter,
|
|
48
|
+
procedure: createProcedure,
|
|
49
|
+
contractProcedure: createContractProcedure,
|
|
50
|
+
middleware: createMiddleware,
|
|
51
|
+
guard: createGuard,
|
|
52
|
+
filter: createFilter,
|
|
53
|
+
job: createJob,
|
|
54
|
+
step: createStep,
|
|
55
|
+
hook: createHook,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { c } from '@nmtjs/contract'
|
|
59
|
+
export { Scope } from '@nmtjs/core'
|
|
60
|
+
export {
|
|
61
|
+
type ConnectionIdentityType,
|
|
62
|
+
GatewayHook,
|
|
63
|
+
ProxyableTransportType,
|
|
64
|
+
} from '@nmtjs/gateway'
|
|
65
|
+
export { ConnectionType, ErrorCode, ProtocolBlob } from '@nmtjs/protocol'
|
|
66
|
+
export { t } from '@nmtjs/type'
|
|
67
|
+
export {
|
|
68
|
+
ApiError,
|
|
69
|
+
defineApplication,
|
|
70
|
+
JobWorkerQueue,
|
|
71
|
+
LifecycleHook,
|
|
72
|
+
StoreType,
|
|
73
|
+
WorkerType,
|
|
74
|
+
} from 'nmtjs/runtime'
|
|
75
|
+
|
|
76
|
+
export { neemata as n }
|
|
77
|
+
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 @@
|
|
|
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/src/cli.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
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<string, { path: string; specifier: string }>
|
|
34
|
+
|
|
35
|
+
const mainCommand = defineCommand({
|
|
36
|
+
meta: { description: 'Neemata CLI' },
|
|
37
|
+
args: { ...commonArgs },
|
|
38
|
+
async setup(ctx) {
|
|
39
|
+
const configPath = resolve(ctx.args.config as string)
|
|
40
|
+
config = await import(configPath).then((m) => m.default)
|
|
41
|
+
|
|
42
|
+
for (const env of config.env) {
|
|
43
|
+
if (typeof env === 'string') {
|
|
44
|
+
const { error } = dotenv({ path: env })
|
|
45
|
+
if (error) console.warn(error)
|
|
46
|
+
} else if (typeof env === 'object') {
|
|
47
|
+
for (const key in env) {
|
|
48
|
+
process.env[key] = env[key]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// const applicationEntryPaths: Record<string, string> = {}
|
|
54
|
+
applicationImports = {}
|
|
55
|
+
const currentPkg = resolver.sync(process.cwd(), './package.json')
|
|
56
|
+
|
|
57
|
+
for (const [appName, appSpecifier] of Object.entries(config.applications)) {
|
|
58
|
+
const resolution = resolver.sync(process.cwd(), appSpecifier)
|
|
59
|
+
if (resolution.error)
|
|
60
|
+
throw new Error(
|
|
61
|
+
`Failed to resolve application path for ${appName}: ${resolution.error}`,
|
|
62
|
+
)
|
|
63
|
+
if (!resolution.path)
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Failed to resolve application path for ${appName}: no path found`,
|
|
66
|
+
)
|
|
67
|
+
const specifier =
|
|
68
|
+
resolution.packageJsonPath === currentPkg.path
|
|
69
|
+
? relative(resolve('.neemata'), resolution.path)
|
|
70
|
+
: appSpecifier
|
|
71
|
+
applicationImports[appName] = { path: resolution.path, specifier }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
viteConfigOptions = {
|
|
75
|
+
applicationImports,
|
|
76
|
+
serverEntryPath: resolve(config.serverPath),
|
|
77
|
+
...baseViteConfigOptions,
|
|
78
|
+
configPath,
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
subCommands: {
|
|
82
|
+
prepare: defineCommand({
|
|
83
|
+
async run(ctx) {
|
|
84
|
+
await generateTypings(applicationImports)
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
87
|
+
dev: defineCommand({
|
|
88
|
+
async run(ctx) {
|
|
89
|
+
const { runner } = await createMainServer(
|
|
90
|
+
viteConfigOptions,
|
|
91
|
+
'development',
|
|
92
|
+
config,
|
|
93
|
+
)
|
|
94
|
+
await runner.import(viteConfigOptions.entrypointMainPath)
|
|
95
|
+
await once(process, 'beforeExit')
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
preview: defineCommand({
|
|
99
|
+
async run(ctx) {
|
|
100
|
+
const { runner } = await createMainServer(
|
|
101
|
+
viteConfigOptions,
|
|
102
|
+
'production',
|
|
103
|
+
config,
|
|
104
|
+
)
|
|
105
|
+
await runner.import(viteConfigOptions.entrypointMainPath)
|
|
106
|
+
await once(process, 'beforeExit')
|
|
107
|
+
},
|
|
108
|
+
}),
|
|
109
|
+
build: defineCommand({
|
|
110
|
+
async run(ctx) {
|
|
111
|
+
const builder = await createBuilder(viteConfigOptions, config)
|
|
112
|
+
await builder.build()
|
|
113
|
+
},
|
|
114
|
+
}),
|
|
115
|
+
// command: defineCommand({
|
|
116
|
+
// async run(ctx) {
|
|
117
|
+
// const runner = await createRunner(
|
|
118
|
+
// viteConfigOptions,
|
|
119
|
+
// 'production',
|
|
120
|
+
// config,
|
|
121
|
+
// )
|
|
122
|
+
// const workerModule = await runner.import<
|
|
123
|
+
// typeof import('./entrypoints/worker.ts')
|
|
124
|
+
// >(import.meta.resolve('./entrypoints/worker.js'))
|
|
125
|
+
// const commandModule = await runner.import<
|
|
126
|
+
// typeof import('./command.ts')
|
|
127
|
+
// >(import.meta.resolve('./command.js'))
|
|
128
|
+
// const worker = await workerModule.default({
|
|
129
|
+
// applicationWorkerData: undefined,
|
|
130
|
+
// type: ApplicationType.Command,
|
|
131
|
+
// workerType: ApplicationWorkerType.Command,
|
|
132
|
+
// })
|
|
133
|
+
// await runMain(commandModule.default(worker), { rawArgs: ctx.rawArgs })
|
|
134
|
+
// },
|
|
135
|
+
// }),
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
runMain(mainCommand)
|
package/src/command.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// import type { ApplicationWorker } from '@nmtjs/runtime/worker'
|
|
2
|
+
// import { typeToString } from '@nmtjs/type'
|
|
3
|
+
// import { defineCommand } from 'citty'
|
|
4
|
+
|
|
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/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]: 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,111 @@
|
|
|
1
|
+
import type { Worker } from 'node:worker_threads'
|
|
2
|
+
import EventEmitter from 'node:events'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
|
|
5
|
+
import type { ServerConfig } from '@nmtjs/runtime'
|
|
6
|
+
import type { ViteDevServer } from 'vite'
|
|
7
|
+
import { ApplicationServer, isServerConfig } from '@nmtjs/runtime'
|
|
8
|
+
|
|
9
|
+
declare global {
|
|
10
|
+
const __VITE_CONFIG__: string
|
|
11
|
+
const __APPLICATIONS_CONFIG__: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class InvalidServerConfigError extends Error {
|
|
15
|
+
constructor() {
|
|
16
|
+
super(
|
|
17
|
+
`Server config file does not have a default export, or it not a valid application. Please, make sure the application is defined using defineApplication().`,
|
|
18
|
+
)
|
|
19
|
+
this.name = 'InvalidServerConfigError'
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const _ext = new URL(import.meta.url).pathname.endsWith('.ts') ? '.ts' : '.js'
|
|
24
|
+
const _vite = __VITE_CONFIG__ ? JSON.parse(__VITE_CONFIG__) : undefined
|
|
25
|
+
const applicationsConfig = __APPLICATIONS_CONFIG__
|
|
26
|
+
? JSON.parse(__APPLICATIONS_CONFIG__)
|
|
27
|
+
: {}
|
|
28
|
+
|
|
29
|
+
let _viteServerEvents: EventEmitter<{ worker: [Worker] }> | undefined
|
|
30
|
+
let _viteWorkerServer: ViteDevServer | undefined
|
|
31
|
+
|
|
32
|
+
let server: ApplicationServer
|
|
33
|
+
|
|
34
|
+
if (import.meta.env.DEV && import.meta.hot) {
|
|
35
|
+
import.meta.hot.accept('#server', async (module) => {
|
|
36
|
+
await server.stop()
|
|
37
|
+
await createServer(module?.default)
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (_vite) {
|
|
42
|
+
const { createWorkerServer } = await import('../vite/servers/worker.ts')
|
|
43
|
+
const neemataConfig = await import(
|
|
44
|
+
/* @vite-ignore */
|
|
45
|
+
_vite.options.configPath
|
|
46
|
+
).then((m) => m.default as import('../config.ts').NeemataConfig)
|
|
47
|
+
_viteServerEvents = new EventEmitter<{ worker: [Worker] }>()
|
|
48
|
+
_viteWorkerServer = await createWorkerServer(
|
|
49
|
+
_vite.options,
|
|
50
|
+
_vite.mode,
|
|
51
|
+
neemataConfig,
|
|
52
|
+
_viteServerEvents,
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function createServer(config: ServerConfig) {
|
|
57
|
+
if (!isServerConfig(config)) throw new InvalidServerConfigError()
|
|
58
|
+
server = new ApplicationServer(config, applicationsConfig, {
|
|
59
|
+
path: fileURLToPath(import.meta.resolve(`./thread${_ext}`)),
|
|
60
|
+
workerData: { vite: _vite?.mode },
|
|
61
|
+
worker: _viteServerEvents
|
|
62
|
+
? (worker) => {
|
|
63
|
+
_viteServerEvents.emit('worker', worker)
|
|
64
|
+
}
|
|
65
|
+
: undefined,
|
|
66
|
+
})
|
|
67
|
+
await server.start()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let isTerminating = false
|
|
71
|
+
|
|
72
|
+
async function handleTermination() {
|
|
73
|
+
if (isTerminating) return
|
|
74
|
+
isTerminating = true
|
|
75
|
+
await server?.stop()
|
|
76
|
+
_viteWorkerServer?.close()
|
|
77
|
+
process.exit(0)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function handleUnexpectedError(error: Error) {
|
|
81
|
+
console.error(new Error('Unexpected Error:', { cause: error }))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
process.once('SIGTERM', handleTermination)
|
|
85
|
+
process.once('SIGINT', handleTermination)
|
|
86
|
+
process.on('uncaughtException', handleUnexpectedError)
|
|
87
|
+
process.on('unhandledRejection', handleUnexpectedError)
|
|
88
|
+
|
|
89
|
+
await createServer(
|
|
90
|
+
await import(
|
|
91
|
+
// @ts-expect-error
|
|
92
|
+
'#server'
|
|
93
|
+
).then((m) => m.default),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
const { format } = Intl.NumberFormat('en', {
|
|
97
|
+
notation: 'compact',
|
|
98
|
+
maximumFractionDigits: 2,
|
|
99
|
+
unit: 'byte',
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
const printMem = () => {
|
|
103
|
+
globalThis.gc?.()
|
|
104
|
+
// print memory usage every 10 seconds
|
|
105
|
+
const memoryUsage = process.memoryUsage()
|
|
106
|
+
console.log(
|
|
107
|
+
`Memory Usage: RSS=${format(memoryUsage.rss)}, HeapTotal=${format(memoryUsage.heapTotal)}, HeapUsed=${format(memoryUsage.heapUsed)}, External=${format(memoryUsage.external)}, ArrayBuffers=${format(memoryUsage.arrayBuffers)}`,
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
// printMem()
|
|
111
|
+
// setInterval(printMem, 5000)
|
|
@@ -0,0 +1,69 @@
|
|
|
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 { ThreadPortMessage } from '@nmtjs/runtime'
|
|
6
|
+
import type { ModuleRunner } from 'vite/module-runner'
|
|
7
|
+
|
|
8
|
+
export type RunWorkerOptions = {
|
|
9
|
+
port: MessagePort
|
|
10
|
+
runtime:
|
|
11
|
+
| { type: 'application'; name: string; path: string; transportsData: any }
|
|
12
|
+
| { type: 'jobs'; jobWorkerQueue: string }
|
|
13
|
+
vite?: 'development' | 'production'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const workerData = _workerData as RunWorkerOptions
|
|
17
|
+
|
|
18
|
+
const ext = new URL(import.meta.url).pathname.endsWith('.ts') ? '.ts' : '.js'
|
|
19
|
+
const workerPath = fileURLToPath(import.meta.resolve(`./worker${ext}`))
|
|
20
|
+
|
|
21
|
+
process.on('uncaughtException', (error) => {
|
|
22
|
+
console.error(new Error('Uncaught Exception:', { cause: error }))
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
process.on('unhandledRejection', (error) => {
|
|
26
|
+
console.error(new Error('Unhandled Promise Rejection:', { cause: error }))
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
process.on('beforeExit', (code) => {
|
|
30
|
+
runner?.close()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
let runner: ModuleRunner
|
|
34
|
+
let workerModule: typeof import('./worker.ts')
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
if (workerData.vite) {
|
|
38
|
+
const { createModuleRunner } = (await import(
|
|
39
|
+
'../vite/runners/worker.ts'
|
|
40
|
+
)) as typeof import('../vite/runners/worker.ts')
|
|
41
|
+
|
|
42
|
+
runner = createModuleRunner(workerData.vite)
|
|
43
|
+
workerModule = await runner.import(workerPath)
|
|
44
|
+
} else {
|
|
45
|
+
runner = undefined as any
|
|
46
|
+
workerModule = await import(
|
|
47
|
+
/* @vite-ignore */
|
|
48
|
+
workerPath
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const runtime = await workerModule.run(workerData.runtime)
|
|
53
|
+
|
|
54
|
+
workerData.port.on('message', async (msg) => {
|
|
55
|
+
if (msg.type === 'stop') {
|
|
56
|
+
await runtime.stop()
|
|
57
|
+
process.exit(0)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const hosts = (await runtime?.start()) || undefined
|
|
62
|
+
|
|
63
|
+
workerData.port.postMessage({
|
|
64
|
+
type: 'ready',
|
|
65
|
+
data: { hosts },
|
|
66
|
+
} satisfies ThreadPortMessage)
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error(new Error('Worker thread error:', { cause: error }))
|
|
69
|
+
}
|
|
@@ -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 { jobWorkerQueue } = options
|
|
40
|
+
const runtime = new JobWorkerRuntime(serverConfig, {
|
|
41
|
+
queueName: jobWorkerQueue,
|
|
42
|
+
port: workerData.port,
|
|
43
|
+
})
|
|
44
|
+
return runtime
|
|
45
|
+
} else {
|
|
46
|
+
throw new Error(`Unknown runtime type: ${(workerData.runtime as any).type}`)
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/resolver.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ResolverFactory } from 'oxc-resolver'
|
|
2
|
+
|
|
3
|
+
const fallback: Record<string, [string]> = {}
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
// oxc-resolver fails to resolve uWebSockets.js for some reason
|
|
7
|
+
const mdl = 'uWebSockets.js'
|
|
8
|
+
const path = import.meta.resolve(mdl)
|
|
9
|
+
fallback[mdl] = [path]
|
|
10
|
+
} catch {}
|
|
11
|
+
|
|
12
|
+
export const resolver = new ResolverFactory({
|
|
13
|
+
tsconfig: 'auto',
|
|
14
|
+
extensions: ['.ts', '.js', '.mjs', '.mts', '.json', '.node'],
|
|
15
|
+
fallback,
|
|
16
|
+
})
|
package/src/typings.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises'
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
|
|
4
|
+
import dedent from 'dedent'
|
|
5
|
+
|
|
6
|
+
export async function generateTypings(
|
|
7
|
+
applicationImports: Record<string, { path: string; specifier: string }>,
|
|
8
|
+
) {
|
|
9
|
+
await mkdir('.neemata', { recursive: true }).catch(() => {})
|
|
10
|
+
await writeFile(
|
|
11
|
+
resolve('.neemata', 'types.d.ts'),
|
|
12
|
+
dedent`
|
|
13
|
+
/// <reference types="@nmtjs/runtime/types" />
|
|
14
|
+
|
|
15
|
+
declare module '@nmtjs/runtime/types' {
|
|
16
|
+
interface Applications {
|
|
17
|
+
${Object.entries(applicationImports)
|
|
18
|
+
.map(
|
|
19
|
+
([appName, { specifier }]) =>
|
|
20
|
+
`'${appName}': typeof import('${specifier}').default`,
|
|
21
|
+
)
|
|
22
|
+
.join('\n')}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
`,
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { isBuiltin } from 'node:module'
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
|
|
4
|
+
import { build as viteBuild } from 'vite'
|
|
5
|
+
|
|
6
|
+
import type { NeemataConfig } from '../config.ts'
|
|
7
|
+
import type { ViteConfigOptions } from './config.ts'
|
|
8
|
+
// import pkgJson from '../../package.json' with { type: 'json' }
|
|
9
|
+
import { createConfig } from './config.ts'
|
|
10
|
+
import { buildPlugins } from './plugins.ts'
|
|
11
|
+
|
|
12
|
+
export async function createBuilder(
|
|
13
|
+
configOptions: ViteConfigOptions,
|
|
14
|
+
neemataConfig: NeemataConfig,
|
|
15
|
+
) {
|
|
16
|
+
const config = createConfig(configOptions)
|
|
17
|
+
async function build(): Promise<any> {
|
|
18
|
+
const packageJson = await import(resolve('./package.json'), {
|
|
19
|
+
with: { type: 'json' },
|
|
20
|
+
}).then((mod) => mod.default)
|
|
21
|
+
|
|
22
|
+
// techinically it's possible to do the same with rolldown directly,
|
|
23
|
+
// but vite handles a lot of things, like defines substitutions, etc.
|
|
24
|
+
// also, since during dev the code is processed via vite anyway,
|
|
25
|
+
// using vite for build as well ensures consistency between dev and prod
|
|
26
|
+
return await viteBuild({
|
|
27
|
+
appType: 'custom',
|
|
28
|
+
clearScreen: false,
|
|
29
|
+
resolve: { alias: config.alias },
|
|
30
|
+
ssr: { noExternal: true },
|
|
31
|
+
plugins: [...buildPlugins, ...neemataConfig.plugins],
|
|
32
|
+
build: {
|
|
33
|
+
lib: { entry: config.entries, formats: ['es'] },
|
|
34
|
+
ssr: true,
|
|
35
|
+
target: 'node20',
|
|
36
|
+
sourcemap: true,
|
|
37
|
+
outDir: resolve(neemataConfig.build.outDir),
|
|
38
|
+
minify: neemataConfig.build.minify,
|
|
39
|
+
emptyOutDir: true,
|
|
40
|
+
rolldownOptions: {
|
|
41
|
+
platform: 'node',
|
|
42
|
+
external: (id) => {
|
|
43
|
+
if (neemataConfig.externalDependencies === 'all') return true
|
|
44
|
+
if (
|
|
45
|
+
isBuiltin(id) ||
|
|
46
|
+
id.includes('vite/server') ||
|
|
47
|
+
id.endsWith('.node')
|
|
48
|
+
)
|
|
49
|
+
return true
|
|
50
|
+
|
|
51
|
+
if (neemataConfig.externalDependencies === 'prod') {
|
|
52
|
+
const prodDeps = Object.keys(packageJson.dependencies ?? {})
|
|
53
|
+
if (prodDeps.includes(id)) return true
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (Array.isArray(neemataConfig.externalDependencies)) {
|
|
57
|
+
for (const dep of neemataConfig.externalDependencies) {
|
|
58
|
+
if (typeof dep === 'string' && dep === id) return true
|
|
59
|
+
if (dep instanceof RegExp && dep.test(id)) return true
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return false
|
|
64
|
+
},
|
|
65
|
+
transform: {
|
|
66
|
+
define: {
|
|
67
|
+
__VITE_CONFIG__: '""',
|
|
68
|
+
__APPLICATIONS_CONFIG__: JSON.stringify(
|
|
69
|
+
JSON.stringify(
|
|
70
|
+
Object.fromEntries(
|
|
71
|
+
Object.keys(configOptions.applicationImports).map(
|
|
72
|
+
(appName) => [appName, `./application.${appName}.js`],
|
|
73
|
+
),
|
|
74
|
+
),
|
|
75
|
+
),
|
|
76
|
+
),
|
|
77
|
+
__dirname: 'new URL(".", import.meta.url).pathname',
|
|
78
|
+
__filename: 'new URL(import.meta.url).pathname',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
output: {
|
|
82
|
+
entryFileNames: '[name].js',
|
|
83
|
+
chunkFileNames: 'chunks/[name]-[hash].js',
|
|
84
|
+
advancedChunks: {
|
|
85
|
+
groups: [
|
|
86
|
+
{
|
|
87
|
+
name: 'ioredis',
|
|
88
|
+
test: /node_modules[\\/](@ioredis|ioredis|redis)/,
|
|
89
|
+
priority: 4,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'bullmq',
|
|
93
|
+
test: /node_modules[\\/]bullmq/,
|
|
94
|
+
priority: 2,
|
|
95
|
+
},
|
|
96
|
+
{ name: 'zod', test: /node_modules[\\/]zod/, priority: 2 },
|
|
97
|
+
{ name: 'pino', test: /node_modules[\\/]pino/, priority: 2 },
|
|
98
|
+
{
|
|
99
|
+
name: '@nmtjs-runtime',
|
|
100
|
+
test: /node_modules[\\/](@nmtjs[\\/]runtime)/,
|
|
101
|
+
priority: 2,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: '@nmtjs-common',
|
|
105
|
+
test: /node_modules[\\/]@nmtjs[\\/](?=[^runtime|nmtjs])/,
|
|
106
|
+
priority: 1,
|
|
107
|
+
},
|
|
108
|
+
{ name: 'vendor', test: /node_modules/, priority: 0 },
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
minify: neemataConfig.build.minify,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
chunkSizeWarningLimit: 10_000,
|
|
115
|
+
},
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
return { build }
|
|
119
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url'
|
|
2
|
+
|
|
3
|
+
export type ViteConfigOptions = {
|
|
4
|
+
applicationImports: Record<string, { path: string; specifier: string }>
|
|
5
|
+
serverEntryPath: string
|
|
6
|
+
entrypointMainPath: string
|
|
7
|
+
entrypointWorkerPath: string
|
|
8
|
+
entrypointThreadPath: string
|
|
9
|
+
// entrypointCLIPath: string
|
|
10
|
+
configPath: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const ext = new URL(import.meta.url).pathname.endsWith('.ts') ? '.ts' : '.js'
|
|
14
|
+
|
|
15
|
+
export const baseViteConfigOptions = {
|
|
16
|
+
entrypointMainPath: fileURLToPath(
|
|
17
|
+
import.meta.resolve(`../entrypoints/main${ext}`),
|
|
18
|
+
),
|
|
19
|
+
entrypointWorkerPath: fileURLToPath(
|
|
20
|
+
import.meta.resolve(`../entrypoints/worker${ext}`),
|
|
21
|
+
),
|
|
22
|
+
entrypointThreadPath: fileURLToPath(
|
|
23
|
+
import.meta.resolve(`../entrypoints/thread${ext}`),
|
|
24
|
+
),
|
|
25
|
+
// entrypointCLIPath: fileURLToPath(import.meta.resolve('../entrypoints/cli')),
|
|
26
|
+
} satisfies Partial<ViteConfigOptions>
|
|
27
|
+
|
|
28
|
+
export function createConfig(options: ViteConfigOptions) {
|
|
29
|
+
const alias = { '#server': options.serverEntryPath }
|
|
30
|
+
const entries = {
|
|
31
|
+
server: options.serverEntryPath,
|
|
32
|
+
main: options.entrypointMainPath,
|
|
33
|
+
thread: options.entrypointThreadPath,
|
|
34
|
+
worker: options.entrypointWorkerPath,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (const [name, { path }] of Object.entries(options.applicationImports)) {
|
|
38
|
+
entries[`application.${name}`] = path
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { alias, entries }
|
|
42
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { dirname, join } from 'node:path'
|
|
2
|
+
|
|
3
|
+
import type { Plugin } from 'vite'
|
|
4
|
+
import type { Target } from 'vite-plugin-static-copy'
|
|
5
|
+
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
|
6
|
+
|
|
7
|
+
import { resolver } from '../resolver.ts'
|
|
8
|
+
|
|
9
|
+
const targets: Target[] = []
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const { path } = resolver.sync(process.cwd(), '@nmtjs/proxy')
|
|
13
|
+
if (path) {
|
|
14
|
+
targets.push({
|
|
15
|
+
src: join(
|
|
16
|
+
dirname(path),
|
|
17
|
+
`neemata-proxy.${process.platform}-${process.arch}*.node`,
|
|
18
|
+
),
|
|
19
|
+
dest: './chunks/',
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
} catch {}
|
|
23
|
+
|
|
24
|
+
export const buildPlugins: Plugin[] = [
|
|
25
|
+
...(targets.length ? viteStaticCopy({ targets }) : []),
|
|
26
|
+
]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BroadcastChannel,
|
|
3
|
+
isInternalThread,
|
|
4
|
+
isMainThread,
|
|
5
|
+
parentPort,
|
|
6
|
+
threadId,
|
|
7
|
+
} from 'node:worker_threads'
|
|
8
|
+
|
|
9
|
+
import type { HotPayload } from 'vite'
|
|
10
|
+
import type { ModuleRunnerTransport } from 'vite/module-runner'
|
|
11
|
+
import { noopFn } from '@nmtjs/common'
|
|
12
|
+
import {
|
|
13
|
+
createNodeImportMeta,
|
|
14
|
+
ESModulesEvaluator,
|
|
15
|
+
ModuleRunner,
|
|
16
|
+
} from 'vite/module-runner'
|
|
17
|
+
|
|
18
|
+
export function createBroadcastChannel(threadId: number): BroadcastChannel {
|
|
19
|
+
return new BroadcastChannel(`nmtjs:vite:${threadId}`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createModuleRunner(
|
|
23
|
+
mode: 'development' | 'production' = 'development',
|
|
24
|
+
): ModuleRunner {
|
|
25
|
+
if (isMainThread || isInternalThread)
|
|
26
|
+
throw new Error('Module runner can only be created inside worker threads.')
|
|
27
|
+
|
|
28
|
+
const channel = createBroadcastChannel(threadId)
|
|
29
|
+
|
|
30
|
+
const transport: ModuleRunnerTransport = {
|
|
31
|
+
connect({ onMessage, onDisconnection }) {
|
|
32
|
+
// @ts-expect-error
|
|
33
|
+
channel.onmessage = (event: MessageEvent<HotPayload>) => {
|
|
34
|
+
onMessage(event.data)
|
|
35
|
+
}
|
|
36
|
+
parentPort!.on('close', onDisconnection)
|
|
37
|
+
},
|
|
38
|
+
send(data) {
|
|
39
|
+
channel.postMessage(data)
|
|
40
|
+
},
|
|
41
|
+
timeout: 5000,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const runner = new ModuleRunner(
|
|
45
|
+
{
|
|
46
|
+
transport,
|
|
47
|
+
createImportMeta: createNodeImportMeta,
|
|
48
|
+
hmr:
|
|
49
|
+
mode === 'development'
|
|
50
|
+
? { logger: { debug: noopFn, error: console.error } }
|
|
51
|
+
: false,
|
|
52
|
+
},
|
|
53
|
+
new ESModulesEvaluator(),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return runner
|
|
57
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { DevEnvironmentOptions, UserConfig } from 'vite'
|
|
2
|
+
import { createServer as createViteServer } from 'vite'
|
|
3
|
+
|
|
4
|
+
import type { ViteConfigOptions } from './config.ts'
|
|
5
|
+
|
|
6
|
+
export function createServer(
|
|
7
|
+
options: ViteConfigOptions,
|
|
8
|
+
config: UserConfig,
|
|
9
|
+
dev: DevEnvironmentOptions = {},
|
|
10
|
+
) {
|
|
11
|
+
return createViteServer({
|
|
12
|
+
...config,
|
|
13
|
+
server: { middlewareMode: true, ws: false },
|
|
14
|
+
resolve: { tsconfigPaths: true, ...config.resolve },
|
|
15
|
+
environments: {
|
|
16
|
+
neemata: {
|
|
17
|
+
consumer: 'server',
|
|
18
|
+
dev,
|
|
19
|
+
define: {
|
|
20
|
+
__VITE_CONFIG__: JSON.stringify(
|
|
21
|
+
JSON.stringify({ options, mode: config.mode }),
|
|
22
|
+
),
|
|
23
|
+
__APPLICATIONS_CONFIG__: JSON.stringify(
|
|
24
|
+
JSON.stringify(
|
|
25
|
+
Object.fromEntries(
|
|
26
|
+
Object.entries(options.applicationImports).map(
|
|
27
|
+
([appName, { path }]) => [appName, path],
|
|
28
|
+
),
|
|
29
|
+
),
|
|
30
|
+
),
|
|
31
|
+
),
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
})
|
|
36
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ViteDevServer } from 'vite'
|
|
2
|
+
import type { ModuleRunner } from 'vite/module-runner'
|
|
3
|
+
import { noopFn } from '@nmtjs/common'
|
|
4
|
+
import { createServerModuleRunner } from 'vite'
|
|
5
|
+
|
|
6
|
+
import type { NeemataConfig } from '../../config.ts'
|
|
7
|
+
import type { ViteConfigOptions } from '../config.ts'
|
|
8
|
+
import { createConfig } from '../config.ts'
|
|
9
|
+
import { buildPlugins } from '../plugins.ts'
|
|
10
|
+
import { createServer } from '../server.ts'
|
|
11
|
+
|
|
12
|
+
export async function createMainServer(
|
|
13
|
+
options: ViteConfigOptions,
|
|
14
|
+
mode: 'development' | 'production',
|
|
15
|
+
neemataConfig: NeemataConfig,
|
|
16
|
+
): Promise<{ server: ViteDevServer; runner: ModuleRunner }> {
|
|
17
|
+
const config = createConfig(options)
|
|
18
|
+
const server = await createServer(options, {
|
|
19
|
+
appType: 'custom',
|
|
20
|
+
clearScreen: false,
|
|
21
|
+
resolve: { alias: config.alias },
|
|
22
|
+
mode,
|
|
23
|
+
plugins: [...buildPlugins, ...neemataConfig.plugins],
|
|
24
|
+
})
|
|
25
|
+
const environment = server.environments.neemata
|
|
26
|
+
|
|
27
|
+
const runner = createServerModuleRunner(environment, {
|
|
28
|
+
hmr:
|
|
29
|
+
mode === 'development'
|
|
30
|
+
? { logger: { debug: noopFn, error: console.error } }
|
|
31
|
+
: false,
|
|
32
|
+
})
|
|
33
|
+
return { server, runner }
|
|
34
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type EventEmitter from 'node:events'
|
|
2
|
+
import type { BroadcastChannel, Worker } from 'node:worker_threads'
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
HotChannel,
|
|
6
|
+
HotChannelClient,
|
|
7
|
+
HotChannelListener,
|
|
8
|
+
HotPayload,
|
|
9
|
+
ViteDevServer,
|
|
10
|
+
} from 'vite'
|
|
11
|
+
import { DevEnvironment } from 'vite'
|
|
12
|
+
|
|
13
|
+
import type { NeemataConfig } from '../../config.ts'
|
|
14
|
+
import type { ViteConfigOptions } from '../config.ts'
|
|
15
|
+
import { createConfig } from '../config.ts'
|
|
16
|
+
import { buildPlugins } from '../plugins.ts'
|
|
17
|
+
import { createBroadcastChannel } from '../runners/worker.ts'
|
|
18
|
+
import { createServer } from '../server.ts'
|
|
19
|
+
|
|
20
|
+
export async function createWorkerServer(
|
|
21
|
+
options: ViteConfigOptions,
|
|
22
|
+
mode: 'development' | 'production',
|
|
23
|
+
neemataConfig: NeemataConfig,
|
|
24
|
+
events: EventEmitter<{ worker: [Worker] }>,
|
|
25
|
+
): Promise<ViteDevServer> {
|
|
26
|
+
const config = createConfig(options)
|
|
27
|
+
const applicationEntries = Object.values(options.applicationImports).map(
|
|
28
|
+
(v) => v.path,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
const _injectHmr = `\n\nif(import.meta.hot) { import.meta.hot.accept(globalThis._hotAccept) }`
|
|
32
|
+
|
|
33
|
+
const server = await createServer(
|
|
34
|
+
options,
|
|
35
|
+
{
|
|
36
|
+
appType: 'custom',
|
|
37
|
+
clearScreen: false,
|
|
38
|
+
resolve: { alias: config.alias },
|
|
39
|
+
mode,
|
|
40
|
+
plugins: [
|
|
41
|
+
...buildPlugins,
|
|
42
|
+
...neemataConfig.plugins,
|
|
43
|
+
mode === 'development'
|
|
44
|
+
? [
|
|
45
|
+
{
|
|
46
|
+
name: 'neemata-worker-application-hmr',
|
|
47
|
+
transform(code, id, options) {
|
|
48
|
+
if (applicationEntries.includes(id)) {
|
|
49
|
+
return code + _injectHmr
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
]
|
|
54
|
+
: [],
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
createEnvironment: async (name, config, context) => {
|
|
59
|
+
const channels = new Map<number, BroadcastChannel>()
|
|
60
|
+
const clients = new Map<BroadcastChannel, HotChannelClient>()
|
|
61
|
+
const handlers = new Map<string, HotChannelListener>()
|
|
62
|
+
|
|
63
|
+
events.on('worker', (worker) => {
|
|
64
|
+
const channel = createBroadcastChannel(worker.threadId)
|
|
65
|
+
channel.onmessage = (event) => {
|
|
66
|
+
const value = event.data
|
|
67
|
+
const handler = handlers.get(value.event)
|
|
68
|
+
if (handler) handler(value.data, client)
|
|
69
|
+
}
|
|
70
|
+
channels.set(worker.threadId, channel)
|
|
71
|
+
const client = {
|
|
72
|
+
send: (payload: HotPayload) => {
|
|
73
|
+
channel.postMessage(payload)
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
clients.set(channel, client)
|
|
77
|
+
worker.on('exit', () => {
|
|
78
|
+
const handler = handlers.get('vite:client:disconnect')
|
|
79
|
+
if (handler) handler(undefined, client)
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const transport: HotChannel = {
|
|
84
|
+
send(data) {
|
|
85
|
+
for (const channel of channels.values()) {
|
|
86
|
+
channel.postMessage(data)
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
on(event, handler) {
|
|
90
|
+
handlers.set(event, handler)
|
|
91
|
+
},
|
|
92
|
+
off(event) {
|
|
93
|
+
handlers.delete(event)
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const environment = new DevEnvironment(name, config, {
|
|
98
|
+
hot: mode === 'development',
|
|
99
|
+
transport,
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
return environment
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
)
|
|
106
|
+
return server
|
|
107
|
+
}
|