Skip to content

Commit 27b70c2

Browse files
os-zhuangclaude
andauthored
docs(spec): SYNC_ARCHITECTURE.md L3 段停止宣传字段映射的值转换能力 (#6384) (#6395)
`FieldMapping.transform` —— 作者面上写作 `connector.fieldMappings[].transform` 与 `externalLookup.fieldMappings[].transform` —— 连同整个五成员 `FieldMappingTransform` 联合(`constant` / `cast` / `lookup` / `javascript` / `map`)已在 @objectstack/spec 17.0.0 按 #5552 / ADR-0049 退役:五个成员没有任何 一个有执行器,`javascript` 成员还在推荐 #3278 已退役的 `js` dialect。文档 L303 示例块的墓碑注释早已写对,散文却没跟着改 —— 与 #5554 / PR #6388 同型,只是换了 一次退役。 正文点名一处(Key Features 打勾行),实测为四处,全部改为如实说法: - L198 Key Features:打勾行 `With transformations and data type conversion` —— `data type conversion` 那半是真的(`dataType`),`With transformations` 那半不是。⛔ 未删行,改为 `dataType` 目标类型 + `syncMode` 逐字段方向,并在 行内显式写出 **no value transformation**,另加引用块。这里用显式否定而非静默 删除:删掉只是不再重复该断言,消不掉读者已经形成的"连接器字段映射能做值转换" 这一信念 —— 而这个信念比编译不过更贵,它会把值转换逻辑规划到一个不执行它的面上。 - L287 示例块内注释:`// Field Mappings with Transformations.` —— 与 15 行后 自己的墓碑注释直接矛盾。改为 `dataType` / `syncMode`,并指向那条墓碑。 - L387 Decision Matrix:⛔ 未删行(joins/aggregations → L2 那半是对的)。原行只 问 `complex transformations`,想做逐字段值转换的作者不会把自己读进"complex", 于是落到 L3 —— 正是本 issue 描述的失败路径。改写为"是否需要转换值(无论复杂 与否)",并写明 **Not** L3。 - L430 Migration Guide L3→L2 引导语:同上,`complex transformations` → "需要转换值(joins/aggregations,或 `fieldMappings` 做不到的逐字段转换)"。 措辞全部复用 `shared/mapping.zod.ts:89` 墓碑现成句,不另造第二种说法(同一次 退役出现两种描述,正是它们日后互相矛盾的成因)。 保留的每一条都对着 schema 核过,不是假定: - `ConnectorFieldMappingSchema`(`connector.zod.ts:121`)= `BaseFieldMappingSchema .extend({ dataType, required, syncMode })`,新增的确实只有这三个键;基类 `FieldMappingSchema`(`shared/mapping.zod.ts:72`)的 `transform` 是 `retiredKey(...)` 墓碑。故行文只说 schema **declares** `dataType`,不宣称运行时 执行 —— 无 `connector` liveness 记录可支撑执行侧断言。 - 墓碑指定的去处真实存在:`MappingSchema.fieldMapping` (`data/mapping.zod.ts:224`)= `z.array(ImportFieldMappingSchema)`,其 `transform: TransformType.default('none')`(:140)+ `params`(:147); `TransformType`(:84)含 `javascript`,而 REST import path 对它回 400 —— 故沿用 墓碑那份六成员列表 + "rejects `javascript` with a 400" 的补语,列表才是诚实的。 - `os migrate meta --from 16` 存在(`packages/cli/src/commands/migrate/meta.ts:146`), 且转换 `field-mapping-transform-removed` 的 `toMajor: 17` (`conversions/registry.ts:4197`)—— `--from 16` 是对的入口。 纯散文改动,未新增/删除任何 ```typescript 块:`grep -c '^```typescript'` 实测仍 为 6,`etl-author-shape.test.ts` 与 `connector-author-shape.test.ts` 两个门禁 30 tests 实跑通过(非假定);packages/spec 全量 338 files / 8644 tests 亦通过。 `packages/spec/docs/` 不在该包 `package.json` 的 `files` 白名单 (`dist`/`json-schema`/`liveness`/`prompts`/`llms.txt`/`README.md`/ `src/**/*.zod.ts`/`CHANGELOG.md`/`api-surface`/`spec-changes.json`)内,不随包 发布,故不写 changeset,改用 `skip-changeset` 标签。 Fixes #6384 Claude-Session: https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5 Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 643b7c7 commit 27b70c2

1 file changed

Lines changed: 26 additions & 4 deletions

File tree

packages/spec/docs/SYNC_ARCHITECTURE.md

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ Complete, production-grade integration with external systems. Includes authentic
195195
-**Authentication**: OAuth2, JWT, SAML, API Key, Basic Auth
196196
-**Webhooks**: Bidirectional event notifications
197197
-**Retry Policies**: Exponential backoff, circuit breaker
198-
-**Field Mapping**: With transformations and data type conversion
198+
-**Field Mapping**: `dataType` target type and `syncMode` per-field direction —
199+
**no value transformation**; see below
199200
-**Conflict Resolution**: Multiple strategies (`ConnectorConflictResolution`)
200201
-**Security**: Signature verification, encryption
201202
-**Monitoring**: Health checks, metrics, logging
@@ -215,6 +216,25 @@ Complete, production-grade integration with external systems. Includes authentic
215216
> rate-limited upstream is `retryConfig` — whose `retryableStatusCodes` default
216217
> `[408, 429, 500, 502, 503, 504]` includes `429` — and `health.circuitBreaker`.
217218
219+
> **Field mapping does not transform values.** The ticked line above used to read
220+
> "With transformations and data type conversion". Only the second half was ever
221+
> true: `ConnectorFieldMappingSchema` (`integration/connector.zod.ts`) extends the
222+
> base mapping with exactly three keys — `dataType`, `required` and `syncMode`.
223+
> `FieldMapping.transform` — authored as `connector.fieldMappings[].transform` and
224+
> `externalLookup.fieldMappings[].transform` — was removed in `@objectstack/spec`
225+
> 17.0.0 (#5552, ADR-0049), and the whole `FieldMappingTransform` union went with
226+
> it (`constant` / `cast` / `lookup` / `javascript` / `map`) — **no runtime ever
227+
> executed any of the five**, and the `javascript` member advertised
228+
> `dialect: "js"`, a dialect retired in #3278. An L3 connector mapping moves a
229+
> value from `source` to `target`; it does not compute one. **Value conversion
230+
> belongs on a surface that runs it:** the L2 import mapping's own `transform`
231+
> (`mapping.fieldMapping[].transform` in `data/mapping.zod.ts` — a string enum,
232+
> `none`/`constant`/`map`/`split`/`join`/`lookup`, with its settings in `params`),
233+
> applied row by row by the REST import path, which rejects its own `javascript`
234+
> value with a 400 rather than pretending to run it — or an ETL transformation
235+
> step (L2 above). Already authored the retired key? `os migrate meta --from 16`
236+
> rewrites it.
237+
218238
### Use Cases
219239

220240
1. **Enterprise SAP Integration** - Full bidirectional sync with complex business logic
@@ -284,7 +304,8 @@ const sapConnector: ConnectorInput = {
284304
deleteMode: 'soft_delete'
285305
},
286306

287-
// Field Mappings with Transformations.
307+
// Field Mappings — `dataType` target type and `syncMode` direction. There is
308+
// no value transformation here; see the tombstone on the second entry.
288309
// The keys are `source` / `target` — the canonical spelling of the base
289310
// protocol in `shared/mapping.zod.ts`, which every mapping surface extends.
290311
fieldMappings: [
@@ -384,7 +405,7 @@ const sapConnector: ConnectorInput = {
384405

385406
| Question | Answer → Level |
386407
|----------|----------------|
387-
| Do you need complex transformations (joins, aggregations)? | **Yes** → L2 (ETL) |
408+
| Do you need to transform values at all — joins and aggregations, or just a per-field convert? | **Yes** → L2 (ETL) for joins/aggregations, or the import mapping's `fieldMapping[].transform` for per-field conversion. **Not** L3: a connector's `fieldMappings` declares `dataType` and `syncMode` and performs no value transformation (#5552) |
388409
| Do you need multi-source aggregation? | **Yes** → L2 (ETL) |
389410
| Do you need real-time webhooks? | **Yes** → L3 (Connector) |
390411
| Do you need advanced authentication (OAuth2, SAML)? | **Yes** → L3 (Connector) |
@@ -427,7 +448,8 @@ Combine levels for complex scenarios.
427448

428449
### From L3 (`syncConfig`) to L2
429450

430-
When a connector's declarative sync needs complex transformations:
451+
When a connector's declarative sync needs to transform values — joins and
452+
aggregations, or a per-field convert that `fieldMappings` cannot do (#5552):
431453

432454
**Before (L3 `syncConfig`):**
433455
```typescript

0 commit comments

Comments
 (0)