lakutata 3.1.3 → 3.1.5

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 (55) hide show
  1. package/README.md +10 -10
  2. package/README.zh-CN.md +5 -5
  3. package/dist/cjs/exports/NwjsAdapter.d.ts +1 -0
  4. package/dist/cjs/exports/NwjsAdapter.js +20 -0
  5. package/dist/esm/exports/NwjsAdapter.js +1 -0
  6. package/dist/types/exports/NwjsAdapter.d.ts +1 -0
  7. package/doc/en/Application.md +1 -1
  8. package/doc/en/Cache.md +2 -1
  9. package/doc/en/Command.md +10 -4
  10. package/doc/en/ContainerdIntegration.md +1 -1
  11. package/doc/en/DatabaseDrivers.md +3 -2
  12. package/doc/en/Entrypoint.md +3 -2
  13. package/doc/en/Exceptions.md +1 -1
  14. package/doc/en/FFI.md +1 -1
  15. package/doc/en/GRPC.md +9 -3
  16. package/doc/en/GettingStarted.md +15 -2
  17. package/doc/en/HTTP.md +35 -0
  18. package/doc/en/I18n.md +1 -1
  19. package/doc/en/Installers.md +34 -6
  20. package/doc/en/MCP.md +4 -2
  21. package/doc/en/Math.md +1 -1
  22. package/doc/en/NativeAddons.md +9 -2
  23. package/doc/en/Netif.md +261 -0
  24. package/doc/en/Overview.md +10 -8
  25. package/doc/en/Packaging.md +4 -1
  26. package/doc/en/ProjectStructure.md +8 -3
  27. package/doc/en/ServiceCalls.md +69 -6
  28. package/doc/en/UpgradingFrom1.md +2 -2
  29. package/doc/en/UpgradingFrom2.md +4 -4
  30. package/doc/en/WebRTC.md +4 -4
  31. package/doc/zh/Application.md +1 -1
  32. package/doc/zh/Cache.md +1 -1
  33. package/doc/zh/Command.md +7 -3
  34. package/doc/zh/ContainerdIntegration.md +1 -1
  35. package/doc/zh/DatabaseDrivers.md +1 -1
  36. package/doc/zh/Entrypoint.md +2 -1
  37. package/doc/zh/Exceptions.md +1 -1
  38. package/doc/zh/FFI.md +1 -1
  39. package/doc/zh/GRPC.md +6 -3
  40. package/doc/zh/GettingStarted.md +11 -2
  41. package/doc/zh/HTTP.md +30 -0
  42. package/doc/zh/I18n.md +1 -1
  43. package/doc/zh/Installers.md +16 -5
  44. package/doc/zh/MCP.md +3 -2
  45. package/doc/zh/Math.md +1 -1
  46. package/doc/zh/NativeAddons.md +3 -2
  47. package/doc/zh/Netif.md +207 -0
  48. package/doc/zh/Overview.md +5 -4
  49. package/doc/zh/Packaging.md +4 -1
  50. package/doc/zh/ProjectStructure.md +4 -2
  51. package/doc/zh/ServiceCalls.md +55 -5
  52. package/doc/zh/UpgradingFrom1.md +2 -2
  53. package/doc/zh/UpgradingFrom2.md +3 -3
  54. package/doc/zh/WebRTC.md +1 -1
  55. package/package.json +29 -15
@@ -38,6 +38,7 @@ node dist/greet.cjs
38
38
  | `--outfile <file>` | 单文件包的路径,默认 `dist/<name>.cjs` |
39
39
  | `--no-minify` | 不压缩(无论是否压缩,类名都会保留) |
40
40
  | `--external <modules...>` | 不打进包里的模块,运行时从单文件包旁边的 `node_modules` 加载 |
41
+ | `--node <version>` | 运行单文件包的 Node.js 版本(NW.js 0.70 是 `18.10`):单文件包用这个版本支持的语法生成;默认是运行工具的那个版本 |
41
42
 
42
43
  ## 3. 原生插件
43
44
 
@@ -99,11 +100,12 @@ npx lakutata package exe --runtime bun # 使用 Bun
99
100
 
100
101
  | 选项 | 含义 |
101
102
  |---|---|
102
- | `--runtime node`(默认) | 把单文件包嵌入运行工具的那个 Node.js 的副本中(Node.js 的单可执行文件应用):在 macOS arm64 上约 110 MB |
103
+ | `--runtime node`(默认) | 把单文件包嵌入运行工具的 Node.js(或 `--node-program` 指定的 Node.js)的副本中(Node.js 的单可执行文件应用):在 macOS arm64 上约 110 MB |
103
104
  | `--runtime bun` | 由 Bun 编译单文件包(优先用项目中的 `bun` 包,否则用系统的 `bun`):在 macOS arm64 上约 70 MB,启动更快;运行时也就是 Bun |
104
105
  | `--layout file`(默认) | 单个文件:原生插件、按路径读取的文件和资源都嵌入其中,启动时解压 |
105
106
  | `--layout directory` | `dist/<name>/` 中包含可执行文件、`native/` 和 `resources/`,直接从那里加载,不解压(安装包用的就是这种布局) |
106
107
  | `--outdir <directory>` | 可执行文件的输出目录,默认 `dist` |
108
+ | `--node-program <path>` | 嵌入另一个 Node.js,而不是运行工具的那个:需要 Node.js 20.12、21.7 或更高版本(比如 `$(nvm which 22)`),这样较旧的 Node.js 也能运行这个命令;单文件包用它支持的语法生成 |
107
109
  | `--entry`、`--no-minify`、`--external` | 与单文件包相同 |
108
110
 
109
111
  - **解压**:单文件布局的可执行文件在第一次运行时,把嵌入的内容解压到缓存目录中一个以内容命名的子目录,所以每个版本只解压一次:macOS 上是 `~/Library/Caches/<name>`,Windows 上是 `%LOCALAPPDATA%\<name>\Cache`,Linux 上是 `$XDG_CACHE_HOME/<name>` 或 `~/.cache/<name>`,也可以用 `LAKUTATA_NATIVE_DIR` 指定目录。缓存目录不可写时,改用临时目录中的 `<name>`(指定了 `LAKUTATA_NATIVE_DIR` 时不会这样)。
@@ -134,6 +136,7 @@ npx lakutata package exe --runtime bun # 使用 Bun
134
136
  | 可执行文件,单文件布局 | `true` | 资源解压到的目录 |
135
137
  | 可执行文件,目录布局,或已安装的应用 | `true` | 可执行文件旁边的 `resources` |
136
138
  | 打包后的 Electron 应用 | `true` | Electron 资源目录中的 `main.resources` |
139
+ | NW.js 应用(由 `lakutata nwjs run` 运行,或已打包) | `true` | 应用目录中的 `resources` |
137
140
 
138
141
  即使应用没有声明资源,`resourcesPath()` 也会返回路径,所以这个目录可能不存在。应用自己决定从哪里读取文件:
139
142
 
@@ -45,7 +45,7 @@ Application
45
45
 
46
46
  ## 2. 工作区模板
47
47
 
48
- 其他模板都是 npm 工作区:应用是 `packages/app` 这个包,带界面或安装包的模板还会加上 `packages/ui`(网页,或 Electron 的渲染进程)和 `packages/resources`(图标)。`service` 模板:
48
+ 其他模板都是 npm 工作区:应用是 `packages/app` 这个包,带界面或安装包的模板还会加上 `packages/ui`(网页、Electron 的渲染进程,或 NW.js 的用户界面)和 `packages/resources`(图标)。`service` 模板:
49
49
 
50
50
  ```
51
51
  shop/
@@ -79,6 +79,7 @@ shop/
79
79
  - `web-next` 和 `web-nuxt`:`packages/ui`,一个 Next.js 或 Nuxt 应用,由应用在同一个进程中渲染。
80
80
  - `cli`:一个命令行工具(`commanderCLIEntrypoint`),带有 `package:bundle` 和 `package:exe` 脚本,把它打包到 `dist/`([Packaging](Packaging.md))。
81
81
  - `electron-react` 和 `electron-vue`:`packages/app` 中是 Electron 的主进程(它的窗口,以及通过 IPC 提供的服务动作),`packages/ui` 中是渲染进程,还有制作安装包的脚本。
82
+ - `nwjs-react` 和 `nwjs-vue`:`packages/app` 中是 NW.js 的后台脚本(它的窗口:窗口的宿主页面在 `packages/app/host`,用 webview 承载用户界面;以及它的服务动作),`packages/ui` 中是用户界面,NW.js 的版本在 `packages/app/nwjs.json`(由 `npm run nwjs:use -- <版本>` 写入),还有制作安装包的脚本。
82
83
 
83
84
  打包产物写到工作区根目录的 `dist/` 中;`.gitignore` 忽略 `build`、`dist` 以及界面的构建输出。
84
85
 
@@ -90,8 +91,9 @@ shop/
90
91
  |---|---|---|
91
92
  | `i18n` | 全部 | `src/locales` 中各语言的消息(`en`、`zh-CN`)、`i18n` 组件、语言中间件,以及 `i18n:check` 和 `i18n:types` 脚本([I18n](I18n.md)) |
92
93
  | `installer` | `cli`、`service`、`web-*` | 应用的 `lakutata.installer`、制作安装包的脚本(在本机制作,以及在 Docker 中制作 Linux 的安装包),服务类模板还会注册系统服务([Installers](Installers.md)) |
94
+ | `win7` | `nwjs-*` | 用 NW.js 0.72.0 代替模板发布时的版本:它是最后一个能在 Windows 7、8 和 8.1 上运行的版本;还有制作 32 位 Windows 安装包的 `package:ia32` 脚本 |
93
95
 
94
- Electron 模板不需要这个功能也能制作安装包。
96
+ Electron 和 NW.js 模板不需要 `installer` 功能也能制作安装包。
95
97
 
96
98
  ## 4. 自己的模板
97
99
 
@@ -1,6 +1,6 @@
1
1
  # Lakutata 服务调用
2
2
 
3
- 服务动作(`@ServiceAction`)通过消息而不是 URL 来调用:消息是一个对象,其中一部分键用来选择动作,称为**模式**,其余的键就是输入。同一组动作可以同时通过多种传输方式提供:本指南介绍的普通 HTTP、Socket.IO、JSON-RPC 2.0(基于 HTTP、WebSocket 或标准输入输出)和 Electron 的 IPC,以及在各自指南中介绍的 gRPC、NATS 和 WebRTC 的数据通道。控制器及其动作见 [Controllers](Controllers.md),入口及其适配器见 [Entrypoint](Entrypoint.md)。
3
+ 服务动作(`@ServiceAction`)通过消息而不是 URL 来调用:消息是一个对象,其中一部分键用来选择动作,称为**模式**,其余的键就是输入。同一组动作可以同时通过多种传输方式提供:本指南介绍的普通 HTTP、Socket.IO、JSON-RPC 2.0(基于 HTTP、WebSocket 或标准输入输出)、Electron 的 IPC 和 NW.js 的窗口,以及在各自指南中介绍的 gRPC、NATS 和 WebRTC 的数据通道。控制器及其动作见 [Controllers](Controllers.md),入口及其适配器见 [Entrypoint](Entrypoint.md)。
4
4
 
5
5
  ## 1. 第一个服务
6
6
 
@@ -128,7 +128,7 @@ class OrderController extends Controller {
128
128
 
129
129
  ## 3. 响应
130
130
 
131
- HTTP 服务、Socket.IO 和 Electron 以封套响应;JSON-RPC 使用它自己的响应格式(第 4 节)。
131
+ HTTP 服务、Socket.IO、Electron 和 NW.js 以封套响应;JSON-RPC 使用它自己的响应格式(第 4 节)。
132
132
 
133
133
  ```text
134
134
  {"success": true, "error": null, "payload": <结果>}
@@ -165,6 +165,7 @@ export async function call<T>(message: Record<string, unknown>): Promise<T> {
165
165
  | `socketIOServiceEntrypoint()` | `lakutata/adapter/socket.io` | `socket.io`(客户端需要 `socket.io-client`) | 带确认回调的 emit | Socket.IO 事件 |
166
166
  | `jsonRPCServiceEntrypoint()` | `lakutata/adapter/jsonrpc` | WebSocket 需要 `ws` | JSON-RPC 2.0 请求 | 通知(WebSocket、stdio) |
167
167
  | `electronIPCServiceEntrypoint()` | `lakutata/adapter/electron` | (Electron) | `ipcRenderer.invoke()` | IPC 消息 |
168
+ | `nwjsServiceEntrypoint()` | `lakutata/adapter/nwjs` | (NW.js) | 窗口的宿主页面对桥接对象的一次调用 | 调用宿主页面交给它的函数 |
168
169
  | `grpcServiceEntrypoint()` | `@lakutata/grpc/service` | `@lakutata/grpc` | 见 [GRPC](GRPC.md) | gRPC 流 |
169
170
  | `natsServiceEntrypoint()` | `@lakutata/nats` | `@lakutata/nats` | 见 [NATS](NATS.md) | NATS 消息 |
170
171
  | `webrtcServiceEntrypoint()` | `@lakutata/webrtc` | `@lakutata/webrtc` | 数据通道上的 JSON-RPC 2.0 请求,见 [WebRTC](WebRTC.md) | 通知 |
@@ -181,7 +182,7 @@ export async function call<T>(message: Record<string, unknown>): Promise<T> {
181
182
  | `host` | 所有网络接口 | 监听的地址 |
182
183
  | `path` | `'/'` | 请求的路径;事件在它下面的 `<path>/events` |
183
184
  | `limit` | `10485760`(10 MiB) | 请求体的最大字节数 |
184
- | `listening` | | `(server) => …`,服务器开始监听后调用 |
185
+ | `listening` | | `(server, listeners) => …`,服务器开始监听后调用(`listeners`:按网口监听时才有,见下文) |
185
186
 
186
187
  - 请求是发往 `path` 的 `POST`,请求体是 JSON 格式的消息。无论动作的结果如何,都以 200 和封套响应。请求体不是 JSON 时以 400 响应,超过 `limit` 时以 413 响应,其他方法或路径以 404 响应;这些响应也带有封套。
187
188
  - CORS 响应头允许任何来源,允许的方法是 `GET`、`POST` 和 `OPTIONS`,并允许任何请求头,例如 `Authorization`。
@@ -255,7 +256,7 @@ Application
255
256
  | `options` | | Socket.IO 服务器的选项:`cors`、`path`、`maxHttpBufferSize`…… |
256
257
  | `server` | | 共用的 HTTP 服务器,或者返回它的函数(见下文) |
257
258
  | `io` | | 一个已有的 Socket.IO 服务器,按原样使用 |
258
- | `listening` | | `(io, server) => …`,适配器就绪后调用 |
259
+ | `listening` | | `(io, server, listeners) => …`,适配器就绪后调用 |
259
260
 
260
261
  ```typescript
261
262
  import {io, type Socket} from 'socket.io-client'
@@ -307,7 +308,7 @@ export const entrypoint = buildEntrypoints({
307
308
  | `input`、`output` | `process.stdin`、`process.stdout` | stdio 使用的流 |
308
309
  | `end` | `'application'` | stdio 的输入结束时:`'application'` 在正在处理的请求都得到响应后退出应用,`'none'` 什么都不做,或者一个函数 |
309
310
  | `heartbeat` | `30000` | WebSocket 的 ping 间隔,单位毫秒;`0` 表示不发送 |
310
- | `listening` | | `(server) => …`,HTTP 或 WebSocket 的服务器开始监听后调用 |
311
+ | `listening` | | `(server, listeners) => …`,HTTP 或 WebSocket 的服务器开始监听后调用 |
311
312
 
312
313
  请求的方法名和参数构成消息:`{"method": "order.get", "params": {"id": 1}}` 就是消息 `{method: 'order.get', id: 1}`,所以动作要在模式中声明方法名:
313
314
 
@@ -378,6 +379,19 @@ $ echo '{"jsonrpc": "2.0", "id": 1, "method": "sum", "params": {"values": [1, 2,
378
379
 
379
380
  输入结束、请求都得到响应之后,应用就会退出(`end`)。
380
381
 
382
+ ### 按网口监听
383
+
384
+ `httpServiceEntrypoint()`、使用自己服务器的 `socketIOServiceEntrypoint()`,以及走 HTTP 或 WebSocket 的 `jsonRPCServiceEntrypoint()`,都可以按网口监听,而不是按地址监听,不管网口现在是什么地址。这需要另外安装 `@lakutata/netif` 包。它们接受和 HTTP 适配器一样的 `interface`、`family`、`strict`、`onError` 和 `interfaceListening` 选项(见 [HTTP](HTTP.md) 第 2 节),`listening` 回调的最后一个参数是网口的监听器:
385
+
386
+ ```typescript
387
+ import {httpServiceEntrypoint} from 'lakutata/com/entrypoint'
388
+
389
+ //服务只在 eth1 上提供,不管它是什么地址
390
+ export const service = httpServiceEntrypoint({port: 3000, interface: 'eth1'})
391
+ ```
392
+
393
+ 在 Bun 上,交给服务器的连接不能升级为 WebSocket(在 Bun 1.4 上观察到)。所以在 Bun 上,走 WebSocket 的 JSON-RPC 会启动失败;Socket.IO 也会启动失败,除非只用轮询(`options: {transports: ['polling']}`,客户端也要这样设置)。
394
+
381
395
  ### Electron
382
396
 
383
397
  `electronIPCServiceEntrypoint()` 在 Electron 应用的主进程中,向它的窗口提供动作。
@@ -407,6 +421,40 @@ contextBridge.exposeInMainWorld('service', {
407
421
  - `lakutata/adapter/electron` 中的 `ipcEventOfContext(this.context)` 返回请求的 IPC 事件,用来找到发出请求的窗口。
408
422
  - `lakutata create` 的 Electron 模板包含同样形式的预加载脚本,并为页面提供了 `window.ipc`,它的 `invoke()` 返回结果,或者抛出错误。
409
423
 
424
+ ### NW.js
425
+
426
+ `nwjsServiceEntrypoint()` 在 NW.js 应用的后台脚本(它的 Node.js 上下文)中,向它的窗口提供动作。NW.js 让窗口里的页面都能使用 Node.js,页面里的 iframe 也挡不住 iframe 中的内容使用它(iframe 的 sandbox 会被忽略):所以用户界面运行在没有 Node.js 的 `<webview>` 中,由窗口里一个受信任的页面(宿主页面)承载。适配器在 NW.js 与窗口共享的全局对象上放一个桥接对象 `global[Symbol.for(key)]`;宿主页面把界面的请求转发给它,并把向界面推送事件的函数交给它。
427
+
428
+ | 选项 | 默认值 | 含义 |
429
+ |---|---|---|
430
+ | `key` | `'lakutata:service'`(`NWJS_SERVICE_BRIDGE`) | 桥接对象在全局对象上的键 |
431
+ | `global` | NW.js 的全局对象 | 放置桥接对象的对象,测试时可以传入替身 |
432
+
433
+ | 桥接对象的方法 | 含义 |
434
+ |---|---|
435
+ | `invoke(message, clientId)` | 调用一个动作:解析为封套,从不拒绝 |
436
+ | `subscribe(clientId, push)` | 把一个页面登记为客户端:推送给它的事件交给 `push(event, args)` |
437
+ | `unsubscribe(clientId)` | 页面重新加载或窗口关闭时移除这个客户端:它进行中的请求会被中止 |
438
+
439
+ 宿主页面只转发自己的 `<webview>` 里加载的页面发来的消息:
440
+
441
+ ```javascript
442
+ const view = document.querySelector('webview')
443
+ const origin = new URL(view.src).origin
444
+ const bridge = global[Symbol.for('lakutata:service')]
445
+ const send = (message) => view.contentWindow.postMessage(message, origin)
446
+ window.addEventListener('message', async (event) => {
447
+ if (event.source !== view.contentWindow || event.origin !== origin) return
448
+ const {type, id, message, clientId} = event.data
449
+ if (type === 'invoke') send({type: 'response', id: id, response: await bridge.invoke(message, clientId)})
450
+ if (type === 'subscribe') bridge.subscribe(clientId, (name, args) => send({type: 'event', name: name, args: args}))
451
+ })
452
+ ```
453
+
454
+ - `<webview>` 的分区在 NW.js 的清单中声明过,它才能加载应用的文件:`"webview": {"partitions": [{"name": "trusted", "accessible_resources": ["<all_urls>"]}]}`(由构建器写入)。
455
+ - `<webview>` 里重新加载的页面是一个新的客户端:宿主页面会取消前一个客户端的订阅。
456
+ - `lakutata create` 的 NW.js 模板包含这样的宿主页面(`packages/app/host`),并为界面提供了和 Electron 模板一样的 `window.ipc`。
457
+
410
458
  ## 5. 客户端与事件
411
459
 
412
460
  保持连接的传输方式可以向客户端推送事件。客户端通过它给出的 id 来识别:
@@ -419,6 +467,7 @@ contextBridge.exposeInMainWorld('service', {
419
467
  | 基于 WebSocket 的 JSON-RPC | 连接的 `id` 请求头或 `id` 查询参数,否则为随机 id |
420
468
  | 基于 stdio 的 JSON-RPC | `'stdio'` |
421
469
  | Electron | 页面在订阅或调用时给出的 id |
470
+ | NW.js | 宿主页面为它的页面交给桥接对象的 id |
422
471
  | WebRTC | 调用方在房间里的成员 id |
423
472
 
424
473
  动作从 `this.context.clientId` 读取它的客户端 id。`entrypoint` 组件负责推送事件:
@@ -453,6 +502,7 @@ class TaskController extends Controller {
453
502
  | Socket.IO | `socket.on('progress', (percent, label) => …)` |
454
503
  | JSON-RPC(WebSocket、stdio) | `{"jsonrpc": "2.0", "method": "progress", "params": [50, "half"]}` |
455
504
  | Electron | 通过上面预加载脚本中的 `subscribe` 收到 `('progress', [50, 'half'])` |
505
+ | NW.js | 交给桥接对象 `subscribe` 的函数收到 `('progress', [50, 'half'])` |
456
506
  | WebRTC | `PeerServiceProxy` 的 `proxy.on('progress', (percent, label) => …)` |
457
507
 
458
508
  ## 6. 停止与离开
@@ -13,7 +13,7 @@ Lakutata 1.x 是一组包:`@lakutata/core` 1.x、模块 `@lakutata-module/*`
13
13
  | 模块系统 | CommonJS:导入不带扩展名,使用 `__dirname` | ES 模块:相对导入以 `.js` 结尾 |
14
14
  | 服务动作 | `@lakutata-module/service` 的 `@Controller({_ctrl: …})` 和 `@PATTERN({_act: …})` | `lakutata/decorator/ctrl` 的 `@ServiceAction({…})` |
15
15
  | 校验 | 交给 `@Accept` 的 `Validator` schema(yup) | 带 `@Expect` 的 DTO 类 |
16
- | Node.js | `>=14.15.1`(`@lakutata/core` 1.0.60) | `^22.13.0 \|\| >=24.11.0` |
16
+ | Node.js | `>=14.15.1`(`@lakutata/core` 1.0.60) | `>=18.10.0` |
17
17
 
18
18
  **3.x 的 `@lakutata/core` 不是 1.x 那个库的新版本。** 它是 Lakutata 3 的核心,只是用了同一个名字,API 完全不同。在 1.x 项目中执行 `npm install @lakutata/core@latest`,或者使用 `*` 这样的版本范围,都会用它替换 1.x 的核心库,导致构建失败。不要逐个升级 1.x 的包:移植后的应用依赖 `lakutata` 3,它会带来自己的 `@lakutata/core`。
19
19
 
@@ -314,7 +314,7 @@ Application.run(() => ({
314
314
 
315
315
  - **测试。** 1.x 中连接共享开发服务器、直接启动应用的 `npm test`,改为对编译后的测试运行 `node --test`:每个测试文件用 `Application.run(config(port))` 在自己的端口上启动应用,调用它,最后释放它;组件放在 `Container` 中单独测试;其他服务用替身代替(见[示例](examples/README.md))。
316
316
  - **`package.json` 中的 `lakutata` 字段**在 1.x 中告诉命令行工具项目的类型(`type`)、目录和配置文件。在 3.x 中这个字段有别的含义(打包应用的 `resources` 和 `installer`,见 [Packaging](Packaging.md)):请删除 1.x 的这个字段。
317
- - **Node.js、TypeScript 和模块系统。** Node.js `^22.13.0 || >=24.11.0`,与模板一样使用 TypeScript 6,ES 模块(`"type": "module"`,相对导入以 `.js` 结尾),`tsconfig.json` 继承 `lakutata/tsconfig.json`。
317
+ - **Node.js、TypeScript 和模块系统。** Node.js `>=18.10.0`,与模板一样使用 TypeScript 6,ES 模块(`"type": "module"`,相对导入以 `.js` 结尾),`tsconfig.json` 继承 `lakutata/tsconfig.json`。
318
318
  - **端口。** 1.x 的服务模块监听自己的端口(默认 6912),注册中心监听另一个端口(6911)。3.x 的适配器监听各自选项中的端口,而使用 NATS 的服务只主动连接 NATS 服务器:`Dockerfile` 改为暴露这些端口。
319
319
  - **脚本。** 模板的脚本取代了 `shx rm -rf ./build`(改为 `lakutata clean`)、安装时编译的 `prepare`,以及开发入口;`lakutata-deploy` 没有对应项:`service` 模板带有 `Dockerfile` 和 `docker:build` 脚本,镜像由部署环境自己的工具推送。
320
320
 
@@ -14,13 +14,13 @@
14
14
  | 导出的函数 | 大驼峰:`BuildEntrypoints`、`BuildDatabaseOptions`、`MD5` | 小驼峰:`buildEntrypoints`、`buildDatabaseOptions`、`md5` |
15
15
  | 传输方式 | 手写:`BuildHTTPEntrypoint((module, routeMap, handler, registerDestroy) => …)` | 适配器:`lakutata/adapter/express` 中的 `expressHTTPEntrypoint()` 等 |
16
16
  | 只有 3.x 才有的子路径 | | `lakutata/utils`、`lakutata/security`、`lakutata/adapter/*`、`lakutata/com/{containerd,mutex,queue,i18n}` |
17
- | Node.js | `>=20` | `^22.13.0 \|\| >=24.11.0` |
17
+ | Node.js | `>=20` | `>=18.10.0` |
18
18
 
19
19
  `npx @lakutata/cli info` 会显示命令行工具、框架和 Node.js 的版本,以及项目声明的和实际安装的 `lakutata` 版本。没有升级命令:下面的步骤需要手动完成。
20
20
 
21
21
  ## 2. 升级步骤
22
22
 
23
- - **Node.js** `^22.13.0 || >=24.11.0`(22.13 或更高的 22.x,或者 24.11 及以上;不包括 23)。同时更新 `engines`、CI 的版本矩阵,以及 Dockerfile 的基础镜像。
23
+ - **Node.js** `>=18.10.0`(3.0 到 3.1 需要 `^22.13.0 || >=24.11.0`)。2.x 需要的版本(`>=20`)都包括在内:`engines`、CI 的版本矩阵和 Dockerfile 都可以不变。
24
24
  - **支持 `"module": "Node20"` 的 TypeScript**(模板使用 TypeScript 6):`lakutata/tsconfig.json` 现在设置了这个选项(2.x 中是 `Node16`),更旧的编译器不认识它。
25
25
  - **`package.json`**:`"type": "module"`,`lakutata` 为 `^3.0.0`,`@lakutata/*` 包也写 `^3.0.0`,`@lakutata/cli` 放在 `devDependencies` 中。不要把 `lakutata` 的版本号照抄给其他包:补丁版只发布改动过的包,各包的最新版本可能不同(比如 `lakutata` 是 3.1.4,`@lakutata/ffi` 是 3.1.2)。
26
26
  - **全新安装**:删除 `node_modules` 和锁文件,再重新安装。原地升级可能留下多份 `@lakutata/core`,它们的类和装饰器元数据互不相通(跨副本的 `instanceof` 和注入都会失败)。`npm ls @lakutata/core` 必须只显示一个版本。
@@ -204,7 +204,7 @@ Application
204
204
  ## 6. 检查升级结果
205
205
 
206
206
  ```bash
207
- node --version # ^22.13.0 || >=24.11.0
207
+ node --version # >=18.10.0
208
208
  npm ls lakutata @lakutata/core # 每个包只有一个版本
209
209
  npm run build # tsc 没有错误:没有遗留的 2.x 导入
210
210
  grep -rnE "process\.on\('SIG|\|\| ''|(startup|launched|beforeDestroy|acceptsOption)\(|AccessDenyException|=== 500" src
package/doc/zh/WebRTC.md CHANGED
@@ -163,7 +163,7 @@ export class Telemetry extends Component {
163
163
  }
164
164
  ```
165
165
 
166
- `http:` 或 `https:` 的 URL 使用 Socket.IO(需要安装 `socket.io-client`),`ws:` 或 `wss:` 的 URL 使用 Node.js 22 和 Bun 自带的 `WebSocket` 走 JSON-RPC。它的选项:`url`、`id`(默认是随机的 UUID)、`path`(Socket.IO 的路径)、`auth`(Socket.IO 握手的更多字段,比如令牌)、`timeout`,以及它的连接的选项(`iceTransportPolicy`、`icePortRange`、`iceAdditionalHostAddresses`、`codecs`、`threads`)。它是 `@Transient` 的:每次 `getObject()` 都得到一个新的客户端,`close()` 会离开它的房间并断开连接。
166
+ `http:` 或 `https:` 的 URL 使用 Socket.IO(需要安装 `socket.io-client`),`ws:` 或 `wss:` 的 URL 使用 Node.js 22 和 Bun 自带的 `WebSocket` 走 JSON-RPC,没有时使用 `ws` 包的 `WebSocket`(需要安装 `ws`)。它的选项:`url`、`id`(默认是随机的 UUID)、`path`(Socket.IO 的路径)、`auth`(Socket.IO 握手的更多字段,比如令牌)、`timeout`,以及它的连接的选项(`iceTransportPolicy`、`icePortRange`、`iceAdditionalHostAddresses`、`codecs`、`threads`)。它是 `@Transient` 的:每次 `getObject()` 都得到一个新的客户端,`close()` 会离开它的房间并断开连接。
167
167
 
168
168
  ### 调用应用的动作
169
169
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lakutata",
3
- "version": "3.1.3",
3
+ "version": "3.1.5",
4
4
  "description": "An IoC application framework for Node.js and TypeScript: one programming model for web services, CLI tools, desktop (Electron) and embedded applications.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -194,6 +194,16 @@
194
194
  "default": "./dist/cjs/exports/ElectronAdapter.js"
195
195
  }
196
196
  },
197
+ "./adapter/nwjs": {
198
+ "import": {
199
+ "types": "./dist/types/exports/NwjsAdapter.d.ts",
200
+ "default": "./dist/esm/exports/NwjsAdapter.js"
201
+ },
202
+ "require": {
203
+ "types": "./dist/cjs/exports/NwjsAdapter.d.ts",
204
+ "default": "./dist/cjs/exports/NwjsAdapter.js"
205
+ }
206
+ },
197
207
  "./com/logger": {
198
208
  "import": {
199
209
  "types": "./dist/types/exports/LoggerComponent.d.ts",
@@ -314,7 +324,7 @@
314
324
  }
315
325
  },
316
326
  "engines": {
317
- "node": "^22.13.0 || >=24.11.0"
327
+ "node": ">=18.10.0"
318
328
  },
319
329
  "author": "alex.ma",
320
330
  "license": "MIT",
@@ -354,19 +364,19 @@
354
364
  "mcp"
355
365
  ],
356
366
  "dependencies": {
357
- "@lakutata/cache": "3.1.1",
358
- "@lakutata/containerd": "3.1.1",
359
- "@lakutata/core": "3.1.1",
360
- "@lakutata/docker": "3.1.1",
361
- "@lakutata/entrypoint": "3.1.1",
362
- "@lakutata/i18n": "3.1.1",
363
- "@lakutata/logger": "3.1.1",
364
- "@lakutata/monitor": "3.1.1",
365
- "@lakutata/mutex": "3.1.3",
366
- "@lakutata/orm": "3.1.1",
367
- "@lakutata/queue": "3.1.1",
368
- "@lakutata/security": "3.1.1",
369
- "@lakutata/utils": "3.1.1"
367
+ "@lakutata/cache": "3.1.5",
368
+ "@lakutata/containerd": "3.1.5",
369
+ "@lakutata/core": "3.1.5",
370
+ "@lakutata/docker": "3.1.5",
371
+ "@lakutata/entrypoint": "3.1.5",
372
+ "@lakutata/i18n": "3.1.5",
373
+ "@lakutata/logger": "3.1.5",
374
+ "@lakutata/monitor": "3.1.5",
375
+ "@lakutata/mutex": "3.1.5",
376
+ "@lakutata/orm": "3.1.5",
377
+ "@lakutata/queue": "3.1.5",
378
+ "@lakutata/security": "3.1.5",
379
+ "@lakutata/utils": "3.1.5"
370
380
  },
371
381
  "files": [
372
382
  "dist",
@@ -379,6 +389,7 @@
379
389
  ],
380
390
  "peerDependencies": {
381
391
  "@inquirer/prompts": "^7.0.0 || ^8.0.0",
392
+ "@lakutata/netif": "~3.1.5",
382
393
  "commander": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0",
383
394
  "express": "^4.21.0 || ^5.0.0",
384
395
  "fastify": "^5.0.0",
@@ -403,6 +414,9 @@
403
414
  },
404
415
  "ws": {
405
416
  "optional": true
417
+ },
418
+ "@lakutata/netif": {
419
+ "optional": true
406
420
  }
407
421
  }
408
422
  }