Skip to main content

fraiseql_cli/schema/intermediate/
mod.rs

1//! Intermediate Schema Format
2//!
3//! Language-agnostic schema representation that all language libraries output.
4//! See `docs/architecture/intermediate-schema.md` for full specification.
5
6pub mod advanced_types;
7pub mod analytics;
8pub mod fragments;
9pub mod operations;
10pub mod subscriptions;
11pub mod types;
12
13pub use advanced_types::{
14    IntermediateInputField, IntermediateInputObject, IntermediateInterface, IntermediateUnion,
15};
16pub use analytics::{
17    IntermediateAggregateQuery, IntermediateDimensionPath, IntermediateDimensions,
18    IntermediateFactTable, IntermediateFilter, IntermediateMeasure,
19};
20pub use fragments::{
21    IntermediateAppliedDirective, IntermediateDirective, IntermediateFragment,
22    IntermediateFragmentField, IntermediateFragmentFieldDef,
23};
24use fraiseql_core::schema::{
25    DebugConfig, McpConfig, NamingConvention, RestConfig, SessionVariablesConfig,
26    SubscriptionsConfig, ValidationConfig,
27};
28pub use operations::{
29    IntermediateArgument, IntermediateAutoParams, IntermediateMutation, IntermediateQuery,
30    IntermediateQueryDefaults,
31};
32use serde::{Deserialize, Serialize};
33pub use subscriptions::{
34    IntermediateFilterCondition, IntermediateObserver, IntermediateObserverAction,
35    IntermediateRetryConfig, IntermediateSubscription, IntermediateSubscriptionFilter,
36};
37pub use types::{
38    IntermediateDeprecation, IntermediateEnum, IntermediateEnumValue, IntermediateField,
39    IntermediateScalar, IntermediateType,
40};
41
42/// Intermediate schema - universal format from all language libraries
43#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
44pub struct IntermediateSchema {
45    /// Schema format version
46    #[serde(default = "default_version")]
47    pub version: String,
48
49    /// GraphQL object types
50    #[serde(default)]
51    pub types: Vec<IntermediateType>,
52
53    /// GraphQL enum types
54    #[serde(default)]
55    pub enums: Vec<IntermediateEnum>,
56
57    /// GraphQL input object types
58    #[serde(default)]
59    pub input_types: Vec<IntermediateInputObject>,
60
61    /// GraphQL interface types (per GraphQL spec §3.7)
62    #[serde(default)]
63    pub interfaces: Vec<IntermediateInterface>,
64
65    /// GraphQL union types (per GraphQL spec §3.10)
66    #[serde(default)]
67    pub unions: Vec<IntermediateUnion>,
68
69    /// GraphQL queries
70    #[serde(default)]
71    pub queries: Vec<IntermediateQuery>,
72
73    /// GraphQL mutations
74    #[serde(default)]
75    pub mutations: Vec<IntermediateMutation>,
76
77    /// GraphQL subscriptions
78    #[serde(default)]
79    pub subscriptions: Vec<IntermediateSubscription>,
80
81    /// GraphQL fragments (reusable field selections)
82    #[serde(default, skip_serializing_if = "Option::is_none")]
83    pub fragments: Option<Vec<IntermediateFragment>>,
84
85    /// GraphQL directive definitions (custom directives)
86    #[serde(default, skip_serializing_if = "Option::is_none")]
87    pub directives: Option<Vec<IntermediateDirective>>,
88
89    /// Analytics fact tables (optional)
90    #[serde(default, skip_serializing_if = "Option::is_none")]
91    pub fact_tables: Option<Vec<IntermediateFactTable>>,
92
93    /// Analytics aggregate queries (optional)
94    #[serde(default, skip_serializing_if = "Option::is_none")]
95    pub aggregate_queries: Option<Vec<IntermediateAggregateQuery>>,
96
97    /// Observer definitions (database change event listeners)
98    #[serde(default, skip_serializing_if = "Option::is_none")]
99    pub observers: Option<Vec<IntermediateObserver>>,
100
101    /// Scheduled ingress source definitions (#573) — the dual of `observers`.
102    /// Reuses the compiled [`SourceDefinition`](fraiseql_core::schema::SourceDefinition)
103    /// shape directly (it already carries serde defaults); the converter copies them
104    /// into the compiled schema.
105    #[serde(default, skip_serializing_if = "Option::is_none")]
106    pub sources: Option<Vec<fraiseql_core::schema::SourceDefinition>>,
107
108    /// Custom scalar type definitions
109    ///
110    /// Defines custom GraphQL scalar types with validation rules.
111    /// Custom scalars can be defined in Python, TypeScript, Java, Go, and Rust SDKs,
112    /// and are compiled into the CompiledSchema's CustomTypeRegistry.
113    #[serde(default, skip_serializing_if = "Option::is_none")]
114    pub custom_scalars: Option<Vec<IntermediateScalar>>,
115
116    /// Security configuration (from fraiseql.toml)
117    /// Compiled from the security section of fraiseql.toml at compile time.
118    /// Optional - if not provided, defaults are used.
119    #[serde(default, skip_serializing_if = "Option::is_none")]
120    pub security: Option<serde_json::Value>,
121
122    /// Observers/event system configuration (from fraiseql.toml).
123    ///
124    /// Contains backend connection settings (redis_url, nats_url, etc.) compiled
125    /// from the `[observers]` TOML section. Embedded verbatim into the compiled schema.
126    #[serde(default, skip_serializing_if = "Option::is_none")]
127    pub observers_config: Option<serde_json::Value>,
128
129    /// Federation configuration.
130    ///
131    /// Two authoring workflows feed this field:
132    /// - The TOML merger embeds the `[federation]` section under `federation_config`.
133    /// - Language SDKs (e.g. the Python SDK's `export_schema(federation=...)`) emit the block
134    ///   under the top-level `federation` key in `schema.json`; the `alias` binds it here so the
135    ///   legacy JSON workflow does not silently drop it.
136    ///
137    /// Carried verbatim by the converter into `CompiledSchema.federation`.
138    #[serde(default, alias = "federation", skip_serializing_if = "Option::is_none")]
139    pub federation_config: Option<serde_json::Value>,
140
141    /// WebSocket subscription configuration (hooks, limits).
142    ///
143    /// Compiled from the `[subscriptions]` TOML section. Embedded verbatim into
144    /// the compiled schema for server-side consumption.
145    #[serde(default, skip_serializing_if = "Option::is_none")]
146    pub subscriptions_config: Option<SubscriptionsConfig>,
147
148    /// Query validation config (depth/complexity limits).
149    ///
150    /// Compiled from `[validation]` in `fraiseql.toml`. Embedded into the compiled
151    /// schema for server-side consumption.
152    #[serde(default, skip_serializing_if = "Option::is_none")]
153    pub validation_config: Option<ValidationConfig>,
154
155    /// Debug/development configuration.
156    ///
157    /// Compiled from `[debug]` in `fraiseql.toml`. Embedded into the compiled
158    /// schema for server-side consumption.
159    #[serde(default, skip_serializing_if = "Option::is_none")]
160    pub debug_config: Option<DebugConfig>,
161
162    /// MCP (Model Context Protocol) server configuration.
163    ///
164    /// Compiled from `[mcp]` in `fraiseql.toml`. Embedded into the compiled
165    /// schema for server-side consumption.
166    #[serde(default, skip_serializing_if = "Option::is_none")]
167    pub mcp_config: Option<McpConfig>,
168
169    /// REST transport configuration.
170    ///
171    /// Compiled from `[rest]` in `fraiseql.toml`. Embedded into the compiled
172    /// schema for server-side consumption.
173    #[serde(default, skip_serializing_if = "Option::is_none")]
174    pub rest_config: Option<RestConfig>,
175
176    /// Global auto-param defaults for list queries (injected from TOML by the merger).
177    ///
178    /// Never present in `schema.json` — populated at compile time from `[query_defaults]`
179    /// in `fraiseql.toml`. Used by the converter to resolve per-query `auto_params`.
180    #[serde(default, skip_serializing_if = "Option::is_none")]
181    pub query_defaults: Option<IntermediateQueryDefaults>,
182
183    /// Naming convention for GraphQL operation names.
184    ///
185    /// Compiled from `fraiseql.toml` top-level `naming_convention` setting.
186    #[serde(default)]
187    pub naming_convention: NamingConvention,
188
189    /// Session variable injection configuration.
190    ///
191    /// When populated, the executor calls `set_config()` before each query and
192    /// mutation to inject per-request values (JWT claims, HTTP headers, or literals)
193    /// as PostgreSQL transaction-scoped settings.
194    ///
195    /// Embedded verbatim from the `session_variables` key in `schema.json`.
196    #[serde(default, skip_serializing_if = "Option::is_none")]
197    pub session_variables: Option<SessionVariablesConfig>,
198
199    /// Hierarchy definitions for ID-based ltree operators.
200    ///
201    /// Compiled from the `[hierarchies]` TOML section. Maps hierarchy names
202    /// to table/path_column pairs for subquery generation.
203    #[serde(default, skip_serializing_if = "Option::is_none")]
204    pub hierarchies_config: Option<fraiseql_core::schema::HierarchiesConfig>,
205
206    /// Changelog GraphQL-exposure configuration.
207    ///
208    /// Compiled from the `[changelog]` TOML section by the merger. Drives the
209    /// converter's injection of the `EntityChangeLog` / `TransportCheckpoint`
210    /// types and their operations.
211    #[serde(default, skip_serializing_if = "Option::is_none")]
212    pub changelog_config: Option<fraiseql_core::schema::ChangelogConfig>,
213}
214
215fn default_version() -> String {
216    "2.0.0".to_string()
217}
218
219#[cfg(test)]
220mod tests;