Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tangy-lights-tickle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect-app/vue-components": patch
---

Adds support to nested constructors in OmegaForm defaultsValue with schema
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@ describe("OmegaForm withDefaultConstructor with persistency", () => {
third: S.NullOr(S.String).withDefault,
fourth: S
.Struct({
addForm: S.NullOr(S.String),
b: S.PositiveNumber
})
.pipe(S.withDefaultConstructor(() => ({
addForm: null,
b: S.PositiveNumber(100)
}))),
addForm: S.NullOr(S.String).withDefault,
b: S.PositiveNumber.pipe(S.withDefaultConstructor(() => S.PositiveNumber(100))),
c: S.Struct({
d: S.Number.pipe(S.withDefaultConstructor(() => 10))
})
}),
fifth: S.Email,
sixth: S.NumberFromString.pipe(S.withDefaultConstructor(() => 1000))
})
Expand All @@ -33,7 +32,7 @@ describe("OmegaForm withDefaultConstructor with persistency", () => {
// Format: pathname-key1-key2-key3...
// Keys from meta will be flattened with dot notation for nested fields
const pathname = "/test"
const keys = ["first", "second", "third", "fourth.addForm", "fourth.b", "fifth", "sixth"]
const keys = ["first", "second", "third", "fourth.addForm", "fourth.b", "fourth.c.d", "fifth", "sixth"]
const persistencyKey = `${pathname}-${keys.join("-")}`
const queryValue = JSON.stringify({ first: 1234 })

Expand Down Expand Up @@ -95,7 +94,10 @@ describe("OmegaForm withDefaultConstructor with persistency", () => {
third: null, // Default from NullOr withDefault
fourth: {
addForm: null,
b: 100 // Default from withDefaultConstructor
b: 100, // Default from withDefaultConstructor
c: {
d: 10 // Default from withDefaultConstructor
}
},
sixth: "1000"
})
Expand Down
55 changes: 52 additions & 3 deletions packages/vue-components/src/components/OmegaForm/useOmegaForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -706,17 +706,66 @@ export const useOmegaForm = <
return normalized
}

// Helper function to recursively extract default values from schema AST
const extractDefaultsFromAST = (schemaObj: any): any => {
const result: Record<string, any> = {}

// Check if this schema has fields (struct)
if (schemaObj?.fields && typeof schemaObj.fields === "object") {
for (const [key, fieldSchema] of Object.entries(schemaObj.fields)) {
// Check if this field has a default value in its AST
if ((fieldSchema as any)?.ast?.defaultValue) {
try {
const defaultValue = (fieldSchema as any).ast.defaultValue()
if (defaultValue !== undefined) {
result[key] = defaultValue
}
} catch {
// Silently ignore if defaultValue() throws
}
}

// Recursively check nested fields for structs
const nestedDefaults = extractDefaultsFromAST(fieldSchema as any)
if (Object.keys(nestedDefaults).length > 0) {
// If we already have a default value for this key, merge with nested
if (result[key] && typeof result[key] === "object") {
Object.assign(result[key], nestedDefaults)
} else if (!result[key]) {
// Only set nested defaults if we don't have a default value
result[key] = nestedDefaults
}
}
}
}

return result
}

// Extract default values from schema constructors (e.g., withDefaultConstructor)
const extractSchemaDefaults = (defaultValues: Partial<From> = {}) => {
try {
// First try to use schema.make() if available
// Note: Partial schemas don't have .make() method yet (https://github.com/Effect-TS/effect/issues/4222)
if ("make" in schema && typeof (schema as any).make === "function") {
const decoded = (schema as any).make(defaultValues, { disableValidation: true })
const decoded = (schema as any).make(defaultValues)
return S.encodeSync(schema.pipe(S.partial))(decoded)
}
} catch (error) {
console.warn("Could not extract schema constructor defaults:", error)
return {}
// If make() fails, try to extract defaults from AST
if (window.location.hostname === "localhost") {
console.warn("schema.make() failed, extracting defaults from AST:", error)
}
try {
const astDefaults = extractDefaultsFromAST(schema)

return S.encodeSync(schema.pipe(S.partial))(astDefaults)
} catch (astError) {
if (window.location.hostname === "localhost") {
console.warn("Could not extract defaults from AST:", astError)
}
return {}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@ const AddSchema = S.Struct({
third: S.NullOr(S.String).withDefault,
fourth: S
.Struct({
addForm: S.NullOr(S.String),
b: S.PositiveNumber
})
.pipe(S.withDefaultConstructor(() => ({
addForm: null,
b: S.PositiveNumber(100)
}))),
addForm: S.NullOr(S.String).withDefault,
b: S.PositiveNumber.pipe(S.withDefaultConstructor(() => S.PositiveNumber(100))),
c: S.Struct({
d: S.Number.pipe(S.withDefaultConstructor(() => 10))
})
}),
fifth: S.Email,
sixth: S.NumberFromString.pipe(S.withDefaultConstructor(() => 1000))
})
Expand Down