foorm 0.0.2 → 0.1.0

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/index.cjs CHANGED
@@ -1,21 +1,28 @@
1
1
  'use strict';
2
2
 
3
- var ftring$1 = require('@prostojs/ftring');
3
+ var deserializeFn = require('@prostojs/deserialize-fn');
4
+ var serializeFn = require('@prostojs/serialize-fn');
4
5
 
5
- function isFtring(input) {
6
- return (typeof input === 'object' &&
7
- input.__is_ftring__ &&
8
- typeof input.v === 'string');
9
- }
10
- function ftring(strings, __type__) {
11
- return {
12
- __is_ftring__: true,
13
- v: strings.join(''),
14
- __type__,
15
- };
6
+ function evalParameter(fn, scope, forField) {
7
+ if (typeof fn === 'function') {
8
+ if (fn.__deserialized) {
9
+ return fn(scope);
10
+ }
11
+ else {
12
+ const args = (forField
13
+ ? [scope.v, scope.data, scope.context, scope.entry]
14
+ : [scope.data, scope.context, scope.entry]);
15
+ return fn(...args);
16
+ }
17
+ }
18
+ return fn;
16
19
  }
17
20
 
21
+ /* eslint-disable no-inner-declarations */
22
+ /* eslint-disable @typescript-eslint/naming-convention */
23
+ /* eslint-disable @typescript-eslint/no-explicit-any */
18
24
  class Foorm {
25
+ // private fns!: FNPool<string | boolean, TFoormFnScope>
19
26
  constructor(opts) {
20
27
  this.entries = (opts === null || opts === void 0 ? void 0 : opts.entries) || [];
21
28
  this.submit = opts === null || opts === void 0 ? void 0 : opts.submit;
@@ -34,55 +41,23 @@ class Foorm {
34
41
  setContext(context) {
35
42
  this.context = context;
36
43
  }
37
- /**
38
- * Normalizes form metadata and removes all the functions
39
- * from validators.
40
- *
41
- * @param replaceContext a context to be transported along with metadata
42
- * @returns form metadata without functions
43
- */
44
- transportable(replaceContext, replaceValues) {
45
- var _a, _b;
44
+ getDefinition() {
46
45
  return {
47
- title: (_a = this.title) !== null && _a !== void 0 ? _a : '',
48
- submit: (_b = this.submit) !== null && _b !== void 0 ? _b : { text: 'Submit' },
49
- context: replaceContext || this.context,
50
- entries: this.entries.map(e => (Object.assign(Object.assign({}, e), { value: replaceValues ? replaceValues[e.field] : e.value, validators: (e.validators || []).filter(v => isFtring(v)) }))),
46
+ title: this.title,
47
+ submit: this.submit || { text: 'Submit' },
48
+ context: this.context,
49
+ entries: this.entries,
51
50
  };
52
51
  }
53
52
  normalizeEntry(e) {
54
- return Object.assign(Object.assign({}, e), { name: e.name || e.field, label: e.label || e.field, type: e.type || 'text' });
53
+ return Object.assign(Object.assign({}, e), { name: e.name || e.field, label: (e.label || e.field), type: e.type || 'text' });
55
54
  }
56
- /**
57
- * Evaluates all the ftrings into functions, makes it ready for execution
58
- *
59
- * @returns form metadata with functions
60
- */
61
55
  executable() {
62
- var _a, _b;
63
- if (!this.fns) {
64
- this.fns = new ftring$1.FtringsPool();
65
- }
66
56
  return {
67
- title: transformFtrings(this.title || '', this.fns),
68
- submit: {
69
- text: transformFtrings(((_a = this.submit) === null || _a === void 0 ? void 0 : _a.text) || 'Submit', this.fns),
70
- disabled: transformFtrings((_b = this.submit) === null || _b === void 0 ? void 0 : _b.disabled, this.fns),
71
- },
57
+ title: this.title || '',
58
+ submit: this.submit || { text: 'Submit' },
72
59
  context: this.context,
73
- entries: this.entries
74
- .map(e => this.normalizeEntry(e))
75
- .map(e => (Object.assign(Object.assign({}, e), {
76
- // strings
77
- label: transformFtrings(e.label, this.fns), description: transformFtrings(e.description, this.fns), hint: transformFtrings(e.hint, this.fns), placeholder: transformFtrings(e.placeholder, this.fns),
78
- // strings || objects
79
- classes: transformFtringsInObj(e.classes, this.fns), styles: transformFtringsInObj(e.styles, this.fns),
80
- // booleans
81
- optional: transformFtrings(e.optional, this.fns), disabled: transformFtrings(e.disabled, this.fns), hidden: transformFtrings(e.hidden, this.fns), validators: this.prepareValidators(e.validators),
82
- // options
83
- options: transformFtrings(e.options, this.fns),
84
- // attrs
85
- attrs: transformFtringsInObj(e.attrs, this.fns) }))),
60
+ entries: this.entries.map(e => this.normalizeEntry(e)),
86
61
  };
87
62
  }
88
63
  createFormData() {
@@ -95,27 +70,25 @@ class Foorm {
95
70
  return data;
96
71
  }
97
72
  prepareValidators(_validators) {
98
- const validators = (_validators || []).map(v => (isFtring(v) ? this.fns.getFn(v.v) : v));
99
- validators.unshift(this.fns.getFn('entry.optional || !!v || "Required"'));
73
+ const validators = _validators || [];
74
+ validators.unshift((v, _d, _c, entry) => entry.optional || !!v || 'Required');
100
75
  return validators;
101
76
  }
102
77
  supportsAltAction(altAction) {
103
78
  return !!this.entries.some(e => e.altAction === altAction);
104
79
  }
105
80
  getFormValidator() {
106
- if (!this.fns) {
107
- this.fns = new ftring$1.FtringsPool();
108
- }
109
81
  const entries = this.executable().entries;
110
82
  const fields = {};
111
83
  for (const entry of entries) {
112
84
  if (entry.field) {
85
+ const validators = this.prepareValidators(entry.validators);
113
86
  fields[entry.field] = {
114
87
  entry,
115
- validators: this.prepareValidators(entry.validators),
88
+ validators,
116
89
  };
90
+ fields[entry.field].validators = validators;
117
91
  }
118
- fields[entry.field].validators.unshift(this.fns.getFn('entry.optional || !!v || "Required"'));
119
92
  }
120
93
  return (data) => {
121
94
  let passed = true;
@@ -132,27 +105,12 @@ class Foorm {
132
105
  name: evalEntry.name,
133
106
  length: evalEntry.length,
134
107
  },
135
- data,
108
+ data: data,
136
109
  };
137
110
  if (scope.entry) {
138
- if (typeof evalEntry.disabled === 'function') {
139
- scope.entry.disabled = evalEntry.disabled = evalEntry.disabled(scope);
140
- }
141
- else {
142
- scope.entry.disabled = evalEntry.disabled;
143
- }
144
- if (typeof evalEntry.optional === 'function') {
145
- scope.entry.optional = evalEntry.optional = evalEntry.optional(scope);
146
- }
147
- else {
148
- scope.entry.optional = evalEntry.optional;
149
- }
150
- if (typeof evalEntry.hidden === 'function') {
151
- scope.entry.hidden = evalEntry.hidden = evalEntry.hidden(scope);
152
- }
153
- else {
154
- scope.entry.hidden = evalEntry.hidden;
155
- }
111
+ scope.entry.disabled = evalParameter(evalEntry.disabled, scope, true);
112
+ scope.entry.optional = evalParameter(evalEntry.optional, scope, true);
113
+ scope.entry.hidden = evalParameter(evalEntry.hidden, scope, true);
156
114
  }
157
115
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
158
116
  const result = validate({
@@ -160,7 +118,7 @@ class Foorm {
160
118
  context: this.context,
161
119
  validators: value.validators,
162
120
  entry: scope.entry,
163
- data,
121
+ data: data,
164
122
  });
165
123
  if (!result.passed) {
166
124
  passed = false;
@@ -178,12 +136,12 @@ class Foorm {
178
136
  }
179
137
  function validate(opts) {
180
138
  for (const validator of opts.validators || []) {
181
- const result = validator({
139
+ const result = evalParameter(validator, {
182
140
  v: opts.v,
183
141
  context: opts.context,
184
142
  data: opts.data,
185
143
  entry: opts.entry,
186
- });
144
+ }, true);
187
145
  if (result !== true) {
188
146
  return {
189
147
  passed: false,
@@ -193,30 +151,96 @@ function validate(opts) {
193
151
  }
194
152
  return { passed: true };
195
153
  }
196
- function transformFtrings(value, fns) {
197
- if (value === undefined) {
198
- return value;
199
- }
200
- return isFtring(value) ? fns.getFn(value.v) : value;
154
+
155
+ /* eslint-disable @typescript-eslint/naming-convention */
156
+ const pool = new deserializeFn.FNPool();
157
+ function deserializeForm(form) {
158
+ return new Foorm({
159
+ title: deserializeComputedFn(form.title),
160
+ context: form.context,
161
+ submit: deserializeComputedFn(form.submit, true),
162
+ entries: form.entries.map(e => (Object.assign(Object.assign({}, e), {
163
+ // strings
164
+ label: deserializeComputedFn(e.label), description: deserializeComputedFn(e.description), hint: deserializeComputedFn(e.hint), placeholder: deserializeComputedFn(e.placeholder),
165
+ // strings || objects
166
+ classes: deserializeComputedFn(e.classes, true), styles: deserializeComputedFn(e.styles, true),
167
+ // booleans
168
+ optional: deserializeComputedFn(e.optional), disabled: deserializeComputedFn(e.disabled), hidden: deserializeComputedFn(e.hidden),
169
+ // options
170
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
171
+ options: deserializeComputedFn(e.options),
172
+ // attrs
173
+ attrs: deserializeComputedFn(e.attrs, true),
174
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
175
+ value: e.value, validators: (e.validators || []).map(fn => deserializeComputedFn(fn)) }))),
176
+ });
201
177
  }
202
- function transformFtringsInObj(value, fns) {
203
- if (isFtring(value)) {
204
- return transformFtrings(value, fns);
178
+ function deserializeComputedFn(v, asObj) {
179
+ if (typeof v === 'object' && typeof v.__fn__ === 'string') {
180
+ const fn = pool.getFn(v.__fn__);
181
+ fn.__deserialized = true;
182
+ return fn;
183
+ }
184
+ if (typeof v === 'object' && asObj) {
185
+ const o = {};
186
+ for (const [key, val] of Object.entries(v)) {
187
+ o[key] = deserializeComputedFn(val);
188
+ }
189
+ return o;
205
190
  }
206
- if (typeof value === 'function') {
207
- return value;
191
+ return v;
192
+ }
193
+
194
+ /* eslint-disable @typescript-eslint/naming-convention */
195
+ function serializeForm(form, opts) {
196
+ const def = form.getDefinition();
197
+ return {
198
+ title: serializeComputedFn(def.title),
199
+ submit: {
200
+ text: serializeComputedFn(def.submit.text || 'Submit'),
201
+ disabled: serializeComputedFn(def.submit.disabled),
202
+ },
203
+ context: (opts === null || opts === void 0 ? void 0 : opts.replaceContext) || def.context,
204
+ entries: def.entries.map(e => (Object.assign(Object.assign({}, e), {
205
+ // strings
206
+ label: serializeComputedFnWithVal(e.label), description: serializeComputedFnWithVal(e.description), hint: serializeComputedFnWithVal(e.hint), placeholder: serializeComputedFnWithVal(e.placeholder),
207
+ // strings || objects
208
+ classes: serializeComputedFnWithVal(e.classes, true), styles: serializeComputedFnWithVal(e.styles, true),
209
+ // booleans
210
+ optional: serializeComputedFnWithVal(e.optional), disabled: serializeComputedFnWithVal(e.disabled), hidden: serializeComputedFnWithVal(e.hidden),
211
+ // options
212
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
213
+ options: serializeComputedFnWithVal(e.options),
214
+ // attrs
215
+ attrs: serializeComputedFnWithVal(e.attrs, true),
216
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
217
+ value: (opts === null || opts === void 0 ? void 0 : opts.replaceValues) ? opts.replaceValues[e.field] : e.value, validators: (e.validators || [])
218
+ .filter(v => serializeFn.isCleanFn(v))
219
+ .map(fn => serializeComputedFnWithVal(fn)) }))),
220
+ };
221
+ }
222
+ function serializeComputedFn(fn) {
223
+ if (typeof fn === 'function') {
224
+ return { __fn__: serializeFn.serializeFn(fn, 'data', 'context') };
208
225
  }
209
- if (typeof value === 'object' && value !== null) {
210
- const obj = {};
211
- for (const [key, val] of Object.entries(value)) {
212
- obj[key] = transformFtrings(val, fns);
226
+ return fn;
227
+ }
228
+ function serializeComputedFnWithVal(fn, inObj) {
229
+ if (inObj && typeof fn === 'object') {
230
+ const o = {};
231
+ for (const [key, val] of Object.entries(fn)) {
232
+ o[key] = serializeComputedFnWithVal(val);
213
233
  }
214
- return obj;
234
+ return o;
235
+ }
236
+ if (typeof fn === 'function') {
237
+ return { __fn__: serializeFn.serializeFn(fn, 'v', 'data', 'context', 'entry') };
215
238
  }
216
- return value;
239
+ return fn;
217
240
  }
218
241
 
219
242
  exports.Foorm = Foorm;
220
- exports.ftring = ftring;
221
- exports.isFtring = isFtring;
243
+ exports.deserializeForm = deserializeForm;
244
+ exports.evalParameter = evalParameter;
245
+ exports.serializeForm = serializeForm;
222
246
  exports.validate = validate;
package/dist/index.d.ts CHANGED
@@ -1,117 +1,143 @@
1
- interface TFtring {
2
- __is_ftring__: true;
3
- v: string;
4
- __type__?: 'boolean' | 'string' | 'number';
1
+ type TFoormFnTop<OF, D, C> = (data: D, ctx: C) => OF;
2
+ type TFoormFnField<OF, V, D, C> = (v: V, data: D, ctx: C, entry: TFoormEntryEvaluated) => OF;
3
+ interface TFoormFnSerializedTop<OF, D, C> {
4
+ (ctx: TFoormFnScope<undefined, D, C>): OF;
5
+ __deserialized?: boolean;
5
6
  }
6
- type StringOrFtring = string | TFtring;
7
- type ObjSOF = Record<string, StringOrFtring>;
8
- interface TFoormFnScope<T = string> {
9
- v?: T;
10
- data: Record<string, unknown>;
11
- context: Record<string, unknown>;
12
- entry?: Pick<TFoormEntry<T, unknown, string, boolean>, TRelevantFields> & {
13
- optional?: boolean;
14
- disabled?: boolean;
15
- hidden?: boolean;
16
- };
7
+ interface TFoormFnSerializedField<OF, V, D, C> {
8
+ (ctx: TFoormFnScope<V, D, C>): OF;
9
+ __deserialized?: boolean;
10
+ }
11
+ type TComputed<OF, D, C> = OF | TFoormFnTop<OF, D, C> | TFoormFnSerializedTop<OF, D, C>;
12
+ type TComputedWithVal<OF, V, D, C> = OF | TFoormFnField<OF, V, D, C> | TFoormFnSerializedField<OF, V, D, C>;
13
+ interface TFoormFnScope<V = string, D = Record<string, unknown>, C = Record<string, unknown>> {
14
+ v?: V;
15
+ data: D;
16
+ context: C;
17
+ entry?: TFoormEntryEvaluated;
17
18
  action?: string;
18
19
  }
19
- type TFoormValidatorFn<T = string> = (ctx: TFoormFnScope<T>) => string | boolean;
20
- type TFoormFn<T = string, R = string | boolean> = (ctx: TFoormFnScope<T>) => R;
21
- type TRelevantFields = 'field' | 'type' | 'component' | 'name' | 'attrs' | 'length';
20
+ type TFoormValidatorFn<V, D, C> = Exclude<Exclude<TComputedWithVal<boolean | string, V, D, C>, boolean>, string>;
22
21
  type TFoormEntryOptions = {
23
22
  key: string;
24
23
  label: string;
25
24
  } | string;
26
- interface TFoormEntry<T = string, O = TFoormEntryOptions, SFTR = TFtring, BFTR = TFtring, FNFTR = TFtring, OFTR = TFtring> {
25
+ interface TFoormEntry<V, D, C, O extends TFoormEntryOptions> {
27
26
  field: string;
28
27
  altAction?: string;
29
- label?: string | SFTR;
30
- description?: string | SFTR;
31
- hint?: string | SFTR;
32
- placeholder?: string | SFTR;
33
- classes?: (string | SFTR) | Record<string, boolean | BFTR>;
34
- styles?: (string | SFTR) | Record<string, string | SFTR>;
28
+ label?: TComputedWithVal<string, V, D, C>;
29
+ description?: TComputedWithVal<string, V, D, C>;
30
+ hint?: TComputedWithVal<string, V, D, C>;
31
+ placeholder?: TComputedWithVal<string, V, D, C>;
32
+ classes?: TComputedWithVal<string, V, D, C> | Record<string, TComputedWithVal<boolean, V, D, C>>;
33
+ styles?: TComputedWithVal<string, V, D, C> | Record<string, TComputedWithVal<string, V, D, C>>;
35
34
  type?: string;
36
35
  component?: string;
37
36
  autocomplete?: string;
38
37
  name?: string;
39
- value?: T;
40
- options?: O[] | OFTR;
41
- attrs?: Record<string, string | SFTR>;
42
- optional?: boolean | BFTR;
43
- disabled?: boolean | BFTR;
44
- hidden?: boolean | BFTR;
38
+ value?: V;
39
+ options?: O[] | TComputedWithVal<O[], V, D, C>;
40
+ attrs?: Record<string, TComputedWithVal<any, V, D, C>>;
41
+ optional?: TComputedWithVal<boolean, V, D, C>;
42
+ disabled?: TComputedWithVal<boolean, V, D, C>;
43
+ hidden?: TComputedWithVal<boolean, V, D, C>;
45
44
  length?: number;
46
- validators?: Array<FNFTR | TFoormValidatorFn<T>>;
45
+ validators?: Array<TFoormValidatorFn<V, D, C>>;
47
46
  }
48
- type TFoormEntryExecutable<T = unknown, O = TFoormEntryOptions> = TFoormEntry<T, O, TFoormFn<T, string>, TFoormFn<T, boolean>, TFoormValidatorFn<T>, TFoormFn<T, O[]>> & {
49
- name: string;
50
- label: string | TFoormFn<T, string>;
51
- type: string;
52
- };
53
- interface TFoormMetaExecutable {
54
- title: string | TFoormFn<undefined, string>;
47
+ interface TFoormMetaExecutable<D, C> {
48
+ title: TComputed<string, D, C>;
55
49
  submit: {
56
- text: string | TFoormFn<undefined, string>;
57
- disabled: boolean | TFoormFn<undefined, boolean>;
50
+ text: TComputed<string, D, C>;
51
+ disabled?: TComputed<boolean, D, C>;
58
52
  };
59
- context: Record<string, unknown>;
60
- entries: TFoormEntryExecutable[];
53
+ context: C;
54
+ entries: Array<TFoormEntry<any, D, C, TFoormEntryOptions>>;
61
55
  }
62
-
63
- interface TFoormSubmit<S = TFtring, B = TFtring> {
64
- text: string | S;
65
- disabled?: boolean | B;
56
+ interface TSerializedFn {
57
+ __fn__: string;
58
+ }
59
+ interface TFoormSerialized<V = any, O = any> {
60
+ title?: string | TSerializedFn;
61
+ submit: {
62
+ text: string | TSerializedFn;
63
+ disabled: boolean | TSerializedFn;
64
+ };
65
+ context: any;
66
+ entries: Array<{
67
+ field: string;
68
+ altAction?: string;
69
+ label?: string | TSerializedFn;
70
+ description?: string | TSerializedFn;
71
+ hint?: string | TSerializedFn;
72
+ placeholder?: string | TSerializedFn;
73
+ classes?: string | TSerializedFn | Record<string, TSerializedFn>;
74
+ styles?: string | TSerializedFn | Record<string, string | TSerializedFn>;
75
+ type?: string;
76
+ component?: string;
77
+ autocomplete?: string;
78
+ name?: string;
79
+ value?: V;
80
+ options?: O[] | TSerializedFn;
81
+ attrs?: Record<string, string | TSerializedFn>;
82
+ optional?: TSerializedFn;
83
+ disabled?: TSerializedFn;
84
+ hidden?: TSerializedFn;
85
+ length?: number;
86
+ validators?: TSerializedFn[];
87
+ }>;
66
88
  }
67
- interface TFoormOptions {
68
- title?: StringOrFtring;
69
- entries: TFoormEntry[];
70
- submit?: TFoormSubmit;
71
- context?: Record<string, unknown>;
89
+ interface TFoormEntryEvaluated {
90
+ field: string;
91
+ type: string;
92
+ component?: string;
93
+ name: string;
94
+ length?: number;
95
+ disabled?: boolean;
96
+ optional?: boolean;
97
+ hidden?: boolean;
72
98
  }
73
- declare class Foorm {
74
- protected entries: TFoormEntry[];
75
- protected submit?: TFoormSubmit;
76
- protected title?: StringOrFtring;
77
- protected context: Record<string, unknown>;
78
- private fns;
79
- constructor(opts?: TFoormOptions);
80
- addEntry(entry: TFoormEntry): void;
99
+
100
+ interface TFoormOptions<D, C> {
101
+ title?: TComputed<string, D, C>;
102
+ entries: Array<TFoormEntry<any, D, C, TFoormEntryOptions>>;
103
+ submit?: TFoormMetaExecutable<D, C>['submit'];
104
+ context?: C;
105
+ }
106
+ declare class Foorm<D = any, C = any> {
107
+ protected entries: Array<TFoormEntry<any, D, C, TFoormEntryOptions>>;
108
+ protected submit?: TFoormMetaExecutable<D, C>['submit'];
109
+ protected title?: TComputed<string, D, C>;
110
+ protected context: C;
111
+ constructor(opts?: TFoormOptions<D, C>);
112
+ addEntry<V>(entry: TFoormEntry<V, D, C, TFoormEntryOptions>): void;
81
113
  setTitle(title: string): void;
82
- setSubmit(submit: TFoormSubmit): void;
83
- setContext<T extends Record<string, unknown>>(context: T): void;
84
- /**
85
- * Normalizes form metadata and removes all the functions
86
- * from validators.
87
- *
88
- * @param replaceContext a context to be transported along with metadata
89
- * @returns form metadata without functions
90
- */
91
- transportable<T extends Record<string, unknown>>(replaceContext?: T, replaceValues?: Record<string, unknown>): Required<TFoormOptions> & {
92
- context?: Record<string, unknown>;
114
+ setSubmit(submit: TFoormMetaExecutable<D, C>['submit']): void;
115
+ setContext(context: C): void;
116
+ getDefinition(): {
117
+ title: TComputed<string, D, C> | undefined;
118
+ submit: {
119
+ text: TComputed<string, D, C>;
120
+ disabled?: TComputed<boolean, D, C> | undefined;
121
+ };
122
+ context: C;
123
+ entries: TFoormEntry<any, D, C, TFoormEntryOptions>[];
93
124
  };
94
- protected normalizeEntry<T, O>(e: TFoormEntry<T, O>): TFoormEntry<T, O> & {
125
+ protected normalizeEntry<V, O extends TFoormEntryOptions>(e: TFoormEntry<V, D, C, O>): TFoormEntry<V, D, C, O> & {
95
126
  name: string;
96
- label: string | TFtring;
127
+ label: TComputed<string, D, C>;
97
128
  type: string;
98
129
  };
99
- /**
100
- * Evaluates all the ftrings into functions, makes it ready for execution
101
- *
102
- * @returns form metadata with functions
103
- */
104
- executable(): TFoormMetaExecutable;
130
+ executable(): TFoormMetaExecutable<D, C>;
105
131
  createFormData<T extends Record<string, unknown>>(): T;
106
- prepareValidators(_validators: TFoormEntry['validators']): TFoormValidatorFn<string>[];
132
+ prepareValidators(_validators: TFoormEntry<any, D, C, TFoormEntryOptions>['validators']): TFoormValidatorFn<any, D, C>[];
107
133
  supportsAltAction(altAction: string): boolean;
108
134
  getFormValidator(): (inputs: Record<string, unknown>) => {
109
135
  passed: boolean;
110
136
  errors: Record<string, string>;
111
137
  };
112
138
  }
113
- declare function validate<T = string>(opts: TFoormFnScope<T> & {
114
- validators: Array<TFoormValidatorFn<T>>;
139
+ declare function validate<V, D, C>(opts: TFoormFnScope<V, D, C> & {
140
+ validators: TFoormEntry<any, D, C, TFoormEntryOptions>['validators'];
115
141
  }): {
116
142
  passed: boolean;
117
143
  error: string;
@@ -120,7 +146,13 @@ declare function validate<T = string>(opts: TFoormFnScope<T> & {
120
146
  error?: undefined;
121
147
  };
122
148
 
123
- declare function isFtring(input: unknown): input is TFtring;
124
- declare function ftring(strings: TemplateStringsArray, __type__?: TFtring['__type__']): TFtring;
149
+ declare function deserializeForm<D = unknown, C = unknown>(form: TFoormSerialized): Foorm<D, C>;
150
+
151
+ declare function serializeForm<D, C>(form: Foorm<D, C>, opts?: {
152
+ replaceContext?: C;
153
+ replaceValues?: Record<string, unknown>;
154
+ }): TFoormSerialized;
155
+
156
+ declare function evalParameter<OF>(fn: TComputed<OF, any, any> | TComputedWithVal<OF, any, any, any>, scope: TFoormFnScope<any, any, any>, forField?: boolean): OF | undefined;
125
157
 
126
- export { Foorm, type ObjSOF, type StringOrFtring, type TFoormEntry, type TFoormEntryExecutable, type TFoormEntryOptions, type TFoormFn, type TFoormFnScope, type TFoormMetaExecutable, type TFoormOptions, type TFoormSubmit, type TFoormValidatorFn, type TFtring, ftring, isFtring, validate };
158
+ export { Foorm, type TComputed, type TComputedWithVal, type TFoormEntry, type TFoormEntryEvaluated, type TFoormEntryOptions, type TFoormFnField, type TFoormFnScope, type TFoormFnSerializedField, type TFoormFnSerializedTop, type TFoormFnTop, type TFoormMetaExecutable, type TFoormOptions, type TFoormSerialized, type TFoormValidatorFn, type TSerializedFn, deserializeForm, evalParameter, serializeForm, validate };
package/dist/index.mjs CHANGED
@@ -1,19 +1,26 @@
1
- import { FtringsPool } from '@prostojs/ftring';
1
+ import { FNPool } from '@prostojs/deserialize-fn';
2
+ import { isCleanFn, serializeFn } from '@prostojs/serialize-fn';
2
3
 
3
- function isFtring(input) {
4
- return (typeof input === 'object' &&
5
- input.__is_ftring__ &&
6
- typeof input.v === 'string');
7
- }
8
- function ftring(strings, __type__) {
9
- return {
10
- __is_ftring__: true,
11
- v: strings.join(''),
12
- __type__,
13
- };
4
+ function evalParameter(fn, scope, forField) {
5
+ if (typeof fn === 'function') {
6
+ if (fn.__deserialized) {
7
+ return fn(scope);
8
+ }
9
+ else {
10
+ const args = (forField
11
+ ? [scope.v, scope.data, scope.context, scope.entry]
12
+ : [scope.data, scope.context, scope.entry]);
13
+ return fn(...args);
14
+ }
15
+ }
16
+ return fn;
14
17
  }
15
18
 
19
+ /* eslint-disable no-inner-declarations */
20
+ /* eslint-disable @typescript-eslint/naming-convention */
21
+ /* eslint-disable @typescript-eslint/no-explicit-any */
16
22
  class Foorm {
23
+ // private fns!: FNPool<string | boolean, TFoormFnScope>
17
24
  constructor(opts) {
18
25
  this.entries = (opts === null || opts === void 0 ? void 0 : opts.entries) || [];
19
26
  this.submit = opts === null || opts === void 0 ? void 0 : opts.submit;
@@ -32,55 +39,23 @@ class Foorm {
32
39
  setContext(context) {
33
40
  this.context = context;
34
41
  }
35
- /**
36
- * Normalizes form metadata and removes all the functions
37
- * from validators.
38
- *
39
- * @param replaceContext a context to be transported along with metadata
40
- * @returns form metadata without functions
41
- */
42
- transportable(replaceContext, replaceValues) {
43
- var _a, _b;
42
+ getDefinition() {
44
43
  return {
45
- title: (_a = this.title) !== null && _a !== void 0 ? _a : '',
46
- submit: (_b = this.submit) !== null && _b !== void 0 ? _b : { text: 'Submit' },
47
- context: replaceContext || this.context,
48
- entries: this.entries.map(e => (Object.assign(Object.assign({}, e), { value: replaceValues ? replaceValues[e.field] : e.value, validators: (e.validators || []).filter(v => isFtring(v)) }))),
44
+ title: this.title,
45
+ submit: this.submit || { text: 'Submit' },
46
+ context: this.context,
47
+ entries: this.entries,
49
48
  };
50
49
  }
51
50
  normalizeEntry(e) {
52
- return Object.assign(Object.assign({}, e), { name: e.name || e.field, label: e.label || e.field, type: e.type || 'text' });
51
+ return Object.assign(Object.assign({}, e), { name: e.name || e.field, label: (e.label || e.field), type: e.type || 'text' });
53
52
  }
54
- /**
55
- * Evaluates all the ftrings into functions, makes it ready for execution
56
- *
57
- * @returns form metadata with functions
58
- */
59
53
  executable() {
60
- var _a, _b;
61
- if (!this.fns) {
62
- this.fns = new FtringsPool();
63
- }
64
54
  return {
65
- title: transformFtrings(this.title || '', this.fns),
66
- submit: {
67
- text: transformFtrings(((_a = this.submit) === null || _a === void 0 ? void 0 : _a.text) || 'Submit', this.fns),
68
- disabled: transformFtrings((_b = this.submit) === null || _b === void 0 ? void 0 : _b.disabled, this.fns),
69
- },
55
+ title: this.title || '',
56
+ submit: this.submit || { text: 'Submit' },
70
57
  context: this.context,
71
- entries: this.entries
72
- .map(e => this.normalizeEntry(e))
73
- .map(e => (Object.assign(Object.assign({}, e), {
74
- // strings
75
- label: transformFtrings(e.label, this.fns), description: transformFtrings(e.description, this.fns), hint: transformFtrings(e.hint, this.fns), placeholder: transformFtrings(e.placeholder, this.fns),
76
- // strings || objects
77
- classes: transformFtringsInObj(e.classes, this.fns), styles: transformFtringsInObj(e.styles, this.fns),
78
- // booleans
79
- optional: transformFtrings(e.optional, this.fns), disabled: transformFtrings(e.disabled, this.fns), hidden: transformFtrings(e.hidden, this.fns), validators: this.prepareValidators(e.validators),
80
- // options
81
- options: transformFtrings(e.options, this.fns),
82
- // attrs
83
- attrs: transformFtringsInObj(e.attrs, this.fns) }))),
58
+ entries: this.entries.map(e => this.normalizeEntry(e)),
84
59
  };
85
60
  }
86
61
  createFormData() {
@@ -93,27 +68,25 @@ class Foorm {
93
68
  return data;
94
69
  }
95
70
  prepareValidators(_validators) {
96
- const validators = (_validators || []).map(v => (isFtring(v) ? this.fns.getFn(v.v) : v));
97
- validators.unshift(this.fns.getFn('entry.optional || !!v || "Required"'));
71
+ const validators = _validators || [];
72
+ validators.unshift((v, _d, _c, entry) => entry.optional || !!v || 'Required');
98
73
  return validators;
99
74
  }
100
75
  supportsAltAction(altAction) {
101
76
  return !!this.entries.some(e => e.altAction === altAction);
102
77
  }
103
78
  getFormValidator() {
104
- if (!this.fns) {
105
- this.fns = new FtringsPool();
106
- }
107
79
  const entries = this.executable().entries;
108
80
  const fields = {};
109
81
  for (const entry of entries) {
110
82
  if (entry.field) {
83
+ const validators = this.prepareValidators(entry.validators);
111
84
  fields[entry.field] = {
112
85
  entry,
113
- validators: this.prepareValidators(entry.validators),
86
+ validators,
114
87
  };
88
+ fields[entry.field].validators = validators;
115
89
  }
116
- fields[entry.field].validators.unshift(this.fns.getFn('entry.optional || !!v || "Required"'));
117
90
  }
118
91
  return (data) => {
119
92
  let passed = true;
@@ -130,27 +103,12 @@ class Foorm {
130
103
  name: evalEntry.name,
131
104
  length: evalEntry.length,
132
105
  },
133
- data,
106
+ data: data,
134
107
  };
135
108
  if (scope.entry) {
136
- if (typeof evalEntry.disabled === 'function') {
137
- scope.entry.disabled = evalEntry.disabled = evalEntry.disabled(scope);
138
- }
139
- else {
140
- scope.entry.disabled = evalEntry.disabled;
141
- }
142
- if (typeof evalEntry.optional === 'function') {
143
- scope.entry.optional = evalEntry.optional = evalEntry.optional(scope);
144
- }
145
- else {
146
- scope.entry.optional = evalEntry.optional;
147
- }
148
- if (typeof evalEntry.hidden === 'function') {
149
- scope.entry.hidden = evalEntry.hidden = evalEntry.hidden(scope);
150
- }
151
- else {
152
- scope.entry.hidden = evalEntry.hidden;
153
- }
109
+ scope.entry.disabled = evalParameter(evalEntry.disabled, scope, true);
110
+ scope.entry.optional = evalParameter(evalEntry.optional, scope, true);
111
+ scope.entry.hidden = evalParameter(evalEntry.hidden, scope, true);
154
112
  }
155
113
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
114
  const result = validate({
@@ -158,7 +116,7 @@ class Foorm {
158
116
  context: this.context,
159
117
  validators: value.validators,
160
118
  entry: scope.entry,
161
- data,
119
+ data: data,
162
120
  });
163
121
  if (!result.passed) {
164
122
  passed = false;
@@ -176,12 +134,12 @@ class Foorm {
176
134
  }
177
135
  function validate(opts) {
178
136
  for (const validator of opts.validators || []) {
179
- const result = validator({
137
+ const result = evalParameter(validator, {
180
138
  v: opts.v,
181
139
  context: opts.context,
182
140
  data: opts.data,
183
141
  entry: opts.entry,
184
- });
142
+ }, true);
185
143
  if (result !== true) {
186
144
  return {
187
145
  passed: false,
@@ -191,27 +149,92 @@ function validate(opts) {
191
149
  }
192
150
  return { passed: true };
193
151
  }
194
- function transformFtrings(value, fns) {
195
- if (value === undefined) {
196
- return value;
197
- }
198
- return isFtring(value) ? fns.getFn(value.v) : value;
152
+
153
+ /* eslint-disable @typescript-eslint/naming-convention */
154
+ const pool = new FNPool();
155
+ function deserializeForm(form) {
156
+ return new Foorm({
157
+ title: deserializeComputedFn(form.title),
158
+ context: form.context,
159
+ submit: deserializeComputedFn(form.submit, true),
160
+ entries: form.entries.map(e => (Object.assign(Object.assign({}, e), {
161
+ // strings
162
+ label: deserializeComputedFn(e.label), description: deserializeComputedFn(e.description), hint: deserializeComputedFn(e.hint), placeholder: deserializeComputedFn(e.placeholder),
163
+ // strings || objects
164
+ classes: deserializeComputedFn(e.classes, true), styles: deserializeComputedFn(e.styles, true),
165
+ // booleans
166
+ optional: deserializeComputedFn(e.optional), disabled: deserializeComputedFn(e.disabled), hidden: deserializeComputedFn(e.hidden),
167
+ // options
168
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
169
+ options: deserializeComputedFn(e.options),
170
+ // attrs
171
+ attrs: deserializeComputedFn(e.attrs, true),
172
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
173
+ value: e.value, validators: (e.validators || []).map(fn => deserializeComputedFn(fn)) }))),
174
+ });
199
175
  }
200
- function transformFtringsInObj(value, fns) {
201
- if (isFtring(value)) {
202
- return transformFtrings(value, fns);
176
+ function deserializeComputedFn(v, asObj) {
177
+ if (typeof v === 'object' && typeof v.__fn__ === 'string') {
178
+ const fn = pool.getFn(v.__fn__);
179
+ fn.__deserialized = true;
180
+ return fn;
181
+ }
182
+ if (typeof v === 'object' && asObj) {
183
+ const o = {};
184
+ for (const [key, val] of Object.entries(v)) {
185
+ o[key] = deserializeComputedFn(val);
186
+ }
187
+ return o;
203
188
  }
204
- if (typeof value === 'function') {
205
- return value;
189
+ return v;
190
+ }
191
+
192
+ /* eslint-disable @typescript-eslint/naming-convention */
193
+ function serializeForm(form, opts) {
194
+ const def = form.getDefinition();
195
+ return {
196
+ title: serializeComputedFn(def.title),
197
+ submit: {
198
+ text: serializeComputedFn(def.submit.text || 'Submit'),
199
+ disabled: serializeComputedFn(def.submit.disabled),
200
+ },
201
+ context: (opts === null || opts === void 0 ? void 0 : opts.replaceContext) || def.context,
202
+ entries: def.entries.map(e => (Object.assign(Object.assign({}, e), {
203
+ // strings
204
+ label: serializeComputedFnWithVal(e.label), description: serializeComputedFnWithVal(e.description), hint: serializeComputedFnWithVal(e.hint), placeholder: serializeComputedFnWithVal(e.placeholder),
205
+ // strings || objects
206
+ classes: serializeComputedFnWithVal(e.classes, true), styles: serializeComputedFnWithVal(e.styles, true),
207
+ // booleans
208
+ optional: serializeComputedFnWithVal(e.optional), disabled: serializeComputedFnWithVal(e.disabled), hidden: serializeComputedFnWithVal(e.hidden),
209
+ // options
210
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
211
+ options: serializeComputedFnWithVal(e.options),
212
+ // attrs
213
+ attrs: serializeComputedFnWithVal(e.attrs, true),
214
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
215
+ value: (opts === null || opts === void 0 ? void 0 : opts.replaceValues) ? opts.replaceValues[e.field] : e.value, validators: (e.validators || [])
216
+ .filter(v => isCleanFn(v))
217
+ .map(fn => serializeComputedFnWithVal(fn)) }))),
218
+ };
219
+ }
220
+ function serializeComputedFn(fn) {
221
+ if (typeof fn === 'function') {
222
+ return { __fn__: serializeFn(fn, 'data', 'context') };
206
223
  }
207
- if (typeof value === 'object' && value !== null) {
208
- const obj = {};
209
- for (const [key, val] of Object.entries(value)) {
210
- obj[key] = transformFtrings(val, fns);
224
+ return fn;
225
+ }
226
+ function serializeComputedFnWithVal(fn, inObj) {
227
+ if (inObj && typeof fn === 'object') {
228
+ const o = {};
229
+ for (const [key, val] of Object.entries(fn)) {
230
+ o[key] = serializeComputedFnWithVal(val);
211
231
  }
212
- return obj;
232
+ return o;
233
+ }
234
+ if (typeof fn === 'function') {
235
+ return { __fn__: serializeFn(fn, 'v', 'data', 'context', 'entry') };
213
236
  }
214
- return value;
237
+ return fn;
215
238
  }
216
239
 
217
- export { Foorm, ftring, isFtring, validate };
240
+ export { Foorm, deserializeForm, evalParameter, serializeForm, validate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foorm",
3
- "version": "0.0.2",
3
+ "version": "0.1.0",
4
4
  "description": "foorm",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -35,6 +35,7 @@
35
35
  },
36
36
  "homepage": "https://github.com/foormjs/foormjs/tree/main/packages/foorm#readme",
37
37
  "dependencies": {
38
- "@prostojs/ftring": "^0.0.4"
38
+ "@prostojs/serialize-fn": "^0.0.5",
39
+ "@prostojs/deserialize-fn": "^0.0.5"
39
40
  }
40
41
  }