commit 660855ecd673d3b5c88d36ba82e4aced2b1af6f8 Author: Iwo Strzeboński Date: Fri Aug 28 16:12:03 2026 +0000 Initial boilerplate scaffold for continuum-schemas Protobuf data model (Template/PrintJob/MaterialProfile/PrintParameters) with codegen targets for TypeBox+TS types (packages/ts-types) and prost-derived Rust structs (packages/rust-types). Co-Authored-By: Claude Sonnet 5 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..f823f74 --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +# continuum-schemas has no runtime — it only emits generated types/structs. +# This file is kept for consistency with the other Continuum repos. + +# Optional: private registry token, only needed if packages/* are ever +# published to a private npm/cargo registry instead of consumed as +# workspace/path dependencies. +NPM_REGISTRY_TOKEN= +CARGO_REGISTRY_TOKEN= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..88eaf6d --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +dist/ +target/ +*.log +.env + +packages/ts-types/src/generated/* +!packages/ts-types/src/generated/.gitkeep diff --git a/README.md b/README.md new file mode 100644 index 0000000..e97c21b --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# continuum-schemas + +Single source of truth for the Continuum data model. Protobuf definitions in +`proto/` are compiled into two consumer packages: + +- `packages/ts-types` — TypeScript types generated via `ts-proto`, plus + hand-written [TypeBox](https://github.com/sinclairzx81/typebox) schemas + under `src/schemas/` used for runtime validation in `continuum-backend`'s + ElysiaJS routes. +- `packages/rust-types` — a Rust crate (`continuum-rust-types`) that compiles + the same `.proto` files with `prost-build` (via `build.rs`), producing + `serde`-derived structs for `continuum-proxy` and `continuum-ai-worker`. + +## Layout + +``` +proto/continuum/v1/ canonical .proto message definitions +packages/ts-types/ TypeBox schemas + generated TS proto types +packages/rust-types/ prost-generated Rust structs (build.rs codegen) +``` + +## Build + +Requires `protoc` on `PATH` and `npm install` run at the repo root first. + +``` +npm install +npm run build:ts # protoc + ts-proto -> packages/ts-types/src/generated +npm run build:rust # cargo build, runs build.rs -> prost-generated structs +npm run build # both +``` + +Generated TS output under `packages/ts-types/src/generated` is build-derived +and gitignored. The Rust crate's generated code lands in `OUT_DIR` per +standard `prost-build` conventions and is never committed. diff --git a/package.json b/package.json new file mode 100644 index 0000000..e1dfa06 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "continuum-schemas", + "version": "0.1.0", + "private": true, + "workspaces": [ + "packages/*" + ], + "scripts": { + "build:ts": "protoc --plugin=protoc-gen-ts_proto=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=packages/ts-types/src/generated --ts_proto_opt=esModuleInterop=true,outputServices=false,useOptionals=messages -I proto $(find proto -name '*.proto')", + "build:rust": "cargo build -p continuum-rust-types --manifest-path packages/rust-types/Cargo.toml", + "build": "npm run build:ts && npm run build:rust", + "typecheck": "tsc -b" + }, + "devDependencies": { + "ts-proto": "^1.181.0", + "typescript": "^5.5.0" + } +} diff --git a/packages/rust-types/Cargo.toml b/packages/rust-types/Cargo.toml new file mode 100644 index 0000000..b9283a8 --- /dev/null +++ b/packages/rust-types/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "continuum-rust-types" +version = "0.1.0" +edition = "2021" + +[lib] +name = "continuum_types" +path = "src/lib.rs" + +[dependencies] +prost = "0.13" +serde = { version = "1", features = ["derive"] } + +[build-dependencies] +prost-build = "0.13" diff --git a/packages/rust-types/build.rs b/packages/rust-types/build.rs new file mode 100644 index 0000000..2d99ad9 --- /dev/null +++ b/packages/rust-types/build.rs @@ -0,0 +1,27 @@ +use std::path::PathBuf; + +fn main() { + let proto_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../proto") + .canonicalize() + .expect("proto root must exist"); + + let proto_files = [ + proto_root.join("continuum/v1/material.proto"), + proto_root.join("continuum/v1/print_params.proto"), + proto_root.join("continuum/v1/template.proto"), + proto_root.join("continuum/v1/print_job.proto"), + ]; + + for file in &proto_files { + println!("cargo:rerun-if-changed={}", file.display()); + } + + let mut config = prost_build::Config::new(); + config.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]"); + config.type_attribute(".", "#[serde(rename_all = \"camelCase\")]"); + + config + .compile_protos(&proto_files, &[proto_root]) + .expect("failed to compile continuum .proto sources"); +} diff --git a/packages/rust-types/src/lib.rs b/packages/rust-types/src/lib.rs new file mode 100644 index 0000000..5d844c8 --- /dev/null +++ b/packages/rust-types/src/lib.rs @@ -0,0 +1,9 @@ +pub mod continuum { + pub mod v1 { + include!(concat!(env!("OUT_DIR"), "/continuum.v1.rs")); + } +} + +pub use continuum::v1::{ + MaterialProfile, PrintJob, PrintJobStatus, PrintParameters, Template, +}; diff --git a/packages/ts-types/package.json b/packages/ts-types/package.json new file mode 100644 index 0000000..9375436 --- /dev/null +++ b/packages/ts-types/package.json @@ -0,0 +1,14 @@ +{ + "name": "@continuum/ts-types", + "version": "0.1.0", + "private": true, + "type": "module", + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": "./src/index.ts" + }, + "dependencies": { + "@sinclair/typebox": "^0.32.0" + } +} diff --git a/packages/ts-types/src/generated/.gitkeep b/packages/ts-types/src/generated/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/ts-types/src/index.ts b/packages/ts-types/src/index.ts new file mode 100644 index 0000000..9721a86 --- /dev/null +++ b/packages/ts-types/src/index.ts @@ -0,0 +1,7 @@ +export * from "./schemas"; + +// `src/generated/**` is produced by `npm run build:ts` (protoc + ts-proto) +// from the .proto sources in /proto and is not committed. Re-export it +// here once generated so consumers get both the TypeBox runtime schemas +// and the proto-derived TS types from a single package entry point. +export * as proto from "./generated"; diff --git a/packages/ts-types/src/schemas/index.ts b/packages/ts-types/src/schemas/index.ts new file mode 100644 index 0000000..2e3ff77 --- /dev/null +++ b/packages/ts-types/src/schemas/index.ts @@ -0,0 +1,4 @@ +export * from "./material"; +export * from "./print-params"; +export * from "./template"; +export * from "./print-job"; diff --git a/packages/ts-types/src/schemas/material.ts b/packages/ts-types/src/schemas/material.ts new file mode 100644 index 0000000..cde61d7 --- /dev/null +++ b/packages/ts-types/src/schemas/material.ts @@ -0,0 +1,13 @@ +import { Type, type Static } from "@sinclair/typebox"; + +export const MaterialProfileSchema = Type.Object({ + id: Type.String({ format: "uuid" }), + name: Type.String({ minLength: 1 }), + filamentType: Type.String({ minLength: 1 }), + nozzleTempC: Type.Number(), + bedTempC: Type.Number(), + densityGCm3: Type.Number(), + colorHex: Type.String({ pattern: "^#[0-9a-fA-F]{6}$" }), +}); + +export type MaterialProfile = Static; diff --git a/packages/ts-types/src/schemas/print-job.ts b/packages/ts-types/src/schemas/print-job.ts new file mode 100644 index 0000000..8106ff8 --- /dev/null +++ b/packages/ts-types/src/schemas/print-job.ts @@ -0,0 +1,27 @@ +import { Type, type Static } from "@sinclair/typebox"; + +export const PrintJobStatusSchema = Type.Union([ + Type.Literal("PRINT_JOB_STATUS_UNSPECIFIED"), + Type.Literal("PRINT_JOB_STATUS_QUEUED"), + Type.Literal("PRINT_JOB_STATUS_PRINTING"), + Type.Literal("PRINT_JOB_STATUS_PAUSED"), + Type.Literal("PRINT_JOB_STATUS_COMPLETED"), + Type.Literal("PRINT_JOB_STATUS_FAILED"), + Type.Literal("PRINT_JOB_STATUS_CANCELLED"), +]); + +export type PrintJobStatus = Static; + +export const PrintJobSchema = Type.Object({ + id: Type.String({ format: "uuid" }), + farmId: Type.String({ format: "uuid" }), + printerId: Type.String({ format: "uuid" }), + templateId: Type.String({ format: "uuid" }), + status: PrintJobStatusSchema, + progressPercent: Type.Number({ minimum: 0, maximum: 100 }), + startedAtUnix: Type.Integer(), + completedAtUnix: Type.Integer(), + gcodeFileKey: Type.String({ minLength: 1 }), +}); + +export type PrintJob = Static; diff --git a/packages/ts-types/src/schemas/print-params.ts b/packages/ts-types/src/schemas/print-params.ts new file mode 100644 index 0000000..a93c8d6 --- /dev/null +++ b/packages/ts-types/src/schemas/print-params.ts @@ -0,0 +1,12 @@ +import { Type, type Static } from "@sinclair/typebox"; + +export const PrintParametersSchema = Type.Object({ + layerHeightMm: Type.Number({ minimum: 0.04, maximum: 1.0 }), + infillPercent: Type.Integer({ minimum: 0, maximum: 100 }), + infillPattern: Type.String({ minLength: 1 }), + printSpeedMmS: Type.Number({ minimum: 0 }), + supportsEnabled: Type.Boolean(), + wallCount: Type.Integer({ minimum: 0 }), +}); + +export type PrintParameters = Static; diff --git a/packages/ts-types/src/schemas/template.ts b/packages/ts-types/src/schemas/template.ts new file mode 100644 index 0000000..b315cb9 --- /dev/null +++ b/packages/ts-types/src/schemas/template.ts @@ -0,0 +1,18 @@ +import { Type, type Static } from "@sinclair/typebox"; +import { MaterialProfileSchema } from "./material"; +import { PrintParametersSchema } from "./print-params"; + +export const TemplateSchema = Type.Object({ + id: Type.String({ format: "uuid" }), + name: Type.String({ minLength: 1 }), + description: Type.String(), + ownerId: Type.String({ format: "uuid" }), + modelFileKey: Type.String({ minLength: 1 }), + material: MaterialProfileSchema, + printParameters: PrintParametersSchema, + createdAtUnix: Type.Integer(), + updatedAtUnix: Type.Integer(), + tags: Type.Array(Type.String()), +}); + +export type Template = Static; diff --git a/packages/ts-types/tsconfig.json b/packages/ts-types/tsconfig.json new file mode 100644 index 0000000..49e05ce --- /dev/null +++ b/packages/ts-types/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src"] +} diff --git a/proto/continuum/v1/material.proto b/proto/continuum/v1/material.proto new file mode 100644 index 0000000..138fe8f --- /dev/null +++ b/proto/continuum/v1/material.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package continuum.v1; + +message MaterialProfile { + string id = 1; + string name = 2; + string filament_type = 3; + double nozzle_temp_c = 4; + double bed_temp_c = 5; + double density_g_cm3 = 6; + string color_hex = 7; +} diff --git a/proto/continuum/v1/print_job.proto b/proto/continuum/v1/print_job.proto new file mode 100644 index 0000000..1b08357 --- /dev/null +++ b/proto/continuum/v1/print_job.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package continuum.v1; + +import "continuum/v1/template.proto"; + +enum PrintJobStatus { + PRINT_JOB_STATUS_UNSPECIFIED = 0; + PRINT_JOB_STATUS_QUEUED = 1; + PRINT_JOB_STATUS_PRINTING = 2; + PRINT_JOB_STATUS_PAUSED = 3; + PRINT_JOB_STATUS_COMPLETED = 4; + PRINT_JOB_STATUS_FAILED = 5; + PRINT_JOB_STATUS_CANCELLED = 6; +} + +message PrintJob { + string id = 1; + string farm_id = 2; + string printer_id = 3; + string template_id = 4; + PrintJobStatus status = 5; + double progress_percent = 6; + int64 started_at_unix = 7; + int64 completed_at_unix = 8; + string gcode_file_key = 9; +} diff --git a/proto/continuum/v1/print_params.proto b/proto/continuum/v1/print_params.proto new file mode 100644 index 0000000..f0f8ac3 --- /dev/null +++ b/proto/continuum/v1/print_params.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package continuum.v1; + +message PrintParameters { + double layer_height_mm = 1; + int32 infill_percent = 2; + string infill_pattern = 3; + double print_speed_mm_s = 4; + bool supports_enabled = 5; + int32 wall_count = 6; +} diff --git a/proto/continuum/v1/template.proto b/proto/continuum/v1/template.proto new file mode 100644 index 0000000..116071f --- /dev/null +++ b/proto/continuum/v1/template.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package continuum.v1; + +import "continuum/v1/material.proto"; +import "continuum/v1/print_params.proto"; + +message Template { + string id = 1; + string name = 2; + string description = 3; + string owner_id = 4; + string model_file_key = 5; + MaterialProfile material = 6; + PrintParameters print_parameters = 7; + int64 created_at_unix = 8; + int64 updated_at_unix = 9; + repeated string tags = 10; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..07f894f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022"], + "strict": true, + "declaration": true, + "composite": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true + } +}