lakutata 3.0.0 → 3.1.1
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 +50 -30
- package/README.zh-CN.md +130 -0
- package/dist/cjs/exports/QueueComponent.d.ts +1 -0
- package/dist/cjs/exports/QueueComponent.js +20 -0
- package/dist/esm/exports/QueueComponent.js +1 -0
- package/dist/types/exports/QueueComponent.d.ts +1 -0
- package/doc/en/Application.md +107 -0
- package/doc/en/CLI.md +274 -0
- package/doc/en/Cache.md +379 -0
- package/doc/en/Command.md +110 -0
- package/doc/en/ContainerdIntegration.md +17 -16
- package/doc/en/Controllers.md +256 -0
- package/doc/en/DBus.md +291 -0
- package/doc/en/DTOValidation.md +72 -43
- package/doc/en/Database.md +188 -0
- package/doc/en/DatabaseDrivers.md +287 -0
- package/doc/en/Decorator.md +52 -40
- package/doc/en/DockerIntegration.md +7 -6
- package/doc/en/Entities.md +240 -0
- package/doc/en/Entrypoint.md +216 -0
- package/doc/en/Exceptions.md +174 -0
- package/doc/en/GRPC.md +251 -0
- package/doc/en/GettingStarted.md +165 -0
- package/doc/en/HTTP.md +511 -0
- package/doc/en/HelperFunctions.md +11 -11
- package/doc/en/I18n.md +119 -115
- package/doc/en/Injection.md +144 -0
- package/doc/en/Installers.md +280 -0
- package/doc/en/Logger.md +206 -0
- package/doc/en/MCP.md +293 -0
- package/doc/en/Middlewares.md +213 -0
- package/doc/en/Migrations.md +202 -0
- package/doc/en/MongoEmbedded.md +149 -0
- package/doc/en/Monitor.md +164 -0
- package/doc/en/Mutex.md +27 -25
- package/doc/en/NATS.md +246 -0
- package/doc/en/NativeAddons.md +212 -0
- package/doc/en/Objects.md +143 -0
- package/doc/en/Overview.md +63 -85
- package/doc/en/Packaging.md +193 -0
- package/doc/en/ProjectStructure.md +124 -0
- package/doc/en/Queue.md +269 -0
- package/doc/en/Schedules.md +217 -0
- package/doc/en/Security.md +10 -9
- package/doc/en/ServiceCalls.md +508 -0
- package/doc/en/Time.md +19 -20
- package/doc/en/UpgradingFrom1.md +435 -0
- package/doc/en/UpgradingFrom2.md +315 -0
- package/doc/en/WebRTC.md +1364 -0
- package/doc/en/Workspaces.md +189 -0
- package/doc/en/examples/BackgroundJobs.md +677 -0
- package/doc/en/examples/CommandLineTool.md +664 -0
- package/doc/en/examples/DesktopApp.md +809 -0
- package/doc/en/examples/DeviceService.md +607 -0
- package/doc/en/examples/MCPServer.md +653 -0
- package/doc/en/examples/Meeting.md +573 -0
- package/doc/en/examples/README.md +17 -0
- package/doc/en/examples/RestAPI.md +490 -0
- package/doc/en/examples/ServiceCalls.md +829 -0
- package/doc/en/examples/WebApp.md +568 -0
- package/doc/zh/Application.md +75 -0
- package/doc/zh/CLI.md +250 -0
- package/doc/zh/Cache.md +328 -0
- package/doc/zh/Command.md +78 -0
- package/doc/zh/ContainerdIntegration.md +9 -9
- package/doc/zh/Controllers.md +228 -0
- package/doc/zh/DBus.md +257 -0
- package/doc/zh/DTOValidation.md +49 -19
- package/doc/zh/Database.md +175 -0
- package/doc/zh/DatabaseDrivers.md +261 -0
- package/doc/zh/Decorator.md +25 -18
- package/doc/zh/DockerIntegration.md +6 -6
- package/doc/zh/Entities.md +224 -0
- package/doc/zh/Entrypoint.md +193 -0
- package/doc/zh/Exceptions.md +158 -0
- package/doc/zh/GRPC.md +226 -0
- package/doc/zh/GettingStarted.md +148 -0
- package/doc/zh/HTTP.md +446 -0
- package/doc/zh/HelperFunctions.md +2 -2
- package/doc/zh/I18n.md +26 -26
- package/doc/zh/Injection.md +129 -0
- package/doc/zh/Installers.md +232 -0
- package/doc/zh/Logger.md +188 -0
- package/doc/zh/MCP.md +265 -0
- package/doc/zh/Middlewares.md +198 -0
- package/doc/zh/Migrations.md +178 -0
- package/doc/zh/MongoEmbedded.md +118 -0
- package/doc/zh/Monitor.md +145 -0
- package/doc/zh/Mutex.md +10 -10
- package/doc/zh/NATS.md +207 -0
- package/doc/zh/NativeAddons.md +180 -0
- package/doc/zh/Objects.md +119 -0
- package/doc/zh/Overview.md +47 -71
- package/doc/zh/Packaging.md +154 -0
- package/doc/zh/ProjectStructure.md +102 -0
- package/doc/zh/Queue.md +214 -0
- package/doc/zh/Schedules.md +193 -0
- package/doc/zh/Security.md +6 -6
- package/doc/zh/ServiceCalls.md +462 -0
- package/doc/zh/Time.md +2 -2
- package/doc/zh/UpgradingFrom1.md +347 -0
- package/doc/zh/UpgradingFrom2.md +230 -0
- package/doc/zh/WebRTC.md +1049 -0
- package/doc/zh/Workspaces.md +150 -0
- package/doc/zh/examples/BackgroundJobs.md +618 -0
- package/doc/zh/examples/CommandLineTool.md +618 -0
- package/doc/zh/examples/DesktopApp.md +748 -0
- package/doc/zh/examples/DeviceService.md +546 -0
- package/doc/zh/examples/MCPServer.md +604 -0
- package/doc/zh/examples/Meeting.md +524 -0
- package/doc/zh/examples/README.md +15 -0
- package/doc/zh/examples/RestAPI.md +458 -0
- package/doc/zh/examples/ServiceCalls.md +765 -0
- package/doc/zh/examples/WebApp.md +520 -0
- package/package.json +46 -17
- package/doc/en/ActionPattern.md +0 -408
- package/doc/en/Core.md +0 -312
- package/doc/en/ORMIntegration.md +0 -341
- package/doc/zh/ActionPattern.md +0 -334
- package/doc/zh/Core.md +0 -256
- package/doc/zh/ORMIntegration.md +0 -319
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# Upgrading from Lakutata 2.x
|
|
2
|
+
|
|
3
|
+
This page brings a project from Lakutata 2.x (the last release is 2.0.126) to 3.x: how to tell the versions apart, the
|
|
4
|
+
steps, the renamed and moved APIs, the behavior that changes without a compile error, and how to check the result.
|
|
5
|
+
|
|
6
|
+
It is for the projects that depend on the package `lakutata` at version 2. A project on Lakutata 1.x depends instead on
|
|
7
|
+
`@lakutata/core` at version 1 and on `@lakutata-component/*` or `@lakutata-module/*` packages: it follows
|
|
8
|
+
[UpgradingFrom1](UpgradingFrom1.md), not this page.
|
|
9
|
+
|
|
10
|
+
## 1. Telling 2.x from 3.x
|
|
11
|
+
|
|
12
|
+
| Sign | 2.x | 3.x |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| `lakutata` in `package.json` | `^2.…` | `^3.…` |
|
|
15
|
+
| `node_modules/lakutata/package.json` | No `dependencies` (everything bundled) | Depends on `@lakutata/core`, `@lakutata/entrypoint`… at its own version |
|
|
16
|
+
| Helpers | `import {Delay} from 'lakutata/helper'` | `import {delay} from 'lakutata/utils'` |
|
|
17
|
+
| Exported functions | PascalCase: `BuildEntrypoints`, `BuildDatabaseOptions`, `MD5` | camelCase: `buildEntrypoints`, `buildDatabaseOptions`, `md5` |
|
|
18
|
+
| Transports | Written by hand: `BuildHTTPEntrypoint((module, routeMap, handler, registerDestroy) => …)` | Adapters: `expressHTTPEntrypoint()` from `lakutata/adapter/express`… |
|
|
19
|
+
| Subpaths of 3.x only | | `lakutata/utils`, `lakutata/security`, `lakutata/adapter/*`, `lakutata/com/{containerd,mutex,queue,i18n}` |
|
|
20
|
+
| Node.js | `>=20` | `^22.13.0 \|\| >=24.11.0` |
|
|
21
|
+
|
|
22
|
+
`npx @lakutata/cli info` prints the versions of the tool, of the framework and of Node.js, and the `lakutata` version
|
|
23
|
+
that the project declares and the one installed. There is no upgrade command: the steps below are done by hand.
|
|
24
|
+
|
|
25
|
+
## 2. The steps
|
|
26
|
+
|
|
27
|
+
- **Node.js** `^22.13.0 || >=24.11.0` (22.13 or a later 22.x, or 24.11 and later; not 23). Update `engines`, the CI
|
|
28
|
+
matrix and the base images of the Dockerfiles.
|
|
29
|
+
- **A TypeScript that knows `"module": "Node20"`** (the templates use TypeScript 6): `lakutata/tsconfig.json` now sets
|
|
30
|
+
it (2.x: `Node16`), and older compilers reject it.
|
|
31
|
+
- **`package.json`**: `"type": "module"`, `lakutata` `^3.0.0`, every `@lakutata/*` package at the same version (they
|
|
32
|
+
depend on each other's exact version), and `@lakutata/cli` in `devDependencies`.
|
|
33
|
+
- **A clean install**: remove `node_modules` and the lock file, then install. Upgrading in place can leave several
|
|
34
|
+
copies of `@lakutata/core`, whose classes and decorator metadata do not match (`instanceof` and injection fail across
|
|
35
|
+
them). `npm ls @lakutata/core` must print one version.
|
|
36
|
+
- **`tsconfig.json`** extends `lakutata/tsconfig.json` and sets its own `rootDir`, `outDir` and `include`. Keep
|
|
37
|
+
`include`: the published options set `allowJs`, so without it a configuration file at the root of the project
|
|
38
|
+
(`eslint.config.js`) is compiled too. A project that keeps its own options needs `experimentalDecorators`,
|
|
39
|
+
`emitDecoratorMetadata` and `useDefineForClassFields: false`.
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"extends": "lakutata/tsconfig.json",
|
|
44
|
+
"compilerOptions": {
|
|
45
|
+
"rootDir": "src",
|
|
46
|
+
"outDir": "build"
|
|
47
|
+
},
|
|
48
|
+
"include": ["src"]
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- **Relative imports** end in `.js` (`import {config} from './config/Config.js'`), as in the ES module projects of 2.x.
|
|
53
|
+
- **The libraries of the features used** are optional peer dependencies of `lakutata`, which the project installs (a
|
|
54
|
+
2.x project already has those that its own transports import); see [GettingStarted](GettingStarted.md), section 3:
|
|
55
|
+
|
|
56
|
+
| Used | Install |
|
|
57
|
+
|---|---|
|
|
58
|
+
| `lakutata/adapter/express` | `express` (4.21+ or 5), `@types/express` |
|
|
59
|
+
| `lakutata/adapter/fastify` | `fastify` 5 |
|
|
60
|
+
| `lakutata/adapter/commander` | `commander` (12 to 15); `@inquirer/prompts` (7 or 8) for the prompts |
|
|
61
|
+
| `lakutata/adapter/socket.io` | `socket.io` 4 |
|
|
62
|
+
| `lakutata/adapter/jsonrpc` over WebSocket | `ws` 8 |
|
|
63
|
+
| A database `type` | `pg`, `mysql2` (3.15.3+), `better-sqlite3` 12 (instead of `sqlite3`), `mongodb` 7, `mssql` 12, `oracledb`, `sql.js`… |
|
|
64
|
+
| A cache store | `redis` (4 or 5) or `@redis/client`, `memjs`, `mongodb`, `mysql2`, `pg`, `better-sqlite3` (the SQLite store used `sqlite3` in 2.x) |
|
|
65
|
+
| NATS, D-Bus | `@lakutata/nats`, `@lakutata/dbus`, at the version of `lakutata` |
|
|
66
|
+
|
|
67
|
+
- **Rename** with the tables of section 3, then build: none of the renamed functions has an alias, so the compiler
|
|
68
|
+
reports every import left. Then go through section 4, which the compiler does not report.
|
|
69
|
+
|
|
70
|
+
## 3. Renamed and moved
|
|
71
|
+
|
|
72
|
+
| 2.x | 3.x |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `lakutata/helper` | `lakutata/utils` (the same helpers, renamed below, plus `Time`, `cloneDeep`, `isSubclassOf`) |
|
|
75
|
+
| `Paginator`, `PaginatorHandlerResult` from `lakutata/helper` | `paginator`, `PaginatorHandlerResult` from `lakutata/dtos` |
|
|
76
|
+
| `IPatRun` from `lakutata` | Removed: the pattern matching is internal |
|
|
77
|
+
|
|
78
|
+
The other subpaths of 2.x keep their names: `lakutata`, `lakutata/orm`, `lakutata/dtos`,
|
|
79
|
+
`lakutata/com/{entrypoint,database,cacher,docker,monitor,logger}`, `lakutata/provider/{database,passwordHash}`,
|
|
80
|
+
`lakutata/decorator/{di,dto,ctrl,asst,orm}` and `lakutata/tsconfig.json`.
|
|
81
|
+
|
|
82
|
+
Every exported function is in camelCase, and the 2.x names have no alias. Most of them only lower their first letter
|
|
83
|
+
(`ArrayToSet` becomes `arrayToSet`):
|
|
84
|
+
|
|
85
|
+
- `lakutata/utils`: `arrayToSet`, `convertArrayLikeToIterable`, `convertArrayLikeToStream`, `delay`,
|
|
86
|
+
`getObjectNestingDepth`, `getObjectPropertyPaths`, `hexToIEEE754`, `hexToSigned`, `hexToUnsigned`, `isAbortError`,
|
|
87
|
+
`isEmptyObject`, `isGlobString`, `isNativeFunction`, `isPath`, `isPromise`, `isPromiseLike`, `isSymbol`, `isXML`,
|
|
88
|
+
`mergeArray`, `mergeMap`, `mergeSet`, `noCase`, `objectConstructor`, `objectHash`, `objectParentConstructor`,
|
|
89
|
+
`objectParentConstructors`, `objectPrototype`, `objectToMap`, `randomString`, `setToArray`, `signedToHex`,
|
|
90
|
+
`sortArray`, `sortKeys`, `sortObject`, `templating`, `uniqueArray`, `unsignedToHex`;
|
|
91
|
+
- `lakutata/com/entrypoint`: `buildEntrypoints`, `buildHTTPEntrypoint`, `buildCLIEntrypoint`, `buildServiceEntrypoint`;
|
|
92
|
+
- `lakutata/com/cacher`: `buildCacherOptions`; `lakutata/com/database` and `lakutata/provider/database`:
|
|
93
|
+
`buildDatabaseOptions`;
|
|
94
|
+
- `lakutata/com/docker`: `buildDockerConnectionOptions`, `buildDockerSocketConnectionOptions`,
|
|
95
|
+
`buildDockerHttpConnectionOptions`, `buildDockerHttpsConnectionOptions`, `buildDockerSSHConnectionOptions`,
|
|
96
|
+
`parseEnvToRecord`, `parseRepositoryTag`.
|
|
97
|
+
|
|
98
|
+
`As`, `DevNull` and `Glob` (with the new `GlobSync`) stay in PascalCase, as do the classes (`Time`, `URLBuilder`,
|
|
99
|
+
`UUID`, `ObjectPath`, `MessagePack`, `Statistics`, `IP`, `IPv4`, `IPv6`) and the decorators. The other renames:
|
|
100
|
+
|
|
101
|
+
| 2.x | 3.x |
|
|
102
|
+
|---|---|
|
|
103
|
+
| `GraceExit` | `gracefulExit` |
|
|
104
|
+
| `IsExists` | `pathExists` |
|
|
105
|
+
| `IsHtml` | `isHTML` |
|
|
106
|
+
| `NonceStr` | `nonceString` |
|
|
107
|
+
| `MD5`, `SHA1`, `SHA256` | `md5`, `sha1`, `sha256` |
|
|
108
|
+
| `IEEE754ToHex` | `ieee754ToHex` |
|
|
109
|
+
| `Split`, `ToLower`, `ToUpper` | Removed: `noCase(input).split(' ')` gives the words in lower case (`['']` for an input without words) |
|
|
110
|
+
| Type `SortOptions` | `SortArrayOptions` |
|
|
111
|
+
| Types `IPv4SubNet`, `IPv6SubNet` | `IPv4Subnet`, `IPv6Subnet` |
|
|
112
|
+
| Field `comperssed` of `IPv6Result` | `compressed` |
|
|
113
|
+
| `AccessDenyException` | `AccessDeniedException`: its `errno` is still `E_ACCESS_DENY`, its `err` (the class name) changes |
|
|
114
|
+
| Type `OnDelEventData` (`lakutata/com/cacher`) | `OnDeleteEventData`; the event is still `'del'` |
|
|
115
|
+
| `@Lifetime('APPLICATION_SINGLETON')`, `'MODULE_SINGLETON'` | `@Singleton()`: the lifetimes are `SINGLETON`, `SCOPED` and `TRANSIENT` |
|
|
116
|
+
| Static `databaseSymbol` of `Database` | Removed |
|
|
117
|
+
|
|
118
|
+
The ORM moves from TypeORM 0.3.24 to TypeORM 1.1.2, whose source is part of `@lakutata/orm` (there is no `typeorm`
|
|
119
|
+
package):
|
|
120
|
+
|
|
121
|
+
| 2.x | 3.x |
|
|
122
|
+
|---|---|
|
|
123
|
+
| `type: 'sqlite'` (driver `sqlite3`) | `type: 'better-sqlite3'` (driver `better-sqlite3`) |
|
|
124
|
+
| `PostgresConnectionOptions`, `MysqlConnectionOptions`… | `PostgresDataSourceOptions`, `MysqlDataSourceOptions`… (the old names are deprecated aliases) |
|
|
125
|
+
| `SqliteConnectionOptions` | `BetterSqlite3DataSourceOptions` |
|
|
126
|
+
| `AbstractRepository` | Removed: a `Repository`, or `repository.extend({...})` |
|
|
127
|
+
| `relations: ['author']`, `select: ['id']` | `relations: {author: true}`, `select: {id: true}` |
|
|
128
|
+
| `import {In} from 'typeorm'` | `import {In} from 'lakutata/orm'`: a `typeorm` package would be another copy |
|
|
129
|
+
|
|
130
|
+
The packages published apart from `lakutata` in 2.x are renamed the same way:
|
|
131
|
+
|
|
132
|
+
| 2.x | 3.x |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `SetupNatsServiceEntrypoint` (`@lakutata/nats`) | `natsServiceEntrypoint` |
|
|
135
|
+
| `BuildServiceProxy`, `JSONCodec`, `MessagePackCodec` | `buildServiceProxy`, `jsonCodec`, `messagePackCodec` |
|
|
136
|
+
| `CreateDBusError` (`@lakutata/dbus`) | `createDBusError` |
|
|
137
|
+
| D-Bus client events `online`, `offline`, `replaced`, `NameOwnerChanged`, `NameAcquired`, `NameLost` | `serviceOnline`, `serviceOffline`, `serviceReplaced`, `nameOwnerChanged`, `nameAcquired`, `nameLost` (a listener of an old name is never called) |
|
|
138
|
+
| D-Bus client options `advancedResponse`, `convertBigIntToNumber` | `decode`: `{types: 'all'}` keeps the D-Bus types, `{int64: 'bigint'}` the bigints |
|
|
139
|
+
|
|
140
|
+
## 4. Behavior that changes without a compile error
|
|
141
|
+
|
|
142
|
+
### The application and its objects
|
|
143
|
+
|
|
144
|
+
See [Application](Application.md) and [Objects](Objects.md).
|
|
145
|
+
|
|
146
|
+
- **The mode** (`app.mode()`) is the `MODE` environment variable, else `NODE_ENV`, else the `mode` option, else
|
|
147
|
+
`development`, written back to both variables. 2.x took the option (`development` by default) and overwrote
|
|
148
|
+
`NODE_ENV` with it: a deployment that sets `NODE_ENV=production` now runs in production, whatever the option says.
|
|
149
|
+
- **SIGTERM and SIGINT** end the application gracefully (`onDone` runs, the entrypoints stop, the objects are destroyed,
|
|
150
|
+
the exit code is 0); 2.x left them to Node.js. Remove the application's own `process.on('SIGTERM', …)`, or set
|
|
151
|
+
`shutdownSignals: false`. The `onDone` handlers receive a third argument, `{code, signal?}`.
|
|
152
|
+
- **A process with nothing left to do** (no server, connection or timer) ends the application gracefully: the `onDone`
|
|
153
|
+
handlers and the `destroy()` hooks run then too. In 2.x the process just ended.
|
|
154
|
+
- **A failed launch** exits with code 1 when there is no `onFatalException` handler. 2.x logged the error, and the
|
|
155
|
+
process kept running or ended with code 0.
|
|
156
|
+
- **The options are checked at launch** (`checkOptions`, on by default): an invalid value of a `@Configurable` property
|
|
157
|
+
fails the launch with an `InvalidObjectOptionsException` (`E_INVALID_OBJECT_OPTIONS`) that lists every invalid
|
|
158
|
+
option, even for an object never created; an option that is not a `@Configurable` property produces a warning. `''`
|
|
159
|
+
fails a required string: write `process.env.X || undefined`, not `|| ''`. A class that accepts other options declares
|
|
160
|
+
a static `acceptsOption(name, value)`.
|
|
161
|
+
- **The initializer of a `@Configurable` property is its default** (`protected readonly port: number = 8080`), and it
|
|
162
|
+
is validated like a configured value. 2.x replaced it with the default of the schema, or `undefined`.
|
|
163
|
+
- **New lifecycle hooks**: `startup()` (after `init()`, before the object is handed out), `launched()` (once the
|
|
164
|
+
application has launched), `beforeDestroy()` (when the teardown starts), and the static `acceptsOption()`. A 2.x class
|
|
165
|
+
with a member of one of these names is now called by the framework: rename the member.
|
|
166
|
+
- **A `Component` is locked to the singleton lifetime**: `@Transient()` or `@Scoped()` on a subclass throws a
|
|
167
|
+
`LifetimeLockedException` when the class is defined (2.x accepted it). Make it a `Provider`, which is scoped by
|
|
168
|
+
default and may be `@Singleton()` or `@Transient()`.
|
|
169
|
+
- **A built-in component declared with `class`** (`components: {log: {class: MyLogger}}`) replaces the preset one; 2.x
|
|
170
|
+
silently kept the preset. A declaration without `class` still configures the preset `log` or `entrypoint`.
|
|
171
|
+
- **A glob of objects** (`objects.anonymous`, `container.load()`) registers every exported `BaseObject` subclass of each
|
|
172
|
+
file (2.x: only the first export), and a file that fails to import fails the launch (2.x skipped it).
|
|
173
|
+
|
|
174
|
+
### Errors and values
|
|
175
|
+
|
|
176
|
+
- **Statuses**: `AccessDeniedException` has the `statusCode` 403, `ControllerActionNotFoundException` 404 and
|
|
177
|
+
`InvalidValueException` 400. All three had 500 in 2.x, so clients and tests that check for 500 change.
|
|
178
|
+
- **Messages**: an exception created without arguments takes the words of its class name (`Order not found exception`;
|
|
179
|
+
2.x gave `Ordernotfoundexception`); a single parameter fills `{0}` (`new OrderException('Order {0}', 7)`); the `%`
|
|
180
|
+
placeholders follow `util.format()` (`%s %d %i %f %j %o %O %c %%`), and a `%` before another character is kept.
|
|
181
|
+
- **DTOs are strict, as in 2.x**: the HTTP adapters give the query and the route parameters as strings, so a number or
|
|
182
|
+
a boolean read from them needs `.strict(false)`. `PaginationSearchDTO` now converts its `limit` and `offset`.
|
|
183
|
+
- **`Time`**: a date without a time (`new Time('2024-03-01')`) is midnight in the default time zone (2.x: UTC).
|
|
184
|
+
- **`PasswordHash`**: `hash()` throws a `PasswordTooLongException` above 72 bytes of UTF-8, and `validate()` returns
|
|
185
|
+
`false` for such a password. 2.x hashed its first 72 bytes, so those users can no longer log in. `saltRounds` is
|
|
186
|
+
between 4 and 31.
|
|
187
|
+
- **`Glob`** takes only `cwd`, `absolute`, `dot`, `nodir`, `follow`, `ignore` and `nocase`, and returns a sorted
|
|
188
|
+
`string[]` without duplicates: `withFileTypes` and the extended patterns (`+(a|b)`) are gone.
|
|
189
|
+
|
|
190
|
+
### The entrypoint
|
|
191
|
+
|
|
192
|
+
See [HTTP](HTTP.md), section 8, and [ServiceCalls](ServiceCalls.md).
|
|
193
|
+
|
|
194
|
+
- **The transport functions of 2.x still work** under `http`, `cli` and `service` (renamed `buildHTTPEntrypoint`…).
|
|
195
|
+
They are called when the entrypoint starts and their promise is awaited: the launch waits for the server, a rejection
|
|
196
|
+
(a port in use) fails it, and a function that awaits a long-running command (`await program.parseAsync()`) holds the
|
|
197
|
+
launch until the command ends. Call it without `await`, or move to `commanderCLIEntrypoint()`.
|
|
198
|
+
- **Moving to an adapter**: the middlewares and routes of an Express server written by hand go into the `before` hook of
|
|
199
|
+
`expressHTTPEntrypoint()`, its body limit into `body`, the input it built into `input`. The responses change: the HTTP
|
|
200
|
+
adapters answer an error with its status and `{errno, errMsg}`, and `jsonEnvelope({actionErrorStatus: 200, extra})`
|
|
201
|
+
gives the `{errno, errMsg, data}` envelope that 2.x applications often wrote themselves (example below). The service
|
|
202
|
+
adapters (HTTP service, Socket.IO, JSON-RPC, Electron) answer `{success, error, payload}`.
|
|
203
|
+
- **Nested service patterns are compared key by key**: `@ServiceAction({res: {name: 'user'}, act: 'get'})` matches that
|
|
204
|
+
nested value only. 2.x compared any object as `[object Object]`, so two actions that differed by a nested value
|
|
205
|
+
replaced each other.
|
|
206
|
+
- **`Controller.context`** is `CLIContext | HTTPContext | ServiceContext | BaseContext`: a function whose parameter is
|
|
207
|
+
the 2.x union no longer accepts `this.context`. Add `BaseContext`, or narrow it with `instanceof`.
|
|
208
|
+
- **Electron**: Lakutata 2.x had no Electron transport. 3.x serves the actions in the main process with
|
|
209
|
+
`electronIPCServiceEntrypoint()` from `lakutata/adapter/electron`: the preload script calls
|
|
210
|
+
`ipcRenderer.invoke('lakutata:service', message, clientId)`, answered with `{success, error, payload}`, and the events
|
|
211
|
+
come from `entrypoint.notify(clientId, event, ...args)` and `entrypoint.broadcast(event, ...args)`, with the id in
|
|
212
|
+
`this.context.clientId`. The Electron templates have a preload script and a `window.ipc` whose `invoke()` returns the
|
|
213
|
+
payload or throws; code that reads the raw responses of an IPC layer of its own changes.
|
|
214
|
+
|
|
215
|
+
### The components
|
|
216
|
+
|
|
217
|
+
See [Database](Database.md), [DockerIntegration](DockerIntegration.md) and [Logger](Logger.md).
|
|
218
|
+
|
|
219
|
+
- **Database**: a `null` or `undefined` value in a where condition is still left out (`invalidWhereValuesBehavior`),
|
|
220
|
+
so `findOneBy({id: undefined})` returns the first row: check the values taken from requests. With table or column
|
|
221
|
+
names outside ASCII, TypeORM 1 names indices and constraints differently: `namingStrategy: new NamingStrategyV03()`
|
|
222
|
+
(from `lakutata/orm`) keeps the names of TypeORM 0.3.
|
|
223
|
+
- **Docker**: `exec()` resolves `{exitCode, stdout, stderr}` (2.x: the output as a string); a string `cmd` of `exec()`
|
|
224
|
+
and `createTTY()` runs through `/bin/sh -c`, and `createTTY()` runs `/bin/sh` by default (2.x: `bash`).
|
|
225
|
+
- **Logger**: colors only when the standard output is a terminal and `NO_COLOR` is not set (2.x: always); the
|
|
226
|
+
`Authorization`, `Cookie`, `Set-Cookie` and `Proxy-Authorization` headers of a logged request are redacted
|
|
227
|
+
(`redactedHeaders: []` keeps them); a `level` that is not a level fails the launch.
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import {Application, type ApplicationExitReason, type ApplicationOptions, type ILogger} from 'lakutata'
|
|
231
|
+
import {buildEntrypoints, Controller} from 'lakutata/com/entrypoint'
|
|
232
|
+
import {GET} from 'lakutata/decorator/ctrl'
|
|
233
|
+
import {expressHTTPEntrypoint, jsonEnvelope} from 'lakutata/adapter/express'
|
|
234
|
+
import {delay, md5} from 'lakutata/utils'
|
|
235
|
+
|
|
236
|
+
class HashController extends Controller {
|
|
237
|
+
//2.x: Delay() and MD5() from 'lakutata/helper'
|
|
238
|
+
@GET('/hash')
|
|
239
|
+
public async hash(): Promise<{ hash: string }> {
|
|
240
|
+
await delay(10)
|
|
241
|
+
return {hash: md5('text').toString('hex')}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
Application
|
|
246
|
+
.run((): ApplicationOptions => ({
|
|
247
|
+
id: 'com.example.app',
|
|
248
|
+
name: 'Example',
|
|
249
|
+
//Used only when the environment sets neither MODE nor NODE_ENV
|
|
250
|
+
mode: 'production',
|
|
251
|
+
components: {
|
|
252
|
+
entrypoint: buildEntrypoints({
|
|
253
|
+
controllers: [HashController],
|
|
254
|
+
http: expressHTTPEntrypoint({
|
|
255
|
+
port: 8080,
|
|
256
|
+
//The errors of the actions answered with 200 in {errno, errMsg, data, timestamp}
|
|
257
|
+
respond: jsonEnvelope({
|
|
258
|
+
actionErrorStatus: 200,
|
|
259
|
+
extra: (): Record<string, unknown> => ({timestamp: Date.now()})
|
|
260
|
+
})
|
|
261
|
+
})
|
|
262
|
+
})
|
|
263
|
+
},
|
|
264
|
+
bootstrap: ['entrypoint']
|
|
265
|
+
}))
|
|
266
|
+
.onDone((app: Application, log: ILogger, reason: ApplicationExitReason): void => {
|
|
267
|
+
log.info('%s stopped with code %d', app.appName, reason.code)
|
|
268
|
+
})
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## 5. Tools that help
|
|
272
|
+
|
|
273
|
+
- `lakutata create --name probe --template <template> --path <a scratch directory>` makes a fresh 3.x project of the
|
|
274
|
+
same kind, to compare its `package.json`, `tsconfig.json` and scripts, and for Electron its preload script. `--init`
|
|
275
|
+
creates it in an existing directory, keeps the files already there and lists the ones it did not copy.
|
|
276
|
+
- `lakutata migration generate|create|run|revert|show` runs the migrations (the `MIGRATION_*` environment variables of
|
|
277
|
+
2.x still work); `lakutata package bundle|exe|installer` packs the application. See [Command](Command.md).
|
|
278
|
+
|
|
279
|
+
## 6. Checking the upgrade
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
node --version # ^22.13.0 || >=24.11.0
|
|
283
|
+
npm ls lakutata @lakutata/core # one version of each
|
|
284
|
+
npm run build # tsc without errors: no 2.x import left
|
|
285
|
+
grep -rnE "process\.on\('SIG|\|\| ''|(startup|launched|beforeDestroy|acceptsOption)\(|AccessDenyException|=== 500" src
|
|
286
|
+
npm test
|
|
287
|
+
MODE=production npm start # the launch log, then Ctrl+C: onDone runs, exit code 0
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
- The `grep` lists what compiles but behaves differently: signal handlers, empty strings given to options, members
|
|
291
|
+
named like the new hooks, comparisons with the error name or the status of 2.x. Check each line it finds.
|
|
292
|
+
- The launch shows the expected mode, no `InvalidObjectOptionsException`, and no warning that ends with
|
|
293
|
+
`is not a configurable property, it is ignored`.
|
|
294
|
+
- An invalid input is answered with 400 `E_INVALID_VALUE`, a denied access with 403 `E_ACCESS_DENY`, an unknown route
|
|
295
|
+
with 404, in the envelope that the clients expect.
|
|
296
|
+
- A broken configuration makes the process exit with code 1.
|
|
297
|
+
- `lakutata migration generate` against a copy of the production database generates no change: a change there means a
|
|
298
|
+
difference of naming or of entities (see `NamingStrategyV03` in section 4), not a migration to run.
|
|
299
|
+
|
|
300
|
+
## 7. Common mistakes
|
|
301
|
+
|
|
302
|
+
- A search-and-replace of a renamed function also changes string literals, log messages, object keys and longer names
|
|
303
|
+
that contain it (`Delay` in `RequestDelay`): match whole words and read the diff. The new camelCase name can also
|
|
304
|
+
collide with a local variable or a parameter (`delay`, `paginator`, `md5`).
|
|
305
|
+
- One process must load one copy of the framework. `lakutata` ships ES module and CommonJS builds as separate files: an
|
|
306
|
+
ES module application that loads a CommonJS-only library requiring `lakutata` gets two copies, whose classes and
|
|
307
|
+
metadata differ. Keep the libraries built for both module systems, or the whole process on one of them.
|
|
308
|
+
- `process.env.X || ''` for a required option fails the launch; `|| undefined` lets the default apply.
|
|
309
|
+
- A member named `startup`, `launched`, `beforeDestroy` or `acceptsOption` is now called by the framework.
|
|
310
|
+
- The `NODE_ENV` of the deployment, not the `mode` option, decides the mode.
|
|
311
|
+
- The SQLite files of 2.x open with `type: 'better-sqlite3'`. `better-sqlite3` is a native addon built for one version
|
|
312
|
+
of Node.js: an Electron application needs the build made for its Electron.
|
|
313
|
+
- The hashes that 2.x made of passwords longer than 72 bytes no longer validate: plan a password reset for those users.
|
|
314
|
+
- Code that checks `statusCode === 500` for a denied access, or compares `err === 'AccessDenyException'`, no longer
|
|
315
|
+
matches: compare the `errno`.
|