@stalfh233/omc-cli 0.4.37 → 0.4.39
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/dist/bench/call-face-eval.js +1 -1
- package/dist/bench/discovery-cost-eval.js +1 -1
- package/dist/bench/driver.js +1 -1
- package/dist/bench/intent-face-eval.js +1 -1
- package/dist/capability-shape.js +2 -153
- package/dist/cli.js +35 -12
- package/dist/contract-consistency.js +5 -1
- package/dist/execution-metadata.js +4 -3
- package/dist/field-families.js +2 -22
- package/dist/json-column.js +2 -67
- package/dist/lanes/businessrule-contract.js +4 -3
- package/dist/lanes/field.js +3 -1
- package/dist/lanes/list-view-shapes.js +13 -98
- package/dist/lanes/model-contract.js +2 -2
- package/dist/lanes/rule-graph.js +2 -2
- package/dist/lanes/runtime-data-contract.js +6 -83
- package/dist/lanes/runtime-data.js +1 -1
- package/dist/recipes/runtime-data.js +1 -1
- package/dist/tool-types.js +2 -7
- package/dist/zip.js +2 -155
- package/package.json +3 -2
- package/dist/bench/fake-http-server.js +0 -65
- package/dist/fake/app.js +0 -45
- package/dist/fake/b2-registration.js +0 -620
- package/dist/fake/businessrule.js +0 -242
- package/dist/fake/datarule.js +0 -72
- package/dist/fake/dictionary.js +0 -108
- package/dist/fake/environment.js +0 -30
- package/dist/fake/field.js +0 -167
- package/dist/fake/form.js +0 -101
- package/dist/fake/index.js +0 -121
- package/dist/fake/list-view.js +0 -289
- package/dist/fake/model.js +0 -136
- package/dist/fake/online-js.js +0 -18
- package/dist/fake/report.js +0 -253
- package/dist/fake/routes.js +0 -47
- package/dist/fake/rule-lifecycle.js +0 -37
- package/dist/fake/runtime-data.js +0 -367
- package/dist/fake/state.js +0 -67
- package/dist/fake/workflow.js +0 -364
|
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { startFakeHttpServer } from "
|
|
5
|
+
import { startFakeHttpServer } from "@stalfh233/omc-fake-platform/http-server";
|
|
6
6
|
/**
|
|
7
7
|
* Call-face eval (Spec 100 acceptance / Spec 107 baseline): a scripted
|
|
8
8
|
* reference agent completes real tasks using ONLY the omc CLI face
|
|
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { startFakeHttpServer } from "
|
|
5
|
+
import { startFakeHttpServer } from "@stalfh233/omc-fake-platform/http-server";
|
|
6
6
|
const packageRoot = fileURLToPath(new URL("../..", import.meta.url));
|
|
7
7
|
const cli = join(packageRoot, "dist", "cli.js");
|
|
8
8
|
const PASSWORD = "discovery-cost-password";
|
package/dist/bench/driver.js
CHANGED
|
@@ -6,7 +6,7 @@ import { ConfigStore, defaultPaths } from "../config-store.js";
|
|
|
6
6
|
import { SessionManager } from "../session-manager.js";
|
|
7
7
|
import { makeToolContext } from "../context.js";
|
|
8
8
|
import { buildToolRegistry } from "../tools.js";
|
|
9
|
-
import { FakeCloudPivot } from "
|
|
9
|
+
import { FakeCloudPivot } from "@stalfh233/omc-fake-platform";
|
|
10
10
|
import { resetWireReadCacheStats } from "../wire.js";
|
|
11
11
|
import { ToolTap, WireTap } from "./instrument.js";
|
|
12
12
|
export function makeBenchHarness(mode, liveEnvironment) {
|
|
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { startFakeHttpServer } from "
|
|
5
|
+
import { startFakeHttpServer } from "@stalfh233/omc-fake-platform/http-server";
|
|
6
6
|
/**
|
|
7
7
|
* Intent-face eval (Spec 107 acceptance): the SAME scripted reference-agent
|
|
8
8
|
* task set as the call-face eval, driven through the declarative face
|
package/dist/capability-shape.js
CHANGED
|
@@ -1,154 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
1: 2,
|
|
4
|
-
2: 4,
|
|
5
|
-
3: 3,
|
|
6
|
-
4: 8,
|
|
7
|
-
5: 62,
|
|
8
|
-
6: 9,
|
|
9
|
-
8: 201,
|
|
10
|
-
9: 80,
|
|
11
|
-
10: 14,
|
|
12
|
-
11: 81,
|
|
13
|
-
12: 5,
|
|
14
|
-
13: 6,
|
|
15
|
-
14: 7,
|
|
16
|
-
15: 19,
|
|
17
|
-
16: 82,
|
|
18
|
-
17: 17,
|
|
19
|
-
28: 28,
|
|
20
|
-
50: 50,
|
|
21
|
-
51: 51,
|
|
22
|
-
60: 60,
|
|
23
|
-
61: 61,
|
|
24
|
-
100: 89,
|
|
25
|
-
});
|
|
26
|
-
const commonEvidence = [
|
|
27
|
-
"8.6.26 BizPropertyType/BizFormPropertyType",
|
|
28
|
-
"desensitized-config-shapes.md §3.2",
|
|
29
|
-
"frontend-dist/admin/js/4280.*.js designer bundle",
|
|
30
|
-
];
|
|
31
|
-
const shape = (propertyType, field, control, evidence = commonEvidence) => ({ propertyType, controlType: CAPABILITY_CONTROL_TYPES[propertyType], field, control, evidence });
|
|
32
|
-
const face = (requiredKeys, defaults, evidence = commonEvidence, nonEmptyKeys) => ({ requiredKeys, defaults, evidence, ...(nonEmptyKeys ? { nonEmptyKeys } : {}) });
|
|
33
|
-
export const FIELD_CAPABILITY_SHAPES = Object.freeze({
|
|
34
|
-
0: shape(0, face([], { dictionaryData: "" }), face(["defaultValue", "maxLength"], { defaultValue: null, placeholder: "请输入", maxLength: 200, currentMaxLength: 2000000, noRepeat: false, enableEncrypt: false })),
|
|
35
|
-
1: shape(1, face([], { dictionaryData: "" }), face(["defaultValue", "maxLength"], { defaultValue: null, textAreaType: "longText", maxLength: 2000, placeholder: "请输入" })),
|
|
36
|
-
2: shape(2, face([], { dictionaryData: "" }), face(["defaultValue", "format"], { defaultValue: null, placeholder: "请输入", computeFormula: "", format1: "decimal", format: "decimal", verifyFormula: "", isFlag: false })),
|
|
37
|
-
3: shape(3, face(["format"], { dictionaryData: "", format: "YYYY-MM-DD" }), face(["format", "format1"], { defaultValue: null, dataItemDefault: "", format1: "YYYY-MM-DD", format: "YYYY-MM-DD", minDate: "", maxDate: "", verifyFormula: "" })),
|
|
38
|
-
4: shape(4, face(["defaultValue"], { defaultValue: false }), face(["defaultValue"], { defaultValue: true })),
|
|
39
|
-
6: shape(6, face([], {}), face(["displayTemplate", "limitNumber", "fileNumber", "batch"], { displayTemplate: true, limitNumber: "unlimited", fileNumber: 1, batch: true, displayFormula: "", requiredFormula: "", readonlyFormula: false, limit: "200M", onUpload: "", onDelete: "" }), ["cp-sources/frontend-dist/admin/js/12404.1675d362.js attachment schema", "cp-sources/frontend-dist/admin/js/24187.6e75bd30.js uploadOptions/baseUploadOptions"]),
|
|
40
|
-
// CHILD_TABLE(8) — scalar child table. Lifted from blocked on 2026-09-18:
|
|
41
|
-
// the wire shape is live-accepted (omc-core `childtable-field-contract.ts`,
|
|
42
|
-
// live record `childtable-execute-20260908070635135.md`, 2026-09-08) and the
|
|
43
|
-
// Sheet201 form control is live-proven (omc-core
|
|
44
|
-
// `childtable-sheet201-contract.ts`). The field row carries no `options`; its
|
|
45
|
-
// definition is the `subSchema` ({code:<parent field code>, properties:[...]})
|
|
46
|
-
// and the form control is Sheet(201) with `columns`. Nested CHILD_TABLE (a
|
|
47
|
-
// child of a child) remains out of coverage and is refused in
|
|
48
|
-
// `assertFieldFamilyAllowed`.
|
|
49
|
-
8: shape(8, face([], {}), face([], {}), ["omc-core childtable-field-contract.ts (live 2026-09-08)", "omc-core childtable-sheet201-contract.ts", "8.6.26 BizPropertyType CHILD_TABLE=8 / BizFormPropertyType Sheet=201"]),
|
|
50
|
-
9: shape(9, face([], { dictionaryData: "" }), face([], { schemaCode: "", displayField: "name", relativePropertyMatchCode: "name", queryCode: "", linkMode: true, showStyle: "", selectMode: "popup", isAuthorize: true, treeView: false, treeStartFromRoot: false, mappings: "", searchFormula: "", conditions: "" })),
|
|
51
|
-
10: shape(10, face(["defaultValue"], { defaultValue: "" }), face(["locationType", "addressDetail", "showEmpty", "emptyValue", "autoGetLocation"], { locationType: "pp-cc-aa", addressDetail: "true", showEmpty: "false", emptyValue: "请选择", autoGetLocation: "false" }), [".scratch/omc-app-management/research/desensitized-config-shapes.md §3.2 ADDRESS", "cp-sources/frontend-dist/admin/js/12404.1675d362.js address schema", ".scratch/omc-app-management/evidence/field-families-fwb-20260914.md:113-119 (fwb field row {\"defaultValue\":\"\",\"virtualField\":false})"]),
|
|
52
|
-
11: shape(11, face([], { dictionaryData: "" }), face([], { schemaCode: "", displayField: "name", relativePropertyMatchCode: "name", queryCode: "", linkMode: true, showStyle: "", selectMode: "popup", isAuthorize: true, treeView: false, treeStartFromRoot: false, mappings: "", searchFormula: "", conditions: "" })),
|
|
53
|
-
// Ticket 108 determination: selection families (12/13/14/15) emit NO
|
|
54
|
-
// dictionaryData — designer-produced rows carry none of it (fwb §12-15
|
|
55
|
-
// outer key unions) and live 8.6.26 proves the platform merely
|
|
56
|
-
// accepts-and-persists the extra key without requiring it. The other
|
|
57
|
-
// text/date/relation families keep the designer-carried empty-string
|
|
58
|
-
// default; ATTACHMENT(6) stays intentionally empty (system rows often
|
|
59
|
-
// have no options). The wider 2026-09-14 key unions are multi-field
|
|
60
|
-
// unions of designer rows — platform auto-fill territory, never required
|
|
61
|
-
// for creation (minimal default + caller passthrough is the boundary).
|
|
62
|
-
12: shape(12, face([], {}), face([], { defaultValue: "", options: "", hasLabel: false })),
|
|
63
|
-
13: shape(13, face([], {}), face([], { defaultValue: "", options: "", hasLabel: false, direction: "horizontal", displaySetting: "showSelected" })),
|
|
64
|
-
14: shape(14, face([], {}), face([], { displayEmpty: true, emptyValue: "请选择", multi: false })),
|
|
65
|
-
15: shape(15, face([], {}), face([], { displayEmpty: true, emptyValue: "请选择", multi: true })),
|
|
66
|
-
16: shape(16, face([], { dictionaryData: "" }), face([], {})),
|
|
67
|
-
17: shape(17, face(["defaultValue", "dictionaryData", "maxLength"], { defaultValue: "", dictionaryData: "", maxLength: 200 }), face(["defaultValue", "placeholder", "maxLength"], { defaultValue: "", placeholder: "请输入", maxLength: 200 }), [".scratch/omc-app-management/research/desensitized-config-shapes.md §3.2 HYPER_LINK", "cp-sources/frontend-dist/admin/js/12404.1675d362.js URL schema"]),
|
|
68
|
-
28: shape(28, face(["format"], { defaultValue: "", format: "HH:mm" }), face(["format", "format1"], { defaultValue: "none", dataItemDefault: "", format: "HH:mm", format1: "HH:mm", syncFormate: true }), [".scratch/omc-app-management/research/desensitized-config-shapes.md §3.2 TIME", "cp-sources/frontend-dist/admin/js/46948.4825f4e2.js Time1760153573819"]),
|
|
69
|
-
// Org-selector families. Evidence: the yst/fwb published-face dumps
|
|
70
|
-
// (origin/research/yst-examples, 2656 rows of types 5/50/51/60/61) show the
|
|
71
|
-
// FIELD row (bizproperty) options as null or `{defaultValue, mappings,
|
|
72
|
-
// virtualField, noRepeat}` — deptVisible/multi NEVER appear there. The
|
|
73
|
-
// deptVisible/multi discriminator is FORM-CONTROL-face only (source:
|
|
74
|
-
// DataModelInitUtils.updateSelectionType / BizPropertyTypeUpdateUtils.
|
|
75
|
-
// disposeUpdateType read `control.options`; ticket 69 already writes them on
|
|
76
|
-
// the control face). Field faces below mirror the observed publish-safe
|
|
77
|
-
// shapes (single: defaultValue ""; multi: defaultValue []).
|
|
78
|
-
5: shape(5, face([], { defaultValue: [], mappings: "", virtualField: false }), face(["defaultValue", "deptVisible", "multi"], { defaultValue: [], defaultValueType: "", deptVisible: "all", multi: "true" })),
|
|
79
|
-
50: shape(50, face([], { defaultValue: "", mappings: "", virtualField: false }), face(["deptVisible", "multi"], { deptVisible: "user", multi: "false" })),
|
|
80
|
-
51: shape(51, face([], { defaultValue: [], mappings: "", virtualField: false }), face(["deptVisible", "multi"], { deptVisible: "user", multi: "true" })),
|
|
81
|
-
60: shape(60, face([], { defaultValue: [], mappings: "", virtualField: false }), face(["deptVisible", "multi"], { deptVisible: "org", multi: "false" })),
|
|
82
|
-
61: shape(61, face([], { defaultValue: [], mappings: "", virtualField: false }), face(["deptVisible", "multi"], { deptVisible: "org", multi: "true" })),
|
|
83
|
-
100: shape(100, face([], {}), face([], { defaultValue: null })),
|
|
84
|
-
});
|
|
85
|
-
export const BLOCKED_CAPABILITY_SHAPES = Object.freeze({});
|
|
86
|
-
/**
|
|
87
|
-
* Ticket 101 — control codes observed in real forms but with NO field-family
|
|
88
|
-
* emission path. PHONE(24) and PHOTO(10) are authoritative 8.6.26 control
|
|
89
|
-
* codes (BizFormPropertyType.java:23-51), but BizPropertyType.java:27-54 has
|
|
90
|
-
* no PHONE/PHOTO entry, so no accepted field family can scaffold them and no
|
|
91
|
-
* options shape is evidenced for a write. Declared blocked so the absence
|
|
92
|
-
* reads as policy (fail-closed), not omission; caller-provided explicit
|
|
93
|
-
* layouts are unaffected. Adding an emission path requires new family
|
|
94
|
-
* evidence AND consciously lifting this declaration.
|
|
95
|
-
*/
|
|
96
|
-
export const BLOCKED_CONTROL_CODES = Object.freeze({
|
|
97
|
-
24: "PHONE control has no BizPropertyType mapping (BizPropertyType.java:27-54) — no field family emits it; creation not offered until a family + options shape are evidenced",
|
|
98
|
-
10: "PHOTO control has no BizPropertyType mapping (BizPropertyType.java:27-54) — no field family emits it; creation not offered until a family + options shape are evidenced",
|
|
99
|
-
});
|
|
100
|
-
export const CAPABILITY_SHAPE_ACCEPTED_TYPES = Object.freeze(Object.keys(FIELD_CAPABILITY_SHAPES).map(Number));
|
|
101
|
-
export function capabilityShapeFor(type) {
|
|
102
|
-
return FIELD_CAPABILITY_SHAPES[type] ?? BLOCKED_CAPABILITY_SHAPES[type];
|
|
103
|
-
}
|
|
104
|
-
export function capabilityShapeError(type, faceName, options) {
|
|
105
|
-
const current = capabilityShapeFor(type);
|
|
106
|
-
if (!current)
|
|
107
|
-
return `propertyType ${type} has no capability shape`;
|
|
108
|
-
if (current.blocked)
|
|
109
|
-
return `${current.citation ?? "blocked capability"} (propertyType ${type})`;
|
|
110
|
-
const face = current[faceName];
|
|
111
|
-
const missing = face.requiredKeys.filter((key) => options[key] === undefined || ((face.nonEmptyKeys ?? []).includes(key) && options[key] === ""));
|
|
112
|
-
return missing.length > 0 ? `capability shape ${faceName} missing required keys for propertyType ${type}: ${missing.join(", ")}` : undefined;
|
|
113
|
-
}
|
|
114
|
-
export function assertCapabilityShapeContract() {
|
|
115
|
-
for (const type of CAPABILITY_SHAPE_ACCEPTED_TYPES) {
|
|
116
|
-
const current = FIELD_CAPABILITY_SHAPES[type];
|
|
117
|
-
if (!current)
|
|
118
|
-
throw new Error(`capability shape missing for propertyType ${type}`);
|
|
119
|
-
if (current.blocked && !current.citation)
|
|
120
|
-
throw new Error(`blocked capability shape ${type} requires citation`);
|
|
121
|
-
if (current.controlType !== CAPABILITY_CONTROL_TYPES[type])
|
|
122
|
-
throw new Error(`capability shape control type mismatch for ${type}`);
|
|
123
|
-
for (const key of current.field.requiredKeys)
|
|
124
|
-
if (!(key in current.field.defaults))
|
|
125
|
-
throw new Error(`field shape ${type} missing default for ${key}`);
|
|
126
|
-
for (const key of current.control.requiredKeys)
|
|
127
|
-
if (!(key in current.control.defaults))
|
|
128
|
-
throw new Error(`control shape ${type} missing default for ${key}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
export function assertCapabilityFace(type, faceName, options) {
|
|
132
|
-
const current = capabilityShapeFor(type);
|
|
133
|
-
if (!current)
|
|
134
|
-
throw new Error(`capability shape missing for propertyType ${type}`);
|
|
135
|
-
if (current.blocked)
|
|
136
|
-
throw new Error(`propertyType ${type} is blocked: ${current.citation ?? "missing evidence"}`);
|
|
137
|
-
const face = current[faceName];
|
|
138
|
-
const missing = face.requiredKeys.filter((key) => options[key] === undefined || ((face.nonEmptyKeys ?? []).includes(key) && options[key] === ""));
|
|
139
|
-
if (missing.length > 0)
|
|
140
|
-
throw new Error(`capability shape ${faceName} missing required keys for propertyType ${type}: ${missing.join(", ")}`);
|
|
141
|
-
}
|
|
142
|
-
export function deriveFieldOptions(type, explicit) {
|
|
143
|
-
const current = capabilityShapeFor(type);
|
|
144
|
-
if (!current || current.blocked)
|
|
145
|
-
throw new Error(`propertyType ${type} is blocked or has no capability shape`);
|
|
146
|
-
return { ...current.field.defaults, ...(explicit ?? {}) };
|
|
147
|
-
}
|
|
148
|
-
export function deriveControlOptions(type, explicit) {
|
|
149
|
-
const current = capabilityShapeFor(type);
|
|
150
|
-
if (!current || current.blocked)
|
|
151
|
-
throw new Error(`propertyType ${type} is blocked or has no capability shape`);
|
|
152
|
-
return { ...current.control.defaults, ...(explicit ?? {}) };
|
|
153
|
-
}
|
|
1
|
+
// C1 拆分:capability-shape 已下沉 omc-core(真 lane 与 @stalfh233/omc-fake-platform 共用唯一事实源);此处 re-export 保持既有 import 路径不变。
|
|
2
|
+
export * from "omc-core/capability-shape";
|
|
154
3
|
//# sourceMappingURL=capability-shape.js.map
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// T04:SQLite 警告过滤器必须保持第一个 import(先于任何拉起 omc-core/node:sqlite 的模块)。
|
|
3
3
|
import "./sqlite-warning-filter.js";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
4
5
|
import { createInterface } from "node:readline/promises";
|
|
5
6
|
import { stdin, argv } from "node:process";
|
|
6
7
|
import { createRequire } from "node:module";
|
|
@@ -113,6 +114,23 @@ async function cmdDoctor(store, environment, serviceCode) {
|
|
|
113
114
|
const manifest = loadCoverageManifest();
|
|
114
115
|
const steps = [];
|
|
115
116
|
let ok = true;
|
|
117
|
+
// r45 Windows 实测:PowerShell 管道按 GBK(936) 解码 CLI 的 UTF-8 输出 → 全屏乱码,
|
|
118
|
+
// 且用户常误判为「OMC 输出坏了」。doctor 是入口体检,顺手检测活动代码页。
|
|
119
|
+
if (process.platform === "win32") {
|
|
120
|
+
try {
|
|
121
|
+
const chcp = spawnSync("chcp.com", [], { encoding: "utf8" });
|
|
122
|
+
const active = /([0-9]{3,5})\s*$/.exec((chcp.stdout || "").trim().split("\n").pop() ?? "")?.[1];
|
|
123
|
+
if (active && active !== "65001") {
|
|
124
|
+
steps.push(`编码 ⚠ 活动代码页 ${active}(非 65001)——PowerShell 管道会把 CLI 的 UTF-8 中文输出解成乱码;这是终端解码问题不是 OMC 故障。先 \`chcp 65001\` + \`[Console]::OutputEncoding=[Text.Encoding]::UTF8\` 再读 CLI 输出`);
|
|
125
|
+
}
|
|
126
|
+
else if (active === "65001") {
|
|
127
|
+
steps.push("编码 ok 活动代码页 65001(UTF-8,中文输出无乱码风险)");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// chcp 不可用(非传统控制台等)——跳过,不阻塞体检
|
|
132
|
+
}
|
|
133
|
+
}
|
|
116
134
|
try {
|
|
117
135
|
const resolved = store.resolveEnvironment(environment);
|
|
118
136
|
steps.push(`配置 ok 环境=${resolved.name}(来源 ${resolved.source})`);
|
|
@@ -367,32 +385,35 @@ function resolvedDefaultEnvironment(store) {
|
|
|
367
385
|
return "default";
|
|
368
386
|
}
|
|
369
387
|
}
|
|
370
|
-
/**
|
|
371
|
-
* Bounded JSON-path projection for `artifact get --jq`. Supports dotted
|
|
372
|
-
* (`content.activities.0.name`) and slashed (`content/activities/0/name`)
|
|
373
|
-
* segments. Not a general jq: a path walk only, so it can never evaluate code.
|
|
374
|
-
*/
|
|
375
388
|
function projectJsonPath(value, path) {
|
|
376
|
-
const
|
|
389
|
+
const normalized = path
|
|
390
|
+
.trim()
|
|
391
|
+
.replace(/^[.\/]+/, "")
|
|
392
|
+
.replace(/\[(\d+)\]/g, ".$1")
|
|
393
|
+
.replace(/\[(?:"([^"]+)"|'([^']+)')\]/g, (_all, dq, sq) => `.${dq || sq}`)
|
|
394
|
+
.replace(/\.{2,}/g, ".");
|
|
395
|
+
const segments = normalized.split(/[./]/).filter((segment) => segment.length > 0);
|
|
377
396
|
let current = value;
|
|
378
397
|
for (const segment of segments) {
|
|
379
398
|
if (Array.isArray(current)) {
|
|
380
399
|
const index = Number(segment);
|
|
381
400
|
if (!Number.isInteger(index) || index < 0 || index >= current.length)
|
|
382
|
-
return
|
|
401
|
+
return { missed: true, at: segment, available: `数组长度 ${current.length}(下标 0-${Math.max(current.length - 1, 0)})` };
|
|
383
402
|
current = current[index];
|
|
384
403
|
}
|
|
385
404
|
else if (current && typeof current === "object") {
|
|
386
405
|
const record = current;
|
|
387
|
-
if (!Object.prototype.hasOwnProperty.call(record, segment))
|
|
388
|
-
|
|
406
|
+
if (!Object.prototype.hasOwnProperty.call(record, segment)) {
|
|
407
|
+
const keys = Object.keys(record);
|
|
408
|
+
return { missed: true, at: segment, available: keys.length > 0 ? keys.slice(0, 12) : "(该层是空对象)" };
|
|
409
|
+
}
|
|
389
410
|
current = record[segment];
|
|
390
411
|
}
|
|
391
412
|
else {
|
|
392
|
-
return undefined;
|
|
413
|
+
return { missed: true, at: segment, available: `路径在第 ${segments.indexOf(segment) + 1} 段之前已到标量/undefined(前段类型 ${current === null ? "null" : typeof current})` };
|
|
393
414
|
}
|
|
394
415
|
}
|
|
395
|
-
return current;
|
|
416
|
+
return { missed: false, value: current };
|
|
396
417
|
}
|
|
397
418
|
async function cmdArtifactGet(id, transport, options = {}) {
|
|
398
419
|
try {
|
|
@@ -443,7 +464,9 @@ async function cmdArtifactGet(id, transport, options = {}) {
|
|
|
443
464
|
}
|
|
444
465
|
if (options.jq !== undefined) {
|
|
445
466
|
const projected = projectJsonPath(artifact.content.toString("utf8") === "" ? undefined : content, options.jq);
|
|
446
|
-
return emitTransportResult(
|
|
467
|
+
return emitTransportResult(projected.missed
|
|
468
|
+
? { status: "ok", artifactId: id, sha256: artifact.sha256, path: options.jq, content: null, pathMiss: true, missedAt: projected.at, available: projected.available, hint: `路径未命中段 "${projected.at}"——available 是该层可用的键/范围;方括号下标(a[0])与点式(a.0)等价` }
|
|
469
|
+
: { status: "ok", artifactId: id, sha256: artifact.sha256, path: options.jq, content: projected.value ?? null }, transport);
|
|
447
470
|
}
|
|
448
471
|
const result = { status: "ok", artifactId: id, sha256: artifact.sha256, content };
|
|
449
472
|
if (Buffer.byteLength(JSON.stringify(result), "utf8") > transport.maxOutputBytes)
|
|
@@ -56,7 +56,11 @@ function assertBindingReference(ref) {
|
|
|
56
56
|
throw new Error(`binding reference ${ref} is not a module symbol reference`);
|
|
57
57
|
const moduleName = target.slice(0, separator);
|
|
58
58
|
const symbol = target.slice(separator + 1);
|
|
59
|
-
|
|
59
|
+
// C1 拆分:wire 事实定义下沉 omc-core 后,绑定引用以 omc-core/<module>.SYMBOL 指向真实定义处(仍要求源内定义存在,强度不变)。
|
|
60
|
+
const isCoreRef = moduleName.startsWith("omc-core/");
|
|
61
|
+
const filePath = isCoreRef
|
|
62
|
+
? join(packageRoot, "..", "omc-core", "src", `${moduleName.slice("omc-core/".length)}.ts`)
|
|
63
|
+
: join(packageRoot, "src", `${moduleName}.ts`);
|
|
60
64
|
if (!existsSync(filePath))
|
|
61
65
|
throw new Error(`binding module ${moduleName} does not exist`);
|
|
62
66
|
const source = readFileSync(filePath, "utf8");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
// 配方按 lane 分居维护(src/recipes/<lane>.ts
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// 配方按 lane 分居维护(src/recipes/<lane>.ts);本文件是聚合接缝:按 lane 分组顺序
|
|
2
|
+
// 合并(非原中央表字面量序——已核实全仓零 Object.keys/entries 枚举消费者,消费方一律
|
|
3
|
+
// 按工具名查找,键序无行为影响),并在模块加载时对重复工具键 fail-fast
|
|
4
|
+
// (lane 各自注册后静默覆盖是拆分最大风险)。
|
|
4
5
|
import { MODEL_METADATA } from "./recipes/model.js";
|
|
5
6
|
import { FORM_METADATA } from "./recipes/form.js";
|
|
6
7
|
import { FIELD_METADATA } from "./recipes/field.js";
|
package/dist/field-families.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { relationTargetOf } from "omc-core/relation-shape";
|
|
2
|
+
import { relationOptions } from "omc-core/relation-shape";
|
|
1
3
|
import { z } from "zod";
|
|
2
4
|
import { closestMatch } from "./closest-match.js";
|
|
3
5
|
import { ToolRefusalError } from "./tool-types.js";
|
|
@@ -398,28 +400,6 @@ export function fieldControlOptions(type, name, relativeCode, relationMappings,
|
|
|
398
400
|
default: return merged;
|
|
399
401
|
}
|
|
400
402
|
}
|
|
401
|
-
/** relation options face decode; policy: string-or-object accepted, garbage → undefined. */
|
|
402
|
-
function relationOptions(options) {
|
|
403
|
-
return decodeJsonObjectColumn(options, { invalid: "undefined", array: "accept", object: "accept" });
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* The relation target model of a bizproperty row. Relation families
|
|
407
|
-
* (WORK_SHEET / MULT_WORK_SHEET) carry the target model code both as the
|
|
408
|
-
* row-level `relativeCode` and as the `schemaCode` key inside the options JSON
|
|
409
|
-
* (field.create writes both). RELEVANCE_DATA(16) is deliberately excluded: it
|
|
410
|
-
* references another *field* (relevanceField/referenceField), not a model, so
|
|
411
|
-
* it adds no inbound model reference of its own — the local WORK_SHEET field it
|
|
412
|
-
* resolves through already carries that reference. Either face counts as an
|
|
413
|
-
* inbound reference for the model.delete precheck (ticket 23: the server's
|
|
414
|
-
* deleteFunctions dispatcher refuses asynchronously with
|
|
415
|
-
* "xxx被模型 yyy 引用,不能删除" — the tool surfaces that check at call time).
|
|
416
|
-
*/
|
|
417
|
-
export function relationTargetOf(row) {
|
|
418
|
-
if (typeof row.relativeCode === "string" && row.relativeCode.trim())
|
|
419
|
-
return row.relativeCode.trim();
|
|
420
|
-
const schemaCode = relationOptions(row.options)?.schemaCode;
|
|
421
|
-
return typeof schemaCode === "string" && schemaCode.trim() ? schemaCode.trim() : undefined;
|
|
422
|
-
}
|
|
423
403
|
export function relationDisplayField(options) {
|
|
424
404
|
const displayField = relationOptions(options)?.displayField;
|
|
425
405
|
return typeof displayField === "string" && displayField.trim() ? displayField.trim() : undefined;
|
package/dist/json-column.js
CHANGED
|
@@ -1,68 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
* string (or an object)" decoding/encoding. Previously every lane carried a
|
|
4
|
-
* local copy with subtly different behaviour; the deliberate differences are
|
|
5
|
-
* now NAMED POLICIES chosen at each call site:
|
|
6
|
-
*
|
|
7
|
-
* - `JSON_COLUMN_PASSTHROUGH` — non-strings pass through untouched
|
|
8
|
-
* (undefined stays undefined); unparseable strings return verbatim.
|
|
9
|
-
* (datarule options, field.list readback)
|
|
10
|
-
* - `JSON_COLUMN_NULL_COALESCE` — non-strings null-coalesce (undefined →
|
|
11
|
-
* null); blank/unparseable strings return verbatim ("" stays "").
|
|
12
|
-
* (businessrule node/route/schedulerSetting, b2 configJson/parameter faces,
|
|
13
|
-
* rule-support parameter faces, list-view condition options)
|
|
14
|
-
* - `{ invalid: "throw" }` — a corrupt column is a hard error
|
|
15
|
-
* (field.update merge base reads the stored options strictly).
|
|
16
|
-
*
|
|
17
|
-
* Blank strings ("", " ") are not valid JSON, so they always take the
|
|
18
|
-
* `invalid` branch; at every previous site that meant passthrough.
|
|
19
|
-
*/
|
|
20
|
-
export const JSON_COLUMN_PASSTHROUGH = { nonString: "passthrough", invalid: "passthrough" };
|
|
21
|
-
export const JSON_COLUMN_NULL_COALESCE = { nonString: "null", invalid: "passthrough" };
|
|
22
|
-
export function decodeJsonColumn(value, policy) {
|
|
23
|
-
if (typeof value !== "string")
|
|
24
|
-
return policy.nonString === "null" ? value ?? null : value;
|
|
25
|
-
try {
|
|
26
|
-
return JSON.parse(value);
|
|
27
|
-
}
|
|
28
|
-
catch (error) {
|
|
29
|
-
if (policy.invalid === "throw")
|
|
30
|
-
throw error;
|
|
31
|
-
return value;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
/** Wire encode: a string is sent as-is, anything else is JSON-stringified. */
|
|
35
|
-
export function encodeJsonColumn(value) {
|
|
36
|
-
return typeof value === "string" ? value : JSON.stringify(value);
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Decode an options face that may be a JSON string or already an object into
|
|
40
|
-
* a plain record (or undefined). Policies preserve the previous per-site
|
|
41
|
-
* semantics: relation options accept strings/objects/arrays loosely; the
|
|
42
|
-
* FORMULA formulaConfig validator only accepts strings that parse to a
|
|
43
|
-
* non-array object; field.update/form-scaffold strict reads throw on corrupt
|
|
44
|
-
* strings.
|
|
45
|
-
*/
|
|
46
|
-
export function decodeJsonObjectColumn(value, policy) {
|
|
47
|
-
const asRecord = (parsed) => {
|
|
48
|
-
if (!parsed || typeof parsed !== "object")
|
|
49
|
-
return undefined;
|
|
50
|
-
if (Array.isArray(parsed))
|
|
51
|
-
return policy.array === "reject" ? undefined : parsed;
|
|
52
|
-
return parsed;
|
|
53
|
-
};
|
|
54
|
-
if (typeof value === "string") {
|
|
55
|
-
try {
|
|
56
|
-
return asRecord(JSON.parse(value));
|
|
57
|
-
}
|
|
58
|
-
catch (error) {
|
|
59
|
-
if (policy.invalid === "throw")
|
|
60
|
-
throw error;
|
|
61
|
-
return undefined;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
if (policy.object === "reject")
|
|
65
|
-
return undefined;
|
|
66
|
-
return asRecord(value);
|
|
67
|
-
}
|
|
1
|
+
// C1 拆分:json-column 已下沉 omc-core(真 lane 与 @stalfh233/omc-fake-platform 共用唯一事实源);此处 re-export 保持既有 import 路径不变。
|
|
2
|
+
export * from "omc-core/json-column";
|
|
68
3
|
//# sourceMappingURL=json-column.js.map
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
export { RULE_STATUS } from "omc-core/wire-facts";
|
|
2
|
+
import { RULE_STATUS } from "omc-core/wire-facts";
|
|
1
3
|
import { behaviorBinding, CONTRACT_FAMILY, CONTRACT_REFERENCE_VERSION, assertDescriptionCarries, assertLaneContract, describeTool, emitterBinding } from "./contract-support.js";
|
|
2
4
|
import { LANES } from "./lane-ids.js";
|
|
3
5
|
import { RULE_ACTION_TYPES, RULE_CODE_TYPES, RULE_CONDITION_TYPES, RULE_DATA_SOURCE_TYPES, RULE_NODE_TEMPLATES, RULE_NODE_TYPES, RULE_SCHEDULER_CYCLE_TYPES, RULE_SCHEDULER_END_TYPES, RULE_SCHEDULER_EXECUTE_TYPES, RULE_SCHEDULER_SETTING_TEMPLATE, RULE_SCHEDULER_TRIGGER_TIME_TYPES, RULE_VALUE_TYPES } from "./rule-graph.js";
|
|
4
6
|
/** Rule `enabled` is the runtime dispatch truth; `status` may lag (platform quirk). */
|
|
5
|
-
export const RULE_STATUS = Object.freeze({ enabled: "ENABLE", disabled: "DISABLED" });
|
|
6
7
|
/** Enum re-exports: rule-graph.ts is the single source (validator + fake + contract all import it). */
|
|
7
8
|
export { RULE_VALUE_TYPES, RULE_ACTION_TYPES, RULE_CONDITION_TYPES, RULE_NODE_TYPES, RULE_NODE_TEMPLATES };
|
|
8
9
|
/** The DISABLED dead-state body every fresh rule.save upsert writes. */
|
|
@@ -120,8 +121,8 @@ export const BUSINESSRULE_LANE_CONTRACT = Object.freeze({
|
|
|
120
121
|
bizActionPersistence: Object.freeze([behaviorBinding("test/rule-list.test.ts#DYNAMIC UPDATE uses the production source-field wire and backfills the related target row", assertBusinessRuleContract)]),
|
|
121
122
|
triggerConditions: Object.freeze([behaviorBinding("test/rule-graph-validation.test.ts#C.2: UPDATE with an empty filterCondition is flagged (warning) as a whole-table operation", assertBusinessRuleContract)]),
|
|
122
123
|
compatibilityMatrix: Object.freeze([behaviorBinding("test/ticket-05-rule-compatibility-preflight.test.ts#known-invalid CREATE plus dictionary selection is refused before rule mutation", assertBusinessRuleContract)]),
|
|
123
|
-
dynamicConditionSource: Object.freeze([emitterBinding("
|
|
124
|
-
dynamicActionSource: Object.freeze([emitterBinding("
|
|
124
|
+
dynamicConditionSource: Object.freeze([emitterBinding("omc-core/wire-facts.DYNAMIC_CONDITION_SOURCE_KEY", assertBusinessRuleContract)]),
|
|
125
|
+
dynamicActionSource: Object.freeze([emitterBinding("omc-core/wire-facts.DYNAMIC_ACTION_SOURCE_KEY", assertBusinessRuleContract)]),
|
|
125
126
|
dynamicConditionValue: Object.freeze([behaviorBinding("test/rule-graph-validation.test.ts#negative: ticket 09 lesion", assertBusinessRuleContract)]),
|
|
126
127
|
dynamicActionValue: Object.freeze([behaviorBinding("test/rule-graph-validation.test.ts#negative: ticket 09 lesion", assertBusinessRuleContract)]),
|
|
127
128
|
dynamicNameKeys: Object.freeze([behaviorBinding("test/rule-list.test.ts#DYNAMIC UPDATE uses the production source-field wire", assertBusinessRuleContract)]),
|
package/dist/lanes/field.js
CHANGED
|
@@ -135,7 +135,9 @@ export function registerFieldLane(tools) {
|
|
|
135
135
|
});
|
|
136
136
|
return {
|
|
137
137
|
schemaCode: args.schemaCode,
|
|
138
|
-
|
|
138
|
+
// r45: 这是"本次查询的面选择器"回显(draft/published),不是字段发布状态——
|
|
139
|
+
// 曾被误读为"字段未发布"。改名 face 消歧义;要查发布态用 field.publish 回读或详情面。
|
|
140
|
+
face: args.published === true ? "published" : "draft",
|
|
139
141
|
...(selector.length > 0 ? { selected: selector } : {}),
|
|
140
142
|
...(args.all !== true ? { businessOnly: true } : {}),
|
|
141
143
|
fields: projected,
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
+
export { QUERY_ACTION_TYPE, QUERY_ACTION_RELATIVE, validateButtonActionConfig } from "omc-core/list-view-wire";
|
|
2
|
+
import { QUERY_ACTION_TYPE, QUERY_ACTION_RELATIVE, validateButtonActionConfig, wireEnum } from "omc-core/list-view-wire";
|
|
1
3
|
import { z } from "zod";
|
|
2
4
|
import { ToolRefusalError } from "../tool-types.js";
|
|
3
5
|
import { decodeJsonColumn, encodeJsonColumn, JSON_COLUMN_NULL_COALESCE } from "../json-column.js";
|
|
4
6
|
import { FIELD_PROPERTY_TYPES } from "../field-families.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Ticket 53 — each list-view concept (its wire enums, its input schema, its
|
|
9
|
+
* encode to the wire, its decode from readback, and its dry lint) is described
|
|
10
|
+
* ONCE here. `src/lanes/list-view.ts` consumes these descriptors to register
|
|
11
|
+
* the tools and `src/fake/list-view.ts` reuses the same enum constants, so a
|
|
12
|
+
* per-shape change is a one-file edit instead of four.
|
|
13
|
+
*
|
|
14
|
+
* Everything in this module is moved verbatim from list-view.ts; wire payloads
|
|
15
|
+
* and readback objects stay byte-identical (proven by the registry-dump hash,
|
|
16
|
+
* the existing list-view tests, and the bench +0.0 gate).
|
|
17
|
+
*/
|
|
13
18
|
/** Encode an enum name to its wire numeric index (undefined when unknown). */
|
|
14
19
|
export function encodeEnumName(descriptor, name) {
|
|
15
20
|
return typeof name === "string" ? descriptor.index[name] : undefined;
|
|
@@ -70,40 +75,6 @@ function defaultQueryFilterType(propertyType) {
|
|
|
70
75
|
export const SORT_DIRECTION = wireEnum("SortDirectionType", { ASC: 1, DESC: 2 });
|
|
71
76
|
export const DISPLAY_TYPE = wireEnum("DisplayType", { INPUT: 0, RADIO: 1, CHECKBOX: 2, SELECT: 3, RANGE: 4 });
|
|
72
77
|
export const CHOICE_TYPE = wireEnum("ChoiceType", { SELECT: 1, POPUP: 2, TREE_LEFT: 3 });
|
|
73
|
-
/**
|
|
74
|
-
* BizQueryActionModel enums. `QueryActionType` (QueryActionType.java:13-35) and
|
|
75
|
-
* `QueryActionRelativeType` (BIZSHEET 0/WORKFLOW 1) both carry
|
|
76
|
-
* `@JsonValue getIndex()` so the wire body is numeric; the tool speaks the enum
|
|
77
|
-
* name and decodes on readback. `associationType`/`associationCode` are the
|
|
78
|
-
* JSON names of the DTO fields `queryActionRelativeType`/`relativeCode`
|
|
79
|
-
* (`@JsonProperty`, BizQueryActionModel.java:31-35).
|
|
80
|
-
*/
|
|
81
|
-
export const QUERY_ACTION_TYPE = wireEnum("QueryActionType", {
|
|
82
|
-
QUERY: 1,
|
|
83
|
-
ADD: 2,
|
|
84
|
-
EDIT: 3,
|
|
85
|
-
DELETE: 4,
|
|
86
|
-
IMPORT: 5,
|
|
87
|
-
EXPORT: 6,
|
|
88
|
-
CUSTOM: 7,
|
|
89
|
-
QR_CODE: 8,
|
|
90
|
-
EDITOWNER: 9,
|
|
91
|
-
BATCH_PRINT: 10,
|
|
92
|
-
BATCH_UPDATE: 11,
|
|
93
|
-
CUSTOMIZE_BTN: 12,
|
|
94
|
-
PRINT: 13,
|
|
95
|
-
COPY: 14,
|
|
96
|
-
TEMPORARY: 15,
|
|
97
|
-
SUBMIT: 16,
|
|
98
|
-
CLEAR_ROW: 17,
|
|
99
|
-
ADD_BEFORE: 18,
|
|
100
|
-
ADD_AFTER: 19,
|
|
101
|
-
MANUAL_SYNC: 20,
|
|
102
|
-
IMPORT_ATTACHMENT: 21,
|
|
103
|
-
EXPORT_ATTACHMENT: 22,
|
|
104
|
-
BATCH_SUBMIT: 23,
|
|
105
|
-
});
|
|
106
|
-
export const QUERY_ACTION_RELATIVE = wireEnum("QueryActionRelativeType", { BIZSHEET: 0, WORKFLOW: 1 });
|
|
107
78
|
/**
|
|
108
79
|
* `BizButtonModel` enums (listview custom buttons). Unlike the query DTOs
|
|
109
80
|
* above, these implement `IStringEnum` with no `@JsonValue`, so Hutool parses
|
|
@@ -391,62 +362,6 @@ export function validatePresentationRenderShape(presentation) {
|
|
|
391
362
|
presentationShape(presentation[key], key);
|
|
392
363
|
}
|
|
393
364
|
}
|
|
394
|
-
function decodeButtonActionConfig(value, code) {
|
|
395
|
-
if (value === undefined)
|
|
396
|
-
return undefined;
|
|
397
|
-
if (typeof value === "string") {
|
|
398
|
-
try {
|
|
399
|
-
value = JSON.parse(value);
|
|
400
|
-
}
|
|
401
|
-
catch (error) {
|
|
402
|
-
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: actionConfig must be valid JSON (${error.message})` });
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
406
|
-
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: actionConfig must decode to an object` });
|
|
407
|
-
}
|
|
408
|
-
return value;
|
|
409
|
-
}
|
|
410
|
-
function requireButtonConfig(config, code, bindAction) {
|
|
411
|
-
const required = bindAction === "SCRIPT" ? ["scriptData"] : bindAction === "BUSINESS_RULE" || bindAction === "FLOW" ? ["targetCode"] : bindAction === "ADD_DATA" || bindAction === "FORM" ? ["schemaCodeVal", "formListData", "configureList"] : [];
|
|
412
|
-
for (const key of required) {
|
|
413
|
-
if (config?.[key] === undefined || config[key] === null) {
|
|
414
|
-
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: ${bindAction} actionConfig requires ${key}` });
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
if (bindAction === "SCRIPT" && typeof config?.scriptData !== "string") {
|
|
418
|
-
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: SCRIPT actionConfig.scriptData must be a string` });
|
|
419
|
-
}
|
|
420
|
-
if ((bindAction === "ADD_DATA" || bindAction === "FORM") && (typeof config?.schemaCodeVal !== "object" || config.schemaCodeVal === null || Array.isArray(config.schemaCodeVal) || !Array.isArray(config.formListData) || !Array.isArray(config.configureList))) {
|
|
421
|
-
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: ${bindAction} actionConfig requires object schemaCodeVal and array formListData/configureList` });
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
export function validateButtonActionConfig(entry) {
|
|
425
|
-
const bindAction = typeof entry.bindAction === "string" ? entry.bindAction : "";
|
|
426
|
-
const code = String(entry.code ?? "");
|
|
427
|
-
const config = decodeButtonActionConfig(entry.actionConfig, code);
|
|
428
|
-
if (bindAction === "BUSINESS_RULE") {
|
|
429
|
-
if (typeof entry.targetCode !== "string" || entry.targetCode.length === 0) {
|
|
430
|
-
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: BUSINESS_RULE requires targetCode` });
|
|
431
|
-
}
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
if (bindAction === "FLOW") {
|
|
435
|
-
if (typeof entry.targetCode !== "string" || entry.targetCode.length === 0) {
|
|
436
|
-
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: FLOW requires targetCode (workflow code)` });
|
|
437
|
-
}
|
|
438
|
-
// A caller-supplied FLOW actionConfig must carry WorkflowListData; the
|
|
439
|
-
// runtime indexes [0].workflowCode and iterates configureList. Omitted is
|
|
440
|
-
// fine — expandButtonActionConfigs derives both from targetCode.
|
|
441
|
-
if (config !== undefined && (typeof config !== "object" || config === null || !Array.isArray(config.WorkflowListData))) {
|
|
442
|
-
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: FLOW actionConfig requires a WorkflowListData array (or omit actionConfig and the tool derives it from targetCode)` });
|
|
443
|
-
}
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
if ((bindAction === "ADD_DATA" || bindAction === "FORM") && (entry.targetModel !== undefined || entry.targetForm !== undefined || entry.configureList !== undefined) && entry.actionConfig === undefined)
|
|
447
|
-
return;
|
|
448
|
-
requireButtonConfig(config, code, bindAction);
|
|
449
|
-
}
|
|
450
365
|
export function columnsRenderCompat(columns) {
|
|
451
366
|
const violations = [];
|
|
452
367
|
for (const [index, column] of columns.entries()) {
|
|
@@ -26,7 +26,7 @@ export const MODEL_WIRE = Object.freeze({
|
|
|
26
26
|
* bypasses the tool precheck; 50000 stands for the generic ServiceException
|
|
27
27
|
* envelope family and the authoritative signal is the verbatim errmsg text.
|
|
28
28
|
*/
|
|
29
|
-
export
|
|
29
|
+
export { MODEL_REFERENCED_DELETE_ERRCODE } from "omc-core/wire-facts";
|
|
30
30
|
/**
|
|
31
31
|
* The platform create-body defaults the emitter sends verbatim. The emitter
|
|
32
32
|
* imports THIS constant to build the wire body (model.create and the
|
|
@@ -86,7 +86,7 @@ export const MODEL_LANE_CONTRACT = Object.freeze({
|
|
|
86
86
|
deleteIdNamespace: Object.freeze([behaviorBinding("test/model-delete-cross-app.test.ts#model.delete refuses fail-closed when the record exists", assertModelContract)]),
|
|
87
87
|
deleteTolerance: Object.freeze([behaviorBinding("test/model-delete-many.test.ts#model.deleteMany short-circuits already-absent codes", assertModelContract)]),
|
|
88
88
|
referencePrecheck: Object.freeze([
|
|
89
|
-
emitterBinding("
|
|
89
|
+
emitterBinding("omc-core/wire-facts.MODEL_REFERENCED_DELETE_ERRCODE", assertModelContract),
|
|
90
90
|
behaviorBinding("test/model-delete-reference-block.test.ts#model.delete refuses with model-referenced-by", assertModelContract),
|
|
91
91
|
]),
|
|
92
92
|
dependencyInvertedDelete: Object.freeze([behaviorBinding("test/model-delete-reference-block.test.ts#dependency-inverted cleanup chain passes", assertModelContract)]),
|
package/dist/lanes/rule-graph.js
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
* node, the key, and what is missing. It runs client-side before any wire
|
|
10
10
|
* call (dry lint) so a mis-shaped graph cannot reach the platform.
|
|
11
11
|
*/
|
|
12
|
+
export { DYNAMIC_CONDITION_SOURCE_KEY, DYNAMIC_ACTION_SOURCE_KEY } from "omc-core/wire-facts";
|
|
13
|
+
import { DYNAMIC_CONDITION_SOURCE_KEY, DYNAMIC_ACTION_SOURCE_KEY } from "omc-core/wire-facts";
|
|
12
14
|
import { decodeJsonColumn, JSON_COLUMN_NULL_COALESCE } from "../json-column.js";
|
|
13
15
|
export const RULE_VALUE_TYPES = ["DYNAMIC", "FIXED", "EMPTY", "VARIABLE", "EXPRESSION", "SYSTEM", "AGGREGATION"];
|
|
14
16
|
/**
|
|
@@ -107,8 +109,6 @@ export const RULE_NODES_NEEDING_INPUT_PARAM = Object.freeze([
|
|
|
107
109
|
"MESSAGE",
|
|
108
110
|
"QUERY",
|
|
109
111
|
]);
|
|
110
|
-
export const DYNAMIC_CONDITION_SOURCE_KEY = "currentSchemaDataItem";
|
|
111
|
-
export const DYNAMIC_ACTION_SOURCE_KEY = "currentDataItemValue";
|
|
112
112
|
/**
|
|
113
113
|
* BRANCH `exitConditionGroups` deserialize into `DataFilterCondition`, whose
|
|
114
114
|
* operator property is `ruleDataConditionType` and whose left/source property
|