@lakutata/cli 3.1.1 → 3.1.2

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 CHANGED
@@ -57,7 +57,8 @@ the command fails.
57
57
  A template package keeps its files in its `template/` directory. npm packages cannot hold some files under their real
58
58
  names, so `_gitignore`, `_npmrc`… are renamed `.gitignore`, `.npmrc`… when the project is created. The text files are
59
59
  filled with the information of the project: `{$APP_NAME}`, `{$APP_ID}`, `{$APP_DESC}`, `{$APP_AUTHOR}`,
60
- `{$APP_LICENSE}` and `{$LAKUTATA_VERSION}` (the version of the framework).
60
+ `{$APP_LICENSE}` and `{$LAKUTATA_VERSION}` (the minor version of the framework, `3.1.0` for 3.1.2: a patch releases
61
+ only the packages it changes, so `^{$LAKUTATA_VERSION}` finds every package of lakutata).
61
62
 
62
63
  A template can have optional features, which `lakutata create --with <features>` enables (or which the user chooses in
63
64
  a terminal):
@@ -213,6 +214,34 @@ Packages are still published with `npm publish -w <package>` (the packages `laku
213
214
  `lerna.json` with `"version": "independent"` cannot be bumped), write changelogs from the commits or cache builds:
214
215
  workspaces that need these keep lerna (or nx, turborepo) alongside it.
215
216
 
217
+ ## Native libraries
218
+
219
+ ### `lakutata ffi generate <header>`
220
+
221
+ Generate the class of a native library of `@lakutata/ffi` from its C header: its declarations, a property by function
222
+ with its TypeScript type, an interface by struct and union, its integer macros as constants. The header is run through
223
+ the preprocessor of the C compiler (`cc -E`; `cl /P` on Windows, in the Developer Command Prompt of Visual Studio); the
224
+ declarations of the library are kept (the ones of the header, and of the headers of its directory unless it is a
225
+ directory of the system), with the types of the system they use. The functions are then checked against the library:
226
+ the ones it does not export, or that cannot be called (a `va_list` parameter), are left out. The command lists what
227
+ it left out and why. The tool loads `@lakutata/ffi` from the project: install it there.
228
+
229
+ | Option | |
230
+ |---|---|
231
+ | `--library <name or file>` | the library the functions are checked against: a name, as `@Library` takes it, or a file; the name of the header by default (`sqlite3.h`: `sqlite3`; zlib needs `--library z`). When it cannot be loaded, the class is generated without the check, with a warning |
232
+ | `--class-name <name>` | the name of the class, the name of the header in PascalCase by default |
233
+ | `--out <file>` | the file written, `<class>.ts` in the current directory by default |
234
+ | `--force` | replace the file when it exists |
235
+ | `--include <directories...>` | the directories of the headers it includes (`-I`) |
236
+ | `--define <macros...>` | the macros defined before it: `NAME` or `NAME=value` (`-D`) |
237
+ | `--own <directories...>` | more directories whose headers are the ones of the library |
238
+ | `--compiler <compiler>` | the C compiler whose preprocessor reads the header: `cc`, `gcc`, `clang`, `cl` (the one of `CC` by default, else `cl` on Windows and `cc` elsewhere) |
239
+ | `--no-check` | do not check the functions against the library |
240
+
241
+ The class is generated for the platform the command runs on. A header does not say which pointers are output
242
+ parameters, nor which functions block: mark them in the file generated (`_Out_`, `{async: true}`). See the guide to
243
+ native libraries ([FFI](https://lakutata.com/guide/FFI), section 9).
244
+
216
245
  ## Bundles and executables
217
246
 
218
247
  These commands pack the application in the current directory, which must be compiled first (by `tsc`, which emits the
package/README.zh-CN.md CHANGED
@@ -42,7 +42,7 @@ lakutata create --name my-service --template service --with i18n --yes
42
42
 
43
43
  官方模板取与工具主版本号相同的最新正式版。下载过的模板会缓存起来:连不上仓库时,使用缓存中这个主版本的最新版本;缓存中也没有时,命令失败。
44
44
 
45
- 模板包把文件放在它的 `template/` 目录中。有些文件无法以真实的文件名放进 npm 包,所以 `_gitignore`、`_npmrc` 等文件会在创建项目时改名为 `.gitignore`、`.npmrc` 等。文本文件中会填入项目的信息:`{$APP_NAME}`、`{$APP_ID}`、`{$APP_DESC}`、`{$APP_AUTHOR}`、`{$APP_LICENSE}` 和 `{$LAKUTATA_VERSION}`(框架的版本)。
45
+ 模板包把文件放在它的 `template/` 目录中。有些文件无法以真实的文件名放进 npm 包,所以 `_gitignore`、`_npmrc` 等文件会在创建项目时改名为 `.gitignore`、`.npmrc` 等。文本文件中会填入项目的信息:`{$APP_NAME}`、`{$APP_ID}`、`{$APP_DESC}`、`{$APP_AUTHOR}`、`{$APP_LICENSE}` 和 `{$LAKUTATA_VERSION}`(框架的次版本,比如框架是 3.1.2 时为 `3.1.0`:补丁版只发布改动过的包,写成 `^{$LAKUTATA_VERSION}` 才能找到 lakutata 的每个包)。
46
46
 
47
47
  模板可以有可选功能,由 `lakutata create --with <features>` 启用(或者由用户在终端里选择):
48
48
 
@@ -130,6 +130,26 @@ lakutata create --name my-service --template service --with i18n --yes
130
130
 
131
131
  包仍然用 `npm publish -w <package>` 发布(`lakutata add` 创建的包是私有的:请先删掉 `"private": true`)。工具只读写 `lerna.json` 中的 `version`:包来自根目录 `package.json` 的 `workspaces`,或者 `pnpm-workspace.yaml`。它不支持给各个包分别设置版本号(`"version": "independent"` 的 `lerna.json` 无法递增版本),不根据提交生成变更日志,也不缓存构建结果:需要这些功能的工作区可以同时保留 lerna(或 nx、turborepo)。
132
132
 
133
+ ## 原生库
134
+
135
+ ### `lakutata ffi generate <header>`
136
+
137
+ 根据 C 头文件生成 `@lakutata/ffi` 原生库的类:包括它的声明、每个函数一个带 TypeScript 类型的属性、每个结构体和联合一个接口,整数宏作为常量。头文件先经过 C 编译器的预处理器(`cc -E`;在 Windows 上是 `cl /P`,请在 Visual Studio 的开发者命令提示符里运行);保留库自己的声明(头文件里的,以及它所在目录里其他头文件的,这个目录是系统目录时除外),以及这些声明用到的系统类型。然后用库检查这些函数:库里没有导出的函数和不能调用的函数(参数是 `va_list`)会被去掉。命令会列出去掉了哪些声明,以及原因。工具从项目里加载 `@lakutata/ffi`:请把它装在项目里。
138
+
139
+ | 选项 | |
140
+ |---|---|
141
+ | `--library <name or file>` | 用来检查函数的库:`@Library` 接受的库名,或一个文件;默认和头文件同名(`sqlite3.h` 对应 `sqlite3`;zlib 要写 `--library z`)。库加载不了时,照样生成类,只是不做检查,并给出警告 |
142
+ | `--class-name <name>` | 类的名称,默认是头文件名的 PascalCase 形式 |
143
+ | `--out <file>` | 要写入的文件,默认是当前目录下的 `<类名>.ts` |
144
+ | `--force` | 文件已存在时覆盖它 |
145
+ | `--include <directories...>` | 头文件引用的其他头文件所在的目录(`-I`) |
146
+ | `--define <macros...>` | 在头文件之前定义的宏:`NAME` 或 `NAME=value`(`-D`) |
147
+ | `--own <directories...>` | 其他属于这个库的头文件的目录 |
148
+ | `--compiler <compiler>` | 用来预处理头文件的 C 编译器:`cc`、`gcc`、`clang`、`cl`(默认取 `CC` 环境变量,没有时在 Windows 上是 `cl`,其他系统上是 `cc`) |
149
+ | `--no-check` | 不用库检查函数 |
150
+
151
+ 类是针对运行命令的平台生成的。头文件看不出哪些指针是输出参数,也看不出哪些函数会阻塞:请在生成的文件里自己标出来(`_Out_`、`{async: true}`)。见原生库的指南([FFI](https://lakutata.com/zh/guide/FFI) 第 9 节)。
152
+
133
153
  ## 单文件包和可执行文件
134
154
 
135
155
  这些命令打包当前目录中的应用,应用必须先编译好(用 `tsc`,它会生成装饰器的元数据)。实际的工作由 `@lakutata/builder` 完成:优先用项目中安装的那份(`npm install --save-dev @lakutata/builder`,需要打包应用的模板都是这样做的),否则用随工具一起安装的那份(全局安装工具时不会带上它,因为它只是工具的可选 peer 依赖)。脚本也可以调用它的函数(`bundleProject`、`buildExecutable`)来打包应用。
@@ -8,13 +8,14 @@ import { WorkspaceController } from './controllers/WorkspaceController.js';
8
8
  import { PackController } from './controllers/PackController.js';
9
9
  import { MigrationController } from './controllers/MigrationController.js';
10
10
  import { NativeController } from './controllers/NativeController.js';
11
+ import { FFIController } from './controllers/FFIController.js';
11
12
  import { I18nController } from './controllers/I18nController.js';
12
13
  import { LANGUAGE_VARIABLE, LanguageMiddleware } from './lib/Language.js';
13
14
  import { toolPackage } from './lib/Versions.js';
14
15
  /**
15
16
  * The controllers of the commands of the tool
16
17
  */
17
- export const CONTROLLERS = [CommandLineController, WorkspaceController, PackController, MigrationController, NativeController, I18nController];
18
+ export const CONTROLLERS = [CommandLineController, WorkspaceController, PackController, MigrationController, NativeController, FFIController, I18nController];
18
19
  /**
19
20
  * The messages of the tool in the languages besides English (its texts in the code): its help, its prompts
20
21
  */
@@ -22,7 +23,8 @@ export const CONTROLLERS = [CommandLineController, WorkspaceController, PackCont
22
23
  * The groups of commands: lakutata package --help lists the commands of the group package
23
24
  */
24
25
  export const COMMAND_GROUPS = {
25
- package: 'Package the compiled application: into one JavaScript file, an executable or its installers'
26
+ package: 'Package the compiled application: into one JavaScript file, an executable or its installers',
27
+ ffi: 'Generate the classes of the native libraries of @lakutata/ffi from their C headers'
26
28
  };
27
29
  export const MESSAGES = { 'zh-CN': zhCN, 'zh-TW': zhTW };
28
30
  /**
@@ -0,0 +1,54 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
11
+ import path from 'node:path';
12
+ import { Controller } from 'lakutata/com/entrypoint';
13
+ import { CLIAction } from 'lakutata/decorator/ctrl';
14
+ import { FFIGenerateDTO } from '../dtos/FFIGenerateDTO.js';
15
+ import { loadGenerator } from '../lib/FFI.js';
16
+ import { done, warn } from '../lib/Output.js';
17
+ /**
18
+ * The commands of the native libraries (@lakutata/ffi)
19
+ */
20
+ export class FFIController extends Controller {
21
+ async generate(input) {
22
+ const generator = await loadGenerator(process.cwd());
23
+ const { className, code, report } = await generator.generateNativeLibrary({
24
+ header: input.header,
25
+ library: input.library,
26
+ className: input.className,
27
+ include: input.include,
28
+ define: input.define,
29
+ own: input.own,
30
+ compiler: input.compiler,
31
+ check: input.check
32
+ });
33
+ const file = path.resolve(input.out ?? `${className}.ts`);
34
+ if (existsSync(file) && !input.force)
35
+ throw new Error(`${path.relative(process.cwd(), file)} exists: give --force to replace it`);
36
+ mkdirSync(path.dirname(file), { recursive: true });
37
+ writeFileSync(file, code);
38
+ const count = (number, noun) => `${number} ${noun}${number === 1 ? '' : 's'}`;
39
+ done(`Generated ${path.relative(process.cwd(), file)} (${className}): ${count(report.functions, 'function')}, ${count(report.variables, 'variable')}, ${count(report.interfaces, 'interface')}, ${count(report.constants, 'constant')}${report.library ? `, checked against ${report.library}` : ''}`);
40
+ if (!report.library)
41
+ warn(input.check ? `The functions were not checked against the library: ${report.unchecked}; give it with --library` : 'The functions were not checked against the library (--no-check)');
42
+ if (report.skipped.length)
43
+ warn(`Left out ${count(report.skipped.length, 'declaration')} of the library:`);
44
+ for (const skipped of report.skipped)
45
+ warn(` ${skipped.name}${skipped.file ? ` (${path.relative(process.cwd(), skipped.file)}:${skipped.line})` : ''}: ${skipped.reason}`);
46
+ return undefined;
47
+ }
48
+ }
49
+ __decorate([
50
+ CLIAction('ffi generate <header>', FFIGenerateDTO, { description: 'Generate the class of a native library from its C header: a property by function, an interface by struct, the constants, with their TypeScript types' }),
51
+ __metadata("design:type", Function),
52
+ __metadata("design:paramtypes", [FFIGenerateDTO]),
53
+ __metadata("design:returntype", Promise)
54
+ ], FFIController.prototype, "generate", null);
@@ -0,0 +1,53 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { DTO } from 'lakutata';
11
+ import { Expect } from 'lakutata/decorator/dto';
12
+ export class FFIGenerateDTO extends DTO {
13
+ }
14
+ __decorate([
15
+ Expect(DTO.String().required().description('The C header of the library')),
16
+ __metadata("design:type", String)
17
+ ], FFIGenerateDTO.prototype, "header", void 0);
18
+ __decorate([
19
+ Expect(DTO.String().optional().description('The library its functions are checked against: a name, as @Library takes it, or a file (the name of the header by default)')),
20
+ __metadata("design:type", String)
21
+ ], FFIGenerateDTO.prototype, "library", void 0);
22
+ __decorate([
23
+ Expect(DTO.String().optional().description('The name of the class (the name of the header in PascalCase by default)')),
24
+ __metadata("design:type", String)
25
+ ], FFIGenerateDTO.prototype, "className", void 0);
26
+ __decorate([
27
+ Expect(DTO.String().optional().description('The TypeScript file written (<class>.ts in the current directory by default)')),
28
+ __metadata("design:type", String)
29
+ ], FFIGenerateDTO.prototype, "out", void 0);
30
+ __decorate([
31
+ Expect(DTO.Array(DTO.String()).optional().description('The directories of the headers it includes (-I)')),
32
+ __metadata("design:type", Array)
33
+ ], FFIGenerateDTO.prototype, "include", void 0);
34
+ __decorate([
35
+ Expect(DTO.Array(DTO.String()).optional().description('The macros defined before it: NAME or NAME=value (-D)')),
36
+ __metadata("design:type", Array)
37
+ ], FFIGenerateDTO.prototype, "define", void 0);
38
+ __decorate([
39
+ Expect(DTO.Array(DTO.String()).optional().description('More directories whose headers are the ones of the library (the header already is, and so are the headers of its directory unless that is a directory of the system)')),
40
+ __metadata("design:type", Array)
41
+ ], FFIGenerateDTO.prototype, "own", void 0);
42
+ __decorate([
43
+ Expect(DTO.String().optional().description('The C compiler whose preprocessor reads the header: cc, gcc, clang, cl (the one of CC by default, else cl on Windows and cc elsewhere)')),
44
+ __metadata("design:type", String)
45
+ ], FFIGenerateDTO.prototype, "compiler", void 0);
46
+ __decorate([
47
+ Expect(DTO.Boolean().optional().default(true).description('Check the functions and the variables against the library, leaving out the ones it cannot give')),
48
+ __metadata("design:type", Boolean)
49
+ ], FFIGenerateDTO.prototype, "check", void 0);
50
+ __decorate([
51
+ Expect(DTO.Boolean().optional().default(false).description('Replace the file when it exists')),
52
+ __metadata("design:type", Boolean)
53
+ ], FFIGenerateDTO.prototype, "force", void 0);
@@ -0,0 +1,21 @@
1
+ import { createRequire } from 'node:module';
2
+ import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ /**
5
+ * The generator of a project: the one of the @lakutata/ffi installed in the project (its node_modules, the ones of its
6
+ * workspace), else the one installed with the tool
7
+ * @param project
8
+ */
9
+ export async function loadGenerator(project) {
10
+ for (const base of [path.join(project, 'package.json'), import.meta.url]) {
11
+ let entry;
12
+ try {
13
+ entry = createRequire(base).resolve('@lakutata/ffi/generator');
14
+ }
15
+ catch {
16
+ continue;
17
+ }
18
+ return await import(pathToFileURL(entry).href);
19
+ }
20
+ throw new Error('@lakutata/ffi is not installed: install it in the project (npm install @lakutata/ffi)');
21
+ }
@@ -17,7 +17,7 @@ import { TemplateCatalog } from './TemplateCatalog.js';
17
17
  import { runCommand } from './Processes.js';
18
18
  import { done, step, warn } from './Output.js';
19
19
  import { applyFeatureBlocks, applyFeatureFiles, projectPath } from './TemplateFeatures.js';
20
- import { frameworkVersion } from './Versions.js';
20
+ import { templateVersion } from './Versions.js';
21
21
  /**
22
22
  * The directories of a template which are not copied
23
23
  */
@@ -154,7 +154,7 @@ let ProjectCreator = class ProjectCreator extends Component {
154
154
  $APP_DESC: plan.description,
155
155
  $APP_AUTHOR: plan.author,
156
156
  $APP_LICENSE: plan.license,
157
- $LAKUTATA_VERSION: frameworkVersion()
157
+ $LAKUTATA_VERSION: templateVersion()
158
158
  }, new Set(plan.features));
159
159
  done('Project information filled');
160
160
  if (plan.packageManager !== 'none') {
@@ -15,6 +15,16 @@ export function toolPackage() {
15
15
  export function frameworkVersion() {
16
16
  return require('lakutata/package.json').version;
17
17
  }
18
+ /**
19
+ * The version of the packages of lakutata written in the projects created, after a ^: the minor version of the
20
+ * framework (3.1.0 for 3.1.2), since a patch releases only the packages it changes; a prerelease as it is, since all
21
+ * the packages are released with it
22
+ */
23
+ export function templateVersion() {
24
+ const version = frameworkVersion();
25
+ const release = /^(\d+)\.(\d+)\.\d+$/.exec(version);
26
+ return release ? `${release[1]}.${release[2]}.0` : version;
27
+ }
18
28
  /**
19
29
  * The latest version of a package on the registry, undefined when it cannot be fetched
20
30
  * @param name
@@ -132,6 +132,26 @@
132
132
  "target": "要修改的构建目标(默认是唯一的那个)"
133
133
  }
134
134
  },
135
+ "ffi": {
136
+ "description": "根据 C 头文件生成 @lakutata/ffi 原生库的类",
137
+ "generate": {
138
+ "description": "根据 C 头文件生成原生库的类:每个函数一个属性,每个结构体一个接口,加上常量,都带有 TypeScript 类型",
139
+ "arguments": {
140
+ "header": "库的 C 头文件"
141
+ },
142
+ "options": {
143
+ "library": "用来检查函数的库:@Library 接受的库名,或一个文件(默认按头文件的名字)",
144
+ "className": "类的名称(默认是头文件名的 PascalCase 形式)",
145
+ "out": "要写入的 TypeScript 文件(默认是当前目录下的 <类名>.ts)",
146
+ "include": "头文件引用的其他头文件所在的目录(-I)",
147
+ "define": "在头文件之前定义的宏:NAME 或 NAME=value(-D)",
148
+ "own": "其他属于这个库的头文件的目录(头文件本身算库的;它所在的目录不是系统目录时,目录里的头文件也算)",
149
+ "compiler": "用来预处理头文件的 C 编译器:cc、gcc、clang、cl(默认取 CC 环境变量,没有时在 Windows 上是 cl,其他系统上是 cc)",
150
+ "check": "用库检查函数和变量,去掉库里没有的",
151
+ "force": "文件已存在时覆盖它"
152
+ }
153
+ }
154
+ },
135
155
  "i18n": {
136
156
  "description": "检查各语言的翻译、声明键名的类型,或添加一种语言",
137
157
  "arguments": {
@@ -132,6 +132,26 @@
132
132
  "target": "要修改的建置目標(預設是唯一的那個)"
133
133
  }
134
134
  },
135
+ "ffi": {
136
+ "description": "根據 C 標頭檔產生 @lakutata/ffi 原生程式庫的類別",
137
+ "generate": {
138
+ "description": "根據 C 標頭檔產生原生程式庫的類別:每個函式一個屬性,每個結構一個介面,加上常數,都帶有 TypeScript 型別",
139
+ "arguments": {
140
+ "header": "程式庫的 C 標頭檔"
141
+ },
142
+ "options": {
143
+ "library": "用來檢查函式的程式庫:@Library 接受的程式庫名稱,或一個檔案(預設依標頭檔的名稱)",
144
+ "className": "類別的名稱(預設是標頭檔名的 PascalCase 形式)",
145
+ "out": "要寫入的 TypeScript 檔案(預設是目前目錄下的 <類別名稱>.ts)",
146
+ "include": "標頭檔引入的其他標頭檔所在的目錄(-I)",
147
+ "define": "在標頭檔之前定義的巨集:NAME 或 NAME=value(-D)",
148
+ "own": "其他屬於這個程式庫的標頭檔的目錄(標頭檔本身算程式庫的;它所在的目錄不是系統目錄時,目錄裡的標頭檔也算)",
149
+ "compiler": "用來前置處理標頭檔的 C 編譯器:cc、gcc、clang、cl(預設取 CC 環境變數,沒有時在 Windows 上是 cl,其他系統上是 cc)",
150
+ "check": "用程式庫檢查函式和變數,去掉程式庫裡沒有的",
151
+ "force": "檔案已存在時覆寫它"
152
+ }
153
+ }
154
+ },
135
155
  "i18n": {
136
156
  "description": "檢查各語言的翻譯、宣告鍵名的型別,或新增一種語言",
137
157
  "arguments": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lakutata/cli",
3
- "version": "3.1.1",
3
+ "version": "3.1.2",
4
4
  "description": "The command line tool of the lakutata framework: create the projects from the templates, run and version the packages of the workspaces, bundle the applications and make their executables (with @lakutata/builder).",
5
5
  "keywords": [
6
6
  "lakutata",
@@ -39,18 +39,23 @@
39
39
  "dependencies": {
40
40
  "@inquirer/prompts": "^8.7.2",
41
41
  "commander": "^15.0.0",
42
- "lakutata": "3.1.1"
42
+ "lakutata": "3.1.2"
43
43
  },
44
44
  "peerDependencies": {
45
- "@lakutata/builder": "3.1.1"
45
+ "@lakutata/builder": "~3.1.1",
46
+ "@lakutata/ffi": "~3.1.2"
46
47
  },
47
48
  "peerDependenciesMeta": {
48
49
  "@lakutata/builder": {
49
50
  "optional": true
51
+ },
52
+ "@lakutata/ffi": {
53
+ "optional": true
50
54
  }
51
55
  },
52
56
  "devDependencies": {
53
- "@lakutata/builder": "3.1.1"
57
+ "@lakutata/builder": "~3.1.1",
58
+ "@lakutata/ffi": "~3.1.2"
54
59
  },
55
60
  "engines": {
56
61
  "node": "^22.13.0 || >=24.11.0"