@effect-app/vue-components 2.9.2 → 2.10.2

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.
Files changed (52) hide show
  1. package/dist/types/components/OmegaForm/OmegaFormStuff.d.ts +2 -0
  2. package/dist/types/components/OmegaForm/useOmegaForm.d.ts +13 -2
  3. package/dist/vue-components.es.js +17 -15
  4. package/dist/vue-components.es10.js +120 -115
  5. package/dist/vue-components.es12.js +282 -204
  6. package/dist/vue-components.es16.js +4 -10
  7. package/dist/vue-components.es17.js +10 -97
  8. package/dist/vue-components.es2.js +29 -27
  9. package/dist/vue-components.es20.js +2 -2
  10. package/dist/vue-components.es21.js +2 -2
  11. package/dist/vue-components.es22.js +1 -1
  12. package/dist/vue-components.es23.js +1 -1
  13. package/dist/vue-components.es31.js +2 -4
  14. package/dist/vue-components.es32.js +1 -1
  15. package/dist/vue-components.es33.js +111 -2
  16. package/dist/vue-components.es35.js +9 -0
  17. package/dist/vue-components.es36.js +32 -7
  18. package/dist/vue-components.es40.js +6 -0
  19. package/dist/vue-components.es41.js +23 -4
  20. package/dist/vue-components.es42.js +5 -23
  21. package/dist/vue-components.es43.js +21 -5
  22. package/dist/vue-components.es44.js +25 -16
  23. package/dist/vue-components.es45.js +15 -23
  24. package/dist/vue-components.es46.js +7 -17
  25. package/dist/vue-components.es47.js +5 -12
  26. package/dist/vue-components.es48.js +19 -5
  27. package/dist/vue-components.es49.js +9 -19
  28. package/dist/vue-components.es50.js +31 -9
  29. package/dist/vue-components.es51.js +42 -25
  30. package/dist/vue-components.es52.js +16 -38
  31. package/dist/vue-components.es53.js +11 -26
  32. package/dist/vue-components.es54.js +65 -11
  33. package/dist/vue-components.es55.js +45 -54
  34. package/dist/vue-components.es56.js +15 -54
  35. package/dist/vue-components.es57.js +31 -15
  36. package/dist/vue-components.es58.js +26 -30
  37. package/dist/vue-components.es59.js +42 -29
  38. package/dist/vue-components.es60.js +2 -42
  39. package/dist/vue-components.es61.js +44 -2
  40. package/dist/vue-components.es62.js +2 -44
  41. package/dist/vue-components.es7.js +1 -1
  42. package/dist/vue-components.es9.js +10 -33
  43. package/package.json +3 -3
  44. package/src/components/OmegaForm/OmegaFormStuff.ts +174 -11
  45. package/src/components/OmegaForm/OmegaTaggedUnionInternal.vue +0 -48
  46. package/src/components/OmegaForm/useOmegaForm.ts +49 -25
  47. package/dist/types/components/OmegaForm/defaultAST.d.ts +0 -4
  48. package/dist/vue-components.es34.js +0 -113
  49. package/dist/vue-components.es37.js +0 -34
  50. package/dist/vue-components.es63.js +0 -4
  51. package/src/components/OmegaForm/defaultAST.ts +0 -191
  52. /package/dist/{vue-components.es39.js → vue-components.es38.js} +0 -0
@@ -12,10 +12,7 @@
12
12
  generic="From extends Record<PropertyKey, any>, To extends Record<PropertyKey, any>, Name extends DeepKeys<From>"
13
13
  >
14
14
  import { type DeepKeys, type DeepValue } from "@tanstack/vue-form"
15
- import { S } from "effect-app"
16
15
  import { watch } from "vue"
17
- import { getTransformationFrom } from "../../utils"
18
- import { extractSchemaDefaults } from "./defaultAST"
19
16
  import { type OmegaFieldInternalApi } from "./InputProps"
20
17
  import { type useOmegaForm } from "./useOmegaForm"
21
18
 
@@ -35,51 +32,6 @@ watch(() => props.state, (newTag, oldTag) => {
35
32
  }
36
33
 
37
34
  if (newTag !== oldTag) {
38
- // get default values from AST for the new tag (only for root level tagged unions)
39
- if (props.name === void 0 && S.AST.isUnion(props.form._schema.ast)) {
40
- const indexOfSelectedMember = props
41
- .form
42
- ._schema
43
- .ast
44
- .types
45
- .map((t, i) => ({ original: i, unwrapped: getTransformationFrom(t) }))
46
- .flatMap((x) =>
47
- S.AST.isTypeLiteral(x.unwrapped) || S.AST.isTransformation(x.unwrapped)
48
- ? x
49
- .unwrapped
50
- .propertySignatures
51
- .filter((ps) => S.AST.isLiteral(ps.type) && ps.type.literal === newTag)
52
- .length > 0
53
- ? [x.original]
54
- : []
55
- : []
56
- )[0]
57
-
58
- // even if the type doesn't say so, indexOfSelectedMember may be undefined
59
- if (
60
- indexOfSelectedMember != void 0
61
- && "members" in props.form._schema
62
- && Array.isArray(props.form._schema.members)
63
- ) {
64
- const defaultsOfSelectedMember = Object.assign(
65
- extractSchemaDefaults(
66
- props
67
- .form
68
- ._schema
69
- .members[indexOfSelectedMember],
70
- values.value
71
- ),
72
- { _tag: newTag }
73
- )
74
-
75
- props.form.reset(defaultsOfSelectedMember)
76
- setTimeout(() => {
77
- props.field.validate("change")
78
- }, 0)
79
- return
80
- }
81
- }
82
-
83
35
  props.form.reset(values.value)
84
36
  setTimeout(() => {
85
37
  props.field.validate("change")
@@ -4,14 +4,13 @@
4
4
  import * as api from "@opentelemetry/api"
5
5
  import { type DeepKeys, DeepValue, type FormAsyncValidateOrFn, type FormValidateOrFn, type StandardSchemaV1, StandardSchemaV1Issue, useForm, ValidationError, ValidationErrorMap } from "@tanstack/vue-form"
6
6
  import { Array, Data, Effect, Fiber, Option, Order, S } from "effect-app"
7
- import { runtimeFiberAsPromise } from "effect-app/utils"
7
+ import { runtimeFiberAsPromise, UnionToTuples } from "effect-app/utils"
8
8
  import { Component, computed, ComputedRef, ConcreteComponent, h, type InjectionKey, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from "vue"
9
- import { deepMerge, extractSchemaDefaults } from "./defaultAST"
10
9
  import { MergedInputProps } from "./InputProps"
11
10
  import OmegaArray from "./OmegaArray.vue"
12
11
  import OmegaAutoGen from "./OmegaAutoGen.vue"
13
12
  import OmegaErrorsInternal from "./OmegaErrorsInternal.vue"
14
- import { BaseProps, DefaultTypeProps, FieldPath, type FormProps, generateMetaFromSchema, type MetaRecord, type NestedKeyOf, OmegaArrayProps, OmegaAutoGenMeta, OmegaError, type OmegaFormApi, OmegaFormState } from "./OmegaFormStuff"
13
+ import { BaseProps, deepMerge, defaultsValueFromSchema, DefaultTypeProps, FieldPath, type FormProps, generateMetaFromSchema, type MetaRecord, type NestedKeyOf, OmegaArrayProps, OmegaAutoGenMeta, OmegaError, type OmegaFormApi, OmegaFormState } from "./OmegaFormStuff"
15
14
  import OmegaInput from "./OmegaInput.vue"
16
15
  import OmegaTaggedUnion from "./OmegaTaggedUnion.vue"
17
16
  import OmegaForm from "./OmegaWrapper.vue"
@@ -148,6 +147,13 @@ const eHoc = (errorProps: {
148
147
  }
149
148
  }
150
149
 
150
+ export type Policies = "local" | "session" | "querystring"
151
+ export type defaultValuesPriorityUnion = "tanstack" | "persistency" | "schema"
152
+
153
+ const includesPolicy = (arr: Policies[], policy: Policies) => {
154
+ return arr.includes(policy)
155
+ }
156
+
151
157
  export type OmegaConfig<T> = {
152
158
  i18nNamespace?: string
153
159
 
@@ -156,8 +162,8 @@ export type OmegaConfig<T> = {
156
162
  * - "querystring": Highest priority when persisting
157
163
  * - "local" and then "session": Lower priority storage options
158
164
  */
159
- policies?: ("local" | "session" | "querystring")[]
160
- overrideDefaultValues?: boolean
165
+ policies?: UnionToTuples<Policies>
166
+ overrideDefaultValues?: "deprecated: use defaultValuesPriority"
161
167
  id?: string
162
168
  } & keysRule<T>
163
169
 
@@ -174,6 +180,16 @@ export type OmegaConfig<T> = {
174
180
  preventWindowExit?: "prevent" | "prevent-and-reset" | "nope"
175
181
 
176
182
  input?: any
183
+
184
+ /**
185
+ * Default values order is: Tanstack default values passed as second parameter to useOmegaForm, then persistency
186
+ * default values from querystring or local/session storage, then defaults from schema
187
+ * You can customize the order and with omegaConfig.defaultValuesPriority
188
+ * default value = ['tanstack', 'persistency', 'schema']
189
+ */
190
+ defaultValuesPriority?: UnionToTuples<defaultValuesPriorityUnion>
191
+
192
+ defaultFromSchema?: "deprecated: use defaultValuesPriority"
177
193
  }
178
194
 
179
195
  export interface OF<From, To> extends OmegaFormApi<From, To> {
@@ -664,18 +680,18 @@ export const useOmegaForm = <
664
680
 
665
681
  const defaultValues = computed(() => {
666
682
  // will contain what we get from querystring or local/session storage
667
- let defValuesPatch
683
+ let persistencyDefaultValues
668
684
 
669
685
  const persistency = omegaConfig?.persistency
670
686
 
671
687
  if (
672
688
  // query string has higher priority than local/session storage
673
689
  persistency?.policies
674
- && !defValuesPatch
675
- && (persistency.policies.includes("local")
676
- || persistency.policies.includes("session"))
690
+ && !persistencyDefaultValues
691
+ && (includesPolicy(persistency.policies, "local")
692
+ || includesPolicy(persistency.policies, "session"))
677
693
  ) {
678
- const storage = persistency.policies.includes("local")
694
+ const storage = includesPolicy(persistency.policies, "local")
679
695
  ? localStorage
680
696
  : sessionStorage
681
697
  if (storage) {
@@ -684,19 +700,19 @@ export const useOmegaForm = <
684
700
  storage.getItem(persistencyKey.value) || "{}"
685
701
  )
686
702
  storage.removeItem(persistencyKey.value)
687
- defValuesPatch = value
703
+ persistencyDefaultValues = value
688
704
  } catch (error) {
689
705
  console.error(error)
690
706
  }
691
707
  }
692
708
  }
693
- if (persistency?.policies && persistency.policies.includes("querystring")) {
709
+ if (persistency?.policies && includesPolicy(persistency.policies, "querystring")) {
694
710
  try {
695
711
  const params = new URLSearchParams(window.location.search)
696
712
  const value = params.get(persistencyKey.value)
697
713
  clearUrlParams()
698
714
  if (value) {
699
- defValuesPatch = deepMerge(defValuesPatch || {}, JSON.parse(value))
715
+ persistencyDefaultValues = deepMerge(persistencyDefaultValues || {}, JSON.parse(value))
700
716
  }
701
717
  } catch (error) {
702
718
  console.error(error)
@@ -704,14 +720,22 @@ export const useOmegaForm = <
704
720
  }
705
721
 
706
722
  // to be sure we have a valid object at the end of the gathering process
707
- defValuesPatch ??= {}
708
-
709
- // we just return what we gathered from the query/storage
710
- return extractSchemaDefaults(
711
- schema,
712
- omegaConfig?.persistency?.overrideDefaultValues
713
- ? deepMerge(tanstackFormOptions?.defaultValues || {}, defValuesPatch)
714
- : deepMerge(defValuesPatch, tanstackFormOptions?.defaultValues || {})
723
+ persistencyDefaultValues ??= {}
724
+
725
+ const defaults: Record<defaultValuesPriorityUnion, any> = {
726
+ tanstack: tanstackFormOptions?.defaultValues || {},
727
+ persistency: persistencyDefaultValues,
728
+ schema: defaultsValueFromSchema(schema)
729
+ }
730
+
731
+ return (omegaConfig?.defaultValuesPriority || ["tanstack", "persistency", "schema"] as const).reverse().reduce(
732
+ (acc, m) => {
733
+ if (!Object.keys(acc).length) {
734
+ return defaults[m]
735
+ }
736
+ return deepMerge(acc, defaults[m])
737
+ },
738
+ {}
715
739
  )
716
740
  })
717
741
 
@@ -805,10 +829,10 @@ export const useOmegaForm = <
805
829
  return
806
830
  }
807
831
  if (
808
- persistency.policies.includes("local")
809
- || persistency.policies.includes("session")
832
+ includesPolicy(persistency.policies, "local")
833
+ || includesPolicy(persistency.policies, "session")
810
834
  ) {
811
- const storage = persistency.policies.includes("local")
835
+ const storage = includesPolicy(persistency.policies, "local")
812
836
  ? localStorage
813
837
  : sessionStorage
814
838
  if (!storage) return
@@ -822,7 +846,7 @@ export const useOmegaForm = <
822
846
  if (!persistency?.policies || persistency.policies.length === 0) {
823
847
  return
824
848
  }
825
- if (persistency.policies.includes("querystring")) {
849
+ if (includesPolicy(persistency.policies, "querystring")) {
826
850
  const values = persistFilter(persistency)
827
851
  const searchParams = new URLSearchParams(window.location.search)
828
852
  searchParams.set(persistencyKey.value, JSON.stringify(values))
@@ -1,4 +0,0 @@
1
- import { S } from "effect-app";
2
- export declare function deepMerge(target: any, source: any): any;
3
- export declare const extractDefaultsFromAST: (schemaObj: any) => any;
4
- export declare const extractSchemaDefaults: <From, To>(schema: S.Schema<To, From, never>, defaultValues?: Partial<From>) => any;
@@ -1,113 +0,0 @@
1
- import { defineComponent as _, getCurrentInstance as B, computed as V, createBlock as i, openBlock as e, Transition as x, withCtx as u, createElementBlock as o, createCommentVNode as p, renderSlot as g, normalizeProps as b, guardReactiveProps as I, resolveDynamicComponent as h, unref as d, normalizeClass as v, createElementVNode as n, toDisplayString as a, Fragment as w, renderList as y, createTextVNode as C } from "vue";
2
- import { useIntl as L } from "./vue-components.es3.js";
3
- const M = {
4
- key: 0,
5
- class: "error-alert"
6
- }, j = { class: "container" }, D = {
7
- key: 0,
8
- viewBox: "0 0 24 24",
9
- fill: "none",
10
- xmlns: "http://www.w3.org/2000/svg"
11
- }, N = { class: "text-h6" }, z = {
12
- key: 0,
13
- class: "error-list"
14
- }, G = ["for"], H = { key: 1 }, T = /* @__PURE__ */ _({
15
- __name: "OmegaErrorsInternal",
16
- props: {
17
- generalErrors: {},
18
- errors: {},
19
- hideErrorDetails: { type: Boolean }
20
- },
21
- setup(l) {
22
- const c = B()?.appContext.components.VAlert, k = l, { trans: E } = L(), m = V(() => k.generalErrors ? k.generalErrors.filter((s) => !!s).flatMap(
23
- (s) => Object.values(s).filter((t) => !!t).flatMap(
24
- (t) => t.filter(
25
- (r) => !!r?.message
26
- ).map((r) => r.message)
27
- )
28
- ) : []);
29
- return (s, t) => (e(), i(x, null, {
30
- default: u(() => [
31
- l.errors.length || m.value.length ? (e(), o("div", M, [
32
- g(s.$slots, "default", b(I({ errors: l.errors, showedGeneralErrors: m.value })), () => [
33
- (e(), i(h(d(c) ? "v-alert" : "div"), {
34
- class: v([d(c) ? "mb-4" : "error-alert-content", "mb-4"]),
35
- type: "error",
36
- variant: "tonal",
37
- role: "alert",
38
- "aria-live": "polite"
39
- }, {
40
- default: u(() => [
41
- n("div", j, [
42
- g(s.$slots, "icon", {}, () => [
43
- d(c) ? p("", !0) : (e(), o("svg", D, [...t[0] || (t[0] = [
44
- n("path", {
45
- d: "M16 2H8L2 8V16L8 22H16L22 16V8L16 2Z",
46
- stroke: "currentColor",
47
- "stroke-width": "2",
48
- "stroke-linecap": "round",
49
- "stroke-linejoin": "round"
50
- }, null, -1),
51
- n("path", {
52
- d: "M12 8V12",
53
- stroke: "currentColor",
54
- "stroke-width": "2",
55
- "stroke-linecap": "round",
56
- "stroke-linejoin": "round"
57
- }, null, -1),
58
- n("path", {
59
- d: "M12 16.0195V16",
60
- stroke: "currentColor",
61
- "stroke-width": "2",
62
- "stroke-linecap": "round",
63
- "stroke-linejoin": "round"
64
- }, null, -1)
65
- ])]))
66
- ], !0),
67
- n("div", null, [
68
- n("div", N, a(d(E)("form.includes_error")) + ": ", 1),
69
- l.errors.length ? (e(), o("ul", z, [
70
- (e(!0), o(w, null, y(l.errors, (r) => (e(), o("li", {
71
- key: r.inputId,
72
- class: "error-item"
73
- }, [
74
- n("div", null, [
75
- n("label", {
76
- for: r.inputId,
77
- class: "error-link"
78
- }, a(r.label), 9, G),
79
- t[1] || (t[1] = C(" " + a(" ") + " ", -1)),
80
- l.hideErrorDetails ? p("", !0) : (e(), o("div", {
81
- key: 0,
82
- class: v(["error-message", r.errors.length < 2 && "single-error"])
83
- }, [
84
- (e(), i(h(r.errors.length > 1 ? "ul" : "div"), { class: "error-list" }, {
85
- default: u(() => [
86
- (e(!0), o(w, null, y(r.errors, (f) => (e(), i(h(r.errors.length > 1 ? "li" : "span"), { key: f }, {
87
- default: u(() => [
88
- C(a(f), 1)
89
- ]),
90
- _: 2
91
- }, 1024))), 128))
92
- ]),
93
- _: 2
94
- }, 1024))
95
- ], 2))
96
- ])
97
- ]))), 128))
98
- ])) : (e(), o("span", H, a(m.value[0]), 1))
99
- ])
100
- ])
101
- ]),
102
- _: 3
103
- }, 8, ["class"]))
104
- ], !0)
105
- ])) : p("", !0)
106
- ]),
107
- _: 3
108
- }));
109
- }
110
- });
111
- export {
112
- T as default
113
- };
@@ -1,34 +0,0 @@
1
- import { defineComponent as m, createElementBlock as d, openBlock as u, withModifiers as f, createElementVNode as l, unref as s, renderSlot as a } from "vue";
2
- import { useStore as b } from "@tanstack/vue-form";
3
- import { usePreventClose as p } from "./vue-components.es11.js";
4
- import { getOmegaStore as c } from "./vue-components.es54.js";
5
- const S = ["disabled"], V = /* @__PURE__ */ m({
6
- __name: "OmegaWrapper",
7
- props: {
8
- form: {},
9
- disabled: { type: Boolean },
10
- subscribe: {}
11
- },
12
- setup(o) {
13
- const e = o, i = b(
14
- e.form.store,
15
- (t) => t.isSubmitting
16
- ), n = c(
17
- e.form,
18
- e.subscribe
19
- );
20
- return e.form.ignorePreventCloseEvents || p(() => e.form.useStore((t) => t.isDirty)), (t, r) => (u(), d("form", {
21
- novalidate: "",
22
- onSubmit: r[0] || (r[0] = f((v) => o.form.handleSubmit(), ["prevent", "stop"]))
23
- }, [
24
- l("fieldset", {
25
- disabled: s(i) || o.disabled
26
- }, [
27
- a(t.$slots, "default", { subscribedValues: s(n) }, void 0, !0)
28
- ], 8, S)
29
- ], 32));
30
- }
31
- });
32
- export {
33
- V as default
34
- };
@@ -1,4 +0,0 @@
1
- var o = typeof globalThis == "object" ? globalThis : typeof self == "object" ? self : typeof window == "object" ? window : typeof global == "object" ? global : {};
2
- export {
3
- o as _globalThis
4
- };
@@ -1,191 +0,0 @@
1
- import { isObject } from "@vueuse/core"
2
- import { S } from "effect-app"
3
- import { isNullableOrUndefined } from "./OmegaFormStuff"
4
-
5
- export function deepMerge(target: any, source: any) {
6
- for (const key in source) {
7
- if (Array.isArray(source[key])) {
8
- // Arrays should be copied directly, not deep merged
9
- target[key] = source[key]
10
- } else if (source[key] && isObject(source[key])) {
11
- if (!target[key]) {
12
- target[key] = {}
13
- }
14
- deepMerge(target[key], source[key])
15
- } else {
16
- target[key] = source[key]
17
- }
18
- }
19
- return target
20
- }
21
-
22
- /**
23
- * Recursively makes all properties in a schema optional, including nested objects.
24
- * Unlike S.partial which only makes top-level properties optional, this utility
25
- * traverses the schema tree and applies partial transformation at every level.
26
- *
27
- * Handles:
28
- * - TypeLiteral (structs): Makes all properties optional and recursively processes nested types
29
- * - Union types: Recursively applies partial to each union member
30
- * - Transformation types: Applies partial to both 'from' and 'to' sides
31
- */
32
- const partialRecursive = <A, I, R>(schema: S.Schema<A, I, R>): S.Schema<Partial<A>, Partial<I>, R> => {
33
- const ast = schema.ast
34
-
35
- // Handle Refinement types (e.g., NonEmptyArray, filters on ExtendedClass)
36
- if (ast._tag === "Refinement") {
37
- const refinementAst = ast as any
38
- // For refinements, bypass the filter and recursively apply partial to the underlying type
39
- const fromSchema = S.make(refinementAst.from)
40
- return partialRecursive(fromSchema as any)
41
- }
42
-
43
- // Handle Union types - recursively apply partial to each member
44
- if (ast._tag === "Union") {
45
- const partialMembers = (ast as any).types.map((memberAst: any) => {
46
- const memberSchema = S.make(memberAst)
47
- const partialMember = partialRecursive(memberSchema as any)
48
- return partialMember.ast
49
- })
50
-
51
- const newAst = {
52
- ...ast,
53
- types: partialMembers
54
- }
55
-
56
- return S.make(newAst as any)
57
- }
58
-
59
- // Handle Transformation types (e.g., withDefaultConstructor, ExtendedClass)
60
- if (ast._tag === "Transformation") {
61
- const transformAst = ast as any
62
-
63
- // Special handling for ExtendedClass (Declaration in 'to' side)
64
- if (transformAst.to._tag === "Declaration") {
65
- // For ExtendedClass, extract the TypeLiteral from the 'from' side
66
- // and make that partial, bypassing the Declaration entirely
67
- const fromSchema = S.make(transformAst.from)
68
- return partialRecursive(fromSchema as any)
69
- }
70
-
71
- // For other transformations, apply partial to both sides
72
- const fromSchema = S.make(transformAst.from)
73
- const toSchema = S.make(transformAst.to)
74
- const partialFrom = partialRecursive(fromSchema as any)
75
- const partialTo = partialRecursive(toSchema as any)
76
-
77
- const newAst = {
78
- ...ast,
79
- from: partialFrom.ast,
80
- to: partialTo.ast
81
- }
82
-
83
- return S.make(newAst as any)
84
- }
85
-
86
- // If this is a TypeLiteral (struct), recursively apply partial to nested fields
87
- if (ast._tag === "TypeLiteral") {
88
- const fields = ast.propertySignatures.map((prop: any) => {
89
- const propType = prop.type
90
- let newType = propType
91
-
92
- // Recursively handle nested complex types (structs, unions, transformations, refinements)
93
- if (
94
- propType._tag === "TypeLiteral" || propType._tag === "Union" || propType
95
- ._tag === "Transformation" || propType
96
- ._tag === "Refinement"
97
- ) {
98
- const nestedSchema = S.make(propType)
99
- const recursivePartial = partialRecursive(nestedSchema as any)
100
- newType = recursivePartial.ast
101
- }
102
-
103
- // Create a new property signature with isOptional: true
104
- return {
105
- ...prop,
106
- type: newType,
107
- isOptional: true
108
- }
109
- })
110
-
111
- const newAst = {
112
- ...ast,
113
- propertySignatures: fields
114
- }
115
-
116
- return S.make(newAst as any)
117
- }
118
-
119
- // For other schema types (primitives, refinements, etc.), return as-is
120
- // These types don't need to be made partial, and S.partial doesn't support them anyway
121
- return schema as any
122
- }
123
-
124
- // Helper function to recursively extract default values from schema AST swag ast
125
- export const extractDefaultsFromAST = (schemaObj: any): any => {
126
- const result: Record<string, any> = {}
127
-
128
- // Check if this schema has fields (struct)
129
- if (schemaObj?.fields && typeof schemaObj.fields === "object") {
130
- for (const [key, fieldSchema] of Object.entries(schemaObj.fields)) {
131
- // Check if this field has a default value in its AST
132
- if ((fieldSchema as any)?.ast?.defaultValue) {
133
- try {
134
- const defaultValue = (fieldSchema as any).ast.defaultValue()
135
- result[key] = defaultValue
136
- } catch {
137
- // Silently ignore if defaultValue() throws
138
- }
139
- } else {
140
- // TODO Should we put to null/undefined only leaves?
141
- const ast = (fieldSchema as any)?.ast
142
- const nullableOrUndefined = isNullableOrUndefined(ast)
143
- switch (nullableOrUndefined) {
144
- case "null":
145
- result[key] = null
146
- break
147
- case "undefined":
148
- result[key] = undefined
149
- break
150
- }
151
- }
152
-
153
- // Recursively check nested fields for structs and unions
154
- const nestedDefaults = extractDefaultsFromAST(fieldSchema as any)
155
- if (Object.keys(nestedDefaults).length > 0) {
156
- // If we already have a default value for this key, merge with nested
157
- if (result[key] && typeof result[key] === "object") {
158
- Object.assign(result[key], nestedDefaults)
159
- } else if (!result[key]) {
160
- // Only set nested defaults if we don't have a default value
161
- result[key] = nestedDefaults
162
- }
163
- }
164
- }
165
- } else {
166
- if (schemaObj?.from?.fields && typeof schemaObj?.from?.fields === "object") {
167
- return extractDefaultsFromAST(schemaObj.from)
168
- }
169
- }
170
-
171
- return result
172
- }
173
-
174
- // Extract default values from schema constructors (e.g., withDefaultConstructor) swag schema defaults
175
- export const extractSchemaDefaults = <From, To>(
176
- schema: S.Schema<To, From, never>,
177
- defaultValues: Partial<From> = {}
178
- ) => {
179
- let result: Partial<From> = {}
180
-
181
- try {
182
- const astDefaults = extractDefaultsFromAST(schema)
183
- result = S.encodeSync(partialRecursive(schema))(astDefaults)
184
- } catch (astError) {
185
- if (window.location.hostname === "localhost") {
186
- console.warn("Could not extract defaults from AST:", astError)
187
- }
188
- }
189
-
190
- return deepMerge(result, defaultValues)
191
- }