@lucas-barake/effect-form 0.15.0 → 0.16.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/src/Validation.ts CHANGED
@@ -1,95 +1,95 @@
1
- import * as Option from "effect/Option"
2
- import * as ParseResult from "effect/ParseResult"
3
- import type * as AST from "effect/SchemaAST"
4
- import { schemaPathToFieldPath } from "./Path.js"
1
+ import * as Option from "effect/Option";
2
+ import * as ParseResult from "effect/ParseResult";
3
+ import type * as AST from "effect/SchemaAST";
4
+ import { schemaPathToFieldPath } from "./Path.js";
5
5
 
6
- export type ErrorSource = "field" | "refinement"
6
+ export type ErrorSource = "field" | "refinement";
7
7
 
8
8
  export interface ErrorEntry {
9
- readonly message: string
10
- readonly source: ErrorSource
9
+ readonly message: string;
10
+ readonly source: ErrorSource;
11
11
  }
12
12
 
13
13
  const getBaseAST = (ast: AST.AST): AST.AST => {
14
14
  switch (ast._tag) {
15
15
  case "Refinement":
16
16
  case "Transformation":
17
- return getBaseAST(ast.from)
17
+ return getBaseAST(ast.from);
18
18
  default:
19
- return ast
19
+ return ast;
20
20
  }
21
- }
21
+ };
22
22
 
23
23
  const isCompositeType = (ast: AST.AST): boolean => {
24
- const base = getBaseAST(ast)
24
+ const base = getBaseAST(ast);
25
25
  switch (base._tag) {
26
26
  case "TypeLiteral": // Schema.Struct
27
27
  case "TupleType": // Schema.Tuple
28
28
  case "Declaration": // Schema.Class, Schema.TaggedClass
29
29
  case "Union": // Schema.Union
30
30
  case "Suspend": // Recursive schemas
31
- return true
31
+ return true;
32
32
  default:
33
- return false
33
+ return false;
34
34
  }
35
- }
35
+ };
36
36
 
37
37
  export const extractFirstError = (error: ParseResult.ParseError): Option.Option<string> => {
38
- const issues = ParseResult.ArrayFormatter.formatErrorSync(error)
38
+ const issues = ParseResult.ArrayFormatter.formatErrorSync(error);
39
39
  if (issues.length === 0) {
40
- return Option.none()
40
+ return Option.none();
41
41
  }
42
- return Option.some(issues[0].message)
43
- }
42
+ return Option.some(issues[0].message);
43
+ };
44
44
 
45
45
  export const routeErrors = (error: ParseResult.ParseError): Map<string, string> => {
46
- const result = new Map<string, string>()
47
- const issues = ParseResult.ArrayFormatter.formatErrorSync(error)
46
+ const result = new Map<string, string>();
47
+ const issues = ParseResult.ArrayFormatter.formatErrorSync(error);
48
48
 
49
49
  for (const issue of issues) {
50
- const fieldPath = schemaPathToFieldPath(issue.path)
50
+ const fieldPath = schemaPathToFieldPath(issue.path);
51
51
  if (fieldPath && !result.has(fieldPath)) {
52
- result.set(fieldPath, issue.message)
52
+ result.set(fieldPath, issue.message);
53
53
  }
54
54
  }
55
55
 
56
- return result
57
- }
56
+ return result;
57
+ };
58
58
 
59
59
  const determineErrorSources = (error: ParseResult.ParseError): Map<string, ErrorSource> => {
60
- const sources = new Map<string, ErrorSource>()
60
+ const sources = new Map<string, ErrorSource>();
61
61
 
62
62
  const walk = (issue: ParseResult.ParseIssue, path: ReadonlyArray<PropertyKey>, source: ErrorSource): void => {
63
63
  switch (issue._tag) {
64
64
  case "Refinement":
65
65
  if (issue.kind === "Predicate" && isCompositeType(issue.ast.from) && path.length === 0) {
66
- walk(issue.issue, path, "refinement")
66
+ walk(issue.issue, path, "refinement");
67
67
  } else {
68
- walk(issue.issue, path, source)
68
+ walk(issue.issue, path, source);
69
69
  }
70
- break
70
+ break;
71
71
  case "Pointer": {
72
- const pointerPath = Array.isArray(issue.path) ? issue.path : [issue.path]
73
- walk(issue.issue, [...path, ...pointerPath], source)
74
- break
72
+ const pointerPath = Array.isArray(issue.path) ? issue.path : [issue.path];
73
+ walk(issue.issue, [...path, ...pointerPath], source);
74
+ break;
75
75
  }
76
76
  case "Composite": {
77
- const issues = Array.isArray(issue.issues) ? issue.issues : [issue.issues]
77
+ const issues = Array.isArray(issue.issues) ? issue.issues : [issue.issues];
78
78
  for (const sub of issues) {
79
- walk(sub, path, source)
79
+ walk(sub, path, source);
80
80
  }
81
- break
81
+ break;
82
82
  }
83
83
  case "Type":
84
84
  case "Missing":
85
85
  case "Unexpected":
86
86
  case "Forbidden": {
87
- const fieldPath = schemaPathToFieldPath(path)
88
- const key = fieldPath ?? ""
87
+ const fieldPath = schemaPathToFieldPath(path);
88
+ const key = fieldPath ?? "";
89
89
  if (!sources.has(key)) {
90
- sources.set(key, source)
90
+ sources.set(key, source);
91
91
  }
92
- break
92
+ break;
93
93
  }
94
94
  case "Transformation":
95
95
  if (
@@ -98,31 +98,31 @@ const determineErrorSources = (error: ParseResult.ParseError): Map<string, Error
98
98
  isCompositeType(issue.ast.from) &&
99
99
  path.length === 0
100
100
  ) {
101
- walk(issue.issue, path, "refinement")
101
+ walk(issue.issue, path, "refinement");
102
102
  } else {
103
- walk(issue.issue, path, source)
103
+ walk(issue.issue, path, source);
104
104
  }
105
- break
105
+ break;
106
106
  }
107
- }
107
+ };
108
108
 
109
- walk(error.issue, [], "field")
110
- return sources
111
- }
109
+ walk(error.issue, [], "field");
110
+ return sources;
111
+ };
112
112
 
113
113
  export const routeErrorsWithSource = (error: ParseResult.ParseError): Map<string, ErrorEntry> => {
114
- const result = new Map<string, ErrorEntry>()
115
- const formattedIssues = ParseResult.ArrayFormatter.formatErrorSync(error)
116
- const sources = determineErrorSources(error)
114
+ const result = new Map<string, ErrorEntry>();
115
+ const formattedIssues = ParseResult.ArrayFormatter.formatErrorSync(error);
116
+ const sources = determineErrorSources(error);
117
117
 
118
118
  for (const issue of formattedIssues) {
119
- const fieldPath = schemaPathToFieldPath(issue.path)
120
- const key = fieldPath ?? ""
119
+ const fieldPath = schemaPathToFieldPath(issue.path);
120
+ const key = fieldPath ?? "";
121
121
  if (!result.has(key)) {
122
- const source = sources.get(key) ?? "field"
123
- result.set(key, { message: issue.message, source })
122
+ const source = sources.get(key) ?? "field";
123
+ result.set(key, { message: issue.message, source });
124
124
  }
125
125
  }
126
126
 
127
- return result
128
- }
127
+ return result;
128
+ };
@@ -1,6 +1,6 @@
1
- import * as Equal from "effect/Equal"
2
- import * as Utils from "effect/Utils"
3
- import { getNestedValue, isPathUnderRoot } from "../Path.js"
1
+ import * as Equal from "effect/Equal";
2
+ import * as Utils from "effect/Utils";
3
+ import { getNestedValue, isPathUnderRoot } from "../Path.js";
4
4
 
5
5
  export const recalculateDirtyFieldsForArray = (
6
6
  dirtyFields: ReadonlySet<string>,
@@ -8,38 +8,38 @@ export const recalculateDirtyFieldsForArray = (
8
8
  arrayPath: string,
9
9
  newItems: ReadonlyArray<unknown>,
10
10
  ): ReadonlySet<string> => {
11
- const initialItems = (getNestedValue(initialValues, arrayPath) ?? []) as ReadonlyArray<unknown>
11
+ const initialItems = (getNestedValue(initialValues, arrayPath) ?? []) as ReadonlyArray<unknown>;
12
12
 
13
13
  if (newItems === initialItems) {
14
- return dirtyFields
14
+ return dirtyFields;
15
15
  }
16
16
 
17
17
  const nextDirty = new Set(
18
18
  Array.from(dirtyFields).filter((path) => !isPathUnderRoot(path, arrayPath)),
19
- )
19
+ );
20
20
 
21
- const loopLength = Math.max(newItems.length, initialItems.length)
21
+ const loopLength = Math.max(newItems.length, initialItems.length);
22
22
  for (let i = 0; i < loopLength; i++) {
23
- const itemPath = `${arrayPath}[${i}]`
24
- const newItem = newItems[i]
25
- const initialItem = initialItems[i]
23
+ const itemPath = `${arrayPath}[${i}]`;
24
+ const newItem = newItems[i];
25
+ const initialItem = initialItems[i];
26
26
 
27
- if (newItem === initialItem) continue
27
+ if (newItem === initialItem) continue;
28
28
 
29
- const isEqual = Utils.structuralRegion(() => Equal.equals(newItem, initialItem))
29
+ const isEqual = Utils.structuralRegion(() => Equal.equals(newItem, initialItem));
30
30
  if (!isEqual) {
31
- nextDirty.add(itemPath)
31
+ nextDirty.add(itemPath);
32
32
  }
33
33
  }
34
34
 
35
35
  if (newItems.length !== initialItems.length) {
36
- nextDirty.add(arrayPath)
36
+ nextDirty.add(arrayPath);
37
37
  } else {
38
- nextDirty.delete(arrayPath)
38
+ nextDirty.delete(arrayPath);
39
39
  }
40
40
 
41
- return nextDirty
42
- }
41
+ return nextDirty;
42
+ };
43
43
 
44
44
  export const recalculateDirtySubtree = (
45
45
  currentDirty: ReadonlySet<string>,
@@ -47,61 +47,61 @@ export const recalculateDirtySubtree = (
47
47
  allValues: unknown,
48
48
  rootPath: string = "",
49
49
  ): ReadonlySet<string> => {
50
- const targetValue = rootPath ? getNestedValue(allValues, rootPath) : allValues
51
- const targetInitial = rootPath ? getNestedValue(allInitial, rootPath) : allInitial
50
+ const targetValue = rootPath ? getNestedValue(allValues, rootPath) : allValues;
51
+ const targetInitial = rootPath ? getNestedValue(allInitial, rootPath) : allInitial;
52
52
 
53
53
  if (targetValue === targetInitial) {
54
54
  if (rootPath === "") {
55
- return new Set()
55
+ return new Set();
56
56
  }
57
57
 
58
- let changed = false
59
- const nextDirty = new Set(currentDirty)
58
+ let changed = false;
59
+ const nextDirty = new Set(currentDirty);
60
60
  for (const path of currentDirty) {
61
61
  if (isPathUnderRoot(path, rootPath)) {
62
- nextDirty.delete(path)
63
- changed = true
62
+ nextDirty.delete(path);
63
+ changed = true;
64
64
  }
65
65
  }
66
- return changed ? nextDirty : currentDirty
66
+ return changed ? nextDirty : currentDirty;
67
67
  }
68
68
 
69
- const nextDirty = new Set(currentDirty)
69
+ const nextDirty = new Set(currentDirty);
70
70
 
71
71
  if (rootPath === "") {
72
- nextDirty.clear()
72
+ nextDirty.clear();
73
73
  } else {
74
74
  for (const path of nextDirty) {
75
75
  if (isPathUnderRoot(path, rootPath)) {
76
- nextDirty.delete(path)
76
+ nextDirty.delete(path);
77
77
  }
78
78
  }
79
79
  }
80
80
 
81
81
  const recurse = (current: unknown, initial: unknown, path: string): void => {
82
- if (current === initial) return
82
+ if (current === initial) return;
83
83
 
84
84
  if (Array.isArray(current)) {
85
- const initialArr = (initial ?? []) as ReadonlyArray<unknown>
85
+ const initialArr = (initial ?? []) as ReadonlyArray<unknown>;
86
86
  for (let i = 0; i < Math.max(current.length, initialArr.length); i++) {
87
- recurse(current[i], initialArr[i], path ? `${path}[${i}]` : `[${i}]`)
87
+ recurse(current[i], initialArr[i], path ? `${path}[${i}]` : `[${i}]`);
88
88
  }
89
89
  } else if (current !== null && typeof current === "object") {
90
- const initialObj = (initial ?? {}) as Record<string, unknown>
90
+ const initialObj = (initial ?? {}) as Record<string, unknown>;
91
91
  for (const key in current as object) {
92
- recurse((current as Record<string, unknown>)[key], initialObj[key], path ? `${path}.${key}` : key)
92
+ recurse((current as Record<string, unknown>)[key], initialObj[key], path ? `${path}.${key}` : key);
93
93
  }
94
94
  for (const key in initialObj) {
95
95
  if (!(key in (current as object))) {
96
- recurse(undefined, initialObj[key], path ? `${path}.${key}` : key)
96
+ recurse(undefined, initialObj[key], path ? `${path}.${key}` : key);
97
97
  }
98
98
  }
99
99
  } else {
100
- const isEqual = Utils.structuralRegion(() => Equal.equals(current, initial))
101
- if (!isEqual && path) nextDirty.add(path)
100
+ const isEqual = Utils.structuralRegion(() => Equal.equals(current, initial));
101
+ if (!isEqual && path) nextDirty.add(path);
102
102
  }
103
- }
103
+ };
104
104
 
105
- recurse(targetValue, targetInitial, rootPath)
106
- return nextDirty
107
- }
105
+ recurse(targetValue, targetInitial, rootPath);
106
+ return nextDirty;
107
+ };
@@ -1,43 +1,43 @@
1
1
  export interface WeakRegistry<V extends object> {
2
- readonly get: (key: string) => V | undefined
3
- readonly set: (key: string, value: V) => void
4
- readonly delete: (key: string) => boolean
5
- readonly clear: () => void
6
- readonly values: () => IterableIterator<V>
2
+ readonly get: (key: string) => V | undefined;
3
+ readonly set: (key: string, value: V) => void;
4
+ readonly delete: (key: string) => boolean;
5
+ readonly clear: () => void;
6
+ readonly values: () => IterableIterator<V>;
7
7
  }
8
8
 
9
9
  export const createWeakRegistry = <V extends object>(): WeakRegistry<V> => {
10
10
  if (typeof WeakRef === "undefined" || typeof FinalizationRegistry === "undefined") {
11
- const map = new Map<string, V>()
11
+ const map = new Map<string, V>();
12
12
  return {
13
13
  get: (key) => map.get(key),
14
14
  set: (key, value) => {
15
- map.set(key, value)
15
+ map.set(key, value);
16
16
  },
17
17
  delete: (key) => map.delete(key),
18
18
  clear: () => map.clear(),
19
19
  values: () => map.values(),
20
- }
20
+ };
21
21
  }
22
22
 
23
- const map = new Map<string, WeakRef<V>>()
23
+ const map = new Map<string, WeakRef<V>>();
24
24
  const registry = new FinalizationRegistry<string>((key) => {
25
- map.delete(key)
26
- })
25
+ map.delete(key);
26
+ });
27
27
 
28
28
  return {
29
29
  get: (key) => map.get(key)?.deref(),
30
30
  set: (key, value) => {
31
- map.set(key, new WeakRef(value))
32
- registry.register(value, key)
31
+ map.set(key, new WeakRef(value));
32
+ registry.register(value, key);
33
33
  },
34
34
  delete: (key) => map.delete(key),
35
35
  clear: () => map.clear(),
36
36
  *values() {
37
37
  for (const ref of map.values()) {
38
- const value = ref.deref()
39
- if (value !== undefined) yield value
38
+ const value = ref.deref();
39
+ if (value !== undefined) yield value;
40
40
  }
41
41
  },
42
- }
43
- }
42
+ };
43
+ };