Compare commits

...

5 Commits

Author SHA1 Message Date
8aa15873b0 version: 1.0.166 2026-07-03 19:24:16 -04:00
0d282cc930 filter synthesis: compile named non-table value types structurally
A property typed as a named value type (a schema-only config object like
an operating-hours schedule) previously got a dangling {type}.filter
reference — no filter is ever synthesized for a non-table-backed schema,
so the whole parent filter failed downstream (PROXY_TYPE_RESOLUTION_FAILED;
the punc generator emitted an empty filter type).

Naming a value type is a reuse choice, not a semantics choice: it now
compiles structurally into the parent filter, exactly like an inline
object, recursively (including array items). Table-backed boundaries keep
the lazy {type}.filter reference. A named type with no compilable
structure is omitted instead of dangling.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 19:23:54 -04:00
581fc8e0c0 flow update 2026-07-03 01:33:00 -04:00
6f0bff8dc7 version: 1.0.165 2026-07-02 23:43:33 -04:00
99c69e27ab minor dict improvements and flow update 2026-07-02 23:43:23 -04:00
4 changed files with 119 additions and 5 deletions

View File

@ -87,6 +87,34 @@
"type": "string"
}
}
},
"schedule": {
"type": [
"opening_hours",
"null"
]
}
}
},
"season": {
"type": "object",
"properties": {
"label": {
"type": "string"
}
}
},
"opening_hours": {
"type": "object",
"properties": {
"open": {
"type": "string"
},
"seasons": {
"type": "array",
"items": {
"type": "season"
}
}
}
}
@ -262,6 +290,7 @@
"uuid_field",
"tags",
"ad_hoc",
"schedule",
"$and",
"$or"
],
@ -277,6 +306,7 @@
"uuid_field",
"tags",
"ad_hoc",
"schedule",
"$and",
"$or"
],
@ -298,6 +328,7 @@
"uuid_field",
"tags",
"ad_hoc",
"schedule",
"$and",
"$or"
],
@ -366,6 +397,41 @@
"string.condition",
"null"
]
},
"schedule": {
"type": [
"filter",
"null"
],
"compiledPropertyNames": [
"open",
"seasons"
],
"properties": {
"open": {
"type": [
"string.condition",
"null"
]
},
"seasons": {
"type": [
"filter",
"null"
],
"compiledPropertyNames": [
"label"
],
"properties": {
"label": {
"type": [
"string.condition",
"null"
]
}
}
}
}
}
},
"type": "filter"
@ -483,10 +549,12 @@
]
}
}
}
},
"opening_hours": {},
"season": {}
}
}
}
]
}
]
]

View File

@ -26,10 +26,14 @@ impl Schema {
if let Some(items) = &child.obj.items {
if !items.is_proxy() {
structural_filter = items.compile_filter(_db, "", _errors);
} else if let Some(target) = Self::value_type_target(items, _db) {
structural_filter = target.compile_filter(_db, "", _errors);
}
}
} else if !child.is_proxy() {
structural_filter = child.compile_filter(_db, "", _errors);
} else if let Some(target) = Self::value_type_target(child, _db) {
structural_filter = target.compile_filter(_db, "", _errors);
}
if let Some(mut inline_schema) = structural_filter {
@ -117,6 +121,37 @@ impl Schema {
None
}
/// Resolves a pure type-pointer schema to a named non-table value type's own schema —
/// a reusable, schema-only object (e.g. an operating-hours config). Naming a value type
/// is a reuse choice, not a semantics choice: it filters structurally, exactly like an
/// inline object. Table-backed boundaries keep the lazy {type}.filter reference instead.
fn value_type_target<'a>(schema: &Arc<Schema>, db: &'a Database) -> Option<&'a Arc<Schema>> {
let t = match &schema.obj.type_ {
Some(SchemaTypeOrArray::Single(t)) => Some(t.as_str()),
Some(SchemaTypeOrArray::Multiple(types)) => {
types.iter().find(|t| *t != "null").map(|s| s.as_str())
}
None => None,
}?;
if matches!(
t,
"string" | "integer" | "number" | "boolean" | "object" | "array" | "null"
) {
return None;
}
if t.ends_with(".condition") || t.ends_with(".filter") {
return None;
}
if db.enums.contains_key(t) {
return None;
}
let base = t.split('.').next_back().unwrap_or(t);
if db.types.contains_key(base) {
return None;
}
db.schemas.get(t)
}
fn resolve_filter_type(schema: &Arc<Schema>, db: &Database) -> Option<Vec<String>> {
if let Some(type_) = &schema.obj.type_ {
match type_ {
@ -151,6 +186,7 @@ impl Schema {
"number" => Some(vec!["number.condition".to_string()]),
"boolean" => Some(vec!["boolean.condition".to_string()]),
"object" => None, // Inline structures are ignored in Composed References
"dict" => None, // Dynamic dictionary maps are ignored in Composed References
"array" => {
if let Some(items) = &schema.obj.items {
return Self::resolve_filter_type(items, db);
@ -164,8 +200,16 @@ impl Schema {
} else if db.enums.contains_key(custom) {
Some(vec![format!("{}.condition", custom)])
} else {
// Assume anything else is a Relational cross-boundary that already has its own .filter dynamically built
Some(vec![format!("{}.filter", custom)])
// A Relational cross-boundary gets a reference to the target's dynamically built
// .filter — but only a table-backed boundary has one. A named non-table value type
// compiles structurally upstream (value_type_target); reaching here means it had
// no compilable structure — omit it rather than emit a dangling .filter reference.
let base = custom.split('.').next_back().unwrap_or(custom);
if db.types.contains_key(base) {
Some(vec![format!("{}.filter", custom)])
} else {
None
}
}
}
}

View File

@ -125,6 +125,7 @@ impl Schema {
child.compile(db, root_id, format!("{}/{}", path, k), errors);
}
}
if let Some(items) = &self.obj.items {
items.compile(db, root_id, format!("{}/items", path), errors);
}
@ -138,6 +139,7 @@ impl Schema {
if let Some(child) = &self.obj.not {
child.compile(db, root_id, format!("{}/not", path), errors);
}
if let Some(child) = &self.obj.contains {
child.compile(db, root_id, format!("{}/contains", path), errors);
}

View File

@ -1 +1 @@
1.0.164
1.0.166