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.
- package/README.md +11 -11
- 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/cjs/exports/ReactNativeAdapter.d.ts +1 -0
- package/dist/cjs/exports/ReactNativeAdapter.js +20 -0
- package/dist/esm/exports/NwjsAdapter.js +1 -0
- package/dist/esm/exports/ReactNativeAdapter.js +1 -0
- package/dist/types/exports/NwjsAdapter.d.ts +1 -0
- package/dist/types/exports/ReactNativeAdapter.d.ts +1 -0
- package/doc/en/Application.md +6 -1
- package/doc/en/Barcode.md +386 -0
- package/doc/en/Cache.md +2 -1
- package/doc/en/Command.md +20 -4
- package/doc/en/ContainerdIntegration.md +1 -1
- package/doc/en/Database.md +4 -0
- package/doc/en/DatabaseDrivers.md +75 -4
- package/doc/en/Entrypoint.md +4 -2
- package/doc/en/Exceptions.md +1 -1
- package/doc/en/FFI.md +1 -1
- package/doc/en/GRPC.md +1 -1
- package/doc/en/GettingStarted.md +16 -2
- package/doc/en/HTTP.md +1 -0
- package/doc/en/I18n.md +1 -1
- package/doc/en/Installers.md +34 -6
- package/doc/en/Math.md +1 -1
- package/doc/en/Migrations.md +2 -2
- package/doc/en/NativeAddons.md +12 -2
- package/doc/en/Overview.md +14 -11
- package/doc/en/Packaging.md +8 -1
- package/doc/en/ProjectStructure.md +18 -3
- package/doc/en/ServiceCalls.md +85 -5
- package/doc/en/UpgradingFrom1.md +2 -2
- package/doc/en/UpgradingFrom2.md +4 -4
- package/doc/en/WebRTC.md +4 -4
- package/doc/en/examples/MobileApp.md +730 -0
- package/doc/en/examples/README.md +2 -0
- package/doc/en/examples/TrayApp.md +1061 -0
- package/doc/zh/Application.md +3 -1
- package/doc/zh/Barcode.md +313 -0
- package/doc/zh/Cache.md +1 -1
- package/doc/zh/Command.md +12 -3
- package/doc/zh/ContainerdIntegration.md +1 -1
- package/doc/zh/Database.md +2 -0
- package/doc/zh/DatabaseDrivers.md +52 -2
- package/doc/zh/Entrypoint.md +3 -1
- package/doc/zh/Exceptions.md +1 -1
- package/doc/zh/FFI.md +1 -1
- package/doc/zh/GRPC.md +1 -1
- package/doc/zh/GettingStarted.md +12 -2
- package/doc/zh/HTTP.md +1 -0
- package/doc/zh/I18n.md +1 -1
- package/doc/zh/Installers.md +16 -5
- package/doc/zh/Math.md +1 -1
- package/doc/zh/Migrations.md +1 -1
- package/doc/zh/NativeAddons.md +4 -2
- package/doc/zh/Overview.md +5 -4
- package/doc/zh/Packaging.md +6 -1
- package/doc/zh/ProjectStructure.md +5 -2
- package/doc/zh/ServiceCalls.md +65 -2
- package/doc/zh/UpgradingFrom1.md +2 -2
- package/doc/zh/UpgradingFrom2.md +3 -3
- package/doc/zh/WebRTC.md +1 -1
- package/doc/zh/examples/MobileApp.md +671 -0
- package/doc/zh/examples/README.md +2 -0
- package/doc/zh/examples/TrayApp.md +980 -0
- 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
|
|
39
|
-
use ES modules (`"type": "module"`), as the templates do; the package also ships CommonJS builds. The
|
|
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`, `
|
|
148
|
-
`Packaging.md`, `Installers.md`; and the examples, in `doc/en/examples` and `doc/zh/examples`:
|
|
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
|
|
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';
|
package/doc/en/Application.md
CHANGED
|
@@ -12,7 +12,7 @@ options:
|
|
|
12
12
|
|
|
13
13
|
| Option | Meaning |
|
|
14
14
|
|---|---|
|
|
15
|
-
| `id`, `name` | Required. The ID (also the `process.title
|
|
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.
|
|
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
|
|
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,
|
|
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
|