@shwfed/config 3.0.4 → 3.0.7

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.
Files changed (87) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/actions/buttons/2026-04-21/com.shwfed.actions.button.navigation/runtime.vue +4 -10
  3. package/dist/runtime/components/actions/buttons/2026-05-21/com.shwfed.actions.button.http.download/schema.d.ts +1 -1
  4. package/dist/runtime/components/actions/buttons/2026-05-21/com.shwfed.actions.button.http.download/schema.js +2 -2
  5. package/dist/runtime/components/actions/buttons/2026-07-13/com.shwfed.actions.button.state.write/runtime.vue +1 -1
  6. package/dist/runtime/components/actions/buttons/2026-07-13/com.shwfed.actions.button.state.write/schema.d.ts +4 -2
  7. package/dist/runtime/components/actions/buttons/2026-07-13/com.shwfed.actions.button.state.write/schema.js +1 -1
  8. package/dist/runtime/components/actions/config.vue +109 -3
  9. package/dist/runtime/components/actions/utils/steps.d.ts +1 -1
  10. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.d.vue.ts +74 -74
  11. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.vue.d.ts +74 -74
  12. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.d.vue.ts +74 -74
  13. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.vue.d.ts +74 -74
  14. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/schema.d.ts +37 -37
  15. package/dist/runtime/components/config/history-picker.vue +6 -4
  16. package/dist/runtime/components/config/index.vue +25 -0
  17. package/dist/runtime/components/config/prune-dialog.d.vue.ts +14 -0
  18. package/dist/runtime/components/config/prune-dialog.vue +83 -0
  19. package/dist/runtime/components/config/prune-dialog.vue.d.ts +14 -0
  20. package/dist/runtime/components/config/schema.js +2 -2
  21. package/dist/runtime/components/config/use-editor.d.ts +9 -0
  22. package/dist/runtime/components/config/use-editor.js +39 -11
  23. package/dist/runtime/components/config/utils/linkage.d.ts +8 -0
  24. package/dist/runtime/components/config/utils/linkage.js +7 -2
  25. package/dist/runtime/components/config/utils/prune.d.ts +68 -0
  26. package/dist/runtime/components/config/utils/prune.js +92 -0
  27. package/dist/runtime/components/form/fields/2026-04-22/com.shwfed.form.field.text/config.d.vue.ts +22 -22
  28. package/dist/runtime/components/form/fields/2026-04-22/com.shwfed.form.field.text/config.vue.d.ts +22 -22
  29. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/config.d.vue.ts +22 -22
  30. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/config.vue.d.ts +22 -22
  31. package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/config.d.vue.ts +31 -0
  32. package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/config.vue +191 -0
  33. package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/config.vue.d.ts +31 -0
  34. package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/expand.d.ts +49 -0
  35. package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/expand.js +87 -0
  36. package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/runtime.d.vue.ts +8 -0
  37. package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/runtime.vue +72 -0
  38. package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/runtime.vue.d.ts +8 -0
  39. package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/schema.d.ts +59 -0
  40. package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/schema.js +139 -0
  41. package/dist/runtime/components/form/unit-config.vue +2 -2
  42. package/dist/runtime/components/form/utils/form-vars.d.ts +5 -15
  43. package/dist/runtime/components/form/utils/form-vars.js +12 -6
  44. package/dist/runtime/components/operations/2026-06-11/com.shwfed.operation.http.request.json/schema.js +2 -2
  45. package/dist/runtime/components/operations/2026-06-15/com.shwfed.operation.http.download/schema.js +2 -2
  46. package/dist/runtime/components/operations/2026-06-15/com.shwfed.operation.navigation/runtime.js +5 -11
  47. package/dist/runtime/components/operations/2026-06-15/com.shwfed.operation.navigation/schema.js +1 -1
  48. package/dist/runtime/components/operations/utils/resolve.d.ts +1 -1
  49. package/dist/runtime/components/operations/utils/step-vars.d.ts +4 -2
  50. package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.select/runtime.vue +1 -21
  51. package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.select/schema.d.ts +2 -0
  52. package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.select/schema.js +16 -1
  53. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +2 -2
  54. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +2 -2
  55. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +2 -2
  56. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +2 -2
  57. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +2 -2
  58. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +2 -2
  59. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +2 -2
  60. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +2 -2
  61. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +2 -2
  62. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +2 -2
  63. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +2 -2
  64. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +2 -2
  65. package/dist/runtime/components/table/config.vue +2 -2
  66. package/dist/runtime/components/table/index.vue +15 -0
  67. package/dist/runtime/components/table/schema.d.ts +74 -74
  68. package/dist/runtime/components/table/utils/resolve.d.ts +26 -0
  69. package/dist/runtime/components/table/utils/resolve.js +2 -1
  70. package/dist/runtime/components/ui/expression-editor/picker-entries.js +1 -1
  71. package/dist/runtime/components/ui/expression-editor/step-chip-extension.js +1 -1
  72. package/dist/runtime/components/ui/expression-editor/step-refs.js +1 -1
  73. package/dist/runtime/share/clipboard.d.ts +15 -9
  74. package/dist/runtime/share/clipboard.js +22 -10
  75. package/dist/runtime/share/event-bus.js +2 -2
  76. package/dist/runtime/share/navigation.d.ts +1 -0
  77. package/dist/runtime/share/navigation.js +4 -0
  78. package/dist/runtime/utils/cel-context.d.ts +2 -2
  79. package/dist/runtime/vendor/cel-js/CLAUDE.md +7 -2
  80. package/dist/runtime/vendor/cel-js/PROMPT.md +27 -0
  81. package/dist/runtime/vendor/cel-js/lib/evaluator.js +2 -0
  82. package/dist/runtime/vendor/cel-js/lib/globals.d.ts +2 -0
  83. package/dist/runtime/vendor/cel-js/lib/globals.js +10 -0
  84. package/dist/runtime/vendor/cel-js/lib/map-builtins.d.ts +2 -0
  85. package/dist/runtime/vendor/cel-js/lib/map-builtins.js +80 -0
  86. package/dist/runtime/vendor/cel-js/lib/operators.js +2 -12
  87. package/package.json +1 -1
@@ -0,0 +1,8 @@
1
+ import type { Value } from './schema.js';
2
+ type __VLS_Props = {
3
+ fieldId: string;
4
+ config: Value;
5
+ };
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,72 @@
1
+ <script setup>
2
+ import { Effect, Option } from "effect";
3
+ import { Fetch } from "fx-fetch";
4
+ import { onMounted, shallowRef } from "vue";
5
+ import { cel as _rawCel } from "../../../../../utils/cel";
6
+ import { celBindings, injectCELContext } from "../../../../../utils/cel-context";
7
+ import { fetchJsonOption } from "../../../../../share/request";
8
+ import FormUnitRenderer from "../../../FormUnitRenderer.vue";
9
+ import { findField } from "../../../utils/resolve";
10
+ import { useDerivedQuiescence } from "../../../utils/derived";
11
+ import { useFormState } from "../../../utils/state";
12
+ import { emptyUnit, expandDynFields } from "./expand";
13
+ defineOptions({ name: "ShwfedDynamicFieldRuntime" });
14
+ const props = defineProps({
15
+ fieldId: { type: String, required: true },
16
+ config: { type: null, required: true }
17
+ });
18
+ const inherited = injectCELContext();
19
+ const $cel = (expression, context) => _rawCel(expression, { ...celBindings(inherited), ...context });
20
+ const formState = useFormState();
21
+ const quiescence = useDerivedQuiescence();
22
+ const unit = shallowRef(emptyUnit(props.config));
23
+ const defaultsOf = (type, compatibilityDate) => findField(type, compatibilityDate)?.defaults?.();
24
+ async function loadDescriptors() {
25
+ quiescence.begin();
26
+ try {
27
+ const transform = props.config.transform;
28
+ if (!transform || transform.trim().length === 0) return;
29
+ const program = Effect.gen(function* () {
30
+ let json = Option.none();
31
+ const request = props.config.request;
32
+ if (request && request.trim().length > 0) {
33
+ json = yield* fetchJsonOption(yield* $cel(request));
34
+ }
35
+ return yield* $cel(transform, { json });
36
+ });
37
+ let list;
38
+ try {
39
+ list = await Effect.runPromise(Effect.provide(program, Fetch.layer));
40
+ } catch (err) {
41
+ console.warn(`[shwfed-form] dynamic fields ${props.fieldId}: request/transform failed`, err);
42
+ unit.value = emptyUnit(props.config);
43
+ return;
44
+ }
45
+ const result = expandDynFields(list, props.config, defaultsOf);
46
+ for (const { binding, value } of result.seeds) {
47
+ if (formState.getAt(binding) === void 0) formState.setAtSilent(binding, value);
48
+ }
49
+ unit.value = { fields: result.fields, layouts: result.layouts };
50
+ } finally {
51
+ quiescence.end();
52
+ }
53
+ }
54
+ onMounted(() => {
55
+ void loadDescriptors();
56
+ });
57
+ function evaluateMedia(expression) {
58
+ return Effect.runSync($cel(expression));
59
+ }
60
+ </script>
61
+
62
+ <template>
63
+ <div
64
+ data-slot="dynamic-fields"
65
+ class="min-w-0"
66
+ >
67
+ <FormUnitRenderer
68
+ :unit="unit"
69
+ :evaluate-media="evaluateMedia"
70
+ />
71
+ </div>
72
+ </template>
@@ -0,0 +1,8 @@
1
+ import type { Value } from './schema.js';
2
+ type __VLS_Props = {
3
+ fieldId: string;
4
+ config: Value;
5
+ };
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,59 @@
1
+ import { Schema } from 'effect';
2
+ import type { Environment } from '../../../../../vendor/cel-js/lib/index.js';
3
+ export declare const type: "com.shwfed.form.field.dynamic";
4
+ export declare const compatibilityDate: "2026-07-13";
5
+ export declare const metadata: {
6
+ readonly name: "动态字段";
7
+ readonly icon: "fluent:form-multiple-20-regular";
8
+ readonly w: {
9
+ readonly initial: 12;
10
+ readonly min: 4;
11
+ readonly max: number;
12
+ };
13
+ readonly h: {
14
+ readonly initial: 4;
15
+ readonly min: 2;
16
+ readonly max: number;
17
+ readonly grow: true;
18
+ };
19
+ };
20
+ declare const DynFieldKind: Schema.Literal<["text", "number", "date", "daterange", "dropdown"]>;
21
+ export type DynFieldKindValue = Schema.Schema.Type<typeof DynFieldKind>;
22
+ export declare const KIND_TO_FIELD: Record<DynFieldKindValue, {
23
+ type: string;
24
+ compatibilityDate: string;
25
+ }>;
26
+ export declare const CHOICE_KINDS: ReadonlySet<DynFieldKindValue>;
27
+ /**
28
+ * The narrow, module-owned DTO the `transform` must emit per field. Wider than
29
+ * the column DTO because these are *edit* definitions — they carry requiredness,
30
+ * options, and default values. Each list element is decoded against this at
31
+ * runtime (partial-drop, loud); the strongest static guarantee is only that
32
+ * `transform` returns `list<dyn>`.
33
+ */
34
+ export declare const DynFieldDescriptor: Schema.Struct<{
35
+ binding: Schema.filter<typeof Schema.String>;
36
+ label: typeof Schema.String;
37
+ kind: Schema.Literal<["text", "number", "date", "daterange", "dropdown"]>;
38
+ required: Schema.optional<typeof Schema.Boolean>;
39
+ options: Schema.optional<Schema.Array$<Schema.Struct<{
40
+ value: Schema.Union<[typeof Schema.String, typeof Schema.Number]>;
41
+ label: typeof Schema.String;
42
+ }>>>;
43
+ default: Schema.optional<typeof Schema.Unknown>;
44
+ }>;
45
+ export type DynFieldDescriptorValue = Schema.Schema.Type<typeof DynFieldDescriptor>;
46
+ export declare function schema(configure: (env: Environment) => void): Schema.Struct<{
47
+ type: Schema.Literal<["com.shwfed.form.field.dynamic"]>;
48
+ compatibilityDate: Schema.Literal<["2026-07-13"]>;
49
+ id: Schema.refine<string, typeof Schema.String>;
50
+ displayName: Schema.optional<Schema.SchemaClass<string, string, never>>;
51
+ hidden: Schema.optional<Schema.Schema<string, string, never>>;
52
+ request: Schema.Schema<string, string, never>;
53
+ transform: Schema.Schema<string, string, never>;
54
+ columns: Schema.optional<Schema.refine<number, Schema.filter<typeof Schema.Number>>>;
55
+ orientation: Schema.optional<Schema.Literal<["vertical", "floating"]>>;
56
+ }>;
57
+ export type Value = Schema.Schema.Type<ReturnType<typeof schema>>;
58
+ export declare function defaults(): Partial<Value>;
59
+ export {};
@@ -0,0 +1,139 @@
1
+ import { Schema } from "effect";
2
+ import { Expression, HttpRequestResult } from "../../../../../share/expression.js";
3
+ import { md } from "../../../../../share/markdown.js";
4
+ import { FieldOrientationSchema } from "../../../utils/common.js";
5
+ import { FORM_VARIABLE_NAMES } from "../../../utils/form-vars.js";
6
+ export const type = "com.shwfed.form.field.dynamic";
7
+ export const compatibilityDate = "2026-07-13";
8
+ export const metadata = {
9
+ name: "\u52A8\u6001\u5B57\u6BB5",
10
+ icon: "fluent:form-multiple-20-regular",
11
+ w: { initial: 12, min: 4, max: Infinity },
12
+ h: { initial: 4, min: 2, max: Infinity, grow: true }
13
+ // Hosts a runtime-generated sub-form; its config (two expressions + two
14
+ // inputs) is small enough for the docked inline pane, so leave `inlineConfig`
15
+ // at its default (supported).
16
+ };
17
+ const DynFieldKind = Schema.Literal(
18
+ "text",
19
+ "number",
20
+ "date",
21
+ "daterange",
22
+ "dropdown"
23
+ );
24
+ export const KIND_TO_FIELD = {
25
+ text: { type: "com.shwfed.form.field.text", compatibilityDate: "2026-04-22" },
26
+ number: { type: "com.shwfed.form.field.number", compatibilityDate: "2026-04-28" },
27
+ date: { type: "com.shwfed.form.field.date", compatibilityDate: "2026-04-27" },
28
+ daterange: { type: "com.shwfed.form.field.daterange", compatibilityDate: "2026-04-27" },
29
+ dropdown: { type: "com.shwfed.form.field.combobox.single", compatibilityDate: "2026-06-14" }
30
+ };
31
+ export const CHOICE_KINDS = /* @__PURE__ */ new Set(["dropdown"]);
32
+ const DynFieldOption = Schema.Struct({
33
+ // The value bound when this option is chosen. A number maps to the leaves'
34
+ // `{ kind: 'number', value }` item shape, a string to `{ kind: 'text', value }`.
35
+ value: Schema.Union(Schema.String, Schema.Number),
36
+ // Plain string, no i18n (confirmed requirement); `expand` wraps it into a zh
37
+ // `Locale` via the dyn-cols `wrapLocale` idiom.
38
+ label: Schema.String
39
+ });
40
+ export const DynFieldDescriptor = Schema.Struct({
41
+ // dot-prop path like any field's binding; for the legacy app = `parameter_name`.
42
+ // Child `id` is namespaced `<blockId>::<binding>`, so it can never collide
43
+ // with a static field or another block in the form-wide validation /
44
+ // selections registries. First-wins on duplicate bindings within one block.
45
+ binding: Schema.String.pipe(Schema.minLength(1)),
46
+ // Plain string, no i18n (confirmed requirement); wrapped into a zh `Locale`.
47
+ label: Schema.String,
48
+ // Selects the pinned leaf (closed vocabulary).
49
+ kind: DynFieldKind,
50
+ // `true` → the child gets `required: 'true'` (the CEL literal), which is both
51
+ // the required star and the non-empty gate.
52
+ required: Schema.optional(Schema.Boolean),
53
+ // Choice kinds' static options. A choice-kind descriptor without `options` is
54
+ // dropped (loud). Legacy idiom:
55
+ // `f.optional_values.split('\n').map(v, {'value': v, 'label': v})`.
56
+ options: Schema.optional(Schema.Array(DynFieldOption)),
57
+ // Initial-value seed: written into form state at `binding` iff that path
58
+ // currently resolves to `undefined` (never clobbering the form-level `initial`
59
+ // seed or user input).
60
+ default: Schema.optional(Schema.Unknown)
61
+ });
62
+ function withoutFormVariables(configure) {
63
+ return (env) => {
64
+ const proxy = new Proxy(env, {
65
+ get(target, prop) {
66
+ if (prop === "registerVariable") {
67
+ return (name, ...rest) => {
68
+ if (FORM_VARIABLE_NAMES.has(name)) return;
69
+ return target.registerVariable(name, ...rest);
70
+ };
71
+ }
72
+ const v = Reflect.get(target, prop, target);
73
+ return typeof v === "function" ? v.bind(target) : v;
74
+ }
75
+ });
76
+ configure(proxy);
77
+ };
78
+ }
79
+ export function schema(configure) {
80
+ const frozenConfigure = withoutFormVariables(configure);
81
+ const CelRequest = Expression({ configure: frozenConfigure, resultType: HttpRequestResult });
82
+ const CelTransform = Expression({
83
+ configure: (env) => {
84
+ frozenConfigure(env);
85
+ const declared = new Set(env.getDefinitions().variables.map((v) => v.name));
86
+ if (!declared.has("json")) {
87
+ env.registerVariable("json", "optional<dyn>", {
88
+ label: "HTTP \u54CD\u5E94\u4F53",
89
+ description: "\u5B57\u6BB5\u5B9A\u4E49\u63A5\u53E3\u7684\u54CD\u5E94\u4F53\uFF08\u5DF2\u89E3\u6790 JSON\uFF09\uFF1B\u672A\u83B7\u53D6\u5230\u65F6\u4E3A none\uFF0C\u901A\u8FC7 `json.orValue(...)` \u89E3\u5305"
90
+ });
91
+ }
92
+ },
93
+ resultType: (t) => t.startsWith("list") || t === "dyn" || t.startsWith("optional")
94
+ });
95
+ const CelBool = Expression({ configure, resultType: "bool" });
96
+ return Schema.Struct({
97
+ type: Schema.Literal(type),
98
+ compatibilityDate: Schema.Literal(compatibilityDate),
99
+ // Design-time identity; placements reference it. The block carries NO
100
+ // `binding` / `required` / `validations` of its own — it has no value.
101
+ id: Schema.UUID.annotations({ description: "\u5B57\u6BB5\u552F\u4E00\u6807\u8BC6\uFF1B\u5E03\u5C40\u901A\u8FC7\u8BE5 id \u5F15\u7528\u8BE5\u52A8\u6001\u533A\u57DF" }),
102
+ displayName: Schema.optional(Schema.String.annotations({
103
+ title: "\u5185\u90E8\u540D\u79F0",
104
+ description: "\u4EC5\u5728\u7F16\u8F91\u5668\u5185\u53EF\u89C1\u7684\u540D\u79F0\uFF0C\u7528\u4E8E\u5728\u4FA7\u8FB9\u680F\u548C\u5E03\u5C40\u7F16\u8F91\u5668\u4E2D\u8BC6\u522B\u8BE5\u52A8\u6001\u533A\u57DF\uFF1B\u8FD0\u884C\u65F6\u4E0D\u5C55\u793A"
105
+ })),
106
+ hidden: Schema.optional(CelBool.annotations({
107
+ title: "\u9690\u85CF\u6761\u4EF6",
108
+ description: "\u8FD4\u56DE `true` \u65F6\u6574\u4E2A\u52A8\u6001\u533A\u57DF\u5728\u6240\u6709\u5E03\u5C40\u4E2D\u90FD\u4E0D\u6E32\u67D3\uFF08\u5E76\u89E6\u53D1\u8F68\u9053\u538B\u7F29\u6536\u8D77\u8BE5\u533A\u57DF\uFF09"
109
+ })),
110
+ request: CelRequest.annotations({
111
+ title: "\u5B57\u6BB5\u5B9A\u4E49\u8BF7\u6C42",
112
+ description: md`
113
+ 获取字段定义列表的 HTTP 请求表达式(与数据源 \`request\` 同为 \`HttpRequest\` 形状)。
114
+ 字段集合在挂载时**一次性**获取,**不随表单状态变化** —— 作用域内没有 \`form\` 等表单运行时变量。
115
+ `
116
+ }),
117
+ transform: CelTransform.annotations({
118
+ title: "\u5B57\u6BB5\u5B9A\u4E49\u6620\u5C04",
119
+ description: md`
120
+ 将 \`json\`(字段定义响应体,用 \`json.orValue([])\` 解包)映射为一组字段描述(\`list\`)。
121
+ 每个元素需符合固定字段(注意 CEL 映射字面量的键要加引号):\`binding\`(写入路径 / 唯一标识)、
122
+ \`label\`(标签,纯文本无 i18n)、\`kind\`(\`text\`/\`number\`/\`date\`/\`daterange\`/\`dropdown\`);
123
+ 可选 \`required\`(布尔)、\`options\`(\`list<{value, label}>\`,\`dropdown\` 必填)、
124
+ \`default\`(初始值)。**所有与后端数据契约相关的知识都在这里**。
125
+ `
126
+ }),
127
+ columns: Schema.optional(Schema.Number.pipe(Schema.int(), Schema.positive()).annotations({
128
+ title: "\u5217\u6570",
129
+ description: "\u6BCF\u884C\u6392\u5E03\u7684\u5B50\u5B57\u6BB5\u6570\uFF1A\u6BCF\u4E2A\u5B50\u5B57\u6BB5\u6070\u597D\u5360\u4E00\u683C\uFF0C\u6309\u5B9A\u4E49\u987A\u5E8F\u884C\u4F18\u5148\u6392\u5E03\u3001\u6EE1\u884C\u6362\u884C\u3002\u9ED8\u8BA4 1\uFF08\u6BCF\u4E2A\u5B57\u6BB5\u5360\u6EE1\u6574\u884C\uFF09"
130
+ })),
131
+ orientation: Schema.optional(FieldOrientationSchema.annotations({
132
+ title: "\u65B9\u5411",
133
+ description: "\u7EDF\u4E00\u65BD\u52A0\u5230\u6BCF\u4E2A\u52A8\u6001\u5B50\u5B57\u6BB5\u7684\u6807\u7B7E\u6392\u5217\u65B9\u5411\uFF08`orientation` \u662F\u914D\u7F6E\u6570\u636E\uFF0C\u8FD0\u884C\u65F6\u6CA1\u6709\u65B9\u5411\u63D0\u4F9B\u8005\uFF09\uFF1B\u641C\u7D22\u8868\u5355\u573A\u666F\u901A\u5E38\u7528 `floating`"
134
+ }))
135
+ }).annotations({ title: "DynamicFields", description: "\u8FD0\u884C\u65F6\u52A8\u6001\u5B57\u6BB5\uFF08\u7531\u670D\u52A1\u7AEF\u5B57\u6BB5\u5B9A\u4E49\u751F\u6210\uFF09" });
136
+ }
137
+ export function defaults() {
138
+ return { request: "", transform: "" };
139
+ }
@@ -199,7 +199,7 @@ async function copyFields(ids) {
199
199
  if (p) placements[id] = p;
200
200
  }
201
201
  }
202
- if (!await writeClip("form", items, placements)) {
202
+ if (!await writeClip("form", { items, placements })) {
203
203
  toast.error("\u590D\u5236\u5931\u8D25");
204
204
  return;
205
205
  }
@@ -208,7 +208,7 @@ async function copyFields(ids) {
208
208
  async function pasteFields(target) {
209
209
  const payload = await readClip();
210
210
  if (!payload || payload.surface !== "form") return;
211
- const { items, placements } = reidFragment(payload.items, payload.placements);
211
+ const { items, placements } = reidFragment(payload);
212
212
  const pasted = items;
213
213
  if (pasted.length === 0) return;
214
214
  const place = placements;
@@ -1,19 +1,9 @@
1
1
  import type { Environment } from '../../../vendor/cel-js/lib/index.js';
2
2
  /**
3
- * Register the form-wide `now` / `form` live variables on `env`, skipping any
4
- * already declared. Idempotent so it composes safely from either side: a
5
- * `FormConfig` nested inside another (the `table` form field embeds a table
6
- * whose `query` is its own sub-form, and the embedding `configure` advertises
7
- * the same pair) would otherwise register `form` twice on one env and the CEL
8
- * registry throws `'form' is already registered`. Mirrors
9
- * `registerRowVariablesIfAbsent`.
10
- *
11
- * This is the single source of truth for the `form` / `now` metadata: the
12
- * schema's expression validators register it here, the designer-time picker
13
- * probes it (`buildFormCelScope`), and the runtime scope derives its
14
- * type/label/description from it too (`buildFormRuntimeScope`). Lives in this
15
- * dependency-light module — not `schema.ts` — so the scope helpers can import it
16
- * without pulling `utils/resolve` (and its `import.meta.glob`) into per-folder
17
- * unit tests. `schema.ts` re-exports it for existing call sites.
3
+ * The names the form registrar owns the form-runtime state a schema can
4
+ * subtract from an expression scope when its semantics forbid depending on the
5
+ * live form (the dynamic-fields block's `request` / `transform`: the field set
6
+ * is frozen at mount and must not read `form`).
18
7
  */
8
+ export declare const FORM_VARIABLE_NAMES: ReadonlySet<string>;
19
9
  export declare function registerFormVariablesIfAbsent(env: Environment): void;
@@ -1,10 +1,16 @@
1
1
  import { OPTIONS_VAR, SELECTIONS_VAR } from "../../../utils/cel-context.js";
2
+ const FORM_VARIABLE_DEFS = [
3
+ { name: "now", type: "Date", meta: { label: "\u5F53\u524D\u65F6\u95F4", description: "\u5F53\u524D\u65E5\u671F/\u65F6\u95F4" } },
4
+ { name: "form", type: "dyn", meta: { label: "\u8868\u5355\u503C", description: "\u5F53\u524D\u8868\u5355\u72B6\u6001" } },
5
+ { name: "undoable", type: "bool", meta: { label: "\u53EF\u64A4\u9500", description: "\u5F53\u524D\u662F\u5426\u6709\u53EF\u64A4\u9500\u7684\u4FEE\u6539" } },
6
+ { name: "redoable", type: "bool", meta: { label: "\u53EF\u91CD\u505A", description: "\u5F53\u524D\u662F\u5426\u6709\u53EF\u91CD\u505A\u7684\u4FEE\u6539" } },
7
+ { name: SELECTIONS_VAR, type: "dyn", meta: { label: "\u9009\u4E2D\u9879", description: "\u5404\u9009\u62E9\u5B57\u6BB5\u5F53\u524D\u9009\u4E2D\u9879\u7684\u5B8C\u6574\u5BF9\u8C61\uFF08\u6309\u5B57\u6BB5\u6807\u8BC6\uFF09" } },
8
+ { name: OPTIONS_VAR, type: "dyn", meta: { label: "\u5168\u90E8\u9009\u9879", description: "\u5404\u9009\u62E9\u5B57\u6BB5\u5F53\u524D\u5DF2\u52A0\u8F7D\u7684\u5168\u90E8\u9009\u9879\uFF08\u6309\u5B57\u6BB5\u6807\u8BC6\uFF0C\u5143\u7D20\u4E3A\u9009\u9879\u5B8C\u6574\u5BF9\u8C61\uFF09" } }
9
+ ];
10
+ export const FORM_VARIABLE_NAMES = new Set(FORM_VARIABLE_DEFS.map((d) => d.name));
2
11
  export function registerFormVariablesIfAbsent(env) {
3
12
  const declared = new Set(env.getDefinitions().variables.map((v) => v.name));
4
- if (!declared.has("now")) env.registerVariable("now", "Date", { label: "\u5F53\u524D\u65F6\u95F4", description: "\u5F53\u524D\u65E5\u671F/\u65F6\u95F4" });
5
- if (!declared.has("form")) env.registerVariable("form", "dyn", { label: "\u8868\u5355\u503C", description: "\u5F53\u524D\u8868\u5355\u72B6\u6001" });
6
- if (!declared.has("undoable")) env.registerVariable("undoable", "bool", { label: "\u53EF\u64A4\u9500", description: "\u5F53\u524D\u662F\u5426\u6709\u53EF\u64A4\u9500\u7684\u4FEE\u6539" });
7
- if (!declared.has("redoable")) env.registerVariable("redoable", "bool", { label: "\u53EF\u91CD\u505A", description: "\u5F53\u524D\u662F\u5426\u6709\u53EF\u91CD\u505A\u7684\u4FEE\u6539" });
8
- if (!declared.has(SELECTIONS_VAR)) env.registerVariable(SELECTIONS_VAR, "dyn", { label: "\u9009\u4E2D\u9879", description: "\u5404\u9009\u62E9\u5B57\u6BB5\u5F53\u524D\u9009\u4E2D\u9879\u7684\u5B8C\u6574\u5BF9\u8C61\uFF08\u6309\u5B57\u6BB5\u6807\u8BC6\uFF09" });
9
- if (!declared.has(OPTIONS_VAR)) env.registerVariable(OPTIONS_VAR, "dyn", { label: "\u5168\u90E8\u9009\u9879", description: "\u5404\u9009\u62E9\u5B57\u6BB5\u5F53\u524D\u5DF2\u52A0\u8F7D\u7684\u5168\u90E8\u9009\u9879\uFF08\u6309\u5B57\u6BB5\u6807\u8BC6\uFF0C\u5143\u7D20\u4E3A\u9009\u9879\u5B8C\u6574\u5BF9\u8C61\uFF09" });
13
+ for (const d of FORM_VARIABLE_DEFS) {
14
+ if (!declared.has(d.name)) env.registerVariable(d.name, d.type, d.meta);
15
+ }
10
16
  }
@@ -8,7 +8,7 @@ export const metadata = {
8
8
  name: "\u53D1\u9001\u8BF7\u6C42",
9
9
  icon: "fluent:cloud-arrow-up-20-regular",
10
10
  // This op publishes its parsed response as a chain-step output, so later
11
- // trigger rows can reference it via `steps["<row-id>"]`.
11
+ // trigger rows can reference it via `steps[?"<row-id>"]`.
12
12
  output: "HTTP \u54CD\u5E94\uFF08\u89E3\u6790\u540E\u7684 JSON\uFF09"
13
13
  };
14
14
  export function schema(configure) {
@@ -69,7 +69,7 @@ export function schema(configure) {
69
69
  description: md`
70
70
  发起一次 HTTP 请求(响应解析为 JSON)。请求失败(网络错误 / 非 2xx / 响应不可解析)
71
71
  或「执行结果」为 \`error\` 时中止本次触发链。在触发列表中串联多个本操作即可按顺序
72
- 调用多个接口——前一个失败后,后续操作不再执行。后面的步骤可经 \`steps["<步骤标识>"]\`
72
+ 调用多个接口——前一个失败后,后续操作不再执行。后面的步骤可经 \`steps[?"<步骤标识>"]\`
73
73
  引用前面步骤的响应。
74
74
 
75
75
  toast 完全由「提示标题」「提示内容」决定(含失败场景);不配置标题、或标题求值为空字符串则静默。
@@ -39,7 +39,7 @@ export function schema(configure) {
39
39
  title: "\u8BF7\u6C42",
40
40
  description: md`
41
41
  构造一个请求,其响应将被取回并保存为文件。
42
- 可经 \`steps["<步骤标识>"]\` 引用前序步骤的输出——把先前
42
+ 可经 \`steps[?"<步骤标识>"]\` 引用前序步骤的输出——把先前
43
43
  \`发送请求\` 行取得的凭据用于构造真正的下载请求。
44
44
  `
45
45
  }),
@@ -75,7 +75,7 @@ export function schema(configure) {
75
75
  发起一次 HTTP 请求:若响应是二进制(非 JSON)则保存为本地文件;若响应是 JSON,
76
76
  则视为后端返回的状态信封,经「执行结果」裁定成败而**不**保存。下载失败(网络错误 / 非 2xx)
77
77
  或「执行结果」为 \`error\` 会中止本次触发链——其后排队的操作不再执行。把本操作接在一个
78
- \`发送请求\` 行之后、并经 \`steps["<步骤标识>"]\` 引用其响应,即可实现
78
+ \`发送请求\` 行之后、并经 \`steps[?"<步骤标识>"]\` 引用其响应,即可实现
79
79
  “先取凭据再下载”的两步式。toast 完全由「提示标题」「提示内容」决定(含失败场景);
80
80
  不配置标题、或标题求值为空字符串则静默。
81
81
  `
@@ -1,12 +1,6 @@
1
1
  import { Effect } from "effect";
2
2
  import { navigateTo, useRouter } from "#app";
3
- function isExternal(url) {
4
- try {
5
- return new URL(url, window.location.href).origin !== window.location.origin;
6
- } catch {
7
- return false;
8
- }
9
- }
3
+ import { isAbsoluteUrl } from "../../../../share/navigation.js";
10
4
  export const handler = (params, ctx) => {
11
5
  const { url, mode } = params ?? {};
12
6
  if (!url || url.trim().length === 0) return Effect.void;
@@ -14,13 +8,13 @@ export const handler = (params, ctx) => {
14
8
  const steps = ctx.step?.outputs() ?? {};
15
9
  const resolved = yield* ctx.cel(url, { steps });
16
10
  const router = useRouter();
17
- const external = isExternal(resolved);
18
- yield* Effect.promise(async () => {
11
+ const absolute = isAbsoluteUrl(resolved);
12
+ yield* Effect.tryPromise(async () => {
19
13
  if (mode === "_blank") {
20
- window.open(external ? resolved : router.resolve(resolved).href, "_blank");
14
+ window.open(absolute ? resolved : router.resolve(resolved).href, "_blank");
21
15
  return;
22
16
  }
23
- await navigateTo(resolved, { external });
17
+ await navigateTo(resolved, { external: absolute });
24
18
  });
25
19
  });
26
20
  return program.pipe(
@@ -28,7 +28,7 @@ export function schema(configure) {
28
28
  }).annotations({
29
29
  title: "NavigationOperation",
30
30
  description: md`
31
- 按所选方式打开一个链接。链接表达式可经 \`steps["<步骤标识>"]\` 引用前序步骤的输出,
31
+ 按所选方式打开一个链接。链接表达式可经 \`steps[?"<步骤标识>"]\` 引用前序步骤的输出,
32
32
  因此可由先前请求的响应拼接目标地址。导航失败不中止本次触发链。
33
33
  `
34
34
  });
@@ -30,7 +30,7 @@ export type OperationMetadata = Readonly<{
30
30
  icon: string;
31
31
  /**
32
32
  * Present iff this operation publishes a chain-step output (`ctx.step.publish`)
33
- * for later trigger rows to read via `steps["<row-id>"]` — the value is the
33
+ * for later trigger rows to read via `steps[?"<row-id>"]` — the value is the
34
34
  * designer-facing label of what gets published (e.g. 'HTTP 响应'). The trigger
35
35
  * editor reads it to decide which rows to offer as step references, so a
36
36
  * non-publishing op never advertises a reference that resolves to nothing.
@@ -6,10 +6,12 @@ import { type CELVar } from '../../../utils/cel-context.js';
6
6
  * an op schema may be embedded under a host whose `configure` already
7
7
  * advertises it, and double registration throws.
8
8
  *
9
- * `dyn` (not `map<string, dyn>`) so `steps["<id>"].data.code` type-checks the
9
+ * `dyn` (not `map<string, dyn>`) so `steps[?"<id>"].data.code` type-checks the
10
10
  * same way `form.foo.bar` does — what a step publishes is backend-defined and
11
11
  * unknowable at design time. Keyed by trigger-row uuid; the picker renders
12
- * each key as its「第 N 步」position.
12
+ * each key as its「第 N 步」position, and writes the OPTIONAL lookup (`[?]`), so
13
+ * a step that published nothing evaluates to `undefined` instead of throwing
14
+ * `No such key`.
13
15
  */
14
16
  export declare function registerStepsVariableIfAbsent(env: Environment): void;
15
17
  /**
@@ -1,33 +1,13 @@
1
1
  <script setup>
2
2
  import { computed } from "vue";
3
- import { Effect } from "effect";
4
3
  import { Icon } from "@iconify/vue";
5
- import { cel as $cel } from "../../../../../utils/cel";
6
- import { celScope, injectCELContext } from "../../../../../utils/cel-context";
7
4
  import { cn } from "../../../../../utils/cn";
8
5
  defineOptions({ name: "ShwfedTableSelectRendererRuntime" });
9
6
  const props = defineProps({
10
7
  column: { type: null, required: true },
11
8
  ctx: { type: Object, required: true }
12
9
  });
13
- const celContext = injectCELContext();
14
- function evalPredicate(expression, fallback) {
15
- try {
16
- return Boolean(Effect.runSync($cel(expression, celScope(celContext))));
17
- } catch (e) {
18
- console.error("[shwfed-table] row-selection predicate failed", e);
19
- return fallback;
20
- }
21
- }
22
- const canSelect = computed(() => {
23
- if (props.column.enableRowSelection && !evalPredicate(props.column.enableRowSelection, true)) {
24
- return false;
25
- }
26
- if (props.column.mode === "checkbox" && props.column.enableMultiRowSelection && !evalPredicate(props.column.enableMultiRowSelection, true)) {
27
- return false;
28
- }
29
- return true;
30
- });
10
+ const canSelect = computed(() => props.ctx.row.getCanSelect());
31
11
  function onCheckboxClick(e) {
32
12
  if (!canSelect.value) return;
33
13
  const { row, table } = props.ctx;
@@ -1,6 +1,7 @@
1
1
  import { Schema } from 'effect';
2
2
  import type { ColumnDef } from '@tanstack/vue-table';
3
3
  import type { Environment } from '../../../../../vendor/cel-js/lib/index.js';
4
+ import type { ColumnRowSelection } from '../../../utils/resolve.js';
4
5
  export declare const type: "com.shwfed.table.column.select";
5
6
  export declare const compatibilityDate: "2026-04-14";
6
7
  export declare const metadata: {
@@ -20,4 +21,5 @@ export declare function schema(configure: (env: Environment) => void): Schema.St
20
21
  }>;
21
22
  export type Value = Schema.Schema.Type<ReturnType<typeof schema>>;
22
23
  export declare function defaults(): Partial<Value>;
24
+ export declare const rowSelection: ColumnRowSelection;
23
25
  export declare function toColumnDef(value: Value): Partial<ColumnDef<unknown, unknown>>;
@@ -1,4 +1,4 @@
1
- import { Schema } from "effect";
1
+ import { Effect, Schema } from "effect";
2
2
  import { h } from "vue";
3
3
  import { Expression } from "../../../../../share/expression.js";
4
4
  import { columnIdentityFields, registerRowVariablesIfAbsent } from "../../../utils/shared.js";
@@ -40,6 +40,21 @@ export function defaults() {
40
40
  mode: "checkbox"
41
41
  };
42
42
  }
43
+ export const rowSelection = (value, deps) => {
44
+ const predicates = [
45
+ value.enableRowSelection,
46
+ value.mode === "checkbox" ? value.enableMultiRowSelection : void 0
47
+ ].filter((e) => typeof e === "string" && e.trim() !== "");
48
+ if (predicates.length === 0) return () => true;
49
+ return (row, index) => predicates.every((expression) => {
50
+ try {
51
+ return Boolean(Effect.runSync(deps.$cel(expression, deps.rowScope(row, index))));
52
+ } catch (e) {
53
+ console.error("[shwfed-table] row-selection predicate failed", e);
54
+ return true;
55
+ }
56
+ });
57
+ };
43
58
  export function toColumnDef(value) {
44
59
  return {
45
60
  header: (ctx) => h(Header, { column: value, ctx }),
@@ -43,6 +43,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
43
43
  readonly data: string;
44
44
  readonly request?: string | undefined;
45
45
  };
46
+ readonly enableSorting?: boolean | undefined;
46
47
  readonly nodeKey: string;
47
48
  readonly nodeChildren: string;
48
49
  readonly nodeLabel: readonly [{
@@ -52,7 +53,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
52
53
  readonly locale: "en" | "ja" | "ko";
53
54
  readonly message: string;
54
55
  }[]];
55
- readonly enableSorting?: boolean | undefined;
56
56
  readonly cascade: "independent" | "cascade-down" | "cascade-both";
57
57
  readonly nodeTooltip?: readonly [{
58
58
  readonly locale: "zh";
@@ -106,6 +106,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
106
106
  readonly data: string;
107
107
  readonly request?: string | undefined;
108
108
  };
109
+ readonly enableSorting?: boolean | undefined;
109
110
  readonly nodeKey: string;
110
111
  readonly nodeChildren: string;
111
112
  readonly nodeLabel: readonly [{
@@ -115,7 +116,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
115
116
  readonly locale: "en" | "ja" | "ko";
116
117
  readonly message: string;
117
118
  }[]];
118
- readonly enableSorting?: boolean | undefined;
119
119
  readonly cascade: "independent" | "cascade-down" | "cascade-both";
120
120
  readonly nodeTooltip?: readonly [{
121
121
  readonly locale: "zh";
@@ -43,6 +43,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
43
43
  readonly data: string;
44
44
  readonly request?: string | undefined;
45
45
  };
46
+ readonly enableSorting?: boolean | undefined;
46
47
  readonly nodeKey: string;
47
48
  readonly nodeChildren: string;
48
49
  readonly nodeLabel: readonly [{
@@ -52,7 +53,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
52
53
  readonly locale: "en" | "ja" | "ko";
53
54
  readonly message: string;
54
55
  }[]];
55
- readonly enableSorting?: boolean | undefined;
56
56
  readonly cascade: "independent" | "cascade-down" | "cascade-both";
57
57
  readonly nodeTooltip?: readonly [{
58
58
  readonly locale: "zh";
@@ -106,6 +106,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
106
106
  readonly data: string;
107
107
  readonly request?: string | undefined;
108
108
  };
109
+ readonly enableSorting?: boolean | undefined;
109
110
  readonly nodeKey: string;
110
111
  readonly nodeChildren: string;
111
112
  readonly nodeLabel: readonly [{
@@ -115,7 +116,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
115
116
  readonly locale: "en" | "ja" | "ko";
116
117
  readonly message: string;
117
118
  }[]];
118
- readonly enableSorting?: boolean | undefined;
119
119
  readonly cascade: "independent" | "cascade-down" | "cascade-both";
120
120
  readonly nodeTooltip?: readonly [{
121
121
  readonly locale: "zh";
@@ -43,6 +43,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
43
43
  readonly data: string;
44
44
  readonly request?: string | undefined;
45
45
  };
46
+ readonly enableSorting?: boolean | undefined;
46
47
  readonly nodeKey: string;
47
48
  readonly nodeChildren: string;
48
49
  readonly nodeLabel: readonly [{
@@ -52,7 +53,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
52
53
  readonly locale: "en" | "ja" | "ko";
53
54
  readonly message: string;
54
55
  }[]];
55
- readonly enableSorting?: boolean | undefined;
56
56
  readonly nodeTooltip?: readonly [{
57
57
  readonly locale: "zh";
58
58
  readonly message: string;
@@ -105,6 +105,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
105
105
  readonly data: string;
106
106
  readonly request?: string | undefined;
107
107
  };
108
+ readonly enableSorting?: boolean | undefined;
108
109
  readonly nodeKey: string;
109
110
  readonly nodeChildren: string;
110
111
  readonly nodeLabel: readonly [{
@@ -114,7 +115,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
114
115
  readonly locale: "en" | "ja" | "ko";
115
116
  readonly message: string;
116
117
  }[]];
117
- readonly enableSorting?: boolean | undefined;
118
118
  readonly nodeTooltip?: readonly [{
119
119
  readonly locale: "zh";
120
120
  readonly message: string;