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.
- package/README.md +10 -10
- package/README.zh-CN.md +5 -5
- package/dist/cjs/exports/NwjsAdapter.d.ts +1 -0
- package/dist/cjs/exports/NwjsAdapter.js +20 -0
- package/dist/esm/exports/NwjsAdapter.js +1 -0
- package/dist/types/exports/NwjsAdapter.d.ts +1 -0
- package/doc/en/Application.md +1 -1
- package/doc/en/Cache.md +2 -1
- package/doc/en/Command.md +10 -4
- package/doc/en/ContainerdIntegration.md +1 -1
- package/doc/en/DatabaseDrivers.md +3 -2
- package/doc/en/Entrypoint.md +3 -2
- package/doc/en/Exceptions.md +1 -1
- package/doc/en/FFI.md +1 -1
- package/doc/en/GRPC.md +9 -3
- package/doc/en/GettingStarted.md +15 -2
- package/doc/en/HTTP.md +35 -0
- package/doc/en/I18n.md +1 -1
- package/doc/en/Installers.md +34 -6
- package/doc/en/MCP.md +4 -2
- package/doc/en/Math.md +1 -1
- package/doc/en/NativeAddons.md +9 -2
- package/doc/en/Netif.md +261 -0
- package/doc/en/Overview.md +10 -8
- package/doc/en/Packaging.md +4 -1
- package/doc/en/ProjectStructure.md +8 -3
- package/doc/en/ServiceCalls.md +69 -6
- package/doc/en/UpgradingFrom1.md +2 -2
- package/doc/en/UpgradingFrom2.md +4 -4
- package/doc/en/WebRTC.md +4 -4
- package/doc/zh/Application.md +1 -1
- package/doc/zh/Cache.md +1 -1
- package/doc/zh/Command.md +7 -3
- package/doc/zh/ContainerdIntegration.md +1 -1
- package/doc/zh/DatabaseDrivers.md +1 -1
- package/doc/zh/Entrypoint.md +2 -1
- package/doc/zh/Exceptions.md +1 -1
- package/doc/zh/FFI.md +1 -1
- package/doc/zh/GRPC.md +6 -3
- package/doc/zh/GettingStarted.md +11 -2
- package/doc/zh/HTTP.md +30 -0
- package/doc/zh/I18n.md +1 -1
- package/doc/zh/Installers.md +16 -5
- package/doc/zh/MCP.md +3 -2
- package/doc/zh/Math.md +1 -1
- package/doc/zh/NativeAddons.md +3 -2
- package/doc/zh/Netif.md +207 -0
- package/doc/zh/Overview.md +5 -4
- package/doc/zh/Packaging.md +4 -1
- package/doc/zh/ProjectStructure.md +4 -2
- package/doc/zh/ServiceCalls.md +55 -5
- package/doc/zh/UpgradingFrom1.md +2 -2
- package/doc/zh/UpgradingFrom2.md +3 -3
- package/doc/zh/WebRTC.md +1 -1
- package/package.json +29 -15
package/doc/en/Netif.md
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Lakutata Netif
|
|
2
|
+
|
|
3
|
+
The package `@lakutata/netif` lets a server of Node.js listen on a network interface instead of an address. A device
|
|
4
|
+
with several interfaces often serves its management pages on one of them only: the address of that port comes from DHCP
|
|
5
|
+
and changes, or is not there yet when the device starts, and listening on `0.0.0.0` would expose the pages on every
|
|
6
|
+
interface. With the package the server listens on "eth1", whatever its addresses, and keeps listening through their
|
|
7
|
+
changes and through the interface unplugged and plugged back. It is not part of `lakutata`, and it needs no
|
|
8
|
+
application. What it is like:
|
|
9
|
+
|
|
10
|
+
- **Any server**: `http`, `https`, `http2`, `tls`, Express and Fastify take the connections of the interface as their
|
|
11
|
+
own; UDP sockets too, for the services that work on one interface (DHCP, mDNS, SSDP).
|
|
12
|
+
- **The entrypoints of lakutata** take an `interface` option, which serves the controllers of an application on one
|
|
13
|
+
interface (section 5).
|
|
14
|
+
- **The system refuses the other interfaces where it can**: on Linux the sockets are bound to the interface, and a
|
|
15
|
+
connection that reaches its address through another interface is refused. On Windows and macOS the addresses of the
|
|
16
|
+
interface are listened on, and `enforced` tells whether the system refuses those connections.
|
|
17
|
+
- **The interfaces and their changes**: every interface of the machine with its state, and events when its addresses,
|
|
18
|
+
its link or its presence change, from the notifications of the system.
|
|
19
|
+
- **A native addon**, compiled when the package is installed, for Linux, macOS and Windows; it also runs in Bun and in
|
|
20
|
+
the main process of Electron.
|
|
21
|
+
|
|
22
|
+
The README of `@lakutata/netif` describes every function and its options; this guide goes through what the package is
|
|
23
|
+
for, with an example of each use.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @lakutata/netif
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The addon is compiled when the package is installed, so the machine needs Python 3 and a C++17 compiler (on Alpine Linux
|
|
30
|
+
the headers of the kernel too: `apk add python3 make g++ linux-headers`). The package manager has to run the install
|
|
31
|
+
script of the package: npm runs it, and may warn that the script is "not yet covered by allowScripts" (`npm
|
|
32
|
+
install-scripts approve @lakutata/netif` records that you allow it); Bun and pnpm run it only for the packages they are
|
|
33
|
+
told to trust (`trustedDependencies` in the package.json for Bun, `pnpm approve-builds` for pnpm). Its types refer to
|
|
34
|
+
the ones of `@lakutata/core`, a peer dependency that the package managers install with it; it is not loaded when the
|
|
35
|
+
package runs.
|
|
36
|
+
|
|
37
|
+
## 1. Listening on an interface
|
|
38
|
+
|
|
39
|
+
`listenOn()` listens on an interface for a server. The interface is given by its name, its hardware address
|
|
40
|
+
(`{mac: '52:54:00:12:34:56'}`, the same whatever the system names it) or its index; on Windows the name is the one of
|
|
41
|
+
the network connection (`'以太网 2'`, `'Ethernet 2'`).
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import {createServer} from 'node:http'
|
|
45
|
+
import express from 'express'
|
|
46
|
+
import {type InterfaceListener, listenOn} from '@lakutata/netif'
|
|
47
|
+
|
|
48
|
+
const app = express()
|
|
49
|
+
app.get('/', (request, response) => response.send(`served on ${request.socket.localAddress}`))
|
|
50
|
+
|
|
51
|
+
const listener: InterfaceListener = await listenOn('eth1', createServer(app), {port: 8080})
|
|
52
|
+
listener.on('error', error => console.error(error))
|
|
53
|
+
console.log(listener.port, listener.addresses)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- The listener accepts the connections and gives them to the server, which handles them as its own. The server itself
|
|
57
|
+
does not listen: its `address()` is `null`, and `server.close()` does not stop the listener; use `listener.port`,
|
|
58
|
+
`listener.addresses` and `listener.close()`.
|
|
59
|
+
- The interface may not exist yet or have no address yet: `listenOn()` returns at once, and the interface is listened
|
|
60
|
+
on when it can be. When it is unplugged and plugged back, or when its addresses change, it is listened on again by
|
|
61
|
+
itself; the connections established are left to the server.
|
|
62
|
+
- The first listening throws its errors (the port taken); the later ones are `error` events, retried a little later.
|
|
63
|
+
Handle the `error` event of the listener, as for a `net.Server`.
|
|
64
|
+
- `family` chooses `'IPv4'`, `'IPv6'` or `'both'` (by default); `port: 0` lets the system choose a port, kept when the
|
|
65
|
+
interface is listened on again.
|
|
66
|
+
|
|
67
|
+
## 2. Which connections are refused
|
|
68
|
+
|
|
69
|
+
A client of another network can send packets for the address of the management port through another interface of the
|
|
70
|
+
device, with a route of its own through it. Whether the system refuses such a connection is what `enforced` tells:
|
|
71
|
+
|
|
72
|
+
| | `enforced` |
|
|
73
|
+
|---|---|
|
|
74
|
+
| Linux | `true`: the sockets are bound to the interface (`SO_BINDTODEVICE`); before Linux 5.7 this needs the capability `CAP_NET_RAW`, without which the addresses are listened on and `enforced` is `false` |
|
|
75
|
+
| Windows | `true` while every other interface receives with the strong host model and does not forward, which is the default |
|
|
76
|
+
| macOS | `false` unless the system is set to refuse those packets (`net.inet.ip.check_interface` set to 2, and `net.inet6.ip6.check_interface` for IPv6) and does not forward |
|
|
77
|
+
|
|
78
|
+
By default the interface is listened on in the best way the system allows, never less than `listen(port, host)` on its
|
|
79
|
+
addresses; `reason` tells why there is no guarantee and how to get it. Where the management port is a boundary of
|
|
80
|
+
security, `strict: true` refuses to listen without the guarantee, and stops the listening while it is lost:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import {createServer} from 'node:http'
|
|
84
|
+
import {type InterfaceListener, NetIfNotEnforcedException, NetworkInterface} from '@lakutata/netif'
|
|
85
|
+
|
|
86
|
+
const management: NetworkInterface = new NetworkInterface({mac: '52:54:00:12:34:56'})
|
|
87
|
+
try {
|
|
88
|
+
const listener: InterfaceListener = await management.listen(createServer(), {port: 443, strict: true})
|
|
89
|
+
listener.on('error', error => console.error(error))
|
|
90
|
+
listener.on('enforced', enforced => console.log(enforced ? 'guaranteed again' : 'stopped: no longer guaranteed'))
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (error instanceof NetIfNotEnforcedException) console.error(error.message)
|
|
93
|
+
throw error
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 3. Following an interface
|
|
98
|
+
|
|
99
|
+
A `NetworkInterface` gives the state of an interface now (`present`, `running`, `addresses`…) and an event at each
|
|
100
|
+
change. The events come from the notifications of the system, not from polling.
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import {type InterfaceAddress, listInterfaces, NetworkInterface} from '@lakutata/netif'
|
|
104
|
+
|
|
105
|
+
console.log(listInterfaces().map(entry => `${entry.name}: ${entry.running ? 'connected' : 'not connected'}`))
|
|
106
|
+
|
|
107
|
+
const wan: NetworkInterface = new NetworkInterface('eth0')
|
|
108
|
+
wan.on('change', ({added, removed}) => {
|
|
109
|
+
for (const address of added) console.log(`new address ${address.cidr}`)
|
|
110
|
+
for (const address of removed) console.log(`address gone ${address.cidr}`)
|
|
111
|
+
})
|
|
112
|
+
wan.on('down', () => console.log('the cable of eth0 is unplugged'))
|
|
113
|
+
wan.on('up', () => console.log('the cable of eth0 is plugged in'))
|
|
114
|
+
const address: InterfaceAddress = await wan.waitForAddress({family: 'IPv4', timeout: 60000})
|
|
115
|
+
console.log(`reachable at ${address.address}`)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- `listInterfaces()` lists the interfaces without an address too, with their state; `os.networkInterfaces()` lists
|
|
119
|
+
only the ones with an address.
|
|
120
|
+
- An IPv6 address counts once it is usable: about a second after it is added, when its duplicate address detection is
|
|
121
|
+
over.
|
|
122
|
+
- `close()` stops following the interface (and closes its listeners).
|
|
123
|
+
|
|
124
|
+
## 4. UDP on an interface
|
|
125
|
+
|
|
126
|
+
`createUDPSocket()` gives a UDP socket of the interface, with the methods and the events of a `dgram` socket. A device
|
|
127
|
+
that answers the discovery broadcasts of its management network only:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import {type InterfaceUDPSocket, NetworkInterface} from '@lakutata/netif'
|
|
131
|
+
|
|
132
|
+
const management: NetworkInterface = new NetworkInterface('eth1')
|
|
133
|
+
const socket: InterfaceUDPSocket = await management.createUDPSocket({port: 30303, family: 'IPv4'})
|
|
134
|
+
socket.setBroadcast(true)
|
|
135
|
+
socket.on('message', (message, remote) => {
|
|
136
|
+
const answer: string = `device at ${management.addresses[0]?.address}`
|
|
137
|
+
if (message.toString() === 'who is there') socket.send(answer, remote.port, remote.address)
|
|
138
|
+
})
|
|
139
|
+
socket.on('error', error => console.error(error))
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
On Linux the socket receives what comes through the interface, its broadcasts and the multicast of the groups joined
|
|
143
|
+
with `addMembership()` included, and nothing of the other interfaces. On Windows and macOS a socket is bound to each
|
|
144
|
+
address of the interface; on macOS such sockets receive no broadcast and no multicast.
|
|
145
|
+
|
|
146
|
+
## 5. In an application
|
|
147
|
+
|
|
148
|
+
The entrypoints of lakutata that listen on a port take an `interface` option: `expressHTTPEntrypoint()`,
|
|
149
|
+
`fastifyHTTPEntrypoint()`, `httpServiceEntrypoint()`, `socketIOServiceEntrypoint()`, `jsonRPCServiceEntrypoint()`,
|
|
150
|
+
`grpcServiceEntrypoint()` of `@lakutata/grpc` and `httpMCPEntrypoint()` of `@lakutata/mcp`. The actions of the
|
|
151
|
+
controllers are then served on that interface, whatever its addresses:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import {Application} from 'lakutata'
|
|
155
|
+
import {buildEntrypoints, Controller} from 'lakutata/com/entrypoint'
|
|
156
|
+
import {GET} from 'lakutata/decorator/ctrl'
|
|
157
|
+
import {expressHTTPEntrypoint} from 'lakutata/adapter/express'
|
|
158
|
+
|
|
159
|
+
class ManagementController extends Controller {
|
|
160
|
+
@GET('/status')
|
|
161
|
+
public async status(): Promise<{ ok: boolean }> {
|
|
162
|
+
return {ok: true}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
Application.run(() => ({
|
|
167
|
+
id: 'device.app',
|
|
168
|
+
name: 'Device',
|
|
169
|
+
components: {
|
|
170
|
+
entrypoint: buildEntrypoints({
|
|
171
|
+
controllers: [ManagementController],
|
|
172
|
+
http: expressHTTPEntrypoint({port: 8080, interface: 'eth1'})
|
|
173
|
+
})
|
|
174
|
+
},
|
|
175
|
+
bootstrap: ['entrypoint']
|
|
176
|
+
}))
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
- `family`, `strict` and `onError` go with `interface`. Without `onError`, the errors of listening again are written to
|
|
180
|
+
the log of the application. The `listening` callback of the adapter gets the listeners. [HTTP](HTTP.md), section 2,
|
|
181
|
+
describes these options.
|
|
182
|
+
- The application starts while the interface does not exist yet, and its actions can be reached once it appears.
|
|
183
|
+
- The entrypoints listen through `IInterfaceListening`, a contract of `@lakutata/core` that this package implements
|
|
184
|
+
and that they load by default. Another implementation is given in their `interfaceListening` option; the
|
|
185
|
+
`InterfaceListener` of the package is an `IInterfaceListener`.
|
|
186
|
+
|
|
187
|
+
A server that is not an entrypoint belongs to a component: it listens in `startup()` and stops in `destroy()`. The
|
|
188
|
+
interface and the port are the options of the component.
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
import {createServer} from 'node:http'
|
|
192
|
+
import express from 'express'
|
|
193
|
+
import {Application, Component, DTO, type ILogger} from 'lakutata'
|
|
194
|
+
import {Configurable, Inject} from 'lakutata/decorator/di'
|
|
195
|
+
import {type InterfaceListener, listenOn} from '@lakutata/netif'
|
|
196
|
+
|
|
197
|
+
class ManagementPages extends Component {
|
|
198
|
+
@Inject('log')
|
|
199
|
+
protected readonly log: ILogger
|
|
200
|
+
|
|
201
|
+
@Configurable(DTO.String().required())
|
|
202
|
+
protected readonly interfaceName: string
|
|
203
|
+
|
|
204
|
+
@Configurable(DTO.Number().port().default(8080))
|
|
205
|
+
protected readonly port: number
|
|
206
|
+
|
|
207
|
+
protected listener: InterfaceListener
|
|
208
|
+
|
|
209
|
+
protected async startup(): Promise<void> {
|
|
210
|
+
const app = express()
|
|
211
|
+
app.get('/', (request, response) => response.send('management'))
|
|
212
|
+
this.listener = await listenOn(this.interfaceName, createServer(app), {port: this.port})
|
|
213
|
+
this.listener.on('error', (error: Error): void => this.log.error(error.message))
|
|
214
|
+
this.listener.on('listening', (): void => this.log.info('listening on %o', this.listener.addresses))
|
|
215
|
+
this.log.info('enforced: %s', this.listener.enforced)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
protected async destroy(): Promise<void> {
|
|
219
|
+
await this.listener?.close()
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
Application.run(() => ({
|
|
224
|
+
id: 'device.app',
|
|
225
|
+
name: 'Device',
|
|
226
|
+
components: {
|
|
227
|
+
management: {class: ManagementPages, interfaceName: 'eth1', port: 8080}
|
|
228
|
+
},
|
|
229
|
+
bootstrap: ['management']
|
|
230
|
+
}))
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## 6. Systems, runtimes and packaging
|
|
234
|
+
|
|
235
|
+
- **Linux** binds the sockets to the interface: the system refuses the other interfaces, an address added is served at
|
|
236
|
+
once, and UDP gets the broadcasts and the multicast of the interface. Before Linux 5.7 give the process `CAP_NET_RAW`
|
|
237
|
+
(`setcap cap_net_raw+ep`, or `AmbientCapabilities=CAP_NET_RAW` in its systemd unit).
|
|
238
|
+
- **Windows** listens on the addresses of the interface; its strong host model refuses the other interfaces unless an
|
|
239
|
+
interface is set to the weak host model (`netsh interface ipv4 set interface "<name>" weakhostreceive=enabled`, which
|
|
240
|
+
some load balancers do) or forwards. `reason` names that interface.
|
|
241
|
+
- **macOS** listens on the addresses of the interface, without the guarantee unless the system is set to it. macOS 27
|
|
242
|
+
hides the hardware addresses from the processes (it gives `02:00:00:00:00:00`): give an interface by its name there.
|
|
243
|
+
- **Bun**: its `https` server answers the connections given to it without TLS, so `listen()` refuses it in Bun (a
|
|
244
|
+
Fastify instance created with `https` too); use `http2.createSecureServer({key, cert, allowHTTP1: true})` or
|
|
245
|
+
`tls.createServer()` there. Its servers do not close the idle keep-alive connections given to them, which the
|
|
246
|
+
entrypoints close themselves. A connection given to a server cannot be upgraded to a WebSocket (seen with Bun 1.4), so
|
|
247
|
+
the entrypoints refuse WebSocket on an interface in Bun.
|
|
248
|
+
- **Electron**: in the main process.
|
|
249
|
+
- The bundles, the executables and the installers made by `lakutata package` carry the compiled addon
|
|
250
|
+
(`build/Release/netif.node`): build them on each target platform, after installing the package there (see
|
|
251
|
+
[Packaging](Packaging.md) and [NativeAddons](NativeAddons.md)).
|
|
252
|
+
|
|
253
|
+
## 7. Exceptions
|
|
254
|
+
|
|
255
|
+
| Exception | When |
|
|
256
|
+
|---|---|
|
|
257
|
+
| `NetIfNotFoundException` | An index no interface has, a hardware address several interfaces have |
|
|
258
|
+
| `NetIfNotEnforcedException` | `strict` where the system cannot refuse the other interfaces, or the guarantee lost (an `error` event) |
|
|
259
|
+
| `NetIfNotSupportedException` | What the system or the runtime cannot do: `bindSocket()` outside Linux, multicast on a socket bound to an address on macOS, an `https` server in Bun |
|
|
260
|
+
| `NetIfTimeoutException` | `waitForAddress()` past its `timeout` |
|
|
261
|
+
| `NetIfException` | The base of all of them; a refusal of the system has its code as `errno` (`'EADDRINUSE'`, `'EACCES'`…) |
|
package/doc/en/Overview.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Lakutata Overview
|
|
2
2
|
|
|
3
3
|
**Lakutata** is an IoC application framework for Node.js and TypeScript. It gives web services, CLI tools, desktop
|
|
4
|
-
(Electron) applications and embedded devices one programming model: an application is a tree of objects
|
|
5
|
-
providers, controllers) that a container creates. DTOs validate their options and inputs, and transports
|
|
6
|
-
the controllers: HTTP (Express, Fastify), CLI (Commander), service RPC (HTTP, Socket.IO, JSON-RPC 2.0, gRPC,
|
|
7
|
-
Electron IPC), schedules and message queues. One controller method can serve all of them at once.
|
|
4
|
+
(Electron, NW.js) applications and embedded devices one programming model: an application is a tree of objects
|
|
5
|
+
(components, providers, controllers) that a container creates. DTOs validate their options and inputs, and transports
|
|
6
|
+
expose the controllers: HTTP (Express, Fastify), CLI (Commander), service RPC (HTTP, Socket.IO, JSON-RPC 2.0, gRPC,
|
|
7
|
+
NATS, Electron IPC, NW.js), schedules and message queues. One controller method can serve all of them at once.
|
|
8
8
|
|
|
9
9
|
The package `lakutata` re-exports the `@lakutata/*` packages under its subpaths (see the table below) and presets two
|
|
10
10
|
components in every application: `log` (the `Logger`, which writes pino-compatible JSON lines) and `entrypoint` (the
|
|
@@ -30,7 +30,7 @@ tool, a web and a desktop application, calls between services, background jobs,
|
|
|
30
30
|
| `lakutata/decorator/ctrl` | `@HTTPAction`, `@GET`…`@OPTIONS`, `@CLIAction`, `@ServiceAction`, `@Schedule`, `@Prefix`, `@UseMiddlewares` |
|
|
31
31
|
| `lakutata/adapter/express`, `lakutata/adapter/fastify` | The HTTP transports |
|
|
32
32
|
| `lakutata/adapter/commander` | The CLI transport |
|
|
33
|
-
| `lakutata/adapter/socket.io`, `lakutata/adapter/jsonrpc`, `lakutata/adapter/electron` | The service transports |
|
|
33
|
+
| `lakutata/adapter/socket.io`, `lakutata/adapter/jsonrpc`, `lakutata/adapter/electron`, `lakutata/adapter/nwjs` | The service transports |
|
|
34
34
|
| `lakutata/com/logger` | The `Logger` component, preset as `log` (pino-compatible JSON lines) |
|
|
35
35
|
| `lakutata/orm`, `lakutata/decorator/orm` | TypeORM and its decorators |
|
|
36
36
|
| `lakutata/com/database`, `lakutata/provider/database` | The `Database` component and provider |
|
|
@@ -48,8 +48,9 @@ Not part of this package: `@lakutata/cli` (the `lakutata` command for creating p
|
|
|
48
48
|
applications, see [Command](Command.md)), `@lakutata/grpc` (a gRPC client, server and service transport),
|
|
49
49
|
`@lakutata/nats` (a NATS component and service transport), `@lakutata/dbus` (D-Bus), `@lakutata/mcp` (the Model
|
|
50
50
|
Context Protocol), `@lakutata/webrtc` (WebRTC: the signaling, a TURN server, a media server, peer connections),
|
|
51
|
-
`@lakutata/ffi` (the C functions of native libraries)
|
|
52
|
-
|
|
51
|
+
`@lakutata/ffi` (the C functions of native libraries), `@lakutata/math` (symbolic and numeric mathematics, as functions
|
|
52
|
+
that need no lakutata application) and `@lakutata/netif` (servers and UDP sockets listening on a network interface
|
|
53
|
+
whatever its addresses, the interfaces of the machine and their changes).
|
|
53
54
|
|
|
54
55
|
## The guides
|
|
55
56
|
|
|
@@ -73,7 +74,7 @@ functions that need no lakutata application).
|
|
|
73
74
|
| [Middlewares](Middlewares.md) | The middlewares and the access control of the actions |
|
|
74
75
|
| [HTTP](HTTP.md) | The HTTP adapters: routes, input, responses, errors, the features of Express and Fastify |
|
|
75
76
|
| [CLI](CLI.md) | Command line tools: commands, options, prompts, output and exit codes |
|
|
76
|
-
| [ServiceCalls](ServiceCalls.md) | The service actions over HTTP, Socket.IO, JSON-RPC 2.0 and
|
|
77
|
+
| [ServiceCalls](ServiceCalls.md) | The service actions over HTTP, Socket.IO, JSON-RPC 2.0, Electron and NW.js: patterns, responses, clients and their events |
|
|
77
78
|
| [GRPC](GRPC.md) | The service actions over gRPC (`@lakutata/grpc`), and calling them from other applications and languages |
|
|
78
79
|
| [NATS](NATS.md) | The NATS component, the service actions over NATS (`@lakutata/nats`), the service proxies and their events |
|
|
79
80
|
| [Schedules](Schedules.md) | Scheduled jobs: intervals, cron expressions, jobs run by one instance |
|
|
@@ -95,6 +96,7 @@ functions that need no lakutata application).
|
|
|
95
96
|
| [WebRTC](WebRTC.md) | Peer-to-peer connections and a media server (`@lakutata/webrtc`): the signaling, the TURN server, browsers and applications, the actions of an application called through data channels, WHIP and WHEP |
|
|
96
97
|
| [FFI](FFI.md) | The C functions of native libraries (`@lakutata/ffi`): declarations copied from headers, structs, callbacks, memory, packaging |
|
|
97
98
|
| [Math](Math.md) | Mathematics (`@lakutata/math`): formulas and exact results, equations, decimals of any precision, matrices, statistics, numeric solutions, units |
|
|
99
|
+
| [Netif](Netif.md) | Network interfaces (`@lakutata/netif`): a server listening on an interface whatever its addresses, the connections of the other interfaces refused, the interfaces and their changes, UDP on one interface |
|
|
98
100
|
| [Command](Command.md) | The `lakutata` command: creating projects, migrations, translations, environment variables |
|
|
99
101
|
| [Workspaces](Workspaces.md) | Projects of several packages: running their scripts, giving them a version, adding packages |
|
|
100
102
|
| [NativeAddons](NativeAddons.md) | Native addons in C++ or Rust: creating them, linking libraries, shipping them |
|
package/doc/en/Packaging.md
CHANGED
|
@@ -49,6 +49,7 @@ directory, once moved with them.
|
|
|
49
49
|
| `--outfile <file>` | The bundle, `dist/<name>.cjs` by default |
|
|
50
50
|
| `--no-minify` | Do not minify the bundle (the names of the classes are kept either way) |
|
|
51
51
|
| `--external <modules...>` | Modules left out of the bundle, loaded at run time from a `node_modules` next to it |
|
|
52
|
+
| `--node <version>` | The version of Node.js running the bundle (`18.10` for NW.js 0.70): the bundle is written in the syntax that version reads; the one running the tool by default |
|
|
52
53
|
|
|
53
54
|
## 3. Native addons
|
|
54
55
|
|
|
@@ -124,11 +125,12 @@ architecture for Linux (`docker run --platform linux/arm64`); the README of the
|
|
|
124
125
|
|
|
125
126
|
| Option | Meaning |
|
|
126
127
|
|---|---|
|
|
127
|
-
| `--runtime node` (default) | The bundle is embedded in a copy of the Node.js running the tool (a single executable application of Node.js): about 110 MB on macOS arm64 |
|
|
128
|
+
| `--runtime node` (default) | The bundle is embedded in a copy of the Node.js running the tool, or of `--node-program` (a single executable application of Node.js): about 110 MB on macOS arm64 |
|
|
128
129
|
| `--runtime bun` | The bundle is compiled by Bun (the `bun` package of the project, else the `bun` of the system): about 70 MB on macOS arm64, and it starts faster; the runtime is then Bun |
|
|
129
130
|
| `--layout file` (default) | One file: the native addons, the files read by path and the resources are embedded, and extracted when it starts |
|
|
130
131
|
| `--layout directory` | `dist/<name>/` holds the executable, `native/` and `resources/`, loaded from there without extraction (the installers use it) |
|
|
131
132
|
| `--outdir <directory>` | Where the executable is written, `dist` by default |
|
|
133
|
+
| `--node-program <path>` | The node embedded instead of the one running the tool: a Node.js 20.12, 21.7 or later (`$(nvm which 22)`), so that an older Node.js can run the tool; the bundle follows its syntax |
|
|
132
134
|
| `--entry`, `--no-minify`, `--external` | As for a bundle |
|
|
133
135
|
|
|
134
136
|
- **Extraction**: an executable of the file layout extracts what it embeds at its first run, into its cache directory,
|
|
@@ -171,6 +173,7 @@ of the project (it may be outside the package, such as `../ui/dist`), `to` its p
|
|
|
171
173
|
| An executable, file layout | `true` | The directory the resources were extracted to |
|
|
172
174
|
| An executable, directory layout, or an installed application | `true` | `resources` next to the executable |
|
|
173
175
|
| A packaged Electron application | `true` | `main.resources` in the resources of Electron |
|
|
176
|
+
| An NW.js application (run by `lakutata nwjs run`, or packaged) | `true` | `resources` in the directory of the application |
|
|
174
177
|
|
|
175
178
|
`resourcesPath()` returns the path even when the application declares no resources, so the directory may not exist.
|
|
176
179
|
The application chooses where to read its files from:
|
|
@@ -53,8 +53,8 @@ templates other than `cli` do.
|
|
|
53
53
|
## 2. The workspace templates
|
|
54
54
|
|
|
55
55
|
The other templates are npm workspaces: the application is the package `packages/app`, and the templates with an
|
|
56
|
-
interface or with installers add `packages/ui` (the web pages,
|
|
57
|
-
(the icon). The `service` template:
|
|
56
|
+
interface or with installers add `packages/ui` (the web pages, the renderer of Electron, or the user interface of
|
|
57
|
+
NW.js) and `packages/resources` (the icon). The `service` template:
|
|
58
58
|
|
|
59
59
|
```
|
|
60
60
|
shop/
|
|
@@ -93,6 +93,10 @@ The other templates add to this layout:
|
|
|
93
93
|
it into `dist/` ([Packaging](Packaging.md)).
|
|
94
94
|
- `electron-react` and `electron-vue`: the main process of Electron in `packages/app` (its window and its service
|
|
95
95
|
actions over IPC), the renderer in `packages/ui`, and the scripts that make the installers.
|
|
96
|
+
- `nwjs-react` and `nwjs-vue`: the background script of NW.js in `packages/app` (its window, whose host page in
|
|
97
|
+
`packages/app/host` holds the user interface in a webview, and its service actions), the user interface in
|
|
98
|
+
`packages/ui`, the version of NW.js in `packages/app/nwjs.json` (written by `npm run nwjs:use -- <version>`), and the
|
|
99
|
+
scripts that make the installers.
|
|
96
100
|
|
|
97
101
|
The packed forms are written to `dist/` at the root of the workspace; `.gitignore` leaves out `build`, `dist` and the
|
|
98
102
|
outputs of the interfaces.
|
|
@@ -105,8 +109,9 @@ outputs of the interfaces.
|
|
|
105
109
|
|---|---|---|
|
|
106
110
|
| `i18n` | all | The messages of the languages in `src/locales` (`en`, `zh-CN`), the `i18n` component, the language middleware, and the scripts `i18n:check` and `i18n:types` ([I18n](I18n.md)) |
|
|
107
111
|
| `installer` | `cli`, `service`, `web-*` | The `lakutata.installer` of the application, the scripts that make the installers (on the machine, and for Linux in Docker), and for the services a system service ([Installers](Installers.md)) |
|
|
112
|
+
| `win7` | `nwjs-*` | NW.js 0.72.0 instead of the release the template was published with: the last release running on Windows 7, 8 and 8.1, and the script `package:ia32` that makes the installers of 32-bit Windows |
|
|
108
113
|
|
|
109
|
-
The Electron templates make installers without
|
|
114
|
+
The Electron and NW.js templates make installers without the `installer` feature.
|
|
110
115
|
|
|
111
116
|
## 4. Templates of your own
|
|
112
117
|
|
package/doc/en/ServiceCalls.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A service action (`@ServiceAction`) is called with a message rather than a URL: an object whose keys select the action,
|
|
4
4
|
its **pattern**, and whose other keys are the input. The same actions can be served over several transports at once:
|
|
5
|
-
plain HTTP, Socket.IO, JSON-RPC 2.0 (over HTTP, WebSocket or the standard input and output)
|
|
6
|
-
this guide describes, and gRPC, NATS and the data channels of WebRTC, which have their own guides. The controllers and
|
|
5
|
+
plain HTTP, Socket.IO, JSON-RPC 2.0 (over HTTP, WebSocket or the standard input and output), the IPC of Electron and the
|
|
6
|
+
windows of NW.js, as this guide describes, and gRPC, NATS and the data channels of WebRTC, which have their own guides. The controllers and
|
|
7
7
|
their actions are described in [Controllers](Controllers.md), the entrypoint and its adapters in
|
|
8
8
|
[Entrypoint](Entrypoint.md).
|
|
9
9
|
|
|
@@ -136,7 +136,7 @@ class OrderController extends Controller {
|
|
|
136
136
|
|
|
137
137
|
## 3. The response
|
|
138
138
|
|
|
139
|
-
The HTTP service, Socket.IO and
|
|
139
|
+
The HTTP service, Socket.IO, Electron and NW.js answer with an envelope; JSON-RPC answers with its own responses (section 4).
|
|
140
140
|
|
|
141
141
|
```text
|
|
142
142
|
{"success": true, "error": null, "payload": <the result>}
|
|
@@ -179,6 +179,7 @@ export async function call<T>(message: Record<string, unknown>): Promise<T> {
|
|
|
179
179
|
| `socketIOServiceEntrypoint()` | `lakutata/adapter/socket.io` | `socket.io` (`socket.io-client` on the clients) | An emit with an acknowledgement | Socket.IO events |
|
|
180
180
|
| `jsonRPCServiceEntrypoint()` | `lakutata/adapter/jsonrpc` | `ws`, for WebSocket | A JSON-RPC 2.0 request | Notifications (WebSocket, stdio) |
|
|
181
181
|
| `electronIPCServiceEntrypoint()` | `lakutata/adapter/electron` | (Electron) | `ipcRenderer.invoke()` | IPC messages |
|
|
182
|
+
| `nwjsServiceEntrypoint()` | `lakutata/adapter/nwjs` | (NW.js) | A call of its bridge, by the host page of a window | Calls of the function the host page gives |
|
|
182
183
|
| `grpcServiceEntrypoint()` | `@lakutata/grpc/service` | `@lakutata/grpc` | See [GRPC](GRPC.md) | gRPC streams |
|
|
183
184
|
| `natsServiceEntrypoint()` | `@lakutata/nats` | `@lakutata/nats` | See [NATS](NATS.md) | NATS messages |
|
|
184
185
|
| `webrtcServiceEntrypoint()` | `@lakutata/webrtc` | `@lakutata/webrtc` | A JSON-RPC 2.0 request on a data channel, see [WebRTC](WebRTC.md) | Notifications |
|
|
@@ -197,7 +198,7 @@ pages of a web UI during its development. The Electron templates add it in devel
|
|
|
197
198
|
| `host` | all the interfaces | The address to listen on |
|
|
198
199
|
| `path` | `'/'` | The path of the requests; the events are under it, at `<path>/events` |
|
|
199
200
|
| `limit` | `10485760` (10 MiB) | The largest body, in bytes |
|
|
200
|
-
| `listening` | | `(server) => …`, called once the server listens |
|
|
201
|
+
| `listening` | | `(server, listeners) => …`, called once the server listens (`listeners`, with `interface`: see below) |
|
|
201
202
|
|
|
202
203
|
- A request is a `POST` to `path`, its body the message as JSON. It is answered with 200 and the envelope, whatever the
|
|
203
204
|
result of the action. A body that is not JSON is answered with 400, a body over `limit` with 413, another method or
|
|
@@ -284,7 +285,7 @@ connected.
|
|
|
284
285
|
| `options` | | The options of the Socket.IO server: `cors`, `path`, `maxHttpBufferSize`… |
|
|
285
286
|
| `server` | | An HTTP server to share, or a function returning it (below) |
|
|
286
287
|
| `io` | | An existing Socket.IO server, used as it is |
|
|
287
|
-
| `listening` | | `(io, server) => …`, called once the adapter is ready |
|
|
288
|
+
| `listening` | | `(io, server, listeners) => …`, called once the adapter is ready |
|
|
288
289
|
|
|
289
290
|
```typescript
|
|
290
291
|
import {io, type Socket} from 'socket.io-client'
|
|
@@ -340,7 +341,7 @@ the tools that speak the protocol, and the parent processes over the standard in
|
|
|
340
341
|
| `input`, `output` | `process.stdin`, `process.stdout` | The streams of stdio |
|
|
341
342
|
| `end` | `'application'` | When the input of stdio ends: `'application'` exits the application once the requests in progress are answered, `'none'` does nothing, or a function |
|
|
342
343
|
| `heartbeat` | `30000` | The interval of the pings of WebSocket, in milliseconds; `0` sends none |
|
|
343
|
-
| `listening` | | `(server) => …`, called once the server of HTTP or WebSocket listens |
|
|
344
|
+
| `listening` | | `(server, listeners) => …`, called once the server of HTTP or WebSocket listens |
|
|
344
345
|
|
|
345
346
|
The method and the params of a request make the message: `{"method": "order.get", "params": {"id": 1}}` is the message
|
|
346
347
|
`{method: 'order.get', id: 1}`, so the action declares the method in its pattern:
|
|
@@ -417,6 +418,25 @@ $ echo '{"jsonrpc": "2.0", "id": 1, "method": "sum", "params": {"values": [1, 2,
|
|
|
417
418
|
|
|
418
419
|
The application exits once its input ends and its requests are answered (`end`).
|
|
419
420
|
|
|
421
|
+
### On a network interface
|
|
422
|
+
|
|
423
|
+
`httpServiceEntrypoint()`, `socketIOServiceEntrypoint()` with its own server, and `jsonRPCServiceEntrypoint()` over HTTP
|
|
424
|
+
or WebSocket can listen on a network interface instead of an address, whatever its addresses, with the package
|
|
425
|
+
`@lakutata/netif` installed apart. They take the options `interface`, `family`, `strict`, `onError` and
|
|
426
|
+
`interfaceListening` of the HTTP adapters (see [HTTP](HTTP.md), section 2), and their `listening` callback gets the
|
|
427
|
+
listeners of the interface as its last argument:
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
import {httpServiceEntrypoint} from 'lakutata/com/entrypoint'
|
|
431
|
+
|
|
432
|
+
//The service on eth1 only, whatever its address
|
|
433
|
+
export const service = httpServiceEntrypoint({port: 3000, interface: 'eth1'})
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
In Bun, a connection given to a server cannot be upgraded to a WebSocket (seen with Bun 1.4). There the start fails for
|
|
437
|
+
JSON-RPC over WebSocket, and for Socket.IO unless it uses polling only (`options: {transports: ['polling']}`, and the
|
|
438
|
+
same on the clients).
|
|
439
|
+
|
|
420
440
|
### Electron
|
|
421
441
|
|
|
422
442
|
`electronIPCServiceEntrypoint()` serves the actions to the windows of an Electron application, in its main process.
|
|
@@ -449,6 +469,47 @@ contextBridge.exposeInMainWorld('service', {
|
|
|
449
469
|
- The Electron templates of `lakutata create` contain a preload script of this shape, and a `window.ipc` for the pages,
|
|
450
470
|
whose `invoke()` returns the payload or throws the error.
|
|
451
471
|
|
|
472
|
+
### NW.js
|
|
473
|
+
|
|
474
|
+
`nwjsServiceEntrypoint()` serves the actions to the windows of an NW.js application, in its background script (its
|
|
475
|
+
Node.js context). NW.js gives Node.js to the pages of its windows, and an iframe of such a page does not keep Node.js
|
|
476
|
+
from its content (its sandbox is ignored): the user interface runs in a `<webview>`, without Node.js, held by a trusted
|
|
477
|
+
page of the window (its host page). The adapter puts a bridge on the global object that NW.js shares with its windows,
|
|
478
|
+
`global[Symbol.for(key)]`; the host page forwards the requests of the interface to it, and gives it the function that
|
|
479
|
+
pushes the events to the interface.
|
|
480
|
+
|
|
481
|
+
| Option | Default | Meaning |
|
|
482
|
+
|---|---|---|
|
|
483
|
+
| `key` | `'lakutata:service'` (`NWJS_SERVICE_BRIDGE`) | The key of the bridge on the global object |
|
|
484
|
+
| `global` | the global object of NW.js | The object the bridge is put on, or a stand-in in the tests |
|
|
485
|
+
|
|
486
|
+
| Method of the bridge | Meaning |
|
|
487
|
+
|---|---|
|
|
488
|
+
| `invoke(message, clientId)` | Invokes an action: resolves to the envelope, never rejects |
|
|
489
|
+
| `subscribe(clientId, push)` | Makes a page a client: the events pushed to it are given to `push(event, args)` |
|
|
490
|
+
| `unsubscribe(clientId)` | Removes a client, when its page is loaded again or its window closed: its requests in progress are aborted |
|
|
491
|
+
|
|
492
|
+
The host page forwards the messages of the interface, from the page loaded in its `<webview>` only:
|
|
493
|
+
|
|
494
|
+
```javascript
|
|
495
|
+
const view = document.querySelector('webview')
|
|
496
|
+
const origin = new URL(view.src).origin
|
|
497
|
+
const bridge = global[Symbol.for('lakutata:service')]
|
|
498
|
+
const send = (message) => view.contentWindow.postMessage(message, origin)
|
|
499
|
+
window.addEventListener('message', async (event) => {
|
|
500
|
+
if (event.source !== view.contentWindow || event.origin !== origin) return
|
|
501
|
+
const {type, id, message, clientId} = event.data
|
|
502
|
+
if (type === 'invoke') send({type: 'response', id: id, response: await bridge.invoke(message, clientId)})
|
|
503
|
+
if (type === 'subscribe') bridge.subscribe(clientId, (name, args) => send({type: 'event', name: name, args: args}))
|
|
504
|
+
})
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
- The `<webview>` loads the files of the application when its partition is declared in the manifest of NW.js:
|
|
508
|
+
`"webview": {"partitions": [{"name": "trusted", "accessible_resources": ["<all_urls>"]}]}` (the builder writes it).
|
|
509
|
+
- A page loaded again in the `<webview>` is a new client: the host page unsubscribes the previous one.
|
|
510
|
+
- The NW.js templates of `lakutata create` contain such a host page (`packages/app/host`), and a `window.ipc` for the
|
|
511
|
+
interface like the one of the Electron templates.
|
|
512
|
+
|
|
452
513
|
## 5. Clients and events
|
|
453
514
|
|
|
454
515
|
The transports that keep their clients connected can push events to them. A client is identified by the id it gives:
|
|
@@ -461,6 +522,7 @@ The transports that keep their clients connected can push events to them. A clie
|
|
|
461
522
|
| JSON-RPC over WebSocket | The `id` header or `id` query parameter of the connection, else a random id |
|
|
462
523
|
| JSON-RPC over stdio | `'stdio'` |
|
|
463
524
|
| Electron | The id the page gives when it subscribes or invokes |
|
|
525
|
+
| NW.js | The id the host page gives to the bridge for its page |
|
|
464
526
|
| WebRTC | The member id of the caller in its room |
|
|
465
527
|
|
|
466
528
|
An action reads the id of its client in `this.context.clientId`. The `entrypoint` component pushes the events:
|
|
@@ -496,6 +558,7 @@ A client receives `notify(clientId, 'progress', 50, 'half')` as:
|
|
|
496
558
|
| Socket.IO | `socket.on('progress', (percent, label) => …)` |
|
|
497
559
|
| JSON-RPC (WebSocket, stdio) | `{"jsonrpc": "2.0", "method": "progress", "params": [50, "half"]}` |
|
|
498
560
|
| Electron | `('progress', [50, 'half'])`, through the `subscribe` of the preload script above |
|
|
561
|
+
| NW.js | `('progress', [50, 'half'])`, given to the function of the `subscribe` of the bridge |
|
|
499
562
|
| WebRTC | `proxy.on('progress', (percent, label) => …)` of a `PeerServiceProxy` |
|
|
500
563
|
|
|
501
564
|
## 6. Stopping and leaving
|
package/doc/en/UpgradingFrom1.md
CHANGED
|
@@ -20,7 +20,7 @@ packages obtained elsewhere may have APIs that this page does not cover.
|
|
|
20
20
|
| Modules | CommonJS: imports without extensions, `__dirname` | ES modules: relative imports ending in `.js` |
|
|
21
21
|
| Service actions | `@Controller({_ctrl: …})` and `@PATTERN({_act: …})` from `@lakutata-module/service` | `@ServiceAction({…})` from `lakutata/decorator/ctrl` |
|
|
22
22
|
| Validation | `Validator` schemas (yup) given to `@Accept` | DTO classes with `@Expect` |
|
|
23
|
-
| Node.js | `>=14.15.1` (`@lakutata/core` 1.0.60) |
|
|
23
|
+
| Node.js | `>=14.15.1` (`@lakutata/core` 1.0.60) | `>=18.10.0` |
|
|
24
24
|
|
|
25
25
|
**`@lakutata/core` 3 is not a new version of the 1.x library.** It is the core of Lakutata 3, a different API under the
|
|
26
26
|
same name. `npm install @lakutata/core@latest`, or a range such as `*`, in a 1.x project puts it in place of the 1.x
|
|
@@ -392,7 +392,7 @@ are built differently, so the entries that 1.x wrote are not found: start with a
|
|
|
392
392
|
- **The `lakutata` field of `package.json`** told the 1.x tool the kind of the project (`type`), its directories and
|
|
393
393
|
its configuration file. In 3.x the field has another meaning (the `resources` and the `installer` of a packed
|
|
394
394
|
application, [Packaging](Packaging.md)): remove the 1.x one.
|
|
395
|
-
- **Node.js, TypeScript and modules.** Node.js
|
|
395
|
+
- **Node.js, TypeScript and modules.** Node.js `>=18.10.0`, TypeScript 6 as in the templates, ES modules
|
|
396
396
|
(`"type": "module"`, relative imports ending in `.js`), `tsconfig.json` extending `lakutata/tsconfig.json`.
|
|
397
397
|
- **Ports.** The service module listened on a port of its own (6912 by default) and the registry on another (6911).
|
|
398
398
|
The 3.x adapters listen on the ports of their options, and a service on NATS only connects out to the NATS server:
|
package/doc/en/UpgradingFrom2.md
CHANGED
|
@@ -17,15 +17,15 @@ It is for the projects that depend on the package `lakutata` at version 2. A pro
|
|
|
17
17
|
| Exported functions | PascalCase: `BuildEntrypoints`, `BuildDatabaseOptions`, `MD5` | camelCase: `buildEntrypoints`, `buildDatabaseOptions`, `md5` |
|
|
18
18
|
| Transports | Written by hand: `BuildHTTPEntrypoint((module, routeMap, handler, registerDestroy) => …)` | Adapters: `expressHTTPEntrypoint()` from `lakutata/adapter/express`… |
|
|
19
19
|
| Subpaths of 3.x only | | `lakutata/utils`, `lakutata/security`, `lakutata/adapter/*`, `lakutata/com/{containerd,mutex,queue,i18n}` |
|
|
20
|
-
| Node.js | `>=20` |
|
|
20
|
+
| Node.js | `>=20` | `>=18.10.0` |
|
|
21
21
|
|
|
22
22
|
`npx @lakutata/cli info` prints the versions of the tool, of the framework and of Node.js, and the `lakutata` version
|
|
23
23
|
that the project declares and the one installed. There is no upgrade command: the steps below are done by hand.
|
|
24
24
|
|
|
25
25
|
## 2. The steps
|
|
26
26
|
|
|
27
|
-
- **Node.js** `^22.13.0 || >=24.11.0`
|
|
28
|
-
|
|
27
|
+
- **Node.js** `>=18.10.0` (3.0 to 3.1 needed `^22.13.0 || >=24.11.0`). The versions 2.x needs (`>=20`) are among them:
|
|
28
|
+
`engines`, the CI matrix and the Dockerfiles can stay.
|
|
29
29
|
- **A TypeScript that knows `"module": "Node20"`** (the templates use TypeScript 6): `lakutata/tsconfig.json` now sets
|
|
30
30
|
it (2.x: `Node16`), and older compilers reject it.
|
|
31
31
|
- **`package.json`**: `"type": "module"`, `lakutata` `^3.0.0`, the `@lakutata/*` packages at `^3.0.0` too, and
|
|
@@ -281,7 +281,7 @@ Application
|
|
|
281
281
|
## 6. Checking the upgrade
|
|
282
282
|
|
|
283
283
|
```bash
|
|
284
|
-
node --version #
|
|
284
|
+
node --version # >=18.10.0
|
|
285
285
|
npm ls lakutata @lakutata/core # one version of each
|
|
286
286
|
npm run build # tsc without errors: no 2.x import left
|
|
287
287
|
grep -rnE "process\.on\('SIG|\|\| ''|(startup|launched|beforeDestroy|acceptsOption)\(|AccessDenyException|=== 500" src
|
package/doc/en/WebRTC.md
CHANGED
|
@@ -224,10 +224,10 @@ export class Telemetry extends Component {
|
|
|
224
224
|
```
|
|
225
225
|
|
|
226
226
|
An `http:` or `https:` URL uses Socket.IO (install `socket.io-client`), a `ws:` or `wss:` URL uses JSON-RPC over the
|
|
227
|
-
`WebSocket` of Node.js 22 and Bun
|
|
228
|
-
(more fields of the Socket.IO handshake, a token), `timeout`,
|
|
229
|
-
`icePortRange`, `iceAdditionalHostAddresses`, `codecs`,
|
|
230
|
-
client, and `close()` leaves its rooms and disconnects.
|
|
227
|
+
global `WebSocket` of Node.js 22 and Bun, else over the `WebSocket` of the package `ws` (install `ws`). Its options: `url`, `id`
|
|
228
|
+
(a random UUID by default), `path` (of Socket.IO), `auth` (more fields of the Socket.IO handshake, a token), `timeout`,
|
|
229
|
+
and the options of its connections (`iceTransportPolicy`, `icePortRange`, `iceAdditionalHostAddresses`, `codecs`,
|
|
230
|
+
`threads`). It is `@Transient`: each `getObject()` gives a new client, and `close()` leaves its rooms and disconnects.
|
|
231
231
|
|
|
232
232
|
### Calling the actions of an application
|
|
233
233
|
|
package/doc/zh/Application.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
| 选项 | 含义 |
|
|
10
10
|
|---|---|
|
|
11
|
-
| `id`、`name` | 必填。应用的 ID(同时作为 `process.title
|
|
11
|
+
| `id`、`name` | 必填。应用的 ID(同时作为 `process.title`,NW.js 中除外)和名称(`app.appId`、`app.appName`) |
|
|
12
12
|
| `timezone` | IANA 时区名,或 `'auto'`(默认)表示使用系统时区;写入 `TZ` |
|
|
13
13
|
| `mode` | `'development'` 或 `'production'`,在环境变量未指定时生效(见第 4 节) |
|
|
14
14
|
| `shutdownSignals` | 使应用优雅退出的信号(默认 `['SIGTERM', 'SIGINT']`),或 `false` |
|
package/doc/zh/Cache.md
CHANGED
|
@@ -250,7 +250,7 @@ Application.run(() => ({
|
|
|
250
250
|
|
|
251
251
|
- `table` 中字母、数字和 `_` 以外的字符会被去掉。
|
|
252
252
|
- 语句同步执行:等待被锁住的数据库时,语句会阻塞事件循环,最长 `busyTimeout`。
|
|
253
|
-
- `better-sqlite3` 是原生插件,针对一种运行时构建:Electron 应用需要使用针对其 Electron 版本构建的 `better-sqlite3
|
|
253
|
+
- `better-sqlite3` 是原生插件,针对一种运行时构建:Electron 应用需要使用针对其 Electron 版本构建的 `better-sqlite3`;NW.js 应用会用 NW.js 的头文件重新构建它(需要 0.111.3 及以后的版本:更早的版本无法加载它)。在 Bun 上,存储改用 `bun:sqlite`。
|
|
254
254
|
|
|
255
255
|
### PostgreSQL
|
|
256
256
|
|