@atscript/ui-fns 0.1.58

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Artem Maltsev <artem@maltsev.nl>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # @atscript/ui-fns
2
+
3
+ Opt-in plugin for [`@atscript/ui`](../ui) that adds **dynamic** field properties driven by `@ui.fn.*` annotations.
4
+
5
+ Part of the [atscript-ui](https://github.com/moostjs/atscript-ui) monorepo.
6
+
7
+ ## Why it's a separate package
8
+
9
+ `@ui.fn.*` annotations let `.as` schemas declare computed UI behaviour (e.g. `@ui.fn.disabled (form) => form.value.kind !== 'admin'`). Evaluating them requires `new Function` at runtime, which is incompatible with strict CSPs. Splitting the dynamic resolver into its own package keeps `@atscript/ui` CSP-safe by default — consumers opt in only when they need expressions.
10
+
11
+ ## Install
12
+
13
+ ```sh
14
+ pnpm add @atscript/ui-fns
15
+ ```
16
+
17
+ ## Entry points
18
+
19
+ | Subpath | What it exports |
20
+ | ------------------------- | ----------------------------------------------------------------------- |
21
+ | `@atscript/ui-fns` | `FnFieldResolver` — drop-in replacement for the default `FieldResolver` |
22
+ | `@atscript/ui-fns/plugin` | atscript build-time plugin that compiles `@ui.fn.*` bodies |
23
+
24
+ ## License
25
+
26
+ MIT © Artem Maltsev
package/dist/index.cjs ADDED
@@ -0,0 +1,169 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _atscript_ui = require("@atscript/ui");
3
+ //#region src/runtime/fn-compiler.ts
4
+ const pool = new (require("@prostojs/deserialize-fn")).FNPool();
5
+ /**
6
+ * Compiles a field-level function string from a `@ui.form.fn.*` / `@ui.table.fn.*` annotation
7
+ * into a callable function. Uses FNPool for caching.
8
+ *
9
+ * The function string should be an arrow or regular function expression:
10
+ * `"(v, data, ctx, entry) => !data.firstName"`
11
+ *
12
+ * The compiled function receives a single TFnScope object:
13
+ * `{ v, data, context, entry }`
14
+ */
15
+ function compileFieldFn(fnStr) {
16
+ const code = `return (${fnStr})(v, data, context, entry)`;
17
+ return pool.getFn(code);
18
+ }
19
+ /**
20
+ * Compiles a form-level function string from a `@ui.form.fn.title` or similar annotation.
21
+ *
22
+ * The function string should be:
23
+ * `"(data, ctx) => someExpression"`
24
+ *
25
+ * The compiled function receives a single TFnScope object:
26
+ * `{ data, context }`
27
+ */
28
+ function compileTopFn(fnStr) {
29
+ const code = `return (${fnStr})(data, context)`;
30
+ return pool.getFn(code);
31
+ }
32
+ /**
33
+ * Compiles a validator function string.
34
+ * Delegates to `compileFieldFn` with a narrowed return type.
35
+ *
36
+ * Returns `true` for valid, or a string error message for invalid.
37
+ */
38
+ function compileValidatorFn(fnStr) {
39
+ return compileFieldFn(fnStr);
40
+ }
41
+ //#endregion
42
+ //#region src/runtime/dynamic-resolver.ts
43
+ /** Fn keys that store `[{ name, fn }]` arrays rather than a single fn string. */
44
+ const ATTR_FN_KEYS = new Set([_atscript_ui.UI_FORM_FN_ATTR, _atscript_ui.UI_TABLE_FN_ATTR]);
45
+ /**
46
+ * Dynamic field resolver — extends static resolution with `new Function` compilation
47
+ * for `ui.fn.*` annotation keys.
48
+ *
49
+ * Install via `installDynamicResolver()` from the package index.
50
+ */
51
+ var DynamicFieldResolver = class {
52
+ resolveFieldProp(prop, fnKey, staticKey, scope, opts) {
53
+ if (ATTR_FN_KEYS.has(fnKey)) return this.resolveAttrFns(prop, fnKey, scope);
54
+ return resolveAnnotatedProp(prop.metadata, fnKey, staticKey, scope, compileFieldFn, opts);
55
+ }
56
+ resolveFormProp(type, fnKey, staticKey, scope, opts) {
57
+ return resolveAnnotatedProp(type.metadata, fnKey, staticKey, scope, compileTopFn, opts);
58
+ }
59
+ hasComputedAnnotations(prop) {
60
+ for (const key of prop.metadata.keys()) {
61
+ const k = key;
62
+ if (k.startsWith(_atscript_ui.UI_FORM_FN_PREFIX) || k.startsWith(_atscript_ui.UI_TABLE_FN_PREFIX)) return true;
63
+ }
64
+ return false;
65
+ }
66
+ resolveAttrFns(prop, fnKey, scope) {
67
+ const fnAttrs = prop.metadata.get(fnKey);
68
+ if (!fnAttrs) return void 0;
69
+ const result = {};
70
+ let hasAttrs = false;
71
+ for (const item of (0, _atscript_ui.asArray)(fnAttrs)) if (typeof item === "object" && item !== null && "name" in item && "fn" in item) {
72
+ const { name, fn } = item;
73
+ result[name] = compileFieldFn(fn)(scope);
74
+ hasAttrs = true;
75
+ }
76
+ return hasAttrs ? result : void 0;
77
+ }
78
+ };
79
+ function resolveAnnotatedProp(metadata, fnKey, staticKey, scope, compileFn, opts) {
80
+ const fnStr = metadata.get(fnKey);
81
+ if (typeof fnStr === "string") return compileFn(fnStr)(scope);
82
+ return (0, _atscript_ui.resolveStatic)(metadata, staticKey, opts);
83
+ }
84
+ /**
85
+ * Builds a `TFieldEvaluated` entry and returns a full scope containing it.
86
+ *
87
+ * Implements the dual-scope pattern:
88
+ * 1. Resolve constraints (disabled/hidden/readonly) from `baseScope`
89
+ * 2. Assemble the entry object
90
+ * 3. Build full scope (`{ ...baseScope, entry }`)
91
+ * 4. Resolve options into the entry using the full scope
92
+ */
93
+ function buildFieldEntry(prop, baseScope, path, opts) {
94
+ const boolOpts = { staticAsBoolean: true };
95
+ const scopeAsRecord = baseScope;
96
+ const entry = {
97
+ field: path,
98
+ type: opts?.type ?? (0, _atscript_ui.getFieldMeta)(prop, _atscript_ui.UI_FORM_TYPE) ?? (0, _atscript_ui.getFieldMeta)(prop, _atscript_ui.UI_TYPE) ?? "text",
99
+ component: opts?.component ?? (0, _atscript_ui.getFieldMeta)(prop, _atscript_ui.UI_FORM_COMPONENT),
100
+ name: opts?.name ?? path.slice(path.lastIndexOf(".") + 1),
101
+ optional: opts?.optional ?? prop.optional,
102
+ disabled: opts?.disabled ?? (0, _atscript_ui.resolveFieldProp)(prop, _atscript_ui.UI_FORM_FN_DISABLED, _atscript_ui.UI_FORM_DISABLED, scopeAsRecord, boolOpts),
103
+ hidden: opts?.hidden ?? (0, _atscript_ui.resolveFieldProp)(prop, _atscript_ui.UI_FORM_FN_HIDDEN, _atscript_ui.UI_FORM_HIDDEN, scopeAsRecord, boolOpts),
104
+ readonly: opts?.readonly ?? (0, _atscript_ui.resolveFieldProp)(prop, _atscript_ui.UI_FORM_FN_READONLY, _atscript_ui.META_READONLY, scopeAsRecord, boolOpts)
105
+ };
106
+ const scope = {
107
+ ...baseScope,
108
+ entry
109
+ };
110
+ entry.options = (0, _atscript_ui.resolveOptions)(prop, scope);
111
+ return scope;
112
+ }
113
+ //#endregion
114
+ //#region src/runtime/validator-plugin.ts
115
+ /**
116
+ * Creates an ATScript validator plugin that processes `@ui.form.validate` annotations
117
+ * using compiled function strings.
118
+ *
119
+ * Usage:
120
+ * const plugin = uiFnsValidatorPlugin()
121
+ * const validator = new Validator(field.prop, { plugins: [plugin] })
122
+ * validator.validate(value, true, { data: formData, context })
123
+ */
124
+ function uiFnsValidatorPlugin() {
125
+ return (ctx, def, value) => {
126
+ const hasValidators = (0, _atscript_ui.getFieldMeta)(def, _atscript_ui.UI_FORM_VALIDATE);
127
+ if (!hasValidators) return void 0;
128
+ const fnsCtx = ctx.context;
129
+ const scope = buildFieldEntry(def, {
130
+ v: value,
131
+ data: fnsCtx?.data ?? {},
132
+ context: fnsCtx?.context ?? {},
133
+ entry: void 0
134
+ }, ctx.path);
135
+ const fns = (0, _atscript_ui.asArray)(hasValidators);
136
+ for (const fnStr of fns) {
137
+ if (typeof fnStr !== "string") continue;
138
+ const result = compileValidatorFn(fnStr)(scope);
139
+ if (result !== true) {
140
+ ctx.error(typeof result === "string" ? result : "Validation failed");
141
+ return false;
142
+ }
143
+ }
144
+ };
145
+ }
146
+ //#endregion
147
+ //#region src/index.ts
148
+ /**
149
+ * Installs the dynamic field resolver into @atscript/ui.
150
+ * Call this once at app startup to enable `ui.fn.*` annotation resolution
151
+ * and `@ui.form.validate` custom validator strings.
152
+ *
153
+ * ```ts
154
+ * import { installDynamicResolver } from '@atscript/ui-fns'
155
+ * installDynamicResolver()
156
+ * ```
157
+ */
158
+ function installDynamicResolver() {
159
+ (0, _atscript_ui.setResolver)(new DynamicFieldResolver());
160
+ (0, _atscript_ui.setDefaultValidatorPlugins)([uiFnsValidatorPlugin()]);
161
+ }
162
+ //#endregion
163
+ exports.DynamicFieldResolver = DynamicFieldResolver;
164
+ exports.buildFieldEntry = buildFieldEntry;
165
+ exports.compileFieldFn = compileFieldFn;
166
+ exports.compileTopFn = compileTopFn;
167
+ exports.compileValidatorFn = compileValidatorFn;
168
+ exports.installDynamicResolver = installDynamicResolver;
169
+ exports.uiFnsValidatorPlugin = uiFnsValidatorPlugin;
@@ -0,0 +1,123 @@
1
+ import { FieldResolver, TFormEntryOptions, TResolveOptions } from "@atscript/ui";
2
+ import { TAtscriptAnnotatedType, TValidatorPlugin } from "@atscript/typescript/utils";
3
+
4
+ //#region src/runtime/types.d.ts
5
+ /**
6
+ * Scope object passed to compiled functions.
7
+ * Properties become variables inside compiled function strings:
8
+ * v, data, context, entry
9
+ */
10
+ interface TFnScope<V = unknown, D = Record<string, unknown>, C = Record<string, unknown>> {
11
+ v?: V;
12
+ data: D;
13
+ context: C;
14
+ entry?: TFieldEvaluated;
15
+ action?: string;
16
+ }
17
+ /**
18
+ * A value that is either static or a function of the fn scope.
19
+ */
20
+ type TComputed<T> = T | ((scope: TFnScope) => T);
21
+ /**
22
+ * Minimal evaluated snapshot of a field — passed to validators and
23
+ * computed functions as `entry`.
24
+ */
25
+ interface TFieldEvaluated {
26
+ field: string;
27
+ type: string;
28
+ component?: string;
29
+ name: string;
30
+ disabled?: boolean;
31
+ optional?: boolean;
32
+ hidden?: boolean;
33
+ readonly?: boolean;
34
+ options?: TFormEntryOptions[];
35
+ }
36
+ //#endregion
37
+ //#region src/runtime/fn-compiler.d.ts
38
+ /**
39
+ * Compiles a field-level function string from a `@ui.form.fn.*` / `@ui.table.fn.*` annotation
40
+ * into a callable function. Uses FNPool for caching.
41
+ *
42
+ * The function string should be an arrow or regular function expression:
43
+ * `"(v, data, ctx, entry) => !data.firstName"`
44
+ *
45
+ * The compiled function receives a single TFnScope object:
46
+ * `{ v, data, context, entry }`
47
+ */
48
+ declare function compileFieldFn<R = unknown>(fnStr: string): (scope: TFnScope) => R;
49
+ /**
50
+ * Compiles a form-level function string from a `@ui.form.fn.title` or similar annotation.
51
+ *
52
+ * The function string should be:
53
+ * `"(data, ctx) => someExpression"`
54
+ *
55
+ * The compiled function receives a single TFnScope object:
56
+ * `{ data, context }`
57
+ */
58
+ declare function compileTopFn<R = unknown>(fnStr: string): (scope: TFnScope) => R;
59
+ /**
60
+ * Compiles a validator function string.
61
+ * Delegates to `compileFieldFn` with a narrowed return type.
62
+ *
63
+ * Returns `true` for valid, or a string error message for invalid.
64
+ */
65
+ declare function compileValidatorFn(fnStr: string): (scope: TFnScope) => string | boolean;
66
+ //#endregion
67
+ //#region src/runtime/dynamic-resolver.d.ts
68
+ /** Options for buildFieldEntry — allows pre-resolved overrides. */
69
+ type TBuildFieldEntryOpts = Partial<Pick<TFieldEvaluated, "name" | "type" | "component" | "optional" | "disabled" | "hidden" | "readonly">>;
70
+ /**
71
+ * Dynamic field resolver — extends static resolution with `new Function` compilation
72
+ * for `ui.fn.*` annotation keys.
73
+ *
74
+ * Install via `installDynamicResolver()` from the package index.
75
+ */
76
+ declare class DynamicFieldResolver implements FieldResolver {
77
+ resolveFieldProp<T>(prop: TAtscriptAnnotatedType, fnKey: string, staticKey: string | undefined, scope: Record<string, unknown>, opts?: TResolveOptions<T>): T | undefined;
78
+ resolveFormProp<T>(type: TAtscriptAnnotatedType, fnKey: string, staticKey: string | undefined, scope: Record<string, unknown>, opts?: TResolveOptions<T>): T | undefined;
79
+ hasComputedAnnotations(prop: TAtscriptAnnotatedType): boolean;
80
+ private resolveAttrFns;
81
+ }
82
+ /**
83
+ * Builds a `TFieldEvaluated` entry and returns a full scope containing it.
84
+ *
85
+ * Implements the dual-scope pattern:
86
+ * 1. Resolve constraints (disabled/hidden/readonly) from `baseScope`
87
+ * 2. Assemble the entry object
88
+ * 3. Build full scope (`{ ...baseScope, entry }`)
89
+ * 4. Resolve options into the entry using the full scope
90
+ */
91
+ declare function buildFieldEntry(prop: TAtscriptAnnotatedType, baseScope: TFnScope, path: string, opts?: TBuildFieldEntryOpts): TFnScope;
92
+ //#endregion
93
+ //#region src/runtime/validator-plugin.d.ts
94
+ /** Per-call context passed via `validator.validate(value, safe, context)`. */
95
+ interface TValidatorContext {
96
+ data: Record<string, unknown>;
97
+ context: Record<string, unknown>;
98
+ }
99
+ /**
100
+ * Creates an ATScript validator plugin that processes `@ui.form.validate` annotations
101
+ * using compiled function strings.
102
+ *
103
+ * Usage:
104
+ * const plugin = uiFnsValidatorPlugin()
105
+ * const validator = new Validator(field.prop, { plugins: [plugin] })
106
+ * validator.validate(value, true, { data: formData, context })
107
+ */
108
+ declare function uiFnsValidatorPlugin(): TValidatorPlugin;
109
+ //#endregion
110
+ //#region src/index.d.ts
111
+ /**
112
+ * Installs the dynamic field resolver into @atscript/ui.
113
+ * Call this once at app startup to enable `ui.fn.*` annotation resolution
114
+ * and `@ui.form.validate` custom validator strings.
115
+ *
116
+ * ```ts
117
+ * import { installDynamicResolver } from '@atscript/ui-fns'
118
+ * installDynamicResolver()
119
+ * ```
120
+ */
121
+ declare function installDynamicResolver(): void;
122
+ //#endregion
123
+ export { DynamicFieldResolver, type TBuildFieldEntryOpts, type TComputed, type TFieldEvaluated, type TFnScope, type TValidatorContext, buildFieldEntry, compileFieldFn, compileTopFn, compileValidatorFn, installDynamicResolver, uiFnsValidatorPlugin };
@@ -0,0 +1,123 @@
1
+ import { FieldResolver, TFormEntryOptions, TResolveOptions } from "@atscript/ui";
2
+ import { TAtscriptAnnotatedType, TValidatorPlugin } from "@atscript/typescript/utils";
3
+
4
+ //#region src/runtime/types.d.ts
5
+ /**
6
+ * Scope object passed to compiled functions.
7
+ * Properties become variables inside compiled function strings:
8
+ * v, data, context, entry
9
+ */
10
+ interface TFnScope<V = unknown, D = Record<string, unknown>, C = Record<string, unknown>> {
11
+ v?: V;
12
+ data: D;
13
+ context: C;
14
+ entry?: TFieldEvaluated;
15
+ action?: string;
16
+ }
17
+ /**
18
+ * A value that is either static or a function of the fn scope.
19
+ */
20
+ type TComputed<T> = T | ((scope: TFnScope) => T);
21
+ /**
22
+ * Minimal evaluated snapshot of a field — passed to validators and
23
+ * computed functions as `entry`.
24
+ */
25
+ interface TFieldEvaluated {
26
+ field: string;
27
+ type: string;
28
+ component?: string;
29
+ name: string;
30
+ disabled?: boolean;
31
+ optional?: boolean;
32
+ hidden?: boolean;
33
+ readonly?: boolean;
34
+ options?: TFormEntryOptions[];
35
+ }
36
+ //#endregion
37
+ //#region src/runtime/fn-compiler.d.ts
38
+ /**
39
+ * Compiles a field-level function string from a `@ui.form.fn.*` / `@ui.table.fn.*` annotation
40
+ * into a callable function. Uses FNPool for caching.
41
+ *
42
+ * The function string should be an arrow or regular function expression:
43
+ * `"(v, data, ctx, entry) => !data.firstName"`
44
+ *
45
+ * The compiled function receives a single TFnScope object:
46
+ * `{ v, data, context, entry }`
47
+ */
48
+ declare function compileFieldFn<R = unknown>(fnStr: string): (scope: TFnScope) => R;
49
+ /**
50
+ * Compiles a form-level function string from a `@ui.form.fn.title` or similar annotation.
51
+ *
52
+ * The function string should be:
53
+ * `"(data, ctx) => someExpression"`
54
+ *
55
+ * The compiled function receives a single TFnScope object:
56
+ * `{ data, context }`
57
+ */
58
+ declare function compileTopFn<R = unknown>(fnStr: string): (scope: TFnScope) => R;
59
+ /**
60
+ * Compiles a validator function string.
61
+ * Delegates to `compileFieldFn` with a narrowed return type.
62
+ *
63
+ * Returns `true` for valid, or a string error message for invalid.
64
+ */
65
+ declare function compileValidatorFn(fnStr: string): (scope: TFnScope) => string | boolean;
66
+ //#endregion
67
+ //#region src/runtime/dynamic-resolver.d.ts
68
+ /** Options for buildFieldEntry — allows pre-resolved overrides. */
69
+ type TBuildFieldEntryOpts = Partial<Pick<TFieldEvaluated, "name" | "type" | "component" | "optional" | "disabled" | "hidden" | "readonly">>;
70
+ /**
71
+ * Dynamic field resolver — extends static resolution with `new Function` compilation
72
+ * for `ui.fn.*` annotation keys.
73
+ *
74
+ * Install via `installDynamicResolver()` from the package index.
75
+ */
76
+ declare class DynamicFieldResolver implements FieldResolver {
77
+ resolveFieldProp<T>(prop: TAtscriptAnnotatedType, fnKey: string, staticKey: string | undefined, scope: Record<string, unknown>, opts?: TResolveOptions<T>): T | undefined;
78
+ resolveFormProp<T>(type: TAtscriptAnnotatedType, fnKey: string, staticKey: string | undefined, scope: Record<string, unknown>, opts?: TResolveOptions<T>): T | undefined;
79
+ hasComputedAnnotations(prop: TAtscriptAnnotatedType): boolean;
80
+ private resolveAttrFns;
81
+ }
82
+ /**
83
+ * Builds a `TFieldEvaluated` entry and returns a full scope containing it.
84
+ *
85
+ * Implements the dual-scope pattern:
86
+ * 1. Resolve constraints (disabled/hidden/readonly) from `baseScope`
87
+ * 2. Assemble the entry object
88
+ * 3. Build full scope (`{ ...baseScope, entry }`)
89
+ * 4. Resolve options into the entry using the full scope
90
+ */
91
+ declare function buildFieldEntry(prop: TAtscriptAnnotatedType, baseScope: TFnScope, path: string, opts?: TBuildFieldEntryOpts): TFnScope;
92
+ //#endregion
93
+ //#region src/runtime/validator-plugin.d.ts
94
+ /** Per-call context passed via `validator.validate(value, safe, context)`. */
95
+ interface TValidatorContext {
96
+ data: Record<string, unknown>;
97
+ context: Record<string, unknown>;
98
+ }
99
+ /**
100
+ * Creates an ATScript validator plugin that processes `@ui.form.validate` annotations
101
+ * using compiled function strings.
102
+ *
103
+ * Usage:
104
+ * const plugin = uiFnsValidatorPlugin()
105
+ * const validator = new Validator(field.prop, { plugins: [plugin] })
106
+ * validator.validate(value, true, { data: formData, context })
107
+ */
108
+ declare function uiFnsValidatorPlugin(): TValidatorPlugin;
109
+ //#endregion
110
+ //#region src/index.d.ts
111
+ /**
112
+ * Installs the dynamic field resolver into @atscript/ui.
113
+ * Call this once at app startup to enable `ui.fn.*` annotation resolution
114
+ * and `@ui.form.validate` custom validator strings.
115
+ *
116
+ * ```ts
117
+ * import { installDynamicResolver } from '@atscript/ui-fns'
118
+ * installDynamicResolver()
119
+ * ```
120
+ */
121
+ declare function installDynamicResolver(): void;
122
+ //#endregion
123
+ export { DynamicFieldResolver, type TBuildFieldEntryOpts, type TComputed, type TFieldEvaluated, type TFnScope, type TValidatorContext, buildFieldEntry, compileFieldFn, compileTopFn, compileValidatorFn, installDynamicResolver, uiFnsValidatorPlugin };
package/dist/index.mjs ADDED
@@ -0,0 +1,163 @@
1
+ import { META_READONLY, UI_FORM_COMPONENT, UI_FORM_DISABLED, UI_FORM_FN_ATTR, UI_FORM_FN_DISABLED, UI_FORM_FN_HIDDEN, UI_FORM_FN_PREFIX, UI_FORM_FN_READONLY, UI_FORM_HIDDEN, UI_FORM_TYPE, UI_FORM_VALIDATE, UI_TABLE_FN_ATTR, UI_TABLE_FN_PREFIX, UI_TYPE, asArray, getFieldMeta, resolveFieldProp, resolveOptions, resolveStatic, setDefaultValidatorPlugins, setResolver } from "@atscript/ui";
2
+ import { FNPool } from "@prostojs/deserialize-fn";
3
+ //#region src/runtime/fn-compiler.ts
4
+ const pool = new FNPool();
5
+ /**
6
+ * Compiles a field-level function string from a `@ui.form.fn.*` / `@ui.table.fn.*` annotation
7
+ * into a callable function. Uses FNPool for caching.
8
+ *
9
+ * The function string should be an arrow or regular function expression:
10
+ * `"(v, data, ctx, entry) => !data.firstName"`
11
+ *
12
+ * The compiled function receives a single TFnScope object:
13
+ * `{ v, data, context, entry }`
14
+ */
15
+ function compileFieldFn(fnStr) {
16
+ const code = `return (${fnStr})(v, data, context, entry)`;
17
+ return pool.getFn(code);
18
+ }
19
+ /**
20
+ * Compiles a form-level function string from a `@ui.form.fn.title` or similar annotation.
21
+ *
22
+ * The function string should be:
23
+ * `"(data, ctx) => someExpression"`
24
+ *
25
+ * The compiled function receives a single TFnScope object:
26
+ * `{ data, context }`
27
+ */
28
+ function compileTopFn(fnStr) {
29
+ const code = `return (${fnStr})(data, context)`;
30
+ return pool.getFn(code);
31
+ }
32
+ /**
33
+ * Compiles a validator function string.
34
+ * Delegates to `compileFieldFn` with a narrowed return type.
35
+ *
36
+ * Returns `true` for valid, or a string error message for invalid.
37
+ */
38
+ function compileValidatorFn(fnStr) {
39
+ return compileFieldFn(fnStr);
40
+ }
41
+ //#endregion
42
+ //#region src/runtime/dynamic-resolver.ts
43
+ /** Fn keys that store `[{ name, fn }]` arrays rather than a single fn string. */
44
+ const ATTR_FN_KEYS = new Set([UI_FORM_FN_ATTR, UI_TABLE_FN_ATTR]);
45
+ /**
46
+ * Dynamic field resolver — extends static resolution with `new Function` compilation
47
+ * for `ui.fn.*` annotation keys.
48
+ *
49
+ * Install via `installDynamicResolver()` from the package index.
50
+ */
51
+ var DynamicFieldResolver = class {
52
+ resolveFieldProp(prop, fnKey, staticKey, scope, opts) {
53
+ if (ATTR_FN_KEYS.has(fnKey)) return this.resolveAttrFns(prop, fnKey, scope);
54
+ return resolveAnnotatedProp(prop.metadata, fnKey, staticKey, scope, compileFieldFn, opts);
55
+ }
56
+ resolveFormProp(type, fnKey, staticKey, scope, opts) {
57
+ return resolveAnnotatedProp(type.metadata, fnKey, staticKey, scope, compileTopFn, opts);
58
+ }
59
+ hasComputedAnnotations(prop) {
60
+ for (const key of prop.metadata.keys()) {
61
+ const k = key;
62
+ if (k.startsWith(UI_FORM_FN_PREFIX) || k.startsWith(UI_TABLE_FN_PREFIX)) return true;
63
+ }
64
+ return false;
65
+ }
66
+ resolveAttrFns(prop, fnKey, scope) {
67
+ const fnAttrs = prop.metadata.get(fnKey);
68
+ if (!fnAttrs) return void 0;
69
+ const result = {};
70
+ let hasAttrs = false;
71
+ for (const item of asArray(fnAttrs)) if (typeof item === "object" && item !== null && "name" in item && "fn" in item) {
72
+ const { name, fn } = item;
73
+ result[name] = compileFieldFn(fn)(scope);
74
+ hasAttrs = true;
75
+ }
76
+ return hasAttrs ? result : void 0;
77
+ }
78
+ };
79
+ function resolveAnnotatedProp(metadata, fnKey, staticKey, scope, compileFn, opts) {
80
+ const fnStr = metadata.get(fnKey);
81
+ if (typeof fnStr === "string") return compileFn(fnStr)(scope);
82
+ return resolveStatic(metadata, staticKey, opts);
83
+ }
84
+ /**
85
+ * Builds a `TFieldEvaluated` entry and returns a full scope containing it.
86
+ *
87
+ * Implements the dual-scope pattern:
88
+ * 1. Resolve constraints (disabled/hidden/readonly) from `baseScope`
89
+ * 2. Assemble the entry object
90
+ * 3. Build full scope (`{ ...baseScope, entry }`)
91
+ * 4. Resolve options into the entry using the full scope
92
+ */
93
+ function buildFieldEntry(prop, baseScope, path, opts) {
94
+ const boolOpts = { staticAsBoolean: true };
95
+ const scopeAsRecord = baseScope;
96
+ const entry = {
97
+ field: path,
98
+ type: opts?.type ?? getFieldMeta(prop, UI_FORM_TYPE) ?? getFieldMeta(prop, UI_TYPE) ?? "text",
99
+ component: opts?.component ?? getFieldMeta(prop, UI_FORM_COMPONENT),
100
+ name: opts?.name ?? path.slice(path.lastIndexOf(".") + 1),
101
+ optional: opts?.optional ?? prop.optional,
102
+ disabled: opts?.disabled ?? resolveFieldProp(prop, UI_FORM_FN_DISABLED, UI_FORM_DISABLED, scopeAsRecord, boolOpts),
103
+ hidden: opts?.hidden ?? resolveFieldProp(prop, UI_FORM_FN_HIDDEN, UI_FORM_HIDDEN, scopeAsRecord, boolOpts),
104
+ readonly: opts?.readonly ?? resolveFieldProp(prop, UI_FORM_FN_READONLY, META_READONLY, scopeAsRecord, boolOpts)
105
+ };
106
+ const scope = {
107
+ ...baseScope,
108
+ entry
109
+ };
110
+ entry.options = resolveOptions(prop, scope);
111
+ return scope;
112
+ }
113
+ //#endregion
114
+ //#region src/runtime/validator-plugin.ts
115
+ /**
116
+ * Creates an ATScript validator plugin that processes `@ui.form.validate` annotations
117
+ * using compiled function strings.
118
+ *
119
+ * Usage:
120
+ * const plugin = uiFnsValidatorPlugin()
121
+ * const validator = new Validator(field.prop, { plugins: [plugin] })
122
+ * validator.validate(value, true, { data: formData, context })
123
+ */
124
+ function uiFnsValidatorPlugin() {
125
+ return (ctx, def, value) => {
126
+ const hasValidators = getFieldMeta(def, UI_FORM_VALIDATE);
127
+ if (!hasValidators) return void 0;
128
+ const fnsCtx = ctx.context;
129
+ const scope = buildFieldEntry(def, {
130
+ v: value,
131
+ data: fnsCtx?.data ?? {},
132
+ context: fnsCtx?.context ?? {},
133
+ entry: void 0
134
+ }, ctx.path);
135
+ const fns = asArray(hasValidators);
136
+ for (const fnStr of fns) {
137
+ if (typeof fnStr !== "string") continue;
138
+ const result = compileValidatorFn(fnStr)(scope);
139
+ if (result !== true) {
140
+ ctx.error(typeof result === "string" ? result : "Validation failed");
141
+ return false;
142
+ }
143
+ }
144
+ };
145
+ }
146
+ //#endregion
147
+ //#region src/index.ts
148
+ /**
149
+ * Installs the dynamic field resolver into @atscript/ui.
150
+ * Call this once at app startup to enable `ui.fn.*` annotation resolution
151
+ * and `@ui.form.validate` custom validator strings.
152
+ *
153
+ * ```ts
154
+ * import { installDynamicResolver } from '@atscript/ui-fns'
155
+ * installDynamicResolver()
156
+ * ```
157
+ */
158
+ function installDynamicResolver() {
159
+ setResolver(new DynamicFieldResolver());
160
+ setDefaultValidatorPlugins([uiFnsValidatorPlugin()]);
161
+ }
162
+ //#endregion
163
+ export { DynamicFieldResolver, buildFieldEntry, compileFieldFn, compileTopFn, compileValidatorFn, installDynamicResolver, uiFnsValidatorPlugin };
@@ -0,0 +1,156 @@
1
+ let _atscript_core = require("@atscript/core");
2
+ //#region src/plugin/annotations.ts
3
+ /**
4
+ * Validates a function string by attempting to compile it with `new Function`.
5
+ * Used by `ui.form.fn.*` / `ui.table.fn.*` and `ui.form.validate` annotation validate hooks.
6
+ */
7
+ function validateFnString(fnStr, range) {
8
+ try {
9
+ new Function("v", "data", "context", "entry", `return (${fnStr})(v, data, context, entry)`);
10
+ } catch (error) {
11
+ return [{
12
+ severity: 1,
13
+ message: `Invalid function string: ${error.message}`,
14
+ range
15
+ }];
16
+ }
17
+ }
18
+ function makeFnAnnotation(description, mode) {
19
+ return new _atscript_core.AnnotationSpec({
20
+ description,
21
+ nodeType: mode === "field" ? ["prop", "type"] : ["interface", "type"],
22
+ argument: {
23
+ name: "fn",
24
+ type: "string",
25
+ description: mode === "field" ? "JS function string: (value, data, context, entry) => result" : "JS function string: (data, context) => result"
26
+ },
27
+ validate: validateFirstArg
28
+ });
29
+ }
30
+ /** Shared validate hook: validates the fn string at args[0]. */
31
+ function validateFirstArg(_token, args) {
32
+ if (args[0]) return validateFnString(args[0].text, args[0].range);
33
+ }
34
+ const fnAnnotation = (description) => makeFnAnnotation(description, "field");
35
+ const fnTopAnnotation = (description) => makeFnAnnotation(description, "top");
36
+ const TABLE_ROW_SCOPE_DOC = "Receives `{ row, ctx }` where `row` is the current row's data object and `ctx` carries table-level context (minimum keys: `searchTerm`, `filters`, `sorters`, `rowIndex`). Per-row+cell scope only — every expression must be meaningful when applied to a single cell.";
37
+ function tableFnAnnotation(description) {
38
+ return new _atscript_core.AnnotationSpec({
39
+ description: `${description}\n\n${TABLE_ROW_SCOPE_DOC}`,
40
+ nodeType: ["prop", "type"],
41
+ argument: {
42
+ name: "fn",
43
+ type: "string",
44
+ description: "JS function string evaluated against the per-row scope `{ row, ctx }`."
45
+ },
46
+ validate: validateFirstArg
47
+ });
48
+ }
49
+ const fnAttrSpec = new _atscript_core.AnnotationSpec({
50
+ description: "Computed custom attribute/prop. Name is the attribute/prop name, fn returns the value.",
51
+ nodeType: ["prop", "type"],
52
+ multiple: true,
53
+ mergeStrategy: "replace",
54
+ argument: [{
55
+ name: "name",
56
+ type: "string",
57
+ description: "Attribute/prop name (e.g., \"data-testid\", \"variant\", \"size\")"
58
+ }, {
59
+ name: "fn",
60
+ type: "string",
61
+ description: "JS function string: (value, data, context, entry) => any"
62
+ }],
63
+ validate(_token, args) {
64
+ if (args[1]) return validateFnString(args[1].text, args[1].range);
65
+ }
66
+ });
67
+ const tableFnAttrSpec = new _atscript_core.AnnotationSpec({
68
+ description: "Per-row computed attribute/prop applied to the rendered `<td>`. Name is the attribute/prop name, fn returns the value.\n\n" + TABLE_ROW_SCOPE_DOC,
69
+ nodeType: ["prop", "type"],
70
+ multiple: true,
71
+ mergeStrategy: "replace",
72
+ argument: [{
73
+ name: "name",
74
+ type: "string",
75
+ description: "Attribute/prop name (e.g., \"title\", \"data-row\", \"aria-label\")"
76
+ }, {
77
+ name: "fn",
78
+ type: "string",
79
+ description: "JS function string evaluated against the per-row scope `{ row, ctx }`."
80
+ }],
81
+ validate(_token, args) {
82
+ if (args[1]) return validateFnString(args[1].text, args[1].range);
83
+ }
84
+ });
85
+ /**
86
+ * Annotation specs for dynamic computed annotations and `ui.form.validate`.
87
+ *
88
+ * Registered as atscript annotations via the `uiFnsPlugin()`.
89
+ * Static `@ui.*` annotations and primitives are provided by `@atscript/ui/plugin`.
90
+ */
91
+ const uiFnsAnnotations = { ui: {
92
+ form: {
93
+ validate: new _atscript_core.AnnotationSpec({
94
+ description: "Custom JS validator function string. Returns true for pass, or an error message string.",
95
+ nodeType: ["prop", "type"],
96
+ multiple: true,
97
+ mergeStrategy: "append",
98
+ argument: {
99
+ name: "fn",
100
+ type: "string",
101
+ description: "JS function string: (value, data, context, entry) => boolean | string"
102
+ },
103
+ validate: validateFirstArg
104
+ }),
105
+ fn: {
106
+ title: fnTopAnnotation("Computed form title: (data, context) => string"),
107
+ submit: {
108
+ text: fnTopAnnotation("Computed submit button text: (data, context) => string"),
109
+ disabled: fnTopAnnotation("Computed submit disabled state: (data, context) => boolean")
110
+ },
111
+ label: fnAnnotation("Computed label: (value, data, context, entry) => string"),
112
+ description: fnAnnotation("Computed description: (value, data, context, entry) => string"),
113
+ hint: fnAnnotation("Computed hint: (value, data, context, entry) => string"),
114
+ placeholder: fnAnnotation("Computed placeholder: (value, data, context, entry) => string"),
115
+ disabled: fnAnnotation("Computed disabled state: (value, data, context, entry) => boolean"),
116
+ hidden: fnAnnotation("Computed hidden state: (value, data, context, entry) => boolean"),
117
+ readonly: fnAnnotation("Computed readonly state: (value, data, context, entry) => boolean"),
118
+ value: fnAnnotation("Computed default value: (value, data, context, entry) => any"),
119
+ classes: fnAnnotation("Computed CSS classes: (value, data, context, entry) => string | Record<string, boolean>"),
120
+ styles: fnAnnotation("Computed inline styles: (value, data, context, entry) => string | Record<string, string>"),
121
+ options: fnAnnotation("Computed select/radio options: (value, data, context, entry) => Array"),
122
+ attr: fnAttrSpec
123
+ }
124
+ },
125
+ table: { fn: {
126
+ attr: tableFnAttrSpec,
127
+ classes: tableFnAnnotation("Per-row computed CSS classes for the cell `<td>`: `(row, ctx) => string | Record<string, boolean>`"),
128
+ styles: tableFnAnnotation("Per-row computed inline styles for the cell `<td>`: `(row, ctx) => string | Record<string, string>`")
129
+ } }
130
+ } };
131
+ //#endregion
132
+ //#region src/plugin.ts
133
+ /**
134
+ * ATScript plugin that registers `ui.form.fn.*` / `ui.table.fn.*` computed annotations and `ui.form.validate`.
135
+ *
136
+ * Static `@ui.*` annotations and UI primitives are provided by `@atscript/ui/plugin`.
137
+ *
138
+ * Install in your `atscript.config.ts`:
139
+ * ```ts
140
+ * import uiFnsPlugin from '@atscript/ui-fns/plugin'
141
+ *
142
+ * export default {
143
+ * plugins: [uiFnsPlugin()],
144
+ * }
145
+ * ```
146
+ */
147
+ function uiFnsPlugin() {
148
+ return {
149
+ name: "ui-fns",
150
+ config() {
151
+ return { annotations: uiFnsAnnotations };
152
+ }
153
+ };
154
+ }
155
+ //#endregion
156
+ module.exports = uiFnsPlugin;
@@ -0,0 +1,19 @@
1
+ import { TAtscriptPlugin } from "@atscript/core";
2
+
3
+ //#region src/plugin.d.ts
4
+ /**
5
+ * ATScript plugin that registers `ui.form.fn.*` / `ui.table.fn.*` computed annotations and `ui.form.validate`.
6
+ *
7
+ * Static `@ui.*` annotations and UI primitives are provided by `@atscript/ui/plugin`.
8
+ *
9
+ * Install in your `atscript.config.ts`:
10
+ * ```ts
11
+ * import uiFnsPlugin from '@atscript/ui-fns/plugin'
12
+ *
13
+ * export default {
14
+ * plugins: [uiFnsPlugin()],
15
+ * }
16
+ * ```
17
+ */
18
+ declare function uiFnsPlugin(): TAtscriptPlugin;
19
+ export = uiFnsPlugin;
@@ -0,0 +1,20 @@
1
+ import { TAtscriptPlugin } from "@atscript/core";
2
+
3
+ //#region src/plugin.d.ts
4
+ /**
5
+ * ATScript plugin that registers `ui.form.fn.*` / `ui.table.fn.*` computed annotations and `ui.form.validate`.
6
+ *
7
+ * Static `@ui.*` annotations and UI primitives are provided by `@atscript/ui/plugin`.
8
+ *
9
+ * Install in your `atscript.config.ts`:
10
+ * ```ts
11
+ * import uiFnsPlugin from '@atscript/ui-fns/plugin'
12
+ *
13
+ * export default {
14
+ * plugins: [uiFnsPlugin()],
15
+ * }
16
+ * ```
17
+ */
18
+ declare function uiFnsPlugin(): TAtscriptPlugin;
19
+ //#endregion
20
+ export { uiFnsPlugin as default };
@@ -0,0 +1,156 @@
1
+ import { AnnotationSpec } from "@atscript/core";
2
+ //#region src/plugin/annotations.ts
3
+ /**
4
+ * Validates a function string by attempting to compile it with `new Function`.
5
+ * Used by `ui.form.fn.*` / `ui.table.fn.*` and `ui.form.validate` annotation validate hooks.
6
+ */
7
+ function validateFnString(fnStr, range) {
8
+ try {
9
+ new Function("v", "data", "context", "entry", `return (${fnStr})(v, data, context, entry)`);
10
+ } catch (error) {
11
+ return [{
12
+ severity: 1,
13
+ message: `Invalid function string: ${error.message}`,
14
+ range
15
+ }];
16
+ }
17
+ }
18
+ function makeFnAnnotation(description, mode) {
19
+ return new AnnotationSpec({
20
+ description,
21
+ nodeType: mode === "field" ? ["prop", "type"] : ["interface", "type"],
22
+ argument: {
23
+ name: "fn",
24
+ type: "string",
25
+ description: mode === "field" ? "JS function string: (value, data, context, entry) => result" : "JS function string: (data, context) => result"
26
+ },
27
+ validate: validateFirstArg
28
+ });
29
+ }
30
+ /** Shared validate hook: validates the fn string at args[0]. */
31
+ function validateFirstArg(_token, args) {
32
+ if (args[0]) return validateFnString(args[0].text, args[0].range);
33
+ }
34
+ const fnAnnotation = (description) => makeFnAnnotation(description, "field");
35
+ const fnTopAnnotation = (description) => makeFnAnnotation(description, "top");
36
+ const TABLE_ROW_SCOPE_DOC = "Receives `{ row, ctx }` where `row` is the current row's data object and `ctx` carries table-level context (minimum keys: `searchTerm`, `filters`, `sorters`, `rowIndex`). Per-row+cell scope only — every expression must be meaningful when applied to a single cell.";
37
+ function tableFnAnnotation(description) {
38
+ return new AnnotationSpec({
39
+ description: `${description}\n\n${TABLE_ROW_SCOPE_DOC}`,
40
+ nodeType: ["prop", "type"],
41
+ argument: {
42
+ name: "fn",
43
+ type: "string",
44
+ description: "JS function string evaluated against the per-row scope `{ row, ctx }`."
45
+ },
46
+ validate: validateFirstArg
47
+ });
48
+ }
49
+ const fnAttrSpec = new AnnotationSpec({
50
+ description: "Computed custom attribute/prop. Name is the attribute/prop name, fn returns the value.",
51
+ nodeType: ["prop", "type"],
52
+ multiple: true,
53
+ mergeStrategy: "replace",
54
+ argument: [{
55
+ name: "name",
56
+ type: "string",
57
+ description: "Attribute/prop name (e.g., \"data-testid\", \"variant\", \"size\")"
58
+ }, {
59
+ name: "fn",
60
+ type: "string",
61
+ description: "JS function string: (value, data, context, entry) => any"
62
+ }],
63
+ validate(_token, args) {
64
+ if (args[1]) return validateFnString(args[1].text, args[1].range);
65
+ }
66
+ });
67
+ const tableFnAttrSpec = new AnnotationSpec({
68
+ description: "Per-row computed attribute/prop applied to the rendered `<td>`. Name is the attribute/prop name, fn returns the value.\n\n" + TABLE_ROW_SCOPE_DOC,
69
+ nodeType: ["prop", "type"],
70
+ multiple: true,
71
+ mergeStrategy: "replace",
72
+ argument: [{
73
+ name: "name",
74
+ type: "string",
75
+ description: "Attribute/prop name (e.g., \"title\", \"data-row\", \"aria-label\")"
76
+ }, {
77
+ name: "fn",
78
+ type: "string",
79
+ description: "JS function string evaluated against the per-row scope `{ row, ctx }`."
80
+ }],
81
+ validate(_token, args) {
82
+ if (args[1]) return validateFnString(args[1].text, args[1].range);
83
+ }
84
+ });
85
+ /**
86
+ * Annotation specs for dynamic computed annotations and `ui.form.validate`.
87
+ *
88
+ * Registered as atscript annotations via the `uiFnsPlugin()`.
89
+ * Static `@ui.*` annotations and primitives are provided by `@atscript/ui/plugin`.
90
+ */
91
+ const uiFnsAnnotations = { ui: {
92
+ form: {
93
+ validate: new AnnotationSpec({
94
+ description: "Custom JS validator function string. Returns true for pass, or an error message string.",
95
+ nodeType: ["prop", "type"],
96
+ multiple: true,
97
+ mergeStrategy: "append",
98
+ argument: {
99
+ name: "fn",
100
+ type: "string",
101
+ description: "JS function string: (value, data, context, entry) => boolean | string"
102
+ },
103
+ validate: validateFirstArg
104
+ }),
105
+ fn: {
106
+ title: fnTopAnnotation("Computed form title: (data, context) => string"),
107
+ submit: {
108
+ text: fnTopAnnotation("Computed submit button text: (data, context) => string"),
109
+ disabled: fnTopAnnotation("Computed submit disabled state: (data, context) => boolean")
110
+ },
111
+ label: fnAnnotation("Computed label: (value, data, context, entry) => string"),
112
+ description: fnAnnotation("Computed description: (value, data, context, entry) => string"),
113
+ hint: fnAnnotation("Computed hint: (value, data, context, entry) => string"),
114
+ placeholder: fnAnnotation("Computed placeholder: (value, data, context, entry) => string"),
115
+ disabled: fnAnnotation("Computed disabled state: (value, data, context, entry) => boolean"),
116
+ hidden: fnAnnotation("Computed hidden state: (value, data, context, entry) => boolean"),
117
+ readonly: fnAnnotation("Computed readonly state: (value, data, context, entry) => boolean"),
118
+ value: fnAnnotation("Computed default value: (value, data, context, entry) => any"),
119
+ classes: fnAnnotation("Computed CSS classes: (value, data, context, entry) => string | Record<string, boolean>"),
120
+ styles: fnAnnotation("Computed inline styles: (value, data, context, entry) => string | Record<string, string>"),
121
+ options: fnAnnotation("Computed select/radio options: (value, data, context, entry) => Array"),
122
+ attr: fnAttrSpec
123
+ }
124
+ },
125
+ table: { fn: {
126
+ attr: tableFnAttrSpec,
127
+ classes: tableFnAnnotation("Per-row computed CSS classes for the cell `<td>`: `(row, ctx) => string | Record<string, boolean>`"),
128
+ styles: tableFnAnnotation("Per-row computed inline styles for the cell `<td>`: `(row, ctx) => string | Record<string, string>`")
129
+ } }
130
+ } };
131
+ //#endregion
132
+ //#region src/plugin.ts
133
+ /**
134
+ * ATScript plugin that registers `ui.form.fn.*` / `ui.table.fn.*` computed annotations and `ui.form.validate`.
135
+ *
136
+ * Static `@ui.*` annotations and UI primitives are provided by `@atscript/ui/plugin`.
137
+ *
138
+ * Install in your `atscript.config.ts`:
139
+ * ```ts
140
+ * import uiFnsPlugin from '@atscript/ui-fns/plugin'
141
+ *
142
+ * export default {
143
+ * plugins: [uiFnsPlugin()],
144
+ * }
145
+ * ```
146
+ */
147
+ function uiFnsPlugin() {
148
+ return {
149
+ name: "ui-fns",
150
+ config() {
151
+ return { annotations: uiFnsAnnotations };
152
+ }
153
+ };
154
+ }
155
+ //#endregion
156
+ export { uiFnsPlugin as default };
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@atscript/ui-fns",
3
+ "version": "0.1.58",
4
+ "description": "Dynamic fn-compiled field properties for @atscript/ui (opt-in, uses new Function)",
5
+ "keywords": [
6
+ "annotations",
7
+ "atscript",
8
+ "dynamic",
9
+ "expressions",
10
+ "form",
11
+ "metadata",
12
+ "type-driven",
13
+ "typescript"
14
+ ],
15
+ "homepage": "https://github.com/moostjs/atscript-ui/tree/main/packages/ui-fns#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/moostjs/atscript-ui/issues"
18
+ },
19
+ "license": "MIT",
20
+ "author": "Artem Maltsev <artem@maltsev.nl>",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/moostjs/atscript-ui.git",
24
+ "directory": "packages/ui-fns"
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "type": "module",
30
+ "main": "dist/index.mjs",
31
+ "types": "dist/index.d.mts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.mts",
35
+ "import": "./dist/index.mjs",
36
+ "require": "./dist/index.cjs"
37
+ },
38
+ "./plugin": {
39
+ "types": "./dist/plugin.d.mts",
40
+ "import": "./dist/plugin.mjs",
41
+ "require": "./dist/plugin.cjs"
42
+ },
43
+ "./package.json": "./package.json"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "dependencies": {
49
+ "@prostojs/deserialize-fn": "^0.0.5",
50
+ "@atscript/ui": "^0.1.58"
51
+ },
52
+ "devDependencies": {
53
+ "@atscript/core": "^0.1.54",
54
+ "@atscript/typescript": "^0.1.54",
55
+ "vitest": "npm:@voidzero-dev/vite-plus-test@latest"
56
+ },
57
+ "peerDependencies": {
58
+ "@atscript/core": "^0.1.54",
59
+ "@atscript/typescript": "^0.1.54"
60
+ },
61
+ "scripts": {
62
+ "build": "vp pack",
63
+ "dev": "vp pack --watch",
64
+ "test": "vp test",
65
+ "check": "vp check"
66
+ }
67
+ }