omc-core 0.4.36 → 0.4.38
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/capability-shape.js +154 -0
- package/dist/childtable-sheet201-contract.js +1 -1
- package/dist/field-probe-runner-contract.js +1 -1
- package/dist/field-probe-schema.js +19 -0
- package/dist/form-scaffold-contract.js +126 -0
- package/dist/formula-controls-contract.js +1 -1
- package/dist/index.js +7 -0
- package/dist/json-column.js +68 -0
- package/dist/list-view-wire.js +101 -0
- package/dist/refusal.js +9 -0
- package/dist/relation-shape.js +25 -0
- package/dist/wire-facts.js +88 -0
- package/dist/zip.js +156 -0
- package/package.json +14 -3
- package/dist/master-plan-approval.js +0 -443
- package/dist/ticket08-test-model-mutation.js +0 -901
- package/dist/ticket09-test-field-mutation.js +0 -1026
- package/dist/ticket10-test-field-publish.js +0 -886
- package/dist/ticket11-test-form-draft.js +0 -1014
- package/dist/ticket12-test-form-publish.js +0 -917
- package/dist/ticket20-test-data-rule.js +0 -780
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
export const CAPABILITY_CONTROL_TYPES = Object.freeze({
|
|
2
|
+
0: 1,
|
|
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
|
+
}
|
|
154
|
+
//# sourceMappingURL=capability-shape.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildChildTableDeleteRequest, buildSheetControlBinding } from "./childtable-field-contract.js";
|
|
2
|
-
import { ticket11ScaffoldControlKeys, buildTicket11ScaffoldControls, ticket11DraftLayoutIsEmpty } from "./
|
|
2
|
+
import { ticket11ScaffoldControlKeys, buildTicket11ScaffoldControls, ticket11DraftLayoutIsEmpty } from "./form-scaffold-contract.js";
|
|
3
3
|
export const CHILD_TABLE_SHEET201_ENDPOINTS = Object.freeze({
|
|
4
4
|
fieldPublish: "/api/api/app/bizproperty/publish",
|
|
5
5
|
formGet: "/api/api/app/bizsheet/get",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CHILD_TABLE_CONTRACT } from "./childtable-field-contract.js";
|
|
2
2
|
import { FORMULA_FIELD_CONTRACT } from "./formula-field-contract.js";
|
|
3
|
-
import { ticket08TargetSchemaCode, isTicket08TargetSchema } from "./
|
|
3
|
+
import { ticket08TargetSchemaCode, isTicket08TargetSchema } from "./field-probe-schema.js";
|
|
4
4
|
export const FIELD_PROBE_MODES = Object.freeze(["rehearsal", "execute"]);
|
|
5
5
|
export const FIELD_PROBE_KINDS = Object.freeze(["formula", "childtable"]);
|
|
6
6
|
export const FIELD_PROBE_PHASES = Object.freeze(["field-create", "publish", "controls"]);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 字段探针目标模型 schema 契约(生产):omc_t08_<16hex> 生成/识别/断言。
|
|
3
|
+
* 原实现住在 ticket08 测试编排内;C2-lite 拆分——field-probe-runner 与测试岛屿共用。
|
|
4
|
+
*/
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
export const TICKET08_TARGET_SCHEMA_PATTERN = /^omc_t08_[0-9a-f]{16}$/;
|
|
7
|
+
const TICKET08_SCHEMA_CODE_PREFIX = "omc_t08_";
|
|
8
|
+
export function ticket08TargetSchemaCode() {
|
|
9
|
+
return `${TICKET08_SCHEMA_CODE_PREFIX}${randomUUID().replaceAll("-", "").slice(0, 16)}`;
|
|
10
|
+
}
|
|
11
|
+
export function isTicket08TargetSchema(targetSchemaCode) {
|
|
12
|
+
return TICKET08_TARGET_SCHEMA_PATTERN.test(targetSchemaCode);
|
|
13
|
+
}
|
|
14
|
+
export function assertTicket08TargetSchema(targetSchemaCode) {
|
|
15
|
+
if (!isTicket08TargetSchema(targetSchemaCode))
|
|
16
|
+
throw new Error("Ticket 08 targetSchemaCode must be a host-scoped omc_t08_<16-hex> schema within the CloudPivot 8.6.26 26-character code contract.");
|
|
17
|
+
return targetSchemaCode;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=field-probe-schema.js.map
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 表单脚手架契约(生产):默认表单 Title/系统行/Group 三行脚手架的构建与解码。
|
|
3
|
+
* 原实现住在 ticket11 测试编排内;C2-lite 拆分——生产消费者
|
|
4
|
+
* (formula-controls / childtable-sheet201 契约)与测试岛屿共用此唯一事实源。
|
|
5
|
+
*/
|
|
6
|
+
export const TICKET11_SCAFFOLD_SYSTEM_ROW = Object.freeze(["creater", "createdTime", "sequenceNo"]);
|
|
7
|
+
export const TICKET11_SCAFFOLD_SYSTEM_CONTROL_TYPES = Object.freeze({ creater: 101, createdTime: 104, sequenceNo: 100 });
|
|
8
|
+
/** The UID rule value is a designer/composer constant, not a per-model scheme reference (research §13.2). */
|
|
9
|
+
export const TICKET11_SCAFFOLD_SEQNO_UID_VALUE = "aa860e64ff2e4f86851a661d663ae358";
|
|
10
|
+
export const TICKET11_SCAFFOLD_GROUP_NAME = "业务标题";
|
|
11
|
+
export const TICKET11_SCAFFOLD_DATE_FORMAT = "YYYY-MM-DD HH:mm:ss";
|
|
12
|
+
/** One shared timestamp per composition mirrors the designer's Title<ts>/Group<ts> pattern (research §13.4). */
|
|
13
|
+
export function ticket11ScaffoldControlKeys(capturedAt) {
|
|
14
|
+
const epochMs = Date.parse(capturedAt);
|
|
15
|
+
if (!Number.isFinite(epochMs) || epochMs <= 0)
|
|
16
|
+
throw new Error("Ticket 11 scaffold composition requires the evidence capture clock; nothing ships on guessed layout keys.");
|
|
17
|
+
return Object.freeze({ titleKey: `Title${epochMs}`, groupKey: `Group${epochMs}` });
|
|
18
|
+
}
|
|
19
|
+
function deepFrozen(value) {
|
|
20
|
+
if (Array.isArray(value))
|
|
21
|
+
return Object.freeze(value.map(deepFrozen));
|
|
22
|
+
if (value && typeof value === "object")
|
|
23
|
+
return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, deepFrozen(item)])));
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
/** The designer-standard scaffolding control map entries, mirroring the live agent_test_customer sample member-for-member (research §13.1). Deep-frozen: the composition is an immutable contract value. */
|
|
27
|
+
export function buildTicket11ScaffoldControls(input) {
|
|
28
|
+
return deepFrozen({
|
|
29
|
+
[input.titleKey]: {
|
|
30
|
+
key: input.titleKey,
|
|
31
|
+
type: 203,
|
|
32
|
+
options: {
|
|
33
|
+
name: input.formName,
|
|
34
|
+
name_i18n: { en: input.formName, zh: input.formName },
|
|
35
|
+
visible: true,
|
|
36
|
+
labelVisible: true,
|
|
37
|
+
span: 24,
|
|
38
|
+
labelAlign: "",
|
|
39
|
+
style: "",
|
|
40
|
+
tips: "",
|
|
41
|
+
dataItemName: "",
|
|
42
|
+
useDataTitle: true,
|
|
43
|
+
styleInfo: { height: 1, bgColor: "#ffffff", bgImg: "", textAlign: "center", fontSize: 28, textColor: "#111218", blod: true, italic: false, textCss: "", textCssSwitch: false },
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
creater: {
|
|
47
|
+
key: "creater",
|
|
48
|
+
type: TICKET11_SCAFFOLD_SYSTEM_CONTROL_TYPES.creater,
|
|
49
|
+
options: { name: "创建人", name_i18n: "", visible: true, labelVisible: true, span: 8, labelAlign: "", style: "", tips: "", dataItemName: "", widgetType: "", dataItemType: "", mappings: "", verifyFormula: "", defaultValue: "" },
|
|
50
|
+
},
|
|
51
|
+
createdTime: {
|
|
52
|
+
key: "createdTime",
|
|
53
|
+
type: TICKET11_SCAFFOLD_SYSTEM_CONTROL_TYPES.createdTime,
|
|
54
|
+
options: { name: "创建时间", name_i18n: "", visible: true, labelVisible: true, span: 8, labelAlign: "", style: "", tips: "", dataItemName: "", widgetType: "", dataItemType: "", format1: TICKET11_SCAFFOLD_DATE_FORMAT, format: TICKET11_SCAFFOLD_DATE_FORMAT, syncFormate: true, verifyFormula: "", defaultValue: "" },
|
|
55
|
+
},
|
|
56
|
+
sequenceNo: {
|
|
57
|
+
key: "sequenceNo",
|
|
58
|
+
type: TICKET11_SCAFFOLD_SYSTEM_CONTROL_TYPES.sequenceNo,
|
|
59
|
+
options: { name: "单据号", name_i18n: "", visible: true, labelVisible: true, span: 8, labelAlign: "", style: "", tips: "", dataItemName: "", widgetType: "", dataItemType: "", seqNoSetting: [{ type: "UID", value: TICKET11_SCAFFOLD_SEQNO_UID_VALUE }], formPropertyType: 100, verifyFormula: "", displayFormula: "", defaultValue: "" },
|
|
60
|
+
},
|
|
61
|
+
[input.groupKey]: {
|
|
62
|
+
key: input.groupKey,
|
|
63
|
+
type: 200,
|
|
64
|
+
options: { name: TICKET11_SCAFFOLD_GROUP_NAME, tips: "", expand: true, align: "", style: "", styleInfo: { fontSize: 18, textColor: "#111218", blod: true, italic: false, textCss: "", textCssSwitch: false }, span: 24 },
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** An auto-created default form decodes with an empty draft layout (research §2): draftViewJson null/[] and draftAttributesJson null/{}. */
|
|
69
|
+
export function ticket11DraftLayoutIsEmpty(form) {
|
|
70
|
+
let view = null;
|
|
71
|
+
let attributes = null;
|
|
72
|
+
try {
|
|
73
|
+
view = form.draftViewJson === null ? [] : JSON.parse(form.draftViewJson);
|
|
74
|
+
attributes = form.draftAttributesJson === null ? {} : JSON.parse(form.draftAttributesJson);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return Array.isArray(view) && view.length === 0 && typeof attributes === "object" && attributes !== null && !Array.isArray(attributes) && Object.keys(attributes).length === 0;
|
|
80
|
+
}
|
|
81
|
+
export function decodeTicket11ScaffoldPresence(form) {
|
|
82
|
+
if (form.draftViewJson === null || form.draftAttributesJson === null)
|
|
83
|
+
return { decodable: false, rowOrderMatchesContract: false };
|
|
84
|
+
let view;
|
|
85
|
+
let attributes;
|
|
86
|
+
try {
|
|
87
|
+
view = JSON.parse(form.draftViewJson);
|
|
88
|
+
attributes = JSON.parse(form.draftAttributesJson);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return { decodable: false, rowOrderMatchesContract: false };
|
|
92
|
+
}
|
|
93
|
+
if (!Array.isArray(view) || view.some((row) => !Array.isArray(row) || row.some((key) => typeof key !== "string")) || !attributes || typeof attributes !== "object" || Array.isArray(attributes))
|
|
94
|
+
return { decodable: false, rowOrderMatchesContract: false };
|
|
95
|
+
const rows = view;
|
|
96
|
+
const map = attributes;
|
|
97
|
+
const entry = (key) => {
|
|
98
|
+
const value = map[key];
|
|
99
|
+
return value && typeof value === "object" && !Array.isArray(value) && typeof value.type === "number" && value.options && typeof value.options === "object"
|
|
100
|
+
? value
|
|
101
|
+
: undefined;
|
|
102
|
+
};
|
|
103
|
+
// The row contract is positional (research §13.5): row 0 is the single-key
|
|
104
|
+
// Title row, row 1 is exactly the system row, row 2 is the single-key Group
|
|
105
|
+
// row. Extra or duplicated scaffold rows decode as non-contract.
|
|
106
|
+
const isTitleKey = (key) => /^Title\d+$/.test(key);
|
|
107
|
+
const isGroupKey = (key) => /^Group\d+$/.test(key);
|
|
108
|
+
const titleShaped = rows.length >= 3 && rows[0].length === 1 && isTitleKey(rows[0][0]) && entry(rows[0][0]) !== undefined;
|
|
109
|
+
const systemShaped = rows.length >= 3 && rows[1].length === 3 && rows[1][0] === "creater" && rows[1][1] === "createdTime" && rows[1][2] === "sequenceNo";
|
|
110
|
+
const groupShaped = rows.length >= 3 && rows[2].length === 1 && isGroupKey(rows[2][0]) && entry(rows[2][0]) !== undefined;
|
|
111
|
+
if (!titleShaped || !systemShaped || !groupShaped)
|
|
112
|
+
return { decodable: false, rowOrderMatchesContract: false };
|
|
113
|
+
const title = entry(rows[0][0]);
|
|
114
|
+
const group = entry(rows[2][0]);
|
|
115
|
+
const systemTypes = rows[1].map((key) => entry(key)?.type ?? -1);
|
|
116
|
+
const scaffoldRowsOnly = rows.slice(0, 3).every((row, index) => index === 0 ? row.length === 1 && isTitleKey(row[0] ?? "") : index === 1 ? row.length === 3 : row.length === 1 && isGroupKey(row[0] ?? "")) && rows.slice(3).every((row) => row.length === 0 || (!isTitleKey(row[0] ?? "") && !isGroupKey(row[0] ?? "") && row[0] !== "creater" && row[0] !== "createdTime" && row[0] !== "sequenceNo"));
|
|
117
|
+
return Object.freeze({
|
|
118
|
+
decodable: true,
|
|
119
|
+
title: Object.freeze({ key: rows[0][0], type: title.type, useDataTitle: title.options.useDataTitle === true, name: title.options.name }),
|
|
120
|
+
systemRow: Object.freeze({ keys: Object.freeze([...rows[1]]), types: Object.freeze(systemTypes) }),
|
|
121
|
+
group: Object.freeze({ key: rows[2][0], type: group.type }),
|
|
122
|
+
rowOrder: Object.freeze(rows.map((row) => row.join("+"))),
|
|
123
|
+
rowOrderMatchesContract: scaffoldRowsOnly,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=form-scaffold-contract.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { decodeDraftControl } from "./cloudpivot-low-code.js";
|
|
2
2
|
import { buildFormulaFieldDeleteRequest } from "./formula-field-contract.js";
|
|
3
|
-
import { buildTicket11ScaffoldControls, decodeTicket11ScaffoldPresence, ticket11DraftLayoutIsEmpty, ticket11ScaffoldControlKeys } from "./
|
|
3
|
+
import { buildTicket11ScaffoldControls, decodeTicket11ScaffoldPresence, ticket11DraftLayoutIsEmpty, ticket11ScaffoldControlKeys } from "./form-scaffold-contract.js";
|
|
4
4
|
export const FORMULA_CONTROL_TYPE = 89;
|
|
5
5
|
export const FORMULA_FORM_DRAFT_ENDPOINT = "/api/api/app/bizsheet/update";
|
|
6
6
|
export const FORMULA_FORM_PUBLISH_ENDPOINT = "/api/api/app/bizsheet/publish";
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export * from "./zip.js";
|
|
2
|
+
export * from "./capability-shape.js";
|
|
3
|
+
export * from "./json-column.js";
|
|
4
|
+
export * from "./refusal.js";
|
|
5
|
+
export * from "./wire-facts.js";
|
|
6
|
+
export * from "./relation-shape.js";
|
|
7
|
+
export * from "./list-view-wire.js";
|
|
1
8
|
export * from "./ordinary-synthesis-router.js";
|
|
2
9
|
export * from "./cloudpivot-adapter.js";
|
|
3
10
|
export * from "./cloudpivot-fingerprints.js";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ticket 52 — one explicit home for "a platform JSON column may arrive as a
|
|
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
|
+
}
|
|
68
|
+
//# sourceMappingURL=json-column.js.map
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/** 列表视图按钮 wire 契约(真 lane 与假平台共用):动作枚举、按钮校验。原居 cli list-view-shapes;C1 下沉。 */
|
|
2
|
+
import { ToolRefusalError } from "./refusal.js";
|
|
3
|
+
export function wireEnum(key, index) {
|
|
4
|
+
return {
|
|
5
|
+
key,
|
|
6
|
+
names: Object.keys(index),
|
|
7
|
+
index,
|
|
8
|
+
name: Object.fromEntries(Object.entries(index).map(([name, value]) => [value, name])),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* BizQueryActionModel enums. `QueryActionType` (QueryActionType.java:13-35) and
|
|
13
|
+
* `QueryActionRelativeType` (BIZSHEET 0/WORKFLOW 1) both carry
|
|
14
|
+
* `@JsonValue getIndex()` so the wire body is numeric; the tool speaks the enum
|
|
15
|
+
* name and decodes on readback. `associationType`/`associationCode` are the
|
|
16
|
+
* JSON names of the DTO fields `queryActionRelativeType`/`relativeCode`
|
|
17
|
+
* (`@JsonProperty`, BizQueryActionModel.java:31-35).
|
|
18
|
+
*/
|
|
19
|
+
export const QUERY_ACTION_TYPE = wireEnum("QueryActionType", {
|
|
20
|
+
QUERY: 1,
|
|
21
|
+
ADD: 2,
|
|
22
|
+
EDIT: 3,
|
|
23
|
+
DELETE: 4,
|
|
24
|
+
IMPORT: 5,
|
|
25
|
+
EXPORT: 6,
|
|
26
|
+
CUSTOM: 7,
|
|
27
|
+
QR_CODE: 8,
|
|
28
|
+
EDITOWNER: 9,
|
|
29
|
+
BATCH_PRINT: 10,
|
|
30
|
+
BATCH_UPDATE: 11,
|
|
31
|
+
CUSTOMIZE_BTN: 12,
|
|
32
|
+
PRINT: 13,
|
|
33
|
+
COPY: 14,
|
|
34
|
+
TEMPORARY: 15,
|
|
35
|
+
SUBMIT: 16,
|
|
36
|
+
CLEAR_ROW: 17,
|
|
37
|
+
ADD_BEFORE: 18,
|
|
38
|
+
ADD_AFTER: 19,
|
|
39
|
+
MANUAL_SYNC: 20,
|
|
40
|
+
IMPORT_ATTACHMENT: 21,
|
|
41
|
+
EXPORT_ATTACHMENT: 22,
|
|
42
|
+
BATCH_SUBMIT: 23,
|
|
43
|
+
});
|
|
44
|
+
export const QUERY_ACTION_RELATIVE = wireEnum("QueryActionRelativeType", { BIZSHEET: 0, WORKFLOW: 1 });
|
|
45
|
+
function decodeButtonActionConfig(value, code) {
|
|
46
|
+
if (value === undefined)
|
|
47
|
+
return undefined;
|
|
48
|
+
if (typeof value === "string") {
|
|
49
|
+
try {
|
|
50
|
+
value = JSON.parse(value);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: actionConfig must be valid JSON (${error.message})` });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
57
|
+
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: actionConfig must decode to an object` });
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
function requireButtonConfig(config, code, bindAction) {
|
|
62
|
+
const required = bindAction === "SCRIPT" ? ["scriptData"] : bindAction === "BUSINESS_RULE" || bindAction === "FLOW" ? ["targetCode"] : bindAction === "ADD_DATA" || bindAction === "FORM" ? ["schemaCodeVal", "formListData", "configureList"] : [];
|
|
63
|
+
for (const key of required) {
|
|
64
|
+
if (config?.[key] === undefined || config[key] === null) {
|
|
65
|
+
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: ${bindAction} actionConfig requires ${key}` });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (bindAction === "SCRIPT" && typeof config?.scriptData !== "string") {
|
|
69
|
+
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: SCRIPT actionConfig.scriptData must be a string` });
|
|
70
|
+
}
|
|
71
|
+
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))) {
|
|
72
|
+
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: ${bindAction} actionConfig requires object schemaCodeVal and array formListData/configureList` });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export function validateButtonActionConfig(entry) {
|
|
76
|
+
const bindAction = typeof entry.bindAction === "string" ? entry.bindAction : "";
|
|
77
|
+
const code = String(entry.code ?? "");
|
|
78
|
+
const config = decodeButtonActionConfig(entry.actionConfig, code);
|
|
79
|
+
if (bindAction === "BUSINESS_RULE") {
|
|
80
|
+
if (typeof entry.targetCode !== "string" || entry.targetCode.length === 0) {
|
|
81
|
+
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: BUSINESS_RULE requires targetCode` });
|
|
82
|
+
}
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (bindAction === "FLOW") {
|
|
86
|
+
if (typeof entry.targetCode !== "string" || entry.targetCode.length === 0) {
|
|
87
|
+
throw new ToolRefusalError({ status: "refused", blocker: "listview-buttons-invalid", message: `buttons ${code}: FLOW requires targetCode (workflow code)` });
|
|
88
|
+
}
|
|
89
|
+
// A caller-supplied FLOW actionConfig must carry WorkflowListData; the
|
|
90
|
+
// runtime indexes [0].workflowCode and iterates configureList. Omitted is
|
|
91
|
+
// fine — expandButtonActionConfigs derives both from targetCode.
|
|
92
|
+
if (config !== undefined && (typeof config !== "object" || config === null || !Array.isArray(config.WorkflowListData))) {
|
|
93
|
+
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)` });
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if ((bindAction === "ADD_DATA" || bindAction === "FORM") && (entry.targetModel !== undefined || entry.targetForm !== undefined || entry.configureList !== undefined) && entry.actionConfig === undefined)
|
|
98
|
+
return;
|
|
99
|
+
requireButtonConfig(config, code, bindAction);
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=list-view-wire.js.map
|
package/dist/refusal.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** 受治理拒收(跨适配器契约):真 lane 与假平台抛同一类、同一实例。原居 cli tool-types;C1 下沉。 */
|
|
2
|
+
export class ToolRefusalError extends Error {
|
|
3
|
+
payload;
|
|
4
|
+
constructor(payload) {
|
|
5
|
+
super(String(payload["blocker"] ?? "tool refused"));
|
|
6
|
+
this.payload = payload;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=refusal.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** 关联字段目标模型解析(真 lane 与假平台共用)。原居 cli field-families;C1 下沉。 */
|
|
2
|
+
import { decodeJsonObjectColumn } from "./json-column.js";
|
|
3
|
+
/** relation options face decode; policy: string-or-object accepted, garbage → undefined. */
|
|
4
|
+
export function relationOptions(options) {
|
|
5
|
+
return decodeJsonObjectColumn(options, { invalid: "undefined", array: "accept", object: "accept" });
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The relation target model of a bizproperty row. Relation families
|
|
9
|
+
* (WORK_SHEET / MULT_WORK_SHEET) carry the target model code both as the
|
|
10
|
+
* row-level `relativeCode` and as the `schemaCode` key inside the options JSON
|
|
11
|
+
* (field.create writes both). RELEVANCE_DATA(16) is deliberately excluded: it
|
|
12
|
+
* references another *field* (relevanceField/referenceField), not a model, so
|
|
13
|
+
* it adds no inbound model reference of its own — the local WORK_SHEET field it
|
|
14
|
+
* resolves through already carries that reference. Either face counts as an
|
|
15
|
+
* inbound reference for the model.delete precheck (ticket 23: the server's
|
|
16
|
+
* deleteFunctions dispatcher refuses asynchronously with
|
|
17
|
+
* "xxx被模型 yyy 引用,不能删除" — the tool surfaces that check at call time).
|
|
18
|
+
*/
|
|
19
|
+
export function relationTargetOf(row) {
|
|
20
|
+
if (typeof row.relativeCode === "string" && row.relativeCode.trim())
|
|
21
|
+
return row.relativeCode.trim();
|
|
22
|
+
const schemaCode = relationOptions(row.options)?.schemaCode;
|
|
23
|
+
return typeof schemaCode === "string" && schemaCode.trim() ? schemaCode.trim() : undefined;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=relation-shape.js.map
|