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
77 changes: 77 additions & 0 deletions .changeset/datasource-config-driver-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
'@objectstack/spec': minor
'@objectstack/service-datasource': minor
---

`datasource.config` is now validated against its driver's contract (#4410)

`config` was the one authorable slot on a datasource with no gate at all. The
schema's own comment claimed "the driver's own `configSchema` is what validates
it" — nothing did: both bundled driver specs set `configSchema: {}`, no code read
the field, and the per-driver zod schemas were not even exported from the
package. So `config: { hostname: 'db.internal' }` (the key is `host`) was
accepted in silence and the datasource connected to `localhost` while the parse,
the save and the connection probe all reported success.

`DatasourceSchema` now parses `config` — and each `readReplicas` entry — against
the contract for the declared driver, and `DatasourceAdminService`
(create/update/test, the Setup wizard's path) applies the same check. Both read
one registry in `@objectstack/spec/data`, which also projects each contract to
JSON Schema for `DriverDefinitionSchema.configSchema` and the Studio connection
form, so the form offers exactly the fields the validator accepts.

New exports from `@objectstack/spec/data`: `PostgresConfigSchema`,
`MysqlConfigSchema`, `SqliteConfigSchema`, `SqliteWasmConfigSchema`,
`MongoConfigSchema`, `MemoryConfigSchema`, plus `resolveDriverId`,
`getDriverConfigSchema`, `getDriverConfigJsonSchemaById` and
`validateDriverConfig`. A driver the platform ships no contract for (a plugin's
`com.vendor.snowflake`) keeps an unvalidated `config`.

**Migration.** A config that was silently ignored now fails with the correction
in the message. The renames:

| Wrote | Write instead | Driver |
| --- | --- | --- |
| `user` | `username` | postgres, mysql, mongo |
| `connectionString` / `dsn` | `url` | postgres, mysql, mongo |
| `uri` | `url` | mongo |
| `file` / `path` / `database` | `filename` | sqlite, sqlite-wasm |
| `hostname` | `host` | postgres, mysql, mongo |
| `searchPath` | `schema` | postgres |

And the relocations — keys that were never driver config:

| Wrote in `config` | Write instead |
| --- | --- |
| `min` / `max` / `idleTimeoutMillis` / `connectionTimeoutMillis` | the datasource's own `pool` block |
| `schemaMode` | next to `driver`, on the datasource |
| `readOnly` | `capabilities: { readOnly: true }` |
| `ssl: { ca, cert, key, rejectUnauthorized }` | the datasource's own `ssl` block — inside `config`, `ssl` is the on/off boolean shorthand |

Two memory-driver keys are **removed**: `indexes` and `maxRecordsPerObject`.
`InMemoryDriverConfig` has no field for either — the driver keeps no indexes and
evicts nothing — so both were inert. Drop them; for real indexing use a driver
that indexes.

A postgres, mysql or mongo datasource must now name a connection target
(`database`, or a `url` that carries it). An empty `config` used to mean "the
client's own localhost default", which is the same defect in its most complete
form.

**Also fixed, because the contract can only be enforced where it is honoured.**
These keys were declared and read by nothing; they now reach the driver:

- `datasource.pool` is honoured by every SQL driver (it was declared, carried
into the connection spec, then overwritten with a hardcoded `{ min: 0, max: 5 }`),
and maps onto the Mongo client's `minPoolSize` / `maxPoolSize`.
- `datasource.schemaMode` reaches the driver. It was dropped between the
datasource record and the connection spec, so a `schemaMode: 'external'`
database — one ObjectStack must never run DDL against — was constructed as
`managed`.
- `datasource.ssl` reaches the SQL clients, certificates and all. It stopped at
the record — nothing put it on the connection spec — so a TLS block configured
nothing, which is exactly what its own schema comment warns about ("a TLS
setting that never took effect looked identical to one that did").
- postgres `schema` (knex `searchPath`), `applicationName` and `statementTimeout`.
- mongo `password`, `authSource` and `options`. A mongo datasource carrying a
`config.password` previously composed its URL with an **empty** password.
50 changes: 50 additions & 0 deletions content/docs/data-modeling/drivers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,56 @@ actually connect to Turso.
> Knex client name (`pg` / `mysql2` / `better-sqlite3`) when you instantiate
> `SqlDriver`.

## `config` is validated per driver

A datasource's `config` is driver-specific — a SQLite `filename` and a Postgres
`host` share no shape — so the datasource schema keeps that slot open at the top
level and parses it against the contract for the driver you named. Each built-in
driver ships that contract as a zod schema, exported from `@objectstack/spec/data`:

| `driver` | Contract | Keys |
| :--- | :--- | :--- |
| `postgres` \| `postgresql` \| `pg` | `PostgresConfigSchema` | `url`, `host`, `port`, `database`, `username`, `password`, `ssl`, `schema`, `applicationName`, `statementTimeout`, `autoMigrate` |
| `mysql` \| `mysql2` \| `mariadb` | `MysqlConfigSchema` | `url`, `host`, `port`, `database`, `username`, `password`, `ssl`, `autoMigrate` |
| `sqlite` \| `sqlite3` | `SqliteConfigSchema` | `filename`, `autoMigrate` |
| `sqlite-wasm` \| `wasm-sqlite` | `SqliteWasmConfigSchema` | `filename`, `persist` |
| `mongo` \| `mongodb` | `MongoConfigSchema` | `url`, `host`, `port`, `database`, `username`, `password`, `authSource`, `options` |
| `memory` \| `in-memory` | `MemoryConfigSchema` | `initialData`, `strictMode`, `persistence` |

An unrecognised key is rejected with its correction, at authoring time and in the
Setup → Datasources wizard alike:

```text
Unrecognized key(s) on this postgres datasource's config: `hostname`.
Did you mean `hostname` → `host`?
```

This matters more than a typical typo check, because the failure it replaces was
silent: a misspelled key was dropped, the driver fell back to its own defaults,
and the datasource connected to `localhost` while every signal — the parse, the
save, the connection probe — reported success.

Two things live **outside** `config`, because they are not driver-specific:

- **Pool sizing** — the `pool` block on the datasource (`min`, `max`,
`idleTimeoutMillis`, `connectionTimeoutMillis`), honoured for every SQL driver
and mapped onto the Mongo client's `minPoolSize` / `maxPoolSize`.
- **TLS certificates** — the `ssl` block on the datasource (`enabled`,
`rejectUnauthorized`, `ca`, `cert`, `key`). Inside `config`, `ssl` is the
on/off boolean shorthand.
- **`schemaMode`** — the ADR-0015 ownership mode, declared next to `driver`.

A plugin-contributed driver (`com.vendor.snowflake`) has no contract in this
repo, so its `config` is left unvalidated rather than judged against a shape the
platform does not have.

<Callout type="info">
The same schemas are projected to JSON Schema for
`DriverDefinitionSchema.configSchema` and for `GET /api/v1/datasources/drivers`,
which the Studio connection form renders — so the form offers exactly the fields
the validator accepts.
</Callout>

## Startup: a driver that cannot connect aborts the boot

`ObjectQLEngine.init()` connects every registered driver during kernel
Expand Down
2 changes: 1 addition & 1 deletion content/docs/data-modeling/external-datasources.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const Warehouse = defineDatasource({
label: 'Analytics Warehouse (Postgres)',
driver: 'postgres',
schemaMode: 'external', // ObjectStack never runs DDL here
config: { host: 'db.internal', port: 5432, database: 'analytics', user: 'readonly' },
config: { host: 'db.internal', port: 5432, database: 'analytics', username: 'readonly' },
external: {
allowWrites: false, // read-only (the default)
credentialsRef: 'sys_secret:9f2c…', // opaque handle minted by the secret store
Expand Down
4 changes: 0 additions & 4 deletions content/docs/references/api/connector.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ description: Connector protocol schemas

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

<Callout type="info">
**Source:** `packages/spec/src/api/connector.zod.ts`
</Callout>

## TypeScript Usage

```typescript
Expand Down
4 changes: 0 additions & 4 deletions content/docs/references/api/core-services.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ description: Core Services protocol schemas

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

<Callout type="info">
**Source:** `packages/spec/src/api/core-services.zod.ts`
</Callout>

## TypeScript Usage

```typescript
Expand Down
4 changes: 0 additions & 4 deletions content/docs/references/api/http.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ description: Http protocol schemas

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

<Callout type="info">
**Source:** `packages/spec/src/api/http.zod.ts`
</Callout>

## TypeScript Usage

```typescript
Expand Down
4 changes: 0 additions & 4 deletions content/docs/references/api/identity.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ description: Identity protocol schemas

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

<Callout type="info">
**Source:** `packages/spec/src/api/identity.zod.ts`
</Callout>

## TypeScript Usage

```typescript
Expand Down
4 changes: 0 additions & 4 deletions content/docs/references/api/metadata-plugin.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ description: Metadata Plugin protocol schemas

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

<Callout type="info">
**Source:** `packages/spec/src/api/metadata-plugin.zod.ts`
</Callout>

## TypeScript Usage

```typescript
Expand Down
4 changes: 0 additions & 4 deletions content/docs/references/api/notification.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ description: Notification protocol schemas

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

<Callout type="info">
**Source:** `packages/spec/src/api/notification.zod.ts`
</Callout>

## TypeScript Usage

```typescript
Expand Down
4 changes: 0 additions & 4 deletions content/docs/references/api/package-registry.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ description: Package Registry protocol schemas

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

<Callout type="info">
**Source:** `packages/spec/src/api/package-registry.zod.ts`
</Callout>

## TypeScript Usage

```typescript
Expand Down
4 changes: 0 additions & 4 deletions content/docs/references/automation/connector.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ description: Connector protocol schemas

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

<Callout type="info">
**Source:** `packages/spec/src/automation/connector.zod.ts`
</Callout>

## TypeScript Usage

```typescript
Expand Down
31 changes: 31 additions & 0 deletions content/docs/references/automation/events-core.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
title: Events Core
description: Events Core protocol schemas
---

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

## TypeScript Usage

```typescript
import { Event } from '@objectstack/spec/automation';
import type { Event } from '@objectstack/spec/automation';

// Validate data
const result = Event.parse(data);
```

---

## Event

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **type** | `string` | ✅ | Event Type (e.g. "APPROVE", "REJECT", "Submit") |
| **schema** | `Record<string, any>` | optional | Expected event payload structure |


---

4 changes: 0 additions & 4 deletions content/docs/references/automation/job.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ description: Job protocol schemas

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

<Callout type="info">
**Source:** `packages/spec/src/automation/job.zod.ts`
</Callout>

## TypeScript Usage

```typescript
Expand Down
1 change: 1 addition & 0 deletions content/docs/references/automation/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"job",
"---More---",
"builtin-node-config",
"events-core",
"flow-function",
"io-node-config",
"schemaless-node-config"
Expand Down
4 changes: 0 additions & 4 deletions content/docs/references/automation/offline.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ description: Offline protocol schemas

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

<Callout type="info">
**Source:** `packages/spec/src/automation/offline.zod.ts`
</Callout>

## TypeScript Usage

```typescript
Expand Down
16 changes: 2 additions & 14 deletions content/docs/references/automation/state-machine.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ Prevent AI "hallucinations" by enforcing valid valid transitions.
## TypeScript Usage

```typescript
import { ActionRef, Event, GuardRef, StateMachine, StateNode, Transition } from '@objectstack/spec/automation';
import type { ActionRef, Event, GuardRef, StateMachine, StateNode, Transition } from '@objectstack/spec/automation';
import { ActionRef, GuardRef, StateMachine, StateNode, Transition } from '@objectstack/spec/automation';
import type { ActionRef, GuardRef, StateMachine, StateNode, Transition } from '@objectstack/spec/automation';

// Validate data
const result = ActionRef.parse(data);
Expand Down Expand Up @@ -53,18 +53,6 @@ Type: `string`
---


---

## Event

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **type** | `string` | ✅ | Event Type (e.g. "APPROVE", "REJECT", "Submit") |
| **schema** | `Record<string, any>` | optional | Expected event payload structure |


---

## GuardRef
Expand Down
4 changes: 0 additions & 4 deletions content/docs/references/cloud/plugin-security.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ description: Plugin Security protocol schemas

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

<Callout type="info">
**Source:** `packages/spec/src/cloud/plugin-security.zod.ts`
</Callout>

## TypeScript Usage

```typescript
Expand Down
4 changes: 0 additions & 4 deletions content/docs/references/cloud/provisioning.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ description: Provisioning protocol schemas

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

<Callout type="info">
**Source:** `packages/spec/src/cloud/provisioning.zod.ts`
</Callout>

## TypeScript Usage

```typescript
Expand Down
Loading
Loading