@stalfh233/omc-cli 0.0.0-stage → 0.4.10
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/LICENSE +21 -0
- package/README.md +293 -3
- package/coverage/m1-coverage-manifest-v1.json +1278 -0
- package/dist/approval-token.js +102 -0
- package/dist/args.js +25 -0
- package/dist/artifacts.js +119 -0
- package/dist/bench/call-face-eval.js +256 -0
- package/dist/bench/context-attribution.js +151 -0
- package/dist/bench/discovery-cost-eval.js +230 -0
- package/dist/bench/driver.js +81 -0
- package/dist/bench/evals.js +236 -0
- package/dist/bench/fake-http-server.js +65 -0
- package/dist/bench/instrument.js +87 -0
- package/dist/bench/intent-face-eval.js +343 -0
- package/dist/bench/run.js +166 -0
- package/dist/bench/scenario.js +343 -0
- package/dist/bench/types.js +76 -0
- package/dist/bi-wire.js +41 -0
- package/dist/bizservice-config.js +581 -0
- package/dist/call.js +153 -0
- package/dist/capability-absences.js +23 -0
- package/dist/capability-overview.js +492 -0
- package/dist/capability-shape.js +154 -0
- package/dist/cli-contract.js +70 -0
- package/dist/cli-output.js +73 -0
- package/dist/cli.js +1418 -0
- package/dist/code-rules.js +69 -0
- package/dist/command-transport.js +155 -0
- package/dist/config-store.js +195 -0
- package/dist/context.js +21 -0
- package/dist/contract-consistency.js +66 -0
- package/dist/contract-resources.js +62 -0
- package/dist/coverage-consistency.js +62 -0
- package/dist/coverage-registry.js +76 -0
- package/dist/coverage.js +130 -0
- package/dist/data-list-filter.js +114 -0
- package/dist/discovery.js +390 -0
- package/dist/endpoints.js +154 -0
- package/dist/environment-policy.js +26 -0
- package/dist/execution-metadata.js +1092 -0
- package/dist/fake/app.js +45 -0
- package/dist/fake/b2-registration.js +594 -0
- package/dist/fake/businessrule.js +242 -0
- package/dist/fake/datarule.js +72 -0
- package/dist/fake/dictionary.js +108 -0
- package/dist/fake/environment.js +30 -0
- package/dist/fake/field.js +167 -0
- package/dist/fake/form.js +101 -0
- package/dist/fake/index.js +121 -0
- package/dist/fake/list-view.js +289 -0
- package/dist/fake/model.js +136 -0
- package/dist/fake/online-js.js +18 -0
- package/dist/fake/report.js +253 -0
- package/dist/fake/routes.js +47 -0
- package/dist/fake/rule-lifecycle.js +37 -0
- package/dist/fake/runtime-data.js +367 -0
- package/dist/fake/state.js +67 -0
- package/dist/fake/workflow.js +364 -0
- package/dist/field-change.js +200 -0
- package/dist/field-families.js +896 -0
- package/dist/form-layout.js +111 -0
- package/dist/form-support.js +821 -0
- package/dist/goal-routes.js +468 -0
- package/dist/governed-execution.js +87 -0
- package/dist/human-summary.js +212 -0
- package/dist/identity.js +62 -0
- package/dist/intent/baseline.js +57 -0
- package/dist/intent/capabilities/bizservice.js +274 -0
- package/dist/intent/capabilities/businessrule.js +493 -0
- package/dist/intent/capabilities/datarule.js +187 -0
- package/dist/intent/capabilities/field.js +545 -0
- package/dist/intent/capabilities/form.js +136 -0
- package/dist/intent/capabilities/index.js +64 -0
- package/dist/intent/capabilities/listview.js +157 -0
- package/dist/intent/capabilities/model.js +100 -0
- package/dist/intent/capabilities/onlinejs.js +108 -0
- package/dist/intent/capabilities/report.js +355 -0
- package/dist/intent/capabilities/workflow.js +458 -0
- package/dist/intent/capability.js +6 -0
- package/dist/intent/cli.js +91 -0
- package/dist/intent/compare.js +56 -0
- package/dist/intent/compiler.js +79 -0
- package/dist/intent/dsl.js +129 -0
- package/dist/intent/plan-file.js +63 -0
- package/dist/intent/readback.js +65 -0
- package/dist/intent/schema.js +158 -0
- package/dist/intent/validation.js +30 -0
- package/dist/intent/yaml.js +315 -0
- package/dist/json-column.js +68 -0
- package/dist/lanes/app-contract.js +95 -0
- package/dist/lanes/app-coverage.js +16 -0
- package/dist/lanes/app.js +174 -0
- package/dist/lanes/apply-changes.js +231 -0
- package/dist/lanes/b2-registration-contract.js +292 -0
- package/dist/lanes/b2-registration-coverage.js +48 -0
- package/dist/lanes/b2-registration.js +1187 -0
- package/dist/lanes/businessrule-contract.js +232 -0
- package/dist/lanes/businessrule-coverage.js +16 -0
- package/dist/lanes/businessrule.js +221 -0
- package/dist/lanes/contract-support.js +65 -0
- package/dist/lanes/coverage-declaration.js +9 -0
- package/dist/lanes/datarule-contract.js +155 -0
- package/dist/lanes/datarule-coverage.js +19 -0
- package/dist/lanes/datarule-protocol.js +308 -0
- package/dist/lanes/datarule.js +818 -0
- package/dist/lanes/dictionary-contract.js +78 -0
- package/dist/lanes/dictionary-coverage.js +24 -0
- package/dist/lanes/dictionary.js +235 -0
- package/dist/lanes/environment-contract.js +70 -0
- package/dist/lanes/environment-coverage.js +14 -0
- package/dist/lanes/environment.js +163 -0
- package/dist/lanes/field-contract.js +223 -0
- package/dist/lanes/field-coverage.js +27 -0
- package/dist/lanes/field.js +374 -0
- package/dist/lanes/form-contract.js +97 -0
- package/dist/lanes/form-coverage.js +16 -0
- package/dist/lanes/form.js +185 -0
- package/dist/lanes/lane-ids.js +34 -0
- package/dist/lanes/list-view-contract.js +175 -0
- package/dist/lanes/list-view-coverage.js +20 -0
- package/dist/lanes/list-view-shapes.js +1207 -0
- package/dist/lanes/list-view.js +578 -0
- package/dist/lanes/meta-contract.js +85 -0
- package/dist/lanes/meta.js +255 -0
- package/dist/lanes/model-contract.js +168 -0
- package/dist/lanes/model-coverage.js +20 -0
- package/dist/lanes/model.js +986 -0
- package/dist/lanes/online-js-contract.js +99 -0
- package/dist/lanes/online-js-coverage.js +28 -0
- package/dist/lanes/online-js.js +127 -0
- package/dist/lanes/report-contract.js +144 -0
- package/dist/lanes/report-coverage.js +21 -0
- package/dist/lanes/report.js +476 -0
- package/dist/lanes/rule-graph.js +1846 -0
- package/dist/lanes/rule-lifecycle-contract.js +92 -0
- package/dist/lanes/rule-lifecycle-coverage.js +20 -0
- package/dist/lanes/rule-lifecycle.js +176 -0
- package/dist/lanes/runtime-data-contract.js +264 -0
- package/dist/lanes/runtime-data-coverage.js +25 -0
- package/dist/lanes/runtime-data.js +1054 -0
- package/dist/lanes/workflow-contract.js +223 -0
- package/dist/lanes/workflow-coverage.js +40 -0
- package/dist/lanes/workflow.js +1813 -0
- package/dist/online-js-layout.js +58 -0
- package/dist/online-js-source.js +276 -0
- package/dist/package-tool.js +51 -0
- package/dist/package.js +73 -0
- package/dist/plan.js +73 -0
- package/dist/read.js +144 -0
- package/dist/redact.js +28 -0
- package/dist/rule-support.js +587 -0
- package/dist/runtime-support.js +134 -0
- package/dist/server.js +92 -0
- package/dist/session-manager.js +30 -0
- package/dist/session.js +149 -0
- package/dist/skills.js +112 -0
- package/dist/support.js +98 -0
- package/dist/tool-types.js +127 -0
- package/dist/tools.js +59 -0
- package/dist/usage-log.js +197 -0
- package/dist/wire.js +287 -0
- package/dist/workflow-support.js +99 -0
- package/dist/write-lease.js +26 -0
- package/dist/write-lock.js +109 -0
- package/dist/write.js +176 -0
- package/dist/zip.js +156 -0
- package/docs/tool-surface-map.md +42 -0
- package/package.json +71 -6
- package/skills/omc-acceptance-criteria.md +47 -0
- package/skills/omc-business-configuration.md +257 -0
- package/skills/omc-capabilities.md +197 -0
- package/skills/omc-capability-scouting.md +55 -0
- package/skills/omc-config-draft-review.md +171 -0
- package/skills/omc-five-piece-flow.md +35 -0
- package/skills/omc-glossary.md +86 -0
- package/skills/omc-refusals.md +110 -0
- package/skills/omc-requirement-analysis.md +161 -0
- package/skills/omc-requirement-vocabulary.md +51 -0
- package/skills/omc-start-here.md +82 -0
- package/skills/omc-tool-selection.md +119 -0
- package/skills/omc-write-hazards.md +87 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { ENDPOINTS } from "../../endpoints.js";
|
|
2
|
+
import { wireCall, WireError } from "../../wire.js";
|
|
3
|
+
import { refuse } from "../capability.js";
|
|
4
|
+
import { describe, isScalarString, pushError } from "../validation.js";
|
|
5
|
+
/** Keys a form block accepts. */
|
|
6
|
+
const FORM_KEYS = new Set(["publish", "fieldCodes"]);
|
|
7
|
+
function parseJsonObject(value) {
|
|
8
|
+
if (typeof value !== "string")
|
|
9
|
+
return {};
|
|
10
|
+
try {
|
|
11
|
+
const parsed = JSON.parse(value);
|
|
12
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The default-form publish chain (CONTEXT.md 表单发布链): draft-first layout,
|
|
20
|
+
* publish, and published-binding readback. It requires the field capability
|
|
21
|
+
* because a form draft binds declared fields and must follow their publication.
|
|
22
|
+
*/
|
|
23
|
+
export const formCapability = {
|
|
24
|
+
id: "form",
|
|
25
|
+
title: "默认表单",
|
|
26
|
+
requires: ["field"],
|
|
27
|
+
operations: ["draft", "publish"],
|
|
28
|
+
validate(node, draft, ctx) {
|
|
29
|
+
const formNode = node.entries.find((entry) => entry.key === "form")?.value;
|
|
30
|
+
if (formNode === undefined || (formNode.kind === "scalar" && formNode.value === null))
|
|
31
|
+
return;
|
|
32
|
+
if (formNode.kind !== "map") {
|
|
33
|
+
pushError(ctx, "intent-form-not-a-map", `form 必须是映射(只接受 publish)——得到 ${describe(formNode)}`, `${ctx.base}.form`, "value");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
for (const entry of formNode.entries) {
|
|
37
|
+
if (!FORM_KEYS.has(entry.key))
|
|
38
|
+
pushError(ctx, "intent-key-unknown", `form 不支持键 "${entry.key}"(只接受 publish / fieldCodes)`, `${ctx.base}.form.${entry.key}`);
|
|
39
|
+
}
|
|
40
|
+
const publishNode = formNode.entries.find((entry) => entry.key === "publish")?.value;
|
|
41
|
+
if (publishNode === undefined) {
|
|
42
|
+
pushError(ctx, "intent-form-publish-missing", "form.publish 缺失(必须显式 true/false——缺 form 键则停在字段发布,不触表单生命周期)", `${ctx.base}.form`, "value");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (publishNode.kind !== "scalar" || typeof publishNode.value !== "boolean") {
|
|
46
|
+
pushError(ctx, "intent-form-publish-not-boolean", `form.publish 必须是布尔值(得到 ${describe(publishNode)})`, `${ctx.base}.form.publish`, "value");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const fieldCodesNode = formNode.entries.find((entry) => entry.key === "fieldCodes")?.value;
|
|
50
|
+
let fieldCodes;
|
|
51
|
+
if (fieldCodesNode !== undefined) {
|
|
52
|
+
const valid = fieldCodesNode.kind === "seq" && fieldCodesNode.items.every((item) => isScalarString(item));
|
|
53
|
+
if (!valid)
|
|
54
|
+
pushError(ctx, "intent-form-fieldcodes-invalid", "form.fieldCodes 必须是非空字符串列表", `${ctx.base}.form.fieldCodes`, "value");
|
|
55
|
+
else
|
|
56
|
+
fieldCodes = fieldCodesNode.items.map((item) => item.value.trim());
|
|
57
|
+
}
|
|
58
|
+
draft.form = { publish: publishNode.value, ...(fieldCodes ? { fieldCodes } : {}) };
|
|
59
|
+
},
|
|
60
|
+
async read(session, _intent, draft) {
|
|
61
|
+
if (!draft.present)
|
|
62
|
+
return;
|
|
63
|
+
try {
|
|
64
|
+
const sheet = (await wireCall(session, { path: ENDPOINTS.sheetGet, query: { schemaCode: draft.code, sheetCode: draft.code } })).data;
|
|
65
|
+
if (!sheet || typeof sheet !== "object")
|
|
66
|
+
return;
|
|
67
|
+
const row = sheet;
|
|
68
|
+
draft.form = {
|
|
69
|
+
present: true,
|
|
70
|
+
published: row.published === true || typeof row.publishedViewJson === "string",
|
|
71
|
+
existDraft: row.existDraft === true || typeof row.draftViewJson === "string",
|
|
72
|
+
boundFieldCodes: Array.isArray(row.binding?.fieldCodes) && row.binding.fieldCodes.length > 0
|
|
73
|
+
? row.binding.fieldCodes.map(String)
|
|
74
|
+
: Object.keys(parseJsonObject(row.publishedAttributesJson ?? row.draftAttributesJson)),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
// Form absence 302024 ("表单不存在!") is the authoritative absence probe
|
|
79
|
+
// on the live face; the fake answers errcode 0 with null data instead.
|
|
80
|
+
if (error instanceof WireError && error.errcode === 302024)
|
|
81
|
+
return;
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
plan(intent, baseline, emit) {
|
|
86
|
+
const form = intent.form;
|
|
87
|
+
if (!form)
|
|
88
|
+
return;
|
|
89
|
+
const declaredCodes = new Set(intent.fields.map((field) => field.code));
|
|
90
|
+
const formFieldCodes = form.fieldCodes ?? intent.fields.map((field) => field.code);
|
|
91
|
+
const invalidFormCodes = formFieldCodes.filter((code) => !declaredCodes.has(code));
|
|
92
|
+
if (invalidFormCodes.length > 0)
|
|
93
|
+
refuse("intent-form-fieldcodes-unknown", `表单 ${intent.code} 声明了模型字段之外的绑定:${invalidFormCodes.join("、")}`);
|
|
94
|
+
if (form.publish !== true)
|
|
95
|
+
return;
|
|
96
|
+
const requiresExactFormBinding = form.fieldCodes !== undefined;
|
|
97
|
+
const sameCodes = (left, right) => left.length === right.length && left.every((code) => right.includes(code));
|
|
98
|
+
const current = baseline.form;
|
|
99
|
+
if (current.present === true && current.published && (!requiresExactFormBinding || sameCodes(current.boundFieldCodes, formFieldCodes))) {
|
|
100
|
+
emit.skip(`form:${intent.code}`, "form-published");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (current.present !== true || !current.existDraft || (requiresExactFormBinding && !sameCodes(current.boundFieldCodes, formFieldCodes)))
|
|
104
|
+
emit.change("form.draft", { schemaCode: intent.code, sheetCode: intent.code, fieldCodes: formFieldCodes }, `form:${intent.code}`, current.present === true ? "form-no-draft" : "form-absent");
|
|
105
|
+
emit.change("form.publish", { schemaCode: intent.code, sheetCode: intent.code }, `form:${intent.code}`, "form-unpublished");
|
|
106
|
+
},
|
|
107
|
+
decode(baseline, draft) {
|
|
108
|
+
// The form attributes JSON carries designer LAYOUT controls (section
|
|
109
|
+
// titles/groups keyed as Title<ts>/Group<ts>) alongside field controls;
|
|
110
|
+
// only real model fields count as bindings (ticket 120 decoder honesty).
|
|
111
|
+
const fieldCodes = new Set(baseline.fields.map((field) => field.code));
|
|
112
|
+
const boundFieldCodes = baseline.form.boundFieldCodes.filter((code) => fieldCodes.has(code));
|
|
113
|
+
draft.form = { ...baseline.form, boundFieldCodes };
|
|
114
|
+
draft.frontend.formBoundFieldCodes = boundFieldCodes;
|
|
115
|
+
},
|
|
116
|
+
compare(intent, live, emit) {
|
|
117
|
+
if (intent.form?.publish !== true)
|
|
118
|
+
return;
|
|
119
|
+
if (!live.present) {
|
|
120
|
+
emit.difference("form-missing", `form:${intent.code}`, `表单 ${intent.code} 随模型缺失`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (!live.form.present) {
|
|
124
|
+
emit.difference("form-missing", `form:${intent.code}`, `默认表单 ${intent.code} 不存在(302024/空 data 解码为 absent)`);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (!live.form.published) {
|
|
128
|
+
emit.difference("form-not-published", `form:${intent.code}`, `默认表单 ${intent.code} 未发布`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const wantedFormFields = intent.form.fieldCodes ?? intent.fields.map((field) => field.code);
|
|
132
|
+
if (wantedFormFields.some((code) => !live.frontend.formBoundFieldCodes.includes(code)))
|
|
133
|
+
emit.difference("form-not-bound", `form:${intent.code}`, `默认表单 ${intent.code} 的控件绑定未覆盖声明字段`);
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=form.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { businessruleCapability } from "./businessrule.js";
|
|
2
|
+
import { bizserviceCapability } from "./bizservice.js";
|
|
3
|
+
import { dataruleCapability } from "./datarule.js";
|
|
4
|
+
import { fieldCapability } from "./field.js";
|
|
5
|
+
import { formCapability } from "./form.js";
|
|
6
|
+
import { listviewCapability } from "./listview.js";
|
|
7
|
+
import { modelCapability } from "./model.js";
|
|
8
|
+
import { onlinejsCapability } from "./onlinejs.js";
|
|
9
|
+
import { reportCapability } from "./report.js";
|
|
10
|
+
import { workflowCapability } from "./workflow.js";
|
|
11
|
+
/**
|
|
12
|
+
* Every Configuration capability the intent face carries, in declaration
|
|
13
|
+
* order. Declaration order is only the tie-break for the topological sort —
|
|
14
|
+
* execution order comes from each capability's `requires`.
|
|
15
|
+
*/
|
|
16
|
+
export const INTENT_CAPABILITIES = Object.freeze([
|
|
17
|
+
modelCapability,
|
|
18
|
+
fieldCapability,
|
|
19
|
+
formCapability,
|
|
20
|
+
listviewCapability,
|
|
21
|
+
bizserviceCapability,
|
|
22
|
+
dataruleCapability,
|
|
23
|
+
businessruleCapability,
|
|
24
|
+
workflowCapability,
|
|
25
|
+
onlinejsCapability,
|
|
26
|
+
reportCapability,
|
|
27
|
+
]);
|
|
28
|
+
/**
|
|
29
|
+
* The capabilities that participate in one model's lifecycle. A model declared
|
|
30
|
+
* `absent:true` is a deletion target, so ONLY the model capability runs — its
|
|
31
|
+
* fields/form/listview/rules/... go with it and must not be read or planned.
|
|
32
|
+
*/
|
|
33
|
+
export function capabilitiesForModel(model) {
|
|
34
|
+
const ordered = orderedCapabilities();
|
|
35
|
+
return model.absent === true ? ordered.filter((capability) => capability.id === "model") : ordered;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Capabilities in execution order. `requires` names capabilities that must run
|
|
39
|
+
* first; among capabilities whose dependencies are already satisfied,
|
|
40
|
+
* declaration order wins, so the result is deterministic. A cycle or an
|
|
41
|
+
* unknown requirement fails closed rather than silently reordering.
|
|
42
|
+
*/
|
|
43
|
+
export function orderedCapabilities() {
|
|
44
|
+
const byId = new Map(INTENT_CAPABILITIES.map((capability) => [capability.id, capability]));
|
|
45
|
+
for (const capability of INTENT_CAPABILITIES) {
|
|
46
|
+
for (const requirement of capability.requires) {
|
|
47
|
+
if (!byId.has(requirement))
|
|
48
|
+
throw new Error(`capability "${capability.id}" requires unknown capability "${requirement}"`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const remaining = [...INTENT_CAPABILITIES];
|
|
52
|
+
const done = new Set();
|
|
53
|
+
const ordered = [];
|
|
54
|
+
while (remaining.length > 0) {
|
|
55
|
+
const index = remaining.findIndex((capability) => capability.requires.every((requirement) => done.has(requirement)));
|
|
56
|
+
if (index < 0)
|
|
57
|
+
throw new Error(`capability requires cycle among: ${remaining.map((capability) => capability.id).join(", ")}`);
|
|
58
|
+
const [capability] = remaining.splice(index, 1);
|
|
59
|
+
ordered.push(capability);
|
|
60
|
+
done.add(capability.id);
|
|
61
|
+
}
|
|
62
|
+
return ordered;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { ENDPOINTS } from "../../endpoints.js";
|
|
2
|
+
import { wireCall, WireError } from "../../wire.js";
|
|
3
|
+
import { refuse } from "../capability.js";
|
|
4
|
+
import { SYSTEM_FIELD_CODES } from "../../field-families.js";
|
|
5
|
+
import { isScalarString, pushError } from "../validation.js";
|
|
6
|
+
/** Keys a list-view block accepts. */
|
|
7
|
+
const LISTVIEW_KEYS = new Set(["code", "name", "fieldCodes", "absent"]);
|
|
8
|
+
/**
|
|
9
|
+
* The list-view design-time header and column lifecycle. It requires the field
|
|
10
|
+
* capability because its columns reference declared fields.
|
|
11
|
+
*/
|
|
12
|
+
export const listviewCapability = {
|
|
13
|
+
id: "listview",
|
|
14
|
+
title: "列表视图",
|
|
15
|
+
requires: ["field"],
|
|
16
|
+
operations: ["create", "configure", "delete"],
|
|
17
|
+
validate(node, draft, ctx) {
|
|
18
|
+
const listviewNode = node.entries.find((entry) => entry.key === "listview")?.value;
|
|
19
|
+
if (listviewNode === undefined || (listviewNode.kind === "scalar" && listviewNode.value === null))
|
|
20
|
+
return;
|
|
21
|
+
if (listviewNode.kind !== "map") {
|
|
22
|
+
pushError(ctx, "intent-listview-not-a-map", "listview 必须是映射(只接受 code / name / fieldCodes / absent)", `${ctx.base}.listview`, "value");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
// Keys the compiler itself derives (never caller-declared): naming them is a
|
|
26
|
+
// harmless misunderstanding of the column model, so explain that fieldCodes
|
|
27
|
+
// is authoritative and the platform defaults are cleared automatically.
|
|
28
|
+
const COMPILER_MANAGED_KEYS = new Set(["hideDefaultSystemFields", "replaceColumns"]);
|
|
29
|
+
// Keys the intent face deliberately does NOT support declaring: business
|
|
30
|
+
// sorts / filters / actions / presentation are cleared to the platform
|
|
31
|
+
// default by the compiler, so a caller who declared them must fall back to
|
|
32
|
+
// `listview.configure` AFTER apply. The old message claimed the compiler
|
|
33
|
+
// "自动处理" these, which read as "declaring them works" and cost a retry
|
|
34
|
+
// (bench67). Name the escape hatch explicitly.
|
|
35
|
+
const CONFIGURE_ONLY_KEYS = new Set(["conditions", "sorts", "actions", "presentation"]);
|
|
36
|
+
for (const entry of listviewNode.entries) {
|
|
37
|
+
if (!LISTVIEW_KEYS.has(entry.key)) {
|
|
38
|
+
const message = COMPILER_MANAGED_KEYS.has(entry.key)
|
|
39
|
+
? `listview 不支持键 "${entry.key}"——**编译器自动处理**:fieldCodes 就是视图的权威列集合(自动 replaceColumns + hideDefaultSystemFields,清掉平台预置的 name/creater/createdTime/sequenceNo 系统列,并清空默认的 createdTime 筛选/排序)。只需写 fieldCodes;要展示某系统列就把它写进 fieldCodes`
|
|
40
|
+
: CONFIGURE_ONLY_KEYS.has(entry.key)
|
|
41
|
+
? `listview 不支持键 "${entry.key}":intent 的 listview 只声明列(code / name / fieldCodes / absent),**不声明业务筛选/排序/按钮/展示**——编译器会把它们清成平台默认。要配置业务条件/排序请 intent apply 之后用 \`omc call listview.configure\`(它的 conditions/sorts/actions 会增量合并到视图)`
|
|
42
|
+
: entry.key === "columns"
|
|
43
|
+
? `listview 不支持键 "columns";请改用 "fieldCodes" 声明列(只接受 code / name / fieldCodes / absent)`
|
|
44
|
+
: `listview 不支持键 "${entry.key}"(只接受 code / name / fieldCodes / absent)`;
|
|
45
|
+
pushError(ctx, "intent-key-unknown", message, `${ctx.base}.listview.${entry.key}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const lvCode = listviewNode.entries.find((entry) => entry.key === "code")?.value;
|
|
49
|
+
const lvName = listviewNode.entries.find((entry) => entry.key === "name")?.value;
|
|
50
|
+
const lvFields = listviewNode.entries.find((entry) => entry.key === "fieldCodes")?.value;
|
|
51
|
+
const lvAbsent = listviewNode.entries.find((entry) => entry.key === "absent")?.value;
|
|
52
|
+
// `code` and `name` default to the platform's own default view (code == the
|
|
53
|
+
// model/schema code) so a caller only has to declare `fieldCodes`. Omitting
|
|
54
|
+
// them used to be a hard error (bench41: two `intent-listview-*-invalid`
|
|
55
|
+
// errors and a retry); the default is unambiguous and matches the
|
|
56
|
+
// documented `listview.configure` behaviour (omit code → the default view).
|
|
57
|
+
if (lvCode !== undefined && !(lvCode.kind === "scalar" && lvCode.value === null) && !isScalarString(lvCode))
|
|
58
|
+
pushError(ctx, "intent-listview-code-invalid", "listview.code 必须是非空字符串或省略(省略即模型默认视图 code=schemaCode)", `${ctx.base}.listview.code`, "value");
|
|
59
|
+
if (lvName !== undefined && !(lvName.kind === "scalar" && lvName.value === null) && !isScalarString(lvName))
|
|
60
|
+
pushError(ctx, "intent-listview-name-invalid", "listview.name 必须是非空字符串或省略(省略即「<模型名>默认视图」)", `${ctx.base}.listview.name`, "value");
|
|
61
|
+
const resolvedCode = isScalarString(lvCode) ? lvCode.value.trim() : draft.code;
|
|
62
|
+
const resolvedName = isScalarString(lvName) ? lvName.value : `${draft.name || draft.code}默认视图`;
|
|
63
|
+
if (lvAbsent !== undefined && (lvAbsent.kind !== "scalar" || typeof lvAbsent.value !== "boolean"))
|
|
64
|
+
pushError(ctx, "intent-listview-absent-invalid", "listview.absent 必须是布尔", `${ctx.base}.listview.absent`, "value");
|
|
65
|
+
if (lvAbsent?.kind === "scalar" && lvAbsent.value === true) {
|
|
66
|
+
for (const key of ["name", "fieldCodes"])
|
|
67
|
+
if (listviewNode.entries.some((entry) => entry.key === key))
|
|
68
|
+
pushError(ctx, "intent-listview-absent-with-shape", `列表视图声明 absent:true 表示删除该视图,不能再声明 ${key}`, `${ctx.base}.listview.${key}`);
|
|
69
|
+
draft.listview = { code: resolvedCode, absent: true };
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const validFields = lvFields?.kind === "seq" && lvFields.items.length > 0 && lvFields.items.every((item) => isScalarString(item));
|
|
73
|
+
if (!validFields)
|
|
74
|
+
pushError(ctx, "intent-listview-fieldcodes-invalid", "listview.fieldCodes 必须是非空字符串列表", `${ctx.base}.listview.fieldCodes`, "value");
|
|
75
|
+
if (validFields)
|
|
76
|
+
draft.listview = { code: resolvedCode, name: resolvedName, fieldCodes: lvFields.items.map((item) => item.value.trim()) };
|
|
77
|
+
},
|
|
78
|
+
async read(session, intent, draft) {
|
|
79
|
+
if (!draft.present || !intent.listview)
|
|
80
|
+
return;
|
|
81
|
+
try {
|
|
82
|
+
const result = await wireCall(session, { path: ENDPOINTS.listQueryGet, query: { schemaCode: intent.code, code: intent.listview.code, clientType: "PC" } });
|
|
83
|
+
const view = result.data;
|
|
84
|
+
if (!view || !view.id)
|
|
85
|
+
return;
|
|
86
|
+
const columns = Array.isArray(view.queryColumns)
|
|
87
|
+
? view.queryColumns.map((column) => String(column.propertyCode ?? "")).filter(Boolean)
|
|
88
|
+
: [];
|
|
89
|
+
draft.listview = { present: true, columns };
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
if (error instanceof WireError && error.errcode === 302025)
|
|
93
|
+
return;
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
plan(intent, baseline, emit) {
|
|
98
|
+
const listview = intent.listview;
|
|
99
|
+
if (!listview)
|
|
100
|
+
return;
|
|
101
|
+
const resource = `listview:${listview.code}`;
|
|
102
|
+
if (listview.absent === true) {
|
|
103
|
+
if (baseline.listview.present)
|
|
104
|
+
emit.change("listview.delete", { schemaCode: intent.code, code: listview.code }, resource, "listview-declared-absent");
|
|
105
|
+
else
|
|
106
|
+
emit.skip(resource, "listview-already-absent");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const fieldCodes = listview.fieldCodes ?? [];
|
|
110
|
+
const declaredCodes = new Set(intent.fields.map((field) => field.code));
|
|
111
|
+
// A declared column may be a business field OR an explicit SYSTEM field
|
|
112
|
+
// (createdTime/creater/sequenceNo/…) — the user opts into a system column by
|
|
113
|
+
// naming it; otherwise system columns are dropped (see the configure below).
|
|
114
|
+
const invalidListviewCodes = fieldCodes.filter((code) => !declaredCodes.has(code) && !SYSTEM_FIELD_CODES.has(code));
|
|
115
|
+
if (invalidListviewCodes.length > 0)
|
|
116
|
+
refuse("intent-listview-fieldcodes-unknown", `列表视图 ${listview.code} 声明了模型字段之外的列:${invalidListviewCodes.join("、")}`);
|
|
117
|
+
if (!baseline.listview.present)
|
|
118
|
+
emit.change("listview.create", { schemaCode: intent.code, name: listview.name, code: listview.code }, resource, "listview-absent");
|
|
119
|
+
// The platform's listview.create seeds system columns (name/creater/
|
|
120
|
+
// createdTime/sequenceNo), a default `createdTime` sort, AND a default
|
|
121
|
+
// `createdTime` filter; a plain incremental configure keeps them. The
|
|
122
|
+
// intent's `fieldCodes` IS the desired view, so configure authoritatively:
|
|
123
|
+
// replaceColumns + hideDefaultSystemFields drop the seeded system columns
|
|
124
|
+
// (unless the caller named one), and `conditions:[]`/`sorts:[]` clear the
|
|
125
|
+
// seeded system filter and sort — the user wants business fields, not the
|
|
126
|
+
// platform's defaults (2026-09-28).
|
|
127
|
+
if (!baseline.listview.present || fieldCodes.some((code) => !baseline.listview.columns.includes(code)) || baseline.listview.columns.length !== fieldCodes.length)
|
|
128
|
+
emit.change("listview.configure", { schemaCode: intent.code, code: listview.code, fieldCodes, replaceColumns: true, hideDefaultSystemFields: true, conditions: [], sorts: [] }, resource, "listview-columns-authoritative");
|
|
129
|
+
},
|
|
130
|
+
decode(baseline, draft) {
|
|
131
|
+
draft.frontend.listviewPresent = baseline.listview.present;
|
|
132
|
+
draft.frontend.listviewColumns = baseline.listview.columns;
|
|
133
|
+
},
|
|
134
|
+
compare(intent, live, emit) {
|
|
135
|
+
const listview = intent.listview;
|
|
136
|
+
if (!listview || !live.present)
|
|
137
|
+
return;
|
|
138
|
+
if (listview.absent === true) {
|
|
139
|
+
if (live.frontend.listviewPresent)
|
|
140
|
+
emit.difference("listview-present-unexpected", `listview:${listview.code}`, `列表视图 ${listview.code} 声明 absent:true 但仍存在`);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (!live.frontend.listviewPresent)
|
|
144
|
+
emit.difference("listview-missing", `listview:${listview.code}`, `列表视图 ${listview.code} 不存在`);
|
|
145
|
+
else if ((listview.fieldCodes ?? []).some((code) => !live.frontend.listviewColumns.includes(code)))
|
|
146
|
+
emit.difference("listview-columns-missing", `listview:${listview.code}`, `列表视图 ${listview.code} 缺少声明列`);
|
|
147
|
+
else {
|
|
148
|
+
// The intent's fieldCodes IS the view; an unexpected SYSTEM column (not
|
|
149
|
+
// declared) means the platform's seeded defaults were never cleared.
|
|
150
|
+
const declared = new Set(listview.fieldCodes ?? []);
|
|
151
|
+
const unexpectedSystem = live.frontend.listviewColumns.filter((code) => SYSTEM_FIELD_CODES.has(code) && !declared.has(code));
|
|
152
|
+
if (unexpectedSystem.length > 0)
|
|
153
|
+
emit.difference("listview-system-columns-unexpected", `listview:${listview.code}`, `列表视图 ${listview.code} 含未声明的系统默认列(应改为业务字段或显式声明):${unexpectedSystem.join("、")}`);
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
//# sourceMappingURL=listview.js.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { validateBusinessCode } from "../../code-rules.js";
|
|
2
|
+
import { isScalarString, pushError } from "../validation.js";
|
|
3
|
+
/** Model keys the capability owns on a model node. */
|
|
4
|
+
const MODEL_KEYS = new Set(["code", "name", "absent", "fields", "form", "listview", "rules", "datarules", "workflow", "onlinejs"]);
|
|
5
|
+
/** Keys that describe a live model and are meaningless on a declared-absent one. */
|
|
6
|
+
const PRESENT_ONLY_KEYS = ["name", "fields", "form", "listview", "rules", "datarules", "workflow", "onlinejs"];
|
|
7
|
+
/**
|
|
8
|
+
* The model resource: identity (code/name) and the model-level lifecycle
|
|
9
|
+
* (model.create on absence, model.update on name drift, model.delete when the
|
|
10
|
+
* intent declares the model `absent:true`). It is the root of the capability
|
|
11
|
+
* graph — everything else requires the model to exist.
|
|
12
|
+
*/
|
|
13
|
+
export const modelCapability = {
|
|
14
|
+
id: "model",
|
|
15
|
+
title: "业务模型",
|
|
16
|
+
requires: [],
|
|
17
|
+
operations: ["create", "update", "delete"],
|
|
18
|
+
validate(node, draft, ctx) {
|
|
19
|
+
for (const entry of node.entries) {
|
|
20
|
+
if (!MODEL_KEYS.has(entry.key))
|
|
21
|
+
pushError(ctx, "intent-key-unknown", `模型不支持键 "${entry.key}"(只接受 code / name / absent / fields / form / listview / rules / datarules / workflow / onlinejs)`, `${ctx.base}.${entry.key}`);
|
|
22
|
+
}
|
|
23
|
+
const codeNode = node.entries.find((entry) => entry.key === "code")?.value;
|
|
24
|
+
const nameNode = node.entries.find((entry) => entry.key === "name")?.value;
|
|
25
|
+
const absentNode = node.entries.find((entry) => entry.key === "absent")?.value;
|
|
26
|
+
if (!isScalarString(codeNode))
|
|
27
|
+
pushError(ctx, "intent-model-code-invalid", "模型 code 必须是非空字符串", `${ctx.base}.code`, "value");
|
|
28
|
+
const code = isScalarString(codeNode) ? codeNode.value.trim() : "";
|
|
29
|
+
let codeValid = code.length > 0;
|
|
30
|
+
if (code.length > 0) {
|
|
31
|
+
const violations = validateBusinessCode(code, { kind: "model" });
|
|
32
|
+
codeValid = violations.length === 0;
|
|
33
|
+
if (violations.length > 0)
|
|
34
|
+
pushError(ctx, "intent-model-code-invalid", `模型 code 未过出门前校验(长度/风格/保留字):${violations.map((violation) => violation.message).join(";")}`, `${ctx.base}.code`, "value", { validation: violations });
|
|
35
|
+
if (ctx.seenModelCodes.has(code))
|
|
36
|
+
pushError(ctx, "intent-model-code-duplicate", `模型 code 重复:${code}`, `${ctx.base}.code`, "value");
|
|
37
|
+
ctx.seenModelCodes.add(code);
|
|
38
|
+
}
|
|
39
|
+
if (absentNode !== undefined && (absentNode.kind !== "scalar" || typeof absentNode.value !== "boolean"))
|
|
40
|
+
pushError(ctx, "intent-model-absent-invalid", "模型 absent 必须是布尔", `${ctx.base}.absent`, "value");
|
|
41
|
+
const absent = absentNode?.kind === "scalar" && absentNode.value === true;
|
|
42
|
+
if (absent) {
|
|
43
|
+
// A declared-absent model is a deletion target: nothing about its shape is
|
|
44
|
+
// meaningful, so refuse shape declarations rather than silently ignoring
|
|
45
|
+
// them (a half-ignored field list would mislead the plan reader).
|
|
46
|
+
for (const key of PRESENT_ONLY_KEYS) {
|
|
47
|
+
if (node.entries.some((entry) => entry.key === key))
|
|
48
|
+
pushError(ctx, "intent-model-absent-with-shape", `模型声明 absent:true 表示删除该模型,不能再声明 ${key}(模型及其字段/表单/规则随删除一并消失)`, `${ctx.base}.${key}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else if (!isScalarString(nameNode)) {
|
|
52
|
+
pushError(ctx, "intent-model-name-invalid", "模型 name 必须是非空字符串", `${ctx.base}.name`, "value");
|
|
53
|
+
}
|
|
54
|
+
draft.code = code;
|
|
55
|
+
draft.name = isScalarString(nameNode) ? nameNode.value : "";
|
|
56
|
+
if (absent)
|
|
57
|
+
draft.absent = true;
|
|
58
|
+
// An invalid identity fails the whole file anyway (errors throw); marking it
|
|
59
|
+
// here keeps "model omitted" consistent with "model has no valid identity".
|
|
60
|
+
draft.identityValid = codeValid && (absent || isScalarString(nameNode));
|
|
61
|
+
},
|
|
62
|
+
async read() {
|
|
63
|
+
// The model slot's identity (present/name) is resolved by the baseline
|
|
64
|
+
// assembler's tree scan, which also yields the ownership note.
|
|
65
|
+
},
|
|
66
|
+
plan(intent, baseline, emit) {
|
|
67
|
+
if (intent.absent === true) {
|
|
68
|
+
if (baseline.present)
|
|
69
|
+
emit.change("model.delete", { schemaCode: intent.code }, `model:${intent.code}`, "model-declared-absent");
|
|
70
|
+
else
|
|
71
|
+
emit.skip(`model:${intent.code}`, "model-already-absent");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (!baseline.present) {
|
|
75
|
+
emit.change("model.create", { name: intent.name, code: intent.code }, `model:${intent.code}`, "model-absent");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
emit.skip(`model:${intent.code}`, "model-present");
|
|
79
|
+
if (baseline.name !== null && baseline.name !== intent.name)
|
|
80
|
+
emit.change("model.update", { schemaCode: intent.code, name: intent.name }, `model:${intent.code}`, "model-name-drift");
|
|
81
|
+
},
|
|
82
|
+
decode() {
|
|
83
|
+
// Identity (code/name/present) is carried straight from the baseline by the
|
|
84
|
+
// readback assembler; the model capability contributes no decode slice.
|
|
85
|
+
},
|
|
86
|
+
compare(intent, live, emit) {
|
|
87
|
+
if (intent.absent === true) {
|
|
88
|
+
if (live.present)
|
|
89
|
+
emit.difference("model-present-unexpected", `model:${intent.code}`, `模型 ${intent.code} 声明 absent:true 但仍存在`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (!live.present) {
|
|
93
|
+
emit.difference("model-missing", `model:${intent.code}`, `模型 ${intent.code} 不存在`);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (live.name !== null && live.name !== intent.name)
|
|
97
|
+
emit.difference("model-name-mismatch", `model:${intent.code}`, `模型名 live=${live.name} intent=${intent.name}`);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { ENDPOINTS } from "../../endpoints.js";
|
|
2
|
+
import { wireCall, WireError } from "../../wire.js";
|
|
3
|
+
import { isScalarString, pushError } from "../validation.js";
|
|
4
|
+
const ONLINEJS_KEYS = new Set(["sheetCode", "source", "replace", "publish"]);
|
|
5
|
+
function extractCustomScript(raw) {
|
|
6
|
+
if (typeof raw !== "string" || raw.trim() === "")
|
|
7
|
+
return "";
|
|
8
|
+
try {
|
|
9
|
+
const elements = JSON.parse(raw);
|
|
10
|
+
if (!Array.isArray(elements))
|
|
11
|
+
return "";
|
|
12
|
+
const script = elements.find((element) => element && typeof element === "object" && element.id === "customScript");
|
|
13
|
+
return typeof script?.innerHTML === "string" ? script.innerHTML : "";
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export const onlinejsCapability = {
|
|
20
|
+
id: "onlinejs",
|
|
21
|
+
title: "表单在线 JS",
|
|
22
|
+
requires: ["form"],
|
|
23
|
+
operations: ["compose"],
|
|
24
|
+
validate(node, draft, ctx) {
|
|
25
|
+
const jsNode = node.entries.find((entry) => entry.key === "onlinejs")?.value;
|
|
26
|
+
if (jsNode === undefined || (jsNode.kind === "scalar" && jsNode.value === null))
|
|
27
|
+
return;
|
|
28
|
+
if (jsNode.kind !== "map") {
|
|
29
|
+
pushError(ctx, "intent-onlinejs-not-a-map", "onlinejs 必须是映射", `${ctx.base}.onlinejs`, "value");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
for (const entry of jsNode.entries)
|
|
33
|
+
if (!ONLINEJS_KEYS.has(entry.key))
|
|
34
|
+
pushError(ctx, "intent-key-unknown", `onlinejs 不支持键 "${entry.key}"(只接受 sheetCode / source / replace / publish)`, `${ctx.base}.onlinejs.${entry.key}`);
|
|
35
|
+
const sheetNode = jsNode.entries.find((entry) => entry.key === "sheetCode")?.value;
|
|
36
|
+
const sourceNode = jsNode.entries.find((entry) => entry.key === "source")?.value;
|
|
37
|
+
const replaceNode = jsNode.entries.find((entry) => entry.key === "replace")?.value;
|
|
38
|
+
const publishNode = jsNode.entries.find((entry) => entry.key === "publish")?.value;
|
|
39
|
+
if (sheetNode !== undefined && (!isScalarString(sheetNode) || sheetNode.value.trim().length === 0))
|
|
40
|
+
pushError(ctx, "intent-onlinejs-sheet-invalid", "onlinejs.sheetCode 必须是非空字符串", `${ctx.base}.onlinejs.sheetCode`, "value");
|
|
41
|
+
if (!isScalarString(sourceNode) || sourceNode.value.trim().length === 0)
|
|
42
|
+
pushError(ctx, "intent-onlinejs-source-invalid", "onlinejs.source 必须是非空字符串", `${ctx.base}.onlinejs.source`, "value");
|
|
43
|
+
if (replaceNode !== undefined && (replaceNode.kind !== "scalar" || typeof replaceNode.value !== "boolean"))
|
|
44
|
+
pushError(ctx, "intent-onlinejs-replace-invalid", "onlinejs.replace 必须是布尔", `${ctx.base}.onlinejs.replace`, "value");
|
|
45
|
+
if (publishNode === undefined || publishNode.kind !== "scalar" || typeof publishNode.value !== "boolean")
|
|
46
|
+
pushError(ctx, "intent-onlinejs-publish-invalid", "onlinejs.publish 必须是布尔", `${ctx.base}.onlinejs.publish`, "value");
|
|
47
|
+
if (isScalarString(sourceNode) && publishNode?.kind === "scalar" && typeof publishNode.value === "boolean") {
|
|
48
|
+
draft.onlinejs = {
|
|
49
|
+
...(isScalarString(sheetNode) ? { sheetCode: sheetNode.value.trim() } : {}),
|
|
50
|
+
source: sourceNode.value.trim(),
|
|
51
|
+
...(replaceNode?.kind === "scalar" && typeof replaceNode.value === "boolean" ? { replace: replaceNode.value } : {}),
|
|
52
|
+
publish: publishNode.value,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
async read(session, intent, draft) {
|
|
57
|
+
const onlinejs = intent.onlinejs;
|
|
58
|
+
if (!onlinejs)
|
|
59
|
+
return;
|
|
60
|
+
const sheetCode = onlinejs.sheetCode ?? intent.code;
|
|
61
|
+
let result = null;
|
|
62
|
+
try {
|
|
63
|
+
result = (await wireCall(session, { path: ENDPOINTS.sheetGet, query: { schemaCode: draft.code, sheetCode } })).data;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (!(error instanceof WireError && error.kind === "business" && (error.errcode === 302024 || error.errcode === 401001)))
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
const publishedSource = extractCustomScript(result?.publishedHtmlJson);
|
|
70
|
+
const draftSource = extractCustomScript(result?.draftHtmlJson);
|
|
71
|
+
draft.onlinejs = { present: result !== null, published: result?.published === true, source: onlinejs.publish ? publishedSource : draftSource };
|
|
72
|
+
},
|
|
73
|
+
plan(intent, baseline, emit) {
|
|
74
|
+
const onlinejs = intent.onlinejs;
|
|
75
|
+
if (!onlinejs)
|
|
76
|
+
return;
|
|
77
|
+
const sheetCode = onlinejs.sheetCode ?? intent.code;
|
|
78
|
+
const current = baseline.onlinejs;
|
|
79
|
+
const sourceSatisfied = current?.present === true && (onlinejs.replace === true ? current.source === onlinejs.source : current.source.includes(onlinejs.source));
|
|
80
|
+
const resource = `onlinejs:${intent.code}.${sheetCode}`;
|
|
81
|
+
if (!sourceSatisfied || (onlinejs.publish && current?.published !== true)) {
|
|
82
|
+
emit.change("onlinejs.compose", { schemaCode: intent.code, sheetCode, source: onlinejs.source, ...(onlinejs.replace !== undefined ? { replace: onlinejs.replace } : {}), publish: onlinejs.publish }, resource, current?.present === true ? "onlinejs-source-drift" : "onlinejs-absent");
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
emit.skip(resource, "onlinejs-present");
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
decode(baseline, draft) {
|
|
89
|
+
if (baseline.onlinejs)
|
|
90
|
+
draft.onlinejs = { present: baseline.onlinejs.present, published: baseline.onlinejs.published, source: baseline.onlinejs.source };
|
|
91
|
+
},
|
|
92
|
+
compare(intent, live, emit) {
|
|
93
|
+
const onlinejs = intent.onlinejs;
|
|
94
|
+
if (!onlinejs)
|
|
95
|
+
return;
|
|
96
|
+
const current = live.onlinejs;
|
|
97
|
+
const resource = `onlinejs:${intent.code}.${onlinejs.sheetCode ?? intent.code}`;
|
|
98
|
+
if (!current?.present) {
|
|
99
|
+
emit.difference("onlinejs-missing", resource, `表单 ${intent.code} 的在线 JS 不存在`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (onlinejs.publish && !current.published)
|
|
103
|
+
emit.difference("onlinejs-not-published", resource, `表单 ${intent.code} 的在线 JS 未发布`);
|
|
104
|
+
if (onlinejs.replace === true ? current.source !== onlinejs.source : !current.source.includes(onlinejs.source))
|
|
105
|
+
emit.difference("onlinejs-source-mismatch", resource, `表单 ${intent.code} 的 customScript 不包含声明代码`);
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=onlinejs.js.map
|