@shwfed/config 2.1.2 → 2.2.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 (51) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.form/config.d.vue.ts +8 -2
  3. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.form/config.vue.d.ts +8 -2
  4. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.form/runtime.d.vue.ts +8 -2
  5. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.form/runtime.vue.d.ts +8 -2
  6. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.form/schema.d.ts +8 -2
  7. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.d.vue.ts +8 -2
  8. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.vue.d.ts +8 -2
  9. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.d.vue.ts +8 -2
  10. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.vue.d.ts +8 -2
  11. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/schema.d.ts +12 -3
  12. package/dist/runtime/components/form/config.vue +36 -5
  13. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/config.d.vue.ts +2 -0
  14. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/config.vue +31 -0
  15. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/config.vue.d.ts +2 -0
  16. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/runtime.vue +12 -2
  17. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/schema.d.ts +1 -0
  18. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/schema.js +4 -0
  19. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.d.vue.ts +2 -0
  20. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.vue +31 -0
  21. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.vue.d.ts +2 -0
  22. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/runtime.vue +16 -8
  23. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/schema.d.ts +1 -0
  24. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/schema.js +4 -0
  25. package/dist/runtime/components/form/fields/2026-05-12/com.shwfed.form.field.upload/config.d.vue.ts +8 -2
  26. package/dist/runtime/components/form/fields/2026-05-12/com.shwfed.form.field.upload/config.vue +51 -15
  27. package/dist/runtime/components/form/fields/2026-05-12/com.shwfed.form.field.upload/config.vue.d.ts +8 -2
  28. package/dist/runtime/components/form/fields/2026-05-12/com.shwfed.form.field.upload/runtime.vue +16 -5
  29. package/dist/runtime/components/form/fields/2026-05-12/com.shwfed.form.field.upload/schema.d.ts +9 -1
  30. package/dist/runtime/components/form/fields/2026-05-12/com.shwfed.form.field.upload/schema.js +25 -4
  31. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.combobox.single.remote/config.d.vue.ts +2 -2
  32. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.combobox.single.remote/config.vue.d.ts +2 -2
  33. package/dist/runtime/components/form/index.vue +4 -1
  34. package/dist/runtime/components/form/schema.d.ts +14 -4
  35. package/dist/runtime/components/form/schema.js +30 -10
  36. package/dist/runtime/components/form/utils/initial.d.ts +26 -0
  37. package/dist/runtime/components/form/utils/initial.js +11 -0
  38. package/dist/runtime/components/table/index.vue +3 -1
  39. package/dist/runtime/components/table/schema.d.ts +16 -4
  40. package/dist/runtime/plugins/i18n/index.js +8 -2
  41. package/dist/runtime/vendor/cel-js/CLAUDE.md +3 -1
  42. package/dist/runtime/vendor/cel-js/PROMPT.md +23 -7
  43. package/dist/runtime/vendor/cel-js/lib/http-builder.d.ts +6 -3
  44. package/dist/runtime/vendor/cel-js/lib/http-builder.js +24 -3
  45. package/dist/runtime/vendor/cel-js/lib/http-builtins.d.ts +3 -3
  46. package/dist/runtime/vendor/cel-js/lib/http-builtins.js +0 -4
  47. package/dist/runtime/vendor/cel-js/lib/operators.js +139 -11
  48. package/dist/runtime/vendor/cel-js/lib/parser.d.ts +2 -1
  49. package/dist/runtime/vendor/cel-js/lib/parser.js +20 -3
  50. package/dist/runtime/vendor/cel-js/lib/serialize.js +5 -1
  51. package/package.json +1 -1
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * `HttpRequestBuilder` — the value an `http.*` CEL expression evaluates to.
3
3
  *
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.
4
+ * A local (non-upstream) extension. A CEL expression only *describes* a
5
+ * request it never issues one. Both terminal methods, `.json()` and
6
+ * `.file()`, are dispatched by the host on the returned builder, keeping
7
+ * expression evaluation free of IO.
7
8
  *
8
9
  * Endpoints are used verbatim — there is no base-URL resolution, so callers
9
10
  * must pass absolute URLs.
@@ -12,6 +13,8 @@ import { Effect } from 'effect';
12
13
  import { Fetch } from 'fx-fetch';
13
14
  export declare class HttpRequestBuilder {
14
15
  #private;
16
+ static setDefaultHeader(name: string, value: string | (() => string | null | undefined)): void;
17
+ static clearDefaultHeader(name: string): void;
15
18
  constructor(url: string, method: string);
16
19
  header(name: string, value: string): this;
17
20
  query(key: string, value: string | number): this;
@@ -38,6 +38,20 @@ export class HttpRequestBuilder {
38
38
  #headers = [];
39
39
  #queries = [];
40
40
  #body;
41
+ // Process-wide defaults applied at `#buildRequest()` time. Each entry is
42
+ // keyed by lowercased header name so an explicit `.header('X', …)` on the
43
+ // builder wins regardless of casing. A getter returning `null` / `undefined`
44
+ // / `''` skips the header for that request — lets the host source values
45
+ // from a live ref (e.g. the current i18n locale) without baking in stale
46
+ // snapshots.
47
+ static #defaultHeaders = /* @__PURE__ */ new Map();
48
+ static setDefaultHeader(name, value) {
49
+ const get = typeof value === "function" ? value : () => value;
50
+ HttpRequestBuilder.#defaultHeaders.set(name.toLowerCase(), { name, get });
51
+ }
52
+ static clearDefaultHeader(name) {
53
+ HttpRequestBuilder.#defaultHeaders.delete(name.toLowerCase());
54
+ }
41
55
  constructor(url, method) {
42
56
  this.#url = url;
43
57
  this.#method = method;
@@ -117,9 +131,16 @@ export class HttpRequestBuilder {
117
131
  url: this.#url,
118
132
  method: this.#method
119
133
  };
120
- if (this.#headers.length > 0) {
121
- const headers = {};
122
- for (const [k, v] of this.#headers) headers[k] = v;
134
+ const explicit = new Set(this.#headers.map(([k]) => k.toLowerCase()));
135
+ const headers = {};
136
+ for (const [k, v] of this.#headers) headers[k] = v;
137
+ for (const [key, { name, get }] of HttpRequestBuilder.#defaultHeaders) {
138
+ if (explicit.has(key)) continue;
139
+ const v = get();
140
+ if (v == null || v === "") continue;
141
+ headers[name] = v;
142
+ }
143
+ if (Object.keys(headers).length > 0) {
123
144
  parts.headers = headers;
124
145
  }
125
146
  if (this.#body !== void 0) {
@@ -3,9 +3,9 @@
3
3
  *
4
4
  * Registers the `http` / `HttpRequest` types, the `http` constant, and the
5
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.
6
+ * builds an `HttpRequestBuilder` a pure *description* of a request. CEL never
7
+ * issues it: both `.json()` and `.file()` are dispatched by the host on the
8
+ * returned builder, so expression evaluation stays free of IO.
9
9
  *
10
10
  * Wired into `globalRegistry` at module load (see `evaluator.ts`), so every
11
11
  * `Environment` gets HTTP for free — both type-checking and evaluation.
@@ -52,8 +52,4 @@ 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
- );
59
55
  }
@@ -74,8 +74,106 @@ function checkOptionalAccessNode(chk, ast, ctx) {
74
74
  const actualType = leftType.kind === "optional" ? leftType.valueType : leftType;
75
75
  return chk.registry.getOptionalType(chk.checkAccessOnType(ast, ctx, actualType, true));
76
76
  }
77
- function checkElement(chk, ctx, expected, el) {
78
- return expected.unify(chk.registry, chk.check(el, ctx)) ?? dynType;
77
+ function spreadInner(node) {
78
+ return node.args;
79
+ }
80
+ function spreadListElementType(chk, ctx, node) {
81
+ const t = chk.check(spreadInner(node), ctx);
82
+ if (t.kind === "dyn") return chk.dynType;
83
+ if (t.kind === "list") return t.valueType;
84
+ throw chk.createError(
85
+ "invalid_spread",
86
+ `Cannot spread '${chk.formatType(t)}' into a list (expected a list)`,
87
+ node
88
+ );
89
+ }
90
+ function spreadMapEntryTypes(chk, ctx, node) {
91
+ const t = chk.check(spreadInner(node), ctx);
92
+ if (t.kind === "dyn") return [chk.dynType, chk.dynType];
93
+ if (t.kind === "map") return [t.keyType, t.valueType];
94
+ if (t.kind === "message") {
95
+ const Base2 = chk;
96
+ return [Base2.stringType, chk.dynType];
97
+ }
98
+ throw chk.createError(
99
+ "invalid_spread",
100
+ `Cannot spread '${chk.formatType(t)}' into a map (expected a map)`,
101
+ node
102
+ );
103
+ }
104
+ const SPREAD_SKIP_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
105
+ function assignFragmentEntry(obj, k, v) {
106
+ const key = k;
107
+ if (SPREAD_SKIP_KEYS.has(key)) return;
108
+ obj[key] = v;
109
+ }
110
+ function mergeMapFragments(ev, frags) {
111
+ const obj = {};
112
+ for (const f of frags) {
113
+ if ("kv" in f) {
114
+ assignFragmentEntry(obj, f.kv[0], f.kv[1]);
115
+ continue;
116
+ }
117
+ const src = f.spread;
118
+ if (src instanceof Map) {
119
+ for (const [k, v] of src) assignFragmentEntry(obj, k, v);
120
+ continue;
121
+ }
122
+ if (src !== null && typeof src === "object" && !isArray(src)) {
123
+ for (const k in src) {
124
+ if (hasOwn(src, k)) assignFragmentEntry(obj, k, src[k]);
125
+ }
126
+ continue;
127
+ }
128
+ throw ev.createError(
129
+ "invalid_spread",
130
+ `Cannot spread a non-map value into a map`,
131
+ f.node
132
+ );
133
+ }
134
+ return obj;
135
+ }
136
+ function evaluateSpreadList(ev, ast, ctx) {
137
+ const arr = ast.args;
138
+ return Effect.all(
139
+ arr.map(
140
+ (el) => el.op === "spread" ? ev.eval(spreadInner(el), ctx).pipe(
141
+ Effect.flatMap(
142
+ (v) => isArray(v) ? Effect.succeed(v) : Effect.fail(
143
+ ev.createError(
144
+ "invalid_spread",
145
+ `Cannot spread a non-list value into a list`,
146
+ el
147
+ )
148
+ )
149
+ )
150
+ ) : ev.eval(el, ctx).pipe(Effect.map((v) => [v]))
151
+ )
152
+ ).pipe(Effect.map((parts) => parts.flat()));
153
+ }
154
+ function evaluateSpreadMap(ev, ast, ctx) {
155
+ const arr = ast.args;
156
+ return Effect.all(
157
+ arr.map((e) => {
158
+ if (e.length === 1 || e[0].op === "spread") {
159
+ const node = e[0];
160
+ return ev.eval(spreadInner(node), ctx).pipe(
161
+ Effect.map((src) => ({ spread: src, node }))
162
+ );
163
+ }
164
+ const pair = e;
165
+ return Effect.all([ev.eval(pair[0], ctx), ev.eval(pair[1], ctx)]).pipe(
166
+ Effect.map(([k, v]) => ({ kv: [k, v] }))
167
+ );
168
+ })
169
+ ).pipe(
170
+ Effect.flatMap(
171
+ (frags) => Effect.try({
172
+ try: () => mergeMapFragments(ev, frags),
173
+ catch: (e) => e
174
+ })
175
+ )
176
+ );
79
177
  }
80
178
  function ternaryConditionError(ev, value, node) {
81
179
  const type = ev.debugRuntimeType(value);
@@ -481,9 +579,19 @@ const OPERATORS_MAP = {
481
579
  const arr = ast.args;
482
580
  const arrLen = arr.length;
483
581
  if (arrLen === 0) return ast.setMeta("evaluate", emptyList) && chk.getType("list<T>");
484
- let valueType = chk.check(arr[0], ctx);
485
- for (let i = 1; i < arrLen; i++)
486
- valueType = checkElement(chk, ctx, valueType, arr[i]);
582
+ let hasSpread = false;
583
+ let valueType;
584
+ for (const el of arr) {
585
+ let t;
586
+ if (el.op === "spread") {
587
+ hasSpread = true;
588
+ t = spreadListElementType(chk, ctx, el);
589
+ } else {
590
+ t = chk.check(el, ctx);
591
+ }
592
+ valueType = valueType === void 0 ? t : valueType.unify(chk.registry, t) ?? dynType;
593
+ }
594
+ if (hasSpread) ast.setMeta("evaluate", evaluateSpreadList);
487
595
  return chk.registry.getListType(valueType);
488
596
  },
489
597
  evaluate(ev, ast, ctx) {
@@ -495,13 +603,23 @@ const OPERATORS_MAP = {
495
603
  const arr = ast.args;
496
604
  const arrLen = arr.length;
497
605
  if (arrLen === 0) return ast.setMeta("evaluate", emptyMap) && chk.getType("map<K, V>");
498
- let keyType = chk.check(arr[0][0], ctx);
499
- let valueType = chk.check(arr[0][1], ctx);
500
- for (let i = 1; i < arrLen; i++) {
501
- const e = arr[i];
502
- keyType = checkElement(chk, ctx, keyType, e[0]);
503
- valueType = checkElement(chk, ctx, valueType, e[1]);
606
+ let hasSpread = false;
607
+ let keyType;
608
+ let valueType;
609
+ for (const e of arr) {
610
+ let k;
611
+ let v;
612
+ if (e.length === 1 || e[0].op === "spread") {
613
+ hasSpread = true;
614
+ [k, v] = spreadMapEntryTypes(chk, ctx, e[0]);
615
+ } else {
616
+ k = chk.check(e[0], ctx);
617
+ v = chk.check(e[1], ctx);
618
+ }
619
+ keyType = keyType === void 0 ? k : keyType.unify(chk.registry, k) ?? dynType;
620
+ valueType = valueType === void 0 ? v : valueType.unify(chk.registry, v) ?? dynType;
504
621
  }
622
+ if (hasSpread) ast.setMeta("evaluate", evaluateSpreadMap);
505
623
  return chk.registry.getMapType(keyType, valueType);
506
624
  },
507
625
  evaluate(ev, ast, ctx) {
@@ -513,6 +631,16 @@ const OPERATORS_MAP = {
513
631
  ).pipe(Effect.map(safeFromEntries));
514
632
  }
515
633
  },
634
+ "spread": {
635
+ // Reached only via direct `ast.check`/`ast.evaluate` — parents consume the
636
+ // node inline, so these are defensive guards.
637
+ check(chk, ast) {
638
+ throw chk.createError("misplaced_spread", `'...' is only valid inside a list or map literal`, ast);
639
+ },
640
+ evaluate(ev, ast) {
641
+ return Effect.fail(ev.createError("misplaced_spread", `'...' is only valid inside a list or map literal`, ast));
642
+ }
643
+ },
516
644
  "comprehension": {
517
645
  check(chk, ast, ctx) {
518
646
  const args = ast.args;
@@ -76,8 +76,9 @@ export declare class Parser {
76
76
  parsePostfix(): ASTNode;
77
77
  parsePrimary(): ASTNode;
78
78
  parseList(): ASTNode;
79
+ parseListElement(): ASTNode;
79
80
  parseMap(): ASTNode;
80
- parseProperty(): [ASTNode, ASTNode];
81
+ parseProperty(): [ASTNode, ASTNode] | [ASTNode];
81
82
  parseArgumentList(): ASTNode[];
82
83
  }
83
84
  export {};
@@ -34,7 +34,8 @@ const TOKEN = {
34
34
  COMMA: 28,
35
35
  COLON: 29,
36
36
  QUESTION: 30,
37
- BYTES: 31
37
+ BYTES: 31,
38
+ ELLIPSIS: 32
38
39
  };
39
40
  const OP_FOR_TOKEN = {
40
41
  [TOKEN.EQ]: OPS["=="],
@@ -208,6 +209,8 @@ class Lexer {
208
209
  case "}":
209
210
  return this.token(this.pos++, TOKEN.RBRACE);
210
211
  case ".":
212
+ if (input[pos + 1] === "." && input[pos + 2] === ".")
213
+ return this.token((this.pos += 3) - 3, TOKEN.ELLIPSIS);
211
214
  return this.token(this.pos++, TOKEN.DOT);
212
215
  case ",":
213
216
  return this.token(this.pos++, TOKEN.COMMA);
@@ -735,12 +738,12 @@ export class Parser {
735
738
  const elements = [];
736
739
  let remainingElements = this.limits.maxListElements;
737
740
  if (!this.match(TOKEN.RBRACKET)) {
738
- elements.push(this.parseExpression());
741
+ elements.push(this.parseListElement());
739
742
  if (!remainingElements--) this.#limitExceeded("maxListElements", elements.at(-1).pos);
740
743
  while (this.match(TOKEN.COMMA)) {
741
744
  this.#advanceToken();
742
745
  if (this.match(TOKEN.RBRACKET)) break;
743
- elements.push(this.parseExpression());
746
+ elements.push(this.parseListElement());
744
747
  if (!remainingElements--) this.#limitExceeded("maxListElements", elements.at(-1).pos);
745
748
  }
746
749
  }
@@ -748,6 +751,10 @@ export class Parser {
748
751
  this.consume(TOKEN.RBRACKET);
749
752
  return this.#node(start, closeEnd, OPS.list, elements);
750
753
  }
754
+ parseListElement() {
755
+ if (this.match(TOKEN.ELLIPSIS)) return this.#parseSpread();
756
+ return this.parseExpression();
757
+ }
751
758
  parseMap() {
752
759
  const start = this.consume(TOKEN.LBRACE);
753
760
  const props = [];
@@ -767,8 +774,18 @@ export class Parser {
767
774
  return this.#node(start, closeEnd, OPS.map, props);
768
775
  }
769
776
  parseProperty() {
777
+ if (this.match(TOKEN.ELLIPSIS)) return [this.#parseSpread()];
770
778
  return [this.parseExpression(), (this.consume(TOKEN.COLON), this.parseExpression())];
771
779
  }
780
+ // `...expr` — only valid as a list element or map entry; the parent literal
781
+ // consumes the node and `parsePrimary` never sees ELLIPSIS, so a stray `...`
782
+ // surfaces as a normal "unexpected token" parse error.
783
+ #parseSpread() {
784
+ const start = this.pos;
785
+ this.#advanceToken();
786
+ const inner = this.parseExpression();
787
+ return this.#node(start, inner.end, OPS.spread, inner);
788
+ }
772
789
  parseArgumentList() {
773
790
  const args = [];
774
791
  let remainingArgs = this.limits.maxCallArguments;
@@ -79,7 +79,11 @@ export function serialize(ast) {
79
79
  case "list":
80
80
  return `[${args.map(serialize).join(", ")}]`;
81
81
  case "map":
82
- return `{${args.map(([k, v]) => `${serialize(k)}: ${serialize(v)}`).join(", ")}}`;
82
+ return `{${args.map(
83
+ (e) => e.length === 1 || e[0].op === "spread" ? serialize(e[0]) : `${serialize(e[0])}: ${serialize(e[1])}`
84
+ ).join(", ")}}`;
85
+ case "spread":
86
+ return `...${serialize(args)}`;
83
87
  case "?:": {
84
88
  const ternArgs = args;
85
89
  return `${wrap(ternArgs[0], op)} ? ${wrap(ternArgs[1], op)} : ${serialize(ternArgs[2])}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shwfed/config",
3
- "version": "2.1.2",
3
+ "version": "2.2.1",
4
4
  "description": "Configurable UI for SHWFED",
5
5
  "type": "module",
6
6
  "publishConfig": {