@rex0220/formula-authoring-tools 0.1.0 → 0.2.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
@@ -27,9 +27,10 @@ Projects/
27
27
  - `src/connections.ts` / `src/workspace.ts` / `src/permission.ts` / `src/commit-file.ts` … kintone の接続のファイル(kSQL の `ksql.config.json` と同じ形。profile、`tokenMap`、`userApiToken`)、`kintone/<profile>/<番号>-<アプリ名>/` のフォルダーと印 `.formula-app.json`、kintone/ の下を変える許可、ファイルの確定とロック(print-craft-authoring tools 2.0.0 の写し)
28
28
  - `src/env.ts` / `src/kintone-rest.ts` / `src/kintone-url.ts` … `.env`(kintone 公式 MCP と同じ `KINTONE_*` + `FORMULA_PLUGIN_ZIP` / `FORMULA_KINTONE_CONFIG` / `FORMULA_USER_API_TOKEN`)、GET 専用・許可 API 固定・送信先固定の REST(kintone REST API と User API)
29
29
  - `src/meta.ts` … tools の版、扱う計算式プラグイン(プラグイン ID `FORMULA_PLUGIN_ID`、封筒の `FORMULA_CONFIG_PLUGIN_ID`、版の下限 `MIN_PLUGIN_VERSION`)、API の版(`SUPPORTED_API_VERSIONS`)、API の契約(`REQUIRED_API`)
30
- - `src/core.ts` … 共通の中核の入口(npm の `@rex0220/formula-authoring-tools/core`。MCP が使う予定)。`scripts/build.mjs` が `dist/core.mjs` と `dist/types/` を作る
30
+ - `src/commands/save.ts` … 保存の約束(formula MCP の `fml_save_settings` / `fml_update_fields` の本体。CLI は使わない)。新しい設定は同じ名前があれば保存しない、既存の設定は `fml_summary` の digest を照合して項目の行(`fieldcode`)を複数まとめて差し替える。normalize は Worker で行い、エラーがあれば確定しない。確定はロック付き(`commit-file.ts`)
31
+ - `src/core.ts` … 共通の中核の入口(npm の `@rex0220/formula-authoring-tools/core`。formula MCP が使う。`CORE_API_VERSION` 2)。`scripts/build.mjs` が `dist/core.mjs` と `dist/types/` を作る。MCP が使う名前は `test/core-exports.test.mjs` で固定する
31
32
  - `scripts/vendor.mjs` … moment 2.24.0 を CDN から `vendor/` に取る(`vendor/moment.json` の SHA-256 と照合)。`scripts/build.mjs` … esbuild で `dist/cli.mjs` / `dist/eval-worker.mjs` / `dist/core.mjs`。`scripts/gen-docs.mjs` … `docs/`(設定ファイル仕様、関数一覧、functions.json、schema-manifest、既定のテストデータ)と `samples/users.json` を zip から機械生成
32
- - `pending/` … print-craft から写してまだ計算式用にしていないもの(`save.ts` = MCP の保存の約束)。npm には入らない
33
+ - `pending/` … print-craft から写してまだ計算式用にしていないもの(`diff.ts` / `normalize.ts` は計算式用の `src/commands/` が別にあるので使っていない)。npm には入らない
33
34
  - `test/` … `node --test`(src の .ts を直接読む。`test/helpers.mjs` が fixture の zip を読む。`test/fixtures/formula/` はテスト用アプリ 2305 の項目定義と設定)
34
35
 
35
36
  ## 使い方(開発)
@@ -58,4 +59,5 @@ npm pack --dry-run # 公開前に中身を確かめる(dist、vendor、LICENS
58
59
 
59
60
  ## 履歴
60
61
 
62
+ - 0.2.0(2026-10): formula MCP のための中核の入口(`CORE_API_VERSION` 2。CLI の動きは変えない)。`core` に workspace / connections / permission / commit-file / take / users の参照コード / JSON の誤りの文 / 行のキーを追加。`src/commands/save.ts`(`saveNewSettings` / `updateFields`)。`runEval` の `workerFile`(Worker の入口の絶対パス)と `captureOutput`(Worker の標準出力・標準エラーを親に流さず受け取る)、`loadEngine` の `vendorDir`(moment の置き場所)。評価の結果に `findings` と calc のレコードごとの行
61
63
  - 0.1.0(2026-10): 最初の公開。version / fields / record / records / pull / summary / diff / users / normalize / calc / take / edit / files。扱う計算式プラグイン Ver.101 以降、authoring API 1
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- /*! @rex0220/formula-authoring-tools 0.1.0 (c) rex0220. MIT License. 計算式エンジンと計算式プラグインのコードは利用者の計算式プラグインプラグインの zip から読む(このファイルには含まれない)。commit 69de71e. */
2
+ /*! @rex0220/formula-authoring-tools 0.2.0 (c) rex0220. MIT License. 計算式エンジンと計算式プラグインのコードは利用者の計算式プラグインプラグインの zip から読む(このファイルには含まれない)。commit 1e50847. */
3
3
 
4
4
  // src/cli.ts
5
5
  import { chmodSync, existsSync as existsSync9, mkdirSync as mkdirSync2, realpathSync as realpathSync5, writeFileSync as writeFileSync3 } from "node:fs";
@@ -355,7 +355,7 @@ function devMeta() {
355
355
  return { toolsVersion: pkg.version, pluginId: FORMULA_PLUGIN_ID, minPluginVersion: MIN_PLUGIN_VERSION, supportedApiVersions: [...SUPPORTED_API_VERSIONS], commit: gitCommit(AUTHORING_ROOT), builtAt: "", mode: "dev" };
356
356
  }
357
357
  function toolsMeta() {
358
- if (true) return JSON.parse('{"toolsVersion":"0.1.0","pluginId":"dhgihghfgfmibkdldkeibboabfnmdpke","minPluginVersion":101,"supportedApiVersions":[1],"commit":"69de71e","builtAt":"2026-10-18T00:25:51.113Z","mode":"build"}');
358
+ if (true) return JSON.parse('{"toolsVersion":"0.2.0","pluginId":"dhgihghfgfmibkdldkeibboabfnmdpke","minPluginVersion":101,"supportedApiVersions":[1],"commit":"1e50847","builtAt":"2026-10-18T02:11:59.644Z","mode":"build"}');
359
359
  return devMeta();
360
360
  }
361
361
 
@@ -659,6 +659,9 @@ function summarizeUsers(src, origin, user) {
659
659
  }
660
660
 
661
661
  // src/engine.ts
662
+ function momentFileOf(opt) {
663
+ return opt.vendorDir ? path6.join(opt.vendorDir, path6.basename(MOMENT_FILE)) : MOMENT_FILE;
664
+ }
662
665
  var DEFAULT_CONTEXT_BASE_URL = "https://example.cybozu.com";
663
666
  var SENTINEL_USER = { id: "0", code: "__sentinel__", name: "", email: "", timezone: "Asia/Tokyo", language: "ja", isGuest: false };
664
667
  var loaded = null;
@@ -758,15 +761,16 @@ async function loadOnce(opt) {
758
761
  }
759
762
  if (!src.api) throw new PluginZipError(`計算式プラグインの zip に ${API_ENTRY} が無い(Ver.${MIN_PLUGIN_VERSION} 以降の zip が要る): ${src.from}`, "no-api", known);
760
763
  if (!src.decimal) warnings.push(`zip に ${DECIMAL_ENTRY} が無い(べき乗の小数の指数がエラーになる)`);
761
- if (!existsSync3(MOMENT_FILE)) throw new Error(`moment が無い: ${MOMENT_FILE}(tools の npm install 時に取る。手で取るなら npm run vendor)`);
764
+ const momentFile = momentFileOf(opt);
765
+ if (!existsSync3(momentFile)) throw new Error(`moment が無い: ${momentFile}(tools の npm install 時に取る。手で取るなら npm run vendor)`);
762
766
  try {
763
- return await startEngine({ ...src, api: src.api }, known, warnings, stamp);
767
+ return await startEngine({ ...src, api: src.api }, known, warnings, stamp, momentFile);
764
768
  } catch (e) {
765
769
  poisoned = { code: e instanceof PluginZipError ? e.code : "unexpected", info: e instanceof PluginZipError ? e.info : known };
766
770
  throw e;
767
771
  }
768
772
  }
769
- async function startEngine(src, known, warnings, stamp) {
773
+ async function startEngine(src, known, warnings, stamp, momentFile) {
770
774
  const g = globalThis;
771
775
  const { Window } = await import("happy-dom");
772
776
  const ctx = {
@@ -842,7 +846,7 @@ async function startEngine(src, known, warnings, stamp) {
842
846
  throw new PluginZipError(`計算式プラグインの zip のコード(${entry})を実行できない: ${e instanceof Error ? e.message : String(e)}`, "engine-unreadable", known);
843
847
  }
844
848
  };
845
- runScript(readFileSync4(MOMENT_FILE, "utf8"), MOMENT_FILE);
849
+ runScript(readFileSync4(momentFile, "utf8"), momentFile);
846
850
  runScript(src.momentTimezone, `${src.from}!${MOMENT_TZ_ENTRY}`, MOMENT_TZ_ENTRY);
847
851
  runScript(src.bignumber, `${src.from}!${BIGNUMBER_ENTRY}`, BIGNUMBER_ENTRY);
848
852
  if (src.decimal) runScript(src.decimal, `${src.from}!${DECIMAL_ENTRY}`, DECIMAL_ENTRY);
@@ -3076,7 +3080,7 @@ function calcSettings(input) {
3076
3080
 
3077
3081
  // src/worker/eval-run.ts
3078
3082
  async function runEvalJob(job) {
3079
- const engine = await loadEngine({ pluginZip: job.pluginZip, devPluginDir: job.devPluginDir, mode: job.mode });
3083
+ const engine = await loadEngine({ pluginZip: job.pluginZip, devPluginDir: job.devPluginDir, mode: job.mode, vendorDir: job.vendorDir });
3080
3084
  if (job.baseUrl) engine.setContext({ baseUrl: job.baseUrl });
3081
3085
  const usersInfo = buildUsersInfo(job.usersSource);
3082
3086
  const usersLabel = job.usersOrigin.kind === "file" ? job.usersOrigin.file : "既定のテストデータ(架空)";
@@ -3093,7 +3097,16 @@ async function runEvalJob(job) {
3093
3097
  usersOrigin: job.usersOrigin
3094
3098
  });
3095
3099
  const outcome = {
3096
- normalize: { summary: res.summary, hasErrors: res.findings.hasErrors, warnings: res.findings.count("warning"), output: res.output, checkDiffs: res.checkDiffs },
3100
+ normalize: {
3101
+ summary: res.summary,
3102
+ hasErrors: res.findings.hasErrors,
3103
+ warnings: res.findings.count("warning"),
3104
+ findings: [...res.findings.items],
3105
+ output: res.output,
3106
+ checkDiffs: res.checkDiffs,
3107
+ appId: res.appId,
3108
+ size: res.size
3109
+ },
3097
3110
  engineWarnings: [...engine.warnings]
3098
3111
  };
3099
3112
  if (job.kind === "calc" && job.calc && !res.findings.hasErrors) {
@@ -3111,7 +3124,7 @@ async function runEvalJob(job) {
3111
3124
  usersInfo,
3112
3125
  usersOrigin: job.usersOrigin
3113
3126
  });
3114
- outcome.calc = { text: r.text, failed: r.records.some((x) => x.errors.length > 0 || x.failed) };
3127
+ outcome.calc = { text: r.text, failed: r.records.some((x) => x.errors.length > 0 || x.failed), records: r.records, contextLine: r.contextLine, notice: r.notice, summary: r.summary };
3115
3128
  }
3116
3129
  return outcome;
3117
3130
  }
@@ -3142,9 +3155,18 @@ function workerEntry() {
3142
3155
  async function runEval(job, opt = {}) {
3143
3156
  const timeoutSec = Math.min(Math.max(opt.timeoutSec ?? DEFAULT_TIMEOUT_SEC, 1), MAX_TIMEOUT_SEC);
3144
3157
  if (opt.inline) return runEvalJob(job);
3145
- const entry = workerEntry();
3158
+ let entry;
3159
+ if (opt.workerFile !== void 0) {
3160
+ if (!path13.isAbsolute(opt.workerFile) || !existsSync8(opt.workerFile)) throw new Error(`Worker の入口が無い(絶対パスで指定する): ${opt.workerFile}`);
3161
+ entry = opt.workerFile;
3162
+ } else entry = workerEntry();
3163
+ const capture = opt.captureOutput;
3146
3164
  return new Promise((resolve, reject) => {
3147
- const worker = new Worker(entry, { workerData: job, stdout: false, stderr: false });
3165
+ const worker = new Worker(entry, { workerData: job, stdout: capture !== void 0, stderr: capture !== void 0 });
3166
+ if (capture) {
3167
+ worker.stdout.on("data", (d) => capture(String(d), "stdout"));
3168
+ worker.stderr.on("data", (d) => capture(String(d), "stderr"));
3169
+ }
3148
3170
  let settled = false;
3149
3171
  const finish = (fn) => {
3150
3172
  if (settled) return;