@prolibu-suite/cobalt-form-core 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/controller.d.ts +35 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +201 -0
- package/dist/controller.js.map +1 -0
- package/dist/describeField.d.ts +5 -0
- package/dist/describeField.d.ts.map +1 -0
- package/dist/describeField.js +37 -0
- package/dist/describeField.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/inlineValidator.d.ts +9 -0
- package/dist/inlineValidator.d.ts.map +1 -0
- package/dist/inlineValidator.js +136 -0
- package/dist/inlineValidator.js.map +1 -0
- package/dist/normalizeValues.d.ts +18 -0
- package/dist/normalizeValues.d.ts.map +1 -0
- package/dist/normalizeValues.js +43 -0
- package/dist/normalizeValues.js.map +1 -0
- package/dist/resolveFieldKind.d.ts +3 -0
- package/dist/resolveFieldKind.d.ts.map +1 -0
- package/dist/resolveFieldKind.js +26 -0
- package/dist/resolveFieldKind.js.map +1 -0
- package/dist/schemaLoader.d.ts +39 -0
- package/dist/schemaLoader.d.ts.map +1 -0
- package/dist/schemaLoader.js +78 -0
- package/dist/schemaLoader.js.map +1 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { FieldDescriptor, FormControllerOptions, FormSchemaLike, FormState, FormStateListener, SubmitResult, Unsubscribe } from './types.js';
|
|
2
|
+
export declare class FormController {
|
|
3
|
+
fields: FieldDescriptor[];
|
|
4
|
+
state: FormState;
|
|
5
|
+
private schemaInstance;
|
|
6
|
+
private readonly initialValues;
|
|
7
|
+
private readonly validateOn;
|
|
8
|
+
private readonly listeners;
|
|
9
|
+
private notifyScheduled;
|
|
10
|
+
constructor(options: FormControllerOptions);
|
|
11
|
+
private collectDefaults;
|
|
12
|
+
getValue(name: string): any;
|
|
13
|
+
setValue(name: string, value: any): void;
|
|
14
|
+
setValues(partial: Record<string, any>): void;
|
|
15
|
+
validate(): boolean;
|
|
16
|
+
/** Mark a single field as touched. */
|
|
17
|
+
touch(name: string): void;
|
|
18
|
+
/** Mark every field as touched. Useful right before showing errors on submit. */
|
|
19
|
+
touchAll(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Swap the underlying schema instance (e.g. on locale change) while preserving
|
|
22
|
+
* the current values and touched map. Re-derives the field descriptors and
|
|
23
|
+
* re-runs validation against the new instance.
|
|
24
|
+
*/
|
|
25
|
+
setSchema(instance: FormSchemaLike): void;
|
|
26
|
+
submit(): Promise<SubmitResult>;
|
|
27
|
+
reset(values?: Record<string, any>): void;
|
|
28
|
+
subscribe(listener: FormStateListener): Unsubscribe;
|
|
29
|
+
private commit;
|
|
30
|
+
private runValidation;
|
|
31
|
+
private computeDirty;
|
|
32
|
+
private scheduleNotify;
|
|
33
|
+
}
|
|
34
|
+
export declare function createForm(options: FormControllerOptions): FormController;
|
|
35
|
+
//# sourceMappingURL=controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACZ,MAAM,YAAY,CAAC;AAWpB,qBAAa,cAAc;IACzB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,KAAK,EAAE,SAAS,CAAC;IAEjB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsB;IACpD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmD;IAC9E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgC;IAC1D,OAAO,CAAC,eAAe,CAAS;gBAEpB,OAAO,EAAE,qBAAqB;IAuB1C,OAAO,CAAC,eAAe;IAUvB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG;IAI3B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAaxC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAS7C,QAAQ,IAAI,OAAO;IAKnB,sCAAsC;IACtC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKzB,iFAAiF;IACjF,QAAQ,IAAI,IAAI;IAMhB;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;IAOnC,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC;IAcrC,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAUzC,SAAS,CAAC,QAAQ,EAAE,iBAAiB,GAAG,WAAW;IAOnD,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,cAAc;CAUvB;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CAEzE"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { describeFields } from './describeField.js';
|
|
2
|
+
import { groupErrorsByField, normalizeForValidation } from './normalizeValues.js';
|
|
3
|
+
import { resolveSchema } from './schemaLoader.js';
|
|
4
|
+
const EMPTY_STATE = Object.freeze({
|
|
5
|
+
values: {},
|
|
6
|
+
errors: {},
|
|
7
|
+
touched: {},
|
|
8
|
+
isValid: true,
|
|
9
|
+
isDirty: false,
|
|
10
|
+
isSubmitting: false,
|
|
11
|
+
});
|
|
12
|
+
export class FormController {
|
|
13
|
+
constructor(options) {
|
|
14
|
+
Object.defineProperty(this, "fields", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true,
|
|
18
|
+
value: void 0
|
|
19
|
+
});
|
|
20
|
+
Object.defineProperty(this, "state", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: void 0
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(this, "schemaInstance", {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
writable: true,
|
|
30
|
+
value: void 0
|
|
31
|
+
});
|
|
32
|
+
Object.defineProperty(this, "initialValues", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
writable: true,
|
|
36
|
+
value: void 0
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(this, "validateOn", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
writable: true,
|
|
42
|
+
value: void 0
|
|
43
|
+
});
|
|
44
|
+
Object.defineProperty(this, "listeners", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
writable: true,
|
|
48
|
+
value: new Set()
|
|
49
|
+
});
|
|
50
|
+
Object.defineProperty(this, "notifyScheduled", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
configurable: true,
|
|
53
|
+
writable: true,
|
|
54
|
+
value: false
|
|
55
|
+
});
|
|
56
|
+
this.schemaInstance = resolveSchema(options.schema);
|
|
57
|
+
this.fields = describeFields(this.schemaInstance.formSchema.properties);
|
|
58
|
+
this.validateOn = options.validateOn ?? 'change';
|
|
59
|
+
// Merge schema defaults under user-provided initialValues so explicit
|
|
60
|
+
// values always win, but unset fields fall back to their `default`.
|
|
61
|
+
const defaults = this.collectDefaults();
|
|
62
|
+
const userInitial = options.initialValues ?? {};
|
|
63
|
+
const initial = { ...defaults, ...userInitial };
|
|
64
|
+
this.initialValues = { ...initial };
|
|
65
|
+
this.state = {
|
|
66
|
+
...EMPTY_STATE,
|
|
67
|
+
values: initial,
|
|
68
|
+
};
|
|
69
|
+
// Run a first validation so consumers see initial errors (matches the
|
|
70
|
+
// behavior of `useFormValidation` in suite-v2, which validates with
|
|
71
|
+
// `immediate: true`).
|
|
72
|
+
this.runValidation();
|
|
73
|
+
}
|
|
74
|
+
collectDefaults() {
|
|
75
|
+
const out = {};
|
|
76
|
+
for (const f of this.fields) {
|
|
77
|
+
const def = f.originalAttrs?.default;
|
|
78
|
+
if (def === undefined)
|
|
79
|
+
continue;
|
|
80
|
+
out[f.name] = typeof def === 'function' ? def() : def;
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
getValue(name) {
|
|
85
|
+
return this.state.values[name];
|
|
86
|
+
}
|
|
87
|
+
setValue(name, value) {
|
|
88
|
+
const nextValues = { ...this.state.values, [name]: value };
|
|
89
|
+
const nextTouched = this.state.touched[name]
|
|
90
|
+
? this.state.touched
|
|
91
|
+
: { ...this.state.touched, [name]: true };
|
|
92
|
+
this.commit({
|
|
93
|
+
values: nextValues,
|
|
94
|
+
touched: nextTouched,
|
|
95
|
+
isDirty: this.computeDirty(nextValues),
|
|
96
|
+
});
|
|
97
|
+
if (this.validateOn === 'change')
|
|
98
|
+
this.runValidation();
|
|
99
|
+
}
|
|
100
|
+
setValues(partial) {
|
|
101
|
+
const nextValues = { ...this.state.values, ...partial };
|
|
102
|
+
this.commit({
|
|
103
|
+
values: nextValues,
|
|
104
|
+
isDirty: this.computeDirty(nextValues),
|
|
105
|
+
});
|
|
106
|
+
if (this.validateOn === 'change')
|
|
107
|
+
this.runValidation();
|
|
108
|
+
}
|
|
109
|
+
validate() {
|
|
110
|
+
this.runValidation();
|
|
111
|
+
return this.state.isValid;
|
|
112
|
+
}
|
|
113
|
+
/** Mark a single field as touched. */
|
|
114
|
+
touch(name) {
|
|
115
|
+
if (this.state.touched[name])
|
|
116
|
+
return;
|
|
117
|
+
this.commit({ touched: { ...this.state.touched, [name]: true } });
|
|
118
|
+
}
|
|
119
|
+
/** Mark every field as touched. Useful right before showing errors on submit. */
|
|
120
|
+
touchAll() {
|
|
121
|
+
const touched = { ...this.state.touched };
|
|
122
|
+
for (const f of this.fields)
|
|
123
|
+
touched[f.name] = true;
|
|
124
|
+
this.commit({ touched });
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Swap the underlying schema instance (e.g. on locale change) while preserving
|
|
128
|
+
* the current values and touched map. Re-derives the field descriptors and
|
|
129
|
+
* re-runs validation against the new instance.
|
|
130
|
+
*/
|
|
131
|
+
setSchema(instance) {
|
|
132
|
+
this.schemaInstance = instance;
|
|
133
|
+
this.fields = describeFields(instance.formSchema.properties);
|
|
134
|
+
this.runValidation();
|
|
135
|
+
this.scheduleNotify();
|
|
136
|
+
}
|
|
137
|
+
async submit() {
|
|
138
|
+
this.commit({ isSubmitting: true });
|
|
139
|
+
try {
|
|
140
|
+
const isValid = this.validate();
|
|
141
|
+
return {
|
|
142
|
+
values: this.state.values,
|
|
143
|
+
isValid,
|
|
144
|
+
errors: this.state.errors,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
this.commit({ isSubmitting: false });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
reset(values) {
|
|
152
|
+
const next = values ? { ...values } : { ...this.initialValues };
|
|
153
|
+
this.state = {
|
|
154
|
+
...EMPTY_STATE,
|
|
155
|
+
values: next,
|
|
156
|
+
};
|
|
157
|
+
this.runValidation();
|
|
158
|
+
this.scheduleNotify();
|
|
159
|
+
}
|
|
160
|
+
subscribe(listener) {
|
|
161
|
+
this.listeners.add(listener);
|
|
162
|
+
return () => {
|
|
163
|
+
this.listeners.delete(listener);
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
commit(patch) {
|
|
167
|
+
this.state = { ...this.state, ...patch };
|
|
168
|
+
this.scheduleNotify();
|
|
169
|
+
}
|
|
170
|
+
runValidation() {
|
|
171
|
+
const properties = this.schemaInstance.formSchema.properties || {};
|
|
172
|
+
const normalized = normalizeForValidation(this.state.values, properties);
|
|
173
|
+
this.schemaInstance.validate(normalized);
|
|
174
|
+
const list = this.schemaInstance.validator?.errors || [];
|
|
175
|
+
const errors = list.length === 0 ? {} : groupErrorsByField(list);
|
|
176
|
+
const isValid = Object.keys(errors).length === 0;
|
|
177
|
+
this.commit({ errors, isValid });
|
|
178
|
+
}
|
|
179
|
+
computeDirty(values) {
|
|
180
|
+
// Cheap enough for typical form sizes (<200 fields). If this becomes a
|
|
181
|
+
// hot spot we can switch to per-field tracking.
|
|
182
|
+
return JSON.stringify(values) !== JSON.stringify(this.initialValues);
|
|
183
|
+
}
|
|
184
|
+
scheduleNotify() {
|
|
185
|
+
if (this.notifyScheduled)
|
|
186
|
+
return;
|
|
187
|
+
if (this.listeners.size === 0)
|
|
188
|
+
return;
|
|
189
|
+
this.notifyScheduled = true;
|
|
190
|
+
queueMicrotask(() => {
|
|
191
|
+
this.notifyScheduled = false;
|
|
192
|
+
const snapshot = this.state;
|
|
193
|
+
for (const listener of this.listeners)
|
|
194
|
+
listener(snapshot);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
export function createForm(options) {
|
|
199
|
+
return new FormController(options);
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FieldDescriptor } from './types.js';
|
|
2
|
+
export declare function labelFromName(name: string): string;
|
|
3
|
+
export declare function describeField(name: string, ajvProperty: any): FieldDescriptor | null;
|
|
4
|
+
export declare function describeFields(properties: Record<string, any>): FieldDescriptor[];
|
|
5
|
+
//# sourceMappingURL=describeField.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"describeField.d.ts","sourceRoot":"","sources":["../src/describeField.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,GAAG,eAAe,GAAG,IAAI,CAkBpF;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,eAAe,EAAE,CAOjF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { resolveFieldKind } from './resolveFieldKind.js';
|
|
2
|
+
export function labelFromName(name) {
|
|
3
|
+
return String(name)
|
|
4
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
5
|
+
.replace(/_/g, ' ')
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.replace(/^./, (c) => c.toUpperCase());
|
|
8
|
+
}
|
|
9
|
+
export function describeField(name, ajvProperty) {
|
|
10
|
+
const orig = ajvProperty?.originalAttrs || {};
|
|
11
|
+
const kind = resolveFieldKind(ajvProperty);
|
|
12
|
+
if (kind === null)
|
|
13
|
+
return null;
|
|
14
|
+
return {
|
|
15
|
+
name,
|
|
16
|
+
kind,
|
|
17
|
+
label: orig.label || labelFromName(name),
|
|
18
|
+
placeholder: orig.placeholder || orig.eg || '',
|
|
19
|
+
required: !!orig.required,
|
|
20
|
+
disabled: !!(orig.readOnly || orig.disabled),
|
|
21
|
+
hidden: !!orig.hidden,
|
|
22
|
+
fullWidth: !!orig.full,
|
|
23
|
+
helperText: orig.helperText || orig.description || '',
|
|
24
|
+
originalAttrs: orig,
|
|
25
|
+
ajvProperty,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function describeFields(properties) {
|
|
29
|
+
const out = [];
|
|
30
|
+
for (const [name, property] of Object.entries(properties)) {
|
|
31
|
+
const desc = describeField(name, property);
|
|
32
|
+
if (desc)
|
|
33
|
+
out.push(desc);
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=describeField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"describeField.js","sourceRoot":"","sources":["../src/describeField.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGzD,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,MAAM,CAAC,IAAI,CAAC;SAChB,OAAO,CAAC,iBAAiB,EAAE,OAAO,CAAC;SACnC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC;SAClB,WAAW,EAAE;SACb,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,WAAgB;IAC1D,MAAM,IAAI,GAAwB,WAAW,EAAE,aAAa,IAAI,EAAE,CAAC;IACnE,MAAM,IAAI,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAC3C,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAE/B,OAAO;QACL,IAAI;QACJ,IAAI;QACJ,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,aAAa,CAAC,IAAI,CAAC;QACxC,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE;QAC9C,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ;QACzB,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC;QAC5C,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM;QACrB,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI;QACtB,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,WAAW,IAAI,EAAE;QACrD,aAAa,EAAE,IAAI;QACnB,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,UAA+B;IAC5D,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC3C,IAAI,IAAI;YAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { FormController, createForm } from './controller.js';
|
|
2
|
+
export { describeField, describeFields, labelFromName } from './describeField.js';
|
|
3
|
+
export { resolveFieldKind } from './resolveFieldKind.js';
|
|
4
|
+
export { extractFieldName, groupErrorsByField, normalizeForValidation } from './normalizeValues.js';
|
|
5
|
+
export { clearSchemaCache, configureFormSchemaBuilder, resolveSchema, wrapRawSchema } from './schemaLoader.js';
|
|
6
|
+
export type { FormSchemaBuilder } from './schemaLoader.js';
|
|
7
|
+
export { createInlineValidator, mongooseToJsonSchema } from './inlineValidator.js';
|
|
8
|
+
export type { FieldDescriptor, FieldKind, FormControllerOptions, FormSchemaInput, FormSchemaLike, FormState, FormStateListener, Locale, RefFieldOption, RefResolver, RefResolverArgs, RefResolverResult, SubmitResult, Unsubscribe, } from './types.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACpG,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC/G,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACnF,YAAY,EACV,eAAe,EACf,SAAS,EACT,qBAAqB,EACrB,eAAe,EACf,cAAc,EACd,SAAS,EACT,iBAAiB,EACjB,MAAM,EACN,cAAc,EACd,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,WAAW,GACZ,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { FormController, createForm } from './controller.js';
|
|
2
|
+
export { describeField, describeFields, labelFromName } from './describeField.js';
|
|
3
|
+
export { resolveFieldKind } from './resolveFieldKind.js';
|
|
4
|
+
export { extractFieldName, groupErrorsByField, normalizeForValidation } from './normalizeValues.js';
|
|
5
|
+
export { clearSchemaCache, configureFormSchemaBuilder, resolveSchema, wrapRawSchema } from './schemaLoader.js';
|
|
6
|
+
export { createInlineValidator, mongooseToJsonSchema } from './inlineValidator.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACpG,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAE/G,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FormSchemaLike, Locale } from './types.js';
|
|
2
|
+
export declare function mongooseToJsonSchema(modelSchema: Record<string, any>): Record<string, any>;
|
|
3
|
+
/**
|
|
4
|
+
* Build a {@link FormSchemaLike} backed by AJV + ajv-formats + ajv-i18n.
|
|
5
|
+
* The returned object has the same shape as a `FormSchemaWeb` instance,
|
|
6
|
+
* so it plugs straight into `FormController` and downstream rendering.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createInlineValidator(modelSchema: Record<string, any>, locale?: Locale): FormSchemaLike;
|
|
9
|
+
//# sourceMappingURL=inlineValidator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inlineValidator.d.ts","sourceRoot":"","sources":["../src/inlineValidator.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAmEzD,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAgB1F;AAqBD;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAChC,MAAM,GAAE,MAAa,GACpB,cAAc,CA4BhB"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
import addFormats from 'ajv-formats';
|
|
3
|
+
import localizeEs from 'ajv-i18n/localize/es';
|
|
4
|
+
import localizeEn from 'ajv-i18n/localize/en';
|
|
5
|
+
import localizePtBr from 'ajv-i18n/localize/pt-BR';
|
|
6
|
+
const LOCALIZERS = {
|
|
7
|
+
en: localizeEn,
|
|
8
|
+
es: localizeEs,
|
|
9
|
+
pt: localizePtBr,
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Convert a single Mongoose-style field definition into JSON Schema. Handles
|
|
13
|
+
* the common subset of attributes that appear in Prolibu schemas: type, enum,
|
|
14
|
+
* min/max, minLength/maxLength, format, regex, ref.
|
|
15
|
+
*
|
|
16
|
+
* Anything that doesn't map cleanly is dropped — AJV will validate what we
|
|
17
|
+
* give it. Custom Prolibu keywords (originalAttrs, arrayRules, etc.) are NOT
|
|
18
|
+
* handled here; for full Prolibu validation use FormSchemaWeb via
|
|
19
|
+
* configureFormSchemaBuilder.
|
|
20
|
+
*/
|
|
21
|
+
function fieldToJsonSchema(def) {
|
|
22
|
+
const out = {};
|
|
23
|
+
// Type — Mongoose uses PascalCase, JSON Schema uses lowercase.
|
|
24
|
+
const t = typeof def.type === 'string' ? def.type.toLowerCase() : def.type;
|
|
25
|
+
switch (t) {
|
|
26
|
+
case 'string':
|
|
27
|
+
case 'objectid':
|
|
28
|
+
out.type = 'string';
|
|
29
|
+
break;
|
|
30
|
+
case 'number':
|
|
31
|
+
case 'integer':
|
|
32
|
+
out.type = 'number';
|
|
33
|
+
break;
|
|
34
|
+
case 'boolean':
|
|
35
|
+
out.type = 'boolean';
|
|
36
|
+
break;
|
|
37
|
+
case 'date':
|
|
38
|
+
out.type = 'string';
|
|
39
|
+
out.format = 'date-time';
|
|
40
|
+
break;
|
|
41
|
+
case 'mixed':
|
|
42
|
+
// no constraint
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
if (Array.isArray(def.type)) {
|
|
46
|
+
const inner = typeof def.type[0] === 'string' ? def.type[0].toLowerCase() : 'string';
|
|
47
|
+
out.type = 'array';
|
|
48
|
+
out.items = { type: inner === 'number' ? 'number' : 'string' };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (Array.isArray(def.enum) && def.enum.length > 0)
|
|
52
|
+
out.enum = def.enum;
|
|
53
|
+
if (typeof def.min === 'number')
|
|
54
|
+
out.minimum = def.min;
|
|
55
|
+
if (typeof def.max === 'number')
|
|
56
|
+
out.maximum = def.max;
|
|
57
|
+
if (typeof def.minLength === 'number')
|
|
58
|
+
out.minLength = def.minLength;
|
|
59
|
+
if (typeof def.maxLength === 'number')
|
|
60
|
+
out.maxLength = def.maxLength;
|
|
61
|
+
const pattern = def.regex ?? def.match;
|
|
62
|
+
if (pattern instanceof RegExp)
|
|
63
|
+
out.pattern = pattern.source;
|
|
64
|
+
else if (typeof pattern === 'string')
|
|
65
|
+
out.pattern = pattern;
|
|
66
|
+
if (typeof def.format === 'string')
|
|
67
|
+
out.format = def.format;
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
export function mongooseToJsonSchema(modelSchema) {
|
|
71
|
+
const properties = {};
|
|
72
|
+
const required = [];
|
|
73
|
+
for (const [name, def] of Object.entries(modelSchema)) {
|
|
74
|
+
if (!def || typeof def !== 'object')
|
|
75
|
+
continue;
|
|
76
|
+
properties[name] = fieldToJsonSchema(def);
|
|
77
|
+
if (def.required)
|
|
78
|
+
required.push(name);
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
type: 'object',
|
|
82
|
+
properties,
|
|
83
|
+
required,
|
|
84
|
+
additionalProperties: true,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const ajvCache = new WeakMap();
|
|
88
|
+
function compile(modelSchema) {
|
|
89
|
+
const cached = ajvCache.get(modelSchema);
|
|
90
|
+
if (cached)
|
|
91
|
+
return cached;
|
|
92
|
+
const ajv = new Ajv({
|
|
93
|
+
allErrors: true,
|
|
94
|
+
strict: false,
|
|
95
|
+
validateFormats: true,
|
|
96
|
+
});
|
|
97
|
+
addFormats(ajv);
|
|
98
|
+
const jsonSchema = mongooseToJsonSchema(modelSchema);
|
|
99
|
+
const validate = ajv.compile(jsonSchema);
|
|
100
|
+
ajvCache.set(modelSchema, validate);
|
|
101
|
+
return validate;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Build a {@link FormSchemaLike} backed by AJV + ajv-formats + ajv-i18n.
|
|
105
|
+
* The returned object has the same shape as a `FormSchemaWeb` instance,
|
|
106
|
+
* so it plugs straight into `FormController` and downstream rendering.
|
|
107
|
+
*/
|
|
108
|
+
export function createInlineValidator(modelSchema, locale = 'es') {
|
|
109
|
+
// Property table — same shape `describeField` and `resolveFieldKind` expect.
|
|
110
|
+
// Each property carries `originalAttrs` as the side-channel for UI metadata.
|
|
111
|
+
const properties = {};
|
|
112
|
+
for (const [name, def] of Object.entries(modelSchema)) {
|
|
113
|
+
if (!def || typeof def !== 'object')
|
|
114
|
+
continue;
|
|
115
|
+
const originalAttrs = { ...def };
|
|
116
|
+
if (typeof originalAttrs.type === 'string') {
|
|
117
|
+
originalAttrs.type = originalAttrs.type.toLowerCase();
|
|
118
|
+
}
|
|
119
|
+
properties[name] = { originalAttrs };
|
|
120
|
+
}
|
|
121
|
+
const validateFn = compile(modelSchema);
|
|
122
|
+
const localize = LOCALIZERS[locale] || LOCALIZERS.es;
|
|
123
|
+
const instance = {
|
|
124
|
+
formSchema: { properties },
|
|
125
|
+
validator: { errors: [] },
|
|
126
|
+
validate(data) {
|
|
127
|
+
validateFn(data);
|
|
128
|
+
const errs = validateFn.errors ?? [];
|
|
129
|
+
if (errs.length > 0)
|
|
130
|
+
localize(errs);
|
|
131
|
+
instance.validator.errors = errs;
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
return instance;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=inlineValidator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inlineValidator.js","sourceRoot":"","sources":["../src/inlineValidator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip populated ref objects down to their `_id` so AJV's `objectid` format
|
|
3
|
+
* validator (which only accepts hex strings) doesn't reject loaded records.
|
|
4
|
+
*/
|
|
5
|
+
export declare function normalizeForValidation(data: Record<string, any>, properties: Record<string, any>): Record<string, any>;
|
|
6
|
+
interface AjvError {
|
|
7
|
+
keyword?: string;
|
|
8
|
+
instancePath?: string;
|
|
9
|
+
params?: {
|
|
10
|
+
missingProperty?: string;
|
|
11
|
+
};
|
|
12
|
+
message?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Map an AJV error to the field name it belongs to. */
|
|
15
|
+
export declare function extractFieldName(err: AjvError): string | null;
|
|
16
|
+
export declare function groupErrorsByField(errors: AjvError[]): Record<string, string[]>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=normalizeValues.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizeValues.d.ts","sourceRoot":"","sources":["../src/normalizeValues.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAcrB;AAED,UAAU,QAAQ;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wDAAwD;AACxD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAS7D;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAS/E"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip populated ref objects down to their `_id` so AJV's `objectid` format
|
|
3
|
+
* validator (which only accepts hex strings) doesn't reject loaded records.
|
|
4
|
+
*/
|
|
5
|
+
export function normalizeForValidation(data, properties) {
|
|
6
|
+
const out = { ...data };
|
|
7
|
+
for (const [name, def] of Object.entries(properties)) {
|
|
8
|
+
if (!def?.originalAttrs?.ref)
|
|
9
|
+
continue;
|
|
10
|
+
const v = out[name];
|
|
11
|
+
if (Array.isArray(v)) {
|
|
12
|
+
out[name] = v.map((item) => item && typeof item === 'object' && item._id ? item._id : item);
|
|
13
|
+
}
|
|
14
|
+
else if (v && typeof v === 'object' && v._id) {
|
|
15
|
+
out[name] = v._id;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
/** Map an AJV error to the field name it belongs to. */
|
|
21
|
+
export function extractFieldName(err) {
|
|
22
|
+
if (err.keyword === 'required' && err.params?.missingProperty) {
|
|
23
|
+
return err.params.missingProperty;
|
|
24
|
+
}
|
|
25
|
+
if (typeof err.instancePath === 'string' && err.instancePath.length > 0) {
|
|
26
|
+
const segments = err.instancePath.split('/').filter(Boolean);
|
|
27
|
+
return segments[0] || null;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
export function groupErrorsByField(errors) {
|
|
32
|
+
const out = {};
|
|
33
|
+
for (const err of errors) {
|
|
34
|
+
const field = extractFieldName(err);
|
|
35
|
+
if (!field)
|
|
36
|
+
continue;
|
|
37
|
+
if (!out[field])
|
|
38
|
+
out[field] = [];
|
|
39
|
+
out[field].push(err.message || 'Invalid value');
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=normalizeValues.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizeValues.js","sourceRoot":"","sources":["../src/normalizeValues.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAyB,EACzB,UAA+B;IAE/B,MAAM,GAAG,GAAwB,EAAE,GAAG,IAAI,EAAE,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACrD,IAAI,CAAE,GAAW,EAAE,aAAa,EAAE,GAAG;YAAE,SAAS;QAChD,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YACrB,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACzB,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAC/D,CAAC;QACJ,CAAC;aAAM,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;YAC/C,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AASD,wDAAwD;AACxD,MAAM,UAAU,gBAAgB,CAAC,GAAa;IAC5C,IAAI,GAAG,CAAC,OAAO,KAAK,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC;QAC9D,OAAO,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC;IACpC,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ,IAAI,GAAG,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxE,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC7D,OAAO,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAC7B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAkB;IACnD,MAAM,GAAG,GAA6B,EAAE,CAAC;IACzC,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACjC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,eAAe,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveFieldKind.d.ts","sourceRoot":"","sources":["../src/resolveFieldKind.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAmB5C,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,GAAG,GAAG,SAAS,GAAG,IAAI,CAUnE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Order matters — first non-undefined wins. `null` means "skip this field".
|
|
2
|
+
const RESOLVERS = [
|
|
3
|
+
(orig) => (orig.hidden ? null : undefined),
|
|
4
|
+
(orig) => (orig.uiCom === 'TextArea' ? 'textarea' : undefined),
|
|
5
|
+
(orig) => (orig.uiCom === 'HtmlEditor' ? 'html' : undefined),
|
|
6
|
+
(orig) => (orig.ref ? 'ref' : undefined),
|
|
7
|
+
(orig) => (Array.isArray(orig.enum) ? 'select' : undefined),
|
|
8
|
+
(orig) => (orig.type === 'boolean' ? 'boolean' : undefined),
|
|
9
|
+
(orig) => (orig.type === 'number' || orig.type === 'integer' ? 'number' : undefined),
|
|
10
|
+
(orig) => (orig.type === 'date' ? 'date' : undefined),
|
|
11
|
+
(orig) => (orig.type === 'string' ? 'text' : undefined),
|
|
12
|
+
];
|
|
13
|
+
export function resolveFieldKind(ajvProperty) {
|
|
14
|
+
const orig = ajvProperty?.originalAttrs;
|
|
15
|
+
if (!orig)
|
|
16
|
+
return null;
|
|
17
|
+
for (const resolve of RESOLVERS) {
|
|
18
|
+
const result = resolve(orig);
|
|
19
|
+
if (result === null)
|
|
20
|
+
return null;
|
|
21
|
+
if (result !== undefined)
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
return 'text';
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=resolveFieldKind.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveFieldKind.js","sourceRoot":"","sources":["../src/resolveFieldKind.ts"],"names":[],"mappings":"AAMA,4EAA4E;AAC5E,MAAM,SAAS,GAAe;IAC5B,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1C,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9D,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5D,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACxC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IACpF,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IACrD,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;CACxD,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,WAAgB;IAC/C,MAAM,IAAI,GAAG,WAAW,EAAE,aAA0C,CAAC;IACrE,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACjC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC;IAC1C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { FormSchemaInput, FormSchemaLike, Locale } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Constructor signature for `FormSchemaWeb` when consumers want us to build
|
|
4
|
+
* the instance from a raw Mongoose-style modelSchema. We accept this via
|
|
5
|
+
* dependency injection (see {@link configureFormSchemaBuilder}) so that
|
|
6
|
+
* `form-core` itself stays free of a hard dependency on `@skemify/cases`.
|
|
7
|
+
*/
|
|
8
|
+
export type FormSchemaBuilder = (modelSchema: Record<string, any>, locale: Locale) => FormSchemaLike;
|
|
9
|
+
/**
|
|
10
|
+
* Register the FormSchemaWeb builder once, at application bootstrap.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* import { configureFormSchemaBuilder } from '@prolibu-suite/cobalt-form-core'
|
|
14
|
+
* import { FormSchemaWeb } from '@skemify/cases/schemas'
|
|
15
|
+
*
|
|
16
|
+
* configureFormSchemaBuilder((modelSchema, locale) => {
|
|
17
|
+
* const inst = new FormSchemaWeb(modelSchema, locale)
|
|
18
|
+
* inst.buildAvjSchema()
|
|
19
|
+
* return inst
|
|
20
|
+
* })
|
|
21
|
+
*/
|
|
22
|
+
export declare function configureFormSchemaBuilder(fn: FormSchemaBuilder): void;
|
|
23
|
+
export declare function resolveSchema(input: FormSchemaInput): FormSchemaLike;
|
|
24
|
+
export declare function clearSchemaCache(): void;
|
|
25
|
+
/**
|
|
26
|
+
* Wrap a raw Mongoose/Prolibu-style modelSchema as a {@link FormSchemaLike}
|
|
27
|
+
* with a no-op validator.
|
|
28
|
+
*
|
|
29
|
+
* Useful for the `<co-form>` Web Component on the CDN, where the host page
|
|
30
|
+
* cannot ship the private `@skemify/cases` SDK. Fields render and values are
|
|
31
|
+
* tracked; validation is the caller's responsibility (use `coSubmit` and
|
|
32
|
+
* validate before persisting, or register a real builder).
|
|
33
|
+
*
|
|
34
|
+
* The raw schema must look like `{ fieldName: { type, required, ... } }`.
|
|
35
|
+
* Each entry becomes a property whose `originalAttrs` is the entry itself,
|
|
36
|
+
* which is the shape `describeField` / `resolveFieldKind` already expect.
|
|
37
|
+
*/
|
|
38
|
+
export declare function wrapRawSchema(modelSchema: Record<string, any>): FormSchemaLike;
|
|
39
|
+
//# sourceMappingURL=schemaLoader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaLoader.d.ts","sourceRoot":"","sources":["../src/schemaLoader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC9B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAChC,MAAM,EAAE,MAAM,KACX,cAAc,CAAC;AAIpB;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAEtE;AAQD,wBAAgB,aAAa,CAAC,KAAK,EAAE,eAAe,GAAG,cAAc,CAgBpE;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,cAAc,CAoB9E"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { createInlineValidator } from './inlineValidator.js';
|
|
2
|
+
let builder = null;
|
|
3
|
+
/**
|
|
4
|
+
* Register the FormSchemaWeb builder once, at application bootstrap.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import { configureFormSchemaBuilder } from '@prolibu-suite/cobalt-form-core'
|
|
8
|
+
* import { FormSchemaWeb } from '@skemify/cases/schemas'
|
|
9
|
+
*
|
|
10
|
+
* configureFormSchemaBuilder((modelSchema, locale) => {
|
|
11
|
+
* const inst = new FormSchemaWeb(modelSchema, locale)
|
|
12
|
+
* inst.buildAvjSchema()
|
|
13
|
+
* return inst
|
|
14
|
+
* })
|
|
15
|
+
*/
|
|
16
|
+
export function configureFormSchemaBuilder(fn) {
|
|
17
|
+
builder = fn;
|
|
18
|
+
}
|
|
19
|
+
const cache = new Map();
|
|
20
|
+
function cacheKey(modelSchema, locale) {
|
|
21
|
+
return `${locale}::${Object.keys(modelSchema).sort().join(',')}`;
|
|
22
|
+
}
|
|
23
|
+
export function resolveSchema(input) {
|
|
24
|
+
if ('instance' in input)
|
|
25
|
+
return input.instance;
|
|
26
|
+
const { modelSchema, locale = 'es' } = input;
|
|
27
|
+
const key = cacheKey(modelSchema, locale);
|
|
28
|
+
const cached = cache.get(key);
|
|
29
|
+
if (cached)
|
|
30
|
+
return cached;
|
|
31
|
+
// Use the host-registered builder (e.g. FormSchemaWeb) if available, else
|
|
32
|
+
// fall back to the bundled AJV-based inline validator. Means
|
|
33
|
+
// `createForm({ schema: { modelSchema } })` "just works" without any
|
|
34
|
+
// bootstrap step, while hosts that want full Prolibu validation can opt-in
|
|
35
|
+
// via configureFormSchemaBuilder().
|
|
36
|
+
const built = builder ? builder(modelSchema, locale) : createInlineValidator(modelSchema, locale);
|
|
37
|
+
cache.set(key, built);
|
|
38
|
+
return built;
|
|
39
|
+
}
|
|
40
|
+
export function clearSchemaCache() {
|
|
41
|
+
cache.clear();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Wrap a raw Mongoose/Prolibu-style modelSchema as a {@link FormSchemaLike}
|
|
45
|
+
* with a no-op validator.
|
|
46
|
+
*
|
|
47
|
+
* Useful for the `<co-form>` Web Component on the CDN, where the host page
|
|
48
|
+
* cannot ship the private `@skemify/cases` SDK. Fields render and values are
|
|
49
|
+
* tracked; validation is the caller's responsibility (use `coSubmit` and
|
|
50
|
+
* validate before persisting, or register a real builder).
|
|
51
|
+
*
|
|
52
|
+
* The raw schema must look like `{ fieldName: { type, required, ... } }`.
|
|
53
|
+
* Each entry becomes a property whose `originalAttrs` is the entry itself,
|
|
54
|
+
* which is the shape `describeField` / `resolveFieldKind` already expect.
|
|
55
|
+
*/
|
|
56
|
+
export function wrapRawSchema(modelSchema) {
|
|
57
|
+
const properties = {};
|
|
58
|
+
for (const [name, def] of Object.entries(modelSchema)) {
|
|
59
|
+
if (!def || typeof def !== 'object')
|
|
60
|
+
continue;
|
|
61
|
+
const originalAttrs = { ...def };
|
|
62
|
+
// Normalize Mongoose-style type strings ('String') to JSON-Schema style ('string')
|
|
63
|
+
// so resolveFieldKind matches them. Keep the original casing on the side-channel
|
|
64
|
+
// in case downstream code cares.
|
|
65
|
+
if (typeof originalAttrs.type === 'string') {
|
|
66
|
+
originalAttrs.type = originalAttrs.type.toLowerCase();
|
|
67
|
+
}
|
|
68
|
+
properties[name] = { originalAttrs };
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
formSchema: { properties },
|
|
72
|
+
validate: () => {
|
|
73
|
+
/* no-op */
|
|
74
|
+
},
|
|
75
|
+
validator: { errors: [] },
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=schemaLoader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaLoader.js","sourceRoot":"","sources":["../src/schemaLoader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAc7D,IAAI,OAAO,GAA6B,IAAI,CAAC;AAE7C;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,0BAA0B,CAAC,EAAqB;IAC9D,OAAO,GAAG,EAAE,CAAC;AACf,CAAC;AAED,MAAM,KAAK,GAAG,IAAI,GAAG,EAA0B,CAAC;AAEhD,SAAS,QAAQ,CAAC,WAAgC,EAAE,MAAc;IAChE,OAAO,GAAG,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAsB;IAClD,IAAI,UAAU,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC,QAAQ,CAAC;IAE/C,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;IAC7C,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,0EAA0E;IAC1E,6DAA6D;IAC7D,qEAAqE;IACrE,2EAA2E;IAC3E,oCAAoC;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAClG,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACtB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAAC,WAAgC;IAC5D,MAAM,UAAU,GAAwB,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QACtD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,SAAS;QAC9C,MAAM,aAAa,GAAG,EAAE,GAAG,GAAG,EAAE,CAAC;QACjC,mFAAmF;QACnF,iFAAiF;QACjF,iCAAiC;QACjC,IAAI,OAAO,aAAa,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC3C,aAAa,CAAC,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACxD,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,EAAE,CAAC;IACvC,CAAC;IACD,OAAO;QACL,UAAU,EAAE,EAAE,UAAU,EAAE;QAC1B,QAAQ,EAAE,GAAG,EAAE;YACb,WAAW;QACb,CAAC;QACD,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;KAC1B,CAAC;AACJ,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export type Locale = 'en' | 'es' | 'pt';
|
|
2
|
+
export type FieldKind = 'text' | 'textarea' | 'number' | 'date' | 'boolean' | 'select' | 'ref' | 'html' | 'custom';
|
|
3
|
+
export interface FieldDescriptor {
|
|
4
|
+
name: string;
|
|
5
|
+
kind: FieldKind;
|
|
6
|
+
label: string;
|
|
7
|
+
placeholder: string;
|
|
8
|
+
required: boolean;
|
|
9
|
+
disabled: boolean;
|
|
10
|
+
hidden: boolean;
|
|
11
|
+
fullWidth: boolean;
|
|
12
|
+
helperText: string;
|
|
13
|
+
originalAttrs: Record<string, any>;
|
|
14
|
+
ajvProperty: Record<string, any>;
|
|
15
|
+
}
|
|
16
|
+
export interface FormState {
|
|
17
|
+
values: Record<string, any>;
|
|
18
|
+
errors: Record<string, string[]>;
|
|
19
|
+
touched: Record<string, boolean>;
|
|
20
|
+
isValid: boolean;
|
|
21
|
+
isDirty: boolean;
|
|
22
|
+
isSubmitting: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface SubmitResult {
|
|
25
|
+
values: Record<string, any>;
|
|
26
|
+
isValid: boolean;
|
|
27
|
+
errors: Record<string, string[]>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Structural type for a `FormSchemaWeb` instance from `@skemify/cases`.
|
|
31
|
+
* Defined structurally so we don't impose a hard runtime dep on the SDK.
|
|
32
|
+
*/
|
|
33
|
+
export interface FormSchemaLike {
|
|
34
|
+
formSchema: {
|
|
35
|
+
properties: Record<string, any>;
|
|
36
|
+
};
|
|
37
|
+
validate: (data: Record<string, any>) => void;
|
|
38
|
+
validator?: {
|
|
39
|
+
errors?: any[];
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export type FormSchemaInput = {
|
|
43
|
+
instance: FormSchemaLike;
|
|
44
|
+
} | {
|
|
45
|
+
modelSchema: Record<string, any>;
|
|
46
|
+
locale?: Locale;
|
|
47
|
+
};
|
|
48
|
+
export interface FormControllerOptions {
|
|
49
|
+
schema: FormSchemaInput;
|
|
50
|
+
initialValues?: Record<string, any>;
|
|
51
|
+
validateOn?: 'change' | 'blur' | 'submit';
|
|
52
|
+
}
|
|
53
|
+
export type FormStateListener = (state: FormState) => void;
|
|
54
|
+
export type Unsubscribe = () => void;
|
|
55
|
+
/** A single selectable record returned by a {@link RefResolver}. */
|
|
56
|
+
export interface RefFieldOption {
|
|
57
|
+
value: string;
|
|
58
|
+
label: string;
|
|
59
|
+
meta?: Record<string, any>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Arguments handed to a {@link RefResolver}. The host inspects them and
|
|
63
|
+
* decides what to fetch.
|
|
64
|
+
*
|
|
65
|
+
* - `query`/`page` is the live-search case (user typing or scrolling).
|
|
66
|
+
* - `ids` is the hydration case (form opened with raw `_id` values; we need
|
|
67
|
+
* labels for the chips).
|
|
68
|
+
*/
|
|
69
|
+
export interface RefResolverArgs {
|
|
70
|
+
model: string;
|
|
71
|
+
query?: string;
|
|
72
|
+
page?: number;
|
|
73
|
+
pageSize?: number;
|
|
74
|
+
ids?: string[];
|
|
75
|
+
}
|
|
76
|
+
export interface RefResolverResult {
|
|
77
|
+
options: RefFieldOption[];
|
|
78
|
+
total?: number;
|
|
79
|
+
hasMore?: boolean;
|
|
80
|
+
}
|
|
81
|
+
export type RefResolver = (args: RefResolverArgs) => Promise<RefResolverResult>;
|
|
82
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAExC,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,UAAU,GACV,QAAQ,GACR,MAAM,GACN,SAAS,GACT,QAAQ,GACR,KAAK,GACL,MAAM,GACN,QAAQ,CAAC;AAEb,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAClC;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,CAAC;IAChD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;IAC9C,SAAS,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,GAAG,EAAE,CAAA;KAAE,CAAC;CAChC;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,EAAE,cAAc,CAAA;CAAE,GAC5B;IAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;CAC3C;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;AAC3D,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AAErC,oEAAoE;AACpE,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,eAAe,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@prolibu-suite/cobalt-form-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Cobalt Form — framework-agnostic core. Schema-driven form controller backed by AJV via FormSchemaWeb.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"form",
|
|
20
|
+
"schema",
|
|
21
|
+
"ajv",
|
|
22
|
+
"cobalt",
|
|
23
|
+
"prolibu",
|
|
24
|
+
"headless"
|
|
25
|
+
],
|
|
26
|
+
"author": "Prolibu <contact@prolibu.com>",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"ajv": "^8.17.1",
|
|
30
|
+
"ajv-formats": "^3.0.1",
|
|
31
|
+
"ajv-i18n": "^4.2.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"typescript": "^6.0.3",
|
|
35
|
+
"vitest": "^4.1.4"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc -p tsconfig.json",
|
|
42
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
43
|
+
"clean": "rm -rf dist",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"test:watch": "vitest"
|
|
46
|
+
}
|
|
47
|
+
}
|