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
package/README.md CHANGED
@@ -19,10 +19,10 @@ English | [中文](README.zh-CN.md)
19
19
  # lakutata
20
20
 
21
21
  An IoC-based application framework for Node.js and TypeScript, with one programming model for web services, CLI tools,
22
- desktop (Electron) applications and embedded devices. An application is a tree of objects (components, providers,
22
+ desktop (Electron, NW.js) applications and embedded devices. An application is a tree of objects (components, providers,
23
23
  controllers) that a container creates. DTOs validate their options and inputs, and transports expose the controllers:
24
- HTTP (Express, Fastify), CLI (Commander), service RPC (HTTP, Socket.IO, JSON-RPC 2.0, gRPC, NATS, Electron IPC),
25
- schedules and message queues. One controller method can serve all of them at once.
24
+ HTTP (Express, Fastify), CLI (Commander), service RPC (HTTP, Socket.IO, JSON-RPC 2.0, gRPC, NATS, Electron IPC, NW.js,
25
+ React Native), schedules and message queues. One controller method can serve all of them at once.
26
26
 
27
27
  This package is the framework itself: it re-exports the `@lakutata/*` packages under the subpaths listed below and
28
28
  presets two components in every application, `log` and `entrypoint`. The typings of each subpath document every
@@ -31,13 +31,13 @@ declaration with examples, and each `@lakutata/*` package has its own README.
31
31
  ## Installation
32
32
 
33
33
  ```shell
34
- npx @lakutata/cli create --name my-app # a new project from a template (web, service, CLI, Electron…)
34
+ npx @lakutata/cli create --name my-app # a new project from a template (web, service, CLI, Electron, NW.js…)
35
35
  npm install lakutata # or the framework in an existing project
36
36
  ```
37
37
 
38
- The framework needs Node.js `^22.13.0 || >=24.11.0`: 22.13 or a later 22.x, or 24.11 and later (not 23). Projects
39
- use ES modules (`"type": "module"`), as the templates do; the package also ships CommonJS builds. The TypeScript
40
- options that the framework needs are published as `lakutata/tsconfig.json`:
38
+ The framework needs Node.js 18.10 or later (`>=18.10.0`); a few features need a newer one (see Getting Started).
39
+ Projects use ES modules (`"type": "module"`), as the templates do; the package also ships CommonJS builds. The
40
+ TypeScript options that the framework needs are published as `lakutata/tsconfig.json`:
41
41
 
42
42
  ```json
43
43
  {
@@ -108,7 +108,7 @@ Application
108
108
  | `lakutata/decorator/ctrl` | `@lakutata/entrypoint` | `@HTTPAction`, `@GET`…`@OPTIONS`, `@CLIAction`, `@ServiceAction`, `@Schedule`, `@Prefix`, `@UseMiddlewares` |
109
109
  | `lakutata/adapter/express`, `lakutata/adapter/fastify` | `@lakutata/entrypoint` | The HTTP transports, `staticUI` (Express only) |
110
110
  | `lakutata/adapter/commander` | `@lakutata/entrypoint` | The CLI transport |
111
- | `lakutata/adapter/socket.io`, `lakutata/adapter/jsonrpc`, `lakutata/adapter/electron` | `@lakutata/entrypoint` | The service transports |
111
+ | `lakutata/adapter/socket.io`, `lakutata/adapter/jsonrpc`, `lakutata/adapter/electron`, `lakutata/adapter/nwjs`, `lakutata/adapter/react-native` | `@lakutata/entrypoint` | The service transports |
112
112
  | `lakutata/com/logger` | `@lakutata/logger` | The `Logger` component preset as `log` (pino-compatible JSON lines) |
113
113
  | `lakutata/orm`, `lakutata/decorator/orm` | `@lakutata/orm` | TypeORM, its decorators |
114
114
  | `lakutata/com/database`, `lakutata/provider/database` | `@lakutata/orm` | The `Database` component and provider, the migration commands |
@@ -144,9 +144,9 @@ on. Each package's README lists its exceptions.
144
144
  `Entrypoint.md`, `Middlewares.md`, `HTTP.md`, `CLI.md`, `ServiceCalls.md`, `GRPC.md`, `NATS.md`, `Schedules.md`,
145
145
  `Queue.md`, `Database.md`, `DatabaseDrivers.md`, `MongoEmbedded.md`, `Entities.md`, `Migrations.md`, `Cache.md`,
146
146
  `Mutex.md`, `I18n.md`, `DockerIntegration.md`, `ContainerdIntegration.md`, `Monitor.md`, `Security.md`, `DBus.md`,
147
- `MCP.md`, `WebRTC.md`, `FFI.md`, `Math.md`, `Netif.md`, `Command.md`, `Workspaces.md`, `NativeAddons.md`,
148
- `Packaging.md`, `Installers.md`; and the examples, in `doc/en/examples` and `doc/zh/examples`: whole applications
149
- built from the templates.
147
+ `MCP.md`, `WebRTC.md`, `FFI.md`, `Barcode.md`, `Math.md`, `Netif.md`, `Command.md`, `Workspaces.md`,
148
+ `NativeAddons.md`, `Packaging.md`, `Installers.md`; and the examples, in `doc/en/examples` and `doc/zh/examples`:
149
+ whole applications built from the templates.
150
150
  - `@lakutata/cli`: creating, building and packaging applications.
151
151
  - The official Agent Skill for AI coding assistants that work on lakutata projects:
152
152
  [lakutata.com/skill](https://lakutata.com/skill/).
package/README.zh-CN.md CHANGED
@@ -18,18 +18,18 @@
18
18
 
19
19
  # lakutata
20
20
 
21
- 一个基于 IoC 的 Node.js 和 TypeScript 应用框架,让 Web 服务、命令行工具、桌面(Electron)应用和嵌入式设备共用同一套编程模型。应用是一棵由容器创建的对象树(组件、提供者、控制器)。DTO 校验对象的选项和输入,各种传输方式把控制器暴露出去:HTTP(Express、Fastify)、CLI(Commander)、服务调用(HTTP、Socket.IO、JSON-RPC 2.0、gRPC、NATS、Electron IPC)、定时任务和消息队列。同一个控制器方法可以同时通过所有这些方式调用。
21
+ 一个基于 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)、定时任务和消息队列。同一个控制器方法可以同时通过所有这些方式调用。
22
22
 
23
23
  这个包就是框架本身:它通过下面列出的子路径重新导出各个 `@lakutata/*` 包,并为每个应用预置两个组件:`log` 和 `entrypoint`。各子路径的类型声明中,每一项都附有说明和示例,每个 `@lakutata/*` 包也都有自己的 README。
24
24
 
25
25
  ## 安装
26
26
 
27
27
  ```shell
28
- npx @lakutata/cli create --name my-app # 用模板创建新项目(Web、服务、CLI、Electron……)
28
+ npx @lakutata/cli create --name my-app # 用模板创建新项目(Web、服务、CLI、Electron、NW.js……)
29
29
  npm install lakutata # 或者在已有项目中安装框架
30
30
  ```
31
31
 
32
- 框架需要 Node.js `^22.13.0 || >=24.11.0`:22.13 或更高的 22.x 版本,或者 24.11 及以上(不支持 23)。项目使用 ES 模块(`"type": "module"`),模板也是如此;这个包也提供 CommonJS 版本。框架需要的 TypeScript 选项发布为 `lakutata/tsconfig.json`:
32
+ 框架需要 Node.js 18.10 或更高版本(`>=18.10.0`),少数功能需要较新的版本(见快速开始)。项目使用 ES 模块(`"type": "module"`),模板也是如此;这个包也提供 CommonJS 版本。框架需要的 TypeScript 选项发布为 `lakutata/tsconfig.json`:
33
33
 
34
34
  ```json
35
35
  {
@@ -97,7 +97,7 @@ Application
97
97
  | `lakutata/decorator/ctrl` | `@lakutata/entrypoint` | `@HTTPAction`、`@GET`…`@OPTIONS`、`@CLIAction`、`@ServiceAction`、`@Schedule`、`@Prefix`、`@UseMiddlewares` |
98
98
  | `lakutata/adapter/express`、`lakutata/adapter/fastify` | `@lakutata/entrypoint` | HTTP 传输、`staticUI`(仅 Express) |
99
99
  | `lakutata/adapter/commander` | `@lakutata/entrypoint` | CLI 传输 |
100
- | `lakutata/adapter/socket.io`、`lakutata/adapter/jsonrpc`、`lakutata/adapter/electron` | `@lakutata/entrypoint` | 服务传输 |
100
+ | `lakutata/adapter/socket.io`、`lakutata/adapter/jsonrpc`、`lakutata/adapter/electron`、`lakutata/adapter/nwjs`、`lakutata/adapter/react-native` | `@lakutata/entrypoint` | 服务传输 |
101
101
  | `lakutata/com/logger` | `@lakutata/logger` | `Logger` 组件,预置为 `log`(输出与 pino 兼容的 JSON 行) |
102
102
  | `lakutata/orm`、`lakutata/decorator/orm` | `@lakutata/orm` | TypeORM 及其装饰器 |
103
103
  | `lakutata/com/database`、`lakutata/provider/database` | `@lakutata/orm` | `Database` 组件和提供者、迁移命令 |
@@ -121,7 +121,7 @@ Application
121
121
  ## 另见
122
122
 
123
123
  - 网站 <https://lakutata.com/zh/>:指南、示例和 API 参考,中英文都有。
124
- - 指南在这个包的 `doc/zh`(中文)和 `doc/en`(英文)中:`Overview.md`、`GettingStarted.md`、`ProjectStructure.md`、`UpgradingFrom2.md`、`UpgradingFrom1.md`、`Application.md`、`Objects.md`、`Injection.md`、`DTOValidation.md`、`Exceptions.md`、`Logger.md`、`Decorator.md`、`Time.md`、`HelperFunctions.md`、`Controllers.md`、`Entrypoint.md`、`Middlewares.md`、`HTTP.md`、`CLI.md`、`ServiceCalls.md`、`GRPC.md`、`NATS.md`、`Schedules.md`、`Queue.md`、`Database.md`、`DatabaseDrivers.md`、`MongoEmbedded.md`、`Entities.md`、`Migrations.md`、`Cache.md`、`Mutex.md`、`I18n.md`、`DockerIntegration.md`、`ContainerdIntegration.md`、`Monitor.md`、`Security.md`、`DBus.md`、`MCP.md`、`WebRTC.md`、`FFI.md`、`Math.md`、`Netif.md`、`Command.md`、`Workspaces.md`、`NativeAddons.md`、`Packaging.md`、`Installers.md`;示例在 `doc/zh/examples` 和 `doc/en/examples` 中:从模板构建的完整应用。
124
+ - 指南在这个包的 `doc/zh`(中文)和 `doc/en`(英文)中:`Overview.md`、`GettingStarted.md`、`ProjectStructure.md`、`UpgradingFrom2.md`、`UpgradingFrom1.md`、`Application.md`、`Objects.md`、`Injection.md`、`DTOValidation.md`、`Exceptions.md`、`Logger.md`、`Decorator.md`、`Time.md`、`HelperFunctions.md`、`Controllers.md`、`Entrypoint.md`、`Middlewares.md`、`HTTP.md`、`CLI.md`、`ServiceCalls.md`、`GRPC.md`、`NATS.md`、`Schedules.md`、`Queue.md`、`Database.md`、`DatabaseDrivers.md`、`MongoEmbedded.md`、`Entities.md`、`Migrations.md`、`Cache.md`、`Mutex.md`、`I18n.md`、`DockerIntegration.md`、`ContainerdIntegration.md`、`Monitor.md`、`Security.md`、`DBus.md`、`MCP.md`、`WebRTC.md`、`FFI.md`、`Barcode.md`、`Math.md`、`Netif.md`、`Command.md`、`Workspaces.md`、`NativeAddons.md`、`Packaging.md`、`Installers.md`;示例在 `doc/zh/examples` 和 `doc/en/examples` 中:从模板构建的完整应用。
125
125
  - `@lakutata/cli`:创建、构建和打包应用。
126
126
  - 官方 Agent Skill(智能体技能),供 AI 编程助手在 lakutata 项目中使用:<https://lakutata.com/zh/skill/>。
127
127
 
@@ -0,0 +1 @@
1
+ export * from '@lakutata/entrypoint/adapter/nwjs';
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("@lakutata/entrypoint/adapter/nwjs"), exports);
18
+
19
+ //The default of the entry: the entry itself (scripts/package-build/cjs-package.mjs)
20
+ if (!Object.prototype.hasOwnProperty.call(exports, 'default')) Object.defineProperty(exports, 'default', {value: exports})
@@ -0,0 +1 @@
1
+ export * from '@lakutata/entrypoint/adapter/react-native';
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("@lakutata/entrypoint/adapter/react-native"), exports);
18
+
19
+ //The default of the entry: the entry itself (scripts/package-build/cjs-package.mjs)
20
+ if (!Object.prototype.hasOwnProperty.call(exports, 'default')) Object.defineProperty(exports, 'default', {value: exports})
@@ -0,0 +1 @@
1
+ export * from '@lakutata/entrypoint/adapter/nwjs';
@@ -0,0 +1 @@
1
+ export * from '@lakutata/entrypoint/adapter/react-native';
@@ -0,0 +1 @@
1
+ export * from '@lakutata/entrypoint/adapter/nwjs';
@@ -0,0 +1 @@
1
+ export * from '@lakutata/entrypoint/adapter/react-native';
@@ -12,7 +12,7 @@ options:
12
12
 
13
13
  | Option | Meaning |
14
14
  |---|---|
15
- | `id`, `name` | Required. The ID (also the `process.title`) and the name of the application (`app.appId`, `app.appName`) |
15
+ | `id`, `name` | Required. The ID (also the `process.title`, except in NW.js) and the name of the application (`app.appId`, `app.appName`) |
16
16
  | `timezone` | An IANA time zone, or `'auto'` (default) for the system's; written to `TZ` |
17
17
  | `mode` | `'development'` or `'production'`, when the environment gives none (see section 4) |
18
18
  | `shutdownSignals` | The signals that end the application gracefully (default `['SIGTERM', 'SIGINT']`), or `false` |
@@ -55,6 +55,11 @@ Application
55
55
  })
56
56
  ```
57
57
 
58
+ The environment variables and the aliases are set when the application launches, before its options are read. An option
59
+ that uses an alias, such as `path.resolve('@data/app.sqlite')`, is therefore written in an options function given to
60
+ `run()`, as above: an object given to `run()` is made before the launch, when `@data` is not declared yet, and
61
+ `path.resolve()` gives `app.sqlite` in a directory named `@data` of the working directory.
62
+
58
63
  `app.exit(code)` ends the application gracefully with that exit code; `app.exit(true)` ends the process immediately with
59
64
  exit code 2, skipping the done handlers and the teardown.
60
65
 
@@ -0,0 +1,386 @@
1
+ # Lakutata Barcode
2
+
3
+ The package `@lakutata/barcode` generates barcodes and QR codes and reads them from images: product labels, shipping
4
+ labels, tickets and links printed as QR codes, scanned from uploaded photos or from the frames of a camera. It is not
5
+ part of `lakutata`: install it on its own. What it is like:
6
+
7
+ - **What it generates, it reads back**: 32 linear and matrix formats, from EAN-13, UPC, Code 128 and the GS1 DataBar to
8
+ QR Code, Data Matrix, PDF417, Aztec and MaxiCode. The formats only one side handles (the postal codes, Han Xin,
9
+ DotCode…) are left out.
10
+ - **Generated** as a PNG, an SVG or pixels, at the size, in the colors and the rotation, with the error correction asked
11
+ for.
12
+ - **Read** from PNG, JPEG, GIF, BMP, WebP and TGA images, or from pixels; every code of an image, with its position.
13
+ - **A component**, `Barcode`, injected without being registered, with the default options; the components registered
14
+ under names in the configuration have defaults, a time limit and a concurrency of their own. Every call can take a
15
+ time limit, and can be cancelled.
16
+ - **A native addon** built on [zint](https://zint.org.uk) and [zxing-cpp](https://github.com/zxing-cpp/zxing-cpp),
17
+ compiled when the package is installed on Linux, macOS and Windows, and by `lakutata mobile stage` for the mobile
18
+ applications of Android, iOS and HarmonyOS; it also runs in Bun and in the main process of Electron. Its work runs in
19
+ its own threads, never in the thread of JavaScript.
20
+
21
+ The README of `@lakutata/barcode` describes every option; this guide goes through what the package is for, with an
22
+ example of each use.
23
+
24
+ ```bash
25
+ npm install @lakutata/barcode
26
+ ```
27
+
28
+ The addon is compiled when the package is installed, so the machine needs Python 3 and a C++20 compiler (the Command
29
+ Line Tools of Xcode, `build-essential` or `build-base` on Linux, the Build Tools of Visual Studio on Windows). The
30
+ package manager has to run the install script of the package: npm runs it, and may warn that the script is "not yet
31
+ covered by allowScripts" (`npm install-scripts approve @lakutata/barcode` records that you allow it); Bun and pnpm run
32
+ it only for the packages they are told to trust (`trustedDependencies` in the package.json for Bun, `pnpm
33
+ approve-builds` for pnpm). The component needs `@lakutata/core`, a peer dependency that `lakutata` brings.
34
+
35
+ ## 1. Generating a code
36
+
37
+ `generate(format, content, options)` takes the content as text, encoded in UTF-8, or as bytes (a `Uint8Array`), and
38
+ gives a PNG by default:
39
+
40
+ ```typescript
41
+ import {Container} from 'lakutata'
42
+ import {Barcode, type RawImage} from '@lakutata/barcode'
43
+
44
+ const container: Container = new Container()
45
+ const barcode: Barcode = await container.get(Barcode)
46
+
47
+ const png: Buffer = await barcode.generate('QRCode', 'https://example.com/orders/123')
48
+ const svg: string = await barcode.generate('DataMatrix', 'LKT-1001', {output: 'svg'})
49
+ const pixels: RawImage = await barcode.generate('Code128', 'LKT-1001', {output: 'raw', scale: 2})
50
+ console.log(png.length, svg.length) //704 1008
51
+ console.log(pixels.width, pixels.height, pixels.channels) //264 116 1
52
+
53
+ await container.destroy()
54
+ ```
55
+
56
+ - The size of a matrix code (its version) and the way its content is encoded are chosen for the content: the smallest
57
+ symbol that holds it, unless `version` asks for one.
58
+ - The pixels are gray in black and white (1 byte a pixel), RGB with colors (3), RGBA with transparency (4): what a
59
+ canvas, an image library or a printer driver takes.
60
+ - A content the format cannot encode (letters in an EAN-13, too long for the version asked) throws
61
+ `BarcodeContentException`, with the reason given by zint.
62
+
63
+ In an application, the component is injected instead of taken from a container; for options of your own, register a
64
+ component under a name in the configuration and inject it by that name (section 5).
65
+
66
+ ## 2. Size, colors and printing
67
+
68
+ `scale` is the size of a module, the narrowest bar or a square of a matrix code: pixels in a PNG and in pixels, units
69
+ in an SVG. It is a whole number, so the bars of a PNG keep sharp edges. For print, the size of a module in millimetres
70
+ gives the scale: `scale = mm × dpi / 25.4`, rounded. An EAN-13 has modules of 0.33 mm at its nominal size, and may be
71
+ printed from 80% to 200% of it: on a thermal label printer of 203 dpi, a scale of 3 gives modules of 0.375 mm (114%);
72
+ at 300 dpi, a scale of 4 gives 0.34 mm. An SVG prints at any size: its scale only sets its units.
73
+
74
+ - `height` sets the height of the bars of a linear format, in modules, so it follows the scale.
75
+ - `showText` prints the content under the bars of a linear format, from a scale of 2 in a PNG or in pixels (the font
76
+ needs 2 pixels a module).
77
+ - `quietZone` keeps the margins the standard of the format asks for (by default); a scanner needs them to find the code,
78
+ so a label without them must leave the space around the code blank.
79
+ - `foreground` and `background` take `#RRGGBB` and `#RRGGBBAA`. Keep dark bars on a light background: the package
80
+ reads light on dark too (`tryInvert`), but many scanners do not. A transparent background is read as if it were white.
81
+ - `rotate` turns the code by 90, 180 or 270 degrees, clockwise, for a label printed sideways.
82
+ - `errorCorrection` lets a matrix code be read with part of it damaged or covered: from `'L'` (about 7%) to `'H'`
83
+ (about 30%) for QR Code. A higher level makes a larger code for the same content.
84
+
85
+ ## 3. EAN, UPC and GS1
86
+
87
+ The retail formats compute their check digit, and verify the one they are given:
88
+
89
+ ```typescript
90
+ import {Container} from 'lakutata'
91
+ import {Barcode, type BarcodeResult, BarcodeContentException} from '@lakutata/barcode'
92
+
93
+ const container: Container = new Container()
94
+ const barcode: Barcode = await container.get(Barcode)
95
+
96
+ //12 digits: the check digit is computed
97
+ const ean: Buffer = await barcode.generate('EAN13', '590123412345', {addOn: '12'})
98
+ const [product]: BarcodeResult[] = await barcode.read(ean)
99
+ console.log(product.format, product.text, product.addOn) //EAN13 5901234123457 12
100
+
101
+ //13 digits: the check digit is verified
102
+ await barcode.generate('EAN13', '5901234123458').catch((error: BarcodeContentException) => console.log(error.message))
103
+ //Error 275: Invalid check digit '8', expecting '7'
104
+
105
+ //A logistics label: the GTIN, the expiry date and the batch
106
+ const gs1: Buffer = await barcode.generate('Code128', '[01]09506000134352[17]261231[10]ABC123', {gs1: true})
107
+ const [label]: BarcodeResult[] = await barcode.read(gs1)
108
+ console.log(label.text, label.gs1, label.symbologyIdentifier) //(01)09506000134352(17)261231(10)ABC123 true ]C1
109
+
110
+ await container.destroy()
111
+ ```
112
+
113
+ - **UPC.** A UPC-A is an EAN-13 that starts with 0, and is read as an `EAN13` of 13 digits: `01234567890` gives
114
+ `0012345678905`. Asking for `formats: ['UPCA']` without `EAN13` gives the format `UPCA`, with the same 13 digits. An
115
+ ISBN is read as an `EAN13` too.
116
+ - **Add-ons.** The add-on of 2 or 5 digits (an issue number, a price) is in `addOn`, apart from the text. With
117
+ `addOn: 'require'`, a read keeps only the codes that have one.
118
+ - **GS1.** The application identifiers are written in brackets or in parentheses; with `gs1: true`, Code 128 becomes
119
+ GS1-128, and QR Code, Data Matrix and Aztec take GS1 data too. The DataBar Expanded formats always hold GS1 data; the
120
+ other DataBar formats hold a GTIN, given with or without its check digit. A read gives the data in parentheses, with
121
+ `gs1: true`.
122
+
123
+ The README of the package lists, format by format, the format it is read as and the text a read gives.
124
+
125
+ ## 4. Reading images
126
+
127
+ `read(image, options)` takes an image file (its bytes, a `Buffer` or a `Uint8Array`) or pixels, and gives every code it
128
+ finds, an empty array when there is none:
129
+
130
+ ```typescript
131
+ import {readFileSync} from 'node:fs'
132
+ import {Container} from 'lakutata'
133
+ import {Barcode, type BarcodeResult, type RawImage} from '@lakutata/barcode'
134
+
135
+ const container: Container = new Container()
136
+ const barcode: Barcode = await container.get(Barcode)
137
+
138
+ const photo: Buffer = readFileSync('qrcode.jpeg')
139
+ for (const result of await barcode.read(photo, {formats: ['QRCode', 'DataMatrix']})) {
140
+ console.log(result.format, result.text, result.position.topLeft, result.orientation)
141
+ //QRCode https://lakutata.com/fixtures { x: 16, y: 16 } 0
142
+ }
143
+
144
+ //Pixels: 1 byte a pixel in gray, 3 in RGB, 4 in RGBA (the ImageData of a canvas, a decoded video frame)
145
+ const frame: RawImage = await barcode.generate('DataMatrix', 'LKT-1001', {output: 'raw'})
146
+ const rgba: Uint8Array = new Uint8Array(frame.width * frame.height * 4)
147
+ frame.data.forEach((gray: number, index: number): void => rgba.set([gray, gray, gray, 255], index * 4))
148
+ const results: BarcodeResult[] = await barcode.read({data: rgba, width: frame.width, height: frame.height, channels: 4})
149
+ console.log(results.map((result: BarcodeResult): string => result.text)) //[ 'LKT-1001' ]
150
+
151
+ await container.destroy()
152
+ ```
153
+
154
+ - **Faster reads.** `formats` limits the search to the formats expected: reading a QR Code in a 1920×1080 frame takes
155
+ about 8 ms looking for every format, 1.4 ms looking for QR codes only (section 7). `pure: true` reads an image that
156
+ holds one code, straight, with nothing around it, such as an image made by `generate()`.
157
+ - **Harder images.** `tryHarder`, `tryRotate`, `tryInvert` and `tryDownscale` are on by default: they look again for
158
+ codes that are small, turned, light on dark or in large images. Turned off, a read of images known to be easy is
159
+ faster.
160
+ - **What is not read.** A code whose modules are less than about 2 pixels wide, a blurred or a glaring photo, a code cut
161
+ by the edge of the image. The read then gives fewer codes, or none: it throws only for an image it cannot decode
162
+ (`BarcodeImageException`).
163
+ - **Large images** of more than 50 million pixels are refused before their pixels are decoded, so an uploaded file
164
+ cannot make the process allocate gigabytes.
165
+ - **The bytes.** `bytes` holds the content as it is encoded; `text` is its readable form (with the check digits, GS1 in
166
+ parentheses), and `symbologyIdentifier` tells how to read the bytes (`]Q1` a QR Code, `]C1` GS1-128…).
167
+
168
+ ## 5. In an application
169
+
170
+ The component is injected without being registered (`@Inject(Barcode)`), with the default options. For options of your
171
+ own, register a component in the `components` of the configuration: `buildBarcodeOptions()` declares it under a name,
172
+ with defaults for `generate()` and `read()`, a time limit and a concurrency; an application may register several, under
173
+ different names, each with its own options. The application below registers two, `labels` to print labels and
174
+ `scanner` to read photos:
175
+
176
+ ```typescript
177
+ import {Application, Component} from 'lakutata'
178
+ import {Inject} from 'lakutata/decorator/di'
179
+ import {type Barcode, type BarcodeResult, buildBarcodeOptions} from '@lakutata/barcode'
180
+
181
+ class Warehouse extends Component {
182
+ @Inject('labels')
183
+ protected readonly labels: Barcode
184
+
185
+ @Inject('scanner')
186
+ protected readonly scanner: Barcode
187
+
188
+ //The label of an item: a Code 128 with its number printed under the bars
189
+ public async label(sku: string): Promise<Buffer> {
190
+ return await this.labels.generate('Code128', sku)
191
+ }
192
+
193
+ //The codes of a photo: Code 128 and QR codes only
194
+ public async scan(photo: Buffer): Promise<BarcodeResult[]> {
195
+ return await this.scanner.read(photo)
196
+ }
197
+ }
198
+
199
+ Application
200
+ .run(() => ({
201
+ id: 'warehouse.app',
202
+ name: 'Warehouse',
203
+ components: {
204
+ //A label printer of 203 dpi: modules of 3 pixels, the content under the bars
205
+ labels: buildBarcodeOptions({defaults: {generate: {scale: 3, showText: true}}}),
206
+ //At most 2 seconds a photo
207
+ scanner: buildBarcodeOptions({defaults: {read: {formats: ['Code128', 'QRCode']}}, timeout: 2000}),
208
+ warehouse: {class: Warehouse}
209
+ }
210
+ }))
211
+ .onLaunched(async (app: Application): Promise<void> => {
212
+ const warehouse: Warehouse = await app.getObject<Warehouse>('warehouse')
213
+ const [code]: BarcodeResult[] = await warehouse.scan(await warehouse.label('LKT-1001'))
214
+ console.log(code.format, code.text) //Code128 LKT-1001
215
+ })
216
+ ```
217
+
218
+ - `buildBarcodeOptions()` declares the component under the name of your choice; the other objects inject it by that name
219
+ (`@Inject('labels')`) or get it with `getObject('labels')`.
220
+ - A registered component is injected by its name only: `@Inject(Barcode)` gives another component, autoloaded with the
221
+ default options; the options of the configuration do not apply to it.
222
+
223
+ A controller that serves the label of a product as a PNG and reads the codes of an uploaded image:
224
+
225
+ ```typescript
226
+ import express, {type Express, type Request} from 'express'
227
+ import {Application, DTO} from 'lakutata'
228
+ import {buildEntrypoints, Controller, type FileResult, Result} from 'lakutata/com/entrypoint'
229
+ import {GET, POST} from 'lakutata/decorator/ctrl'
230
+ import {Inject} from 'lakutata/decorator/di'
231
+ import {Expect} from 'lakutata/decorator/dto'
232
+ import {expressHTTPEntrypoint} from 'lakutata/adapter/express'
233
+ import {Barcode, type BarcodeResult, buildBarcodeOptions} from '@lakutata/barcode'
234
+
235
+ class LabelDTO extends DTO {
236
+ @Expect(DTO.String().pattern(/^\d{12,13}$/).required())
237
+ public ean: string
238
+ }
239
+
240
+ class ScanDTO extends DTO {
241
+ @Expect(DTO.Binary().required())
242
+ public image: Buffer
243
+ }
244
+
245
+ class LabelController extends Controller {
246
+ @Inject('labels')
247
+ protected readonly labels: Barcode
248
+
249
+ //GET /labels/590123412345: the EAN-13 of a product, as a PNG
250
+ @GET('/labels/:ean', LabelDTO)
251
+ public async label(input: LabelDTO): Promise<FileResult> {
252
+ return Result.file(await this.labels.generate('EAN13', input.ean), {contentType: 'image/png'})
253
+ }
254
+
255
+ //POST /scans, an image as its body: the codes found in it
256
+ @POST('/scans', ScanDTO)
257
+ public async scan(input: ScanDTO): Promise<{ format: string, text: string }[]> {
258
+ const results: BarcodeResult[] = await this.labels.read(input.image)
259
+ return results.map((result: BarcodeResult) => ({format: result.format, text: result.text}))
260
+ }
261
+ }
262
+
263
+ Application.run(() => ({
264
+ id: 'shop.app',
265
+ name: 'Shop',
266
+ components: {
267
+ labels: buildBarcodeOptions({defaults: {generate: {scale: 3}, read: {formats: ['EAN13', 'QRCode']}}, timeout: 10000}),
268
+ entrypoint: buildEntrypoints({
269
+ controllers: [LabelController],
270
+ http: expressHTTPEntrypoint({
271
+ port: 8080,
272
+ before: (app: Express): void => {
273
+ //The body of POST /scans is the image
274
+ app.post('/scans', express.raw({type: 'image/*', limit: '10mb'}))
275
+ },
276
+ input: (request: Request): Record<string, unknown> => Buffer.isBuffer(request.body) ? {image: request.body} : {...request.query, ...request.body}
277
+ })
278
+ })
279
+ },
280
+ bootstrap: ['entrypoint']
281
+ }))
282
+ ```
283
+
284
+ ```text
285
+ $ curl -s -o label.png localhost:8080/labels/590123412345
286
+ $ curl -s --data-binary @label.png -H 'Content-Type: image/png' localhost:8080/scans
287
+ [{"format":"EAN13","text":"5901234123457"}]
288
+ ```
289
+
290
+ - The options of a call go over the defaults of the component: `this.labels.generate('QRCode', url, {scale: 6})` keeps
291
+ the other defaults.
292
+ - The arguments are validated: an unknown format or a color not written `#RRGGBB` is refused with
293
+ `InvalidMethodAcceptException` before the addon is called.
294
+ - One component serves every controller at once: its calls do not depend on one another, except for its
295
+ `concurrency`.
296
+ - In a mobile application of React Native (the mobile template), the component runs in the Node.js of the application in
297
+ the same way: `lakutata mobile stage` compiles the addon from its sources for Android (with the NDK), iOS (Xcode) and
298
+ HarmonyOS (DevEco Studio).
299
+
300
+ ## 6. Time limits, cancellation and concurrency
301
+
302
+ Every call takes a `signal` (an `AbortSignal`) and a `timeout` in milliseconds, which count from the call; the
303
+ `timeout` of the component applies to the calls without one.
304
+
305
+ ```typescript
306
+ import {readFileSync} from 'node:fs'
307
+ import {Container} from 'lakutata'
308
+ import {Barcode, type BarcodeResult} from '@lakutata/barcode'
309
+
310
+ const container: Container = new Container()
311
+ const barcode: Barcode = await container.get(Barcode)
312
+ const photo: Buffer = readFileSync('qrcode.jpeg')
313
+
314
+ //At most 2 seconds, the wait for its turn included
315
+ const results: BarcodeResult[] = await barcode.read(photo, {timeout: 2000})
316
+
317
+ //Until the scan is cancelled
318
+ const scan: AbortController = new AbortController()
319
+ const reading: Promise<BarcodeResult[]> = barcode.read(photo, {signal: scan.signal})
320
+ scan.abort()
321
+ await reading.catch((error: Error) => console.log(error.constructor.name)) //BarcodeCancelledException
322
+
323
+ await container.destroy()
324
+ ```
325
+
326
+ - An operation that has not started yet is dropped. One that is running cannot be stopped, since zint and zxing-cpp
327
+ have no point where they could be interrupted: its promise is rejected at once (`BarcodeTimeoutException`,
328
+ `BarcodeCancelledException`), and its result is discarded when it ends. The operations are short (section 7); the
329
+ time limits are there for the large photos and the hostile uploads.
330
+ - `concurrency` limits the operations of a component running at once; the others wait for their turn, and their time
331
+ limit runs while they wait. Without it, the operations queue in the threads of the addon.
332
+ - The addon runs `LAKUTATA_BARCODE_THREADS` threads, by default one less than the processors, shared by every
333
+ component; it does not use the thread pool of libuv, so file and DNS operations are not held up.
334
+ - When the application stops, the component waits for its operations running, at most `drainTimeout` milliseconds
335
+ (10000 by default); the operations still waiting are rejected with `BarcodeCancelledException`, and so is every call
336
+ after that.
337
+
338
+ ## 7. Speed
339
+
340
+ Measured on an Apple M4 (Node.js 24, one call at a time unless stated):
341
+
342
+ | Operation | Time |
343
+ |---|---|
344
+ | A QR Code of a URL, as a PNG (148×148) | 0.19 ms |
345
+ | The same QR Code as pixels / as an SVG | 0.08 ms / 0.17 ms |
346
+ | An EAN-13 / a Code 128 as a PNG, with its text | 0.62 ms / 1.6 ms |
347
+ | Reading that QR Code PNG, every format / `formats: ['QRCode']` | 0.33 ms / 0.11 ms |
348
+ | Reading its pixels, `formats: ['QRCode']`, `pure: true` | 0.06 ms |
349
+ | Reading a QR Code in a 1920×1080 frame, every format / `formats: ['QRCode']` | 7.8 ms / 1.4 ms |
350
+ | 1000 QR Code PNGs generated at once / read at once (9 threads) | 63 ms / 33 ms |
351
+
352
+ A photo taken by a phone is slower to read than these images made by `generate()`: its code is smaller, turned and
353
+ blurred, and the `try…` options work harder on it.
354
+
355
+ ## 8. How it is checked, and what it does not do
356
+
357
+ The tests generate every format and read it back from its PNG and from its pixels, with random contents (text in several
358
+ scripts, and bytes), check the effect of each option, read images in the seven file formats, and read 240 damaged
359
+ images, which must give codes or `BarcodeImageException` and nothing else. They run on macOS (arm64: Node.js 24 and
360
+ 18.10, Bun 1.4), on Linux (arm64: Node.js 22 on Debian 12, Node.js 24 on Alpine) and on Windows 11 (arm64: Node.js 24);
361
+ the package also runs in the main process of Electron 44, and in the mobile applications of Android, iOS and HarmonyOS,
362
+ where every format and the seven file formats are read back on their emulators.
363
+
364
+ What it does not do, whether by design or not yet:
365
+
366
+ - The formats zxing-cpp cannot read back are not generated: the four-state postal codes, Han Xin, DotCode, Code One,
367
+ Grid Matrix, Codablock-F, Code 16K, Code 49 and the other stacked formats of zint. Telepen Numeric is left out too,
368
+ since a read takes it for Telepen unless it looks for nothing else.
369
+ - A code split over several symbols (structured append) is read symbol by symbol: `structuredAppend` gives the place of
370
+ each, and joining their contents is left to the application.
371
+ - The pixels are gray, RGB or RGBA; BGR and BGRA (some camera APIs) are converted by the application. A GIF is read
372
+ from its first frame.
373
+ - The output is PNG, SVG or pixels: EPS, EMF and PDF are not offered.
374
+
375
+ ## 9. Exceptions
376
+
377
+ All of them extend `BarcodeException`, which extends the `Exception` of lakutata.
378
+
379
+ | Exception | `errno` | When |
380
+ |---|---|---|
381
+ | `BarcodeContentException` | `E_BARCODE_CONTENT` | A content the format cannot encode, with the reason given by zint |
382
+ | `BarcodeOptionException` | `E_BARCODE_OPTION` | An option of the symbol the format does not take (`errorCorrection` for Code 128), or a value out of its range (`version: 41`) |
383
+ | `BarcodeImageException` | `E_BARCODE_IMAGE` | An image that cannot be decoded or has more than 50 million pixels; pixels that are not `width × height × channels` bytes |
384
+ | `BarcodeTimeoutException` | `E_BARCODE_TIMEOUT` | An operation past its time limit |
385
+ | `BarcodeCancelledException` | `E_BARCODE_CANCELLED` | An operation whose signal aborted, or a call to a destroyed component |
386
+ | `BarcodeException` | `E_BARCODE` | An internal error of the addon |
package/doc/en/Cache.md CHANGED
@@ -290,7 +290,8 @@ The values are kept as they are, not copied (section 7). Each process has its ow
290
290
  - The statements run synchronously: a statement that waits for a locked database blocks the event loop, up to
291
291
  `busyTimeout`.
292
292
  - `better-sqlite3` is a native addon built for one runtime: an Electron application needs its build for its version of
293
- Electron. On Bun, the store uses `bun:sqlite` instead.
293
+ Electron; an NW.js application has it built again with the headers of NW.js (0.111.3 or later: the earlier releases
294
+ cannot load it). On Bun, the store uses `bun:sqlite` instead.
294
295
 
295
296
  ### PostgreSQL
296
297
 
package/doc/en/Command.md CHANGED
@@ -13,8 +13,7 @@ npm install --global @lakutata/cli # or once for the machine, then: laku
13
13
  ```
14
14
 
15
15
  The templates add `@lakutata/cli` to the `devDependencies` of the project, so its scripts call `lakutata` directly and
16
- `npx lakutata` works inside it. The tool needs Node.js `^22.13.0 || >=24.11.0`: 22.13 or a later 22.x, or 24.11 and
17
- later (not 23).
16
+ `npx lakutata` works inside it. The tool needs Node.js 18.10 or later (`>=18.10.0`).
18
17
 
19
18
  - `lakutata --help` lists the commands, `lakutata help <command>` (or `lakutata <command> --help`) describes one, and
20
19
  `lakutata --version` prints the version of the tool. Without a command, the tool prints its help and exits with code
@@ -42,11 +41,12 @@ lakutata create --name tool --template cli --package-manager none --no-git
42
41
  |---|---|---|
43
42
  | `--name` | asked in a terminal | The name of the application, and of its directory |
44
43
  | `--id` | the name in lower case | The ID of the application and the name of its package |
44
+ | `--bundle-id` | `com.example.<name>` | The id of the application on the mobile systems (reverse domain name), for the mobile templates; the stores refuse `com.example` |
45
45
  | `--template` | `default` | An official template (`lakutata templates`), `npm:<package>[@<version>]`, `github:<owner>/<repo>[#<ref>]`, or a directory |
46
46
  | `--path` | `.` | Where the project directory is created (`<path>/<name>`) |
47
47
  | `--init` | | Create the project in `--path` itself, a directory that already exists |
48
48
  | `--description`, `--author`, `--license` | license `UNLICENSED` | The information of the package |
49
- | `--with` | | The optional features of the template, separated by commas (`i18n`, `installer`) |
49
+ | `--with` | | The optional features of the template, separated by commas (`i18n`, `installer`, `win7`) |
50
50
  | `--package-manager` | `npm`, or pnpm or yarn when one of them started the tool | `npm`, `pnpm`, `yarn`, or `none` to skip the installation |
51
51
  | `--no-git` | | Do not create a git repository |
52
52
  | `--yes` | | Do not ask for the features or for a confirmation |
@@ -99,6 +99,18 @@ adds a language. See [I18n](I18n.md#command-line-tools).
99
99
  that runs without Node.js: see [Packaging](Packaging.md). `lakutata package installer` makes its installers, which can
100
100
  register it as a service: see [Installers](Installers.md).
101
101
 
102
+ An NW.js application gets its version of NW.js from `lakutata nwjs use <version>` (`latest`, `win7` or a version, which
103
+ `lakutata nwjs versions` lists), which writes it into its `package.json`. `lakutata nwjs run [args...]` runs the compiled
104
+ application in the sdk flavor of that NW.js (with the DevTools), its native addons made to load in it, and gives it the
105
+ arguments. Its installers hold the normal flavor ([Installers](Installers.md), section 9).
106
+
107
+ A mobile application of React Native laid out as the `mobile-react` template runs in a libnode that its native module
108
+ embeds. `lakutata mobile node <android|ios|harmony>` downloads the libnode of a system from the releases of
109
+ lakutata/libnode; `lakutata mobile stage <platform>` makes the Node.js project of the application where the native
110
+ project of the system takes it; `lakutata mobile run <platform>` builds the application and runs it on an emulator, a
111
+ device or a simulator; `lakutata mobile deploy <platform>` puts the Node.js project into the application installed there
112
+ and starts the application again.
113
+
102
114
  ## 6. Environment variables
103
115
 
104
116
  | Variable | Meaning |
@@ -106,7 +118,11 @@ register it as a service: see [Installers](Installers.md).
106
118
  | `LAKUTATA_LANG` | The language of the tool (`en`, `zh-CN`, `zh-TW`) |
107
119
  | `LAKUTATA_NPM_REGISTRY` | The registry the templates come from (else `npm_config_registry`, else `https://registry.npmjs.org`) |
108
120
  | `LAKUTATA_GITHUB_DOWNLOAD` | Where the `github:` templates are downloaded from (`https://github.com` by default) |
109
- | `LAKUTATA_CACHE_DIR` | The cache of the tool: the templates, and the tools that make installers (`$XDG_CACHE_HOME/lakutata`, else `~/.cache/lakutata`) |
121
+ | `LAKUTATA_CACHE_DIR` | The cache of the tool: the templates, the tools that make installers, the runtimes of NW.js, and the libnode of the mobile applications with their native addons (`$XDG_CACHE_HOME/lakutata`, else `~/.cache/lakutata`) |
122
+ | `LAKUTATA_NWJS_MIRROR` | Where the runtimes of NW.js are downloaded from (`https://dl.nwjs.io` by default) |
123
+ | `LAKUTATA_NWJS_VERSIONS` | The list of the releases of NW.js (`https://nwjs.io/versions.json` by default) |
124
+ | `LAKUTATA_LIBNODE_MIRROR` | A mirror of the releases of lakutata/libnode, which the libnode of the mobile applications is downloaded from (`<mirror>/v<version>/libnode_<platform>_<arch>.tar.gz`) |
125
+ | `LAKUTATA_MOBILE_NODE_DIR` | A libnode of your own for the mobile applications, built with the repository of lakutata/libnode (a directory per system: `android`, `ios`, `harmony`), used instead of the releases |
110
126
  | `NO_COLOR` | Turns the colors off |
111
127
 
112
128
  The scripts and the applications the tool runs get the environment the tool was started with. The variables of the