@shwfed/config 2.0.3 → 2.1.1

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 (92) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/config.d.vue.ts +16 -6
  3. package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/config.vue +263 -76
  4. package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/config.vue.d.ts +16 -6
  5. package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/runtime.d.vue.ts +16 -6
  6. package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/runtime.vue +104 -12
  7. package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/runtime.vue.d.ts +16 -6
  8. package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/schema.d.ts +51 -15
  9. package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/schema.js +67 -22
  10. package/dist/runtime/components/form/ai/fields-button.d.vue.ts +13 -0
  11. package/dist/runtime/components/form/ai/fields-button.vue +458 -0
  12. package/dist/runtime/components/form/ai/fields-button.vue.d.ts +13 -0
  13. package/dist/runtime/components/form/ai/fields-task.md +71 -0
  14. package/dist/runtime/components/form/config.d.vue.ts +1 -1
  15. package/dist/runtime/components/form/config.vue +4 -36
  16. package/dist/runtime/components/form/config.vue.d.ts +1 -1
  17. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.d.vue.ts +18 -18
  18. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.vue.d.ts +18 -18
  19. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetime/config.d.vue.ts +4 -4
  20. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetime/config.vue.d.ts +4 -4
  21. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.d.vue.ts +22 -22
  22. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.vue.d.ts +22 -22
  23. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.time/config.d.vue.ts +2 -2
  24. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.time/config.vue.d.ts +2 -2
  25. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.d.vue.ts +4 -4
  26. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.vue.d.ts +4 -4
  27. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.d.vue.ts +12 -12
  28. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.vue +87 -11
  29. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.vue.d.ts +12 -12
  30. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/runtime.vue +18 -6
  31. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/schema.d.ts +1 -1
  32. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/schema.js +11 -4
  33. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.switch/config.d.vue.ts +10 -10
  34. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.switch/config.vue.d.ts +10 -10
  35. package/dist/runtime/components/form/fields/2026-05-12/com.shwfed.form.field.upload/config.d.vue.ts +12 -12
  36. package/dist/runtime/components/form/fields/2026-05-12/com.shwfed.form.field.upload/config.vue.d.ts +12 -12
  37. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/config.d.vue.ts +2 -2
  38. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/config.vue +15 -0
  39. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/config.vue.d.ts +2 -2
  40. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.d.vue.ts +1 -0
  41. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.vue +13 -4
  42. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.vue.d.ts +1 -0
  43. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/runtime.vue +1 -0
  44. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/schema.d.ts +1 -1
  45. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/schema.js +5 -1
  46. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/config.d.vue.ts +131 -0
  47. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/config.vue +170 -0
  48. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/config.vue.d.ts +131 -0
  49. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/runtime.d.vue.ts +8 -0
  50. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/runtime.vue +52 -0
  51. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/runtime.vue.d.ts +8 -0
  52. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/schema.d.ts +112 -0
  53. package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/schema.js +44 -0
  54. package/dist/runtime/components/form/index.vue +5 -4
  55. package/dist/runtime/components/form/schema.d.ts +10 -0
  56. package/dist/runtime/components/form/schema.js +6 -2
  57. package/dist/runtime/components/form/unit-config.d.vue.ts +16 -0
  58. package/dist/runtime/components/form/unit-config.vue +30 -3
  59. package/dist/runtime/components/form/unit-config.vue.d.ts +16 -0
  60. package/dist/runtime/components/form/utils/cel-scope.d.ts +13 -0
  61. package/dist/runtime/components/form/utils/cel-scope.js +32 -0
  62. package/dist/runtime/components/form/utils/schema-meta.d.ts +13 -0
  63. package/dist/runtime/components/form/utils/schema-meta.js +15 -0
  64. package/dist/runtime/components/table/ai/columns-task.md +10 -1
  65. package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.markdown/config.vue +2 -2
  66. package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.markdown/runtime.vue +14 -4
  67. package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.markdown/schema.js +3 -2
  68. package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.text/config.vue +2 -2
  69. package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.text/runtime.vue +14 -4
  70. package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.text/schema.js +3 -2
  71. package/dist/runtime/components/table/config.d.vue.ts +11 -1
  72. package/dist/runtime/components/table/config.vue +4 -0
  73. package/dist/runtime/components/table/config.vue.d.ts +11 -1
  74. package/dist/runtime/components/table/index.d.vue.ts +4 -0
  75. package/dist/runtime/components/table/index.vue +28 -2
  76. package/dist/runtime/components/table/index.vue.d.ts +4 -0
  77. package/dist/runtime/components/table/schema.d.ts +12 -0
  78. package/dist/runtime/components/table/schema.js +6 -1
  79. package/dist/runtime/components/table/utils/shared.d.ts +2 -1
  80. package/dist/runtime/components/ui/date-range-picker/DateRangePickerDateTimePanel.d.vue.ts +1 -1
  81. package/dist/runtime/components/ui/date-range-picker/DateRangePickerDateTimePanel.vue.d.ts +1 -1
  82. package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.d.vue.ts +1 -1
  83. package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.vue.d.ts +1 -1
  84. package/dist/runtime/share/expression.d.ts +1 -2
  85. package/dist/runtime/share/slot-renderer.vue +7 -6
  86. package/dist/runtime/vendor/cel-js/CLAUDE.md +1 -1
  87. package/dist/runtime/vendor/cel-js/PROMPT.md +12 -3
  88. package/dist/runtime/vendor/cel-js/lib/http-builder.d.ts +10 -3
  89. package/dist/runtime/vendor/cel-js/lib/http-builder.js +28 -5
  90. package/dist/runtime/vendor/cel-js/lib/http-builtins.d.ts +4 -3
  91. package/dist/runtime/vendor/cel-js/lib/http-builtins.js +4 -0
  92. package/package.json +1 -1
@@ -23,6 +23,18 @@ export declare const metadata: {
23
23
  readonly id: "clear-selection";
24
24
  readonly name: "清空所有选中行";
25
25
  readonly icon: "fluent:select-all-off-20-regular";
26
+ }, {
27
+ readonly id: "add-row";
28
+ readonly name: "新增一行";
29
+ readonly icon: "fluent:add-20-regular";
30
+ }, {
31
+ readonly id: "duplicate-selected";
32
+ readonly name: "复制选中行";
33
+ readonly icon: "fluent:copy-20-regular";
34
+ }, {
35
+ readonly id: "delete-selected";
36
+ readonly name: "删除选中行";
37
+ readonly icon: "fluent:delete-20-regular";
26
38
  }];
27
39
  };
28
40
  declare const InitialState: Schema.Struct<{
@@ -13,11 +13,16 @@ export const metadata = {
13
13
  // Atomic operations — compose them on one button (ordered triggers) to get
14
14
  // the legacy combined behaviours. Only `search` actually fetches data;
15
15
  // `reset-query` / `reset-pagination` / `clear-selection` just prepare state.
16
+ // The `*-row` / `*-selected` trio edits the row list locally — no server
17
+ // round-trip — for tables wired up as an editable surface.
16
18
  operations: [
17
19
  { id: "reset-query", name: "\u91CD\u7F6E\u641C\u7D22\u6761\u4EF6", icon: "fluent:arrow-reset-20-regular" },
18
20
  { id: "reset-pagination", name: "\u91CD\u7F6E\u5206\u9875\u5E76\u6267\u884C\u641C\u7D22", icon: "fluent:arrow-previous-20-regular" },
19
21
  { id: "search", name: "\u6267\u884C\u641C\u7D22", icon: "fluent:search-20-regular" },
20
- { id: "clear-selection", name: "\u6E05\u7A7A\u6240\u6709\u9009\u4E2D\u884C", icon: "fluent:select-all-off-20-regular" }
22
+ { id: "clear-selection", name: "\u6E05\u7A7A\u6240\u6709\u9009\u4E2D\u884C", icon: "fluent:select-all-off-20-regular" },
23
+ { id: "add-row", name: "\u65B0\u589E\u4E00\u884C", icon: "fluent:add-20-regular" },
24
+ { id: "duplicate-selected", name: "\u590D\u5236\u9009\u4E2D\u884C", icon: "fluent:copy-20-regular" },
25
+ { id: "delete-selected", name: "\u5220\u9664\u9009\u4E2D\u884C", icon: "fluent:delete-20-regular" }
21
26
  ]
22
27
  };
23
28
  const SortItem = Schema.Struct({
@@ -1,5 +1,6 @@
1
1
  import { Schema } from 'effect';
2
2
  import type { Environment } from '../../../vendor/cel-js/lib/index.js';
3
+ import { type ResultType } from '../../../share/expression.js';
3
4
  export declare const Align: Schema.Literal<["left", "center", "right"]>;
4
5
  export declare function columnIdentityFields(): {
5
6
  id: Schema.refine<string, typeof Schema.String>;
@@ -29,7 +30,7 @@ export declare function columnFields(configure: (env: Environment) => void): {
29
30
  };
30
31
  export declare function registerRowVariablesIfAbsent(env: Environment): void;
31
32
  export declare function CelRowAccess(configure: (env: Environment) => void, options?: {
32
- resultType?: string | string[];
33
+ resultType?: ResultType;
33
34
  }): Schema.Schema<string, string, never>;
34
35
  export declare function LocaleMarkdownWithRow(configure: (env: Environment) => void): Schema.filter<Schema.TupleType<readonly [Schema.Struct<{
35
36
  locale: Schema.Literal<["zh"]>;
@@ -14,10 +14,10 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
14
14
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
15
15
  "onUpdate:modelValue"?: ((args_0: DateRange | undefined) => any) | undefined;
16
16
  }>, {
17
+ rangeSeparatorIcon: string;
17
18
  numberOfMonths: number;
18
19
  granularity: "hour" | "minute" | "second";
19
20
  hourCycle: 12 | 24;
20
- rangeSeparatorIcon: string;
21
21
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
22
22
  declare const _default: typeof __VLS_export;
23
23
  export default _default;
@@ -14,10 +14,10 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
14
14
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
15
15
  "onUpdate:modelValue"?: ((args_0: DateRange | undefined) => any) | undefined;
16
16
  }>, {
17
+ rangeSeparatorIcon: string;
17
18
  numberOfMonths: number;
18
19
  granularity: "hour" | "minute" | "second";
19
20
  hourCycle: 12 | 24;
20
- rangeSeparatorIcon: string;
21
21
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
22
22
  declare const _default: typeof __VLS_export;
23
23
  export default _default;
@@ -24,11 +24,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
24
24
  "onUpdate:endValue"?: ((args_0: Time | undefined) => any) | undefined;
25
25
  }>, {
26
26
  size: "sm" | "md" | "lg";
27
+ rangeSeparatorIcon: string;
27
28
  granularity: "hour" | "minute" | "second";
28
29
  hourCycle: 12 | 24;
29
30
  clearable: boolean;
30
31
  clearIcon: string;
31
- rangeSeparatorIcon: string;
32
32
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
33
33
  declare const _default: typeof __VLS_export;
34
34
  export default _default;
@@ -24,11 +24,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
24
24
  "onUpdate:endValue"?: ((args_0: Time | undefined) => any) | undefined;
25
25
  }>, {
26
26
  size: "sm" | "md" | "lg";
27
+ rangeSeparatorIcon: string;
27
28
  granularity: "hour" | "minute" | "second";
28
29
  hourCycle: 12 | 24;
29
30
  clearable: boolean;
30
31
  clearIcon: string;
31
- rangeSeparatorIcon: string;
32
32
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
33
33
  declare const _default: typeof __VLS_export;
34
34
  export default _default;
@@ -1,7 +1,7 @@
1
1
  import { Schema } from 'effect';
2
2
  import { Environment } from '../vendor/cel-js/lib/index.js';
3
3
  import { Locale } from './locale.js';
4
- type ResultType = string | string[] | ((type: string) => boolean);
4
+ export type ResultType = string | string[] | ((type: string) => boolean);
5
5
  export declare function Expression(options: {
6
6
  configure: (env: Environment) => void;
7
7
  resultType?: ResultType;
@@ -19,4 +19,3 @@ export declare function Markdown(options: {
19
19
  export declare function LocaleMarkdown(options: {
20
20
  configure: (env: Environment) => void;
21
21
  }): Schema.filter<typeof Locale>;
22
- export {};
@@ -35,14 +35,15 @@ watch(() => props.slotValue.layouts, (sets) => {
35
35
  const gridStyle = computed(() => {
36
36
  const l = activeLayout.value?.layout;
37
37
  if (!l) return "";
38
- const tracks = (n) => `repeat(${n}, minmax(0, 1fr))`;
38
+ const tracks = (n, min) => `repeat(${n}, minmax(${min}, 1fr))`;
39
+ const gap = `calc(${l.gap ?? DEFAULT_GAP} * 0.25rem)`;
40
+ const colGap = l.columns > 1 ? `min(${gap}, calc(100% / ${l.columns - 1}))` : gap;
39
41
  const parts = [
40
42
  "display: grid",
41
- `grid-template-columns: ${tracks(l.columns)}`,
42
- l.rows ? `grid-template-rows: ${tracks(l.rows)}` : "",
43
- // Missing `gap` resolves to `DEFAULT_GAP` — the layout editor's fallback —
44
- // so the editor and the rendered slot agree. `gap: 0` stays 0.
45
- `gap: calc(${l.gap ?? DEFAULT_GAP} * 0.25rem)`,
43
+ `grid-template-columns: ${tracks(l.columns, "0")}`,
44
+ l.rows ? `grid-template-rows: ${tracks(l.rows, "auto")}` : "",
45
+ `column-gap: ${colGap}`,
46
+ `row-gap: ${gap}`,
46
47
  l.style ?? ""
47
48
  ].filter(Boolean);
48
49
  return parts.join("; ");
@@ -20,7 +20,7 @@ The `homogeneousAggregateLiterals` and `enableOptionalTypes` environment options
20
20
 
21
21
  The custom `Optional` class has been replaced with Effect's `Option` type (`import { Option } from 'effect'`). Internal helpers `optionalOf(value)` and `optionalValue(opt)` in `optional.js` handle the CEL-specific semantics (treating `undefined` as `None`, throwing `EvaluationError` on missing value access).
22
22
 
23
- 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 *describes* a request; it evaluates to an `HttpRequestBuilder`, and the host dispatches it via `.json()` / `.file()`. Endpoints must be absolute URLs — there is no base-URL resolution. Depends on `fx-fetch`.
23
+ 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 builds an `HttpRequestBuilder`; the terminal `HttpRequest.file(): File` method issues the request and evaluates to the downloaded `File`, while `.json()` is dispatched by the host on the builder value (it is not a CEL method). Endpoints must be absolute URLs — there is no base-URL resolution. Depends on `fx-fetch`.
24
24
 
25
25
  ## Architecture
26
26
 
@@ -197,7 +197,7 @@ location.param("id") // first value when a param repeats
197
197
 
198
198
  ## HTTP
199
199
 
200
- Build HTTP requests using the `http` builder. CEL expressions only *describe* a request the host dispatches it. There is no way to send a request or read a response from CEL.
200
+ Build HTTP requests using the `http` builder. A CEL expression *describes* a request; calling `.file()` on it issues the request and evaluates to the downloaded `File`. Any other request is handed to the host to dispatch there is no way to read a JSON or text response from CEL.
201
201
 
202
202
  **URLs must be absolute** — pass a full `https://…` URL. There is no base URL, so relative paths like `/api/users` will not resolve.
203
203
 
@@ -229,12 +229,21 @@ http.get("https://api.example.com/users")
229
229
  .query(form)
230
230
  .query("page", string(pageIndex))
231
231
 
232
- // Set request body (auto-serializes to JSON):
232
+ // Set request body. An object serializes to JSON; set the Content-Type
233
+ // header yourself. Optional values are unwrapped — a present `.?` field
234
+ // keeps its value, an absent one becomes `null` (the key is kept):
233
235
  http.post("https://api.example.com/users")
234
- .body({"name": "Alice", "email": "alice@example.com"})
236
+ .header("Content-Type", "application/json")
237
+ .body({"name": "Alice", "channel": query.?channel})
238
+
239
+ // Download the response as a File. Unlike the chainable methods above,
240
+ // .file() is terminal — it issues the request and the expression evaluates
241
+ // to a File (filename from the Content-Disposition header, else the URL path):
242
+ http.get("https://api.example.com/files/report.pdf").file()
235
243
 
236
244
  // Full example — the expression returns an HttpRequest; the host sends it:
237
245
  http.post("https://api.example.com/users")
246
+ .header("Content-Type", "application/json")
238
247
  .header("Authorization", "Bearer " + token)
239
248
  .body({"name": "Alice"})
240
249
  ```
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * `HttpRequestBuilder` — the value an `http.*` CEL expression evaluates to.
3
3
  *
4
- * A local (non-upstream) extension. CEL only *describes* a request; the host
5
- * dispatches it by calling `.json()` / `.file()` on the returned builder.
4
+ * A local (non-upstream) extension. A CEL expression builds and *describes* a
5
+ * request; `.file()` is exposed as a CEL method and evaluates to the downloaded
6
+ * `File`, while `.json()` is dispatched by the host on the returned builder.
6
7
  *
7
8
  * Endpoints are used verbatim — there is no base-URL resolution, so callers
8
9
  * must pass absolute URLs.
@@ -15,7 +16,13 @@ export declare class HttpRequestBuilder {
15
16
  header(name: string, value: string): this;
16
17
  query(key: string, value: string | number): this;
17
18
  query(record: Record<string, unknown>): this;
18
- body(data: FormData | unknown): this;
19
+ /**
20
+ * Set the request body. A pre-built `FormData` / `Blob` / `ArrayBuffer` /
21
+ * string is used verbatim; anything else is normalized (see
22
+ * `normalizeBody`) and JSON-serialized. The `Content-Type` header is *not*
23
+ * set here — the caller adds `.header('Content-Type', …)` explicitly.
24
+ */
25
+ body(data: unknown): this;
19
26
  /**
20
27
  * A structural snapshot of the request this builder will issue — an equal
21
28
  * snapshot means an equal HTTP request. Handy for change-detection, e.g.
@@ -1,9 +1,28 @@
1
- import { Effect } from "effect";
1
+ import { Effect, Option } from "effect";
2
2
  import {
3
3
  Fetch,
4
4
  Request as FxRequest,
5
5
  Response as FxResponse
6
6
  } from "fx-fetch";
7
+ import { Decimal } from "./decimal.js";
8
+ function normalizeBody(value) {
9
+ if (value === null || value === void 0) return value;
10
+ if (value instanceof Decimal) return value.toNumber();
11
+ if (Option.isOption(value)) {
12
+ return Option.isSome(value) ? normalizeBody(value.value) : null;
13
+ }
14
+ if (Array.isArray(value)) return value.map(normalizeBody);
15
+ if (typeof value === "object") {
16
+ const proto = Object.getPrototypeOf(value);
17
+ if (proto !== Object.prototype && proto !== null) return value;
18
+ const out = {};
19
+ for (const key of Object.keys(value)) {
20
+ out[key] = normalizeBody(value[key]);
21
+ }
22
+ return out;
23
+ }
24
+ return value;
25
+ }
7
26
  function extractFilename(response, url) {
8
27
  const disposition = FxResponse.getHeader(response, "content-disposition");
9
28
  if (disposition) {
@@ -53,11 +72,15 @@ export class HttpRequestBuilder {
53
72
  }
54
73
  throw new Error(`query: nested object values are not supported (key: "${key}")`);
55
74
  }
75
+ /**
76
+ * Set the request body. A pre-built `FormData` / `Blob` / `ArrayBuffer` /
77
+ * string is used verbatim; anything else is normalized (see
78
+ * `normalizeBody`) and JSON-serialized. The `Content-Type` header is *not*
79
+ * set here — the caller adds `.header('Content-Type', …)` explicitly.
80
+ */
56
81
  body(data) {
57
- this.#body = data instanceof FormData || data instanceof Blob || data instanceof ArrayBuffer || typeof data === "string" ? data : JSON.stringify(data);
58
- if (!(data instanceof FormData || data instanceof Blob || data instanceof ArrayBuffer || typeof data === "string")) {
59
- this.#headers.push(["Content-Type", "application/json"]);
60
- }
82
+ const value = normalizeBody(data);
83
+ this.#body = value instanceof FormData || value instanceof Blob || value instanceof ArrayBuffer || typeof value === "string" ? value : JSON.stringify(value);
61
84
  return this;
62
85
  }
63
86
  /**
@@ -2,9 +2,10 @@
2
2
  * HTTP built-ins for CEL — a local (non-upstream) extension.
3
3
  *
4
4
  * Registers the `http` / `HttpRequest` types, the `http` constant, and the
5
- * request-builder functions on the registry. A CEL expression only *describes*
6
- * a request `http.get(url).header(...).body(...)` evaluates to an
7
- * `HttpRequestBuilder`; the host dispatches it via `.json()` / `.file()`.
5
+ * request-builder functions on the registry. `http.get(url).header(...).body(...)`
6
+ * builds an `HttpRequestBuilder`; the terminal `.file()` method issues the
7
+ * request and evaluates to the downloaded `File`. `.json()` is *not* a CEL
8
+ * method — the host dispatches that on the returned builder value.
8
9
  *
9
10
  * Wired into `globalRegistry` at module load (see `evaluator.ts`), so every
10
11
  * `Environment` gets HTTP for free — both type-checking and evaluation.
@@ -52,4 +52,8 @@ export function registerHttpBuiltins(registry) {
52
52
  "HttpRequest.body(dyn): HttpRequest",
53
53
  (b, data) => b.body(data)
54
54
  );
55
+ registry.registerFunctionOverload(
56
+ "HttpRequest.file(): File",
57
+ (b) => b.file()
58
+ );
55
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shwfed/config",
3
- "version": "2.0.3",
3
+ "version": "2.1.1",
4
4
  "description": "Configurable UI for SHWFED",
5
5
  "type": "module",
6
6
  "publishConfig": {