@shwfed/config 3.0.13 → 3.0.14

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 (90) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/actions/buttons/2026-04-18/com.shwfed.actions.button.http.request.json/config.d.vue.ts +2 -2
  3. package/dist/runtime/components/actions/buttons/2026-04-18/com.shwfed.actions.button.http.request.json/config.vue.d.ts +2 -2
  4. package/dist/runtime/components/actions/buttons/2026-04-18/com.shwfed.actions.button.http.request.json.confirm/config.d.vue.ts +2 -2
  5. package/dist/runtime/components/actions/buttons/2026-04-18/com.shwfed.actions.button.http.request.json.confirm/config.vue.d.ts +2 -2
  6. package/dist/runtime/components/actions/buttons/2026-06-08/com.shwfed.actions.button.http.request.json.batch/config.d.vue.ts +2 -2
  7. package/dist/runtime/components/actions/buttons/2026-06-08/com.shwfed.actions.button.http.request.json.batch/config.vue.d.ts +2 -2
  8. package/dist/runtime/components/actions/buttons/2026-06-25/com.shwfed.actions.button.state.write/config.d.vue.ts +8 -8
  9. package/dist/runtime/components/actions/buttons/2026-06-25/com.shwfed.actions.button.state.write/config.vue.d.ts +8 -8
  10. package/dist/runtime/components/config/blocks/2026-06-17/com.shwfed.block.tree.single/config.d.vue.ts +18 -18
  11. package/dist/runtime/components/config/blocks/2026-06-17/com.shwfed.block.tree.single/config.vue.d.ts +18 -18
  12. package/dist/runtime/components/config/blocks/2026-06-17/com.shwfed.block.tree.single/runtime.d.vue.ts +18 -18
  13. package/dist/runtime/components/config/blocks/2026-06-17/com.shwfed.block.tree.single/runtime.vue.d.ts +18 -18
  14. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/config.d.vue.ts +2 -0
  15. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/config.vue +10 -2
  16. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/config.vue.d.ts +2 -0
  17. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/runtime.d.vue.ts +2 -0
  18. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/runtime.vue.d.ts +2 -0
  19. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/schema.d.ts +1 -0
  20. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.digital/schema.js +5 -0
  21. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.realestate.lease/config.d.vue.ts +96 -0
  22. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.realestate.lease/config.vue +385 -0
  23. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.realestate.lease/config.vue.d.ts +96 -0
  24. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.realestate.lease/items-colgroup.d.vue.ts +3 -0
  25. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.realestate.lease/items-colgroup.vue +20 -0
  26. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.realestate.lease/items-colgroup.vue.d.ts +3 -0
  27. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.realestate.lease/runtime.d.vue.ts +96 -0
  28. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.realestate.lease/runtime.vue +177 -0
  29. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.realestate.lease/runtime.vue.d.ts +96 -0
  30. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.realestate.lease/schema.d.ts +102 -0
  31. package/dist/runtime/components/config/blocks/2026-07-17/com.shwfed.block.invoice.realestate.lease/schema.js +186 -0
  32. package/dist/runtime/components/config/index.d.vue.ts +2 -2
  33. package/dist/runtime/components/config/index.vue.d.ts +2 -2
  34. package/dist/runtime/components/form/fields/2026-04-22/com.shwfed.form.field.text/config.d.vue.ts +22 -22
  35. package/dist/runtime/components/form/fields/2026-04-22/com.shwfed.form.field.text/config.vue.d.ts +22 -22
  36. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/config.d.vue.ts +22 -22
  37. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/config.vue.d.ts +22 -22
  38. package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.d.vue.ts +26 -26
  39. package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.vue.d.ts +26 -26
  40. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.multi/config.d.vue.ts +26 -26
  41. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.multi/config.vue.d.ts +26 -26
  42. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.single/config.d.vue.ts +26 -26
  43. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.single/config.vue.d.ts +26 -26
  44. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/config.d.vue.ts +26 -26
  45. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/config.vue.d.ts +26 -26
  46. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/config.d.vue.ts +26 -26
  47. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/config.vue.d.ts +26 -26
  48. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/config.d.vue.ts +26 -26
  49. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/config.vue.d.ts +26 -26
  50. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/config.d.vue.ts +26 -26
  51. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/config.vue.d.ts +26 -26
  52. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-remote/config.d.vue.ts +2 -2
  53. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-remote/config.vue.d.ts +2 -2
  54. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-static/config.d.vue.ts +2 -2
  55. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-static/config.vue.d.ts +2 -2
  56. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-remote/config.d.vue.ts +2 -2
  57. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-remote/config.vue.d.ts +2 -2
  58. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-static/config.d.vue.ts +2 -2
  59. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-static/config.vue.d.ts +2 -2
  60. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi.remote/config.d.vue.ts +2 -2
  61. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi.remote/config.vue.d.ts +2 -2
  62. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single.remote/config.d.vue.ts +2 -2
  63. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single.remote/config.vue.d.ts +2 -2
  64. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +8 -8
  65. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +8 -8
  66. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +8 -8
  67. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +8 -8
  68. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-multi/config.d.vue.ts +2 -2
  69. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-multi/config.vue.d.ts +2 -2
  70. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-single/config.d.vue.ts +2 -2
  71. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-single/config.vue.d.ts +2 -2
  72. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +8 -8
  73. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +8 -8
  74. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +8 -8
  75. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +8 -8
  76. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/config.d.vue.ts +2 -2
  77. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/config.vue.d.ts +2 -2
  78. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/config.d.vue.ts +2 -2
  79. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/config.vue.d.ts +2 -2
  80. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +8 -8
  81. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +8 -8
  82. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +8 -8
  83. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +8 -8
  84. package/dist/runtime/components/table/columns/2026-07-16/com.shwfed.table.column.upload/runtime.vue +8 -2
  85. package/dist/runtime/share/slot-renderer.vue +83 -24
  86. package/dist/runtime/vendor/cel-js/CLAUDE.md +3 -1
  87. package/dist/runtime/vendor/cel-js/PROMPT.md +31 -2
  88. package/dist/runtime/vendor/cel-js/lib/functions.js +28 -1
  89. package/dist/runtime/vendor/cel-js/lib/registry.js +2 -1
  90. package/package.json +1 -1
@@ -36,15 +36,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
36
36
  }[]] | undefined;
37
37
  readonly keywords?: string | undefined;
38
38
  readonly readonly?: string | undefined;
39
- readonly dataSource: {
40
- readonly data: string;
41
- readonly request?: string | undefined;
42
- };
43
39
  readonly binding: string;
44
40
  readonly derived?: {
45
41
  readonly mode: "formula" | "prefill";
46
42
  readonly expression: string;
47
43
  } | undefined;
44
+ readonly dataSource: {
45
+ readonly data: string;
46
+ readonly request?: string | undefined;
47
+ };
48
48
  readonly nodeKey: string;
49
49
  readonly nodeChildren: string;
50
50
  readonly nodeLabel: string;
@@ -95,15 +95,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
95
95
  }[]] | undefined;
96
96
  readonly keywords?: string | undefined;
97
97
  readonly readonly?: string | undefined;
98
- readonly dataSource: {
99
- readonly data: string;
100
- readonly request?: string | undefined;
101
- };
102
98
  readonly binding: string;
103
99
  readonly derived?: {
104
100
  readonly mode: "formula" | "prefill";
105
101
  readonly expression: string;
106
102
  } | undefined;
103
+ readonly dataSource: {
104
+ readonly data: string;
105
+ readonly request?: string | undefined;
106
+ };
107
107
  readonly nodeKey: string;
108
108
  readonly nodeChildren: string;
109
109
  readonly nodeLabel: string;
@@ -36,15 +36,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
36
36
  }[]] | undefined;
37
37
  readonly keywords?: string | undefined;
38
38
  readonly readonly?: string | undefined;
39
- readonly dataSource: {
40
- readonly data: string;
41
- readonly request?: string | undefined;
42
- };
43
39
  readonly binding: string;
44
40
  readonly derived?: {
45
41
  readonly mode: "formula" | "prefill";
46
42
  readonly expression: string;
47
43
  } | undefined;
44
+ readonly dataSource: {
45
+ readonly data: string;
46
+ readonly request?: string | undefined;
47
+ };
48
48
  readonly nodeKey: string;
49
49
  readonly nodeChildren: string;
50
50
  readonly nodeLabel: string;
@@ -94,15 +94,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
94
94
  }[]] | undefined;
95
95
  readonly keywords?: string | undefined;
96
96
  readonly readonly?: string | undefined;
97
- readonly dataSource: {
98
- readonly data: string;
99
- readonly request?: string | undefined;
100
- };
101
97
  readonly binding: string;
102
98
  readonly derived?: {
103
99
  readonly mode: "formula" | "prefill";
104
100
  readonly expression: string;
105
101
  } | undefined;
102
+ readonly dataSource: {
103
+ readonly data: string;
104
+ readonly request?: string | undefined;
105
+ };
106
106
  readonly nodeKey: string;
107
107
  readonly nodeChildren: string;
108
108
  readonly nodeLabel: string;
@@ -36,15 +36,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
36
36
  }[]] | undefined;
37
37
  readonly keywords?: string | undefined;
38
38
  readonly readonly?: string | undefined;
39
- readonly dataSource: {
40
- readonly data: string;
41
- readonly request?: string | undefined;
42
- };
43
39
  readonly binding: string;
44
40
  readonly derived?: {
45
41
  readonly mode: "formula" | "prefill";
46
42
  readonly expression: string;
47
43
  } | undefined;
44
+ readonly dataSource: {
45
+ readonly data: string;
46
+ readonly request?: string | undefined;
47
+ };
48
48
  readonly nodeKey: string;
49
49
  readonly nodeChildren: string;
50
50
  readonly nodeLabel: string;
@@ -94,15 +94,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
94
94
  }[]] | undefined;
95
95
  readonly keywords?: string | undefined;
96
96
  readonly readonly?: string | undefined;
97
- readonly dataSource: {
98
- readonly data: string;
99
- readonly request?: string | undefined;
100
- };
101
97
  readonly binding: string;
102
98
  readonly derived?: {
103
99
  readonly mode: "formula" | "prefill";
104
100
  readonly expression: string;
105
101
  } | undefined;
102
+ readonly dataSource: {
103
+ readonly data: string;
104
+ readonly request?: string | undefined;
105
+ };
106
106
  readonly nodeKey: string;
107
107
  readonly nodeChildren: string;
108
108
  readonly nodeLabel: string;
@@ -289,9 +289,15 @@ async function onDownloadEntry(entry) {
289
289
  </script>
290
290
 
291
291
  <template>
292
- <div class="p-[0.125rem] w-full">
292
+ <!--
293
+ The cell's own content (one add button, or a short file list) is usually
294
+ shorter than the row, which is sized by its tallest neighbour — so the
295
+ wrapper fills the height and centres, rather than letting the button pin to
296
+ the top edge.
297
+ -->
298
+ <div class="p-[0.125rem] w-full h-full flex items-center">
293
299
  <div
294
- class="flex flex-col gap-1.5"
300
+ class="flex w-full flex-col gap-1.5"
295
301
  :data-disabled="isDisabled || void 0"
296
302
  :data-readonly="isReadonly || void 0"
297
303
  >
@@ -14,6 +14,16 @@ const props = defineProps({
14
14
  const emit = defineEmits(["update:slotValue"]);
15
15
  const inherited = injectCELContext();
16
16
  const $cel = (expression, context) => _rawCel(expression, { ...celBindings(inherited), ...context });
17
+ function isHidden(block) {
18
+ const expr = block.hidden;
19
+ if (typeof expr !== "string" || expr.length === 0) return false;
20
+ try {
21
+ return Effect.runSync($cel(expr));
22
+ } catch (err) {
23
+ console.error(`[shwfed-slot-renderer] failed to evaluate hidden for ${block.id}:`, err);
24
+ return false;
25
+ }
26
+ }
17
27
  function pickLayout(sets) {
18
28
  if (sets.length === 0) return void 0;
19
29
  for (const set of sets) {
@@ -40,6 +50,7 @@ const placedBlocks = computed(() => {
40
50
  const block = b;
41
51
  const placement = layout.placements[block.id];
42
52
  if (!placement) continue;
53
+ if (isHidden(block)) continue;
43
54
  const entry = props.findEntry(block.type, block.compatibilityDate);
44
55
  if (!entry) {
45
56
  const key = `${block.type}@${block.compatibilityDate}`;
@@ -53,37 +64,83 @@ const placedBlocks = computed(() => {
53
64
  }
54
65
  return out;
55
66
  });
67
+ const authoredAreas = computed(() => {
68
+ const layout = activeLayout.value?.layout;
69
+ if (!layout) return [];
70
+ const out = [];
71
+ for (const b of props.slotValue.blocks) {
72
+ const block = b;
73
+ const placement = layout.placements[block.id];
74
+ if (!placement) continue;
75
+ if (!props.findEntry(block.type, block.compatibilityDate)) continue;
76
+ out.push(placement.area);
77
+ }
78
+ return out;
79
+ });
80
+ function buildRemap(count, keep) {
81
+ const line = Array.from({ length: count + 2 });
82
+ const keptFlags = [];
83
+ let acc = 1;
84
+ for (let L = 1; L <= count + 1; L++) {
85
+ line[L] = acc;
86
+ if (L <= count) {
87
+ const k = keep(L);
88
+ keptFlags.push(k);
89
+ if (k) acc++;
90
+ }
91
+ }
92
+ return { line, keptFlags, kept: acc - 1 };
93
+ }
94
+ const compaction = computed(() => {
95
+ const l = activeLayout.value?.layout;
96
+ if (!l) return null;
97
+ const authored = authoredAreas.value;
98
+ const visible = placedBlocks.value.map((p) => p.placement.area);
99
+ const coversCol = (a, c) => a[0][0] <= c && c < a[1][0];
100
+ const coversRow = (a, r) => a[0][1] <= r && r < a[1][1];
101
+ const rowCount = l.rows ?? authored.reduce((m, a) => Math.max(m, a[1][1] - 1), 0);
102
+ const grows = Array.from({ length: rowCount }, () => false);
103
+ for (const p of placedBlocks.value) {
104
+ if (!p.entry.metadata?.h?.grow) continue;
105
+ const [[, y1], [, y2]] = p.placement.area;
106
+ const lo = Math.max(1, y1);
107
+ const hi = Math.min(rowCount, y2 - 1);
108
+ for (let r = lo; r <= hi; r++) grows[r - 1] = true;
109
+ }
110
+ const col = buildRemap(l.columns, (c) => visible.some((a) => coversCol(a, c)) || !authored.some((a) => coversCol(a, c)));
111
+ const row = buildRemap(rowCount, (r) => visible.some((a) => coversRow(a, r)) || !authored.some((a) => coversRow(a, r)));
112
+ const rowTemplate = l.rows ? row.keptFlags.map((keep, i) => keep ? grows[i] ? "auto" : "minmax(auto, 1fr)" : null).filter(Boolean).join(" ") : "";
113
+ return {
114
+ colLine: col.line,
115
+ rowLine: row.line,
116
+ keptColumns: Math.max(1, col.kept),
117
+ rowTemplate
118
+ };
119
+ });
56
120
  const gridStyle = computed(() => {
57
121
  const l = activeLayout.value?.layout;
58
- if (!l) return "";
59
- const tracks = (n, min) => `repeat(${n}, minmax(${min}, 1fr))`;
122
+ const comp = compaction.value;
123
+ if (!l || !comp) return "";
124
+ const colTemplate = `repeat(${comp.keptColumns}, minmax(0, 1fr))`;
60
125
  const gap = `calc(${l.gap ?? DEFAULT_GAP} * 0.25rem)`;
61
- const colGap = l.columns > 1 ? `min(${gap}, calc(100% / ${l.columns - 1}))` : gap;
62
- let rowTemplate = "";
63
- if (l.rows) {
64
- const grows = Array.from({ length: l.rows }, () => false);
65
- for (const p of placedBlocks.value) {
66
- if (!p.entry.metadata?.h?.grow) continue;
67
- const [[, y1], [, y2]] = p.placement.area;
68
- const lo = Math.max(1, y1);
69
- const hi = Math.min(l.rows, y2 - 1);
70
- for (let r = lo; r <= hi; r++) grows[r - 1] = true;
71
- }
72
- rowTemplate = grows.map((g) => g ? "auto" : "minmax(auto, 1fr)").join(" ");
73
- }
126
+ const colGap = comp.keptColumns > 1 ? `min(${gap}, calc(100% / ${comp.keptColumns - 1}))` : gap;
74
127
  const parts = [
75
128
  "display: grid",
76
- `grid-template-columns: ${tracks(l.columns, "0")}`,
77
- rowTemplate ? `grid-template-rows: ${rowTemplate}` : "",
129
+ `grid-template-columns: ${colTemplate}`,
130
+ comp.rowTemplate ? `grid-template-rows: ${comp.rowTemplate}` : "",
78
131
  `column-gap: ${colGap}`,
79
132
  `row-gap: ${gap}`,
80
133
  l.style ?? ""
81
134
  ].filter(Boolean);
82
135
  return parts.join("; ");
83
136
  });
84
- function cellStyle(placement) {
137
+ function cellStyle(placement, comp) {
85
138
  const [[x1, y1], [x2, y2]] = placement.area;
86
- const parts = [`grid-column: ${x1} / ${x2}`, `grid-row: ${y1} / ${y2}`, "min-width: 0"];
139
+ const parts = [
140
+ `grid-column: ${comp.colLine[x1]} / ${comp.colLine[x2]}`,
141
+ `grid-row: ${comp.rowLine[y1]} / ${comp.rowLine[y2]}`,
142
+ "min-width: 0"
143
+ ];
87
144
  const h = placement.h && placement.h !== "stretch" ? placement.h : null;
88
145
  const v = placement.v && placement.v !== "stretch" ? placement.v : null;
89
146
  if (h || v) {
@@ -94,14 +151,16 @@ function cellStyle(placement) {
94
151
  }
95
152
  return parts.join("; ");
96
153
  }
97
- const rendered = computed(
98
- () => placedBlocks.value.map((p) => ({
154
+ const rendered = computed(() => {
155
+ const comp = compaction.value;
156
+ if (!comp) return [];
157
+ return placedBlocks.value.map((p) => ({
99
158
  key: p.block.id,
100
- style: cellStyle(p.placement),
159
+ style: cellStyle(p.placement, comp),
101
160
  component: p.entry.runtime,
102
161
  block: p.block
103
- }))
104
- );
162
+ }));
163
+ });
105
164
  function updateBlock(id, next) {
106
165
  const blocks = props.slotValue.blocks.map((b) => b.id === id ? next : b);
107
166
  emit("update:slotValue", { ...props.slotValue, blocks });
@@ -20,11 +20,13 @@ A `list.slice(number)` / `list.slice(number, number)` receiver pair has been add
20
20
 
21
21
  A `string.replace(string, string): string` / `string.replace(string, string, number): string` receiver pair has been added (in `functions.ts` beside `string.trim()`), matching the CEL strings-extension `replace`. The two-arg form delegates to JS `String.prototype.replaceAll` (so all non-overlapping occurrences are replaced; an empty `from` interleaves `to`). The three-arg form takes a limit and mirrors Go's `strings.Replace` via the `replaceLimited` helper: `limit < 0` replaces all, `0` replaces none, otherwise the first `limit` occurrences (`"foo".replace("", "_", 2) == "_f_oo"`).
22
22
 
23
+ A `number.format(map): string` receiver method has been added (not from upstream, in `functions.ts` beside the `string(number)` conversion) — renders a number as a display string via `Intl.NumberFormat`. The map argument is passed through verbatim as the second (`options`) argument of `new Intl.NumberFormat('en-US', options)` — the same options object `Number.prototype.toLocaleString(locales, options)` accepts (`minimumFractionDigits`, `maximumFractionDigits`, `useGrouping`, `style`, `currency`, …); the locale is hardcoded to `'en-US'` so grouping renders as a comma and results are deterministic. Two precision-critical details: (1) the value is handed to `.format()` as a **string** (`Decimal.toString()`), not a JS number — `Intl` parses the decimal string at full precision, whereas the `.toNumber()` round-trip would clip past the safe-integer range (this matters for money); the `format` type only admits `number | bigint`, so the call is cast (`fmt.format as (value: string) => string`). (2) The option *values* arrive as `Decimal` (a CEL map literal's `2` is a `Decimal`) and must be unwrapped to JS numbers before `Intl` sees them — `intlOptionsFromMap` walks the map (plain object or host `Map`) and `.toNumber()`s any `Decimal`/`bigint` value, leaving strings/bools; these are small config knobs so their float round-trip is precision-safe. An impossible option combination (e.g. `maximumFractionDigits < minimumFractionDigits`) throws a `RangeError` from the `Intl` constructor, surfaced as `invalid_format_options`. A `dyn` receiver that is a number at runtime resolves too (overload resolution is by runtime type). Tested in `__tests__/number-format.test.ts`.
24
+
23
25
  `evaluate` now returns `Effect<T, ParseError | EvaluationError | TypeError>` instead of a plain value. The entire evaluation pipeline is Effect-based — every AST node's `evaluate` function returns an Effect, and sub-expressions are composed via `Effect.gen` / `yield*`. Promise-based async handling has been removed. User-registered functions may return `Effect` values, which are automatically flatMapped into the evaluation pipeline. Import `Effect` from `effect` and use `Effect.runSync` or `Effect.runPromise` to execute the returned Effect.
24
26
 
25
27
  The `homogeneousAggregateLiterals` and `enableOptionalTypes` environment options have been removed. Aggregate data structures (lists, maps) are always heterogeneous — mixed-type literals are inferred as `list<dyn>` or `map<dyn, dyn>`. Optional types (`.?`, `[?]`, `optional.*` helpers) are always enabled.
26
28
 
27
- 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). The optional surface is `optional.of(A): optional<A>` (the lift / `pure` — `of(null)` is `Some(null)`), `optional.ofNullable(A): optional<A>` (the null-tolerant lift, Java-style — `null` becomes `None`), `optional.none()`, `optional.hasValue()`, `optional.value()`, `optional.or(optional)`, `optional.orValue(value)`, plus `optional<A>.flatMap(var, body): optional<B>` (monadic bind), `optional<A>.map(var, body): optional<B>` (the Functor `fmap`), `optional<A>.zip(other, x, y, body): optional<C>` (the applicative `liftA2` / `zipWith`), `optional<A>.filter(var, predicate): optional<A>` (Rust `Option::filter` / Haskell `mfilter`), and the folds `optional<A>.exists(var, predicate): bool` (predicate on `Some`, `false` on `None` — Rust `is_some_and`) and `optional<A>.all(var, predicate): bool` (predicate on `Some`, vacuously `true` on `None` — Scala `forall`) — all local additions (not from upstream). All are variable-binding macros (registered with `ast` arg types so the unbound args stay unevaluated): on `Some` they bind the value(s) and evaluate `body`; on `None` they short-circuit without touching `body`. `flatMap`'s body must itself yield an `optional`; `map`'s body is a plain value that gets re-wrapped in `Some`; `filter` / `exists` / `all` predicates must be `bool` (enforced at type-check when static, at runtime via `invalid_condition_type` otherwise). `map` is the derived `o.flatMap(v, optional.of(body))` promoted to a first-class macro. `zip` is the flattened `a.flatMap(x, b.map(y, body))`: it type-checks `other` in the outer ctx, binds `x` then `y` under two chained overlays, re-wraps the plain body in `Some`, and short-circuits to `None` if either side is `None` (a `None` receiver skips evaluating `other`). Because CEL has no tuple type the transform is fused (there is no useful `optional<pair>` to return), so `zip` is `zipWith`, not Rust/Scala's pair-building `Option.zip`. The short-circuit makes `zip` observationally monadic — harmless for `optional` (a `None` carries no payload) but it would not generalise to an error-accumulating applicative. An `optional.none()` receiver/argument carries a `param` element type; the shared `bindOptionalValueType` helper (exported from `optional.ts`) binds it as `dyn` so a boolean body still type-checks — every binding macro (`map` / `flatMap` / `zip` / `filter` / `exists` / `all`) goes through it (leaking the `param` into the iteration variable was a real bug in `map` / `flatMap`, regression-tested in `optional-extras.test.ts`). `zip` lives in `optional.ts` beside `flatMap`; the two-variable binding is why it is not folded into the shared `map` expander. NOTE: `map`, `filter`, `all`, and `exists` are **registered in `macros.ts`**, not here — the parser resolves a macro by name before types are known, so the optional variants and the list-comprehension variants must share one expander per name that dispatches on the receiver type at type-check (an `optional` receiver → optional semantics; `list`/`map` → comprehension; a `dyn` receiver is deferred to a runtime `Option.isOption` check). `exists_one` and `find` stay list-only.
29
+ 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). **Optional field access diverges from CEL**: `#getOptionalField` in `registry.ts` collapses a present-but-`null` field to `None`, where upstream (and cel-go) yield `Some(null)` — so `row.?foo` and `row.items[?0]` are absent-tolerant against backend JSON, in which `null` is overwhelmingly "absent" (without it `row.?foo.orValue([])` returns `null` and a downstream `size()` fails the overload check — a designer-facing footgun, since `.?` reads as the null-safe path). The collapse is **scoped to field access on purpose**: `optionalOf` itself keeps CEL semantics, so `optional.of(null)` is still `Some(null)` and `find` can still match a null element (its `Option` is the only channel for "did it match", so collapsing there would make a hit indistinguishable from a miss — unlike field access, where `has(row.foo)` and plain `row.foo` remain as escape hatches). `URL.query` is unaffected either way: it null-coalesces (`?? undefined`) before `optionalOf`. Pinned in `optional-extras.test.ts`. The optional surface is `optional.of(A): optional<A>` (the lift / `pure` — `of(null)` is `Some(null)`), `optional.ofNullable(A): optional<A>` (the null-tolerant lift, Java-style — `null` becomes `None`), `optional.none()`, `optional.hasValue()`, `optional.value()`, `optional.or(optional)`, `optional.orValue(value)`, plus `optional<A>.flatMap(var, body): optional<B>` (monadic bind), `optional<A>.map(var, body): optional<B>` (the Functor `fmap`), `optional<A>.zip(other, x, y, body): optional<C>` (the applicative `liftA2` / `zipWith`), `optional<A>.filter(var, predicate): optional<A>` (Rust `Option::filter` / Haskell `mfilter`), and the folds `optional<A>.exists(var, predicate): bool` (predicate on `Some`, `false` on `None` — Rust `is_some_and`) and `optional<A>.all(var, predicate): bool` (predicate on `Some`, vacuously `true` on `None` — Scala `forall`) — all local additions (not from upstream). All are variable-binding macros (registered with `ast` arg types so the unbound args stay unevaluated): on `Some` they bind the value(s) and evaluate `body`; on `None` they short-circuit without touching `body`. `flatMap`'s body must itself yield an `optional`; `map`'s body is a plain value that gets re-wrapped in `Some`; `filter` / `exists` / `all` predicates must be `bool` (enforced at type-check when static, at runtime via `invalid_condition_type` otherwise). `map` is the derived `o.flatMap(v, optional.of(body))` promoted to a first-class macro. `zip` is the flattened `a.flatMap(x, b.map(y, body))`: it type-checks `other` in the outer ctx, binds `x` then `y` under two chained overlays, re-wraps the plain body in `Some`, and short-circuits to `None` if either side is `None` (a `None` receiver skips evaluating `other`). Because CEL has no tuple type the transform is fused (there is no useful `optional<pair>` to return), so `zip` is `zipWith`, not Rust/Scala's pair-building `Option.zip`. The short-circuit makes `zip` observationally monadic — harmless for `optional` (a `None` carries no payload) but it would not generalise to an error-accumulating applicative. An `optional.none()` receiver/argument carries a `param` element type; the shared `bindOptionalValueType` helper (exported from `optional.ts`) binds it as `dyn` so a boolean body still type-checks — every binding macro (`map` / `flatMap` / `zip` / `filter` / `exists` / `all`) goes through it (leaking the `param` into the iteration variable was a real bug in `map` / `flatMap`, regression-tested in `optional-extras.test.ts`). `zip` lives in `optional.ts` beside `flatMap`; the two-variable binding is why it is not folded into the shared `map` expander. NOTE: `map`, `filter`, `all`, and `exists` are **registered in `macros.ts`**, not here — the parser resolves a macro by name before types are known, so the optional variants and the list-comprehension variants must share one expander per name that dispatches on the receiver type at type-check (an `optional` receiver → optional semantics; `list`/`map` → comprehension; a `dyn` receiver is deferred to a runtime `Option.isOption` check). `exists_one` and `find` stay list-only.
28
30
 
29
31
  A `list.filterMap(var, body): list<dyn>` macro has been added (not from upstream, in `macros.ts` beside `find`) — Rust's `Iterator::filter_map`. The body evaluates to an `optional<B>` per element; `Some` values are kept (unwrapped), `None`s dropped, yielding `list<B>`. It fuses the map-then-strip-absent pattern (which would otherwise need `.map(...)` to build a `list<optional>` plus a second pass to drop the `None`s) and is the optional-gated complement to the 3-arg `map(var, filter, transform)`, whose gate is a bool predicate. NOTE: it is **not** a comprehension desugar — the accu/ternary primitives gate on a bool, not on `Option` presence — so unlike the 2/3-arg `map` and `filter` it runs a manual fold like `find` (eval receiver → coerce Set/Map/object to an array → per-element `ev.eval(body)` → `ensureOptional` → push the `Some`s). Type-check: the receiver element type comes from list `valueType` / map `keyType` / `dyn` (same `invalid_comprehension_range` contract as `find`); the body must be `optional<B>` when statically typed (via `ensureOptionalType`), and a `dyn` body defers to the runtime `ensureOptional` (matching `flatMap`); the result is `registry.getListType(bindOptionalValueType(bodyType))`. Because the body already returns an `optional`, `filterMap` needs no receiver-type dispatch (there is no `optional<A>.filterMap` — that role is `flatMap`), so it is a single list-only expander. Tested in `__tests__/filter-map-macro.test.ts`.
30
32
 
@@ -91,8 +91,8 @@ time when statically known, at runtime when the source is `dyn`.
91
91
  ```cel
92
92
  obj.field // access field on map/object
93
93
  obj["field"] // bracket access (equivalent)
94
- obj.?field // optional field access (returns optional)
95
- obj[?0] // optional index access (returns optional)
94
+ obj.?field // optional field access (returns optional; null counts as absent)
95
+ obj[?0] // optional index access (returns optional; null counts as absent)
96
96
  ```
97
97
 
98
98
  ## Built-in Functions
@@ -116,6 +116,26 @@ There is no `int()`, `uint()`, or `double()` — `number` is the only numeric
116
116
  type. To produce an integer, round explicitly with `math.trunc` / `math.floor`
117
117
  / `math.ceil` / `math.round`.
118
118
 
119
+ `string(number)` renders a plain number (no grouping). For a **formatted**
120
+ display string — thousands separators, fixed decimal places, currency, percent —
121
+ use `number.format(options)`:
122
+ ```cel
123
+ (1234567).format({}) // "1,234,567" (comma grouping on by default)
124
+ (1234567.5).format({"minimumFractionDigits": 2}) // "1,234,567.50"
125
+ (3.14159).format({"maximumFractionDigits": 2}) // "3.14"
126
+ (1234.5).format({"minimumFractionDigits": 2,
127
+ "maximumFractionDigits": 2}) // "1,234.50" (fixed 2 decimals)
128
+ (1234567).format({"useGrouping": false}) // "1234567" (no separators)
129
+ (1234.5).format({"style": "currency", "currency": "CNY"}) // "CN¥1,234.50"
130
+ (0.42).format({"style": "percent"}) // "42%"
131
+ ```
132
+ The map is exactly the `options` object of JavaScript's
133
+ `Number.prototype.toLocaleString(locales, options)` / `Intl.NumberFormat` — the
134
+ locale is fixed to `en-US` (so grouping renders as a comma). The number is
135
+ formatted at full precision (large integers past the float safe range keep every
136
+ digit). An impossible combination (e.g. `minimumFractionDigits` above
137
+ `maximumFractionDigits`) is an error.
138
+
119
139
  ### Math
120
140
  ```cel
121
141
  math.trunc(3.7) // 3 — drop the fraction (toward zero)
@@ -414,12 +434,21 @@ An `optional<T>` is either present (`Some`) or absent (`None`). They come from
414
434
  (the lift — wraps a value as present), `optional.ofNullable(x)` (like `of`, but
415
435
  `null` lifts to absent), and `optional.none()`.
416
436
 
437
+ `.?` / `[?]` treat a `null` field as absent, so `row.?foo` is `None` whether
438
+ `foo` is missing, `null`, or `row` itself is `null` — reach for `orValue` and
439
+ trust it. (This differs from standard CEL, where a null field is present.) Only
440
+ `null` collapses; `""`, `0` and `false` are ordinary present values.
441
+
417
442
  ```cel
418
443
  optional.of(5) // present, holds 5
419
444
  optional.of(null) // present, holds null (of never inspects the value)
420
445
  optional.ofNullable(row.middleName) // absent when the field is null
421
446
  optional.none() // absent
422
447
 
448
+ row.?foo.orValue([]) // [] when foo is missing OR null
449
+ row.?foo.orValue("-") // "-" when absent; "" stays "" (only null collapses)
450
+ has(row.foo) // true even when foo is null — tells null from missing
451
+
423
452
  o.hasValue() // bool — is it present?
424
453
  o.value() // unwrap — ERRORS if absent
425
454
  o.orValue(default) // unwrap, or `default` when absent
@@ -3,7 +3,7 @@ import { Decimal } from "./decimal.js";
3
3
  import { TYPES, Type } from "./registry.js";
4
4
  import { Option } from "effect";
5
5
  import { register as registerOptional, optionalOf } from "./optional.js";
6
- import { objKeys, arrayFrom } from "./globals.js";
6
+ import { objKeys, arrayFrom, isArray } from "./globals.js";
7
7
  import { TZDate } from "@date-fns/tz";
8
8
  import { startOfMinute, startOfHour, startOfDay, startOfWeek, startOfMonth, startOfYear, endOfMinute, endOfHour, endOfDay, endOfWeek, endOfMonth, endOfYear, addMilliseconds, addSeconds, addMinutes, addHours, addDays, addWeeks, addMonths, addYears, setMilliseconds, setSeconds, setMinutes, setHours, setDate as setDayOfMonth, setMonth, setYear, format as formatDate, differenceInMilliseconds, differenceInSeconds, differenceInMinutes, differenceInHours, differenceInDays, differenceInWeeks, differenceInMonths, differenceInYears } from "date-fns";
9
9
  function toNum(v) {
@@ -87,6 +87,18 @@ export function registerFunctions(registry) {
87
87
  functionOverload("string(bool): string", (v) => `${v}`);
88
88
  functionOverload("string(number): string", (v) => Decimal.from(v).toString());
89
89
  functionOverload("string(bytes): string", (v) => ByteOpts.toUtf8(v));
90
+ functionOverload("number.format(map): string", (v, options) => {
91
+ const intlOptions = intlOptionsFromMap(options);
92
+ let fmt;
93
+ try {
94
+ fmt = new Intl.NumberFormat("en-US", intlOptions);
95
+ } catch (e) {
96
+ throw evaluationError("invalid_format_options", `number.format(): ${e.message}`);
97
+ }
98
+ return fmt.format(
99
+ Decimal.from(v).toString()
100
+ );
101
+ });
90
102
  functionOverload("string.startsWith(string): bool", (a, b) => a.startsWith(b));
91
103
  functionOverload("string.endsWith(string): bool", (a, b) => a.endsWith(b));
92
104
  functionOverload("string.contains(string): bool", (a, b) => a.includes(b));
@@ -440,6 +452,21 @@ export function registerFunctions(registry) {
440
452
  functionOverload("string(URL): string", (v) => v.href);
441
453
  registerOptional(registry);
442
454
  }
455
+ function intlOptionsFromMap(m) {
456
+ const out = {};
457
+ const put = (k, val) => {
458
+ out[String(k)] = val instanceof Decimal ? val.toNumber() : typeof val === "bigint" ? Number(val) : val;
459
+ };
460
+ if (m instanceof Map) {
461
+ for (const [k, val] of m) put(k, val);
462
+ } else if (m !== null && typeof m === "object" && !isArray(m)) {
463
+ const obj = m;
464
+ for (const k of objKeys(obj)) put(k, obj[k]);
465
+ } else {
466
+ throw evaluationError("invalid_argument", "number.format(): options must be a map");
467
+ }
468
+ return out;
469
+ }
443
470
  function stringSize(str) {
444
471
  let count = 0;
445
472
  for (const _ of str) count++;
@@ -180,7 +180,8 @@ export class TypeDeclaration {
180
180
  if (unwrapped === void 0) return Option.none();
181
181
  const type = ev.debugType(unwrapped);
182
182
  try {
183
- return optionalOf(type.fieldLazy(unwrapped, key, ast, ev));
183
+ const value = type.fieldLazy(unwrapped, key, ast, ev);
184
+ return value === null ? Option.none() : optionalOf(value);
184
185
  } catch (e) {
185
186
  if (e instanceof EvaluationError) return Option.none();
186
187
  throw e;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shwfed/config",
3
- "version": "3.0.13",
3
+ "version": "3.0.14",
4
4
  "description": "Configurable UI for SHWFED",
5
5
  "type": "module",
6
6
  "publishConfig": {