@rex0220/print-craft-authoring-tools 1.0.0 → 1.1.0
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 +7 -7
- package/SECURITY.md +2 -2
- package/dist/cli.mjs +168 -206
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# print-craft authoring tools(開発メモ)
|
|
2
2
|
|
|
3
|
-
印刷屋プラグイン(print-craft)の設定 JSON を AI で作る・確かめるための tools。npm パッケージ `@rex0220/print-craft-authoring-tools`(MIT
|
|
3
|
+
印刷屋プラグイン(print-craft)の設定 JSON を AI で作る・確かめるための tools。npm パッケージ `@rex0220/print-craft-authoring-tools`(MIT。版は印刷屋プラグインの版と独立。扱う印刷屋はプラグイン ID と版の下限(Ver.6 以降)で決まり、`version` コマンドが出す)のソース。リポジトリのルートがテンプレート(利用者が "Use this template" で使う側)。計画と決定は print-craft の `docs/authoring-plan.md` 12 章。
|
|
4
4
|
|
|
5
|
-
**tools は印刷屋のコードを含まない。** 計算式エンジン(`desktop_js/KintoneFormulaPCraft.min.js`)と印刷屋の設定画面・帳票のコード + kit(`config_js/print-craft-authoring-api.js`。Ver.6 から zip に同梱。print-craft の `src/authoring/api.ts`)は、利用者の印刷屋 zip(`.env` の `PCRAFT_PLUGIN_ZIP`)から実行のたびにメモリに読む(`src/plugin-zip.ts`、`src/engine.ts`)。zip
|
|
5
|
+
**tools は印刷屋のコードを含まない。** 計算式エンジン(`desktop_js/KintoneFormulaPCraft.min.js`)と印刷屋の設定画面・帳票のコード + kit(`config_js/print-craft-authoring-api.js`。Ver.6 から zip に同梱。print-craft の `src/authoring/api.ts`)は、利用者の印刷屋 zip(`.env` の `PCRAFT_PLUGIN_ZIP`)から実行のたびにメモリに読む(`src/plugin-zip.ts`、`src/engine.ts`)。zip の `PUBKEY` から出るプラグイン ID が印刷屋のもの(`src/meta.ts` の `PRINT_CRAFT_PLUGIN_ID`)でなければ実行せずに止まる(fail closed。`SIGNATURE` は検証しない。`SECURITY.md`。1.1.0 から。1.0.0 までは 4 ファイルの SHA-256 を既知のリリースと照合していた)。開発中にソースから動かすとき(`node src/cli.ts …`)だけ、環境変数 `PCRAFT_ALLOW_DEV_PLUGIN=1` を置けば隣の print-craft の `prod/` からも読める(この場合はプラグイン ID を確かめず、警告を出して続く。ビルドした `dist/cli.mjs` では読まない)。ビルド(`scripts/build.mjs`)は bundle に印刷屋 / kit のコードが入っていないことを確かめて止まる。
|
|
6
6
|
|
|
7
|
-
**ビルドとテストには、隣に印刷屋のリポジトリ(非公開)と plugin-config-kit / rexgrid が要る**(`package.json` の devDependencies が `file:` で参照する。型の import と、テストの fixture の zip `print-craft/dist/print-craft-plugin6.zip`
|
|
7
|
+
**ビルドとテストには、隣に印刷屋のリポジトリ(非公開)と plugin-config-kit / rexgrid が要る**(`package.json` の devDependencies が `file:` で参照する。型の import と、テストの fixture の zip `print-craft/dist/print-craft-plugin6.zip`(Ver.7 の `print-craft-plugin7.zip` があればそれも読む)のため。公開レジストリの tools を使うだけなら要らない)。zip の読み取りと版の照合のテスト(`test/plugin-zip-synthetic.test.mjs`、`test/engine-contract.test.mjs`)は印刷屋のコードを含まない合成 zip(`test/zip-helper.mjs`)でも動く。
|
|
8
8
|
|
|
9
9
|
```
|
|
10
10
|
Projects/
|
|
@@ -19,12 +19,12 @@ Projects/
|
|
|
19
19
|
|
|
20
20
|
- `src/cli.ts` … `pcraft-authoring <command>`(version / fields / record / pull / normalize / preview / diff / buttons。pull は API ラボのプラグインの設定の GET。fields と record の `--summary` は取得済みのファイルの要約で通信しない。record の要約は値を出さない)。読むのは cwd の中、書くのは fields/ records/ settings/ temp/ out/ の下だけ(`src/safe-path.ts`)。`.env` / policy / zip の場所は固定
|
|
21
21
|
- `src/plugin-zip.ts` … 印刷屋の zip(contents.zip の 2 層)を Node の zlib だけで読む。大きさ・entry 数・展開後の上限、CRC-32、名前の一致、重複を検査
|
|
22
|
-
- `src/engine.ts` … zip のエンジンと authoring API を happy-dom +
|
|
22
|
+
- `src/engine.ts` … zip のエンジンと authoring API を happy-dom + スタブで動かす。照合(プラグイン ID、印刷屋の版、API の版と pluginVersion、API のキーと型)
|
|
23
23
|
- `src/kintone-url.ts` … 接続先の検証(`*.cybozu.com` / `*.kintone.com` / `*.cybozu.cn`、ユーザー情報・パス・ポート無し)
|
|
24
24
|
- `src/workspace.ts` … 開発と本番(environments.json)、`kintone/<ホスト名>/<番号>-<アプリ名>/` のフォルダー、ダウンロードの名前(`src/commands/take.ts` が inbox から移す)
|
|
25
|
-
- `src/env.ts` / `src/kintone-rest.ts` … `.env`(kintone 公式 MCP と同じ `KINTONE_*` + `PCRAFT_PLUGIN_ZIP
|
|
25
|
+
- `src/env.ts` / `src/kintone-rest.ts` … `.env`(kintone 公式 MCP と同じ `KINTONE_*` + `PCRAFT_PLUGIN_ZIP`)と GET 専用・許可 API 固定・送信先固定の REST
|
|
26
26
|
- `src/commands/` … 各コマンド。`src/normalize/` … 派生値の生成、検査(HTML / CSS の allowlist、policy、大きさ)、行の差分。`src/preview/` … 帳票 HTML(sandbox + CSP + DOM の無害化)
|
|
27
|
-
- `src/meta.ts` … tools
|
|
27
|
+
- `src/meta.ts` … tools の版、扱う印刷屋(プラグイン ID `PRINT_CRAFT_PLUGIN_ID`、版の下限 `MIN_PLUGIN_VERSION`)、API の版(`SUPPORTED_API_VERSIONS`)、API の契約(`REQUIRED_API`)
|
|
28
28
|
- `scripts/vendor.mjs` … moment 2.24.0 を CDN から `vendor/` に取る(`vendor/moment.json` の SHA-256 と照合)
|
|
29
29
|
- `scripts/build.mjs` … esbuild で `dist/cli.mjs`(Node 20、ESM。印刷屋 / kit のコードが入ったら失敗)
|
|
30
30
|
- `scripts/gen-schema-manifest.mjs` … `docs/schema-manifest.json` と `docs/defaults/*.json`(API の CONFIG_SCHEMA から)
|
|
@@ -48,7 +48,7 @@ npm pack --dry-run # 公開前に中身を確かめる(dist、vendor、LICENS
|
|
|
48
48
|
## 決まり
|
|
49
49
|
|
|
50
50
|
- 印刷屋の関数は `engine.api`(zip の authoring API)経由で使う。`src/` から print-craft を import するのは **型だけ**(`import type`)。実行コードを import すると build が止まる
|
|
51
|
-
- 印刷屋の `
|
|
51
|
+
- 印刷屋の `AUTHORING_API_VERSION` が上がったら、tools の `SUPPORTED_API_VERSIONS` に足し(古い版は利用者が残っている間は外さない)、`REQUIRED_API` を合わせて tools の新しい版として公開する。印刷屋の zip の中身が変わっただけ・版を上げただけなら tools の公開は要らない(1.1.0 から。プラグイン ID で読む)。印刷屋の鍵(`private.ppk`)を変えるとプラグイン ID が変わるので `PRINT_CRAFT_PLUGIN_ID` も変える。tools の版は印刷屋の版と独立(semver。2026-10-06 Takashi「B」で 0.1.0 から)
|
|
52
52
|
- kintone への書き込みは行わない。HTTP 層は GET 専用で、送れるパスと送信先を固定する
|
|
53
53
|
- CLI に `.env` / policy / zip の場所を変えるオプションを足さない(AI が書けるファイルを読ませない)。書き込み先を増やすなら `src/safe-path.ts` の `WRITE_ROOTS`
|
|
54
54
|
- `vendor/*.js`、`dist/`、`out/`、`node_modules/` はコミットしない
|
package/SECURITY.md
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
- **kintone 以外とは通信しない。** `normalize` / `preview` / `diff` / `buttons` / `version` と、`fields` / `record` の `--summary` はネットワークを使わない。利用状況の送信(テレメトリ)は無い
|
|
28
28
|
- **認証情報を出力しない。** `.env` または OS の環境変数から読み、API トークン・パスワード・ユーザー名は画面・ファイル・エラーの文言に出さない。エラーの文言は HTTP の状態と kintone のエラーコードと固定のヒントだけ(サーバーの message は出さない)。出すのは接続先の URL と「API トークン / ログインユーザー」の区別だけ
|
|
29
29
|
- **レコードの値を標準出力に出さない。** `record` は項目の数とテーブルの行数だけ表示し、値は `records/<app>-<id>.json` に書く(テンプレートの `.gitignore` でコミット対象外)。`--fields-from` で設定が使う項目だけに絞れる。`record --summary` も形(文字数・行数・数値の桁・件数・添付の種類)だけで、値・ファイル名・ユーザー名は出さない
|
|
30
|
-
- **印刷屋の zip
|
|
30
|
+
- **印刷屋の zip でなければ実行しない(fail closed)。** 外側の zip の `PUBKEY`(kintone のプラグインの公開鍵)から出るプラグイン ID が印刷屋のもの(`src/meta.ts` の `PRINT_CRAFT_PLUGIN_ID`)で、`manifest.json` の版が Ver.6 以上の整数で、authoring API があるときだけ zip のコードを実行する。違えば **コードを実行する前に止まる**。実行した後に、API の版(`SUPPORTED_API_VERSIONS`)、API の印刷屋の版と manifest の版の一致、tools が使う API のキーと型を確かめる。**`SIGNATURE`(kintone の署名)は検証しない**ので、本物の zip の `PUBKEY` を写して中身を差し替えた zip は止められない(1.1.0。2026-10-08。1.0.0 までは 4 ファイルの SHA-256 を既知のリリースと照合していたが、印刷屋の zip が変わるたびに tools の公開が要った)。zip の読み取りでは、外側と中身の大きさ・entry の数・展開後の大きさの上限、central directory と local header の名前の一致、CRC-32、同名 entry の重複を確かめる(zip bomb と改変の対策)。公開ビルド(npm の `dist/cli.mjs`)が読むのは `PCRAFT_PLUGIN_ZIP` の zip だけで、`node_modules` や隣のフォルダーのファイルは読まない(開発者がソースから動かし、環境変数 `PCRAFT_ALLOW_DEV_PLUGIN=1` を置いたときだけ、隣の print-craft の `prod/` を警告付きで読む)
|
|
31
31
|
- **印刷屋のコードを含まない。** エンジンと API は利用者の zip からメモリへ読むだけで、コピーや書き出しはしない。ビルドは、npm に入る `dist/cli.mjs` に印刷屋 / plugin-config-kit / rexgrid の実行コードが混ざっていないことを検査する
|
|
32
32
|
- **CLI が読む・書く場所を限る。** 読むのは作業フォルダーの中のファイルだけ(`.env` と `node_modules` は読まない)、書くのは `fields/` `records/` `settings/` `temp/` `out/` `kintone/` の下だけ(realpath で判定。junction / symlink で外へは出られない。Windows の予約名は使わない)。`.env`、`policy/authoring-policy.json`、印刷屋の zip の場所(`PCRAFT_PLUGIN_ZIP`)は作業フォルダーのものに固定で、`--env` / `--policy` / `--plugin-zip` のようなオプションは無い。iframe の同一オリジンの判定は `.env` の `KINTONE_BASE_URL` **だけ**を使い、AI が書き換えられる `fields/*.json` の値は使わない(`.env` に接続先が無ければ iframe は使えない)。パスの検査から書き込みまでの間にリンクが差し替えられる競合(TOCTOU)は残るが、AI と同じ OS ユーザーの範囲の話であり、この tools では防がない
|
|
33
33
|
- **`normalize` は許可した書き方だけ通す(allowlist)。** 要素は文章・表・画像の要素だけ、属性は共通の属性(class / id / style …)と要素ごとの属性(img の src / srcset / alt、td の colspan …)と `data-*` / `aria-*` だけで、一覧に無い要素・属性、`on*` 属性、インラインの `<svg>` / `<math>`、`<script>` `<object>` `<embed>` `<link>` `<base>` `<meta>` `<form>` 系、`<template>` などはエラー。URL は Chrome の URL パーサーと同じ前処理(タブ・改行・制御文字を捨てる、`\` は `/`)をしてから `https:` / `data:image/` / 置き換えタグ / 相対だけを通す(`a` の `href` は `data:` も不可、`iframe` は利用者の kintone と同じオリジンだけ)。CSS はコメントを外してエスケープを復号してから、`url()` / `image-set()` / `image()` / `src()` / `@import` の URL を分類し、`@import` / `expression(` / `behavior:` / `-moz-binding:` を止める。属性値の `${式}` はエラー。**kintone 以外への読み込みとスクリプトは印刷屋 Ver.6 が描画の前に除く**(共通の設定「外部参照」`externalRefs: "block"`。帳票の HTML / CSS を DOMParser で DOM 化して、外部へ向く URL 属性・CSS の `url()` / `@import`・`script` などの要素・`on*` 属性を外してから画面に入れる)ので、tools はそれを前提にする: `"block"` の設定の HTML / CSS のテンプレートにある外部 URL は**エラー**(帳票に出ない。添付ファイルか `data:image/` にする)。`"allow"`(Ver.5 と同じく何も除かない。自己責任。キーが無い Ver.5 の設定も印刷屋は `allow` で動くので tools も `allow` とみなす)は、利用者が AI の書けない `policy/authoring-policy.json` の `allowExternalRefs` にその設定ファイルを書いていなければ**エラー**。`allow` の設定の外部 URL と、外部参照の設定に関わらず Web フォント(印刷屋は除かない)は `allowExternal`(形を検証する)で承認し、承認済みは情報、無ければ**警告**。帳票の行の**計算式が作る HTML** は静的に追い切れない(計算式のインタープリターを tools に二重に持たない。Codex レビュー 5 回の結論)ので**警告だけ**: HTML を作る関数の使用、`TAG` の要素名や `ATTR` / `STYLE` / `BATTR` の値が定数でない、文字列の定数に HTML / CSS があれば HTML / CSS と同じ検査を警告として出す。`${式}` の安全判定は式全体で行い、`&` でつないだ項がすべて安全なときだけ警告しない。エラーがあれば書き戻さない
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
## tools がしないこと(利用者が守ること)
|
|
37
37
|
|
|
38
|
-
- **zip は印刷屋プラグインの配布元から入手したものを使う。** 出所の分からない zip を `PCRAFT_PLUGIN_ZIP`
|
|
38
|
+
- **zip は印刷屋プラグインの配布元から入手したものを使う。** 出所の分からない zip を `PCRAFT_PLUGIN_ZIP` に書かない。tools はプラグイン ID だけを見るので、中身を差し替えた zip を見分けられない(その zip のコードがこの PC の権限で動く)
|
|
39
39
|
- `normalize` は HTML / CSS / 計算式の**静的な**検査で、計算式の**実行結果**(レコードの値が HTML として差し込まれること、関数が組み立てる HTML)は見ない。`TABLE_HTML` などの生の HTML を入れる関数、`TAG` / `ATTR` / `STYLE`、ESC_HTML を通さない `${式}` は警告にとどまる。外部への読み込みとスクリプトを止めるのは印刷屋 Ver.6 の描画前の掃除(`externalRefs: "block"`)で、文章や表の崩れは防げない。`"allow"` の設定ではその掃除も無い(利用者の承認と責任)。**インポートする前に `diff` の差分を人が見る**
|
|
40
40
|
- preview の「承認した Web フォントの配信元以外と通信・遷移が起きない」ことは、CSP と sandbox と DOM からの除去の組み合わせで担保している。Chromium を自動で動かして確かめる試験はまだ無い(Chrome の DevTools の Network で確かめられる)
|
|
41
41
|
- kintone への書き込みは、テンプレートの `.claude/settings.json` が kintone MCP の書き込みツール 15 個と読み取り 3 個(検索・スペース・コメント)を拒否し、CLAUDE.md が禁じているが、最終的な担保はサーバー側の権限(**レコード閲覧だけの API トークン**か、閲覧権限だけのアカウント)。設定・権限・API トークンの管理は利用者の責任
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/*! @rex0220/print-craft-authoring-tools 1.
|
|
2
|
+
/*! @rex0220/print-craft-authoring-tools 1.1.0 (c) rex0220. MIT License. 計算式エンジンと印刷屋のコードは利用者の印刷屋プラグインの zip から読む(このファイルには含まれない)。commit eb915ae. */
|
|
3
3
|
|
|
4
4
|
// src/cli.ts
|
|
5
5
|
import { existsSync as existsSync7, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -49,8 +49,7 @@ var NAMES = {
|
|
|
49
49
|
token: ["KINTONE_API_TOKEN", "KSQL_TOKEN"],
|
|
50
50
|
username: ["KINTONE_USERNAME", "KSQL_USERNAME"],
|
|
51
51
|
password: ["KINTONE_PASSWORD", "KSQL_PASSWORD"],
|
|
52
|
-
pluginZip: ["PCRAFT_PLUGIN_ZIP"]
|
|
53
|
-
allowUnknown: ["PCRAFT_ALLOW_UNKNOWN_PLUGIN"]
|
|
52
|
+
pluginZip: ["PCRAFT_PLUGIN_ZIP"]
|
|
54
53
|
};
|
|
55
54
|
function parseDotEnv(text) {
|
|
56
55
|
const out = {};
|
|
@@ -133,9 +132,6 @@ function pluginZipPath(opt = {}) {
|
|
|
133
132
|
if (!v) return void 0;
|
|
134
133
|
return path.resolve(path.dirname(envFileOf(opt)), v);
|
|
135
134
|
}
|
|
136
|
-
function allowUnknownPlugin(opt = {}) {
|
|
137
|
-
return picker(opt)(NAMES.allowUnknown) === "1";
|
|
138
|
-
}
|
|
139
135
|
function describeAuth(auth) {
|
|
140
136
|
return auth.token ? "API トークン" : "ログインユーザー";
|
|
141
137
|
}
|
|
@@ -383,12 +379,12 @@ function envsOfHost(ws, host) {
|
|
|
383
379
|
}
|
|
384
380
|
|
|
385
381
|
// src/commands/take.ts
|
|
386
|
-
import { existsSync as
|
|
387
|
-
import
|
|
382
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync6, unlinkSync, writeFileSync, mkdirSync } from "node:fs";
|
|
383
|
+
import path8 from "node:path";
|
|
388
384
|
|
|
389
385
|
// src/commands/normalize.ts
|
|
390
|
-
import { readFileSync as
|
|
391
|
-
import
|
|
386
|
+
import { readFileSync as readFileSync5, statSync as statSync3 } from "node:fs";
|
|
387
|
+
import path7 from "node:path";
|
|
392
388
|
|
|
393
389
|
// src/normalize/findings.ts
|
|
394
390
|
var Findings = class {
|
|
@@ -422,6 +418,115 @@ var Findings = class {
|
|
|
422
418
|
}
|
|
423
419
|
};
|
|
424
420
|
|
|
421
|
+
// src/meta.ts
|
|
422
|
+
import { execFileSync } from "node:child_process";
|
|
423
|
+
import { createHash } from "node:crypto";
|
|
424
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
425
|
+
import path5 from "node:path";
|
|
426
|
+
|
|
427
|
+
// src/paths.ts
|
|
428
|
+
import { existsSync as existsSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
429
|
+
import path4 from "node:path";
|
|
430
|
+
import { fileURLToPath } from "node:url";
|
|
431
|
+
var here = path4.dirname(fileURLToPath(import.meta.url));
|
|
432
|
+
var AUTHORING_ROOT = path4.resolve(here, "..");
|
|
433
|
+
var REPO_ROOT = path4.resolve(AUTHORING_ROOT, "..");
|
|
434
|
+
var VENDOR_DIR = path4.join(AUTHORING_ROOT, "vendor");
|
|
435
|
+
var MOMENT_FILE = path4.join(VENDOR_DIR, "moment-with-locales.min.js");
|
|
436
|
+
function linkedRoot(name, envVar, fallback) {
|
|
437
|
+
const env = process.env[envVar];
|
|
438
|
+
if (env) return env;
|
|
439
|
+
const linked = path4.join(AUTHORING_ROOT, "node_modules", name);
|
|
440
|
+
if (existsSync3(path4.join(linked, "package.json"))) {
|
|
441
|
+
try {
|
|
442
|
+
return realpathSync2(linked);
|
|
443
|
+
} catch {
|
|
444
|
+
return linked;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return fallback;
|
|
448
|
+
}
|
|
449
|
+
var PRINT_CRAFT_ROOT = linkedRoot("print-craft", "PCRAFT_PRINT_CRAFT_ROOT", path4.resolve(REPO_ROOT, "..", "print-craft"));
|
|
450
|
+
function devPluginDir() {
|
|
451
|
+
const dir = path4.join(PRINT_CRAFT_ROOT, "prod");
|
|
452
|
+
return existsSync3(path4.join(dir, "desktop_js", "KintoneFormulaPCraft.min.js")) ? dir : null;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// src/meta.ts
|
|
456
|
+
var PRINT_CRAFT_PLUGIN_ID = "lcapkanpjdabgphknkabojmcfhonhkhp";
|
|
457
|
+
var MIN_PLUGIN_VERSION = 6;
|
|
458
|
+
var SUPPORTED_API_VERSIONS = [1, 2];
|
|
459
|
+
function isSupportedPluginVersion(version2) {
|
|
460
|
+
return /^[1-9]\d{0,5}$/.test(version2) && Number(version2) >= MIN_PLUGIN_VERSION;
|
|
461
|
+
}
|
|
462
|
+
var REQUIRED_API = {
|
|
463
|
+
apiVersion: "number",
|
|
464
|
+
pluginVersion: "string",
|
|
465
|
+
pluginId: "string",
|
|
466
|
+
CONFIG_SCHEMA: "object",
|
|
467
|
+
CONFIG_LIMITS: "object",
|
|
468
|
+
PAGE_SIZES: "object",
|
|
469
|
+
DPI_OPTIONS: "object",
|
|
470
|
+
PAPER_NAMES: "object",
|
|
471
|
+
PRINT_MODES: "object",
|
|
472
|
+
buildMenuRows: "function",
|
|
473
|
+
normalizeTagsInfo: "function",
|
|
474
|
+
normalizeCssRows: "function",
|
|
475
|
+
toSavedRows: "function",
|
|
476
|
+
computeUsage: "function",
|
|
477
|
+
computePluginUOG: "function",
|
|
478
|
+
createCheckRecord: "function",
|
|
479
|
+
createFieldsInfo: "function",
|
|
480
|
+
isTargetType: "function",
|
|
481
|
+
stripComments: "function",
|
|
482
|
+
defaultCssRows: "function",
|
|
483
|
+
defaultMenuInfo: "function",
|
|
484
|
+
expandFields: "function",
|
|
485
|
+
buildReportCss: "function",
|
|
486
|
+
fileNameOf: "function",
|
|
487
|
+
formulaOf: "function",
|
|
488
|
+
mountReportHtml: "function",
|
|
489
|
+
replaceTags: "function",
|
|
490
|
+
getPaperSize: "function",
|
|
491
|
+
webFontOf: "function",
|
|
492
|
+
DUMMY_IMAGE: "string",
|
|
493
|
+
validate: "function",
|
|
494
|
+
parseJsonSafely: "function",
|
|
495
|
+
ValidationError: "function",
|
|
496
|
+
writeConfig: "function",
|
|
497
|
+
DEFAULT_LIMITS: "object",
|
|
498
|
+
ConfigStoreError: "function",
|
|
499
|
+
// pull(アプリのプラグインの設定を読んで封筒形式にする。2026-10-05)
|
|
500
|
+
readConfig: "function",
|
|
501
|
+
buildExportData: "function"
|
|
502
|
+
};
|
|
503
|
+
function stableJson(v) {
|
|
504
|
+
return JSON.stringify(v, (_k, x) => x instanceof RegExp ? `/${x.source}/${x.flags}` : x);
|
|
505
|
+
}
|
|
506
|
+
function sha256Hex(data) {
|
|
507
|
+
return createHash("sha256").update(data).digest("hex");
|
|
508
|
+
}
|
|
509
|
+
function schemaRevisionOf(schema) {
|
|
510
|
+
return sha256Hex(stableJson(schema)).slice(0, 12);
|
|
511
|
+
}
|
|
512
|
+
function gitCommit(dir) {
|
|
513
|
+
try {
|
|
514
|
+
const head = execFileSync("git", ["-C", dir, "rev-parse", "--short", "HEAD"], { encoding: "utf8" }).trim();
|
|
515
|
+
const dirty = execFileSync("git", ["-C", dir, "status", "--porcelain", "--untracked-files=no"], { encoding: "utf8" }).trim() !== "";
|
|
516
|
+
return dirty ? `${head}+dirty` : head;
|
|
517
|
+
} catch {
|
|
518
|
+
return "unknown";
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
function devMeta() {
|
|
522
|
+
const pkg = JSON.parse(readFileSync3(path5.join(AUTHORING_ROOT, "package.json"), "utf8"));
|
|
523
|
+
return { toolsVersion: pkg.version, pluginId: PRINT_CRAFT_PLUGIN_ID, minPluginVersion: MIN_PLUGIN_VERSION, supportedApiVersions: [...SUPPORTED_API_VERSIONS], commit: gitCommit(AUTHORING_ROOT), builtAt: "", mode: "dev" };
|
|
524
|
+
}
|
|
525
|
+
function toolsMeta() {
|
|
526
|
+
if (true) return JSON.parse('{"toolsVersion":"1.1.0","pluginId":"lcapkanpjdabgphknkabojmcfhonhkhp","minPluginVersion":6,"supportedApiVersions":[1,2],"commit":"eb915ae","builtAt":"2026-10-08T00:26:18.929Z","mode":"build"}');
|
|
527
|
+
return devMeta();
|
|
528
|
+
}
|
|
529
|
+
|
|
425
530
|
// src/normalize/model.ts
|
|
426
531
|
var PSEUDO_FIELDS = /* @__PURE__ */ new Set(["$id", "$revision", "$out", "$html", "$rseq"]);
|
|
427
532
|
var UOG_MARK = "$UGO$";
|
|
@@ -1002,11 +1107,11 @@ function expressionsOf(html) {
|
|
|
1002
1107
|
}
|
|
1003
1108
|
|
|
1004
1109
|
// src/normalize/policy.ts
|
|
1005
|
-
import { existsSync as
|
|
1006
|
-
import
|
|
1110
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, statSync as statSync2 } from "node:fs";
|
|
1111
|
+
import path6 from "node:path";
|
|
1007
1112
|
var PolicyError = class extends Error {
|
|
1008
1113
|
};
|
|
1009
|
-
var POLICY_FILE =
|
|
1114
|
+
var POLICY_FILE = path6.join("policy", "authoring-policy.json");
|
|
1010
1115
|
var DEFAULT_ALLOWED_ORIGINS = ["https://fonts.googleapis.com", "https://fonts.gstatic.com"];
|
|
1011
1116
|
var MAX_POLICY_BYTES = 1024 * 1024;
|
|
1012
1117
|
var MAX_RULES = 200;
|
|
@@ -1104,10 +1209,10 @@ function isExternalRefsAllowed(policy, settingsFile) {
|
|
|
1104
1209
|
}
|
|
1105
1210
|
}
|
|
1106
1211
|
function loadPolicy(opt = {}) {
|
|
1107
|
-
const file =
|
|
1108
|
-
if (!
|
|
1212
|
+
const file = path6.join(opt.cwd ?? process.cwd(), POLICY_FILE);
|
|
1213
|
+
if (!existsSync4(file)) return { allowExternal: [] };
|
|
1109
1214
|
if (statSync2(file).size > MAX_POLICY_BYTES) throw new PolicyError(`${file} が大きすぎる(${MAX_POLICY_BYTES} バイトまで)`);
|
|
1110
|
-
return parsePolicy(
|
|
1215
|
+
return parsePolicy(readFileSync4(file, "utf8"), file);
|
|
1111
1216
|
}
|
|
1112
1217
|
function originOf(url) {
|
|
1113
1218
|
try {
|
|
@@ -1510,7 +1615,10 @@ async function normalizeSettings(input) {
|
|
|
1510
1615
|
return { findings: f, summary: "封筒形式でない" };
|
|
1511
1616
|
}
|
|
1512
1617
|
if (envelope.pluginID !== api.pluginId) f.error("envelope.pluginID", where, `pluginID は "${api.pluginId}": ${JSON.stringify(envelope.pluginID)}(封筒なしの素の設定は作らない)`);
|
|
1513
|
-
|
|
1618
|
+
const envVersion = String(envelope.PluginVersion ?? "");
|
|
1619
|
+
if (!isSupportedPluginVersion(envVersion)) f.error("envelope.version", where, `PluginVersion は印刷屋の版(Ver.${MIN_PLUGIN_VERSION} 以降、zip は ${pluginVersion}): ${JSON.stringify(envelope.PluginVersion)}`);
|
|
1620
|
+
else if (Number(envVersion) > Number(pluginVersion)) f.error("envelope.version", where, `PluginVersion ${envVersion} の設定を、それより古い印刷屋の zip(版 ${pluginVersion})で検査しようとしている。アプリに入れた版の zip を .env の PCRAFT_PLUGIN_ZIP に書く`);
|
|
1621
|
+
else if (envVersion !== pluginVersion) f.info("envelope.version.upgrade", where, `PluginVersion ${envVersion} の設定を印刷屋の版 ${pluginVersion}(zip)で検査し、書き出す封筒は ${pluginVersion} にする`);
|
|
1514
1622
|
if (envelope.appId !== void 0) {
|
|
1515
1623
|
if (typeof envelope.appId !== "number" || !Number.isInteger(envelope.appId) || envelope.appId <= 0) f.error("envelope.appId", where, `appId は正の整数(数値): ${JSON.stringify(envelope.appId)}`);
|
|
1516
1624
|
else if (envelope.appId !== input.fields.appId) f.warning("envelope.appId", where, `appId ${envelope.appId} が fields のアプリ ${input.fields.appId} と違う`);
|
|
@@ -1571,7 +1679,7 @@ async function normalizeSettings(input) {
|
|
|
1571
1679
|
function readTextLimited(file, maxBytes = MAX_INPUT_BYTES) {
|
|
1572
1680
|
const size = statSync3(file).size;
|
|
1573
1681
|
if (size > maxBytes) throw new InputError(`${file} が大きすぎる(${size.toLocaleString()} バイト。上限 ${maxBytes.toLocaleString()})`);
|
|
1574
|
-
return
|
|
1682
|
+
return readFileSync5(file, "utf8");
|
|
1575
1683
|
}
|
|
1576
1684
|
function readJsonLimited(file, maxBytes = MAX_INPUT_BYTES) {
|
|
1577
1685
|
let v;
|
|
@@ -1591,12 +1699,12 @@ async function readFieldsFile(file) {
|
|
|
1591
1699
|
return fields2;
|
|
1592
1700
|
}
|
|
1593
1701
|
function relativeSettingsPath(file) {
|
|
1594
|
-
const rel =
|
|
1702
|
+
const rel = path7.relative(process.cwd(), path7.resolve(file));
|
|
1595
1703
|
return rel.startsWith("..") ? file : rel.replace(/\\/g, "/");
|
|
1596
1704
|
}
|
|
1597
1705
|
|
|
1598
1706
|
// src/commands/take.ts
|
|
1599
|
-
var
|
|
1707
|
+
var PRINT_CRAFT_PLUGIN_ID2 = "rex0220 Print craft plugin";
|
|
1600
1708
|
function envFor(ws, appId, envName) {
|
|
1601
1709
|
if (envName) return ws.environments[envName] ?? `環境「${envName}」は environments.json に無い`;
|
|
1602
1710
|
const hits = Object.values(ws.environments).filter((e) => ws.apps.some((a) => a.ids[e.name] === appId));
|
|
@@ -1608,8 +1716,8 @@ function envFor(ws, appId, envName) {
|
|
|
1608
1716
|
}
|
|
1609
1717
|
function takeInbox(cwd, ws, envName) {
|
|
1610
1718
|
const result = { moved: [], skipped: [] };
|
|
1611
|
-
const inbox =
|
|
1612
|
-
if (!
|
|
1719
|
+
const inbox = path8.join(cwd, INBOX);
|
|
1720
|
+
if (!existsSync5(inbox)) return result;
|
|
1613
1721
|
for (const name of readdirSync2(inbox).filter((n) => n.toLowerCase().endsWith(".json")).sort()) {
|
|
1614
1722
|
const rel = `${INBOX}/${name}`;
|
|
1615
1723
|
const src = resolveRead(rel, cwd);
|
|
@@ -1620,7 +1728,7 @@ function takeInbox(cwd, ws, envName) {
|
|
|
1620
1728
|
result.skipped.push({ file: rel, reason: e.message });
|
|
1621
1729
|
continue;
|
|
1622
1730
|
}
|
|
1623
|
-
if (data.pluginID !==
|
|
1731
|
+
if (data.pluginID !== PRINT_CRAFT_PLUGIN_ID2) {
|
|
1624
1732
|
result.skipped.push({ file: rel, reason: "印刷屋の設定のファイルではない(pluginID が違う)" });
|
|
1625
1733
|
continue;
|
|
1626
1734
|
}
|
|
@@ -1640,21 +1748,21 @@ function takeInbox(cwd, ws, envName) {
|
|
|
1640
1748
|
continue;
|
|
1641
1749
|
}
|
|
1642
1750
|
const dir = appDirFor(cwd, env, appId, typeof data.appName === "string" ? data.appName : "");
|
|
1643
|
-
const dest = resolveWrite(
|
|
1644
|
-
const text =
|
|
1645
|
-
if (
|
|
1646
|
-
if (
|
|
1647
|
-
result.skipped.push({ file: rel, reason: `行き先に同じ名前の別の中身がある: ${
|
|
1751
|
+
const dest = resolveWrite(path8.join(dir, name), WRITE_ROOTS.kintone, cwd);
|
|
1752
|
+
const text = readFileSync6(src, "utf8");
|
|
1753
|
+
if (existsSync5(dest)) {
|
|
1754
|
+
if (readFileSync6(dest, "utf8") !== text) {
|
|
1755
|
+
result.skipped.push({ file: rel, reason: `行き先に同じ名前の別の中身がある: ${path8.relative(cwd, dest)}` });
|
|
1648
1756
|
continue;
|
|
1649
1757
|
}
|
|
1650
1758
|
unlinkSync(src);
|
|
1651
|
-
result.moved.push({ file: rel, to:
|
|
1759
|
+
result.moved.push({ file: rel, to: path8.relative(cwd, dest), same: true });
|
|
1652
1760
|
continue;
|
|
1653
1761
|
}
|
|
1654
|
-
mkdirSync(
|
|
1762
|
+
mkdirSync(path8.dirname(dest), { recursive: true });
|
|
1655
1763
|
writeFileSync(dest, text, "utf8");
|
|
1656
1764
|
unlinkSync(src);
|
|
1657
|
-
result.moved.push({ file: rel, to:
|
|
1765
|
+
result.moved.push({ file: rel, to: path8.relative(cwd, dest), same: false });
|
|
1658
1766
|
}
|
|
1659
1767
|
return result;
|
|
1660
1768
|
}
|
|
@@ -2036,7 +2144,7 @@ function formatDiff(lines, context2 = 2) {
|
|
|
2036
2144
|
}
|
|
2037
2145
|
|
|
2038
2146
|
// src/commands/buttons.ts
|
|
2039
|
-
import { createHash } from "node:crypto";
|
|
2147
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
2040
2148
|
var ButtonNotFoundError = class extends Error {
|
|
2041
2149
|
};
|
|
2042
2150
|
var kb = (v) => `${(Buffer.byteLength(JSON.stringify(v) ?? "", "utf8") / 1024).toFixed(1)} KB`;
|
|
@@ -2046,7 +2154,7 @@ var oneLine = (s, max = 160) => {
|
|
|
2046
2154
|
};
|
|
2047
2155
|
var chars = (s) => [...s ?? ""].length;
|
|
2048
2156
|
function shortenDataUrls(s, opt = {}) {
|
|
2049
|
-
return s.replace(/data:[^"'\s)<>]{120,}/g, (m) => `${m.slice(0, 40)}…(data URL ${m.length.toLocaleString()} 文字${opt.hash ? `、sha256 ${
|
|
2157
|
+
return s.replace(/data:[^"'\s)<>]{120,}/g, (m) => `${m.slice(0, 40)}…(data URL ${m.length.toLocaleString()} 文字${opt.hash ? `、sha256 ${createHash2("sha256").update(m).digest("hex").slice(0, 8)}` : ""})`);
|
|
2050
2158
|
}
|
|
2051
2159
|
function screensOf(b) {
|
|
2052
2160
|
const ids = (b.viewsCsv ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -2265,37 +2373,9 @@ import { existsSync as existsSync6, readFileSync as readFileSync8 } from "node:f
|
|
|
2265
2373
|
import path9 from "node:path";
|
|
2266
2374
|
import vm from "node:vm";
|
|
2267
2375
|
|
|
2268
|
-
// src/paths.ts
|
|
2269
|
-
import { existsSync as existsSync5, realpathSync as realpathSync2 } from "node:fs";
|
|
2270
|
-
import path7 from "node:path";
|
|
2271
|
-
import { fileURLToPath } from "node:url";
|
|
2272
|
-
var here = path7.dirname(fileURLToPath(import.meta.url));
|
|
2273
|
-
var AUTHORING_ROOT = path7.resolve(here, "..");
|
|
2274
|
-
var REPO_ROOT = path7.resolve(AUTHORING_ROOT, "..");
|
|
2275
|
-
var VENDOR_DIR = path7.join(AUTHORING_ROOT, "vendor");
|
|
2276
|
-
var MOMENT_FILE = path7.join(VENDOR_DIR, "moment-with-locales.min.js");
|
|
2277
|
-
function linkedRoot(name, envVar, fallback) {
|
|
2278
|
-
const env = process.env[envVar];
|
|
2279
|
-
if (env) return env;
|
|
2280
|
-
const linked = path7.join(AUTHORING_ROOT, "node_modules", name);
|
|
2281
|
-
if (existsSync5(path7.join(linked, "package.json"))) {
|
|
2282
|
-
try {
|
|
2283
|
-
return realpathSync2(linked);
|
|
2284
|
-
} catch {
|
|
2285
|
-
return linked;
|
|
2286
|
-
}
|
|
2287
|
-
}
|
|
2288
|
-
return fallback;
|
|
2289
|
-
}
|
|
2290
|
-
var PRINT_CRAFT_ROOT = linkedRoot("print-craft", "PCRAFT_PRINT_CRAFT_ROOT", path7.resolve(REPO_ROOT, "..", "print-craft"));
|
|
2291
|
-
function devPluginDir() {
|
|
2292
|
-
const dir = path7.join(PRINT_CRAFT_ROOT, "prod");
|
|
2293
|
-
return existsSync5(path7.join(dir, "desktop_js", "KintoneFormulaPCraft.min.js")) ? dir : null;
|
|
2294
|
-
}
|
|
2295
|
-
|
|
2296
2376
|
// src/plugin-zip.ts
|
|
2297
|
-
import { createHash as
|
|
2298
|
-
import { readFileSync as
|
|
2377
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2378
|
+
import { readFileSync as readFileSync7, statSync as statSync4 } from "node:fs";
|
|
2299
2379
|
import { inflateRawSync } from "node:zlib";
|
|
2300
2380
|
var SIG_EOCD = 101010256;
|
|
2301
2381
|
var SIG_CENTRAL = 33639248;
|
|
@@ -2396,18 +2476,18 @@ var BIGNUMBER_ENTRY = "desktop_js/bignumber.min.js";
|
|
|
2396
2476
|
var MOMENT_TZ_ENTRY = "desktop_js/moment-timezone-with-data.min.js";
|
|
2397
2477
|
var API_ENTRY = "config_js/print-craft-authoring-api.js";
|
|
2398
2478
|
var MANIFEST_ENTRY = "manifest.json";
|
|
2399
|
-
function
|
|
2400
|
-
return
|
|
2479
|
+
function sha256Hex2(data) {
|
|
2480
|
+
return createHash3("sha256").update(data).digest("hex");
|
|
2401
2481
|
}
|
|
2402
2482
|
function pluginIdOf(publicKey) {
|
|
2403
|
-
return
|
|
2483
|
+
return sha256Hex2(publicKey).slice(0, 32).replace(/[0-9a-f]/g, (c) => "abcdefghijklmnop"["0123456789abcdef".indexOf(c)]);
|
|
2404
2484
|
}
|
|
2405
2485
|
function readPluginZip(file) {
|
|
2406
2486
|
let outer;
|
|
2407
2487
|
try {
|
|
2408
2488
|
const size = statSync4(file).size;
|
|
2409
2489
|
if (size > ZIP_LIMITS.maxOuterBytes) throw new PluginZipError(`大きすぎる(${size} バイト)`);
|
|
2410
|
-
outer = unzip(
|
|
2490
|
+
outer = unzip(readFileSync7(file));
|
|
2411
2491
|
} catch (e) {
|
|
2412
2492
|
throw new PluginZipError(`印刷屋の zip を読めない: ${file}(${e instanceof Error ? e.message : String(e)})`);
|
|
2413
2493
|
}
|
|
@@ -2446,109 +2526,15 @@ function readPluginZip(file) {
|
|
|
2446
2526
|
momentTimezone: momentTimezone.toString("utf8"),
|
|
2447
2527
|
api: api?.toString("utf8"),
|
|
2448
2528
|
sha256: {
|
|
2449
|
-
engine:
|
|
2450
|
-
...api ? { api:
|
|
2451
|
-
bignumber:
|
|
2452
|
-
momentTimezone:
|
|
2453
|
-
contents:
|
|
2529
|
+
engine: sha256Hex2(engine),
|
|
2530
|
+
...api ? { api: sha256Hex2(api) } : {},
|
|
2531
|
+
bignumber: sha256Hex2(bignumber),
|
|
2532
|
+
momentTimezone: sha256Hex2(momentTimezone),
|
|
2533
|
+
contents: sha256Hex2(contents)
|
|
2454
2534
|
}
|
|
2455
2535
|
};
|
|
2456
2536
|
}
|
|
2457
2537
|
|
|
2458
|
-
// src/meta.ts
|
|
2459
|
-
import { execFileSync } from "node:child_process";
|
|
2460
|
-
import { createHash as createHash3 } from "node:crypto";
|
|
2461
|
-
import { readFileSync as readFileSync7 } from "node:fs";
|
|
2462
|
-
import path8 from "node:path";
|
|
2463
|
-
var SUPPORTED_PLUGIN_VERSIONS = ["6"];
|
|
2464
|
-
var SUPPORTED_API_VERSION = 1;
|
|
2465
|
-
var KNOWN_PLUGIN_RELEASES = {
|
|
2466
|
-
"6": [
|
|
2467
|
-
{
|
|
2468
|
-
engine: "8a5f78f78f51e04b5daaa435b0e8a8c0ea4d08d388d84d279f0cd1b50a857140",
|
|
2469
|
-
api: "59c27e480f419b9d3480c24426ae93dadf0ce7c46c940bcf7457d8252b26dea3",
|
|
2470
|
-
bignumber: "eca7c1c71fee589d7b5c58bd3df3f31d56fbd234821ed8c60ec4e2050ec50129",
|
|
2471
|
-
momentTimezone: "31b9bea01ffef2e8f311eafdbbcdd944a12194fa216d8f54489e15a7188d47dc",
|
|
2472
|
-
note: "Ver.6(2026-10-04 の PR #2 マージ時点。print-craft 73097d2。描画前の掃除が入る前)"
|
|
2473
|
-
},
|
|
2474
|
-
{
|
|
2475
|
-
engine: "8a5f78f78f51e04b5daaa435b0e8a8c0ea4d08d388d84d279f0cd1b50a857140",
|
|
2476
|
-
api: "3c029ba0bb2b2c869de9dc074e588cf3e5cae8caac91a83e3da196ef2aeee673",
|
|
2477
|
-
bignumber: "eca7c1c71fee589d7b5c58bd3df3f31d56fbd234821ed8c60ec4e2050ec50129",
|
|
2478
|
-
momentTimezone: "31b9bea01ffef2e8f311eafdbbcdd944a12194fa216d8f54489e15a7188d47dc",
|
|
2479
|
-
note: "Ver.6(2026-10-04〜05 描画前の掃除 sanitize.ts と共通の設定「外部参照」externalRefs、load.ts の derivedOf(無効な行と空の計算式は formula / usedFields を持たない)を入れた後。API に sanitizeReportCss / sanitizeReportNodes / isAllowedReportUrl / EXTERNAL_REFS / externalRefsOf を追加。途中の API 9cc7daf8… / b22564da… / 6c2e447d… は配っていないので載せない)"
|
|
2480
|
-
}
|
|
2481
|
-
]
|
|
2482
|
-
};
|
|
2483
|
-
var KNOWN_ENGINE_SHA256 = Object.fromEntries(Object.entries(KNOWN_PLUGIN_RELEASES).map(([v, rs]) => [v, rs.map((r) => r.engine)]));
|
|
2484
|
-
var REQUIRED_API = {
|
|
2485
|
-
apiVersion: "number",
|
|
2486
|
-
pluginVersion: "string",
|
|
2487
|
-
pluginId: "string",
|
|
2488
|
-
CONFIG_SCHEMA: "object",
|
|
2489
|
-
CONFIG_LIMITS: "object",
|
|
2490
|
-
PAGE_SIZES: "object",
|
|
2491
|
-
DPI_OPTIONS: "object",
|
|
2492
|
-
PAPER_NAMES: "object",
|
|
2493
|
-
PRINT_MODES: "object",
|
|
2494
|
-
buildMenuRows: "function",
|
|
2495
|
-
normalizeTagsInfo: "function",
|
|
2496
|
-
normalizeCssRows: "function",
|
|
2497
|
-
toSavedRows: "function",
|
|
2498
|
-
computeUsage: "function",
|
|
2499
|
-
computePluginUOG: "function",
|
|
2500
|
-
createCheckRecord: "function",
|
|
2501
|
-
createFieldsInfo: "function",
|
|
2502
|
-
isTargetType: "function",
|
|
2503
|
-
stripComments: "function",
|
|
2504
|
-
defaultCssRows: "function",
|
|
2505
|
-
defaultMenuInfo: "function",
|
|
2506
|
-
expandFields: "function",
|
|
2507
|
-
buildReportCss: "function",
|
|
2508
|
-
fileNameOf: "function",
|
|
2509
|
-
formulaOf: "function",
|
|
2510
|
-
mountReportHtml: "function",
|
|
2511
|
-
replaceTags: "function",
|
|
2512
|
-
getPaperSize: "function",
|
|
2513
|
-
webFontOf: "function",
|
|
2514
|
-
DUMMY_IMAGE: "string",
|
|
2515
|
-
validate: "function",
|
|
2516
|
-
parseJsonSafely: "function",
|
|
2517
|
-
ValidationError: "function",
|
|
2518
|
-
writeConfig: "function",
|
|
2519
|
-
DEFAULT_LIMITS: "object",
|
|
2520
|
-
ConfigStoreError: "function",
|
|
2521
|
-
// pull(アプリのプラグインの設定を読んで封筒形式にする。2026-10-05)
|
|
2522
|
-
readConfig: "function",
|
|
2523
|
-
buildExportData: "function"
|
|
2524
|
-
};
|
|
2525
|
-
function stableJson(v) {
|
|
2526
|
-
return JSON.stringify(v, (_k, x) => x instanceof RegExp ? `/${x.source}/${x.flags}` : x);
|
|
2527
|
-
}
|
|
2528
|
-
function sha256Hex2(data) {
|
|
2529
|
-
return createHash3("sha256").update(data).digest("hex");
|
|
2530
|
-
}
|
|
2531
|
-
function schemaRevisionOf(schema) {
|
|
2532
|
-
return sha256Hex2(stableJson(schema)).slice(0, 12);
|
|
2533
|
-
}
|
|
2534
|
-
function gitCommit(dir) {
|
|
2535
|
-
try {
|
|
2536
|
-
const head = execFileSync("git", ["-C", dir, "rev-parse", "--short", "HEAD"], { encoding: "utf8" }).trim();
|
|
2537
|
-
const dirty = execFileSync("git", ["-C", dir, "status", "--porcelain", "--untracked-files=no"], { encoding: "utf8" }).trim() !== "";
|
|
2538
|
-
return dirty ? `${head}+dirty` : head;
|
|
2539
|
-
} catch {
|
|
2540
|
-
return "unknown";
|
|
2541
|
-
}
|
|
2542
|
-
}
|
|
2543
|
-
function devMeta() {
|
|
2544
|
-
const pkg = JSON.parse(readFileSync7(path8.join(AUTHORING_ROOT, "package.json"), "utf8"));
|
|
2545
|
-
return { toolsVersion: pkg.version, supportedPluginVersions: SUPPORTED_PLUGIN_VERSIONS, supportedApiVersion: SUPPORTED_API_VERSION, commit: gitCommit(AUTHORING_ROOT), builtAt: "", mode: "dev" };
|
|
2546
|
-
}
|
|
2547
|
-
function toolsMeta() {
|
|
2548
|
-
if (true) return JSON.parse('{"toolsVersion":"1.0.0","supportedPluginVersions":["6"],"supportedApiVersion":1,"commit":"d662e0b","builtAt":"2026-10-06T05:47:45.315Z","mode":"build"}');
|
|
2549
|
-
return devMeta();
|
|
2550
|
-
}
|
|
2551
|
-
|
|
2552
2538
|
// src/engine.ts
|
|
2553
2539
|
var DEFAULT_CONTEXT_BASE_URL = "https://example.cybozu.com";
|
|
2554
2540
|
var DEFAULT_USER = { id: "1", code: "authoring", name: "authoring", email: "", language: "ja" };
|
|
@@ -2581,47 +2567,22 @@ function resolvePluginSources(opt = {}, mode = opt.mode ?? toolsMeta().mode) {
|
|
|
2581
2567
|
bignumber,
|
|
2582
2568
|
momentTimezone,
|
|
2583
2569
|
api,
|
|
2584
|
-
sha256: { engine:
|
|
2570
|
+
sha256: { engine: sha256Hex2(engine), ...api ? { api: sha256Hex2(api) } : {}, bignumber: sha256Hex2(bignumber), momentTimezone: sha256Hex2(momentTimezone) }
|
|
2585
2571
|
};
|
|
2586
2572
|
}
|
|
2587
|
-
var PARTS = [
|
|
2588
|
-
["計算式エンジン", "engine"],
|
|
2589
|
-
["authoring API", "api"],
|
|
2590
|
-
["bignumber", "bignumber"],
|
|
2591
|
-
["moment-timezone", "momentTimezone"]
|
|
2592
|
-
];
|
|
2593
|
-
function unknownParts(src, releases = KNOWN_PLUGIN_RELEASES) {
|
|
2594
|
-
const list = releases[src.pluginVersion];
|
|
2595
|
-
if (!list?.length) return ["版の一覧が無い"];
|
|
2596
|
-
const matches = (r) => PARTS.filter(([, k]) => src.sha256[k] && r[k] === src.sha256[k]).length;
|
|
2597
|
-
if (list.some((r) => matches(r) === PARTS.length)) return [];
|
|
2598
|
-
const show2 = ([label, k]) => `${label} ${src.sha256[k] ? src.sha256[k].slice(0, 12) + "…" : "無し"}`;
|
|
2599
|
-
const bestScore = Math.max(...list.map(matches));
|
|
2600
|
-
if (bestScore === 0) return PARTS.map(show2);
|
|
2601
|
-
const candidates = list.filter((r) => matches(r) === bestScore);
|
|
2602
|
-
const diffs = candidates.map((r) => PARTS.filter(([, k]) => !src.sha256[k] || r[k] !== src.sha256[k]).map(([label]) => label).join("|"));
|
|
2603
|
-
if (new Set(diffs).size !== 1) return PARTS.map(show2);
|
|
2604
|
-
const best = candidates[0];
|
|
2605
|
-
const diff2 = PARTS.filter(([, k]) => !src.sha256[k] || best[k] !== src.sha256[k]).map(show2);
|
|
2606
|
-
return diff2.length ? diff2 : PARTS.map(show2);
|
|
2607
|
-
}
|
|
2608
2573
|
async function loadEngine(opt = {}) {
|
|
2609
2574
|
if (loaded) return loaded;
|
|
2610
2575
|
const meta = toolsMeta();
|
|
2611
2576
|
const src = resolvePluginSources(opt, opt.mode ?? meta.mode);
|
|
2612
2577
|
const warnings = [];
|
|
2613
|
-
if (
|
|
2614
|
-
throw new PluginZipError(
|
|
2615
|
-
}
|
|
2616
|
-
if (
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
if (!engineKnown) {
|
|
2620
|
-
const head = `印刷屋の zip の中身が tools の既知のリリースと違う: ${unknown.join("、")}(${src.from})`;
|
|
2621
|
-
if (src.kind === "dev") warnings.push(`${head}。開発中の print-craft(PCRAFT_ALLOW_DEV_PLUGIN=1)なので続ける`);
|
|
2622
|
-
else if (opt.allowUnknown) warnings.push(`${head}。PCRAFT_ALLOW_UNKNOWN_PLUGIN=1 なので続ける(結果が設定画面と違うことがある)`);
|
|
2623
|
-
else throw new PluginZipError(`${head}。配布元から取り直した zip を使うか、tools を新しい版にする。新しい修正版の zip だと分かっていて続けるなら、利用者が .env に PCRAFT_ALLOW_UNKNOWN_PLUGIN=1 を書く(zip の中のコードはこの PC の権限で動く。AI は .env を書けない)`);
|
|
2578
|
+
if (src.kind === "zip" && src.pluginId !== PRINT_CRAFT_PLUGIN_ID) {
|
|
2579
|
+
throw new PluginZipError(`印刷屋プラグインの zip ではない(プラグイン ID ${src.pluginId ?? "不明(PUBKEY が無い)"}。印刷屋は ${PRINT_CRAFT_PLUGIN_ID}): ${src.from}。配布元から入手した印刷屋の zip を PCRAFT_PLUGIN_ZIP に書く`);
|
|
2580
|
+
}
|
|
2581
|
+
if (src.kind === "dev") warnings.push(`開発中の print-craft(${src.from}。PCRAFT_ALLOW_DEV_PLUGIN=1)から読む。プラグイン ID は確かめない`);
|
|
2582
|
+
if (!isSupportedPluginVersion(src.pluginVersion)) {
|
|
2583
|
+
throw new PluginZipError(`印刷屋プラグインの版 ${src.pluginVersion || "不明"}(${src.from})には対応していない。tools が扱うのは Ver.${MIN_PLUGIN_VERSION} 以降`);
|
|
2624
2584
|
}
|
|
2585
|
+
if (!src.api) throw new PluginZipError(`印刷屋の zip に ${API_ENTRY} が無い(Ver.${MIN_PLUGIN_VERSION} 以降の zip が要る): ${src.from}`);
|
|
2625
2586
|
const g = globalThis;
|
|
2626
2587
|
const { Window } = await import("happy-dom");
|
|
2627
2588
|
const ctx = { baseUrl: DEFAULT_CONTEXT_BASE_URL, appId: 1, loginUser: { ...DEFAULT_USER } };
|
|
@@ -2677,7 +2638,7 @@ async function loadEngine(opt = {}) {
|
|
|
2677
2638
|
if (typeof Ctor !== "function") throw new Error("KintoneFormulaPCraft is not loaded");
|
|
2678
2639
|
const api = window.rex0220PrintCraftAuthoring;
|
|
2679
2640
|
if (!api || typeof api !== "object") throw new PluginZipError("印刷屋の authoring API(rex0220PrintCraftAuthoring)が読めない");
|
|
2680
|
-
if (api.apiVersion
|
|
2641
|
+
if (!SUPPORTED_API_VERSIONS.includes(api.apiVersion)) throw new PluginZipError(`印刷屋の authoring API の版 ${String(api.apiVersion)} には対応していない(tools は ${SUPPORTED_API_VERSIONS.join(", ")})。tools を新しい版にする`);
|
|
2681
2642
|
if (String(api.pluginVersion) !== src.pluginVersion) throw new PluginZipError(`authoring API の印刷屋の版 ${String(api.pluginVersion)} が zip の manifest の版 ${src.pluginVersion} と違う(組み替えられた zip)`);
|
|
2682
2643
|
const bad = Object.entries(REQUIRED_API).filter(([k, t]) => typeof api[k] !== t).map(([k]) => k);
|
|
2683
2644
|
if (bad.length) throw new PluginZipError(`印刷屋の authoring API に tools が使うものが無い、または型が違う: ${bad.join(", ")}(tools と印刷屋の版を合わせる)`);
|
|
@@ -2685,7 +2646,7 @@ async function loadEngine(opt = {}) {
|
|
|
2685
2646
|
window,
|
|
2686
2647
|
Ctor,
|
|
2687
2648
|
api,
|
|
2688
|
-
source: { kind: src.kind, from: src.from, pluginVersion: src.pluginVersion, engineSha256: src.sha256.engine, apiSha256: src.sha256.api,
|
|
2649
|
+
source: { kind: src.kind, from: src.from, pluginVersion: src.pluginVersion, engineSha256: src.sha256.engine, apiSha256: src.sha256.api, ...src.pluginId ? { pluginId: src.pluginId } : {} },
|
|
2689
2650
|
warnings,
|
|
2690
2651
|
setContext(next) {
|
|
2691
2652
|
if (next.baseUrl) ctx.baseUrl = next.baseUrl.replace(/\/+$/, "");
|
|
@@ -2990,7 +2951,7 @@ function shown(file) {
|
|
|
2990
2951
|
return rel && !rel.startsWith("..") ? rel : file;
|
|
2991
2952
|
}
|
|
2992
2953
|
async function engineFor() {
|
|
2993
|
-
const engine = await loadEngine({ pluginZip: pluginZipPath()
|
|
2954
|
+
const engine = await loadEngine({ pluginZip: pluginZipPath() });
|
|
2994
2955
|
for (const w of engine.warnings) console.error(`注意: ${w}`);
|
|
2995
2956
|
return engine;
|
|
2996
2957
|
}
|
|
@@ -3007,12 +2968,12 @@ async function version(args) {
|
|
|
3007
2968
|
if (expect !== void 0 && !expect) throw new UsageError("--expect には印刷屋の版(例: 6)を続ける");
|
|
3008
2969
|
let expectError;
|
|
3009
2970
|
if (expect !== void 0 && engine) {
|
|
3010
|
-
if (!
|
|
2971
|
+
if (!isSupportedPluginVersion(expect)) expectError = `--expect ${expect}: tools ${meta.toolsVersion} が扱う印刷屋の版は Ver.${meta.minPluginVersion} 以降`;
|
|
3011
2972
|
else if (engine.source.pluginVersion !== expect) expectError = `--expect ${expect}: zip の印刷屋の版は ${engine.source.pluginVersion}(${engine.source.from}。アプリに入れた版の zip を PCRAFT_PLUGIN_ZIP に書く)`;
|
|
3012
2973
|
}
|
|
3013
2974
|
const info = {
|
|
3014
2975
|
...meta,
|
|
3015
|
-
plugin: engine ? { source: engine.source.kind, from: engine.source.from, pluginVersion: engine.source.pluginVersion, apiVersion: engine.api.apiVersion, engineSha256: engine.source.engineSha256, apiSha256: engine.source.apiSha256,
|
|
2976
|
+
plugin: engine ? { source: engine.source.kind, from: engine.source.from, pluginVersion: engine.source.pluginVersion, apiVersion: engine.api.apiVersion, engineSha256: engine.source.engineSha256, apiSha256: engine.source.apiSha256, pluginId: engine.source.pluginId ?? null, schemaRevision: schemaRevisionOf(engine.api.CONFIG_SCHEMA) } : null,
|
|
3016
2977
|
expected: expect,
|
|
3017
2978
|
ok: !!engine && !expectError,
|
|
3018
2979
|
error: error ?? expectError
|
|
@@ -3020,10 +2981,11 @@ async function version(args) {
|
|
|
3020
2981
|
if (flag(args, "json")) {
|
|
3021
2982
|
console.log(JSON.stringify(info, null, 2));
|
|
3022
2983
|
} else {
|
|
3023
|
-
console.log(`@rex0220/print-craft-authoring-tools ${meta.toolsVersion}${meta.mode === "dev" ? "(開発中)" : ""}
|
|
2984
|
+
console.log(`@rex0220/print-craft-authoring-tools ${meta.toolsVersion}${meta.mode === "dev" ? "(開発中)" : ""}(扱う印刷屋 Ver.${meta.minPluginVersion} 以降、API ${meta.supportedApiVersions.join(", ")}。commit ${meta.commit}${meta.builtAt ? `、ビルド ${meta.builtAt}` : ""})`);
|
|
3024
2985
|
if (engine) {
|
|
3025
2986
|
console.log(`印刷屋プラグイン: 版 ${engine.source.pluginVersion}、authoring API ${engine.api.apiVersion}(${engine.source.kind === "zip" ? "zip" : "開発中の print-craft"}: ${engine.source.from})`);
|
|
3026
|
-
console.log(
|
|
2987
|
+
console.log(`プラグイン ID: ${engine.source.pluginId ?? "(開発中の print-craft。確かめない)"}`);
|
|
2988
|
+
console.log(`計算式エンジン: sha256 ${engine.source.engineSha256}`);
|
|
3027
2989
|
console.log(`設定スキーマの版: ${schemaRevisionOf(engine.api.CONFIG_SCHEMA)}`);
|
|
3028
2990
|
if (expectError) console.error(expectError);
|
|
3029
2991
|
else if (expect !== void 0) console.log(`--expect ${expect}: 一致`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rex0220/print-craft-authoring-tools",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "rex0220 印刷屋プラグイン(kintone)の設定 JSON を AI で作る・確かめるための tools(fields / record / pull / normalize / preview / diff / buttons / version)。計算式エンジンと印刷屋のコードは利用者の印刷屋プラグインの zip から読む",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|