The schema system provides a robust, cross-platform data modeling solution ensuring type safety and consistency between TypeScript and Unity C# codebases.
- Located in
SvelteKit/BackSpace/src/lib/schemas/*.ts
- Defines core data structures using Zod for TypeScript type safety
- Uses a "description hack" to embed metadata (like type converters) in property descriptions
- Handles variations in data formats (e.g., fields that can be strings or arrays)
schema-export.js
converts Zod schemas to JSON Schema format- Processes descriptions to extract metadata into
x_meta
fields - Outputs processed JSON schemas to
Content/schemas
(SSOT) - Maintains strict rules for runtime safety, especially for WebGL/IL2CPP compatibility
- JSON schemas are copied to Unity's
StreamingAssets/Content/schemas
SchemaGenerator.cs
generates C# classes from JSON schemas- Generated classes avoid reflection-based serialization for WebGL compatibility
- Uses custom converters for type-safe string handling
- Type-safe data modeling across platforms
- Runtime validation at all levels
- WebGL/IL2CPP compatibility through careful code generation
- Metadata-driven type conversion
- Direct property access without reflection
- Support for complex types and nested structures
- No reflection-based JSON.NET methods in runtime code
- ❌ AVOID:
JsonConvert.DeserializeObject<T>()
- ✅ USE:
JToken.Parse()
with explicit type handling
- ❌ AVOID:
- No attribute-based serialization
- Direct property access and type checking
- Custom converters for type-safe string handling
- Strict validation at all pipeline stages
- Test in WebGL builds before deploying
BackSpace/
└── src/
└── lib/
└── schemas/ # TypeScript schema definitions
Content/
└── schemas/ # Central JSON schema repository
Unity/CraftSpace/Assets/
├── Content/
│ └── Schemas/ # Unity copy of JSON schemas
├── Generated/
│ └── Schemas/ # Generated C# classes
└── Scripts/
└── Models/
└── Extensions/ # Unity extensions
- Schema Definition: Core data models defined using Zod in TypeScript
- Schema Export: Zod schemas converted to standard JSON Schema format
- Schema Copy: JSON Schema files copied to Unity project
- C# Generation: SchemaImporter tool generates C# classes from JSON Schema
- Unity Integration: Generated classes used in Unity for type-safe serialization
- id: Unique identifier for the collection
- name: Display name of the collection
- query: Internet Archive query string
- description: Detailed collection description
- totalItems: Count of items in the collection
- lastUpdated: Timestamp of last update
- id: Unique identifier for the item
- title: Display title of the item
- creator: Original creator/author
- description: Detailed item description
- mediaType: Type of media (text, video, audio, etc.)
- date: Publication or creation date
- files: Associated files for this item
- Incremental Schema Changes: Make small, incremental changes
- Documentation: Document all schema changes in a changelog
- Test Coverage: Ensure validation tests for all platforms
- Strict Validation: Use stricter validation during development
- Schema Visualization: Use tools like JSON Schema Viewer for visualization
- Follow Naming Conventions: Be consistent with property naming
- Keep Schemas DRY: Extract common patterns into reusable schema components
- Include Descriptions: Add clear descriptions for each property
- WebGL-Safe JSON Parsing:
- Avoid
JsonConvert.DeserializeObject<T>()
for WebGL builds - Use JToken-based parsing for safer cross-platform compatibility
- Test WebGL builds early and often to catch reflection-related issues
- Implement explicit type checking rather than relying on automatic type conversion
- Avoid
- Handle Internet Archive Metadata Gracefully:
- Use flexible JToken parsing for Internet Archive's variable metadata
- Implement fallbacks for missing or malformed properties
- Log warnings instead of throwing errors when encountering unexpected metadata
- Field Categorization:
- Essential IA fields as top-level schema properties
- Additional properties preserved in the C# implementation's
extraFields
- Field Normalization:
- String fields normalized to always be strings
- Array fields normalized to always be arrays
- Optional fields have safe defaults
- Type Converters:
StringOrNullToString
: Handles null/undefinedStringOrStringArrayToString
: Handles string/array variationsNullOrStringToStringArray
: Ensures string arraysStringToDateTime
: Handles date/time conversions
The schema system provides a special mechanism for handling undefined properties in the Unity C# implementation:
-
C# Implementation Only:
extraFields
only exists in the Unity C# side- It is NOT exposed in Zod schemas or JSON schemas
- It's implemented as a JObject in the SchemaGeneratedObject base class
-
Automatic Field Management:
ImportExtraFields()
captures any undefined fields from JSONExportToJson()
includes both known properties and extraFields- This happens transparently without additional code
-
Implementation Details:
- The SchemaGenerator ensures the
extraFields
property is handled correctly - Generated schema classes include special case handling for
extraFields
- The HasDefinedProperty method recognizes
extraFields
as a special case
- The SchemaGenerator ensures the
This approach ensures that custom metadata fields from Internet Archive or other sources are preserved without cluttering the schema definition.
- Update Zod schema in
SvelteKit/BackSpace/src/lib/schemas/
- Run complete schema generation:
cd SvelteKit/BackSpace npm run schema:generate-all
- Open Unity and use
Tools > Import JSON Schema
- Update affected TypeScript code
- Test validation across platforms
- Verify schemas directory exists:
mkdir -p SvelteKit/BackSpace/schemas Content/schemas
- Run debug scripts:
npm run path:debug npm run schema:debug
- For Unity issues:
- Delete generated C# files
- Restart Unity
- Re-run import process
- Schema-Aware Custom Editor:
- Generalized Unity Editor system for JSON data
- Dynamic Inspector UI based on schema metadata
- Custom controls based on schema hints
- Schema Validation:
- Optional validation during
ImportFromJson
- Warning/rejection of non-conforming data
- Optional validation during
- Performance Optimization:
- Async loading support
- Improved caching strategies
- Batch loading operations
- Developer Experience:
- Better error messages
- Schema validation in editor
- Visual schema designer
This document describes the schema system used in the CraftSpace project.
This document outlines the schema-driven development approach used in BackSpace, focusing on how we maintain type safety and data consistency across multiple platforms.
- Single Source of Truth: JSON schemas define all data structures
- Cross-Platform Compatibility: Schema definitions flow to all platforms
- Static Type Safety: Auto-generated types in TypeScript and C#
- Runtime Validation: Consistent validation across environments
- Developer Experience: Streamlined workflow for schema evolution
- Direct Integration: No adapters or translation layers needed
BackSpace uses a clean, straightforward schema generation process:
- Define Schemas: Create JSON schema definitions
- Generate C# Classes: Run the schema importer to generate C# classes in the CraftSpace.Schema namespace
- Import in Unity: Use Unity's "Tools > Import JSON Schema" menu to apply changes
- Extend in Unity: Use partial classes to add Unity-specific functionality
JSON schemas are defined and stored in:
SvelteKit/BackSpace/schemas/
Example schemas:
collection.json
- Collection schemaitem.json
- Item schema
Run the schema importer from the BackSpace directory:
cd SvelteKit/BackSpace
npm run schema:generate-all
This command:
- Generates C# classes from JSON schemas
- Copies JSON schemas to central Content/schemas directory
- Copies JSON schemas to Unity's Content/Schemas directory
After generating the schemas and copying them to Unity:
- Open Unity and refresh the project
- Go to the menu:
Tools > Import JSON Schema
- C# classes are generated in
Assets/Generated/Schemas
- Refresh Unity again to see the new classes
There are Unity integration focused subclasses of the dynamically generated classes CollectinSchema and ItemSchema called Collection and Item that have code for managing multiple views, cover images, and other unity integation tasks. Extend Unity functionality by modifying these classes (not subclassing or extending them).
BackSpace/
└── src/
└── lib/
└── schemas/ # TypeScript schema definitions
Content/
└── schemas/ # Central JSON schema repository
Unity/CraftSpace/Assets/
├── Content/
│ └── Schemas/ # Unity copy of JSON schemas
├── Generated/
│ └── Schemas/ # Generated C# classes
└── Scripts/
└── Models/
└── Extensions/ # Unity extensions
This outlines the key locations and their roles in the schema pipeline, following the flow from definition to runtime use.
-
Zod Schemas (TypeScript - The Ultimate SSOT):
SvelteKit/BackSpace/src/lib/schemas/*.ts
- Purpose: Define the core data structures and embed metadata via the "description hack".
-
Exported JSON Schemas (SSOT for Unity & Other Consumers):
Content/schemas/*.json
(Located at the project root)- Purpose: Central repository for processed JSON Schemas derived from Zod. These are the direct output of the
schema:export
script and serve as the source for downstream processes like copying to Unity.
-
JSON Schemas (Unity Runtime Content):
Unity/CraftSpace/Assets/StreamingAssets/Content/schemas/*.json
- Purpose: A filtered mirror of the central
Content/schemas
directory, copied into the Unity project'sStreamingAssets
for runtime access and used as the input for the C# Code Generator. This copy step (managed outside the scripts detailed here for now) allows different targets (like Unity) to potentially use a subset of the available schemas.
-
C# Code Generator (Tool):
Unity/CraftSpace/Assets/Editor/SchemaGenerator/SchemaGenerator.cs
- Purpose: Reads processed JSON schemas from
StreamingAssets/Content/schemas
and generates the C# Schema classes. Crucially, it generates explicit, reflection-free code for serialization/deserialization, designed to work hand-in-hand with theSchemaGeneratedObject
base class and direct converter calls.
-
Generated C# Schema Classes (Derived from JSON Schemas):
Unity/CraftSpace/Assets/Scripts/Schemas/Generated/*Schema.cs
- Purpose: Auto-generated C# representation of the schema data structure, inheriting from
SchemaGeneratedObject
. Contains the generatedImportKnownProperties
andExportKnownProperties
methods. DO NOT EDIT MANUALLY.
-
C# Base Class (Foundation):
Unity/CraftSpace/Assets/Scripts/Schemas/SchemaGeneratedObject.cs
- Purpose: Provides the runtime framework for reflection-free serialization/deserialization (
ImportFromJson
,ImportKnownProperties
, etc.), manages extra fields, provides theScriptableObject
base, and centralizes common logic like setting the Unity object name (TypeName-Id
).
-
Manual C# Unity Integration Classes (e.g.,
Collection
,Item
):Unity/CraftSpace/Assets/Scripts/Schemas/*.cs
- Purpose: Inherit from generated classes (
Item : ItemSchema
). These classes:- Store lists of IDs for related objects (e.g.,
private List<string> _itemIds;
populated from index files). - Implement properties (e.g.,
public IEnumerable<Item> Items { get; }
) whose getters perform on-demand lookups via theContentRegistry
(Brewster) using the stored IDs. - Contain methods to load index files (e.g.,
LoadItemIndex()
) which populate the ID lists, called by the registry. - Add any other Unity-specific runtime logic or non-serialized fields (like
Texture2D cover
).
- Store lists of IDs for related objects (e.g.,
-
Central Content Registry (
Brewster
):Unity/CraftSpace/Assets/Scripts/Core/Brewster.cs
- Purpose: Manages the runtime loading and caching of all schema-derived
ScriptableObject
instances.- Maintains flat dictionaries (maps) per content type (e.g.,
Dictionary<string, Collection> _loadedCollections
,Dictionary<string, Item> _loadedItems
). - Provides public static singleton instance methods to retrieve objects by ID (e.g.,
GetCollection(string id)
,GetItem(string itemId)
). - Handles on-demand loading: If an object is requested but not in its dictionary, the registry:
- Constructs the path to the object's JSON file in
StreamingAssets
. - Reads the JSON file.
- Calls the static
FromJson
factory method for the corresponding type. - Adds the newly created
ScriptableObject
instance to the appropriate dictionary (cache). - Triggers necessary post-load actions (like
LoadItemIndex
for collections). - Returns the instance.
- Constructs the path to the object's JSON file in
- Initializes by loading only the collection index (
collections-index.json
).
- Maintains flat dictionaries (maps) per content type (e.g.,
-
C# Converters:
Unity/CraftSpace/Assets/Scripts/Bridge/BridgeJsonConverter.cs
(or similar).- Purpose: Implement specific, IL2CPP-safe value conversion logic called directly by generated code (No change here).
Schemas follow this naming convention:
- JSON schema:
[Name].json
(e.g.,Collection.json
) - Generated C# class:
[Name].cs
(e.g.,Collection.cs
) - Unity extension:
[Name]Extensions.cs
(e.g.,CollectionExtensions.cs
)
These scripts automate parts of the schema definition, export, and Unity integration process:
npm run schema:generate:json
:- Action: Reads Zod schemas, processes descriptions, and generates processed JSON Schema files (
*.json
) into the centralContent/schema/
directory.
- Action: Reads Zod schemas, processes descriptions, and generates processed JSON Schema files (
npm run schemas:export:unity
:- Action: Exports (copies) the processed JSON schema files from the central SSOT (
Content/schema/
) directory into the Unity project'sStreamingAssets/Content/schemas/
folder.
- Action: Exports (copies) the processed JSON schema files from the central SSOT (
npm run schemas:prepare:unity
:- Action: Orchestrates the full preparation of schemas needed by Unity before C# generation (runs
schema:generate:json
thenschemas:export:unity
).
- Action: Orchestrates the full preparation of schemas needed by Unity before C# generation (runs
npm run schemas:export
:- Action: Top-level export command. For now, it defaults to preparing schemas for Unity (
schemas:prepare:unity
). Can be expanded later.
- Action: Top-level export command. For now, it defaults to preparing schemas for Unity (
npm run schemas:regenerate:unity
:- Action: Prepares the JSON schemas for Unity (
schemas:prepare:unity
) and then triggers the Unity C# code regeneration process (viaunity-automation.js
with theregenerate-schemas
argument, assuming it executesSchemaGenerator.GenerateSchemas_Commandline
). See Naming Conventions below.
- Action: Prepares the JSON schemas for Unity (
npm run pipeline:schemas:unity
:- Action: Convenience script to run the entire schema preparation and C# code regeneration pipeline for Unity (effectively calls
schemas:regenerate:unity
).
- Action: Convenience script to run the entire schema preparation and C# code regeneration pipeline for Unity (effectively calls
The schema pipeline scripts follow these conventions:
schemas:*
prefix: Used for scripts primarily dealing with the schema definition, processing, or movement from the Single Source of Truth (SSOT) perspective, or end-to-end schema pipeline tasks.unity:*
prefix: Used for scripts where the final, critical action takes place within the Unity environment (like C# regeneration or building the Unity project), OR for utility scripts focused solely on Unity interaction (likeunity:setup
).- Prepare vs. Regenerate:
schemas:prepare:unity
includes all steps necessary to get the JSON files ready for Unity (Generation + Export/Copy).schemas:regenerate:unity
includes the preparation step and the final C# code regeneration step inside Unity.
Naming Rationale: The script schemas:regenerate:unity
is now grouped under schemas:
because it represents the full end-to-end schema pipeline for Unity, even though the final step executes within the Unity environment. Utility scripts like unity:setup
remain under the unity:
prefix.
If you encounter issues:
-
Verify the schemas directory exists:
mkdir -p SvelteKit/BackSpace/schemas Content/schemas
-
Run the debug script to check paths:
npm run path:debug
-
Check for schema errors:
npm run schema:debug
-
If Unity doesn't recognize new schema changes:
- Delete the generated C# files
- Restart Unity
- Re-run the import process
The Simple Schema Module provides a streamlined approach for using schema objects directly in Unity without complex adapters or compatibility layers.
Located in Assets/Scripts/Schema
, this module:
- Directly extends generated schema classes
- Provides clean, easy-to-use interfaces
- Simplifies data loading, serialization, and UI binding
- Eliminates the need for wrapper or adapter classes
Schema classes directly inherit from generated code while adding Unity-specific functionality.
The module includes a Model-View pattern for binding schema objects to UI components.
The Brewster class acts as a central registry for all schema objects, managing:
- Loading objects from JSON
- Caching loaded objects
- Tracking relationships between objects
- Providing global access to all content
- Simplicity - Direct inheritance with no complex adapter layers
- Performance - No translation overhead between schema and Unity objects
- Type Safety - Full type checking for all schema properties
- Unity Integration - Schema objects are ScriptableObjects with Unity-specific extensions
Assets/Scripts/Schema/
├── Schema.cs # Core schema classes and utilities
├── Examples/ # Example usage scripts
│ └── SimpleSchemaExample.cs
└── README.md # Module-specific documentation
Zod serves as our primary schema definition tool, providing both TypeScript type generation and runtime validation.
- TypeScript-First: Automatic type inference for TypeScript
- Runtime Validation: Full validation at runtime
- Schema Composition: Build complex schemas from simpler ones
- Documentation: Schemas are self-documenting with descriptions
- Meta Properties: Support for additional metadata
src/lib/schemas/
├── collection.ts - Collection schemas
├── item.ts - Item schemas
├── export-profile.ts - Export profile schemas
├── connector.ts - Connector schemas
└── index.ts - Re-exports all schemas
Zod generates TypeScript types for compile-time safety.
Zod schemas provide runtime validation of data structures.
Schemas can be integrated with form libraries for automatic validation.
We export Zod schemas to JSON Schema format for cross-platform use.
The generated C# classes work seamlessly with JSON.NET.
The schema system works with our P/Invoke bridge for WebGL communication.
For client-side validation, we can use Ajv with our exported JSON schemas.
Server-side API endpoints use Zod for validation.
When updating a schema:
- Update the Zod schema definition in
SvelteKit/BackSpace/src/lib/schemas/
- Run the complete schema generation process:
This will:
cd SvelteKit/BackSpace npm run schema:generate-all
- Export JSON Schemas to BackSpace schemas directory
- Copy JSON Schemas to central Content/schemas directory
- Copy JSON Schemas to Unity's Content/Schemas directory
- Generate C# classes
- Open Unity and go to
Tools > Import JSON Schema
to update C# classes - Update any affected TypeScript code
- Test validation across all platforms
- Discussion Phase: Discuss and document the needed schema changes
- Implementation: Update Zod schema in TypeScript
- Testing: Create tests for new schema validation
- Export: Run the schema generation process
- Unity Import: Import schemas in Unity and verify C# classes
- Integration: Update any code that uses the schema
- End-to-End Testing: Test the full data pipeline
- All schemas are defined using Zod for TypeScript type safety
- Use
id
in model properties to align with JSON - Use camelCase for variable and parameter names:
collectionId
,itemId
- Use snake_case for function names that handle these IDs:
get_collection()
,update_item()
- Keep schemas focused on one responsibility/entity
- Include detailed descriptions for all properties to auto-generate documentation
- Prefer composition over inheritance for schema reuse
This document outlines the end-to-end process for defining, exporting, and consuming data schemas within the CraftSpace project.
-
Zod Schemas (TypeScript Source of Truth)
- Located in
SvelteKit/BackSpace/src/lib/schemas/*.ts
- Define core data structures and embedded metadata
- Located in
-
Export Script
- Converts Zod schemas to JSON Schema format
- Processes descriptions and extracts metadata into
x_meta
fields - Outputs to central JSON schema repository
-
JSON Schemas (Storage)
- Located in
Unity/CraftSpace/Assets/StreamingAssets/Content/schemas/
- Standard JSON Schema files with embedded metadata
- Located in
-
C# Code Generation
- Reads JSON schemas and generates corresponding C# classes
- Creates type-safe, reflection-free code
-
C# Schema Base Class
SchemaGeneratedObject
provides common functionality- Handles serialization/deserialization without reflection
-
Unity Integration Classes
- Extend generated schema classes with Unity-specific features
- Handle relationships between objects through ID-based references
-
Content Registry (Brewster)
- Manages loading, caching, and relationships between objects
- Provides global access to schema objects
Schema objects are managed using an ID-based registry pattern that provides:
- On-demand loading of objects
- Memory optimization
- Simple serialization/deserialization
- Clean handling of object relationships
The Brewster registry system provides a central point to access all schema objects:
// Get a collection by its ID
Collection collection = Brewster.Instance.GetCollection("collectionId");
// Get an item by its ID (requires collection context)
Item item = Brewster.Instance.GetItem("collectionId", "itemId");
JSON.NET's generic deserialization methods like JsonConvert.DeserializeObject<T>()
use heavy reflection that can cause hard crashes in WebGL builds. These methods often work in the Unity Editor but fail catastrophically at runtime in WebGL:
// 🛑 AVOID THIS - Crashes in WebGL!
List<string> items = JsonConvert.DeserializeObject<List<string>>(jsonContent);
Instead, use the non-generic JToken.Parse()
approach followed by explicit type handling:
// ✅ WebGL-safe approach
JToken token = JToken.Parse(jsonContent);
if (token is JArray array)
{
List<string> items = new List<string>();
foreach (JToken item in array)
{
if (item.Type == JTokenType.String)
{
items.Add(item.Value<string>());
}
}
}
- WebGL/IL2CPP Compatibility: Minimal reflection, works reliably in WebGL builds
- Dynamic Type Handling: Better for handling variable data structures
- Polymorphic Data: Ideal for Internet Archive's diverse metadata formats
- Error Resilience: More graceful handling of malformed JSON
- Direct JSON Model: Stays closer to the raw JSON structure
The schema implementation follows a clear inheritance pattern:
SchemaGeneratedObject
- Base abstract class with common functionality*Schema
classes - Generated code from JSON schemas (e.g.,CollectionSchema
,ItemSchema
)- Extension classes - Manual classes that extend the generated code (e.g.,
Collection
,Item
)
Schema objects are managed using an ID-based registry pattern:
- Objects are loaded on-demand when requested
- Relationships between objects are managed via IDs rather than direct references
- The Brewster registry maintains the object cache
- This approach optimizes memory usage and supports serialization/deserialization