notform 1.0.7 → 2.0.0-alpha.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.js CHANGED
@@ -1,569 +0,0 @@
1
- import { computed, createBlock, createTextVNode, defineComponent, guardReactiveProps, inject, mergeProps, nextTick, normalizeProps, onMounted, openBlock, provide, reactive, ref, renderSlot, resolveDynamicComponent, toDisplayString, toValue, unref, useAttrs, useId, vShow, withCtx, withDirectives } from "vue";
2
- import { getProperty, parsePath, setProperty } from "dot-prop";
3
- import { isEqual } from "es-toolkit/predicate";
4
-
5
- //#region src/utils/not-form-context.ts
6
- /** Internal registry mapping form IDs to their injection keys */
7
- const notFormContextKeys = /* @__PURE__ */ new Map();
8
- /** Injection key for the unique identifier of the current active form */
9
- const CURRENT_NOT_FORM_ID_KEY = Symbol("notform:id");
10
- /**
11
- * Retrieves an existing injection key or creates a new one for a form ID.
12
- * @template TSchema The validation schema type derived from ObjectSchema.
13
- * @param id The unique string identifier for the form.
14
- * @returns A strictly typed InjectionKey for the form context.
15
- */
16
- function getNotFormContextKey(id) {
17
- if (!notFormContextKeys.has(id)) {
18
- const key = Symbol(`notform:${id}:context`);
19
- notFormContextKeys.set(id, key);
20
- }
21
- return notFormContextKeys.get(id);
22
- }
23
- /**
24
- * Locates and returns the active form context associated with a given ID.
25
- * @template TSchema The validation schema type derived from ObjectSchema.
26
- * @param id The unique identifier of the form to access.
27
- * @returns The resolved form context object.
28
- * @throws Error if the context cannot be found in the current component tree.
29
- */
30
- function withContext(id) {
31
- const context = inject(getNotFormContextKey(id));
32
- if (!context) throw new Error(`No form context found for form with id "${id}"`);
33
- return context;
34
- }
35
-
36
- //#endregion
37
- //#region src/components/not-form.vue?vue&type=script&setup=true&lang.ts
38
- /**
39
- * Provides form context and a structural wrapper for form fields.
40
- * @template TSchema The validation schema type derived from ObjectSchema.
41
- */
42
- var not_form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
43
- __name: "not-form",
44
- props: {
45
- id: {
46
- type: String,
47
- required: true
48
- },
49
- asChild: {
50
- type: Boolean,
51
- required: false,
52
- default: false
53
- },
54
- as: {
55
- type: String,
56
- required: false,
57
- default: "form"
58
- }
59
- },
60
- setup(__props) {
61
- const props = __props;
62
- /**
63
- * Slots provided by the Form component.
64
- */
65
- const attributes = useAttrs();
66
- const form = withContext(props.id);
67
- const mergedAttrs = computed(() => ({
68
- ...attributes,
69
- id: form.id
70
- }));
71
- provide(CURRENT_NOT_FORM_ID_KEY, form.id);
72
- return (_ctx, _cache) => {
73
- return props.asChild ? renderSlot(_ctx.$slots, "default", normalizeProps(mergeProps({ key: 0 }, {
74
- ...unref(form),
75
- attributes: mergedAttrs.value
76
- }))) : (openBlock(), createBlock(resolveDynamicComponent(props.as), normalizeProps(mergeProps({ key: 1 }, mergedAttrs.value)), {
77
- default: withCtx(() => [renderSlot(_ctx.$slots, "default", normalizeProps(guardReactiveProps(unref(form))))]),
78
- _: 3
79
- }, 16));
80
- };
81
- }
82
- });
83
-
84
- //#endregion
85
- //#region src/components/not-form.vue
86
- var not_form_default = not_form_vue_vue_type_script_setup_true_lang_default;
87
-
88
- //#endregion
89
- //#region src/components/not-field.vue?vue&type=script&setup=true&lang.ts
90
- /**
91
- * Component for individual form fields.
92
- * Manages field-level state, validation triggers, and events.
93
- */
94
- var not_field_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
95
- __name: "not-field",
96
- props: { name: {
97
- type: String,
98
- required: true
99
- } },
100
- setup(__props) {
101
- const props = __props;
102
- /**
103
- * Slots provided by the NotField component.
104
- */
105
- const formID = inject(CURRENT_NOT_FORM_ID_KEY);
106
- if (!formID) throw new Error("NotField must be used inside a NotForm component");
107
- const { mode, validateOn, touchedFields, dirtyFields, validateField, getFieldErrors, touchField, dirtyField } = withContext(formID);
108
- /**
109
- * Triggers validation for this specific field.
110
- * @returns Promise resolving to the validation result.
111
- */
112
- async function validate() {
113
- return await validateField(props.name);
114
- }
115
- /**
116
- * The consolidated reactive state exposed to the component's template.
117
- */
118
- const context = reactive({
119
- name: computed(() => props.name),
120
- errors: computed(() => getFieldErrors(props.name).map((error) => error.message)),
121
- isTouched: computed(() => touchedFields.value.has(props.name)),
122
- isDirty: computed(() => dirtyFields.value.has(props.name)),
123
- isValid: computed(() => getFieldErrors(props.name).length === 0),
124
- validate,
125
- methods: {
126
- onBlur: function() {
127
- touchField(props.name);
128
- if (!validateOn.includes("blur")) return;
129
- validate();
130
- },
131
- onChange: function() {
132
- dirtyField(props.name);
133
- if (!validateOn.includes("change")) return;
134
- if (mode === "lazy") return;
135
- if (mode === "eager" && getFieldErrors(props.name).length > 0) validate();
136
- },
137
- onInput: function() {
138
- dirtyField(props.name);
139
- if (!validateOn.includes("input")) return;
140
- if (mode === "lazy") return;
141
- if (mode === "eager" && getFieldErrors(props.name).length > 0) validate();
142
- },
143
- onFocus: function() {
144
- if (!validateOn.includes("focus")) return;
145
- validate();
146
- }
147
- }
148
- });
149
- onMounted(async () => {
150
- await nextTick();
151
- if (validateOn.includes("mount")) await validate();
152
- });
153
- return (_ctx, _cache) => {
154
- return renderSlot(_ctx.$slots, "default", normalizeProps(guardReactiveProps(context)));
155
- };
156
- }
157
- });
158
-
159
- //#endregion
160
- //#region src/components/not-field.vue
161
- var not_field_default = not_field_vue_vue_type_script_setup_true_lang_default;
162
-
163
- //#endregion
164
- //#region src/components/not-message.vue?vue&type=script&setup=true&lang.ts
165
- /**
166
- * Displays validation error messages for a specific form field.
167
- * @template TSchema The validation schema type derived from ObjectSchema.
168
- */
169
- var not_message_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
170
- __name: "not-message",
171
- props: {
172
- name: {
173
- type: String,
174
- required: true
175
- },
176
- as: {
177
- type: String,
178
- required: false,
179
- default: "span"
180
- }
181
- },
182
- setup(__props) {
183
- const props = __props;
184
- /**
185
- * Slots provided by the Message component.
186
- */
187
- const attributes = useAttrs();
188
- const formID = inject(CURRENT_NOT_FORM_ID_KEY);
189
- if (!formID) throw new Error("Message must be used inside a NotForm component");
190
- const { getFieldErrors } = withContext(formID);
191
- /**
192
- * The reactive state provided to the component's slot.
193
- */
194
- const context = reactive({ message: computed(() => getFieldErrors(props.name).map((error) => error.message)[0]) });
195
- const mergedAttrs = computed(() => ({
196
- ...context,
197
- attributes
198
- }));
199
- return (_ctx, _cache) => {
200
- return renderSlot(_ctx.$slots, "default", normalizeProps(guardReactiveProps(mergedAttrs.value)), () => [withDirectives((openBlock(), createBlock(resolveDynamicComponent(props.as), normalizeProps(guardReactiveProps(unref(attributes))), {
201
- default: withCtx(() => [createTextVNode(toDisplayString(context.message), 1)]),
202
- _: 1
203
- }, 16)), [[vShow, context.message]])]);
204
- };
205
- }
206
- });
207
-
208
- //#endregion
209
- //#region src/components/not-message.vue
210
- var not_message_default = not_message_vue_vue_type_script_setup_true_lang_default;
211
-
212
- //#endregion
213
- //#region src/components/not-array-field.vue?vue&type=script&setup=true&lang.ts
214
- /**
215
- * Component for managing array-based form fields.
216
- * Provides methods for adding, removing, and updating array items.
217
- * @template TArraySchema Schema for the array field.
218
- * @template TObjectSchema Parent form object schema.
219
- */
220
- var not_array_field_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
221
- __name: "not-array-field",
222
- props: {
223
- name: {
224
- type: String,
225
- required: true
226
- },
227
- schema: {
228
- type: null,
229
- required: true
230
- }
231
- },
232
- setup(__props) {
233
- const props = __props;
234
- /**
235
- * Slots provided by the NotArrayField component.
236
- */
237
- const formID = inject(CURRENT_NOT_FORM_ID_KEY);
238
- if (!formID) throw new Error("NotArrayField must be used inside a NotForm component");
239
- const { state, validateField, getFieldErrors } = withContext(formID);
240
- /**
241
- * Reactive bridge between the form state and the specific array field.
242
- */
243
- const arrayState = computed({
244
- get() {
245
- const fieldValue = getProperty(state.value, props.name);
246
- return Array.isArray(fieldValue) ? fieldValue : [];
247
- },
248
- set(value) {
249
- setProperty(state.value, props.name, value);
250
- validateField(props.name);
251
- }
252
- });
253
- /**
254
- * Computes individual field contexts for each item in the array.
255
- */
256
- const fields = computed(() => {
257
- return arrayState.value.map((value, index) => {
258
- return {
259
- key: index,
260
- index,
261
- value,
262
- first: index === 0,
263
- last: index === arrayState.value.length - 1
264
- };
265
- });
266
- });
267
- /**
268
- * Adds a new item to the end of the array.
269
- * @param data The initial data for the new item.
270
- */
271
- function append(data) {
272
- arrayState.value = [...arrayState.value, data];
273
- }
274
- /**
275
- * Adds a new item to the beginning of the array.
276
- * @param data The initial data for the new item.
277
- */
278
- function prepend(data) {
279
- arrayState.value = [data, ...arrayState.value];
280
- }
281
- /**
282
- * Removes the item at the specified index.
283
- * @param index The index of the item to remove.
284
- */
285
- function remove(index) {
286
- const newArray = [...arrayState.value];
287
- newArray.splice(index, 1);
288
- arrayState.value = newArray;
289
- }
290
- /**
291
- * Inserts a new item at a specific index.
292
- * @param index The insertion index.
293
- * @param data The initial data for the new item.
294
- */
295
- function insert(index, data) {
296
- const newArray = [...arrayState.value];
297
- newArray.splice(index, 0, data);
298
- arrayState.value = newArray;
299
- }
300
- /**
301
- * Replaces the data at a specific index.
302
- * @param index The target index.
303
- * @param data The new data to apply.
304
- */
305
- function update(index, data) {
306
- const newArray = [...arrayState.value];
307
- newArray[index] = data;
308
- arrayState.value = newArray;
309
- }
310
- /**
311
- * Reactive context object exposed to the NotArrayField slot.
312
- */
313
- const context = reactive({
314
- name: computed(() => props.name),
315
- errors: computed(() => getFieldErrors(props.name).map((error) => error.message)),
316
- fields,
317
- append,
318
- prepend,
319
- remove,
320
- insert,
321
- update
322
- });
323
- return (_ctx, _cache) => {
324
- return renderSlot(_ctx.$slots, "default", normalizeProps(guardReactiveProps(context)));
325
- };
326
- }
327
- });
328
-
329
- //#endregion
330
- //#region src/components/not-array-field.vue
331
- var not_array_field_default = not_array_field_vue_vue_type_script_setup_true_lang_default;
332
-
333
- //#endregion
334
- //#region src/utils/helpers.ts
335
- /**
336
- * Normalizes a validation path segment into a standard property key.
337
- * @param segment The path segment to normalize.
338
- * @returns The normalized key.
339
- */
340
- function normalizeSegment(segment) {
341
- if (typeof segment === "object" && segment !== null && "key" in segment) return segment.key;
342
- return segment;
343
- }
344
- /**
345
- * Checks if a validation issue path matches a target field path.
346
- * @param issuePath The path array from the validation issue.
347
- * @param targetPath The normalized path to compare against.
348
- * @returns True if the paths are equivalent.
349
- */
350
- function isIssuePathEqual(issuePath, targetPath) {
351
- if (!issuePath) return false;
352
- if (issuePath.length !== targetPath.length) return false;
353
- return issuePath.every((segment, i) => {
354
- const normalizedSegment = normalizeSegment(segment);
355
- const targetSegment = targetPath[i];
356
- if (typeof normalizedSegment === "number" || typeof targetSegment === "number") return Number(normalizedSegment) === Number(targetSegment);
357
- return normalizedSegment === targetSegment;
358
- });
359
- }
360
- /**
361
- * Recursively retrieves all reachable paths of an object as dot-separated strings.
362
- * @param object The object to traverse.
363
- * @param prefix An optional prefix to prepended to each generated path.
364
- * @returns An array of string paths representing the object's structure.
365
- */
366
- function getObjectPaths(object, prefix = "") {
367
- let paths = [];
368
- if (typeof object !== "object" || object === null) return paths;
369
- for (const key in object) if (Object.prototype.hasOwnProperty.call(object, key)) {
370
- const value = object[key];
371
- const isArray = Array.isArray(object);
372
- const isInt = /^\d+$/.test(key);
373
- let currentKey = key;
374
- if (isArray && isInt) currentKey = `[${key}]`;
375
- const path = prefix ? prefix.endsWith("]") || currentKey.startsWith("[") ? `${prefix}${currentKey}` : `${prefix}.${currentKey}` : currentKey;
376
- paths.push(path);
377
- if (typeof value === "object" && value !== null) paths = paths.concat(getObjectPaths(value, path));
378
- }
379
- return paths;
380
- }
381
-
382
- //#endregion
383
- //#region src/composables/use-not-form.ts
384
- /**
385
- * Initializes a form instance with validation logic, reactive state, and lifecycle methods.
386
- * @template TSchema The validation schema type derived from ObjectSchema.
387
- * @param options Configuration object for the form behavior and initial state.
388
- * @returns The gathered form context object.
389
- */
390
- function useNotForm(options) {
391
- const { id = useId(), schema: _schema, initialErrors: _initialErrors = [], initialState: _initialState = {}, mode = "eager", validateOn = [
392
- "blur",
393
- "input",
394
- "change"
395
- ], onValidate, onReset, onError, onSubmit } = options;
396
- /** Active validation schema reference */
397
- const schema = computed(() => toValue(_schema));
398
- /** Baseline reactive state at creation */
399
- const initialState = structuredClone(_initialState);
400
- /** Baseline validation issues at creation */
401
- const initialErrors = structuredClone(_initialErrors);
402
- /** Internal state set by last custom reset */
403
- const onResetState = ref(null);
404
- /** Internal errors set by last custom reset */
405
- const onResetErrors = ref(null);
406
- /** The unified reactive context for the form instance */
407
- const context = {
408
- id,
409
- schema,
410
- initialState,
411
- initialErrors,
412
- mode,
413
- validateOn,
414
- state: ref(structuredClone(initialState)),
415
- errors: ref([...initialErrors]),
416
- isValidating: ref(false),
417
- isSubmitting: ref(false),
418
- touchedFields: ref(/* @__PURE__ */ new Set()),
419
- dirtyFields: ref(/* @__PURE__ */ new Set()),
420
- isValid: computed(() => context.errors.value.length === 0),
421
- isTouched: computed(() => context.touchedFields.value.size > 0),
422
- isDirty: computed(() => context.dirtyFields.value.size > 0),
423
- async validate() {
424
- context.isValidating.value = true;
425
- try {
426
- const result = await context.schema.value["~standard"].validate(context.state.value);
427
- if (result.issues) {
428
- context.errors.value = [...result.issues];
429
- onError?.([...result.issues]);
430
- return { issues: result.issues };
431
- }
432
- if (onValidate) {
433
- const customResult = await onValidate(result.value);
434
- if (customResult === false) {
435
- const formLevelError = {
436
- message: "Validation failed",
437
- path: []
438
- };
439
- context.errors.value = [formLevelError];
440
- onError?.([formLevelError]);
441
- return { issues: [formLevelError] };
442
- }
443
- if (Array.isArray(customResult) && customResult.length > 0) {
444
- context.errors.value = customResult;
445
- onError?.(customResult);
446
- return { issues: customResult };
447
- }
448
- }
449
- context.errors.value = [];
450
- return { value: result.value };
451
- } finally {
452
- context.isValidating.value = false;
453
- }
454
- },
455
- async validateField(path) {
456
- context.isValidating.value = true;
457
- try {
458
- const result = await context.schema.value["~standard"].validate(context.state.value);
459
- const pathArray = parsePath(path);
460
- context.errors.value = context.errors.value.filter((error) => !isIssuePathEqual(error.path, pathArray));
461
- if (result.issues) {
462
- const errorsForPath = result.issues.filter((error) => isIssuePathEqual(error.path, pathArray));
463
- if (errorsForPath.length > 0) {
464
- context.errors.value = [...context.errors.value, ...errorsForPath];
465
- return { issues: errorsForPath };
466
- }
467
- return { value: getProperty(context.state.value, path) };
468
- }
469
- return { value: getProperty(result.value, path) };
470
- } finally {
471
- context.isValidating.value = false;
472
- }
473
- },
474
- reset(_state, _errors, _validate = false) {
475
- if (_state) {
476
- onResetState.value = structuredClone(_state);
477
- context.state.value = structuredClone(_state);
478
- } else {
479
- onResetState.value = null;
480
- context.state.value = structuredClone(initialState);
481
- }
482
- if (_errors) {
483
- onResetErrors.value = structuredClone(_errors);
484
- context.errors.value = [..._errors];
485
- } else {
486
- onResetErrors.value = null;
487
- context.errors.value = [...initialErrors];
488
- }
489
- context.touchedFields.value.clear();
490
- context.dirtyFields.value.clear();
491
- onReset?.();
492
- if (_validate) context.validate();
493
- },
494
- setState(_state, _validate = true) {
495
- context.state.value = {
496
- ...context.state.value,
497
- ..._state
498
- };
499
- getObjectPaths(_state).forEach((path) => {
500
- context.touchedFields.value.add(path);
501
- if (!isEqual(getProperty(context.state.value, path), getProperty(onResetState.value ?? initialState, path))) context.dirtyFields.value.add(path);
502
- else context.dirtyFields.value.delete(path);
503
- if (_validate) context.validateField(path);
504
- });
505
- },
506
- setErrors(_errors) {
507
- context.errors.value = [...context.errors.value, ..._errors];
508
- },
509
- clearErrors() {
510
- context.errors.value = [];
511
- },
512
- getFieldErrors(field) {
513
- const pathArray = parsePath(field);
514
- return context.errors.value.filter((error) => isIssuePathEqual(error.path, pathArray));
515
- },
516
- touchField(field) {
517
- context.touchedFields.value.add(field);
518
- },
519
- touchAllFields() {
520
- const paths = getObjectPaths(context.state.value);
521
- context.touchedFields.value = new Set(paths);
522
- },
523
- dirtyField(field) {
524
- if (!isEqual(getProperty(context.state.value, field), getProperty(onResetState.value ?? initialState, field))) context.dirtyFields.value.add(field);
525
- else context.dirtyFields.value.delete(field);
526
- },
527
- dirtyAllFields() {
528
- const paths = getObjectPaths(context.state.value);
529
- const newDirty = /* @__PURE__ */ new Set();
530
- paths.forEach((path) => {
531
- if (!isEqual(getProperty(context.state.value, path), getProperty(onResetState.value ?? initialState, path))) newDirty.add(path);
532
- });
533
- context.dirtyFields.value = newDirty;
534
- },
535
- async submit(event) {
536
- context.isSubmitting.value = true;
537
- try {
538
- context.touchAllFields();
539
- context.dirtyAllFields();
540
- const result = await context.validate();
541
- if (result.issues) {
542
- event.preventDefault();
543
- const errors = [...result.issues];
544
- context.errors.value = errors;
545
- onError?.(errors);
546
- return;
547
- }
548
- if (onSubmit) {
549
- event.preventDefault();
550
- await onSubmit(result.value);
551
- }
552
- } catch {
553
- event.preventDefault();
554
- onError?.([{
555
- message: "An unexpected error occurred during submission",
556
- path: []
557
- }]);
558
- } finally {
559
- context.isSubmitting.value = false;
560
- }
561
- }
562
- };
563
- provide(getNotFormContextKey(id), context);
564
- return context;
565
- }
566
- var use_not_form_default = useNotForm;
567
-
568
- //#endregion
569
- export { not_array_field_default as NotArrayField, not_field_default as NotField, not_form_default as NotForm, not_message_default as NotMessage, use_not_form_default as useNotForm };
package/package.json CHANGED
@@ -1,11 +1,8 @@
1
1
  {
2
2
  "name": "notform",
3
- "version": "1.0.7",
3
+ "version": "2.0.0-alpha.0",
4
4
  "type": "module",
5
5
  "private": false,
6
- "publishConfig": {
7
- "access": "public"
8
- },
9
6
  "description": "Vue Forms Without the Friction",
10
7
  "author": "Favour Emeka <favorodera@gmail.com>",
11
8
  "license": "MIT",
@@ -17,14 +14,8 @@
17
14
  "bugs": {
18
15
  "url": "https://github.com/favorodera/notform/issues"
19
16
  },
20
- "module": "./dist/index.js",
21
- "main": "./dist/index.js",
22
17
  "exports": {
23
- ".": {
24
- "types": "./dist/index.d.ts",
25
- "import": "./dist/index.js",
26
- "default": "./dist/index.js"
27
- },
18
+ ".": "./dist/index.js",
28
19
  "./package.json": "./package.json"
29
20
  },
30
21
  "types": "./dist/index.d.ts",
@@ -32,35 +23,28 @@
32
23
  "dist"
33
24
  ],
34
25
  "sideEffects": false,
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "peerDependencies": {
30
+ "vue": "^3.0.0"
31
+ },
35
32
  "devDependencies": {
36
- "@stylistic/eslint-plugin": "^5.7.0",
37
- "@types/node": "^25.0.10",
38
- "@vitejs/plugin-vue": "^6.0.3",
39
- "@vitest/browser-playwright": "^4.0.16",
40
- "@vitest/eslint-plugin": "^1.6.6",
41
- "@vitest/ui": "^4.0.17",
42
- "@vue/eslint-config-typescript": "^14.6.0",
43
- "eslint": "^9.39.2",
44
- "eslint-plugin-vue": "^10.7.0",
45
- "playwright": "^1.57.0",
46
- "tsdown": "^0.18.1",
47
- "type-fest": "^5.4.1",
48
- "typescript": "^5.9.3",
49
- "unplugin-vue": "^7.1.0",
50
- "vite": "^7.3.0",
51
- "vitest": "^4.0.16",
52
- "vitest-browser-vue": "^2.0.1",
53
- "vue": "^3.5.25",
54
- "vue-tsc": "^3.2.2"
33
+ "@types/node": "^25.5.0",
34
+ "@vitejs/plugin-vue": "^6.0.5",
35
+ "@vue/test-utils": "^2.4.6",
36
+ "happy-dom": "^20.8.9",
37
+ "tsdown": "^0.21.7",
38
+ "type-fest": "^5.5.0",
39
+ "vite": "^8.0.3",
40
+ "vitest": "^4.1.2",
41
+ "vue": "^3.5.31",
42
+ "vue-tsc": "^3.2.6"
55
43
  },
56
44
  "dependencies": {
57
45
  "@standard-schema/spec": "^1.1.0",
58
- "dot-prop": "^10.1.0",
59
46
  "es-toolkit": "^1.45.1"
60
47
  },
61
- "engines": {
62
- "node": ">=24.0.0"
63
- },
64
48
  "keywords": [
65
49
  "notform",
66
50
  "vue",
@@ -69,13 +53,18 @@
69
53
  "validator",
70
54
  "vue form validator"
71
55
  ],
56
+ "engines": {
57
+ "node": ">=24.13.0"
58
+ },
59
+ "inlinedDependencies": {
60
+ "type-fest": "5.5.0"
61
+ },
72
62
  "scripts": {
73
63
  "build": "tsdown",
74
64
  "dev": "tsdown --watch",
75
65
  "test": "vitest",
76
66
  "test:watch": "vitest watch --ui",
77
67
  "typecheck": "vue-tsc --noEmit",
78
- "lint": "eslint . --fix",
79
- "cleanup": "rm -rf dist node_modules .turbo package-lock.json"
68
+ "lint": "eslint . --fix"
80
69
  }
81
70
  }