Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions content/docs/guides/airtable-dashboard-analysis.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,7 @@ export const DashboardHeader: React.FC<DashboardHeaderProps> = ({
### 5.1 Full Dashboard Definition (TypeScript)

```typescript
import { Dashboard } from '@objectstack/spec';
import { Dashboard } from '@objectstack/spec/ui';

export const salesPerformanceDashboard = Dashboard.create({
name: 'sales_performance',
Expand Down Expand Up @@ -1068,7 +1068,6 @@ For reference, here's the same dashboard definition in pure JSON format:

- [Dashboard Metadata Guide](/docs/guides/metadata/dashboard) — Current dashboard protocol documentation
- [Chart Configuration Reference](/docs/references/ui/chart) — Chart type taxonomy and configuration
- [Airtable Interface Gap Analysis](/docs/design/airtable-interface-gap-analysis) — Interface-level gap analysis (superseded)

---

Expand Down
24 changes: 15 additions & 9 deletions content/docs/guides/analytics-datasets.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -167,15 +167,21 @@ measure values by hand:

## Migrating from inline queries

ADR-0021's terminal state is **one** author-facing shape. The migration runs in
two steps so it can be verified safely:

1. **Dual-form (additive).** A report/widget keeps its legacy inline query AND
gains a `dataset` binding. A read-only reconciliation harness asserts both
forms return identical numbers (the financial-correctness gate).
2. **Single-form (terminal).** Once every surface reconciles and `grep` shows no
inline residue, the inline query fields and `ListChartConfigSchema` are
removed and the union collapses to the single dataset shape.
ADR-0021's terminal state is **one** author-facing shape, and the cutover is now
complete: `report`, `dashboard` widgets, and list-view charts are all
dataset-bound today (`ReportSchema`, `DashboardWidgetSchema`,
`ListChartConfigSchema` in `packages/spec/src/ui`). The migration ran in two
steps so it could be verified safely:

1. **Dual-form (additive).** A report/widget kept its legacy inline query AND
gained a `dataset` binding. A read-only reconciliation harness asserted both
forms returned identical numbers (the financial-correctness gate).
2. **Single-form (terminal).** Once every surface reconciled, the inline query
fields (`objectName`/`columns`/`groupings` on reports, `valueField`/`aggregate`
on widgets, `xAxisField`/`yAxisFields`/`aggregation` on list charts) were
removed and each schema collapsed to the single dataset shape. The
presentation schemas themselves were kept — only their inline query fields
went away.

Author new analytics directly in dataset form; reach for a **named** dataset when
a metric is shared or must be certified, and an inline anonymous dataset for a
Expand Down
27 changes: 18 additions & 9 deletions content/docs/guides/cheatsheets/backward-compatibility.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -111,26 +111,35 @@ v4.0.0 → Feature removed (earliest possible removal)

## Migration Support

### Automated Migrations
### Linting Your Project

ObjectStack provides tooling to ease upgrades across MAJOR versions:
The CLI's `lint` command validates your metadata against the current schema and surfaces issues. Adding `--fix` prints the suggested fix for each issue (dry-run; it does not modify files):

```bash
# Check for deprecated usage in your project
npx @objectstack/cli lint --deprecations
# Validate metadata against the current schema
os lint

# Auto-migrate to the latest schema version
npx @objectstack/cli migrate --from 3.x --to 4.x
# Show what could be auto-fixed (dry-run)
os lint --fix

# Scan your source for deprecated ObjectStack patterns
os doctor --scan-deprecations

# Compare two configs and detect breaking changes between versions
os diff <before> <after> --breaking-only
```

<Callout type="info">
There is no `lint --deprecations` flag — deprecation scanning lives on `os doctor --scan-deprecations`. An automated `migrate`/`codemod` command is referenced in the migration guides but is not yet available; until then, combine `os doctor --scan-deprecations`, `os diff`, and the CHANGELOG migration notes (below) to upgrade across MAJOR versions.
</Callout>

### Migration Guides

Each MAJOR release includes:

- **CHANGELOG.md** — Complete list of changes with migration notes
- **RELEASE_NOTES.md** — High-level summary and upgrade instructions
- **Codemods** — Automated code transformations where possible
- **Schema Diff** — Machine-readable diff of all schema changes
- **Schema Diff** — Use `os diff <before> <after>` to produce a machine-readable (`--json`) diff of breaking changes between two configs

### Support Windows

Expand Down Expand Up @@ -160,7 +169,7 @@ The `@objectstack/spec` package provides additional stability guarantees:

### Runtime Behavior

- `z.parse()` results are guaranteed consistent within a MAJOR version
- `Schema.parse()` results are guaranteed consistent within a MAJOR version
- Default values applied by `.default()` are stable within a MAJOR version
- Validation error messages may change in MINOR/PATCH releases (do not rely on exact error text)

Expand Down
15 changes: 8 additions & 7 deletions content/docs/guides/cheatsheets/error-catalog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ ObjectStack uses a structured error system with **9 error categories** and **41+

<Callout type="info">
**Source:** `packages/spec/src/api/errors.zod.ts`
**Import:** `import { StandardErrorCodeSchema, ErrorCategorySchema, ErrorResponseSchema } from '@objectstack/spec/api'`
**Import:** `import { StandardErrorCode, ErrorCategory, ErrorResponseSchema } from '@objectstack/spec/api'`
</Callout>

---
Expand Down Expand Up @@ -63,7 +63,7 @@ ObjectStack uses a structured error system with **9 error categories** and **41+

### `invalid_field`
**Cause:** A field name in the request does not exist on the target object.
**Fix:** Check the object schema for valid field names. Use `objectstack explain <object>` to see available fields.
**Fix:** Check the object schema for valid field names. Use `os meta get object <name>` to inspect the object's fields.
**Retry:** `no_retry`

### `missing_required_field`
Expand Down Expand Up @@ -210,7 +210,7 @@ ObjectStack uses a structured error system with **9 error categories** and **41+

### `object_not_found`
**Cause:** The specified object (table/entity) does not exist in the schema.
**Fix:** Check the object name. Use `objectstack explain` to list available objects.
**Fix:** Check the object name. Use `os meta list object` to list available objects.
**Retry:** `no_retry`

### `record_not_found`
Expand Down Expand Up @@ -372,10 +372,11 @@ interface EnhancedApiError {

```typescript
interface FieldError {
field: string; // Field name that has the error
message: string; // Human-readable error for this field
code?: string; // Field-level error code
value?: unknown; // The invalid value (if safe to include)
field: string; // Field path (supports dot notation)
code: StandardErrorCode; // Field-level error code
message: string; // Human-readable error for this field
value?: unknown; // The invalid value (if safe to include)
constraint?: unknown; // The constraint that was violated (e.g., max length)
}
```

Expand Down
61 changes: 28 additions & 33 deletions content/docs/guides/cheatsheets/field-type-decision-tree.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ flowchart TD
TEXT -->|Secret value| T8[password]

%% Number branch
NUM -->|Decimal number| N1[number]
NUM -->|Whole number| N2[integer]
NUM -->|Any number| N1[number]
NUM -->|Money amount| N3[currency]
NUM -->|Percentage| N4[percent]
NUM -->|Star rating| N5[rating]
NUM -->|Slider input| N6[slider]

%% Date branch
DATE -->|Date only| D1[date]
Expand All @@ -61,28 +61,26 @@ flowchart TD
REL -->|Reference another object| R1[lookup]
REL -->|Parent-child cascade| R2[master_detail]
REL -->|Self-referencing hierarchy| R3[tree]
REL -->|Multiple object types| R4[polymorphic]

%% File branch
FILE -->|Any file| F1[file]
FILE -->|Image with preview| F2[image]
FILE -->|Multiple files| F3[attachment]
FILE -->|User profile picture| F3[avatar]

%% Computed branch
COMP -->|Calculated value| K1[formula]
COMP -->|Aggregate children| K2[rollup]
COMP -->|Aggregate children| K2[summary]
COMP -->|Auto-incrementing ID| K3[autonumber]

%% Structured branch
STRUCT -->|Postal address| S1[address]
STRUCT -->|GPS coordinates| S2[location]
STRUCT -->|Color picker| S3[color]
STRUCT -->|Arbitrary JSON| S4[json]
STRUCT -->|Sensitive data| S5[encrypted]
STRUCT -->|Sensitive secret value| S5[secret]

%% AI branch
AI -->|Vector embedding| A1[vector]
AI -->|Text embedding| A2[embedding]
AI -->|Vector embedding for semantic search| A1[vector]

style START fill:#4f46e5,color:#fff
style T1 fill:#059669,color:#fff
Expand All @@ -94,10 +92,10 @@ flowchart TD
style T7 fill:#059669,color:#fff
style T8 fill:#059669,color:#fff
style N1 fill:#d97706,color:#fff
style N2 fill:#d97706,color:#fff
style N3 fill:#d97706,color:#fff
style N4 fill:#d97706,color:#fff
style N5 fill:#d97706,color:#fff
style N6 fill:#d97706,color:#fff
style D1 fill:#7c3aed,color:#fff
style D2 fill:#7c3aed,color:#fff
style D3 fill:#7c3aed,color:#fff
Expand All @@ -110,7 +108,6 @@ flowchart TD
style R1 fill:#0284c7,color:#fff
style R2 fill:#0284c7,color:#fff
style R3 fill:#0284c7,color:#fff
style R4 fill:#0284c7,color:#fff
style F1 fill:#be185d,color:#fff
style F2 fill:#be185d,color:#fff
style F3 fill:#be185d,color:#fff
Expand All @@ -123,7 +120,6 @@ flowchart TD
style S4 fill:#0d9488,color:#fff
style S5 fill:#0d9488,color:#fff
style A1 fill:#6d28d9,color:#fff
style A2 fill:#6d28d9,color:#fff
```

---
Expand All @@ -141,17 +137,18 @@ flowchart TD
| `email` | Email address with validation | `email`, `contact_email` |
| `url` | Web URL with validation | `website`, `linkedin_url` |
| `phone` | Phone number | `phone`, `mobile` |
| `password` | Secret/masked value (stored encrypted) | `api_key`, `secret` |
| `password` | One-way hashed credential (owned by auth subsystem) | `user_password` |
| `secret` | Reversible encrypted-at-rest value, masked on read | `api_key`, `db_password` |

### Number Types

| Type | Use When | Example |
|:---|:---|:---|
| `number` | Any decimal number | `price`, `weight`, `score` |
| `integer` | Whole numbers only | `quantity`, `age`, `count` |
| `number` | Any numeric value (set `precision`/`scale` for decimals) | `price`, `weight`, `quantity` |
| `currency` | Money amounts with currency code | `amount`, `total_price` |
| `percent` | Percentage values (0–100 or 0–1) | `completion`, `discount` |
| `rating` | Star ratings (typically 1–5) | `satisfaction`, `difficulty` |
| `slider` | Numeric value via slider UI | `volume`, `priority_level` |

### Date & Time Types

Expand Down Expand Up @@ -179,7 +176,6 @@ flowchart TD
| `lookup` | Reference another object (soft link) | `assigned_to → user` |
| `master_detail` | Parent-child with cascade delete | `line_item → order` |
| `tree` | Self-referencing hierarchy | `category → category` |
| `polymorphic` | Reference multiple object types | `related_to → task\|project\|user` |

<Callout type="tip">
**lookup vs master_detail:** Use `lookup` when the child can exist independently. Use `master_detail` when deleting the parent should delete all children (e.g., order → line items).
Expand All @@ -189,16 +185,16 @@ flowchart TD

| Type | Use When | Example |
|:---|:---|:---|
| `file` | Any single file upload | `contract`, `resume` |
| `image` | Image with preview/thumbnail | `avatar`, `product_photo` |
| `attachment` | Multiple file uploads | `documents`, `screenshots` |
| `file` | File upload (set `multiple: true` for many files) | `contract`, `documents` |
| `image` | Image with preview/thumbnail | `product_photo`, `screenshots` |
| `avatar` | User profile picture | `avatar`, `profile_photo` |

### Computed Types

| Type | Use When | Example |
|:---|:---|:---|
| `formula` | Value calculated from other fields | `full_name = first + last` |
| `rollup` | Aggregate from child records | `total_amount = SUM(items.price)` |
| `summary` | Roll-up aggregate from child records | `total_amount = sum(items.price)` |
| `autonumber` | Auto-incrementing display ID | `ticket_number: TICK-0001` |

### Structured Data Types
Expand All @@ -209,14 +205,13 @@ flowchart TD
| `location` | GPS coordinates (lat/lng) | `office_location`, `delivery_point` |
| `color` | Hex color value with picker | `brand_color`, `label_color` |
| `json` | Arbitrary structured data | `configuration`, `metadata` |
| `encrypted` | Sensitive data at rest | `ssn`, `tax_id` |
| `code` | Code editor (JSON/SQL/JS) | `query_body`, `transform` |

### AI & ML Types

| Type | Use When | Example |
|:---|:---|:---|
| `vector` | High-dimensional vector for similarity search | `content_embedding` |
| `embedding` | Text embedding for semantic search | `description_embedding` |
| `vector` | High-dimensional embedding for similarity search (semantic search, RAG) | `content_embedding` |

---

Expand All @@ -235,28 +230,28 @@ Can't find your use case above? Check this table:
| Feature flags | `json` | Arbitrary key-value configuration |
| User avatar | `image` | Single image with thumbnail support |
| Support ticket ID | `autonumber` | Auto-incrementing human-readable identifier |
| Total order value | `rollup` | SUM of child line item amounts |
| Total order value | `summary` | Roll-up sum of child line item amounts |
| Discount percentage | `percent` | 0–100 range with `%` display |
| Terms accepted | `boolean` | Simple true/false, no UI toggle needed |
| Search index | `vector` | Used for AI similarity search |
| OAuth token | `encrypted` | Sensitive data stored encrypted at rest |
| OAuth token | `secret` | Reversible value stored encrypted at rest |

---

## Decision Summary by Question

| Question | Answer → Field Type |
|:---|:---|
| "Can the user type free text?" | `text`, `textarea`, `richtext`, `markdown` |
| "Is it a number I need to calculate with?" | `number`, `integer`, `currency`, `percent` |
| "Can the user type free text?" | `text`, `textarea`, `richtext`, `markdown`, `html` |
| "Is it a number I need to calculate with?" | `number`, `currency`, `percent`, `slider` |
| "Does the user pick from a list?" | `select`, `multiselect`, `radio`, `checkboxes` |
| "Does it reference another record?" | `lookup`, `master_detail`, `tree`, `polymorphic` |
| "Is it a file the user uploads?" | `file`, `image`, `attachment` |
| "Is the value computed automatically?" | `formula`, `rollup`, `autonumber` |
| "Does it have internal structure?" | `address`, `location`, `color`, `json` |
| "Is it sensitive/secret?" | `password`, `encrypted` |
| "Is it for AI/ML?" | `vector`, `embedding` |
| "Does it reference another record?" | `lookup`, `master_detail`, `tree` |
| "Is it a file the user uploads?" | `file`, `image`, `avatar` |
| "Is the value computed automatically?" | `formula`, `summary`, `autonumber` |
| "Does it have internal structure?" | `address`, `location`, `color`, `json`, `code` |
| "Is it sensitive/secret?" | `password`, `secret` |
| "Is it for AI/ML?" | `vector` |

<Callout type="info">
**Still unsure?** Start with `text` for strings or `number` for numerics. You can always change the field type later ObjectStack handles data migration automatically for compatible type changes.
**Still unsure?** Start with `text` for strings or `number` for numerics. You can change the field type later, but ObjectStack does not auto-convert existing data: compatible changes pass cleanly, while narrowing or incompatible changes surface a build-time warning that existing values may not convert cleanly.
</Callout>
Loading