@rex0220/print-craft-authoring-tools 0.1.1 → 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 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。版は印刷屋プラグインの版と独立。対応する印刷屋の版は `src/meta.ts` の `SUPPORTED_PLUGIN_VERSIONS` で、`version` コマンドが出す)のソース。リポジトリのルートがテンプレート(利用者が "Use this template" で使う側)。計画と決定は print-craft の `docs/authoring-plan.md` 12 章。
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 の中身の SHA-256 が `src/meta.ts` の既知のリリース(4 ファイルの組み合わせ)と違えば実行せずに止まる(fail closed。`SECURITY.md`)。開発中にソースから動かすとき(`node src/cli.ts …`)だけ、環境変数 `PCRAFT_ALLOW_DEV_PLUGIN=1` を置けば隣の print-craft の `prod/` からも読める(この場合は既知でなくても警告で続く。ビルドした `dist/cli.mjs` では読まない)。ビルド(`scripts/build.mjs`)は bundle に印刷屋 / kit のコードが入っていないことを確かめて止まる。
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` のため。公開レジストリの tools を使うだけなら要らない)。zip の読み取りと版の照合のテスト(`test/plugin-zip-synthetic.test.mjs`、`test/engine-contract.test.mjs`)は印刷屋のコードを含まない合成 zip(`test/zip-helper.mjs`)でも動く。
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 + スタブで動かす。版の照合(印刷屋の版、API の版と pluginVersion、4 ファイルの SHA-256、API のキーと型)
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` + `PCRAFT_ALLOW_UNKNOWN_PLUGIN`)と GET 専用・許可 API 固定・送信先固定の REST
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 の版、対応する印刷屋の版と API の版、既知の zip の中身の SHA-256(`KNOWN_PLUGIN_HASHES`)、API の契約(`REQUIRED_API`)
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
- - 印刷屋の `src/authoring/api.ts` の名前や引数を変えるときは `AUTHORING_API_VERSION` を上げ、tools の `SUPPORTED_API_VERSION` と `REQUIRED_API` を合わせる。印刷屋の zip の中身を変えたら `KNOWN_PLUGIN_RELEASES` にリリースの tuple(エンジン・API・bignumber・moment-timezone の 4 つの SHA-256。zip から計算)を足し、印刷屋の版を上げたら `SUPPORTED_PLUGIN_VERSIONS` に足す(古い版は利用者が残っている間は外さない)。どちらも tools の新しい版として公開する。tools の版は印刷屋の版と独立(semver。2026-10-06 Takashi「B」で 0.1.0 から)
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 の中身が既知でなければ実行しない(fail closed)。** 計算式エンジン、authoring API、bignumber、moment-timezone の 4 ファイルの SHA-256 が tools の既知のリリース(`src/meta.ts` の `KNOWN_PLUGIN_RELEASES`。4 つの組み合わせ単位)と一致し、zip の `manifest.json` の版が対応する版で、API の版と印刷屋の版と tools の版が合い、tools が使う API のキーと型がそろっているときだけ実行する。どれか違えば **コードを実行する前に止まる**。zip の読み取りでは、外側と中身の大きさ・entry の数・展開後の大きさの上限、central directory と local header の名前の一致、CRC-32、同名 entry の重複を確かめる(zip bomb と改変の対策)。新しい修正版の zip を使うなど、違いを理解した上で続けるときだけ、**利用者が** `.env` に `PCRAFT_ALLOW_UNKNOWN_PLUGIN=1` を書く(警告を出して続ける。AI は `.env` を書けない)。公開ビルド(npm の `dist/cli.mjs`)が読むのは `PCRAFT_PLUGIN_ZIP` の zip だけで、`node_modules` や隣のフォルダーのファイルは読まない(開発者がソースから動かし、環境変数 `PCRAFT_ALLOW_DEV_PLUGIN=1` を置いたときだけ、隣の print-craft の `prod/` を警告付きで読む)
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` に書かない。`PCRAFT_ALLOW_UNKNOWN_PLUGIN=1` は、tools の既知の一覧より新しい修正版の zip だと分かっているときだけ、理由を理解して書く(その zip のコードがこの PC の権限で動く)
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 0.1.1 (c) rex0220. MIT License. 計算式エンジンと印刷屋のコードは利用者の印刷屋プラグインの zip から読む(このファイルには含まれない)。commit 4e1b956. */
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 existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync5, unlinkSync, writeFileSync, mkdirSync } from "node:fs";
387
- import path6 from "node:path";
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 readFileSync4, statSync as statSync3 } from "node:fs";
391
- import path5 from "node:path";
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 existsSync3, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
1006
- import path4 from "node:path";
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 = path4.join("policy", "authoring-policy.json");
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 = path4.join(opt.cwd ?? process.cwd(), POLICY_FILE);
1108
- if (!existsSync3(file)) return { allowExternal: [] };
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(readFileSync3(file, "utf8"), file);
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
- if (String(envelope.PluginVersion ?? "") !== pluginVersion) f.error("envelope.version", where, `PluginVersion は印刷屋の版 ${pluginVersion}(zip の manifest): ${JSON.stringify(envelope.PluginVersion)}`);
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 readFileSync4(file, "utf8");
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 = path5.relative(process.cwd(), path5.resolve(file));
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 PRINT_CRAFT_PLUGIN_ID = "rex0220 Print craft plugin";
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 = path6.join(cwd, INBOX);
1612
- if (!existsSync4(inbox)) return result;
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 !== PRINT_CRAFT_PLUGIN_ID) {
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(path6.join(dir, name), WRITE_ROOTS.kintone, cwd);
1644
- const text = readFileSync5(src, "utf8");
1645
- if (existsSync4(dest)) {
1646
- if (readFileSync5(dest, "utf8") !== text) {
1647
- result.skipped.push({ file: rel, reason: `行き先に同じ名前の別の中身がある: ${path6.relative(cwd, dest)}` });
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: path6.relative(cwd, dest), same: true });
1759
+ result.moved.push({ file: rel, to: path8.relative(cwd, dest), same: true });
1652
1760
  continue;
1653
1761
  }
1654
- mkdirSync(path6.dirname(dest), { recursive: true });
1762
+ mkdirSync(path8.dirname(dest), { recursive: true });
1655
1763
  writeFileSync(dest, text, "utf8");
1656
1764
  unlinkSync(src);
1657
- result.moved.push({ file: rel, to: path6.relative(cwd, dest), same: false });
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 ${createHash("sha256").update(m).digest("hex").slice(0, 8)}` : ""})`);
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 createHash2 } from "node:crypto";
2298
- import { readFileSync as readFileSync6, statSync as statSync4 } from "node:fs";
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 sha256Hex(data) {
2400
- return createHash2("sha256").update(data).digest("hex");
2479
+ function sha256Hex2(data) {
2480
+ return createHash3("sha256").update(data).digest("hex");
2401
2481
  }
2402
2482
  function pluginIdOf(publicKey) {
2403
- return sha256Hex(publicKey).slice(0, 32).replace(/[0-9a-f]/g, (c) => "abcdefghijklmnop"["0123456789abcdef".indexOf(c)]);
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(readFileSync6(file));
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: sha256Hex(engine),
2450
- ...api ? { api: sha256Hex(api) } : {},
2451
- bignumber: sha256Hex(bignumber),
2452
- momentTimezone: sha256Hex(momentTimezone),
2453
- contents: sha256Hex(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":"0.1.1","supportedPluginVersions":["6"],"supportedApiVersion":1,"commit":"4e1b956","builtAt":"2026-10-06T03:11:27.429Z","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: sha256Hex(engine), ...api ? { api: sha256Hex(api) } : {}, bignumber: sha256Hex(bignumber), momentTimezone: sha256Hex(momentTimezone) }
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 (!SUPPORTED_PLUGIN_VERSIONS.includes(src.pluginVersion)) {
2614
- throw new PluginZipError(`印刷屋プラグインの版 ${src.pluginVersion || "不明"}(${src.from})には対応していない。tools が対応する版: ${SUPPORTED_PLUGIN_VERSIONS.join(", ")}`);
2615
- }
2616
- if (!src.api) throw new PluginZipError(`印刷屋の zip に ${API_ENTRY} が無い(Ver.6 以降の zip が要る): ${src.from}`);
2617
- const unknown = unknownParts(src);
2618
- const engineKnown = unknown.length === 0;
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 !== SUPPORTED_API_VERSION) throw new PluginZipError(`印刷屋の authoring API の版 ${String(api.apiVersion)} には対応していない(tools は ${SUPPORTED_API_VERSION})。tools を印刷屋の版に合わせて更新する`);
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, engineKnown, ...src.pluginId ? { pluginId: src.pluginId } : {} },
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(), allowUnknown: allowUnknownPlugin() });
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 (!meta.supportedPluginVersions.includes(expect)) expectError = `--expect ${expect}: tools ${meta.toolsVersion} が対応する印刷屋の版は ${meta.supportedPluginVersions.join(", ")}(tools を更新する)`;
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, engineKnown: engine.source.engineKnown, schemaRevision: schemaRevisionOf(engine.api.CONFIG_SCHEMA) } : null,
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" ? "(開発中)" : ""}(対応する印刷屋の版 ${meta.supportedPluginVersions.join(", ")}、API ${meta.supportedApiVersion}。commit ${meta.commit}${meta.builtAt ? `、ビルド ${meta.builtAt}` : ""})`);
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(`計算式エンジン: sha256 ${engine.source.engineSha256}${engine.source.engineKnown ? "(zip の中身は既知)" : "(既知の一覧と違う中身を含む)"}`);
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": "0.1.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",