@rex0220/formula-authoring-tools 0.3.0 → 0.3.1
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 +3 -2
- package/dist/cli.mjs +79 -11
- package/dist/core.mjs +79 -10
- package/dist/eval-worker.mjs +65 -9
- package/dist/types/commands/try.d.ts +7 -1
- package/dist/types/core.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ Projects/
|
|
|
30
30
|
- `src/meta.ts` … tools の版、扱う計算式プラグイン(プラグイン ID `FORMULA_PLUGIN_ID`、封筒の `FORMULA_CONFIG_PLUGIN_ID`、版の下限 `MIN_PLUGIN_VERSION`)、API の版(`SUPPORTED_API_VERSIONS`)、API の契約(`REQUIRED_API`)
|
|
31
31
|
- `src/commands/save.ts` … 保存の約束(formula MCP の `fml_save_settings` / `fml_update_fields` の本体。CLI は使わない)。新しい設定は同じ名前があれば保存しない、既存の設定は `fml_summary` の digest を照合して項目の行(`fieldcode`)を複数まとめて差し替える。normalize は Worker で行い、エラーがあれば確定しない。確定はロック付き(`commit-file.ts`)
|
|
32
32
|
- `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` で固定する
|
|
33
|
-
- `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
|
|
33
|
+
- `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 から機械生成。`scripts/verify-patterns.mjs` + `scripts/pattern-cases.mjs` … 計算パターン集の例を同梱のサンプルアプリと架空の値で `try` に評価させ、関数一覧の使用例も評価して `docs/計算パターン_検証.md` を作る(`test/docs-sync.test.mjs` が最新と全部一致を確かめる = エンジンの版が変わったときの回帰検査)
|
|
34
34
|
- `pending/` … print-craft から写してまだ計算式用にしていないもの(`diff.ts` / `normalize.ts` は計算式用の `src/commands/` が別にあるので使っていない)。npm には入らない
|
|
35
35
|
- `test/` … `node --test`(src の .ts を直接読む。`test/helpers.mjs` が fixture の zip を読む。`test/fixtures/formula/` はテスト用アプリ 2305 の項目定義と設定)
|
|
36
36
|
|
|
@@ -60,7 +60,8 @@ npm pack --dry-run # 公開前に中身を確かめる(dist、vendor、LICENS
|
|
|
60
60
|
|
|
61
61
|
## 履歴
|
|
62
62
|
|
|
63
|
-
- 0.3.
|
|
63
|
+
- 0.3.1(2026-10): normalize がスペース項目(要素 ID のある SPACER)とヘッダーの行の計算式を `field.type` で誤って止めていたのを直した(設定画面は許す)。`try` でスペース項目を対象にできる(`--as sp1` / `SPACER`。標本に `sp1` を足した。レコードに値が無いので式の返り値をそのまま返す)
|
|
64
|
+
- 0.3.0(2026-10): 計算パターン集の例の検証(`docs/計算パターン_検証.md`。gen-docs)。式の試し計算 `try`(計画 25 章。formula MCP 0.2.0 の `fml_try`)。同梱のサンプルアプリ(`src/sample-app/`)に 1 行だけの設定を組んで normalize → calc。架空の項目と値(`src/commands/try-data.ts`: `--fields` で標本に項目を足す、`--data` で簡略形の値のレコード、`--app` で利用者のアプリの項目定義に架空の値)。`core` に `trySettings` / `SAMPLE_APP` / `describeSampleApp`、評価の仕事の kind `try`(`EvalJob` の `settingsText` / `settingsFile` / `fields` は try では省略できる)、`calcSettings` の `allowDefaultBaseUrl`。`CORE_API_VERSION` は 2 のまま(足しただけ)
|
|
64
65
|
- 0.2.1(2026-10): formula MCP の Codex レビューへの対応。`updateFields` の `common` を共通の設定のキー(`COMMON_KEYS`)だけに。Worker の中の誤りの種類(`EvalError.kind`)と名前を中核が instanceof で決める(zip のコードの例外の name / constructor.name / 文を応答や標準エラーに出さない。`classifyEvalError` / `builtinErrorName`)。保存の結果の想定外の誤りも種類だけ(`zipCode`)。CLI の想定外の誤りは種類だけ(`FORMULA_DEBUG=1` で文とスタック)。接続の誤りの文の print-craft の名残を直した
|
|
65
66
|
- 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 のレコードごとの行
|
|
66
67
|
- 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.3.
|
|
2
|
+
/*! @rex0220/formula-authoring-tools 0.3.1 (c) rex0220. MIT License. 計算式エンジンと計算式プラグインのコードは利用者の計算式プラグインプラグインの zip から読む(このファイルには含まれない)。commit 3788114. */
|
|
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.3.
|
|
358
|
+
if (true) return JSON.parse('{"toolsVersion":"0.3.1","pluginId":"dhgihghfgfmibkdldkeibboabfnmdpke","minPluginVersion":101,"supportedApiVersions":[1],"commit":"3788114","builtAt":"2026-10-18T08:08:27.541Z","mode":"build"}');
|
|
359
359
|
return devMeta();
|
|
360
360
|
}
|
|
361
361
|
|
|
@@ -3613,6 +3613,14 @@ var fields_default = {
|
|
|
3613
3613
|
width: "250",
|
|
3614
3614
|
innerHeight: "125"
|
|
3615
3615
|
}
|
|
3616
|
+
},
|
|
3617
|
+
{
|
|
3618
|
+
type: "SPACER",
|
|
3619
|
+
elementId: "sp1",
|
|
3620
|
+
size: {
|
|
3621
|
+
width: "458",
|
|
3622
|
+
height: "163"
|
|
3623
|
+
}
|
|
3616
3624
|
}
|
|
3617
3625
|
]
|
|
3618
3626
|
},
|
|
@@ -3998,14 +4006,14 @@ var fields_default = {
|
|
|
3998
4006
|
type: "RADIO_BUTTON",
|
|
3999
4007
|
code: "T1_ラジオ",
|
|
4000
4008
|
size: {
|
|
4001
|
-
width: "
|
|
4009
|
+
width: "227"
|
|
4002
4010
|
}
|
|
4003
4011
|
},
|
|
4004
4012
|
{
|
|
4005
4013
|
type: "CHECK_BOX",
|
|
4006
4014
|
code: "T1_チェック",
|
|
4007
4015
|
size: {
|
|
4008
|
-
width: "
|
|
4016
|
+
width: "227"
|
|
4009
4017
|
}
|
|
4010
4018
|
},
|
|
4011
4019
|
{
|
|
@@ -4019,7 +4027,7 @@ var fields_default = {
|
|
|
4019
4027
|
type: "DROP_DOWN",
|
|
4020
4028
|
code: "T1_ドロップダウン",
|
|
4021
4029
|
size: {
|
|
4022
|
-
width: "
|
|
4030
|
+
width: "158"
|
|
4023
4031
|
}
|
|
4024
4032
|
},
|
|
4025
4033
|
{
|
|
@@ -4125,14 +4133,14 @@ var fields_default = {
|
|
|
4125
4133
|
type: "RADIO_BUTTON",
|
|
4126
4134
|
code: "T2_ラジオ",
|
|
4127
4135
|
size: {
|
|
4128
|
-
width: "
|
|
4136
|
+
width: "227"
|
|
4129
4137
|
}
|
|
4130
4138
|
},
|
|
4131
4139
|
{
|
|
4132
4140
|
type: "CHECK_BOX",
|
|
4133
4141
|
code: "T2_チェック",
|
|
4134
4142
|
size: {
|
|
4135
|
-
width: "
|
|
4143
|
+
width: "227"
|
|
4136
4144
|
}
|
|
4137
4145
|
},
|
|
4138
4146
|
{
|
|
@@ -4146,7 +4154,7 @@ var fields_default = {
|
|
|
4146
4154
|
type: "DROP_DOWN",
|
|
4147
4155
|
code: "T2_ドロップダウン",
|
|
4148
4156
|
size: {
|
|
4149
|
-
width: "
|
|
4157
|
+
width: "158"
|
|
4150
4158
|
}
|
|
4151
4159
|
},
|
|
4152
4160
|
{
|
|
@@ -5540,6 +5548,17 @@ var SAMPLE_APP = {
|
|
|
5540
5548
|
function sampleApp() {
|
|
5541
5549
|
return structuredClone(SAMPLE_APP);
|
|
5542
5550
|
}
|
|
5551
|
+
function spacerIds(layout) {
|
|
5552
|
+
const out = [];
|
|
5553
|
+
for (const row of layout) {
|
|
5554
|
+
if (row.type === "GROUP" && Array.isArray(row.layout)) {
|
|
5555
|
+
out.push(...spacerIds(row.layout));
|
|
5556
|
+
continue;
|
|
5557
|
+
}
|
|
5558
|
+
for (const f of row.fields ?? []) if (f.type === "SPACER" && typeof f.elementId === "string" && f.elementId) out.push(f.elementId);
|
|
5559
|
+
}
|
|
5560
|
+
return out;
|
|
5561
|
+
}
|
|
5543
5562
|
var tableRows = (record, code) => {
|
|
5544
5563
|
const v = record[code]?.value;
|
|
5545
5564
|
return Array.isArray(v) ? v.length : 0;
|
|
@@ -5554,6 +5573,7 @@ function describeSampleApp(targetTypes) {
|
|
|
5554
5573
|
"項目(1 項目 1 行。○ = 計算式を設定できる型。テーブルの子は T1_ / T2_ の接頭辞):",
|
|
5555
5574
|
listFields(fields, { targetTypes }),
|
|
5556
5575
|
"",
|
|
5576
|
+
`スペース項目(要素 ID。as に要素 ID か SPACER。レコードに値は無く、式の返り値をそのまま返す): ${spacerIds(fields.layout).join(", ") || "無し"}`,
|
|
5557
5577
|
"レコード(try の records で選ぶ。既定は 3 件とも):",
|
|
5558
5578
|
...records.map((r) => ` ${r.id}: ${r.label}。${tables.map((t) => `${t} ${tableRows(r.record, t)} 行`).join("、")}`),
|
|
5559
5579
|
"ユーザー・組織・グループの値は既定のテストデータ(架空の 10 人。demo_user01 …)のコード"
|
|
@@ -5811,7 +5831,7 @@ async function normalizeSettings(input) {
|
|
|
5811
5831
|
for (const r of rows) {
|
|
5812
5832
|
const where = `${file} ${r.fieldcode}`;
|
|
5813
5833
|
const t = typeOf(r);
|
|
5814
|
-
if (r.formulaSet && r.formulaSet.trim() && !targetTypes.has(t)) findings.error("field.type", where, `${t} の項目には計算式を設定できない`);
|
|
5834
|
+
if (r.formulaSet && r.formulaSet.trim() && !targetTypes.has(t) && !isSpacerRow(r) && !isHeaderRow(r)) findings.error("field.type", where, `${t} の項目には計算式を設定できない`);
|
|
5815
5835
|
if (r.edit && (isHeaderRow(r) || isSpacerRow(r) || isGroupRow(r))) findings.error("field.flag", where, `${t} に「編集」は付けられない`);
|
|
5816
5836
|
if (r.hide && (isHeaderRow(r) || isSpacerRow(r))) findings.error("field.flag", where, `${t} に「非表示」は付けられない`);
|
|
5817
5837
|
if (r.state && r.formulaSet && isLookupRow(r)) findings.warning("field.lookup", where, "ルックアップ項目に計算式(ルックアップの値を上書きする)");
|
|
@@ -6494,12 +6514,33 @@ var TRY_MAX_FORMULA_CHARS = 4e3;
|
|
|
6494
6514
|
var TRY_DEFAULT_AS = "TEXT";
|
|
6495
6515
|
var TRY_OWN_RULES = /* @__PURE__ */ new Set(["misc.changed", "fieldno.screen", "size.bytes", "schema.derived"]);
|
|
6496
6516
|
var isTable2 = (v) => Array.isArray(v) && v.every((r) => r && typeof r === "object" && "value" in r);
|
|
6517
|
+
function spacersOf(fields) {
|
|
6518
|
+
const out = [];
|
|
6519
|
+
const walk = (rows) => {
|
|
6520
|
+
for (const row of rows) {
|
|
6521
|
+
if (row.type === "GROUP" && Array.isArray(row.layout)) {
|
|
6522
|
+
walk(row.layout);
|
|
6523
|
+
continue;
|
|
6524
|
+
}
|
|
6525
|
+
for (const f of row.fields ?? []) if (f.type === "SPACER" && typeof f.elementId === "string" && f.elementId) out.push(f.elementId);
|
|
6526
|
+
}
|
|
6527
|
+
};
|
|
6528
|
+
walk(fields.layout);
|
|
6529
|
+
return out;
|
|
6530
|
+
}
|
|
6497
6531
|
function resolveTryTarget(fields, targetTypes, as, table, referenced = /* @__PURE__ */ new Set()) {
|
|
6498
6532
|
const props = fields.properties;
|
|
6499
6533
|
const tables = Object.values(props).filter((p) => p.type === "SUBTABLE").map((p) => p.code).sort();
|
|
6500
6534
|
const want = (as ?? TRY_DEFAULT_AS).trim();
|
|
6501
6535
|
if (!want) throw new InputError("as が空(型 NUMBER / TEXT / DATE … か項目コード)");
|
|
6502
6536
|
const upper = want.toUpperCase();
|
|
6537
|
+
const spacers = spacersOf(fields);
|
|
6538
|
+
const spacer = upper === "SPACER" || upper === "SPACE" ? spacers[0] : spacers.find((id) => id === want || `(${id})` === want);
|
|
6539
|
+
if (upper === "SPACER" || upper === "SPACE" || spacer !== void 0) {
|
|
6540
|
+
if (table !== void 0) throw new InputError("スペース項目はテーブルの中に置けない(table を外す)");
|
|
6541
|
+
if (spacer === void 0) throw new InputError("サンプルアプリに要素 ID のあるスペース項目が無い");
|
|
6542
|
+
return { code: `(${spacer})`, type: "SPACER", elementId: spacer };
|
|
6543
|
+
}
|
|
6503
6544
|
const type = TYPE_ALIASES[upper] ?? (targetTypes.has(upper) ? upper : void 0);
|
|
6504
6545
|
let scope = props;
|
|
6505
6546
|
let tableCode;
|
|
@@ -6550,6 +6591,16 @@ function buildTryEnvelope(engine, fields, code, formula, lang) {
|
|
|
6550
6591
|
pluginInfos: [{ fieldcode: code, state: true, formulaSet: formula, edit: false, hide: false, rank: "" }]
|
|
6551
6592
|
};
|
|
6552
6593
|
}
|
|
6594
|
+
function normalizedMarks(n, code) {
|
|
6595
|
+
const scan = n.scans.get(code);
|
|
6596
|
+
if (!scan) return [];
|
|
6597
|
+
const used = Object.keys(scan.functions);
|
|
6598
|
+
const marks = [];
|
|
6599
|
+
if (used.some((f) => /^(UINFO|OINFO|GINFO|OUSERS|GUSERS)$/.test(f))) marks.push("ユーザー情報は既定のテストデータ(架空)で計算(実機では変わる)");
|
|
6600
|
+
if (used.includes("EV_INFO")) marks.push("イベントに依る");
|
|
6601
|
+
if (used.some((f) => /^(RAND|RANDBETWEEN|NOW|TODAY|UUID)$/.test(f))) marks.push("再計算で変わる");
|
|
6602
|
+
return marks;
|
|
6603
|
+
}
|
|
6553
6604
|
function plain(v) {
|
|
6554
6605
|
try {
|
|
6555
6606
|
const s = JSON.stringify(v);
|
|
@@ -6711,6 +6762,23 @@ async function trySettings(input) {
|
|
|
6711
6762
|
}
|
|
6712
6763
|
contextLine ||= c.contextLine;
|
|
6713
6764
|
calcNotice ||= c.notice;
|
|
6765
|
+
if (target.type === "SPACER") {
|
|
6766
|
+
for (const r of c.records) {
|
|
6767
|
+
const src = picked.find((p) => p.id === r.id);
|
|
6768
|
+
engine.clearSinkCalls();
|
|
6769
|
+
const raw = src ? rawValueOf(engine, n.saved.pluginPP, structuredClone(src.record), stripped, target, input.evType, input.event) : null;
|
|
6770
|
+
const v = { record: r.id, value: raw };
|
|
6771
|
+
if (engine.sinkCalls().length) {
|
|
6772
|
+
v.failed = true;
|
|
6773
|
+
v.error = `実行時の sink が呼ばれた: ${engine.sinkCalls().map((s) => `${s.sink} ${s.detail}`.trim()).join(", ")}`;
|
|
6774
|
+
res.status = "failed";
|
|
6775
|
+
}
|
|
6776
|
+
engine.clearSinkCalls();
|
|
6777
|
+
res.values.push(v);
|
|
6778
|
+
}
|
|
6779
|
+
res.marks = [...normalizedMarks(n, target.code), "スペース項目(返り値をそのまま。表示は実機で)"];
|
|
6780
|
+
continue;
|
|
6781
|
+
}
|
|
6714
6782
|
const lineCode = target.table ?? target.code;
|
|
6715
6783
|
for (const r of c.records) {
|
|
6716
6784
|
const line = r.lines.find((l) => l.fieldcode === lineCode);
|
|
@@ -6743,7 +6811,7 @@ async function trySettings(input) {
|
|
|
6743
6811
|
if (extraFields.length) text2.push(`足した項目(架空): ${extraFields.join("、")}`);
|
|
6744
6812
|
for (const r of picked) if (r.id.startsWith("d")) text2.push(`レコード ${r.id}: ${r.label}`);
|
|
6745
6813
|
for (const r of results) {
|
|
6746
|
-
const head = r.target ? `${r.target.code}(${r.target.type}${r.target.table ? `。${r.target.table} の中` : ""})← ` : "";
|
|
6814
|
+
const head = r.target ? `${r.target.type === "SPACER" ? `スペース項目 ${r.target.elementId}` : r.target.code}(${r.target.type}${r.target.table ? `。${r.target.table} の中` : ""})← ` : "";
|
|
6747
6815
|
text2.push(`[${r.index}] ${head}${r.formula.replace(/\s*\n\s*/g, " ")}`);
|
|
6748
6816
|
for (const v of r.values) text2.push(` レコード ${v.record}: ${short2(v.value)}${v.raw !== void 0 ? `(生の値 ${short2(v.raw)})` : ""}${v.error ? ` ! ${v.error}` : ""}${v.failed ? " ! 結果を採らない(実行時の sink)" : ""}`);
|
|
6749
6817
|
if (r.marks.length) text2.push(` 印: ${r.marks.join("、")}`);
|
|
@@ -6945,7 +7013,7 @@ var USAGE = `使い方: npx @rex0220/formula-authoring-tools <command> [options]
|
|
|
6945
7013
|
[--fields '<JSON>'] [--data '<JSON>'] [--base 3] [--app N]
|
|
6946
7014
|
同梱のサンプルアプリ(全項目タイプ × 2、テーブル 2 つ、レコード 3 件 = 典型 / 端 / 空)に 1 行だけの設定を組んで
|
|
6947
7015
|
検査と模擬の計算(関数の使い方や式の断片を確かめる。利用者のアプリは使わない)。--as は型(NUMBER / TEXT / DATE …。
|
|
6948
|
-
既定 TEXT = 文字列 1 行)か項目コード、テーブルの中で計算するなら --table テーブル1
|
|
7016
|
+
既定 TEXT = 文字列 1 行)か項目コード、テーブルの中で計算するなら --table テーブル1。スペース項目は --as sp1(返り値をそのまま)。
|
|
6949
7017
|
--fields '[{"code":"単価","type":"NUMBER"},{"code":"分類","type":"DROP_DOWN","options":["明細","小計"]}]' で項目を足す、
|
|
6950
7018
|
--data '[{"単価":100,"テーブル1":[{"T1_数値":1}]}]' で架空の値のレコード(土台は --base の標本。既定 3 = 空)、
|
|
6951
7019
|
--app N で利用者のアプリの項目定義(fields/N.json かアプリのフォルダー)に --data の架空の値を当てる
|
package/dist/core.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @rex0220/formula-authoring-tools/core 0.3.
|
|
1
|
+
/*! @rex0220/formula-authoring-tools/core 0.3.1 (c) rex0220. MIT License. 計算式エンジンと計算式プラグインのコードは利用者の計算式プラグインプラグインの zip から読む(このファイルには含まれない)。commit 3788114. */
|
|
2
2
|
|
|
3
3
|
// src/safe-path.ts
|
|
4
4
|
import { lstatSync, realpathSync } from "node:fs";
|
|
@@ -190,7 +190,7 @@ function devMeta() {
|
|
|
190
190
|
return { toolsVersion: pkg.version, pluginId: FORMULA_PLUGIN_ID, minPluginVersion: MIN_PLUGIN_VERSION, supportedApiVersions: [...SUPPORTED_API_VERSIONS], commit: gitCommit(AUTHORING_ROOT), builtAt: "", mode: "dev" };
|
|
191
191
|
}
|
|
192
192
|
function toolsMeta() {
|
|
193
|
-
if (true) return JSON.parse('{"toolsVersion":"0.3.
|
|
193
|
+
if (true) return JSON.parse('{"toolsVersion":"0.3.1","pluginId":"dhgihghfgfmibkdldkeibboabfnmdpke","minPluginVersion":101,"supportedApiVersions":[1],"commit":"3788114","builtAt":"2026-10-18T08:08:27.541Z","mode":"build"}');
|
|
194
194
|
return devMeta();
|
|
195
195
|
}
|
|
196
196
|
|
|
@@ -2078,7 +2078,7 @@ async function normalizeSettings(input) {
|
|
|
2078
2078
|
for (const r of rows) {
|
|
2079
2079
|
const where = `${file} ${r.fieldcode}`;
|
|
2080
2080
|
const t = typeOf(r);
|
|
2081
|
-
if (r.formulaSet && r.formulaSet.trim() && !targetTypes.has(t)) findings.error("field.type", where, `${t} の項目には計算式を設定できない`);
|
|
2081
|
+
if (r.formulaSet && r.formulaSet.trim() && !targetTypes.has(t) && !isSpacerRow(r) && !isHeaderRow(r)) findings.error("field.type", where, `${t} の項目には計算式を設定できない`);
|
|
2082
2082
|
if (r.edit && (isHeaderRow(r) || isSpacerRow(r) || isGroupRow(r))) findings.error("field.flag", where, `${t} に「編集」は付けられない`);
|
|
2083
2083
|
if (r.hide && (isHeaderRow(r) || isSpacerRow(r))) findings.error("field.flag", where, `${t} に「非表示」は付けられない`);
|
|
2084
2084
|
if (r.state && r.formulaSet && isLookupRow(r)) findings.warning("field.lookup", where, "ルックアップ項目に計算式(ルックアップの値を上書きする)");
|
|
@@ -4269,6 +4269,14 @@ var fields_default = {
|
|
|
4269
4269
|
width: "250",
|
|
4270
4270
|
innerHeight: "125"
|
|
4271
4271
|
}
|
|
4272
|
+
},
|
|
4273
|
+
{
|
|
4274
|
+
type: "SPACER",
|
|
4275
|
+
elementId: "sp1",
|
|
4276
|
+
size: {
|
|
4277
|
+
width: "458",
|
|
4278
|
+
height: "163"
|
|
4279
|
+
}
|
|
4272
4280
|
}
|
|
4273
4281
|
]
|
|
4274
4282
|
},
|
|
@@ -4654,14 +4662,14 @@ var fields_default = {
|
|
|
4654
4662
|
type: "RADIO_BUTTON",
|
|
4655
4663
|
code: "T1_ラジオ",
|
|
4656
4664
|
size: {
|
|
4657
|
-
width: "
|
|
4665
|
+
width: "227"
|
|
4658
4666
|
}
|
|
4659
4667
|
},
|
|
4660
4668
|
{
|
|
4661
4669
|
type: "CHECK_BOX",
|
|
4662
4670
|
code: "T1_チェック",
|
|
4663
4671
|
size: {
|
|
4664
|
-
width: "
|
|
4672
|
+
width: "227"
|
|
4665
4673
|
}
|
|
4666
4674
|
},
|
|
4667
4675
|
{
|
|
@@ -4675,7 +4683,7 @@ var fields_default = {
|
|
|
4675
4683
|
type: "DROP_DOWN",
|
|
4676
4684
|
code: "T1_ドロップダウン",
|
|
4677
4685
|
size: {
|
|
4678
|
-
width: "
|
|
4686
|
+
width: "158"
|
|
4679
4687
|
}
|
|
4680
4688
|
},
|
|
4681
4689
|
{
|
|
@@ -4781,14 +4789,14 @@ var fields_default = {
|
|
|
4781
4789
|
type: "RADIO_BUTTON",
|
|
4782
4790
|
code: "T2_ラジオ",
|
|
4783
4791
|
size: {
|
|
4784
|
-
width: "
|
|
4792
|
+
width: "227"
|
|
4785
4793
|
}
|
|
4786
4794
|
},
|
|
4787
4795
|
{
|
|
4788
4796
|
type: "CHECK_BOX",
|
|
4789
4797
|
code: "T2_チェック",
|
|
4790
4798
|
size: {
|
|
4791
|
-
width: "
|
|
4799
|
+
width: "227"
|
|
4792
4800
|
}
|
|
4793
4801
|
},
|
|
4794
4802
|
{
|
|
@@ -4802,7 +4810,7 @@ var fields_default = {
|
|
|
4802
4810
|
type: "DROP_DOWN",
|
|
4803
4811
|
code: "T2_ドロップダウン",
|
|
4804
4812
|
size: {
|
|
4805
|
-
width: "
|
|
4813
|
+
width: "158"
|
|
4806
4814
|
}
|
|
4807
4815
|
},
|
|
4808
4816
|
{
|
|
@@ -6196,6 +6204,17 @@ var SAMPLE_APP = {
|
|
|
6196
6204
|
function sampleApp() {
|
|
6197
6205
|
return structuredClone(SAMPLE_APP);
|
|
6198
6206
|
}
|
|
6207
|
+
function spacerIds(layout) {
|
|
6208
|
+
const out = [];
|
|
6209
|
+
for (const row of layout) {
|
|
6210
|
+
if (row.type === "GROUP" && Array.isArray(row.layout)) {
|
|
6211
|
+
out.push(...spacerIds(row.layout));
|
|
6212
|
+
continue;
|
|
6213
|
+
}
|
|
6214
|
+
for (const f of row.fields ?? []) if (f.type === "SPACER" && typeof f.elementId === "string" && f.elementId) out.push(f.elementId);
|
|
6215
|
+
}
|
|
6216
|
+
return out;
|
|
6217
|
+
}
|
|
6199
6218
|
var tableRows = (record, code) => {
|
|
6200
6219
|
const v = record[code]?.value;
|
|
6201
6220
|
return Array.isArray(v) ? v.length : 0;
|
|
@@ -6210,6 +6229,7 @@ function describeSampleApp(targetTypes) {
|
|
|
6210
6229
|
"項目(1 項目 1 行。○ = 計算式を設定できる型。テーブルの子は T1_ / T2_ の接頭辞):",
|
|
6211
6230
|
listFields(fields, { targetTypes }),
|
|
6212
6231
|
"",
|
|
6232
|
+
`スペース項目(要素 ID。as に要素 ID か SPACER。レコードに値は無く、式の返り値をそのまま返す): ${spacerIds(fields.layout).join(", ") || "無し"}`,
|
|
6213
6233
|
"レコード(try の records で選ぶ。既定は 3 件とも):",
|
|
6214
6234
|
...records.map((r) => ` ${r.id}: ${r.label}。${tables.map((t) => `${t} ${tableRows(r.record, t)} 行`).join("、")}`),
|
|
6215
6235
|
"ユーザー・組織・グループの値は既定のテストデータ(架空の 10 人。demo_user01 …)のコード"
|
|
@@ -6631,12 +6651,33 @@ var TRY_MAX_FORMULA_CHARS = 4e3;
|
|
|
6631
6651
|
var TRY_DEFAULT_AS = "TEXT";
|
|
6632
6652
|
var TRY_OWN_RULES = /* @__PURE__ */ new Set(["misc.changed", "fieldno.screen", "size.bytes", "schema.derived"]);
|
|
6633
6653
|
var isTable2 = (v) => Array.isArray(v) && v.every((r) => r && typeof r === "object" && "value" in r);
|
|
6654
|
+
function spacersOf(fields) {
|
|
6655
|
+
const out = [];
|
|
6656
|
+
const walk = (rows) => {
|
|
6657
|
+
for (const row of rows) {
|
|
6658
|
+
if (row.type === "GROUP" && Array.isArray(row.layout)) {
|
|
6659
|
+
walk(row.layout);
|
|
6660
|
+
continue;
|
|
6661
|
+
}
|
|
6662
|
+
for (const f of row.fields ?? []) if (f.type === "SPACER" && typeof f.elementId === "string" && f.elementId) out.push(f.elementId);
|
|
6663
|
+
}
|
|
6664
|
+
};
|
|
6665
|
+
walk(fields.layout);
|
|
6666
|
+
return out;
|
|
6667
|
+
}
|
|
6634
6668
|
function resolveTryTarget(fields, targetTypes, as, table, referenced = /* @__PURE__ */ new Set()) {
|
|
6635
6669
|
const props = fields.properties;
|
|
6636
6670
|
const tables = Object.values(props).filter((p) => p.type === "SUBTABLE").map((p) => p.code).sort();
|
|
6637
6671
|
const want = (as ?? TRY_DEFAULT_AS).trim();
|
|
6638
6672
|
if (!want) throw new InputError("as が空(型 NUMBER / TEXT / DATE … か項目コード)");
|
|
6639
6673
|
const upper = want.toUpperCase();
|
|
6674
|
+
const spacers = spacersOf(fields);
|
|
6675
|
+
const spacer = upper === "SPACER" || upper === "SPACE" ? spacers[0] : spacers.find((id) => id === want || `(${id})` === want);
|
|
6676
|
+
if (upper === "SPACER" || upper === "SPACE" || spacer !== void 0) {
|
|
6677
|
+
if (table !== void 0) throw new InputError("スペース項目はテーブルの中に置けない(table を外す)");
|
|
6678
|
+
if (spacer === void 0) throw new InputError("サンプルアプリに要素 ID のあるスペース項目が無い");
|
|
6679
|
+
return { code: `(${spacer})`, type: "SPACER", elementId: spacer };
|
|
6680
|
+
}
|
|
6640
6681
|
const type = TYPE_ALIASES[upper] ?? (targetTypes.has(upper) ? upper : void 0);
|
|
6641
6682
|
let scope = props;
|
|
6642
6683
|
let tableCode;
|
|
@@ -6687,6 +6728,16 @@ function buildTryEnvelope(engine, fields, code, formula, lang) {
|
|
|
6687
6728
|
pluginInfos: [{ fieldcode: code, state: true, formulaSet: formula, edit: false, hide: false, rank: "" }]
|
|
6688
6729
|
};
|
|
6689
6730
|
}
|
|
6731
|
+
function normalizedMarks(n, code) {
|
|
6732
|
+
const scan = n.scans.get(code);
|
|
6733
|
+
if (!scan) return [];
|
|
6734
|
+
const used = Object.keys(scan.functions);
|
|
6735
|
+
const marks = [];
|
|
6736
|
+
if (used.some((f) => /^(UINFO|OINFO|GINFO|OUSERS|GUSERS)$/.test(f))) marks.push("ユーザー情報は既定のテストデータ(架空)で計算(実機では変わる)");
|
|
6737
|
+
if (used.includes("EV_INFO")) marks.push("イベントに依る");
|
|
6738
|
+
if (used.some((f) => /^(RAND|RANDBETWEEN|NOW|TODAY|UUID)$/.test(f))) marks.push("再計算で変わる");
|
|
6739
|
+
return marks;
|
|
6740
|
+
}
|
|
6690
6741
|
function plain(v) {
|
|
6691
6742
|
try {
|
|
6692
6743
|
const s = JSON.stringify(v);
|
|
@@ -6848,6 +6899,23 @@ async function trySettings(input) {
|
|
|
6848
6899
|
}
|
|
6849
6900
|
contextLine ||= c.contextLine;
|
|
6850
6901
|
calcNotice ||= c.notice;
|
|
6902
|
+
if (target.type === "SPACER") {
|
|
6903
|
+
for (const r of c.records) {
|
|
6904
|
+
const src = picked.find((p) => p.id === r.id);
|
|
6905
|
+
engine.clearSinkCalls();
|
|
6906
|
+
const raw = src ? rawValueOf(engine, n.saved.pluginPP, structuredClone(src.record), stripped, target, input.evType, input.event) : null;
|
|
6907
|
+
const v = { record: r.id, value: raw };
|
|
6908
|
+
if (engine.sinkCalls().length) {
|
|
6909
|
+
v.failed = true;
|
|
6910
|
+
v.error = `実行時の sink が呼ばれた: ${engine.sinkCalls().map((s) => `${s.sink} ${s.detail}`.trim()).join(", ")}`;
|
|
6911
|
+
res.status = "failed";
|
|
6912
|
+
}
|
|
6913
|
+
engine.clearSinkCalls();
|
|
6914
|
+
res.values.push(v);
|
|
6915
|
+
}
|
|
6916
|
+
res.marks = [...normalizedMarks(n, target.code), "スペース項目(返り値をそのまま。表示は実機で)"];
|
|
6917
|
+
continue;
|
|
6918
|
+
}
|
|
6851
6919
|
const lineCode = target.table ?? target.code;
|
|
6852
6920
|
for (const r of c.records) {
|
|
6853
6921
|
const line = r.lines.find((l) => l.fieldcode === lineCode);
|
|
@@ -6880,7 +6948,7 @@ async function trySettings(input) {
|
|
|
6880
6948
|
if (extraFields.length) text2.push(`足した項目(架空): ${extraFields.join("、")}`);
|
|
6881
6949
|
for (const r of picked) if (r.id.startsWith("d")) text2.push(`レコード ${r.id}: ${r.label}`);
|
|
6882
6950
|
for (const r of results) {
|
|
6883
|
-
const head = r.target ? `${r.target.code}(${r.target.type}${r.target.table ? `。${r.target.table} の中` : ""})← ` : "";
|
|
6951
|
+
const head = r.target ? `${r.target.type === "SPACER" ? `スペース項目 ${r.target.elementId}` : r.target.code}(${r.target.type}${r.target.table ? `。${r.target.table} の中` : ""})← ` : "";
|
|
6884
6952
|
text2.push(`[${r.index}] ${head}${r.formula.replace(/\s*\n\s*/g, " ")}`);
|
|
6885
6953
|
for (const v of r.values) text2.push(` レコード ${v.record}: ${short2(v.value)}${v.raw !== void 0 ? `(生の値 ${short2(v.raw)})` : ""}${v.error ? ` ! ${v.error}` : ""}${v.failed ? " ! 結果を採らない(実行時の sink)" : ""}`);
|
|
6886
6954
|
if (r.marks.length) text2.push(` 印: ${r.marks.join("、")}`);
|
|
@@ -7524,6 +7592,7 @@ export {
|
|
|
7524
7592
|
shapeLines,
|
|
7525
7593
|
snapshotNameOf,
|
|
7526
7594
|
snapshotOf,
|
|
7595
|
+
spacersOf,
|
|
7527
7596
|
stronglyConnected,
|
|
7528
7597
|
summarizeFields,
|
|
7529
7598
|
summarizeRecord,
|
package/dist/eval-worker.mjs
CHANGED
|
@@ -93,7 +93,7 @@ function devMeta() {
|
|
|
93
93
|
return { toolsVersion: pkg.version, pluginId: FORMULA_PLUGIN_ID, minPluginVersion: MIN_PLUGIN_VERSION, supportedApiVersions: [...SUPPORTED_API_VERSIONS], commit: gitCommit(AUTHORING_ROOT), builtAt: "", mode: "dev" };
|
|
94
94
|
}
|
|
95
95
|
function toolsMeta() {
|
|
96
|
-
if (true) return JSON.parse('{"toolsVersion":"0.3.
|
|
96
|
+
if (true) return JSON.parse('{"toolsVersion":"0.3.1","pluginId":"dhgihghfgfmibkdldkeibboabfnmdpke","minPluginVersion":101,"supportedApiVersions":[1],"commit":"3788114","builtAt":"2026-10-18T08:08:27.541Z","mode":"build"}');
|
|
97
97
|
return devMeta();
|
|
98
98
|
}
|
|
99
99
|
|
|
@@ -996,7 +996,7 @@ async function normalizeSettings(input) {
|
|
|
996
996
|
for (const r of rows) {
|
|
997
997
|
const where = `${file} ${r.fieldcode}`;
|
|
998
998
|
const t = typeOf(r);
|
|
999
|
-
if (r.formulaSet && r.formulaSet.trim() && !targetTypes.has(t)) findings.error("field.type", where, `${t} の項目には計算式を設定できない`);
|
|
999
|
+
if (r.formulaSet && r.formulaSet.trim() && !targetTypes.has(t) && !isSpacerRow(r) && !isHeaderRow(r)) findings.error("field.type", where, `${t} の項目には計算式を設定できない`);
|
|
1000
1000
|
if (r.edit && (isHeaderRow(r) || isSpacerRow(r) || isGroupRow(r))) findings.error("field.flag", where, `${t} に「編集」は付けられない`);
|
|
1001
1001
|
if (r.hide && (isHeaderRow(r) || isSpacerRow(r))) findings.error("field.flag", where, `${t} に「非表示」は付けられない`);
|
|
1002
1002
|
if (r.state && r.formulaSet && isLookupRow(r)) findings.warning("field.lookup", where, "ルックアップ項目に計算式(ルックアップの値を上書きする)");
|
|
@@ -2319,6 +2319,14 @@ var fields_default = {
|
|
|
2319
2319
|
width: "250",
|
|
2320
2320
|
innerHeight: "125"
|
|
2321
2321
|
}
|
|
2322
|
+
},
|
|
2323
|
+
{
|
|
2324
|
+
type: "SPACER",
|
|
2325
|
+
elementId: "sp1",
|
|
2326
|
+
size: {
|
|
2327
|
+
width: "458",
|
|
2328
|
+
height: "163"
|
|
2329
|
+
}
|
|
2322
2330
|
}
|
|
2323
2331
|
]
|
|
2324
2332
|
},
|
|
@@ -2704,14 +2712,14 @@ var fields_default = {
|
|
|
2704
2712
|
type: "RADIO_BUTTON",
|
|
2705
2713
|
code: "T1_ラジオ",
|
|
2706
2714
|
size: {
|
|
2707
|
-
width: "
|
|
2715
|
+
width: "227"
|
|
2708
2716
|
}
|
|
2709
2717
|
},
|
|
2710
2718
|
{
|
|
2711
2719
|
type: "CHECK_BOX",
|
|
2712
2720
|
code: "T1_チェック",
|
|
2713
2721
|
size: {
|
|
2714
|
-
width: "
|
|
2722
|
+
width: "227"
|
|
2715
2723
|
}
|
|
2716
2724
|
},
|
|
2717
2725
|
{
|
|
@@ -2725,7 +2733,7 @@ var fields_default = {
|
|
|
2725
2733
|
type: "DROP_DOWN",
|
|
2726
2734
|
code: "T1_ドロップダウン",
|
|
2727
2735
|
size: {
|
|
2728
|
-
width: "
|
|
2736
|
+
width: "158"
|
|
2729
2737
|
}
|
|
2730
2738
|
},
|
|
2731
2739
|
{
|
|
@@ -2831,14 +2839,14 @@ var fields_default = {
|
|
|
2831
2839
|
type: "RADIO_BUTTON",
|
|
2832
2840
|
code: "T2_ラジオ",
|
|
2833
2841
|
size: {
|
|
2834
|
-
width: "
|
|
2842
|
+
width: "227"
|
|
2835
2843
|
}
|
|
2836
2844
|
},
|
|
2837
2845
|
{
|
|
2838
2846
|
type: "CHECK_BOX",
|
|
2839
2847
|
code: "T2_チェック",
|
|
2840
2848
|
size: {
|
|
2841
|
-
width: "
|
|
2849
|
+
width: "227"
|
|
2842
2850
|
}
|
|
2843
2851
|
},
|
|
2844
2852
|
{
|
|
@@ -2852,7 +2860,7 @@ var fields_default = {
|
|
|
2852
2860
|
type: "DROP_DOWN",
|
|
2853
2861
|
code: "T2_ドロップダウン",
|
|
2854
2862
|
size: {
|
|
2855
|
-
width: "
|
|
2863
|
+
width: "158"
|
|
2856
2864
|
}
|
|
2857
2865
|
},
|
|
2858
2866
|
{
|
|
@@ -4661,12 +4669,33 @@ var TRY_MAX_FORMULA_CHARS = 4e3;
|
|
|
4661
4669
|
var TRY_DEFAULT_AS = "TEXT";
|
|
4662
4670
|
var TRY_OWN_RULES = /* @__PURE__ */ new Set(["misc.changed", "fieldno.screen", "size.bytes", "schema.derived"]);
|
|
4663
4671
|
var isTable2 = (v) => Array.isArray(v) && v.every((r) => r && typeof r === "object" && "value" in r);
|
|
4672
|
+
function spacersOf(fields) {
|
|
4673
|
+
const out = [];
|
|
4674
|
+
const walk = (rows) => {
|
|
4675
|
+
for (const row of rows) {
|
|
4676
|
+
if (row.type === "GROUP" && Array.isArray(row.layout)) {
|
|
4677
|
+
walk(row.layout);
|
|
4678
|
+
continue;
|
|
4679
|
+
}
|
|
4680
|
+
for (const f of row.fields ?? []) if (f.type === "SPACER" && typeof f.elementId === "string" && f.elementId) out.push(f.elementId);
|
|
4681
|
+
}
|
|
4682
|
+
};
|
|
4683
|
+
walk(fields.layout);
|
|
4684
|
+
return out;
|
|
4685
|
+
}
|
|
4664
4686
|
function resolveTryTarget(fields, targetTypes, as, table, referenced = /* @__PURE__ */ new Set()) {
|
|
4665
4687
|
const props = fields.properties;
|
|
4666
4688
|
const tables = Object.values(props).filter((p) => p.type === "SUBTABLE").map((p) => p.code).sort();
|
|
4667
4689
|
const want = (as ?? TRY_DEFAULT_AS).trim();
|
|
4668
4690
|
if (!want) throw new InputError("as が空(型 NUMBER / TEXT / DATE … か項目コード)");
|
|
4669
4691
|
const upper = want.toUpperCase();
|
|
4692
|
+
const spacers = spacersOf(fields);
|
|
4693
|
+
const spacer = upper === "SPACER" || upper === "SPACE" ? spacers[0] : spacers.find((id) => id === want || `(${id})` === want);
|
|
4694
|
+
if (upper === "SPACER" || upper === "SPACE" || spacer !== void 0) {
|
|
4695
|
+
if (table !== void 0) throw new InputError("スペース項目はテーブルの中に置けない(table を外す)");
|
|
4696
|
+
if (spacer === void 0) throw new InputError("サンプルアプリに要素 ID のあるスペース項目が無い");
|
|
4697
|
+
return { code: `(${spacer})`, type: "SPACER", elementId: spacer };
|
|
4698
|
+
}
|
|
4670
4699
|
const type = TYPE_ALIASES[upper] ?? (targetTypes.has(upper) ? upper : void 0);
|
|
4671
4700
|
let scope = props;
|
|
4672
4701
|
let tableCode;
|
|
@@ -4717,6 +4746,16 @@ function buildTryEnvelope(engine, fields, code, formula, lang) {
|
|
|
4717
4746
|
pluginInfos: [{ fieldcode: code, state: true, formulaSet: formula, edit: false, hide: false, rank: "" }]
|
|
4718
4747
|
};
|
|
4719
4748
|
}
|
|
4749
|
+
function normalizedMarks(n, code) {
|
|
4750
|
+
const scan = n.scans.get(code);
|
|
4751
|
+
if (!scan) return [];
|
|
4752
|
+
const used = Object.keys(scan.functions);
|
|
4753
|
+
const marks = [];
|
|
4754
|
+
if (used.some((f) => /^(UINFO|OINFO|GINFO|OUSERS|GUSERS)$/.test(f))) marks.push("ユーザー情報は既定のテストデータ(架空)で計算(実機では変わる)");
|
|
4755
|
+
if (used.includes("EV_INFO")) marks.push("イベントに依る");
|
|
4756
|
+
if (used.some((f) => /^(RAND|RANDBETWEEN|NOW|TODAY|UUID)$/.test(f))) marks.push("再計算で変わる");
|
|
4757
|
+
return marks;
|
|
4758
|
+
}
|
|
4720
4759
|
function plain(v) {
|
|
4721
4760
|
try {
|
|
4722
4761
|
const s = JSON.stringify(v);
|
|
@@ -4878,6 +4917,23 @@ async function trySettings(input) {
|
|
|
4878
4917
|
}
|
|
4879
4918
|
contextLine ||= c.contextLine;
|
|
4880
4919
|
calcNotice ||= c.notice;
|
|
4920
|
+
if (target.type === "SPACER") {
|
|
4921
|
+
for (const r of c.records) {
|
|
4922
|
+
const src = picked.find((p) => p.id === r.id);
|
|
4923
|
+
engine.clearSinkCalls();
|
|
4924
|
+
const raw = src ? rawValueOf(engine, n.saved.pluginPP, structuredClone(src.record), stripped, target, input.evType, input.event) : null;
|
|
4925
|
+
const v = { record: r.id, value: raw };
|
|
4926
|
+
if (engine.sinkCalls().length) {
|
|
4927
|
+
v.failed = true;
|
|
4928
|
+
v.error = `実行時の sink が呼ばれた: ${engine.sinkCalls().map((s) => `${s.sink} ${s.detail}`.trim()).join(", ")}`;
|
|
4929
|
+
res.status = "failed";
|
|
4930
|
+
}
|
|
4931
|
+
engine.clearSinkCalls();
|
|
4932
|
+
res.values.push(v);
|
|
4933
|
+
}
|
|
4934
|
+
res.marks = [...normalizedMarks(n, target.code), "スペース項目(返り値をそのまま。表示は実機で)"];
|
|
4935
|
+
continue;
|
|
4936
|
+
}
|
|
4881
4937
|
const lineCode = target.table ?? target.code;
|
|
4882
4938
|
for (const r of c.records) {
|
|
4883
4939
|
const line = r.lines.find((l) => l.fieldcode === lineCode);
|
|
@@ -4910,7 +4966,7 @@ async function trySettings(input) {
|
|
|
4910
4966
|
if (extraFields.length) text2.push(`足した項目(架空): ${extraFields.join("、")}`);
|
|
4911
4967
|
for (const r of picked) if (r.id.startsWith("d")) text2.push(`レコード ${r.id}: ${r.label}`);
|
|
4912
4968
|
for (const r of results) {
|
|
4913
|
-
const head = r.target ? `${r.target.code}(${r.target.type}${r.target.table ? `。${r.target.table} の中` : ""})← ` : "";
|
|
4969
|
+
const head = r.target ? `${r.target.type === "SPACER" ? `スペース項目 ${r.target.elementId}` : r.target.code}(${r.target.type}${r.target.table ? `。${r.target.table} の中` : ""})← ` : "";
|
|
4914
4970
|
text2.push(`[${r.index}] ${head}${r.formula.replace(/\s*\n\s*/g, " ")}`);
|
|
4915
4971
|
for (const v of r.values) text2.push(` レコード ${v.record}: ${short2(v.value)}${v.raw !== void 0 ? `(生の値 ${short2(v.raw)})` : ""}${v.error ? ` ! ${v.error}` : ""}${v.failed ? " ! 結果を採らない(実行時の sink)" : ""}`);
|
|
4916
4972
|
if (r.marks.length) text2.push(` 印: ${r.marks.join("、")}`);
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* - 対象は式が参照していない項目を選ぶ(数値1 を使う式なら 数値2)。自己参照になるときのために、1 行の設定は各種設定の circular_refer を ON にして組む(設定画面の検査だけの決まり)
|
|
8
8
|
* - 文脈は calc と同じ(イベント、ログインユーザー、mobile、ユーザー情報のテストデータ、policy の allowFunctions)。APP_URL はサンプルアプリの接続先(既定の例の URL)で評価する
|
|
9
9
|
* - 走査の上の制限(副作用のある関数は policy の承認、EV_SET は不可)も calc と同じ。結果を採らない sink も同じ
|
|
10
|
+
* - スペース項目(レイアウトの SPACER で要素 ID のあるもの。設定画面の行はフィールドコード "(要素ID)")も対象にできる(as: "SPACER" か要素 ID)。レコードに値が無いので calc は通らず、
|
|
11
|
+
* 実機のスペースの処理と同じく式を評価した返り値(ARRAY(ラベル, 書式, 値, 書式) や HTML)をそのまま返す。表示は実機で(計画 25.5。2026-10-18 Takashi)
|
|
10
12
|
* - 架空の項目と値(try-data.ts。計画 25.3): fields で標本に項目を足す(案 2)、data で簡略形の値のレコードを作る(案 1。土台は標本の base = 3 の空のレコード)、
|
|
11
13
|
* appFields で利用者のアプリの項目定義に架空の値(案 3。標本のレコードは使わない。data が要る)
|
|
12
14
|
*/
|
|
@@ -32,6 +34,8 @@ export interface TryTarget {
|
|
|
32
34
|
code: string;
|
|
33
35
|
type: string;
|
|
34
36
|
table?: string;
|
|
37
|
+
/** スペース項目のとき(type "SPACER")の要素 ID。code は "(要素ID)" */
|
|
38
|
+
elementId?: string;
|
|
35
39
|
}
|
|
36
40
|
export interface TryValue {
|
|
37
41
|
/** レコード(標本は "1"〜"3"、架空の値は "d1"〜) */
|
|
@@ -102,9 +106,11 @@ export interface TryResult {
|
|
|
102
106
|
summary: string;
|
|
103
107
|
text: string;
|
|
104
108
|
}
|
|
109
|
+
/** レイアウトのスペース項目(要素 ID のあるもの。グループ枠の中も)。設定画面の行と同じ順 */
|
|
110
|
+
export declare function spacersOf(fields: FieldsFile): string[];
|
|
105
111
|
/**
|
|
106
112
|
* 対象の項目を決める(型か項目コード、テーブルの中かどうか)。型のときは、式が参照していない項目を優先し、短いコードの順で最初(文字列1、数値1 …。数値1 を使う式なら 数値2)。
|
|
107
|
-
* referenced は式の識別子(scanFormula)
|
|
113
|
+
* referenced は式の識別子(scanFormula)。スペース項目は as: "SPACER"(最初の要素 ID)か要素 ID("sp1" / "(sp1)")
|
|
108
114
|
*/
|
|
109
115
|
export declare function resolveTryTarget(fields: FieldsFile, targetTypes: ReadonlySet<string>, as: string | undefined, table: string | undefined, referenced?: ReadonlySet<string>): TryTarget;
|
|
110
116
|
/**
|
package/dist/types/core.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export { normalizeSettings, findCycles, stronglyConnected, type ContextNeeds, ty
|
|
|
29
29
|
export { DEFAULT_EVENT, EVENT_RE, RECORDS_MAX, calcSettings, validateEvent, type CalcInput, type CalcLine, type CalcRecordResult, type CalcResult } from "./commands/calc.ts";
|
|
30
30
|
export { takeInbox, type TakeResult } from "./commands/take.ts";
|
|
31
31
|
export { SAMPLE_APP, describeSampleApp, sampleApp, type SampleApp, type SampleRecord } from "./sample-app.ts";
|
|
32
|
-
export { TRY_DEFAULT_AS, TRY_MAX_FORMULAS, TRY_MAX_FORMULA_CHARS, buildTryEnvelope, resolveTryTarget, trySettings, type TryFormula, type TryFormulaResult, type TryInput, type TryResult, type TryTarget, type TryValue } from "./commands/try.ts";
|
|
32
|
+
export { TRY_DEFAULT_AS, TRY_MAX_FORMULAS, TRY_MAX_FORMULA_CHARS, buildTryEnvelope, resolveTryTarget, spacersOf, trySettings, type TryFormula, type TryFormulaResult, type TryInput, type TryResult, type TryTarget, type TryValue } from "./commands/try.ts";
|
|
33
33
|
export { EXTRA_TYPES, TRY_MAX_DATA, TRY_MAX_EXTRA_FIELDS, TRY_MAX_OPTIONS, TRY_MAX_TABLE_ROWS, TRY_MAX_VALUE_CHARS, TYPE_ALIASES, applyData, canonicalType, checkDataList, convertValue, emptyRecordOf, extendFields, normalizeDefaults, type TryData, type TryFieldDef } from "./commands/try-data.ts";
|
|
34
34
|
export { COMMON_KEYS, MAX_SAVE_INPUT_BYTES, MAX_UPDATE_ROWS, connectionStatusOf, digestOf, saveNewSettings, updateFields, type EvalSource, type SaveContext, type SaveResult, type SaveStatus } from "./commands/save.ts";
|
|
35
35
|
export { DEFAULT_POLICY, POLICY_FILE, PolicyError, loadPolicy, parsePolicy, type Policy } from "./normalize/policy.ts";
|
package/package.json
CHANGED