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 <noreply@anthropic.com>
This commit is contained in:
@@ -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=
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
target/
|
||||||
|
*.log
|
||||||
|
.env
|
||||||
|
|
||||||
|
packages/ts-types/src/generated/*
|
||||||
|
!packages/ts-types/src/generated/.gitkeep
|
||||||
@@ -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.
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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";
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export * from "./material";
|
||||||
|
export * from "./print-params";
|
||||||
|
export * from "./template";
|
||||||
|
export * from "./print-job";
|
||||||
@@ -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<typeof MaterialProfileSchema>;
|
||||||
@@ -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<typeof PrintJobStatusSchema>;
|
||||||
|
|
||||||
|
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<typeof PrintJobSchema>;
|
||||||
@@ -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<typeof PrintParametersSchema>;
|
||||||
@@ -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<typeof TemplateSchema>;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user