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
110 changes: 110 additions & 0 deletions .changeset/field-mapping-tri-source-c12.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
"@objectstack/spec": major
---

BREAKING(spec): `FieldMapping` named three declarations — the two domain-specific
sides are renamed to `ConnectorFieldMapping` and `ImportFieldMapping` (#4703, #4535 C12)

`FieldMapping` / `FieldMappingSchema` were exported by **three** entry points for
**three different declarations**, so which type you got depended only on the import
path — the #4411 trap, one entry worse than the usual pair:

| entry | declaration | keys | shape |
|:--|:--|:--|:--|
| `@objectstack/spec/shared` (**unchanged**) | `shared/mapping.zod.ts` | 4 | the base — plain `z.object` |
| `@objectstack/spec/integration` (**renamed**) | `integration/connector.zod.ts` | 7 | `Base.extend({ dataType, required, syncMode })` |
| `@objectstack/spec/data` (**renamed**) | `data/mapping.zod.ts` | 4 | an independent `strictObject` |

The first two are base-and-superset. The third is **not the same concept at all**: it
is the column mapping of a CSV/table import (`mapping.fieldMapping[]`), not a
connector's remote-field mapping. Three ways the two are mutually unparseable:

1. **`transform` is the same key name with incompatible value types.** `shared` /
`integration` take the discriminated union `FieldMappingTransformSchema`
(`{ type: 'cast', targetType: 'string' }`); `data` takes a flat `TransformType`
enum defaulting to `'none'`, steering a separate `params` bag.
2. **Different cardinality.** `data` accepts `string | string[]` for `source` and
`target` — one target field may be composed from several columns (`split` /
`join`). The other two accept a single `string`.
3. **Opposite failure modes for an unknown key.** `data` is a `strictObject`
(#4001): it **throws**, naming the canonical spelling. The other two are plain
`z.object`: they **strip silently**. Under one shared name, the same typo is a
hard error in one domain and a no-op in the other.

Per **ADR-0112 D9(a)** the domain-specific sides take a domain prefix and the base
keeps the bare name — the same ruling that produced `ConnectorRateLimitConfig`
(#4684), `ConnectorErrorCategory` and `ConnectorRetryStrategy`. This is not a new
convention: `data/ExternalFieldMappingSchema` already extends the same base and,
purely because it carries a prefix, never entered the dual-source baseline at all.

The dual-source baseline shrinks **16 → 14**.

## FROM → TO

```ts
// before — @objectstack/spec/integration
import { FieldMappingSchema, type FieldMapping } from '@objectstack/spec/integration';
// after
import {
ConnectorFieldMappingSchema,
type ConnectorFieldMapping,
} from '@objectstack/spec/integration';

// before — @objectstack/spec/data
import { FieldMappingSchema, type FieldMapping } from '@objectstack/spec/data';
// after
import {
ImportFieldMappingSchema,
type ImportFieldMapping,
} from '@objectstack/spec/data';
```

**Importing from `@objectstack/spec/shared`? Nothing changes** — that `FieldMapping`
is the base, keeps its name, its four keys and its plain-`z.object` behaviour.

No deprecated aliases are kept on either renamed entry: re-exporting the old name
would be a third declaration of it and would re-open the trap this change closes.

⚠️ **Do not "fix" the compile error by re-pointing the import at
`@objectstack/spec/shared`.** That name resolves, and it is the wrong schema. On the
connector side it silently costs you `dataType` / `required` / `syncMode` — the base
is not `.strict()`, so those keys are **stripped at parse time** and the mapping runs
without them. On the import side the base rejects arrays and the enum form of
`transform` outright. Take the prefixed name for the domain you are in.

## Authored metadata needs no migration

This renames TypeScript exports and two internal JSON Schema `$def`s — **not a single
authorable key**. All eleven keys carry over unchanged, verified by the
`authorable-surface.json` ratchet rather than by inspection:

- `connectors[].fieldMappings[]` — `source`, `target`, `transform`, `defaultValue`,
`dataType`, `required`, `syncMode` (7)
- `mapping.fieldMapping[]` — `source`, `target`, `transform`, `params` (4)

Same names, same types, same defaults, same strictness. Existing stack metadata,
stored `sys_metadata` rows and published apps are byte-for-byte unaffected, which is
why this ships with **no ADR-0087 conversion and no tombstone**: nothing was retired.
The `major` is for the two renamed TypeScript exports alone — the only edit an upgrade
needs is the import above.

The published JSON Schema `$id`s move with the defs:
`…/integration/FieldMapping.json` → `…/integration/ConnectorFieldMapping.json`, and
`…/data/FieldMapping.json` → `…/data/ImportFieldMapping.json`.

## Gate change riding along

`scripts/lib/renamed-defs.ts` (the #4684 carry-over table) gets its first entries
beyond the original one, and with them the first rules that only bind when the table
holds **more than one**:

- **two sources onto one target is rejected.** That is a merge, not two renames, and
it defeats the table's purpose: `build-schemas.ts` carries the snapshot into a map
keyed by the *new* key, so two defs' entries for one property name collapse — and
the surviving `[RETIRED]` state is whichever was carried last. A key live under one
def and tombstoned under the other would then read as already-retired, and the
"every live → retired transition needs a registered conversion" check would never
fire for it.
- **a chained rename (A → B → C) is rejected by name.** It was already red as
"B is not emitted", which is true but misdiagnoses it as a typo; the carry is a
single pass, so chains are unsupported outright.
2 changes: 1 addition & 1 deletion content/docs/getting-started/quick-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Core business logic and data modeling schemas.
| **[Validation](/docs/references/data/validation)** | `validation.zod.ts` | ValidationRule | Business validation rules |
| **[Datasource](/docs/references/data/datasource)** | `datasource.zod.ts` | Datasource, DriverDefinition | Database connection configs |
| **[Analytics](/docs/references/data/analytics)** | `analytics.zod.ts` | Analytics | Data analytics and aggregation |
| **[Mapping](/docs/references/data/mapping)** | `mapping.zod.ts` | FieldMapping | Field transformation mappings |
| **[Mapping](/docs/references/data/mapping)** | `mapping.zod.ts` | ImportFieldMapping | Field transformation mappings |
| **[Hook](/docs/references/data/hook)** | `hook.zod.ts` | Hook, HookEvent | Lifecycle event hooks |
| **[Data Engine](/docs/references/data/data-engine)** | `data-engine.zod.ts` | DataEngine | Data engine configuration |
| **[Driver](/docs/references/data/driver)** | `driver.zod.ts` | Driver, DriverCapabilities | Database driver interface |
Expand Down
8 changes: 4 additions & 4 deletions content/docs/references/data/mapping.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ green run.
## TypeScript Usage

```typescript
import { FieldMappingSchema, MappingSchema, TransformType } from '@objectstack/spec/data';
import type { FieldMapping, Mapping } from '@objectstack/spec/data';
import { ImportFieldMappingSchema, MappingSchema, TransformType } from '@objectstack/spec/data';
import type { ImportFieldMapping, Mapping } from '@objectstack/spec/data';

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

---

## FieldMapping
## ImportFieldMapping

### Properties

Expand Down
21 changes: 19 additions & 2 deletions content/docs/references/integration/connector.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ with simple `auth` — or by `[automation/sync.zod.ts](/docs/references/automati
## TypeScript Usage

```typescript
import { CircuitBreakerConfigSchema, ConnectorSchema, ConnectorActionSchema, ConnectorErrorCategorySchema, ConnectorHealthSchema, ConnectorRateLimitConfigSchema, ConnectorRetryStrategySchema, ConnectorStatusSchema, ConnectorTriggerSchema, ConnectorTypeSchema, DataSyncConfigSchema, DeclarativeConnectorEntrySchema, ErrorMappingConfigSchema, ErrorMappingRuleSchema, HealthCheckConfigSchema, RateLimitStrategySchema, RetryConfigSchema, SyncStrategySchema, WebhookConfigSchema, WebhookEventSchema, WebhookSignatureAlgorithmSchema } from '@objectstack/spec/integration';
import type { CircuitBreakerConfig, Connector, ConnectorErrorCategory, ConnectorHealth, ConnectorRateLimitConfig, ConnectorRetryStrategy, ConnectorStatus, ConnectorType, DataSyncConfig, DeclarativeConnectorEntry, ErrorMappingConfig, ErrorMappingRule, HealthCheckConfig, RateLimitStrategy, RetryConfig, SyncStrategy, WebhookConfig, WebhookEvent, WebhookSignatureAlgorithm } from '@objectstack/spec/integration';
import { CircuitBreakerConfigSchema, ConnectorSchema, ConnectorActionSchema, ConnectorErrorCategorySchema, ConnectorFieldMappingSchema, ConnectorHealthSchema, ConnectorRateLimitConfigSchema, ConnectorRetryStrategySchema, ConnectorStatusSchema, ConnectorTriggerSchema, ConnectorTypeSchema, DataSyncConfigSchema, DeclarativeConnectorEntrySchema, ErrorMappingConfigSchema, ErrorMappingRuleSchema, HealthCheckConfigSchema, RateLimitStrategySchema, RetryConfigSchema, SyncStrategySchema, WebhookConfigSchema, WebhookEventSchema, WebhookSignatureAlgorithmSchema } from '@objectstack/spec/integration';
import type { CircuitBreakerConfig, Connector, ConnectorErrorCategory, ConnectorFieldMapping, ConnectorHealth, ConnectorRateLimitConfig, ConnectorRetryStrategy, ConnectorStatus, ConnectorType, DataSyncConfig, DeclarativeConnectorEntry, ErrorMappingConfig, ErrorMappingRule, HealthCheckConfig, RateLimitStrategy, RetryConfig, SyncStrategy, WebhookConfig, WebhookEvent, WebhookSignatureAlgorithm } from '@objectstack/spec/integration';

// Validate data
const result = CircuitBreakerConfigSchema.parse(data);
Expand Down Expand Up @@ -223,6 +223,23 @@ Standard error category
* `integration_error`


---

## ConnectorFieldMapping

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **source** | `string` | ✅ | Source field name |
| **target** | `string` | ✅ | Target field name |
| **transform** | `{ type: 'constant'; value: any } \| { type: 'cast'; targetType: Enum<'string' \| 'number' \| 'boolean' \| 'date'> } \| { type: 'lookup'; table: string; keyField: string; valueField: string } \| { type: 'javascript'; expression: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object } } \| { type: 'map'; mappings: Record<string, any> }` | optional | Transformation to apply |
| **defaultValue** | `any` | optional | Default if source is null/undefined |
| **dataType** | `Enum<'string' \| 'number' \| 'boolean' \| 'date' \| 'datetime' \| 'json' \| 'array'>` | optional | Target data type |
| **required** | `boolean` | optional | Field is required |
| **syncMode** | `Enum<'read_only' \| 'write_only' \| 'bidirectional'>` | optional | Sync mode |


---

## ConnectorHealth
Expand Down
36 changes: 0 additions & 36 deletions content/docs/references/integration/mapping.mdx

This file was deleted.

1 change: 0 additions & 1 deletion content/docs/references/integration/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"---Connectors---",
"connector",
"connector-auth",
"mapping",
"---Transport & Storage---",
"offline"
]
Expand Down
8 changes: 4 additions & 4 deletions packages/spec/api-surface.json
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,6 @@
"FieldGroupCollapse (type)",
"FieldGroupSection (interface)",
"FieldInput (type)",
"FieldMapping (type)",
"FieldMappingSchema (const)",
"FieldNode (type)",
"FieldNodeSchema (const)",
"FieldOperators (type)",
Expand Down Expand Up @@ -411,6 +409,8 @@
"IMPORT_BOOLEAN_TRUE_TOKENS (const)",
"IMPORT_REFERENCE_TYPES (const)",
"INSTANT_TYPES (const)",
"ImportFieldMapping (type)",
"ImportFieldMappingSchema (const)",
"IndexSchema (const)",
"InstantValueSchema (const)",
"JSONValidation (type)",
Expand Down Expand Up @@ -3924,6 +3924,8 @@
"ConnectorDescriptor (interface)",
"ConnectorErrorCategory (type)",
"ConnectorErrorCategorySchema (const)",
"ConnectorFieldMapping (type)",
"ConnectorFieldMappingSchema (const)",
"ConnectorHealth (type)",
"ConnectorHealthSchema (const)",
"ConnectorInput (type)",
Expand Down Expand Up @@ -3958,8 +3960,6 @@
"ErrorMappingConfigSchema (const)",
"ErrorMappingRule (type)",
"ErrorMappingRuleSchema (const)",
"FieldMapping (type)",
"FieldMappingSchema (const)",
"HealthCheckConfig (type)",
"HealthCheckConfigSchema (const)",
"RateLimitStrategy (type)",
Expand Down
22 changes: 11 additions & 11 deletions packages/spec/authorable-surface.json
Original file line number Diff line number Diff line change
Expand Up @@ -3483,10 +3483,6 @@
"data/Field:unique",
"data/Field:visibleWhen",
"data/Field:widget",
"data/FieldMapping:params",
"data/FieldMapping:source",
"data/FieldMapping:target",
"data/FieldMapping:transform",
"data/FieldReference:$field",
"data/FilePersistenceConfig:autoSaveInterval",
"data/FilePersistenceConfig:path",
Expand Down Expand Up @@ -3537,6 +3533,10 @@
"data/HookContext:session",
"data/HookContext:transaction",
"data/HookContext:user",
"data/ImportFieldMapping:params",
"data/ImportFieldMapping:source",
"data/ImportFieldMapping:target",
"data/ImportFieldMapping:transform",
"data/Index:fields",
"data/Index:name",
"data/Index:partial",
Expand Down Expand Up @@ -4141,6 +4141,13 @@
"integration/ConnectorAction:key",
"integration/ConnectorAction:label",
"integration/ConnectorAction:outputSchema",
"integration/ConnectorFieldMapping:dataType",
"integration/ConnectorFieldMapping:defaultValue",
"integration/ConnectorFieldMapping:required",
"integration/ConnectorFieldMapping:source",
"integration/ConnectorFieldMapping:syncMode",
"integration/ConnectorFieldMapping:target",
"integration/ConnectorFieldMapping:transform",
"integration/ConnectorHealth:circuitBreaker",
"integration/ConnectorHealth:healthCheck",
"integration/ConnectorInstanceAPIKeyAuth:credentialRef",
Expand Down Expand Up @@ -4207,13 +4214,6 @@
"integration/ErrorMappingRule:targetCategory",
"integration/ErrorMappingRule:targetCode",
"integration/ErrorMappingRule:userMessage",
"integration/FieldMapping:dataType",
"integration/FieldMapping:defaultValue",
"integration/FieldMapping:required",
"integration/FieldMapping:source",
"integration/FieldMapping:syncMode",
"integration/FieldMapping:target",
"integration/FieldMapping:transform",
"integration/HealthCheckConfig:enabled",
"integration/HealthCheckConfig:endpoint",
"integration/HealthCheckConfig:expectedStatus",
Expand Down
2 changes: 0 additions & 2 deletions packages/spec/dual-source-exports.baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
"EnvironmentArtifactInput — [./cloud (type)] ≠ [./system (type)]",
"EnvironmentArtifactSchema — [./cloud (const)] ≠ [./system (const)]",
"EventSchema — [./automation (const)] ≠ [./kernel (const)]",
"FieldMapping — [./data (type)] ≠ [./integration (type)] ≠ [./shared (type)]",
"FieldMappingSchema — [./data (const)] ≠ [./integration (const)] ≠ [./shared (const)]",
"HttpMethod — [./api, ./shared (type)] ≠ [./ui (type)]",
"PackageDependency — [./cloud (type)] ≠ [./kernel (type)]",
"PackageDependencySchema — [./cloud (const)] ≠ [./kernel (const)]",
Expand Down
4 changes: 2 additions & 2 deletions packages/spec/json-schema.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,6 @@
"data/FeedFilterMode",
"data/FeedItemType",
"data/Field",
"data/FieldMapping",
"data/FieldNode",
"data/FieldReference",
"data/FieldType",
Expand All @@ -769,6 +768,7 @@
"data/HookBodyCapability",
"data/HookContext",
"data/HookEvent",
"data/ImportFieldMapping",
"data/Index",
"data/InstantValue",
"data/JSONValidation",
Expand Down Expand Up @@ -872,6 +872,7 @@
"integration/Connector",
"integration/ConnectorAction",
"integration/ConnectorErrorCategory",
"integration/ConnectorFieldMapping",
"integration/ConnectorHealth",
"integration/ConnectorInstanceAPIKeyAuth",
"integration/ConnectorInstanceAuth",
Expand All @@ -887,7 +888,6 @@
"integration/DeclarativeConnectorEntry",
"integration/ErrorMappingConfig",
"integration/ErrorMappingRule",
"integration/FieldMapping",
"integration/HealthCheckConfig",
"integration/RateLimitStrategy",
"integration/RetryConfig",
Expand Down
Loading
Loading