diff --git a/packages/json_script_example/README.md b/packages/json_script_example/README.md index c87604035..02effefa1 100644 --- a/packages/json_script_example/README.md +++ b/packages/json_script_example/README.md @@ -25,7 +25,9 @@ json_script_example/ │ ├── components.json # UI component definitions │ ├── types.json # TypeScript-style type definitions │ ├── types.schema.json # 📋 JSON Schema for types -│ └── styles.json # Design tokens +│ ├── styles.json # Design tokens +│ └── schema/ +│ └── entities.yaml # 🗄️ Database entity definitions ├── tests/ │ ├── README.md # Testing guide │ ├── math.test.logic.json # Test assertion functions @@ -77,6 +79,46 @@ Benefits: - ✅ **Documentation** - Hover tooltips explain each field - ✅ **Type Safety** - Ensure JSON matches specification +## Database Schema (entities.yaml) + +The package includes **example entity definitions** in [seed/schema/entities.yaml](seed/schema/entities.yaml) demonstrating data modeling for JSON script-based applications: + +### Entities + +**Calculator** - Calculator instance with user preferences +- Fields: id, userId, name, mode (basic/scientific/programmer), precision, theme +- Relations: hasMany calculations, belongsTo user +- ACL: User-scoped (self-only access) + +**Calculation** - Individual calculation history entries +- Fields: id, calculatorId, expression, result, operationType, isStarred, tags, metadata +- Relations: belongsTo calculator +- ACL: Row-level security via calculator.userId + +**ExpressionTemplate** - Reusable calculation templates with variables +- Fields: id, name, template, variables, category, isPublic, useCount +- Relations: belongsTo user +- ACL: Public if shared, otherwise self-only +- Example: `"${principal} * (1 + ${rate}/100)^${years}"` for compound interest + +**ValidationRule** - Custom input validation rules +- Fields: id, name, ruleType (range/pattern/custom), config, errorMessage +- Relations: belongsTo user +- ACL: User-scoped + +**ComponentState** - Persisted UI component state +- Fields: id, componentName (ExpressionDemo/OperatorDemo/ResultDisplay), state, isDefault +- Relations: belongsTo user +- Use case: Save and restore component configurations + +This schema demonstrates: +- **Entity relationships** (belongsTo, hasMany) +- **Field types** (string, int, float, boolean, json, bigint, cuid) +- **Field constraints** (required, nullable, maxLength, min, max, enum, default) +- **Indexes** for query performance +- **Access control** (ACL with row-level security) +- **JSON fields** for flexible metadata storage + ## Exported Functions ### 1. `all_expressions` diff --git a/packages/json_script_example/seed/metadata.json b/packages/json_script_example/seed/metadata.json index fb7f607e4..bfbb1867d 100644 --- a/packages/json_script_example/seed/metadata.json +++ b/packages/json_script_example/seed/metadata.json @@ -52,7 +52,8 @@ }, "seed": { "styles": "seed/styles.json", - "types": "seed/types.json" + "types": "seed/types.json", + "schema": "seed/schema/entities.yaml" }, "storybook": { "stories": [ diff --git a/packages/json_script_example/seed/schema/entities.yaml b/packages/json_script_example/seed/schema/entities.yaml new file mode 100644 index 000000000..b923d7c0b --- /dev/null +++ b/packages/json_script_example/seed/schema/entities.yaml @@ -0,0 +1,366 @@ +# Schema definitions for json_script_example package +# Demonstrates entity modeling for JSON script-based applications + +entities: + - name: Calculator + version: "1.0" + description: "Calculator instance storing calculation history and preferences" + checksum: null + + fields: + id: + type: cuid + primary: true + generated: true + + userId: + type: string + required: true + index: true + description: "Owner of this calculator instance" + + name: + type: string + required: true + maxLength: 100 + default: "My Calculator" + description: "User-friendly name for this calculator" + + mode: + type: string + required: true + default: "basic" + enum: [basic, scientific, programmer] + description: "Calculator mode" + + precision: + type: int + required: true + default: 2 + min: 0 + max: 10 + description: "Decimal precision for results" + + theme: + type: string + nullable: true + enum: [light, dark, auto] + default: "auto" + + lastUsedAt: + type: bigint + nullable: true + description: "Last time this calculator was used" + + createdAt: + type: bigint + required: true + + indexes: + - fields: [userId] + - fields: [userId, lastUsedAt] + + relations: + - name: calculations + type: hasMany + entity: Calculation + foreignKey: calculatorId + - name: user + type: belongsTo + entity: User + field: userId + onDelete: Cascade + + acl: + create: [user] + read: [self] + update: [self] + delete: [self] + rowLevel: "userId = $user.id" + + - name: Calculation + version: "1.0" + description: "Individual calculation entry in history" + checksum: null + + fields: + id: + type: cuid + primary: true + generated: true + + calculatorId: + type: string + required: true + index: true + description: "Calculator instance this belongs to" + + expression: + type: string + required: true + maxLength: 500 + description: "The calculation expression (e.g., '10 + 5 * 2')" + + result: + type: float + required: true + description: "Calculated result" + + operationType: + type: string + required: true + enum: [arithmetic, comparison, logical, bitwise] + description: "Type of operation performed" + + isStarred: + type: boolean + default: false + description: "Whether user starred this calculation" + + tags: + type: json + nullable: true + description: "User-defined tags for organization" + + metadata: + type: json + nullable: true + description: "Additional metadata (intermediate steps, variables, etc.)" + + createdAt: + type: bigint + required: true + + indexes: + - fields: [calculatorId, createdAt] + - fields: [calculatorId, isStarred] + - fields: [operationType] + + relations: + - name: calculator + type: belongsTo + entity: Calculator + field: calculatorId + onDelete: Cascade + + acl: + create: [user] + read: [self] + update: [self] + delete: [self] + rowLevel: "calculator.userId = $user.id" + + - name: ExpressionTemplate + version: "1.0" + description: "Reusable expression templates with variables" + checksum: null + + fields: + id: + type: cuid + primary: true + generated: true + + userId: + type: string + required: true + index: true + description: "Creator of this template" + + name: + type: string + required: true + maxLength: 100 + description: "Template name" + + description: + type: string + nullable: true + maxLength: 500 + description: "What this template does" + + template: + type: string + required: true + maxLength: 1000 + description: "Expression template with ${variable} placeholders" + + variables: + type: json + required: true + description: "Variable definitions with types and defaults" + + category: + type: string + nullable: true + enum: [math, finance, physics, engineering, custom] + description: "Template category" + + isPublic: + type: boolean + default: false + description: "Whether this template is shared publicly" + + useCount: + type: int + default: 0 + description: "Number of times this template has been used" + + createdAt: + type: bigint + required: true + + updatedAt: + type: bigint + nullable: true + + indexes: + - fields: [userId] + - fields: [category] + - fields: [isPublic, category] + - fields: [userId, name] + unique: true + + relations: + - name: user + type: belongsTo + entity: User + field: userId + onDelete: Cascade + + acl: + create: [user] + read: [public_if_shared] + update: [self] + delete: [self] + rowLevel: "userId = $user.id OR isPublic = true" + + - name: ValidationRule + version: "1.0" + description: "Custom validation rules for input validation" + checksum: null + + fields: + id: + type: cuid + primary: true + generated: true + + userId: + type: string + required: true + index: true + + name: + type: string + required: true + maxLength: 100 + description: "Rule name" + + ruleType: + type: string + required: true + enum: [range, pattern, custom] + description: "Type of validation rule" + + config: + type: json + required: true + description: "Rule configuration (min, max, regex, function, etc.)" + + errorMessage: + type: string + required: true + maxLength: 200 + description: "Error message when validation fails" + + isActive: + type: boolean + default: true + description: "Whether this rule is currently active" + + createdAt: + type: bigint + required: true + + indexes: + - fields: [userId] + - fields: [ruleType] + + relations: + - name: user + type: belongsTo + entity: User + field: userId + onDelete: Cascade + + acl: + create: [user] + read: [self] + update: [self] + delete: [self] + rowLevel: "userId = $user.id" + + - name: ComponentState + version: "1.0" + description: "Persisted state for interactive components" + checksum: null + + fields: + id: + type: cuid + primary: true + generated: true + + userId: + type: string + required: true + index: true + + componentName: + type: string + required: true + enum: [ExpressionDemo, OperatorDemo, ResultDisplay] + description: "Which component this state belongs to" + + state: + type: json + required: true + description: "Serialized component state" + + isDefault: + type: boolean + default: false + description: "Whether this is the user's default state for this component" + + label: + type: string + nullable: true + maxLength: 50 + description: "Optional label for this saved state" + + createdAt: + type: bigint + required: true + + updatedAt: + type: bigint + nullable: true + + indexes: + - fields: [userId, componentName] + - fields: [userId, componentName, isDefault] + + relations: + - name: user + type: belongsTo + entity: User + field: userId + onDelete: Cascade + + acl: + create: [user] + read: [self] + update: [self] + delete: [self] + rowLevel: "userId = $user.id"