@shwfed/config 2.9.6 → 2.9.8
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/mcp.mjs +1054 -855
- package/dist/module.json +1 -1
- package/dist/preview/assets/{FieldGroup.vue_vue_type_script_setup_true_lang-B-Cwm94m.js → FieldGroup.vue_vue_type_script_setup_true_lang-ByKzTJkK.js} +1 -1
- package/dist/preview/assets/{badge-nn3uvUrR.js → badge-Dq4c6LC9.js} +1 -1
- package/dist/preview/assets/{config-DO77XC7j.js → config-B8qH4_4x.js} +1 -1
- package/dist/preview/assets/{config-e8Li9jh4.js → config-BBGDw3Em.js} +1 -1
- package/dist/preview/assets/{config-CUkEZF8G.js → config-C32gE_z0.js} +1 -1
- package/dist/preview/assets/{config-CGhV-PCy.js → config-COfUny1G.js} +1 -1
- package/dist/preview/assets/{config-d8ZhbBPg.js → config-CVPTjqGi.js} +1 -1
- package/dist/preview/assets/{config-Dvq_Xh7I.js → config-CgdFXkLJ.js} +1 -1
- package/dist/preview/assets/{config-C6LousLT.js → config-DNr8rSLw.js} +1 -1
- package/dist/preview/assets/{config-Bg95dd9K.js → config-DVEOLj6d.js} +1 -1
- package/dist/preview/assets/{config-BNQXbhtM.js → config-DpRJqJ1d.js} +1 -1
- package/dist/preview/assets/{config-DiD-8mMw.js → config-Dvqs3X3R.js} +1 -1
- package/dist/preview/assets/{config-DbjXscRV.js → config-DyqhZSFe.js} +1 -1
- package/dist/preview/assets/{definition.vue_vue_type_script_setup_true_lang-DuO-EwWA.js → definition.vue_vue_type_script_setup_true_lang-B0MHEyHj.js} +1 -1
- package/dist/preview/assets/index-Br3FtCu2.js +1 -0
- package/dist/preview/assets/index-C1h9lV52.css +1 -0
- package/dist/preview/assets/index-C1wWjgLL.js +717 -0
- package/dist/preview/assets/{index-DZyYLnIs.js → index-CWJ_W3Gk.js} +1 -1
- package/dist/preview/assets/{item-BqnYkxMr.js → item-Bp81qizC.js} +1 -1
- package/dist/preview/assets/runtime-AHwg8SCY.js +1 -0
- package/dist/preview/assets/{runtime-D6blej8e.js → runtime-BHWdj0nM.js} +1 -1
- package/dist/preview/assets/runtime-Be6zYys6.js +1 -0
- package/dist/preview/assets/{runtime-CqoGHW0U.js → runtime-CKj3_07G.js} +1 -1
- package/dist/preview/assets/runtime-CVIZYrHD.js +1 -0
- package/dist/preview/assets/runtime-C_F9tcxh.js +1 -0
- package/dist/preview/assets/runtime-DNHuKamx.js +1 -0
- package/dist/preview/assets/{runtime-DrnADq2i.js → runtime-DambLtyb.js} +1 -1
- package/dist/preview/assets/{runtime-CfdnDil3.js → runtime-DqkDOQp2.js} +1 -1
- package/dist/preview/assets/runtime-UVi5ssie.js +1 -0
- package/dist/preview/index.html +2 -2
- package/dist/runtime/components/actions/buttons/2026-04-18/com.shwfed.actions.button.http.request.json/runtime.vue +1 -1
- package/dist/runtime/components/actions/buttons/2026-04-18/com.shwfed.actions.button.http.request.json/schema.d.ts +1 -1
- package/dist/runtime/components/actions/buttons/2026-04-18/com.shwfed.actions.button.http.request.json/schema.js +2 -2
- package/dist/runtime/components/actions/buttons/2026-04-18/com.shwfed.actions.button.http.request.json.confirm/runtime.vue +1 -1
- package/dist/runtime/components/actions/buttons/2026-05-15/com.shwfed.actions.button.event.dispatch/runtime.vue +5 -1
- package/dist/runtime/components/actions/buttons/2026-05-21/com.shwfed.actions.button.http.download/runtime.vue +2 -2
- package/dist/runtime/components/actions/buttons/2026-05-24/com.shwfed.actions.button.state.write/runtime.vue +1 -1
- package/dist/runtime/components/actions/buttons/2026-06-08/com.shwfed.actions.button.http.request.json.batch/runtime.vue +1 -1
- package/dist/runtime/components/actions/buttons/2026-06-08/com.shwfed.actions.button.http.request.json.batch/schema.d.ts +1 -1
- package/dist/runtime/components/actions/buttons/2026-06-08/com.shwfed.actions.button.http.request.json.batch/schema.js +1 -1
- package/dist/runtime/components/actions/utils/resolve.js +1 -1
- package/dist/runtime/components/block-layout-editor/index.vue +98 -3
- package/dist/runtime/components/config/footer.vue +22 -20
- package/dist/runtime/components/config/use-editor.js +5 -9
- package/dist/runtime/components/config/utils/validation-error.d.ts +1 -0
- package/dist/runtime/components/config/utils/validation-error.js +34 -0
- package/dist/runtime/components/form/fields/2026-06-09/com.shwfed.form.field.upload/config.d.vue.ts +155 -0
- package/dist/runtime/components/form/fields/2026-06-09/com.shwfed.form.field.upload/config.vue +918 -0
- package/dist/runtime/components/form/fields/2026-06-09/com.shwfed.form.field.upload/config.vue.d.ts +155 -0
- package/dist/runtime/components/form/fields/2026-06-09/com.shwfed.form.field.upload/runtime.d.vue.ts +8 -0
- package/dist/runtime/components/form/fields/2026-06-09/com.shwfed.form.field.upload/runtime.vue +482 -0
- package/dist/runtime/components/form/fields/2026-06-09/com.shwfed.form.field.upload/runtime.vue.d.ts +8 -0
- package/dist/runtime/components/form/fields/2026-06-09/com.shwfed.form.field.upload/schema.d.ts +126 -0
- package/dist/runtime/components/form/fields/2026-06-09/com.shwfed.form.field.upload/schema.js +178 -0
- package/dist/runtime/components/operations/utils/resolve.d.ts +7 -0
- package/dist/runtime/components/operations/utils/resolve.js +1 -1
- package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch/runtime.vue +2 -1
- package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/runtime.vue +2 -1
- package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-remote/runtime.vue +1 -1
- package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-static/runtime.vue +1 -1
- package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-remote/runtime.vue +1 -1
- package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-static/runtime.vue +1 -1
- package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi.remote/runtime.vue +1 -1
- package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single.remote/runtime.vue +1 -1
- package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-multi/runtime.vue +1 -1
- package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-single/runtime.vue +1 -1
- package/dist/runtime/share/event-bus.d.ts +20 -2
- package/dist/runtime/share/event-bus.js +4 -3
- package/dist/runtime/vendor/cel-js/CLAUDE.md +1 -1
- package/dist/runtime/vendor/cel-js/PROMPT.md +2 -2
- package/dist/runtime/vendor/cel-js/lib/http-builder.js +13 -3
- package/package.json +1 -1
- package/dist/preview/assets/index-Bpap7h0Q.js +0 -717
- package/dist/preview/assets/index-jHSCIEVi.js +0 -1
- package/dist/preview/assets/index-vPcvbp7e.css +0 -1
- package/dist/preview/assets/runtime-BFAxoqEC.js +0 -1
- package/dist/preview/assets/runtime-BntIEgRD.js +0 -1
- package/dist/preview/assets/runtime-Bu6BEaxq.js +0 -1
- package/dist/preview/assets/runtime-DEsY0ihh.js +0 -1
- package/dist/preview/assets/runtime-JW4LSnRF.js +0 -1
- package/dist/preview/assets/runtime-U7gyBbWI.js +0 -1
package/dist/runtime/components/form/fields/2026-06-09/com.shwfed.form.field.upload/schema.d.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Effect, Schema } from 'effect';
|
|
2
|
+
import type { Environment } from '../../../../../vendor/cel-js/lib/index.js';
|
|
3
|
+
export declare const type: "com.shwfed.form.field.upload";
|
|
4
|
+
export declare const compatibilityDate: "2026-06-09";
|
|
5
|
+
export declare const metadata: {
|
|
6
|
+
readonly name: "文件上传";
|
|
7
|
+
readonly icon: "fluent:cloud-arrow-up-20-regular";
|
|
8
|
+
readonly w: {
|
|
9
|
+
readonly initial: 8;
|
|
10
|
+
readonly min: 8;
|
|
11
|
+
readonly max: number;
|
|
12
|
+
};
|
|
13
|
+
readonly h: {
|
|
14
|
+
readonly initial: 6;
|
|
15
|
+
readonly min: 6;
|
|
16
|
+
readonly max: number;
|
|
17
|
+
readonly grow: true;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export declare const JSON_VAR: {
|
|
21
|
+
readonly type: "dyn";
|
|
22
|
+
readonly label: "HTTP 响应体";
|
|
23
|
+
readonly description: "第一步 `请求` 的响应体(已解析 JSON);用于构造第二步下载请求";
|
|
24
|
+
};
|
|
25
|
+
export declare const FILES_VAR: {
|
|
26
|
+
readonly type: "list<dyn>";
|
|
27
|
+
readonly label: "已选文件";
|
|
28
|
+
readonly description: "本次选择的文件列表(`File` 数组);通常通过 `form({...})` 构造 multipart 请求体";
|
|
29
|
+
};
|
|
30
|
+
export declare const UPLOAD_JSON_VAR: {
|
|
31
|
+
readonly type: "dyn";
|
|
32
|
+
readonly label: "上传响应";
|
|
33
|
+
readonly description: "上传 `请求` 的响应体(已解析 JSON);用于提取已上传文件项";
|
|
34
|
+
};
|
|
35
|
+
export declare const FILE_VAR: {
|
|
36
|
+
readonly type: "dyn";
|
|
37
|
+
readonly label: "文件项";
|
|
38
|
+
readonly description: "表单状态中的单个文件项(服务端回填或上传响应映射所得);返回其展示文件名";
|
|
39
|
+
};
|
|
40
|
+
export declare function schema(configure: (env: Environment) => void): Schema.Struct<{
|
|
41
|
+
label: Schema.optional<Schema.TupleType<readonly [Schema.Struct<{
|
|
42
|
+
locale: Schema.Literal<["zh"]>;
|
|
43
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
44
|
+
}>], [Schema.Struct<{
|
|
45
|
+
locale: Schema.Literal<["ja", "en", "ko"]>;
|
|
46
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
47
|
+
}>]>>;
|
|
48
|
+
description: Schema.optional<Schema.refine<readonly [{
|
|
49
|
+
readonly locale: "zh";
|
|
50
|
+
readonly message: string;
|
|
51
|
+
}, ...{
|
|
52
|
+
readonly locale: "en" | "ja" | "ko";
|
|
53
|
+
readonly message: string;
|
|
54
|
+
}[]], Schema.TupleType<readonly [Schema.Struct<{
|
|
55
|
+
locale: Schema.Literal<["zh"]>;
|
|
56
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
57
|
+
}>], [Schema.Struct<{
|
|
58
|
+
locale: Schema.Literal<["ja", "en", "ko"]>;
|
|
59
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
60
|
+
}>]>>>;
|
|
61
|
+
placeholder: Schema.optional<Schema.TupleType<readonly [Schema.Struct<{
|
|
62
|
+
locale: Schema.Literal<["zh"]>;
|
|
63
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
64
|
+
}>], [Schema.Struct<{
|
|
65
|
+
locale: Schema.Literal<["ja", "en", "ko"]>;
|
|
66
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
67
|
+
}>]>>;
|
|
68
|
+
tooltip: Schema.optional<Schema.TupleType<readonly [Schema.Struct<{
|
|
69
|
+
locale: Schema.Literal<["zh"]>;
|
|
70
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
71
|
+
}>], [Schema.Struct<{
|
|
72
|
+
locale: Schema.Literal<["ja", "en", "ko"]>;
|
|
73
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
74
|
+
}>]>>;
|
|
75
|
+
orientation: Schema.optional<Schema.Literal<["vertical", "floating"]>>;
|
|
76
|
+
binding: Schema.optional<Schema.refine<string, typeof Schema.String>>;
|
|
77
|
+
disabled: Schema.optional<Schema.Schema<string, string, never>>;
|
|
78
|
+
readonly: Schema.optional<Schema.Schema<string, string, never>>;
|
|
79
|
+
multiple: Schema.optional<Schema.SchemaClass<boolean, boolean, never>>;
|
|
80
|
+
accept: Schema.optional<Schema.Array$<typeof Schema.String>>;
|
|
81
|
+
maxFileSize: Schema.optional<Schema.refine<number, typeof Schema.Number>>;
|
|
82
|
+
maxTotalSize: Schema.optional<Schema.refine<number, typeof Schema.Number>>;
|
|
83
|
+
maxFiles: Schema.optional<Schema.refine<number, Schema.filter<typeof Schema.Number>>>;
|
|
84
|
+
upload: Schema.optional<Schema.Struct<{
|
|
85
|
+
request: Schema.Schema<string, string, never>;
|
|
86
|
+
handle: Schema.Schema<string, string, never>;
|
|
87
|
+
filename: Schema.optional<Schema.Schema<string, string, never>>;
|
|
88
|
+
}>>;
|
|
89
|
+
templates: Schema.optional<Schema.Array$<Schema.Struct<{
|
|
90
|
+
request: Schema.Schema<string, string, never>;
|
|
91
|
+
download: Schema.optional<Schema.Schema<string, string, never>>;
|
|
92
|
+
icon: Schema.optional<Schema.refine<string, typeof Schema.String>>;
|
|
93
|
+
label: Schema.optional<Schema.TupleType<readonly [Schema.Struct<{
|
|
94
|
+
locale: Schema.Literal<["zh"]>;
|
|
95
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
96
|
+
}>], [Schema.Struct<{
|
|
97
|
+
locale: Schema.Literal<["ja", "en", "ko"]>;
|
|
98
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
99
|
+
}>]>>;
|
|
100
|
+
}>>>;
|
|
101
|
+
id: Schema.refine<string, typeof Schema.String>;
|
|
102
|
+
displayName: Schema.optional<Schema.SchemaClass<string, string, never>>;
|
|
103
|
+
hidden: Schema.optional<Schema.Schema<string, string, never>>;
|
|
104
|
+
required: Schema.optional<Schema.Schema<string, string, never>>;
|
|
105
|
+
validations: Schema.optional<Schema.Array$<Schema.Struct<{
|
|
106
|
+
when: Schema.Schema<string, string, never>;
|
|
107
|
+
warning: Schema.optional<Schema.SchemaClass<boolean, boolean, never>>;
|
|
108
|
+
message: Schema.refine<readonly [{
|
|
109
|
+
readonly locale: "zh";
|
|
110
|
+
readonly message: string;
|
|
111
|
+
}, ...{
|
|
112
|
+
readonly locale: "en" | "ja" | "ko";
|
|
113
|
+
readonly message: string;
|
|
114
|
+
}[]], Schema.TupleType<readonly [Schema.Struct<{
|
|
115
|
+
locale: Schema.Literal<["zh"]>;
|
|
116
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
117
|
+
}>], [Schema.Struct<{
|
|
118
|
+
locale: Schema.Literal<["ja", "en", "ko"]>;
|
|
119
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
120
|
+
}>]>>;
|
|
121
|
+
}>>>;
|
|
122
|
+
type: Schema.Literal<["com.shwfed.form.field.upload"]>;
|
|
123
|
+
compatibilityDate: Schema.Literal<["2026-06-09"]>;
|
|
124
|
+
}>;
|
|
125
|
+
export type Value = Schema.Schema.Type<ReturnType<typeof schema>>;
|
|
126
|
+
export declare function migrate(prev: unknown): Effect.Effect<unknown, Error>;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { Expression, LocaleMarkdown } from "../../../../../share/expression.js";
|
|
3
|
+
import { Locale } from "../../../../../share/locale.js";
|
|
4
|
+
import { commonFieldFields, FieldOrientationSchema } from "../../../utils/common.js";
|
|
5
|
+
export const type = "com.shwfed.form.field.upload";
|
|
6
|
+
export const compatibilityDate = "2026-06-09";
|
|
7
|
+
export const metadata = {
|
|
8
|
+
name: "\u6587\u4EF6\u4E0A\u4F20",
|
|
9
|
+
icon: "fluent:cloud-arrow-up-20-regular",
|
|
10
|
+
w: { initial: 8, min: 8, max: Infinity },
|
|
11
|
+
h: { initial: 6, min: 6, max: Infinity, grow: true }
|
|
12
|
+
};
|
|
13
|
+
export const JSON_VAR = {
|
|
14
|
+
type: "dyn",
|
|
15
|
+
label: "HTTP \u54CD\u5E94\u4F53",
|
|
16
|
+
description: "\u7B2C\u4E00\u6B65 `\u8BF7\u6C42` \u7684\u54CD\u5E94\u4F53\uFF08\u5DF2\u89E3\u6790 JSON\uFF09\uFF1B\u7528\u4E8E\u6784\u9020\u7B2C\u4E8C\u6B65\u4E0B\u8F7D\u8BF7\u6C42"
|
|
17
|
+
};
|
|
18
|
+
export const FILES_VAR = {
|
|
19
|
+
type: "list<dyn>",
|
|
20
|
+
label: "\u5DF2\u9009\u6587\u4EF6",
|
|
21
|
+
description: "\u672C\u6B21\u9009\u62E9\u7684\u6587\u4EF6\u5217\u8868\uFF08`File` \u6570\u7EC4\uFF09\uFF1B\u901A\u5E38\u901A\u8FC7 `form({...})` \u6784\u9020 multipart \u8BF7\u6C42\u4F53"
|
|
22
|
+
};
|
|
23
|
+
export const UPLOAD_JSON_VAR = {
|
|
24
|
+
type: "dyn",
|
|
25
|
+
label: "\u4E0A\u4F20\u54CD\u5E94",
|
|
26
|
+
description: "\u4E0A\u4F20 `\u8BF7\u6C42` \u7684\u54CD\u5E94\u4F53\uFF08\u5DF2\u89E3\u6790 JSON\uFF09\uFF1B\u7528\u4E8E\u63D0\u53D6\u5DF2\u4E0A\u4F20\u6587\u4EF6\u9879"
|
|
27
|
+
};
|
|
28
|
+
export const FILE_VAR = {
|
|
29
|
+
type: "dyn",
|
|
30
|
+
label: "\u6587\u4EF6\u9879",
|
|
31
|
+
description: "\u8868\u5355\u72B6\u6001\u4E2D\u7684\u5355\u4E2A\u6587\u4EF6\u9879\uFF08\u670D\u52A1\u7AEF\u56DE\u586B\u6216\u4E0A\u4F20\u54CD\u5E94\u6620\u5C04\u6240\u5F97\uFF09\uFF1B\u8FD4\u56DE\u5176\u5C55\u793A\u6587\u4EF6\u540D"
|
|
32
|
+
};
|
|
33
|
+
function withVar(configure, name, spec) {
|
|
34
|
+
return (env) => {
|
|
35
|
+
configure(env);
|
|
36
|
+
env.registerVariable(name, spec.type, { description: spec.description });
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function schema(configure) {
|
|
40
|
+
const CelBool = Expression({ configure, resultType: "bool" });
|
|
41
|
+
const CelHttpRequest = Expression({ configure, resultType: "HttpRequest" });
|
|
42
|
+
const CelDownloadRequest = Expression({ configure: withVar(configure, "json", JSON_VAR), resultType: "HttpRequest" });
|
|
43
|
+
const CelUploadRequest = Expression({ configure: withVar(configure, "files", FILES_VAR), resultType: "HttpRequest" });
|
|
44
|
+
const CelUploadHandle = Expression({ configure: withVar(configure, "json", UPLOAD_JSON_VAR), resultType: "list" });
|
|
45
|
+
const CelItemName = Expression({ configure: withVar(configure, "file", FILE_VAR), resultType: "string" });
|
|
46
|
+
const LocaleMd = LocaleMarkdown({ configure });
|
|
47
|
+
return Schema.Struct({
|
|
48
|
+
type: Schema.Literal(type),
|
|
49
|
+
compatibilityDate: Schema.Literal(compatibilityDate),
|
|
50
|
+
...commonFieldFields(configure),
|
|
51
|
+
label: Schema.optional(Locale.annotations({
|
|
52
|
+
title: "\u6807\u7B7E",
|
|
53
|
+
description: "\u5B57\u6BB5\u524D\u5C55\u793A\u7684\u6587\u672C\uFF1B\u7559\u7A7A\u5219\u4E0D\u6E32\u67D3\u6807\u7B7E"
|
|
54
|
+
})),
|
|
55
|
+
description: Schema.optional(LocaleMd.annotations({
|
|
56
|
+
title: "\u8BF4\u660E",
|
|
57
|
+
description: "\u5C55\u793A\u5728\u4E0A\u4F20\u533A\u57DF\u4E0A\u65B9\u7684\u591A\u8BED\u8A00\u8BF4\u660E\uFF0C\u652F\u6301 Markdown \u4E0E `{{ form.foo }}` \u63D2\u503C"
|
|
58
|
+
})),
|
|
59
|
+
placeholder: Schema.optional(Locale.annotations({
|
|
60
|
+
title: "\u5360\u4F4D\u7B26",
|
|
61
|
+
description: "\u4E0A\u4F20\u533A\u57DF\u4E3A\u7A7A\u65F6\u7684\u63D0\u793A\u6587\u672C"
|
|
62
|
+
})),
|
|
63
|
+
tooltip: Schema.optional(Locale.annotations({
|
|
64
|
+
title: "\u63D0\u793A",
|
|
65
|
+
description: "\u9F20\u6807\u60AC\u505C\u5728\u6807\u7B7E\u4E0A\u65F6\u5C55\u793A\u7684\u8BF4\u660E"
|
|
66
|
+
})),
|
|
67
|
+
orientation: Schema.optional(FieldOrientationSchema),
|
|
68
|
+
binding: Schema.optional(Schema.String.pipe(Schema.minLength(1)).annotations({
|
|
69
|
+
title: "\u7ED1\u5B9A\u8DEF\u5F84",
|
|
70
|
+
description: "\u5199\u5165\u8868\u5355\u72B6\u6001\u7684 `dot-prop` \u8DEF\u5F84\uFF0C\u4F8B\u5982 `attachments.5du4fbuv`\uFF1B\u7559\u7A7A\u5219\u4E3A\u975E\u53D7\u63A7\u5B57\u6BB5"
|
|
71
|
+
})),
|
|
72
|
+
disabled: Schema.optional(CelBool.annotations({
|
|
73
|
+
title: "\u7981\u7528\u6761\u4EF6",
|
|
74
|
+
description: "\u8FD4\u56DE `true` \u65F6\u4E0A\u4F20\u533A\u57DF\u4ECD\u7136\u6E32\u67D3\u4F46\u4E0D\u53EF\u64CD\u4F5C"
|
|
75
|
+
})),
|
|
76
|
+
readonly: Schema.optional(CelBool.annotations({
|
|
77
|
+
title: "\u53EA\u8BFB\u6761\u4EF6",
|
|
78
|
+
description: "\u8FD4\u56DE `true` \u65F6\u4EC5\u4EE5\u6587\u4EF6\u5217\u8868\u5C55\u793A\u5F53\u524D\u503C\uFF0C\u4E0D\u5141\u8BB8\u589E\u5220"
|
|
79
|
+
})),
|
|
80
|
+
multiple: Schema.optional(Schema.Boolean.annotations({
|
|
81
|
+
title: "\u5141\u8BB8\u591A\u6587\u4EF6",
|
|
82
|
+
description: "\u5F00\u542F\u540E\u53EF\u4E00\u6B21\u9009\u62E9/\u62D6\u5165\u591A\u4E2A\u6587\u4EF6\uFF1B\u5173\u95ED\u65F6\u65B0\u6587\u4EF6\u4F1A\u66FF\u6362\u5DF2\u9009\u6587\u4EF6"
|
|
83
|
+
})),
|
|
84
|
+
accept: Schema.optional(Schema.Array(Schema.String).annotations({
|
|
85
|
+
title: "\u5141\u8BB8\u7684\u6587\u4EF6\u7C7B\u578B",
|
|
86
|
+
description: "\u5141\u8BB8\u4E0A\u4F20\u7684 MIME \u7C7B\u578B\u5217\u8868\uFF1B\u7559\u7A7A\u8868\u793A\u4E0D\u9650\u5236"
|
|
87
|
+
})),
|
|
88
|
+
maxFileSize: Schema.optional(Schema.Number.pipe(Schema.positive()).annotations({
|
|
89
|
+
title: "\u5355\u6587\u4EF6\u5927\u5C0F\u9650\u5236",
|
|
90
|
+
description: "\u5355\u4E2A\u6587\u4EF6\u5141\u8BB8\u7684\u6700\u5927\u5B57\u8282\u6570\uFF1B\u8D85\u8FC7\u6B64\u9650\u5236\u7684\u6587\u4EF6\u4F1A\u88AB\u62D2\u7EDD"
|
|
91
|
+
})),
|
|
92
|
+
maxTotalSize: Schema.optional(Schema.Number.pipe(Schema.positive()).annotations({
|
|
93
|
+
title: "\u603B\u5927\u5C0F\u9650\u5236",
|
|
94
|
+
description: "\u6240\u6709\u5DF2\u9009\u6587\u4EF6\u7D2F\u8BA1\u7684\u6700\u5927\u5B57\u8282\u6570\uFF1B\u65B0\u589E\u5BFC\u81F4\u8D85\u51FA\u65F6\u4F1A\u88AB\u62D2\u7EDD"
|
|
95
|
+
})),
|
|
96
|
+
maxFiles: Schema.optional(Schema.Number.pipe(Schema.int(), Schema.positive()).annotations({
|
|
97
|
+
title: "\u6587\u4EF6\u6570\u91CF\u9650\u5236",
|
|
98
|
+
description: "\u5141\u8BB8\u540C\u65F6\u4E0A\u4F20\u7684\u6700\u5927\u6587\u4EF6\u6570\uFF1B\u8FBE\u5230\u4E0A\u9650\u540E\u65B0\u589E\u6587\u4EF6\u4F1A\u88AB\u62D2\u7EDD"
|
|
99
|
+
})),
|
|
100
|
+
// Immediate upload. Its presence enables "upload on pick": each picked file
|
|
101
|
+
// is POSTed right away (`request`), the response is mapped into the stored
|
|
102
|
+
// items (`handle`), and each stored item's display name is resolved via
|
|
103
|
+
// `filename`. Absent → files are held as raw `File[]` and submitted with the
|
|
104
|
+
// form (deferred mode). The canonical→backend write mapping lives in the
|
|
105
|
+
// submit action, not here, so the field stays backend-agnostic.
|
|
106
|
+
upload: Schema.optional(Schema.Struct({
|
|
107
|
+
request: CelUploadRequest.annotations({
|
|
108
|
+
title: "\u4E0A\u4F20\u8BF7\u6C42",
|
|
109
|
+
description: '\u8FD4\u56DE `HttpRequest` \u7684 CEL \u8868\u8FBE\u5F0F\uFF1B\u53EF\u901A\u8FC7 `files` \u5F15\u7528\u672C\u6B21\u9009\u62E9\u7684\u6587\u4EF6\uFF0C\u4F8B\u5982 `http.post(url).body(form({"files": files}))`\u3002\u4EE5 `.json()` \u53D1\u8D77'
|
|
110
|
+
}),
|
|
111
|
+
handle: CelUploadHandle.annotations({
|
|
112
|
+
title: "\u5904\u7406\u54CD\u5E94",
|
|
113
|
+
description: '\u8FD4\u56DE**\u5217\u8868**\u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u901A\u8FC7 `json` \u5F15\u7528\u4E0A\u4F20\u54CD\u5E94\uFF1B\u6620\u5C04\u4E3A\u8FFD\u52A0\u5230\u7ED1\u5B9A\u503C\u7684\u6587\u4EF6\u9879\uFF0C\u4F8B\u5982 `json.data.map(d, {"cacheKey": d.key, "filename": d.fileOriginalName})`'
|
|
114
|
+
}),
|
|
115
|
+
filename: Schema.optional(CelItemName.annotations({
|
|
116
|
+
title: "\u6587\u4EF6\u540D",
|
|
117
|
+
description: "\u8FD4\u56DE `string` \u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u901A\u8FC7 `file` \u5F15\u7528\u5355\u4E2A\u6587\u4EF6\u9879\uFF1B\u7528\u4E8E\u5C55\u793A\u6587\u4EF6\u540D\uFF0C\u4F8B\u5982 `file.filename`\u3002\u7559\u7A7A\u65F6\u56DE\u9000\u5230 `filename` / `name` \u5B57\u6BB5"
|
|
118
|
+
}))
|
|
119
|
+
}).annotations({
|
|
120
|
+
title: "\u5373\u65F6\u4E0A\u4F20",
|
|
121
|
+
description: "\u914D\u7F6E\u540E\u6587\u4EF6\u5728\u9009\u62E9\u65F6\u7ACB\u5373\u4E0A\u4F20\uFF0C\u7ED1\u5B9A\u503C\u5B58\u50A8\u6620\u5C04\u540E\u7684\u6587\u4EF6\u9879\uFF08\u800C\u975E\u539F\u59CB `File`\uFF09\uFF1B\u4E0D\u914D\u7F6E\u5219\u4FDD\u7559\u6587\u4EF6\u968F\u8868\u5355\u4E00\u5E76\u63D0\u4EA4"
|
|
122
|
+
})),
|
|
123
|
+
// Download templates — each entry renders one button next to the upload
|
|
124
|
+
// zone. `request` is the (possibly only) step; `download` is an optional
|
|
125
|
+
// second step that may reference the first response via `json`.
|
|
126
|
+
templates: Schema.optional(Schema.Array(Schema.Struct({
|
|
127
|
+
request: CelHttpRequest.annotations({
|
|
128
|
+
title: "\u8BF7\u6C42",
|
|
129
|
+
description: "\u8FD4\u56DE `HttpRequest` \u7684 CEL \u8868\u8FBE\u5F0F\u3002\u672A\u914D\u7F6E\u300C\u4E0B\u8F7D\u8BF7\u6C42\u300D\u65F6\u76F4\u63A5\u53D1\u8D77\u5E76\u4E0B\u8F7D\u5176\u54CD\u5E94\uFF1B\u914D\u7F6E\u4E86\u300C\u4E0B\u8F7D\u8BF7\u6C42\u300D\u65F6\u4F5C\u4E3A\u83B7\u53D6\u4E0B\u8F7D\u51ED\u636E\u7684\u7B2C\u4E00\u6B65\u8BF7\u6C42"
|
|
130
|
+
}),
|
|
131
|
+
download: Schema.optional(CelDownloadRequest.annotations({
|
|
132
|
+
title: "\u4E0B\u8F7D\u8BF7\u6C42",
|
|
133
|
+
description: "\u53EF\u9009\u7684\u7B2C\u4E8C\u6B65\uFF1A\u8FD4\u56DE `HttpRequest` \u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u53EF\u901A\u8FC7 `json` \u5F15\u7528\u7B2C\u4E00\u6B65\u7684\u54CD\u5E94\u4F53\uFF0C\u4F8B\u5982\u7528 `json.data.key` \u6784\u9020\u771F\u6B63\u7684\u4E0B\u8F7D\u8BF7\u6C42"
|
|
134
|
+
})),
|
|
135
|
+
icon: Schema.optional(Schema.String.pipe(Schema.minLength(1)).annotations({
|
|
136
|
+
title: "\u6A21\u677F\u6309\u94AE\u56FE\u6807",
|
|
137
|
+
description: "Iconify \u56FE\u6807\u6807\u8BC6\u7B26\uFF0C\u4F8B\u5982 `fluent:arrow-download-20-regular`\uFF1B\u7559\u7A7A\u65F6\u4F7F\u7528\u9ED8\u8BA4\u56FE\u6807"
|
|
138
|
+
})),
|
|
139
|
+
label: Schema.optional(Locale.annotations({
|
|
140
|
+
title: "\u6A21\u677F\u6309\u94AE\u6587\u672C",
|
|
141
|
+
description: "\u4E0B\u8F7D\u6A21\u677F\u6309\u94AE\u4E0A\u7684\u672C\u5730\u5316\u6587\u672C\uFF1B\u7559\u7A7A\u65F6\u4F7F\u7528\u300C\u4E0B\u8F7D\u6A21\u677F\u300D"
|
|
142
|
+
}))
|
|
143
|
+
})).annotations({
|
|
144
|
+
title: "\u4E0B\u8F7D\u6A21\u677F",
|
|
145
|
+
description: "\u4E0B\u8F7D\u6A21\u677F\u5217\u8868\uFF1B\u6BCF\u4E00\u9879\u5728\u4E0A\u4F20\u533A\u57DF\u65C1\u6E32\u67D3\u4E00\u4E2A\u4E0B\u8F7D\u6309\u94AE"
|
|
146
|
+
}))
|
|
147
|
+
}).annotations({
|
|
148
|
+
title: "UploadField",
|
|
149
|
+
description: "\u6587\u4EF6\u4E0A\u4F20\u5B57\u6BB5"
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
export function migrate(prev) {
|
|
153
|
+
return Effect.try({
|
|
154
|
+
try: () => {
|
|
155
|
+
if (typeof prev !== "object" || prev === null) {
|
|
156
|
+
throw new Error("\u4E0A\u4F20\u5B57\u6BB5\u8FC1\u79FB\u5931\u8D25\uFF1A\u539F\u914D\u7F6E\u4E0D\u662F\u5BF9\u8C61");
|
|
157
|
+
}
|
|
158
|
+
const {
|
|
159
|
+
template,
|
|
160
|
+
templateIcon,
|
|
161
|
+
templateLabel,
|
|
162
|
+
compatibilityDate: _drop,
|
|
163
|
+
...rest
|
|
164
|
+
} = prev;
|
|
165
|
+
const next = { ...rest, compatibilityDate };
|
|
166
|
+
if (template != null && typeof template === "object" && typeof template.request === "string") {
|
|
167
|
+
const t = template;
|
|
168
|
+
const entry = { request: t.request };
|
|
169
|
+
if (typeof t.download === "string") entry.download = t.download;
|
|
170
|
+
if (typeof templateIcon === "string") entry.icon = templateIcon;
|
|
171
|
+
if (templateLabel != null) entry.label = templateLabel;
|
|
172
|
+
next.templates = [entry];
|
|
173
|
+
}
|
|
174
|
+
return next;
|
|
175
|
+
},
|
|
176
|
+
catch: (error) => error instanceof Error ? error : new Error(String(error))
|
|
177
|
+
});
|
|
178
|
+
}
|
|
@@ -40,6 +40,13 @@ export declare function findOperation(type: string, compatibilityDate: string):
|
|
|
40
40
|
* the request's `params` to the registered `handler` with `ctx`. Unknown
|
|
41
41
|
* refs are skipped. Hosts merge the result with any bespoke param-less
|
|
42
42
|
* handlers (e.g. the page's `close`).
|
|
43
|
+
*
|
|
44
|
+
* When the request carries an emit-site evaluator (`request.cel`, the
|
|
45
|
+
* dispatching button's own scope), it overrides `ctx.cel` for that one call —
|
|
46
|
+
* so a handler's CEL-bearing params resolve where they were authored (the
|
|
47
|
+
* button, with `row` live) instead of against the host. Handlers read `ctx.cel`
|
|
48
|
+
* unchanged; the swap is invisible to them. Falls back to the host's evaluator
|
|
49
|
+
* when the emitter supplied none.
|
|
43
50
|
*/
|
|
44
51
|
export declare function operationHandlers(exposed: ReadonlyArray<{
|
|
45
52
|
type: string;
|
|
@@ -70,7 +70,7 @@ export function operationHandlers(exposed, ctx) {
|
|
|
70
70
|
for (const { type, compatibilityDate } of exposed) {
|
|
71
71
|
const entry = findOperation(type, compatibilityDate);
|
|
72
72
|
if (!entry) continue;
|
|
73
|
-
handlers[type] = (params) => entry.handler(params, ctx);
|
|
73
|
+
handlers[type] = (params, cel) => entry.handler(params, cel ? { ...ctx, cel } : ctx);
|
|
74
74
|
}
|
|
75
75
|
return handlers;
|
|
76
76
|
}
|
package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch/runtime.vue
CHANGED
|
@@ -15,6 +15,7 @@ const props = defineProps({
|
|
|
15
15
|
});
|
|
16
16
|
const celContext = injectCELContext();
|
|
17
17
|
const eventChannel = useEventChannel();
|
|
18
|
+
const triggerCel = (expression, context) => $cel(expression, { ...celBindings(celContext), ...context });
|
|
18
19
|
const rawValue = computed(() => props.ctx.cell.getValue());
|
|
19
20
|
const isMissing = computed(() => rawValue.value === void 0 || rawValue.value === null);
|
|
20
21
|
const checked = computed(() => rawValue.value === true);
|
|
@@ -54,7 +55,7 @@ async function onUpdate(next) {
|
|
|
54
55
|
console.error("[shwfed-table] switch successMessage failed", e);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
|
-
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers));
|
|
58
|
+
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers, triggerCel));
|
|
58
59
|
} catch (e) {
|
|
59
60
|
console.error("[shwfed-table] switch onChange failed", e);
|
|
60
61
|
toast.error("\u8BF7\u6C42\u5931\u8D25");
|
|
@@ -15,6 +15,7 @@ const props = defineProps({
|
|
|
15
15
|
});
|
|
16
16
|
const celContext = injectCELContext();
|
|
17
17
|
const eventChannel = useEventChannel();
|
|
18
|
+
const triggerCel = (expression, context) => $cel(expression, { ...celBindings(celContext), ...context });
|
|
18
19
|
const rawValue = computed(() => props.ctx.cell.getValue());
|
|
19
20
|
const isMissing = computed(() => rawValue.value === void 0 || rawValue.value === null);
|
|
20
21
|
const checked = computed(() => rawValue.value === true);
|
|
@@ -54,7 +55,7 @@ async function onUpdate(next) {
|
|
|
54
55
|
console.error("[shwfed-table] switch successMessage failed", e);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
|
-
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers));
|
|
58
|
+
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers, triggerCel));
|
|
58
59
|
} catch (e) {
|
|
59
60
|
console.error("[shwfed-table] switch onChange failed", e);
|
|
60
61
|
toast.error("\u8BF7\u6C42\u5931\u8D25");
|
|
@@ -204,7 +204,7 @@ async function submit(next) {
|
|
|
204
204
|
console.error("[shwfed-table] combobox-single.remote.options-remote successMessage failed", e);
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
|
-
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers));
|
|
207
|
+
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers, $cel));
|
|
208
208
|
} catch (e) {
|
|
209
209
|
console.error("[shwfed-table] combobox-single.remote.options-remote onChange failed", e);
|
|
210
210
|
toast.error("\u8BF7\u6C42\u5931\u8D25");
|
|
@@ -126,7 +126,7 @@ async function submit(next) {
|
|
|
126
126
|
console.error("[shwfed-table] combobox-single.remote.options-static successMessage failed", e);
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
-
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers));
|
|
129
|
+
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers, $cel));
|
|
130
130
|
} catch (e) {
|
|
131
131
|
console.error("[shwfed-table] combobox-single.remote.options-static onChange failed", e);
|
|
132
132
|
toast.error("\u8BF7\u6C42\u5931\u8D25");
|
|
@@ -218,7 +218,7 @@ async function submit(next) {
|
|
|
218
218
|
console.error("[shwfed-table] combobox-multi.remote.options-remote successMessage failed", e);
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
|
-
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers));
|
|
221
|
+
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers, $cel));
|
|
222
222
|
} catch (e) {
|
|
223
223
|
console.error("[shwfed-table] combobox-multi.remote.options-remote onChange failed", e);
|
|
224
224
|
toast.error("\u8BF7\u6C42\u5931\u8D25");
|
|
@@ -140,7 +140,7 @@ async function submit(next) {
|
|
|
140
140
|
console.error("[shwfed-table] combobox-multi.remote.options-static successMessage failed", e);
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
|
-
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers));
|
|
143
|
+
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers, $cel));
|
|
144
144
|
} catch (e) {
|
|
145
145
|
console.error("[shwfed-table] combobox-multi.remote.options-static onChange failed", e);
|
|
146
146
|
toast.error("\u8BF7\u6C42\u5931\u8D25");
|
|
@@ -253,7 +253,7 @@ async function submit(next) {
|
|
|
253
253
|
console.error("[shwfed-table] combobox-multi.remote successMessage failed", e);
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
|
-
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers));
|
|
256
|
+
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers, $cel));
|
|
257
257
|
} catch (e) {
|
|
258
258
|
console.error("[shwfed-table] combobox-multi.remote onChange failed", e);
|
|
259
259
|
toast.error("\u8BF7\u6C42\u5931\u8D25");
|
|
@@ -233,7 +233,7 @@ async function submit(next) {
|
|
|
233
233
|
console.error("[shwfed-table] combobox-single.remote successMessage failed", e);
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
|
-
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers));
|
|
236
|
+
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers, $cel));
|
|
237
237
|
} catch (e) {
|
|
238
238
|
console.error("[shwfed-table] combobox-single.remote onChange failed", e);
|
|
239
239
|
toast.error("\u8BF7\u6C42\u5931\u8D25");
|
|
@@ -292,7 +292,7 @@ async function submit(next) {
|
|
|
292
292
|
console.error("[shwfed-table] combobox-multi successMessage failed", e);
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
|
-
await Effect.runPromise(dispatchTriggers(eventChannel, triggers));
|
|
295
|
+
await Effect.runPromise(dispatchTriggers(eventChannel, triggers, $cel));
|
|
296
296
|
} catch (e) {
|
|
297
297
|
console.error("[shwfed-table] combobox-multi onChange failed", e);
|
|
298
298
|
toast.error("\u8BF7\u6C42\u5931\u8D25");
|
|
@@ -271,7 +271,7 @@ async function submit(next) {
|
|
|
271
271
|
console.error("[shwfed-table] combobox-single successMessage failed", e);
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
|
-
await Effect.runPromise(dispatchTriggers(eventChannel, triggers));
|
|
274
|
+
await Effect.runPromise(dispatchTriggers(eventChannel, triggers, $cel));
|
|
275
275
|
} catch (e) {
|
|
276
276
|
console.error("[shwfed-table] combobox-single onChange failed", e);
|
|
277
277
|
toast.error("\u8BF7\u6C42\u5931\u8D25");
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Effect, Schema } from 'effect';
|
|
2
2
|
import type { InjectionKey, Ref } from 'vue';
|
|
3
|
+
import type { CelEvaluator } from './interpolate.js';
|
|
3
4
|
/** A descendant button asking an ancestor instance to run an operation. */
|
|
4
5
|
export type OpRequest = Readonly<{
|
|
5
6
|
/** Instance id of the ancestor that should handle this request. */
|
|
@@ -13,6 +14,17 @@ export type OpRequest = Readonly<{
|
|
|
13
14
|
* CEL/locale in them resolves, is the receiving operation's own concern.
|
|
14
15
|
*/
|
|
15
16
|
params?: unknown;
|
|
17
|
+
/**
|
|
18
|
+
* The *emit-site* CEL evaluator — the dispatching button's own context, where
|
|
19
|
+
* descendant bindings like a table row's `row` are live. Params are authored
|
|
20
|
+
* on the trigger row that sits on the button, so any CEL in them reads most
|
|
21
|
+
* naturally in this scope, not the handling ancestor's. Carried (not the
|
|
22
|
+
* pre-rendered string) so interpolation stays in the receiving operation,
|
|
23
|
+
* which alone knows which param fields are CEL — the bus stays param-opaque.
|
|
24
|
+
* Absent when the emitter has no CEL context; the handler then falls back to
|
|
25
|
+
* its host's own evaluator.
|
|
26
|
+
*/
|
|
27
|
+
cel?: CelEvaluator;
|
|
16
28
|
}>;
|
|
17
29
|
/**
|
|
18
30
|
* The persisted form of a trigger — one stored row addressing an ancestor
|
|
@@ -79,7 +91,7 @@ export type EventChannel = Readonly<{
|
|
|
79
91
|
* pre-request trigger list, so validation is explicit and configured rather
|
|
80
92
|
* than an implicit host gate.
|
|
81
93
|
*/
|
|
82
|
-
export type OperationHandlers = Readonly<Record<string, (params?: unknown) => Effect.Effect<void>>>;
|
|
94
|
+
export type OperationHandlers = Readonly<Record<string, (params?: unknown, cel?: CelEvaluator) => Effect.Effect<void>>>;
|
|
83
95
|
export declare const EVENT_CHANNEL_KEY: InjectionKey<EventChannel>;
|
|
84
96
|
/**
|
|
85
97
|
* Build a channel node for an operation-exposing instance. `dispatch` runs
|
|
@@ -109,8 +121,14 @@ export declare function useEventChannel(): EventChannel | undefined;
|
|
|
109
121
|
*
|
|
110
122
|
* A button that fires triggers composes this into its runtime effect at the
|
|
111
123
|
* lifecycle point the triggers should fire — the bus no longer runs them.
|
|
124
|
+
*
|
|
125
|
+
* `cel` is the emitter's own evaluator, stamped onto every request so a
|
|
126
|
+
* parameterized operation can resolve CEL in its params against the *emit-site*
|
|
127
|
+
* scope (e.g. a table row's `row`) rather than the handling ancestor's. Pass it
|
|
128
|
+
* whenever the emitter has a CEL context; omit it and ops fall back to their
|
|
129
|
+
* host evaluator. See `OpRequest.cel`.
|
|
112
130
|
*/
|
|
113
|
-
export declare function dispatchTriggers(channel: EventChannel | undefined, triggers: ReadonlyArray<TriggerValue> | undefined): Effect.Effect<void>;
|
|
131
|
+
export declare function dispatchTriggers(channel: EventChannel | undefined, triggers: ReadonlyArray<TriggerValue> | undefined, cel?: CelEvaluator): Effect.Effect<void>;
|
|
114
132
|
/**
|
|
115
133
|
* One operation a component type exposes. `id` is what the trigger stores;
|
|
116
134
|
* `name`/`icon` are display only.
|
|
@@ -33,7 +33,7 @@ export const EVENT_CHANNEL_KEY = Symbol("shwfed/event-channel");
|
|
|
33
33
|
export function createEventChannel(instanceId, operations, parent) {
|
|
34
34
|
return {
|
|
35
35
|
dispatch(request) {
|
|
36
|
-
const handled = request.target === instanceId ? operations[request.operation]?.(request.params) ?? Effect.void : Effect.void;
|
|
36
|
+
const handled = request.target === instanceId ? operations[request.operation]?.(request.params, request.cel) ?? Effect.void : Effect.void;
|
|
37
37
|
return parent ? Effect.andThen(handled, parent.dispatch(request)) : handled;
|
|
38
38
|
}
|
|
39
39
|
};
|
|
@@ -45,14 +45,15 @@ export function provideEventTarget(instanceId, operations) {
|
|
|
45
45
|
export function useEventChannel() {
|
|
46
46
|
return inject(EVENT_CHANNEL_KEY, void 0);
|
|
47
47
|
}
|
|
48
|
-
export function dispatchTriggers(channel, triggers) {
|
|
48
|
+
export function dispatchTriggers(channel, triggers, cel) {
|
|
49
49
|
if (!channel || !triggers || triggers.length === 0) return Effect.void;
|
|
50
50
|
return Effect.forEach(
|
|
51
51
|
triggers,
|
|
52
52
|
(trigger) => channel.dispatch({
|
|
53
53
|
target: trigger.target,
|
|
54
54
|
operation: trigger.operation,
|
|
55
|
-
params: trigger.params
|
|
55
|
+
params: trigger.params,
|
|
56
|
+
cel
|
|
56
57
|
}),
|
|
57
58
|
{ discard: true }
|
|
58
59
|
);
|
|
@@ -26,7 +26,7 @@ The custom `Optional` class has been replaced with Effect's `Option` type (`impo
|
|
|
26
26
|
|
|
27
27
|
An `http` built-in has been added (`http-builtins.ts`, `http-builder.ts`) — not from upstream. It registers the `http` constant and the `http` / `HttpRequest` types on `globalRegistry`, so `http.get(url).header(...).body(...)` expressions type-check and evaluate in every `Environment`. A CEL expression only builds an `HttpRequestBuilder` — a pure description of a request; it never issues one. Both terminal methods, `.json()` and `.file()`, are dispatched by the host on the returned builder (neither is a CEL method), so expression evaluation stays free of IO. Endpoints must be absolute URLs — there is no base-URL resolution. Depends on `fx-fetch`. The host can register process-wide default headers via `HttpRequestBuilder.setDefaultHeader(name, valueOrGetter)` / `clearDefaultHeader(name)`; they are merged in at `#buildRequest()` time, with case-insensitive precedence to an explicit `.header(...)` on the builder. A getter that returns `null` / `undefined` / `''` skips the header for that request, so the host can source values from live refs (e.g. the active i18n locale for `Accept-Language`) without baking in stale snapshots.
|
|
28
28
|
|
|
29
|
-
A `form(dyn): FormData` built-in has been added (`form-builtins.ts`) — not from upstream. It registers the `FormData` type on `globalRegistry` and a global `form` function that turns a CEL map into a native `FormData`, so authors can write `http.post(url).body(form({"file": myFile, "name": "Alice"}))` (`.body()`
|
|
29
|
+
A `form(dyn): FormData` built-in has been added (`form-builtins.ts`) — not from upstream. It registers the `FormData` type on `globalRegistry` and a global `form` function that turns a CEL map into a native `FormData`, so authors can write `http.post(url).body(form({"file": myFile, "name": "Alice"}))` (`.body()` passes `FormData` through verbatim; `#buildRequest()` serializes it). NOTE: `#buildRequest()` must serialize a `FormData` body itself rather than hand it to fx-fetch — fx-fetch clones bodies via `new Response(body).blob()`, and the File API ASCII-lowercases `Blob#type`, so in Chrome the multipart boundary in the derived `Content-Type` loses its case while the body bytes keep it (boundaries are case-sensitive → every parser sees zero parts). The builder lifts the case-preserved `Content-Type` off the `Response` *header* (header values are never case-normalized), adds it as an explicit header (unless the author set their own `Content-Type`), and passes the bytes as a `Promise<Blob>` (fx-fetch resolves promises verbatim). Value coercion mirrors `.query()`'s flat-record semantics: `null` / `undefined` / `Option.None` skip the key; `Option.Some(x)` recurses on `x`; `File` / `Blob` pass through (preserves the `File` filename); `Decimal` → `.toString()` (preserves precision past safe-integer); `Date` (`TZDate`) → `.toISOString()`; `bool` → `'true'` / `'false'`; arrays append one entry per element; nested objects throw (`multipart/form-data` is flat — same rule as `.query(dyn)`). Top-level input must be a map literal / record (Map or plain object); passing an array or scalar throws.
|
|
30
30
|
The `__proto__` / `constructor` / `prototype` keys are skipped on the top-level record, mirroring `safeFromEntries`.
|
|
31
31
|
|
|
32
32
|
A `JSON` built-in namespace has been added (`json-builtins.ts`) — not from upstream. It registers a `JSON` brand class and a same-named constant on `globalRegistry`, so `JSON.parse(string): dyn` and `JSON.stringify(dyn): string` resolve in every `Environment`. The CEL-side surface mirrors the JS globals deliberately. `JSON.parse` is a thin wrapper over `globalThis.JSON.parse` — numbers come back as plain JS `number` (the evaluator coerces via `Decimal.from` at use time). `JSON.stringify` pre-walks the value via `normalizeForJSON` before handing it to `globalThis.JSON.stringify`: `Decimal` → JS number (via `.toNumber()` — loses precision past safe-integer range, deliberate trade-off for idiomatic JSON output), `Option.Some(x)` → `x`, `Option.None` → `null`, `Map` → plain object, `Date`/`TZDate` passes through so its built-in `toJSON` emits ISO 8601. The pre-walk is required because Effect's `Option` defines its own `toJSON()` that runs before any replacer would see it.
|
|
@@ -198,8 +198,8 @@ http.post("https://api.example.com/files")
|
|
|
198
198
|
.body(form({"file": myFile, "name": "Alice", "tags": ["a", "b"]}))
|
|
199
199
|
```
|
|
200
200
|
`form(map): FormData` turns a flat record into a native `FormData`. Pass it
|
|
201
|
-
straight to `.body(...)` —
|
|
202
|
-
need a `Content-Type` header.
|
|
201
|
+
straight to `.body(...)` — the builder derives the multipart `Content-Type`
|
|
202
|
+
(boundary included), so you don't need a `Content-Type` header.
|
|
203
203
|
|
|
204
204
|
Value coercion is flat (same rules as `.query(...)`):
|
|
205
205
|
- `null` / `undefined` / absent `.?` values → key is skipped
|
|
@@ -134,6 +134,19 @@ export class HttpRequestBuilder {
|
|
|
134
134
|
const explicit = new Set(this.#headers.map(([k]) => k.toLowerCase()));
|
|
135
135
|
const headers = {};
|
|
136
136
|
for (const [k, v] of this.#headers) headers[k] = v;
|
|
137
|
+
if (this.#body !== void 0) {
|
|
138
|
+
if (this.#body instanceof FormData) {
|
|
139
|
+
const serialized = new Response(this.#body);
|
|
140
|
+
const contentType = serialized.headers.get("content-type");
|
|
141
|
+
if (contentType !== null && !explicit.has("content-type")) {
|
|
142
|
+
headers["Content-Type"] = contentType;
|
|
143
|
+
explicit.add("content-type");
|
|
144
|
+
}
|
|
145
|
+
parts.body = serialized.blob();
|
|
146
|
+
} else {
|
|
147
|
+
parts.body = this.#body;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
137
150
|
for (const [key, { name, get }] of HttpRequestBuilder.#defaultHeaders) {
|
|
138
151
|
if (explicit.has(key)) continue;
|
|
139
152
|
const v = get();
|
|
@@ -143,9 +156,6 @@ export class HttpRequestBuilder {
|
|
|
143
156
|
if (Object.keys(headers).length > 0) {
|
|
144
157
|
parts.headers = headers;
|
|
145
158
|
}
|
|
146
|
-
if (this.#body !== void 0) {
|
|
147
|
-
parts.body = this.#body;
|
|
148
|
-
}
|
|
149
159
|
let request = FxRequest.unsafeMake(parts);
|
|
150
160
|
for (const [k, v] of this.#queries) {
|
|
151
161
|
request = FxRequest.appendUrlSearchParam(request, k, v);
|