@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 +4 -2
- package/dist/cli.mjs +33 -11
- package/dist/core.mjs +1361 -35
- package/dist/eval-worker.mjs +21 -8
- package/dist/types/commands/save.d.ts +82 -0
- package/dist/types/commands/take.d.ts +18 -0
- package/dist/types/commit-file.d.ts +38 -0
- package/dist/types/connections.d.ts +106 -0
- package/dist/types/core.d.ts +19 -11
- package/dist/types/engine.d.ts +4 -0
- package/dist/types/permission-error.d.ts +3 -0
- package/dist/types/permission.d.ts +10 -0
- package/dist/types/worker/eval-job.d.ts +18 -0
- package/dist/types/worker/run.d.ts +4 -0
- package/dist/types/workspace.d.ts +126 -0
- package/package.json +2 -1
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/
|
|
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 から写してまだ計算式用にしていないもの(`
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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(
|
|
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: {
|
|
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
|
-
|
|
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:
|
|
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;
|