lakutata 3.1.4 → 3.1.6

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 (67) hide show
  1. package/README.md +11 -11
  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/cjs/exports/ReactNativeAdapter.d.ts +1 -0
  6. package/dist/cjs/exports/ReactNativeAdapter.js +20 -0
  7. package/dist/esm/exports/NwjsAdapter.js +1 -0
  8. package/dist/esm/exports/ReactNativeAdapter.js +1 -0
  9. package/dist/types/exports/NwjsAdapter.d.ts +1 -0
  10. package/dist/types/exports/ReactNativeAdapter.d.ts +1 -0
  11. package/doc/en/Application.md +6 -1
  12. package/doc/en/Barcode.md +386 -0
  13. package/doc/en/Cache.md +2 -1
  14. package/doc/en/Command.md +20 -4
  15. package/doc/en/ContainerdIntegration.md +1 -1
  16. package/doc/en/Database.md +4 -0
  17. package/doc/en/DatabaseDrivers.md +75 -4
  18. package/doc/en/Entrypoint.md +4 -2
  19. package/doc/en/Exceptions.md +1 -1
  20. package/doc/en/FFI.md +1 -1
  21. package/doc/en/GRPC.md +1 -1
  22. package/doc/en/GettingStarted.md +16 -2
  23. package/doc/en/HTTP.md +1 -0
  24. package/doc/en/I18n.md +1 -1
  25. package/doc/en/Installers.md +34 -6
  26. package/doc/en/Math.md +1 -1
  27. package/doc/en/Migrations.md +2 -2
  28. package/doc/en/NativeAddons.md +12 -2
  29. package/doc/en/Overview.md +14 -11
  30. package/doc/en/Packaging.md +8 -1
  31. package/doc/en/ProjectStructure.md +18 -3
  32. package/doc/en/ServiceCalls.md +85 -5
  33. package/doc/en/UpgradingFrom1.md +2 -2
  34. package/doc/en/UpgradingFrom2.md +4 -4
  35. package/doc/en/WebRTC.md +4 -4
  36. package/doc/en/examples/MobileApp.md +730 -0
  37. package/doc/en/examples/README.md +2 -0
  38. package/doc/en/examples/TrayApp.md +1061 -0
  39. package/doc/zh/Application.md +3 -1
  40. package/doc/zh/Barcode.md +313 -0
  41. package/doc/zh/Cache.md +1 -1
  42. package/doc/zh/Command.md +12 -3
  43. package/doc/zh/ContainerdIntegration.md +1 -1
  44. package/doc/zh/Database.md +2 -0
  45. package/doc/zh/DatabaseDrivers.md +52 -2
  46. package/doc/zh/Entrypoint.md +3 -1
  47. package/doc/zh/Exceptions.md +1 -1
  48. package/doc/zh/FFI.md +1 -1
  49. package/doc/zh/GRPC.md +1 -1
  50. package/doc/zh/GettingStarted.md +12 -2
  51. package/doc/zh/HTTP.md +1 -0
  52. package/doc/zh/I18n.md +1 -1
  53. package/doc/zh/Installers.md +16 -5
  54. package/doc/zh/Math.md +1 -1
  55. package/doc/zh/Migrations.md +1 -1
  56. package/doc/zh/NativeAddons.md +4 -2
  57. package/doc/zh/Overview.md +5 -4
  58. package/doc/zh/Packaging.md +6 -1
  59. package/doc/zh/ProjectStructure.md +5 -2
  60. package/doc/zh/ServiceCalls.md +65 -2
  61. package/doc/zh/UpgradingFrom1.md +2 -2
  62. package/doc/zh/UpgradingFrom2.md +3 -3
  63. package/doc/zh/WebRTC.md +1 -1
  64. package/doc/zh/examples/MobileApp.md +671 -0
  65. package/doc/zh/examples/README.md +2 -0
  66. package/doc/zh/examples/TrayApp.md +980 -0
  67. package/package.json +36 -16
@@ -16,7 +16,7 @@ npx lakutata package installer --target pkg # 只制作其中几种
16
16
  ✔ dist/installer/greet-1.0.0-arm64.pkg (pkg, 36.0 MB)
17
17
  ```
18
18
 
19
- 安装包里是目录布局的可执行文件(`lakutata package exe --layout directory`):程序本身、它的原生插件(`native/`)和资源(`resources/`)。程序用 Node.js 制作;`lakutata.installer.runtime` 为 `"bun"` 时用 Bun 制作。
19
+ 安装包里是目录布局的可执行文件(`lakutata package exe --layout directory`):程序本身、它的原生插件(`native/`)和资源(`resources/`)。程序用 Node.js 制作;`lakutata.installer.runtime` 为 `"bun"` 时用 Bun 制作。在低于 20.12 的 Node.js 上,和可执行文件一样,用 `--node-program` 指定一个更新的 Node.js。
20
20
 
21
21
  | 平台 | 安装包(默认) | 文件名 | 制作工具 |
22
22
  |---|---|---|---|
@@ -56,18 +56,18 @@ npx lakutata package installer --target pkg # 只制作其中几种
56
56
  | `description`、`homepage`、`license` | 取自 `package.json` | 软件包的信息 |
57
57
  | `maintainer` | `package.json` 的 `author` | 维护者(`Name <email>`);没有时工具会发出警告 |
58
58
  | `vendor` | 无 | 供应商;在 Windows 上,发布者是供应商,没有时是维护者 |
59
- | `runtime` | `node` | 可执行文件用 `node` 或 `bun`;Electron 应用用 `electron`(第 8 节) |
59
+ | `runtime` | `node` | 可执行文件用 `node` 或 `bun`;Electron 应用用 `electron`(第 8 节),NW.js 应用用 `nwjs`(第 9 节) |
60
60
  | `addToPath` | `true` | 把程序加入 PATH:Linux 上是 `/usr/bin/<program>`,macOS 上是 `/usr/local/bin/<program>`,Windows 上是机器的 PATH |
61
61
  | `service` | 无 | `true`,或服务的选项(第 4 节) |
62
62
  | `linux.targets` | `deb`、`rpm`、`tar.gz`;Alpine 上是 `apk`、`tar.gz` | Linux 的安装包(`deb`、`rpm`、`archlinux`、`tar.gz`;Alpine 上是 `apk`、`tar.gz`) |
63
63
  | `linux.depends`、`linux.recommends` | 无 | 依赖和推荐的系统软件包(总会加上 C++ 运行库) |
64
64
  | `linux.section`、`linux.priority` | 无 | 分类和优先级(deb) |
65
- | `mac.targets` | `pkg`、`tar.gz` | macOS 的安装包 |
65
+ | `mac.targets` | `pkg`、`tar.gz` | macOS 的安装包(NW.js 应用还可以用 `dmg`) |
66
66
  | `win.targets` | `nsis`、`msi`、`zip` | Windows 的安装包 |
67
67
  | `linux.scripts`、`mac.scripts`、`win.scripts` 等 | 无 | 安装包执行的脚本(第 5 节) |
68
68
  | `electron` | 无 | electron-builder 的配置(第 8 节) |
69
69
 
70
- `lakutata.icon`(与 `lakutata.installer` 并列)是一张 256 像素或更大的正方形 PNG 图片:它是 Windows 可执行文件和安装程序的图标,也是 Electron 应用的图标。
70
+ `lakutata.icon`(与 `lakutata.installer` 并列)是一张 256 像素或更大的正方形 PNG 图片:它是 Windows 可执行文件和安装程序的图标,也是 Electron 和 NW.js 应用的图标。
71
71
 
72
72
  配置在构建任何东西之前就会检查,不制作的那些安装包的选项也会检查,出错时会指出是哪个字段:
73
73
 
@@ -169,7 +169,7 @@ fi
169
169
 
170
170
  ## 6. 签名
171
171
 
172
- - **macOS**:`pkg` 安装包没有签名,它安装的程序也只有临时签名(ad hoc)。在组织以外的机器上分发所需要的 Developer ID 签名和公证,目前还不会制作。
172
+ - **macOS**:`pkg` 安装包和 `dmg` 磁盘映像没有签名,它们安装的程序或应用也只有临时签名(ad hoc)。在组织以外的机器上分发所需要的 Developer ID 签名和公证,目前还不会制作。
173
173
  - **Windows**:NSIS 和 MSI 安装包没有签名,分发前请用 signtool 签名。用 Node.js 制作的程序保留着 `node.exe` 的签名,而这个签名已经不再匹配,工具会对此发出警告。
174
174
  - **Linux**:软件包没有签名。
175
175
 
@@ -230,3 +230,14 @@ jobs:
230
230
  - **脚本**在 `deb` 和 `rpm` 软件包(全部四种)、`pkg`(`preinstall`、`postinstall`)和 `nsis` 安装程序(`preinstall`、`postinstall`、`preremove`)中执行;其他目标都不执行。
231
231
  - **原生插件**按安装时的样子复制:electron-builder 不会重新构建它们。基于 Node-API 的插件可以直接运行;其他插件请在打包前安装它们为 Electron 构建的版本(`electron-rebuild`)(见 [NativeAddons](NativeAddons.md) 第 4 节)。
232
232
  - 在 Linux arm64 上,deb 和 rpm 软件包需要系统中的 fpm(`gem install fpm`、`USE_SYSTEM_FPM=true`),Electron 模板的 `Dockerfile.installer` 就是这样安装它的。
233
+
234
+ ## 9. NW.js 应用
235
+
236
+ 使用 `"runtime": "nwjs"` 时,安装包里是由构建器制作的 NW.js 应用:应用所用 NW.js 版本(它的 `lakutata.nwjs`,由 `lakutata nwjs use` 设置)的 normal 版本,只下载一次到 lakutata 的缓存目录,并按其发布版本的校验和核对;应用放在其中,包括为这个 NW.js 的 Node.js 打包的代码、原生插件、资源(`lakutata.resources`,通过 `resourcesPath()` 读取)和 NW.js 的清单。`nwjs-react` 和 `nwjs-vue` 模板就是这样配置的。
237
+
238
+ - **运行时**以应用的名字命名:Windows 上是 `<name>.exe`,图标和版本信息都是应用的;Linux 上是 `<name>`;macOS 上是 `<productName>.app`,它的 `Info.plist` 给出应用的名称、标识(`appId`)、版本和图标,并重新做临时签名。
239
+ - **安装包**是桌面应用的安装包:默认 macOS 上是 `dmg`,Windows 上是 `nsis`,Linux 上是 `deb`(macOS 上还可以用 `pkg` 和 `tar.gz`,Windows 上可以用 `msi` 和 `zip`,Linux 上可以用 `rpm` 和 `tar.gz`)。Windows 的安装程序把应用加到开始菜单和桌面;`dmg` 磁盘映像里是应用和一个指向 /Applications 的链接,用户把应用拖过去即可,`pkg` 安装包则直接把它装到那里(Intel 版的应用也可以装到 Apple 芯片的 Mac 上);Linux 的软件包会添加应用的桌面入口和图标,并依赖 Chromium 所需的库。
240
+ - **架构**默认是本机的架构,也可以用 `--arch` 指定(`x64`、`ia32`、`arm64`):`ia32` 制作 32 位 Windows 的安装包,应用安装在 32 位程序的目录中。没有 Apple 芯片 Mac 构建的 NW.js 版本(如 0.72),会打包成 Intel 版。
241
+ - **NW.js 应用不是服务**,也不会加入 PATH(`addToPath` 默认为 `false`)。
242
+ - **脚本**和其他应用一样执行,只是 macOS 上的应用是直接删除的:那里不会执行卸载脚本。
243
+ - **原生插件**会被处理成能在 NW.js 中加载:为它的架构重新构建,使用 V8 API 的插件用它的头文件重新构建;在 Windows 上,插件加载的是 NW.js 的 Node.js(见 [NativeAddons](NativeAddons.md) 第 4 节)。
package/doc/zh/Math.md CHANGED
@@ -279,7 +279,7 @@ $ curl -X POST localhost:8080/solve -H 'Content-Type: application/json' -d '{"eq
279
279
  - 公式的长度限制不了它的计算量:`(x + 1)^100000` 很短。能限制计算量的是时限。
280
280
  - 这个包的值通过 `toJSON()` 转成 JSON:表达式、方程、十进制数和带单位的值转成它们的文本,解转成文本数组,矩阵转成它的各行。向量是 `Float64Array`,`JSON.stringify()` 会把它写成对象(`{"0": 1, "1": 1}`):请发送 `Array.from(vector)`。
281
281
  - `lakutata package` 打出的单文件包、可执行文件和安装包都带着编译好的原生插件(`build/Release/math.node`)。请在每个目标平台上安装这个包之后再打包;原生插件的规则同样适用(见 [Packaging](Packaging.md) 和 [NativeAddons](NativeAddons.md))。
282
- - 这个包可以在 Node.js `^22.13.0 || >=24.11.0`、Bun 和 Electron 的主进程中运行,后两者直接加载为 Node.js 编译的插件;不能在 Electron 的渲染进程和浏览器中运行。在 Bun 里,worker 线程里的插件运算还没结束就终止这个线程的话,整个进程会因为 Node-API 的致命错误退出(这是 Bun 1.4 的问题)。
282
+ - 这个包可以在 Node.js `>=18.10.0`、Bun 和 Electron 的主进程中运行,后两者直接加载为 Node.js 编译的插件;不能在 Electron 的渲染进程和浏览器中运行。在 Bun 里,worker 线程里的插件运算还没结束就终止这个线程的话,整个进程会因为 Node-API 的致命错误退出(这是 Bun 1.4 的问题)。
283
283
 
284
284
  ## 13. 验证方式,以及不支持的情况
285
285
 
@@ -128,7 +128,7 @@ export class Migrations1792161460658 implements MigrationInterface {
128
128
  - 数据库必须能连上,并且已经执行了已有的迁移:差异是根据它当前的表计算的。
129
129
  - 文件是 `<目录>/<时间戳>-<数据库名>.ts`,类名取自目录(`src/migrations` 得到 `Migrations<时间戳>`),是 TypeScript 文件。随应用一起编译:`migrations` 选项列出的是编译后的文件。
130
130
  - 执行迁移之前先读一遍:重命名的属性会变成删除一列再新增一列,旧列的数据会丢失。必要时修改其中的 SQL。
131
- - 没有任何变化时,命令什么都不写,并以退出码 1 退出(`No changes in database schema were found`)。
131
+ - 没有任何变化时,命令什么都不写,只提示一句(`No changes in database schema were found`),退出码为 0。
132
132
  - 有多个数据库时,每个表结构有差异的数据库各自生成一个文件。给每个数据库的 `migrations` 选项只列出它自己的文件(`'build/migrations/*-db.js'`),否则各个数据库会执行彼此的迁移。
133
133
 
134
134
  ## 5. 启动时执行迁移
@@ -61,14 +61,13 @@ pub fn greet(name: String) -> String {
61
61
  ```typescript
62
62
  import {createRequire} from 'node:module'
63
63
  import path from 'node:path'
64
- import {fileURLToPath} from 'node:url'
65
64
 
66
65
  type Binding = {
67
66
  greet(name: string): string
68
67
  }
69
68
 
70
69
  /** The addon built into build/Release, loaded from the directory of the package */
71
- const binding: Binding = createRequire(import.meta.url)('node-gyp-build')(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'))
70
+ const binding: Binding = createRequire(__filename)('node-gyp-build')(path.resolve(__dirname, '..'))
72
71
 
73
72
  export function greet(name: string): string {
74
73
  return binding.greet(name)
@@ -87,6 +86,7 @@ export function greet(name: string): string {
87
86
  - **错误**:node-addon-api 的 C++ 异常会以同类错误抛到 JavaScript 中(`Napi::TypeError` 变成 `TypeError`)。在 Rust 中,类型不对的参数由 napi-rs 拒绝,抛出一个 `Error`,它的 `code` 说明原因(`StringExpected`)。
88
87
  - **macOS**:C++ 插件在一个文件中同时为 Intel 和 Apple 芯片的 Mac 构建;Rust 插件只为本机的架构构建(用 `rustup target add` 添加其他架构)。
89
88
  - **Node-API**:插件针对 Node-API 第 8 版构建,所有受支持的 Node.js 和 Electron 版本都提供这一版,所以不需要为它们分别重新构建。
89
+ - **CommonJS**:这个包是 CommonJS 包(`"type": "commonjs"`)。`lakutata package` 生成的文件是 CommonJS 格式,用 `require` 加载它,而 `require` 要到 Node.js 20.19、22.12 或更高版本才能加载 ES 模块。
90
90
  - 和 `lakutata add` 创建的所有包一样,这个包是 `"private": true`:原生插件通常随应用一起发布,而不单独发布。
91
91
 
92
92
  ## 2. 链接库
@@ -177,5 +177,7 @@ lakutata native link libc --crate # 一个 crate(Rust:carg
177
177
  - **单文件包和可执行文件**:`lakutata package bundle` 把插件包排除在单文件包之外,连同它们的文件(JavaScript、插件本身、它的动态库,但不包括源码和构建时产生的文件)写入 `dist/<name>.native/`,这个目录随单文件包一起发布;可执行文件则把它们嵌入其中,或者在目录布局中放在自己旁边(见 [Packaging](Packaging.md))。插件是为打包所在机器的平台构建的,所以每个平台的应用要在那个平台上打包(用 CI 的矩阵,Linux 也可以用对应架构的容器)。
178
178
  - **基于框架的插件包**(它依赖 `lakutata` 或 `@lakutata/core`,比如为了提供一个组件)则和应用一起打进单文件包,这样框架只有一份,只有插件和它旁边的文件放进这个目录。插件要用下面几种方式之一加载,才会被放进去:像 `lakutata add` 生成的插件包那样,把包的目录交给 `node-gyp-build`;直接 `require` 插件文件;或者用从模块位置构造的路径(`path.join(__dirname, 'prebuilds', ...)`)。用其他方式加载的插件,`lakutata package` 会发出警告。
179
179
  - **Electron**:插件按安装时的样子复制。`lakutata add` 创建的插件不需要重新构建就能在 Electron 中运行,所有基于 Node-API 的插件都是如此;基于 Node.js V8 API 的插件,打包前要安装它为 Electron 构建的版本(`electron-rebuild`)。
180
+ - **NW.js**:应用在 NW.js 中运行时(`lakutata nwjs run`,以及打包之后),插件会被处理成能在 NW.js 中加载。为运行时以外的架构构建的插件会从源码重新构建,使用 NW.js 内置 Node.js 的头文件;基于 Node.js V8 API 的插件也会重新构建,使用 NW.js 的头文件(需要 NW.js 0.111.3 及以后的版本:更早的版本,例如用于 Windows 7 的 0.72,只能运行基于 Node-API 的插件)。在 Windows 上,插件加载的是 NW.js 的 Node.js(`node.dll`)。项目里的插件保持原样,项目的测试仍然用 Node.js 运行。
181
+ - **手机应用**(React Native):`lakutata mobile stage` 会从源码为 Android、iOS 和鸿蒙编译插件,使用应用内嵌的 libnode(见 `@lakutata/cli` README 中的 `lakutata mobile` 命令)。
180
182
  - **Bun**:Bun 能加载 Node-API 的插件;Bun 可执行文件会对基于 Node.js V8 API 的插件发出警告。
181
183
  - **Alpine(musl)**:Node.js 的可执行文件在那里不能加载原生插件,请用 Bun 制作。
@@ -1,6 +1,6 @@
1
1
  # Lakutata 概述
2
2
 
3
- **Lakutata** 是一个基于 IoC 的 Node.js / TypeScript 应用框架,让 Web 服务、命令行工具、桌面(Electron)应用和嵌入式设备共用同一套编程模型。应用是一棵由容器创建的对象树(组件、提供者、控制器):DTO 校验对象的选项和输入,各种传输方式把控制器暴露出去——HTTP(Express、Fastify)、CLI(Commander)、服务调用(HTTP、Socket.IO、JSON-RPC 2.0、gRPC、NATS、Electron IPC)、定时任务和消息队列。同一个控制器方法可以同时通过所有这些方式调用。
3
+ **Lakutata** 是一个基于 IoC 的 Node.js / TypeScript 应用框架,让 Web 服务、命令行工具、桌面(Electron、NW.js)应用和嵌入式设备共用同一套编程模型。应用是一棵由容器创建的对象树(组件、提供者、控制器):DTO 校验对象的选项和输入,各种传输方式把控制器暴露出去——HTTP(Express、Fastify)、CLI(Commander)、服务调用(HTTP、Socket.IO、JSON-RPC 2.0、gRPC、NATS、Electron IPC、NW.js、React Native)、定时任务和消息队列。同一个控制器方法可以同时通过所有这些方式调用。
4
4
 
5
5
  `lakutata` 包通过各个子路径重新导出 `@lakutata/*` 系列包(见下表),并为每个应用预置两个组件:`log`(`Logger`,输出与 pino 兼容的 JSON 日志行)和 `entrypoint`(对外提供控制器的 `Entrypoint`)。
6
6
 
@@ -21,7 +21,7 @@
21
21
  | `lakutata/decorator/ctrl` | `@HTTPAction`、`@GET`…`@OPTIONS`、`@CLIAction`、`@ServiceAction`、`@Schedule`、`@Prefix`、`@UseMiddlewares` |
22
22
  | `lakutata/adapter/express`、`lakutata/adapter/fastify` | HTTP 传输 |
23
23
  | `lakutata/adapter/commander` | CLI 传输 |
24
- | `lakutata/adapter/socket.io`、`lakutata/adapter/jsonrpc`、`lakutata/adapter/electron` | 服务传输 |
24
+ | `lakutata/adapter/socket.io`、`lakutata/adapter/jsonrpc`、`lakutata/adapter/electron`、`lakutata/adapter/nwjs`、`lakutata/adapter/react-native` | 服务传输 |
25
25
  | `lakutata/com/logger` | `Logger` 组件,预置为 `log`(输出与 pino 兼容的 JSON 行) |
26
26
  | `lakutata/orm`、`lakutata/decorator/orm` | TypeORM 及其装饰器 |
27
27
  | `lakutata/com/database`、`lakutata/provider/database` | `Database` 组件和提供者 |
@@ -35,7 +35,7 @@
35
35
  | `lakutata/security`、`lakutata/provider/passwordHash` | 哈希、加解密、签名、`PasswordHash` 提供者 |
36
36
  | `lakutata/utils` | 辅助函数 |
37
37
 
38
- 不在本包中的:`@lakutata/cli`(`lakutata` 命令:创建项目、执行迁移、打包应用,见 [Command](Command.md))、`@lakutata/grpc`(gRPC 客户端、服务端和服务传输)、`@lakutata/nats`(NATS 组件和服务传输)、`@lakutata/dbus`(D-Bus)、`@lakutata/mcp`(Model Context Protocol)、`@lakutata/webrtc`(WebRTC:信令、TURN 服务器、媒体服务器、对等连接)、`@lakutata/ffi`(调用动态库中的 C 函数)、`@lakutata/math`(符号计算和数值计算,直接作为函数使用,不需要 lakutata 应用)和 `@lakutata/netif`(服务器和 UDP 套接字按网口监听,不管网口的地址怎么变;本机的网口和它们的变化)。
38
+ 不在本包中的:`@lakutata/cli`(`lakutata` 命令:创建项目、执行迁移、打包应用,见 [Command](Command.md))、`@lakutata/grpc`(gRPC 客户端、服务端和服务传输)、`@lakutata/nats`(NATS 组件和服务传输)、`@lakutata/dbus`(D-Bus)、`@lakutata/mcp`(Model Context Protocol)、`@lakutata/webrtc`(WebRTC:信令、TURN 服务器、媒体服务器、对等连接)、`@lakutata/ffi`(调用动态库中的 C 函数)、`@lakutata/math`(符号计算和数值计算,直接作为函数使用,不需要 lakutata 应用)、`@lakutata/barcode`(条形码和二维码的生成和识别,由原生插件完成)和 `@lakutata/netif`(服务器和 UDP 套接字按网口监听,不管网口的地址怎么变;本机的网口和它们的变化)。
39
39
 
40
40
  ## 指南
41
41
 
@@ -59,7 +59,7 @@
59
59
  | [Middlewares](Middlewares.md) | 动作的中间件与访问控制 |
60
60
  | [HTTP](HTTP.md) | HTTP 适配器:路由、输入、响应、错误,以及 Express 和 Fastify 的功能 |
61
61
  | [CLI](CLI.md) | 命令行工具:命令、选项、提示输入、输出与退出码 |
62
- | [ServiceCalls](ServiceCalls.md) | 通过 HTTP、Socket.IO、JSON-RPC 2.0 和 Electron 调用服务动作:模式、响应、客户端及其事件 |
62
+ | [ServiceCalls](ServiceCalls.md) | 通过 HTTP、Socket.IO、JSON-RPC 2.0、Electron 和 NW.js 调用服务动作:模式、响应、客户端及其事件 |
63
63
  | [GRPC](GRPC.md) | 通过 gRPC 提供服务动作(`@lakutata/grpc`),以及从其他应用和其他语言调用它们 |
64
64
  | [NATS](NATS.md) | NATS 组件、通过 NATS 提供服务动作(`@lakutata/nats`)、服务代理及其事件 |
65
65
  | [Schedules](Schedules.md) | 定时任务:间隔、cron 表达式、只在一个实例中执行的任务 |
@@ -80,6 +80,7 @@
80
80
  | [MCP](MCP.md) | 模型上下文协议(`@lakutata/mcp`)的工具、资源和提示词,基于 stdio 和 HTTP |
81
81
  | [WebRTC](WebRTC.md) | 点对点连接和媒体服务器(`@lakutata/webrtc`):信令、TURN 服务器、浏览器和应用、通过数据通道调用应用的动作、WHIP 和 WHEP |
82
82
  | [FFI](FFI.md) | 调用动态库中的 C 函数(`@lakutata/ffi`):从头文件复制的声明、结构体、回调、内存、打包 |
83
+ | [Barcode](Barcode.md) | 条形码和二维码(`@lakutata/barcode`):生成 PNG、SVG 或像素,打印尺寸,EAN、UPC 和 GS1,从图片识别 |
83
84
  | [Math](Math.md) | 数学计算(`@lakutata/math`):公式和精确结果、方程、任意精度的十进制数、矩阵、统计、数值解法、单位 |
84
85
  | [Netif](Netif.md) | 网口(`@lakutata/netif`):服务器按网口监听,不管网口的地址怎么变;挡住别的网口的连接;网口和它们的变化;单个网口上的 UDP |
85
86
  | [Command](Command.md) | `lakutata` 命令:创建项目、迁移、翻译、环境变量 |
@@ -8,6 +8,8 @@
8
8
 
9
9
  `lakutata package bundle`、`package exe` 和 `package installer` 借助 `@lakutata/builder` 生成它们,项目需要安装它(`npm install --save-dev @lakutata/builder`;需要打包的模板已经带上了)。
10
10
 
11
+ `mobile-react` 模板的手机应用用另一种方式发布:`lakutata mobile stage` 把应用打包成面向手机应用内嵌的 Node.js 的单文件包,应用商店需要的发布版本由它的原生工程构建(见模板为项目生成的 README 中的 The packaging 一节)。
12
+
11
13
  ## 1. 打包的内容
12
14
 
13
15
  - **编译后的应用**:打包的是 `tsc` 的输出(`tsc` 会生成装饰器的元数据),而不是 TypeScript 源码,所以要先构建。入口依次是 `--entry`、`package.json` 的 `main`、`build/App.js`。
@@ -38,6 +40,7 @@ node dist/greet.cjs
38
40
  | `--outfile <file>` | 单文件包的路径,默认 `dist/<name>.cjs` |
39
41
  | `--no-minify` | 不压缩(无论是否压缩,类名都会保留) |
40
42
  | `--external <modules...>` | 不打进包里的模块,运行时从单文件包旁边的 `node_modules` 加载 |
43
+ | `--node <version>` | 运行单文件包的 Node.js 版本(NW.js 0.70 是 `18.10`):单文件包用这个版本支持的语法生成;默认是运行工具的那个版本 |
41
44
 
42
45
  ## 3. 原生插件
43
46
 
@@ -99,11 +102,12 @@ npx lakutata package exe --runtime bun # 使用 Bun
99
102
 
100
103
  | 选项 | 含义 |
101
104
  |---|---|
102
- | `--runtime node`(默认) | 把单文件包嵌入运行工具的那个 Node.js 的副本中(Node.js 的单可执行文件应用):在 macOS arm64 上约 110 MB |
105
+ | `--runtime node`(默认) | 把单文件包嵌入运行工具的 Node.js(或 `--node-program` 指定的 Node.js)的副本中(Node.js 的单可执行文件应用):在 macOS arm64 上约 110 MB |
103
106
  | `--runtime bun` | 由 Bun 编译单文件包(优先用项目中的 `bun` 包,否则用系统的 `bun`):在 macOS arm64 上约 70 MB,启动更快;运行时也就是 Bun |
104
107
  | `--layout file`(默认) | 单个文件:原生插件、按路径读取的文件和资源都嵌入其中,启动时解压 |
105
108
  | `--layout directory` | `dist/<name>/` 中包含可执行文件、`native/` 和 `resources/`,直接从那里加载,不解压(安装包用的就是这种布局) |
106
109
  | `--outdir <directory>` | 可执行文件的输出目录,默认 `dist` |
110
+ | `--node-program <path>` | 嵌入另一个 Node.js,而不是运行工具的那个:需要 Node.js 20.12、21.7 或更高版本(比如 `$(nvm which 22)`),这样较旧的 Node.js 也能运行这个命令;单文件包用它支持的语法生成 |
107
111
  | `--entry`、`--no-minify`、`--external` | 与单文件包相同 |
108
112
 
109
113
  - **解压**:单文件布局的可执行文件在第一次运行时,把嵌入的内容解压到缓存目录中一个以内容命名的子目录,所以每个版本只解压一次:macOS 上是 `~/Library/Caches/<name>`,Windows 上是 `%LOCALAPPDATA%\<name>\Cache`,Linux 上是 `$XDG_CACHE_HOME/<name>` 或 `~/.cache/<name>`,也可以用 `LAKUTATA_NATIVE_DIR` 指定目录。缓存目录不可写时,改用临时目录中的 `<name>`(指定了 `LAKUTATA_NATIVE_DIR` 时不会这样)。
@@ -134,6 +138,7 @@ npx lakutata package exe --runtime bun # 使用 Bun
134
138
  | 可执行文件,单文件布局 | `true` | 资源解压到的目录 |
135
139
  | 可执行文件,目录布局,或已安装的应用 | `true` | 可执行文件旁边的 `resources` |
136
140
  | 打包后的 Electron 应用 | `true` | Electron 资源目录中的 `main.resources` |
141
+ | NW.js 应用(由 `lakutata nwjs run` 运行,或已打包) | `true` | 应用目录中的 `resources` |
137
142
 
138
143
  即使应用没有声明资源,`resourcesPath()` 也会返回路径,所以这个目录可能不存在。应用自己决定从哪里读取文件:
139
144
 
@@ -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,8 @@ 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 -- <版本>` 写入),还有制作安装包的脚本。
83
+ - `mobile-react`:`packages/app` 中是应用,它运行在手机应用内嵌的 Node.js 里,为 React Native 界面提供服务动作;`packages/ui` 中是界面(`src/App.tsx`),三个系统共用;`packages/ipc` 连接界面和 Node.js,里面是内嵌 Node.js 的原生模块(`native`)和界面导入的 `ipc`(Electron 和 NW.js 自带这样的通道,React Native 没有,所以模板把它做成一个包);根目录就是 React Native 应用本身:入口 `index.js`(登记 `packages/ui` 的组件)、`app.json`、Metro 和 Babel 的配置,以及原生工程(`android`、`ios`、`harmony`),应用的 ID(`lakutata create --bundle-id`)写在这些原生工程里;还有下载 libnode、在模拟器或设备上运行应用、部署 Node.js 一侧的脚本(`npm run node:android`、`npm run android`、`npm run deploy:android`,`ios` 和 `harmony` 也一样)。
82
84
 
83
85
  打包产物写到工作区根目录的 `dist/` 中;`.gitignore` 忽略 `build`、`dist` 以及界面的构建输出。
84
86
 
@@ -90,8 +92,9 @@ shop/
90
92
  |---|---|---|
91
93
  | `i18n` | 全部 | `src/locales` 中各语言的消息(`en`、`zh-CN`)、`i18n` 组件、语言中间件,以及 `i18n:check` 和 `i18n:types` 脚本([I18n](I18n.md)) |
92
94
  | `installer` | `cli`、`service`、`web-*` | 应用的 `lakutata.installer`、制作安装包的脚本(在本机制作,以及在 Docker 中制作 Linux 的安装包),服务类模板还会注册系统服务([Installers](Installers.md)) |
95
+ | `win7` | `nwjs-*` | 用 NW.js 0.72.0 代替模板发布时的版本:它是最后一个能在 Windows 7、8 和 8.1 上运行的版本;还有制作 32 位 Windows 安装包的 `package:ia32` 脚本 |
93
96
 
94
- Electron 模板不需要这个功能也能制作安装包。
97
+ Electron 和 NW.js 模板不需要 `installer` 功能也能制作安装包。
95
98
 
96
99
  ## 4. 自己的模板
97
100
 
@@ -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 的窗口和 React Native 手机应用的界面,以及在各自指南中介绍的 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 和 React Native 以封套响应;JSON-RPC 使用它自己的响应格式(第 4 节)。
132
132
 
133
133
  ```text
134
134
  {"success": true, "error": null, "payload": <结果>}
@@ -165,6 +165,8 @@ 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) | 窗口的宿主页面对桥接对象的一次调用 | 调用宿主页面交给它的函数 |
169
+ | `reactNativeServiceEntrypoint()` | `lakutata/adapter/react-native` | | 宿主发来的一行 JSON | 一行行 JSON |
168
170
  | `grpcServiceEntrypoint()` | `@lakutata/grpc/service` | `@lakutata/grpc` | 见 [GRPC](GRPC.md) | gRPC 流 |
169
171
  | `natsServiceEntrypoint()` | `@lakutata/nats` | `@lakutata/nats` | 见 [NATS](NATS.md) | NATS 消息 |
170
172
  | `webrtcServiceEntrypoint()` | `@lakutata/webrtc` | `@lakutata/webrtc` | 数据通道上的 JSON-RPC 2.0 请求,见 [WebRTC](WebRTC.md) | 通知 |
@@ -420,6 +422,63 @@ contextBridge.exposeInMainWorld('service', {
420
422
  - `lakutata/adapter/electron` 中的 `ipcEventOfContext(this.context)` 返回请求的 IPC 事件,用来找到发出请求的窗口。
421
423
  - `lakutata create` 的 Electron 模板包含同样形式的预加载脚本,并为页面提供了 `window.ipc`,它的 `invoke()` 返回结果,或者抛出错误。
422
424
 
425
+ ### NW.js
426
+
427
+ `nwjsServiceEntrypoint()` 在 NW.js 应用的后台脚本(它的 Node.js 上下文)中,向它的窗口提供动作。NW.js 让窗口里的页面都能使用 Node.js,页面里的 iframe 也挡不住 iframe 中的内容使用它(iframe 的 sandbox 会被忽略):所以用户界面运行在没有 Node.js 的 `<webview>` 中,由窗口里一个受信任的页面(宿主页面)承载。适配器在 NW.js 与窗口共享的全局对象上放一个桥接对象 `global[Symbol.for(key)]`;宿主页面把界面的请求转发给它,并把向界面推送事件的函数交给它。
428
+
429
+ | 选项 | 默认值 | 含义 |
430
+ |---|---|---|
431
+ | `key` | `'lakutata:service'`(`NWJS_SERVICE_BRIDGE`) | 桥接对象在全局对象上的键 |
432
+ | `global` | NW.js 的全局对象 | 放置桥接对象的对象,测试时可以传入替身 |
433
+
434
+ | 桥接对象的方法 | 含义 |
435
+ |---|---|
436
+ | `invoke(message, clientId)` | 调用一个动作:解析为封套,从不拒绝 |
437
+ | `subscribe(clientId, push)` | 把一个页面登记为客户端:推送给它的事件交给 `push(event, args)` |
438
+ | `unsubscribe(clientId)` | 页面重新加载或窗口关闭时移除这个客户端:它进行中的请求会被中止 |
439
+
440
+ 宿主页面只转发自己的 `<webview>` 里加载的页面发来的消息:
441
+
442
+ ```javascript
443
+ const view = document.querySelector('webview')
444
+ const origin = new URL(view.src).origin
445
+ const bridge = global[Symbol.for('lakutata:service')]
446
+ const send = (message) => view.contentWindow.postMessage(message, origin)
447
+ window.addEventListener('message', async (event) => {
448
+ if (event.source !== view.contentWindow || event.origin !== origin) return
449
+ const {type, id, message, clientId} = event.data
450
+ if (type === 'invoke') send({type: 'response', id: id, response: await bridge.invoke(message, clientId)})
451
+ if (type === 'subscribe') bridge.subscribe(clientId, (name, args) => send({type: 'event', name: name, args: args}))
452
+ })
453
+ ```
454
+
455
+ - `<webview>` 的分区在 NW.js 的清单中声明过,它才能加载应用的文件:`"webview": {"partitions": [{"name": "trusted", "accessible_resources": ["<all_urls>"]}]}`(由构建器写入)。
456
+ - `<webview>` 里重新加载的页面是一个新的客户端:宿主页面会取消前一个客户端的订阅。
457
+ - `lakutata create` 的 NW.js 模板包含这样的宿主页面(`packages/app/host`),并为界面提供了和 Electron 模板一样的 `window.ipc`。
458
+
459
+ ### React Native
460
+
461
+ `reactNativeServiceEntrypoint()` 在 React Native 手机应用(Android、iOS、鸿蒙)内嵌的 Node.js 中,向应用的界面提供动作。应用的原生模块启动 Node.js,并把一个 socket 的一端交给它,文件描述符放在环境变量 `LAKUTATA_NODE_FD` 里。适配器在这个 socket 上读取宿主发来的消息,并把响应和事件写回去,每行一个 JSON 对象。值按 JSON 的规则传递:`Date` 到达时是字符串,`Buffer` 变成 `{type, data}`,`undefined` 被丢掉;JSON 无法表示的结果(`bigint`、循环引用)会以错误作答。
462
+
463
+ | 选项 | 默认值 | 含义 |
464
+ |---|---|---|
465
+ | `socket` | `LAKUTATA_NODE_FD` 中的文件描述符上的 socket | 与宿主的连接,测试中可以换成替身 |
466
+ | `pause` | | 应用切到后台时调用,之后才告诉宿主应用已经准备好 |
467
+ | `resume` | | 应用从后台回来时调用 |
468
+
469
+ | 宿主写入 | 适配器的回应 |
470
+ |---|---|
471
+ | `{"type":"invoke","id":1,"clientId":"ui","data":{"cmd":"greet","name":"you"}}` | `{"type":"response","id":1,"response":{"success":true,"error":null,"payload":…}}` |
472
+ | `{"type":"subscribe","clientId":"ui"}` | `{"type":"subscribed","clientId":"ui"}`,之后是推送给这个客户端的事件 |
473
+ | `{"type":"unsubscribe","clientId":"ui"}` | 没有回应;这个客户端正在处理的请求会被中止 |
474
+ | `{"type":"pause"}` | `pause` 选项执行完后回应 `{"type":"paused"}` |
475
+ | `{"type":"resume"}` | 没有回应;执行 `resume` 选项 |
476
+
477
+ - 宿主在应用开始监听之前写入的行会留在 socket 里,不会丢失。界面收到订阅的回应,就知道应用已经就绪。
478
+ - 在 iOS 上,宿主先向系统申请应用挂起前收尾的时间,再发送 `pause`,收到 `paused`(或时间用完)后才让应用挂起。
479
+ - Node.js 结束时(`process.exit()`、未捕获的异常),手机应用照常运行:原生模块报告退出码,并且可以重新启动 Node.js。
480
+ - `lakutata create` 的 `mobile-react` 模板包含这样的原生模块,还为界面提供了和 Electron 模板一样的 `ipc`,两者都在它的 `packages/ipc` 包中。
481
+
423
482
  ## 5. 客户端与事件
424
483
 
425
484
  保持连接的传输方式可以向客户端推送事件。客户端通过它给出的 id 来识别:
@@ -432,6 +491,8 @@ contextBridge.exposeInMainWorld('service', {
432
491
  | 基于 WebSocket 的 JSON-RPC | 连接的 `id` 请求头或 `id` 查询参数,否则为随机 id |
433
492
  | 基于 stdio 的 JSON-RPC | `'stdio'` |
434
493
  | Electron | 页面在订阅或调用时给出的 id |
494
+ | NW.js | 宿主页面为它的页面交给桥接对象的 id |
495
+ | React Native | 界面在订阅或调用时给出的 id |
435
496
  | WebRTC | 调用方在房间里的成员 id |
436
497
 
437
498
  动作从 `this.context.clientId` 读取它的客户端 id。`entrypoint` 组件负责推送事件:
@@ -466,6 +527,8 @@ class TaskController extends Controller {
466
527
  | Socket.IO | `socket.on('progress', (percent, label) => …)` |
467
528
  | JSON-RPC(WebSocket、stdio) | `{"jsonrpc": "2.0", "method": "progress", "params": [50, "half"]}` |
468
529
  | Electron | 通过上面预加载脚本中的 `subscribe` 收到 `('progress', [50, 'half'])` |
530
+ | NW.js | 交给桥接对象 `subscribe` 的函数收到 `('progress', [50, 'half'])` |
531
+ | React Native | 一行 `{"type":"event","clientId":…,"event":"progress","args":[50,"half"]}`,由宿主转交给界面 |
469
532
  | WebRTC | `PeerServiceProxy` 的 `proxy.on('progress', (percent, label) => …)` |
470
533
 
471
534
  ## 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