@shwfed/config 2.2.3 → 2.2.5
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/module.json +1 -1
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.d.vue.ts +3 -4
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.vue +1 -1
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.vue.d.ts +3 -4
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/runtime.vue +9 -2
- package/dist/runtime/components/form/utils/state.d.ts +10 -2
- package/dist/runtime/components/form/utils/state.js +10 -3
- package/dist/runtime/vendor/cel-js/CLAUDE.md +3 -0
- package/dist/runtime/vendor/cel-js/PROMPT.md +14 -0
- package/dist/runtime/vendor/cel-js/lib/evaluator.js +2 -0
- package/dist/runtime/vendor/cel-js/lib/json-builtins.d.ts +2 -0
- package/dist/runtime/vendor/cel-js/lib/json-builtins.js +51 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.d.vue.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import type { FormUnitValue } from '../../../schema.js';
|
|
2
|
-
import { type FormState } from '../../../utils/state.js';
|
|
3
2
|
type __VLS_Props = {
|
|
4
3
|
unit: FormUnitValue;
|
|
5
4
|
index: number;
|
|
6
5
|
};
|
|
7
6
|
type __VLS_ModelProps = {
|
|
8
|
-
modelValue:
|
|
7
|
+
modelValue: unknown;
|
|
9
8
|
};
|
|
10
9
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
11
10
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
12
|
-
"update:modelValue": (value:
|
|
11
|
+
"update:modelValue": (value: unknown) => any;
|
|
13
12
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
14
|
-
"onUpdate:modelValue"?: ((value:
|
|
13
|
+
"onUpdate:modelValue"?: ((value: unknown) => any) | undefined;
|
|
15
14
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
15
|
declare const _default: typeof __VLS_export;
|
|
17
16
|
export default _default;
|
|
@@ -6,7 +6,7 @@ import FormUnitRenderer from "../../../FormUnitRenderer.vue";
|
|
|
6
6
|
import { useDerived, useDerivedQuiescence } from "../../../utils/derived";
|
|
7
7
|
import { provideFormState } from "../../../utils/state";
|
|
8
8
|
defineOptions({ name: "ShwfedListFieldRow" });
|
|
9
|
-
const state = defineModel({ type:
|
|
9
|
+
const state = defineModel({ type: null, ...{ required: true } });
|
|
10
10
|
const props = defineProps({
|
|
11
11
|
unit: { type: Object, required: true },
|
|
12
12
|
index: { type: Number, required: true }
|
package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.vue.d.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import type { FormUnitValue } from '../../../schema.js';
|
|
2
|
-
import { type FormState } from '../../../utils/state.js';
|
|
3
2
|
type __VLS_Props = {
|
|
4
3
|
unit: FormUnitValue;
|
|
5
4
|
index: number;
|
|
6
5
|
};
|
|
7
6
|
type __VLS_ModelProps = {
|
|
8
|
-
modelValue:
|
|
7
|
+
modelValue: unknown;
|
|
9
8
|
};
|
|
10
9
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
11
10
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
12
|
-
"update:modelValue": (value:
|
|
11
|
+
"update:modelValue": (value: unknown) => any;
|
|
13
12
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
14
|
-
"onUpdate:modelValue"?: ((value:
|
|
13
|
+
"onUpdate:modelValue"?: ((value: unknown) => any) | undefined;
|
|
15
14
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
15
|
declare const _default: typeof __VLS_export;
|
|
17
16
|
export default _default;
|
package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/runtime.vue
CHANGED
|
@@ -14,7 +14,7 @@ import { Button } from "../../../../ui/button";
|
|
|
14
14
|
import { Field, FieldLabel } from "../../../../ui/field";
|
|
15
15
|
import { InputGroupButton } from "../../../../ui/input-group";
|
|
16
16
|
import { Markdown } from "../../../../ui/markdown";
|
|
17
|
-
import { useFormState } from "../../../utils/state";
|
|
17
|
+
import { SELF_BINDING, useFormState } from "../../../utils/state";
|
|
18
18
|
import Row from "./row.vue";
|
|
19
19
|
defineOptions({ name: "ShwfedListFieldRuntime" });
|
|
20
20
|
const props = defineProps({
|
|
@@ -45,6 +45,12 @@ function evalBool(expression, label) {
|
|
|
45
45
|
}
|
|
46
46
|
const isDisabled = computed(() => evalBool(props.config.disabled, "disabled"));
|
|
47
47
|
const reorderable = computed(() => props.config.reorderable ?? true);
|
|
48
|
+
const isScalarRow = computed(() => {
|
|
49
|
+
const fields = props.config.unit.fields;
|
|
50
|
+
if (fields.length !== 1) return false;
|
|
51
|
+
const only = fields[0];
|
|
52
|
+
return only.binding === SELF_BINDING;
|
|
53
|
+
});
|
|
48
54
|
const localItems = ref([]);
|
|
49
55
|
const items = computed(() => {
|
|
50
56
|
const path = props.config.binding;
|
|
@@ -120,7 +126,8 @@ const canRemove = computed(() => !isDisabled.value && !atMin.value);
|
|
|
120
126
|
const canReorder = computed(() => !isDisabled.value && reorderable.value);
|
|
121
127
|
function append() {
|
|
122
128
|
if (!canAdd.value) return;
|
|
123
|
-
|
|
129
|
+
const seed = isScalarRow.value ? null : {};
|
|
130
|
+
writeItems([...items.value, seed]);
|
|
124
131
|
const id = makeId();
|
|
125
132
|
rowKeys.value = [...rowKeys.value, id];
|
|
126
133
|
const next = new Set(expandedKeys.value);
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import type { InjectionKey, Ref } from 'vue';
|
|
2
2
|
export type FormState = Record<string, unknown>;
|
|
3
|
+
/**
|
|
4
|
+
* Sentinel binding meaning "the bag's state itself", not a key inside it.
|
|
5
|
+
* Used by `list` rows that hold scalar items: the inner field's `binding: '.'`
|
|
6
|
+
* tells the bag to read/replace `state.value` wholesale instead of walking
|
|
7
|
+
* into it as a dot-prop path. dot-prop has no notion of a self-path, so the
|
|
8
|
+
* bag intercepts this before delegating.
|
|
9
|
+
*/
|
|
10
|
+
export declare const SELF_BINDING: ".";
|
|
3
11
|
export type FormStateBag = {
|
|
4
|
-
state: Ref<
|
|
12
|
+
state: Ref<unknown>;
|
|
5
13
|
getAt: (path: string) => unknown;
|
|
6
14
|
/**
|
|
7
15
|
* Write a value at `path` on behalf of a **user interaction**. Besides
|
|
@@ -33,5 +41,5 @@ export type FormStateBag = {
|
|
|
33
41
|
markDirty: (path: string) => void;
|
|
34
42
|
};
|
|
35
43
|
export declare const FORM_STATE_KEY: InjectionKey<FormStateBag>;
|
|
36
|
-
export declare function provideFormState(state: Ref<
|
|
44
|
+
export declare function provideFormState(state: Ref<unknown>): FormStateBag;
|
|
37
45
|
export declare function useFormState(): FormStateBag;
|
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import { getProperty, setProperty } from "dot-prop";
|
|
2
2
|
import { inject, provide, ref } from "vue";
|
|
3
|
+
export const SELF_BINDING = ".";
|
|
3
4
|
export const FORM_STATE_KEY = Symbol("shwfed-form-state");
|
|
4
5
|
export function provideFormState(state) {
|
|
5
6
|
const dirty = ref(/* @__PURE__ */ new Set());
|
|
6
7
|
function write(path, value) {
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
const normalized = value === void 0 ? null : value;
|
|
9
|
+
if (path === SELF_BINDING) {
|
|
10
|
+
state.value = normalized;
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const current = state.value;
|
|
14
|
+
setProperty(current, path, normalized);
|
|
15
|
+
state.value = { ...current };
|
|
9
16
|
}
|
|
10
17
|
function markDirty(path) {
|
|
11
18
|
if (!dirty.value.has(path)) dirty.value = new Set(dirty.value).add(path);
|
|
12
19
|
}
|
|
13
20
|
const bag = {
|
|
14
21
|
state,
|
|
15
|
-
getAt: (path) => getProperty(state.value, path),
|
|
22
|
+
getAt: (path) => path === SELF_BINDING ? state.value : getProperty(state.value, path),
|
|
16
23
|
setAt: (path, value) => {
|
|
17
24
|
write(path, value);
|
|
18
25
|
markDirty(path);
|
|
@@ -22,6 +22,8 @@ The custom `Optional` class has been replaced with Effect's `Option` type (`impo
|
|
|
22
22
|
|
|
23
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 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.
|
|
24
24
|
|
|
25
|
+
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.
|
|
26
|
+
|
|
25
27
|
Spread syntax (`...expr`) inside list and map literals — not from upstream. A new `ELLIPSIS` token (3-char lookahead in the lexer) and a `spread` AST op let `[1, ...a, 4]` and `{...a, "x": 1, ...b}` desugar inside the parent literal. The spread node's own `check`/`evaluate` are defensive stubs — `parsePrimary` never produces one, so `...` outside a list/map is an "unexpected token" parse error. List `args` shape is unchanged (`IASTNode[]`, with spread elements detected via `op === 'spread'`); map `args` becomes `([IASTNode, IASTNode] | [IASTNode])[]` — a length-1 tuple represents a spread entry, preserving positional ordering for override semantics (later writes win). The fast path is preserved: the `list`/`map` `check` swaps `evaluate` meta to `evaluateSpreadList`/`evaluateSpreadMap` only when a spread child is present; literals without spreads run the original `resolveAstArray`/`safeFromEntries` paths byte-for-byte. Spread map sources may be plain objects, `Map` instances, or registered message types (typed as `map<string, dyn>`); the `__proto__`/`constructor`/`prototype` skip from `safeFromEntries` is applied to spread sources too. Runtime errors fire on non-list/non-map sources (including `null`). `maxListElements` / `maxMapEntries` count a spread as one entry — runtime expansion bypasses those caps (deliberate trade-off). Call-argument spread (`f(...args)`) is **not** supported.
|
|
26
28
|
|
|
27
29
|
## Architecture
|
|
@@ -39,6 +41,7 @@ lib/
|
|
|
39
41
|
├── macros.ts — map, filter, all, exists, exists_one, find, has, cel.bind
|
|
40
42
|
├── http-builtins.ts — `http` constant + http/HttpRequest types (local addition)
|
|
41
43
|
├── http-builder.ts — `HttpRequestBuilder` — what an http.* expression returns
|
|
44
|
+
├── json-builtins.ts — `JSON` constant + `JSON.parse` / `JSON.stringify` (local addition)
|
|
42
45
|
├── decimal.ts — Decimal class (arbitrary precision)
|
|
43
46
|
├── optional.ts — Optional type support (uses Effect Option)
|
|
44
47
|
├── serialize.ts — AST back to CEL string
|
|
@@ -160,6 +160,20 @@ b"hello".at(0) // 104
|
|
|
160
160
|
size(b"hello") // 5
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
+
### JSON
|
|
164
|
+
```cel
|
|
165
|
+
JSON.parse('{"a":1,"b":[2,3]}') // dyn value: {"a": 1, "b": [2, 3]}
|
|
166
|
+
JSON.parse("42") // 42
|
|
167
|
+
JSON.stringify({"a": 1, "b": [2,3]}) // '{"a":1,"b":[2,3]}'
|
|
168
|
+
JSON.stringify(1 + 2) // "3"
|
|
169
|
+
```
|
|
170
|
+
`JSON` is a built-in namespace — you don't import it, it's always available.
|
|
171
|
+
`JSON.parse` accepts any JSON string and returns a `dyn` value.
|
|
172
|
+
`JSON.stringify` takes any value and serializes it; CEL-internal shapes are
|
|
173
|
+
unwrapped first so the output is plain JSON — numbers (CEL's `Decimal`) become
|
|
174
|
+
JS numbers, optionals become their value (or `null` when empty), and `Date`
|
|
175
|
+
values become ISO 8601 strings.
|
|
176
|
+
|
|
163
177
|
### Dates
|
|
164
178
|
```cel
|
|
165
179
|
date("2023-06-15") // create a Date
|
|
@@ -3,6 +3,7 @@ import { createRegistry, RootContext } from "./registry.js";
|
|
|
3
3
|
import { evaluationError } from "./errors.js";
|
|
4
4
|
import { registerFunctions } from "./functions.js";
|
|
5
5
|
import { registerHttpBuiltins } from "./http-builtins.js";
|
|
6
|
+
import { registerJSONBuiltins } from "./json-builtins.js";
|
|
6
7
|
import { registerMacros } from "./macros.js";
|
|
7
8
|
import { registerOverloads } from "./overloads.js";
|
|
8
9
|
import { TypeChecker } from "./type-checker.js";
|
|
@@ -14,6 +15,7 @@ registerFunctions(globalRegistry);
|
|
|
14
15
|
registerOverloads(globalRegistry);
|
|
15
16
|
registerMacros(globalRegistry);
|
|
16
17
|
registerHttpBuiltins(globalRegistry);
|
|
18
|
+
registerJSONBuiltins(globalRegistry);
|
|
17
19
|
class Environment {
|
|
18
20
|
#registry;
|
|
19
21
|
#evaluator;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Option } from "effect";
|
|
2
|
+
import { evaluationError } from "./errors.js";
|
|
3
|
+
import { Decimal } from "./decimal.js";
|
|
4
|
+
class JSONNamespace {
|
|
5
|
+
brand = "JSON";
|
|
6
|
+
}
|
|
7
|
+
const JSON_INSTANCE = new JSONNamespace();
|
|
8
|
+
function normalizeForJSON(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) ? normalizeForJSON(value.value) : null;
|
|
13
|
+
}
|
|
14
|
+
if (value instanceof Date) return value;
|
|
15
|
+
if (value instanceof Map) {
|
|
16
|
+
const out = {};
|
|
17
|
+
for (const [k, v] of value) out[String(k)] = normalizeForJSON(v);
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
if (Array.isArray(value)) return value.map(normalizeForJSON);
|
|
21
|
+
if (typeof value === "object") {
|
|
22
|
+
const proto = Object.getPrototypeOf(value);
|
|
23
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
24
|
+
const out = {};
|
|
25
|
+
for (const key of Object.keys(value)) {
|
|
26
|
+
out[key] = normalizeForJSON(value[key]);
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
export function registerJSONBuiltins(registry) {
|
|
33
|
+
registry.registerType("JSON", JSONNamespace);
|
|
34
|
+
registry.registerVariable({
|
|
35
|
+
name: "JSON",
|
|
36
|
+
type: "JSON",
|
|
37
|
+
value: JSON_INSTANCE,
|
|
38
|
+
description: "JSON \u7F16\u89E3\u7801"
|
|
39
|
+
});
|
|
40
|
+
registry.registerFunctionOverload("JSON.parse(string): dyn", (_r, s) => {
|
|
41
|
+
try {
|
|
42
|
+
return globalThis.JSON.parse(s);
|
|
43
|
+
} catch (e) {
|
|
44
|
+
throw evaluationError("invalid_json", `JSON.parse(): ${e.message}`);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
registry.registerFunctionOverload(
|
|
48
|
+
"JSON.stringify(dyn): string",
|
|
49
|
+
(_r, v) => globalThis.JSON.stringify(normalizeForJSON(v))
|
|
50
|
+
);
|
|
51
|
+
}
|