@seedprotocol/mapping 0.5.3 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +213 -36
  3. package/dist/adapters/rss.d.ts +2 -1
  4. package/dist/adapters/rss.d.ts.map +1 -1
  5. package/dist/applyMapping.d.ts +21 -2
  6. package/dist/applyMapping.d.ts.map +1 -1
  7. package/dist/autoMap-OMvtOG0S.js +549 -0
  8. package/dist/autoMap-OMvtOG0S.js.map +1 -0
  9. package/dist/autoMap-PTFd4l-9.js +534 -0
  10. package/dist/autoMap-PTFd4l-9.js.map +1 -0
  11. package/dist/autoMap.d.ts +3 -3
  12. package/dist/autoMap.d.ts.map +1 -1
  13. package/dist/classifyUrl.d.ts +14 -0
  14. package/dist/classifyUrl.d.ts.map +1 -0
  15. package/dist/index.d.ts +7 -3
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +91 -402
  18. package/dist/index.js.map +1 -1
  19. package/dist/react/FieldMapper.d.ts +77 -5
  20. package/dist/react/FieldMapper.d.ts.map +1 -1
  21. package/dist/react/FieldMapperWires.d.ts +20 -0
  22. package/dist/react/FieldMapperWires.d.ts.map +1 -0
  23. package/dist/react/LookupEditor.d.ts +20 -0
  24. package/dist/react/LookupEditor.d.ts.map +1 -0
  25. package/dist/react/PropertyRows.d.ts +20 -0
  26. package/dist/react/PropertyRows.d.ts.map +1 -0
  27. package/dist/react/SourceRows.d.ts +19 -0
  28. package/dist/react/SourceRows.d.ts.map +1 -0
  29. package/dist/react/fieldMapperControls.d.ts +9 -0
  30. package/dist/react/fieldMapperControls.d.ts.map +1 -0
  31. package/dist/react/fieldMapperCore.d.ts +83 -0
  32. package/dist/react/fieldMapperCore.d.ts.map +1 -0
  33. package/dist/react/fieldMapperSlots.d.ts +69 -0
  34. package/dist/react/fieldMapperSlots.d.ts.map +1 -0
  35. package/dist/react/fieldMapperTheme.d.ts +34 -0
  36. package/dist/react/fieldMapperTheme.d.ts.map +1 -0
  37. package/dist/react/fieldMapperTypes.d.ts +77 -0
  38. package/dist/react/fieldMapperTypes.d.ts.map +1 -0
  39. package/dist/react/useFieldMapper.d.ts +8 -0
  40. package/dist/react/useFieldMapper.d.ts.map +1 -0
  41. package/dist/react.d.ts +10 -0
  42. package/dist/react.d.ts.map +1 -0
  43. package/dist/react.js +1690 -0
  44. package/dist/react.js.map +1 -0
  45. package/dist/relationLookup.d.ts +34 -0
  46. package/dist/relationLookup.d.ts.map +1 -0
  47. package/dist/resolvedSources.d.ts +32 -0
  48. package/dist/resolvedSources.d.ts.map +1 -0
  49. package/dist/types.d.ts +74 -2
  50. package/dist/types.d.ts.map +1 -1
  51. package/package.json +19 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0
4
+
5
+ - Relation lookup is an edge-local stored intent: `FieldMapping.lookup.entries` as `{ value, ref }[]`. `ref` is opaque (`seedLocalId` or `seedUid`). Empty entries are a valid unassigned map.
6
+ - `applyMapping` (sync) applies lookup entries and emits refs. Unmatched values are omitted. `applyMappingAsync` reports unmatched on `errors[]` and no longer calls the host `resolve` callback for lookup.
7
+ - `autoMap` pairs alias matches such as `author` → List-of-Relation `authors` with `resolve: 'lookup'` and no invented refs.
8
+ - `FieldMapper` `renderLookup?: (row: FieldMapperLookupRow) => ReactNode` — host picker on lookup rows (same split as `renderRowAccessory`). Package uid editor is a fallback only.
9
+ - `useFieldMapper.setLookupEntries`. Row `sampleValue` and `data-state="needsLookup"` when the current sample has no matching entry.
10
+ - `TargetProperty` stays Seed-accurate (`List` + `refValueType: 'Relation'` + `ref`). No `multiple` flag.
11
+ - `MappingDocument.lookups` / `lookups` + `onLookupsChange` are deprecated read/write fallbacks for 0.6.0 documents.
12
+ - `_pendingResolve` is extract/file only; lookup hits appear in the preview bag.
13
+
14
+ ## 0.6.1
15
+
16
+ - `FieldMapper` `renderRowAccessory?: (row: FieldMapperRow) => ReactNode` — host chrome slot on each rows-layout row (`.fm-row-accessory` / `classNames.rowAccessory`), after the transform control and before remove. Package does not fetch or preview; hosts return `null` when idle (e.g. show **Preview** only for `resolve: 'extract' | 'file'`).
17
+ - Optional `SourceNode.subtitle` for a secondary display line (inspector, wires card, source option labels). Prefer this over stuffing origin into `label`. Display-only — not part of `MappingDocument`.
18
+
19
+ ## 0.6.0
20
+
21
+ - `FieldMapper` Phase 5: `layout` defaults to `'rows'`; `'wires'` is deprecated but still supported.
22
+ - `FieldMapper` Phase 4: per-row lookup editors (`slots.lookups: 'row'`), row/JSON preview parity with sync `applyMapping` (+ `_pendingResolve`), JSON behind a `<details>` disclosure. Rows layout defaults `lookups` to `'row'`; wires keep `'section'`.
23
+ - Shared `LookupEditor` + `buildSyncPreviewBag` / upgraded `previewForEdge` (lookup table hits, coerced copy values).
24
+ - `FieldMapper` Phase 3 theming contract: `--sfm-*` tokens (with `--fm-*` aliases), `theme="default" | "structural" | "none"` (`unstyled` shim), `classNames` / `components` / `slots`, `@layer seed-field-mapper`, document-once CSS injection, row `data-source-kind`.
25
+ - Deprecated (still work): `layout="wires"`, `theme="unstyled"`, `showPreview`, `connectionColors` — prefer `layout="rows"`, `theme="none"`, `slots.preview`, and CSS map tokens.
26
+ - `FieldMapper` row layout (Phase 1–2 of the redesign): `layout="rows" | "wires"`, `rowKey="property" | "source"`, `defaultRows`, optional `TargetProperty.required` for coverage.
27
+ - Headless `useFieldMapper` hook + shared core (`setSource` / `setTransform` / coverage / conflicts); wires UI refactored to consume the hook.
28
+ - Rows UI: transform control on the edge (no `buildResolvedSourceNodes` in the rows path), coverage toolbar, mapped+required filter, add-mapping flow, source inspector; source mode conflict + coverage banners.
29
+ - **Breaking:** `FieldMapper` and related UI exports moved to `@seedprotocol/mapping/react`. The default entry is headless-only (no React import or required peer) so Node sidecars can load without React. Install `react` / `react-dom` only when using `/react`.
30
+ - **Breaking:** `FieldMapper` `layout` default flipped from `'wires'` to `'rows'`. Pass `layout="wires"` explicitly if you still need the two-pane connector UI. For rows, pass origin sources only (not `buildResolvedSourceNodes`).
31
+ - `resolve: 'lookup'` for Relation / List-of-Relation targets: persistable `MappingDocument.lookups` (string → seed uid), `applyMappingAsync` table hit + optional host callback on miss; sync apply skips lookup edges.
32
+ - `TargetProperty.ref` / `refValueType`; `isRelationLookupTarget` helper; `autoMap` never plain-copies onto lookup-capable targets (author → authors no longer writes a string into a relation list).
33
+ - `FieldMapper`: connecting to a lookup-capable target sets `resolve: 'lookup'`; optional `lookups` / `onLookupsChange` panel edits string → uid maps (comma-separated for multi).
34
+ - `FieldMapper`: `theme="default" | "unstyled"`, CSS-var connector strokes (`--fm-map-1…8` / `--map-1…8`), optional `connectionColors`, `data-mapping-index` / `data-pair` on wells and paths; pending `.active` only on the selected source.
35
+ - `FieldMapping.resolve?: 'extract' | 'file' | 'lookup'`; sync `applyMapping` skips resolve edges; new `applyMappingAsync` with host callback.
36
+ - `classifyUrl`, `buildResolvedSourceNodes`, `normalizeMappingFromSourceId` for package-owned extract/file candidates.
37
+ - RSS adapter exposes enclosure / media:content as `url` + `contentType` in `meta` (stable `rss-enclosure-N` ids).
38
+ - `autoMap` never maps a raw URL onto html/Image/File without `resolve`; prefers full-text → html, then extract, then file jobs.
39
+ - Allow one source to map to multiple properties (edge-list fan-out); each property remains exclusive.
40
+ - `autoMap` fans `link` / `url` sources out to matching URL-ish targets (`importUrl`, `canonicalUrl`, `url`, `link`, `permalink`).
41
+ - `FieldMapper` supports attaching multiple properties from an active source without clearing prior edges; normalizes `@extract`/`@file` nodes to `resolve` on persist.
42
+
3
43
  ## 0.5.1
4
44
 
5
45
  - Initial Phase 1 release: headless mapping core, markdown + RSS adapters, `FieldMapper` UI.
package/README.md CHANGED
@@ -5,8 +5,15 @@ Source → Seed model property mapping helpers and a props-driven React UI.
5
5
  Use this package to:
6
6
 
7
7
  1. Parse markdown or RSS/Atom into `SourceNode[]`
8
- 2. Author 1:1 `FieldMapping`s (manually, via `autoMap`, or via `FieldMapper`)
9
- 3. `applyMapping` to a coerced property bag for `createItem` / publish
8
+ 2. Optionally expand URL sources with `buildResolvedSourceNodes` (extract / file candidates)
9
+ 3. Author `FieldMapping` edges (manually, via `autoMap`, or via `FieldMapper`)
10
+ 4. `applyMapping` (sync copy + stored lookup) or `applyMappingAsync` (host extract/file callback) → property bag
11
+
12
+ Mappings are an edge list: one source may fan out to multiple properties (e.g. RSS
13
+ `link` → `importUrl` and `canonicalUrl`). Each property is exclusive — at most one
14
+ source may map to it. `resolve: 'extract' | 'file'` is applied by the host callback.
15
+ `resolve: 'lookup'` is a stored string → ref table on the edge — the package never
16
+ fetches, runs Readability, or loads Seed Identity items.
10
17
 
11
18
  ## Install
12
19
 
@@ -14,7 +21,11 @@ Use this package to:
14
21
  bun add @seedprotocol/mapping
15
22
  ```
16
23
 
17
- Peers: `react`, `react-dom`. RSS parsing uses `rss-parser` with the same custom fields as `@seedprotocol/feed` `parseRssString` (aligned, no hard dependency on the feed package barrel).
24
+ The default entry (`@seedprotocol/mapping`) is headless — no React peer required.
25
+ For `FieldMapper`, also install React peers and import from `@seedprotocol/mapping/react`.
26
+
27
+ RSS parsing uses `rss-parser` with the same custom fields as `@seedprotocol/feed`
28
+ `parseRssString` (aligned, no hard dependency on the feed package barrel).
18
29
 
19
30
  ## Headless API
20
31
 
@@ -24,6 +35,9 @@ import {
24
35
  rssXmlToSources,
25
36
  autoMap,
26
37
  applyMapping,
38
+ applyMappingAsync,
39
+ buildResolvedSourceNodes,
40
+ classifyUrl,
27
41
  type TargetProperty,
28
42
  type MappingDocument,
29
43
  } from '@seedprotocol/mapping'
@@ -43,63 +57,226 @@ const doc: MappingDocument = {
43
57
  }
44
58
  ```
45
59
 
46
- RSS:
60
+ RSS with resolve jobs:
47
61
 
48
62
  ```ts
49
- const { channel, items } = await rssXmlToSources(xml)
63
+ const { items } = await rssXmlToSources(xml)
50
64
  const itemSources = items[0]!
65
+ const expanded = buildResolvedSourceNodes(itemSources)
66
+ const postTargets: TargetProperty[] = [
67
+ { name: 'importUrl', dataType: 'String' },
68
+ { name: 'canonicalUrl', dataType: 'String' },
69
+ { name: 'html', dataType: 'Html' },
70
+ { name: 'featureImage', dataType: 'Image' },
71
+ { name: 'title', dataType: 'String' },
72
+ ]
51
73
  const mappings = autoMap(itemSources, postTargets)
52
- const properties = applyMapping(itemSources, mappings, postTargets)
74
+ // link → importUrl/canonicalUrl (copy); missing body → link extract→html;
75
+ // image URLs → featureImage with resolve:'file'
76
+
77
+ // Sync preview applies lookup entries; extract/file stay pending:
78
+ const preview = applyMapping(itemSources, mappings, postTargets)
79
+
80
+ // Publish path — host owns HTTP / extract / store:
81
+ const { properties, errors } = await applyMappingAsync(
82
+ itemSources,
83
+ mappings,
84
+ postTargets,
85
+ {
86
+ resolve: async ({ job, url, contentType, rawValue }) => {
87
+ if (job === 'extract') return await hostExtractHtml(url!)
88
+ if (job === 'lookup') return await hostLookupIdentity(rawValue!)
89
+ return await hostFetchAndStoreFile(url!, contentType)
90
+ },
91
+ },
92
+ )
53
93
  ```
54
94
 
95
+ ### Relation lookup (`resolve: 'lookup'`)
96
+
97
+ Map a string source (e.g. RSS `author`) onto a Relation or List-of-Relation
98
+ target (e.g. `authors` → Identity) via **stored entries on the edge**. Use Seed
99
+ model vocabulary — not a `multiple` flag:
100
+
101
+ ```ts
102
+ const postTargets: TargetProperty[] = [
103
+ {
104
+ name: 'authors',
105
+ dataType: 'List',
106
+ refValueType: 'Relation',
107
+ ref: 'Identity',
108
+ },
109
+ ]
110
+
111
+ const mappings = [
112
+ {
113
+ sourceId: 'rss-author',
114
+ propertyName: 'authors',
115
+ resolve: 'lookup' as const,
116
+ lookup: {
117
+ entries: [{ value: 'Jane Doe', ref: 'identity-seed-uid' }],
118
+ },
119
+ },
120
+ ]
121
+
122
+ const doc: MappingDocument = {
123
+ version: 1,
124
+ sourceKind: 'rss',
125
+ mappings,
126
+ }
127
+
128
+ // Sync apply emits refs from stored entries (extract/file still skipped):
129
+ const bag = applyMapping(itemSources, mappings, postTargets)
130
+ // bag.authors === ['identity-seed-uid']
131
+
132
+ const { properties, errors } = await applyMappingAsync(
133
+ itemSources,
134
+ mappings,
135
+ postTargets,
136
+ )
137
+ // Unmatched values are omitted from the bag and listed in errors[].
138
+ // Do not fetch identities inside apply — persist the assignment, then apply.
139
+ ```
140
+
141
+ `autoMap` pairs alias matches such as `author` → `authors` with
142
+ `resolve: 'lookup'` and **empty** entries. It never invents refs.
143
+
144
+ `MappingDocument.lookups` / `applyMappingAsync({ lookups })` remain a read-only
145
+ fallback for 0.6.0 documents. Prefer `FieldMapping.lookup.entries`.
146
+
147
+ `classifyUrl({ url, contentType? })` is a pure MIME/extension classifier
148
+ (`html` | `image` | `audio` | `video` | `unknown`). Enclosure nodes expose
149
+ `meta.url`, `meta.contentType`, and `meta.class` (not a stringified object).
150
+
55
151
  ## FieldMapper UI
56
152
 
57
- Props-driven only — no Seed hooks or routing. The host app supplies sources/targets and handles create/navigate.
153
+ Props-driven only — no Seed hooks or routing. Import from `@seedprotocol/mapping/react`.
154
+
155
+ Two layouts share the same persisted `FieldMapping[]`. **`layout` defaults to `"rows"`.**
156
+
157
+ | `layout` | Behavior |
158
+ |----------|----------|
159
+ | `"rows"` (default) | Row-based authoring. Pass **origin** sources only — transforms are chosen per row (no `@extract`/`@file` cards). |
160
+ | `"wires"` (**deprecated**) | Two-pane click-to-connect with SVG connectors. Pass `buildResolvedSourceNodes(sources)` so extract/file candidates appear as source cards. Still supported for one minor. |
161
+
162
+ When `layout="rows"`, `rowKey` picks orientation:
163
+
164
+ | `rowKey` | Behavior |
165
+ |----------|----------|
166
+ | `"property"` (default) | One row per target property; source + transform inside the row. Structurally property-exclusive. |
167
+ | `"source"` | One row per edge (insertion order); conflict banner when two rows claim one property; coverage banner for missing `required` targets. |
168
+
169
+ Mark targets with `required: true` so coverage / default filters surface missing fields. Default visible set for property mode is mapped + required (`defaultRows="requiredAndMapped"`).
58
170
 
59
171
  ```tsx
60
172
  import { useState } from 'react'
61
173
  import {
62
- FieldMapper,
174
+ buildResolvedSourceNodes,
63
175
  markdownToSources,
64
- applyMapping,
176
+ rssItemToSources,
177
+ applyMappingAsync,
65
178
  type FieldMapping,
66
179
  } from '@seedprotocol/mapping'
180
+ import { FieldMapper, useFieldMapper } from '@seedprotocol/mapping/react'
67
181
 
68
- function ImportMap({ markdown, targets, onSubmit }) {
69
- const sources = markdownToSources(markdown)
70
- const [mappings, setMappings] = useState<FieldMapping[]>([])
71
-
72
- return (
73
- <>
74
- <FieldMapper
75
- sources={sources}
76
- targets={targets}
77
- mappings={mappings}
78
- onChange={setMappings}
79
- />
80
- <button
81
- onClick={() => onSubmit(applyMapping(sources, mappings, targets))}
82
- >
83
- Create
84
- </button>
85
- </>
86
- )
87
- }
182
+ // Rows — property-keyed (default layout; recommended)
183
+ <FieldMapper
184
+ rowKey="property"
185
+ sources={rssItemToSources(item)} // origin sources; no buildResolvedSourceNodes
186
+ targets={postTargets} // e.g. { name: 'html', dataType: 'Html', required: true }
187
+ mappings={mappings}
188
+ onChange={setMappings}
189
+ slots={{ lookups: 'row', preview: 'row' }}
190
+ renderRowAccessory={(row) =>
191
+ row.mapping?.resolve === 'extract' || row.mapping?.resolve === 'file'
192
+ ? <button type="button">Preview</button>
193
+ : null
194
+ }
195
+ renderLookup={(row) => (
196
+ <IdentityLookup
197
+ sampleValue={row.sampleValue}
198
+ entries={row.mapping?.lookup?.entries ?? []}
199
+ onChange={row.onLookupChange}
200
+ />
201
+ )}
202
+ />
203
+
204
+ // Rows — source-keyed
205
+ <FieldMapper rowKey="source" sources={itemSources} targets={targets} mappings={mappings} onChange={setMappings} />
206
+
207
+ // Wires (deprecated — opt in explicitly)
208
+ <FieldMapper
209
+ layout="wires"
210
+ sources={buildResolvedSourceNodes(markdownToSources(markdown))}
211
+ targets={targets}
212
+ mappings={mappings}
213
+ onChange={setMappings}
214
+ theme="none"
215
+ />
216
+ ```
217
+
218
+ Headless mechanics (same package):
219
+
220
+ ```tsx
221
+ const mapper = useFieldMapper({
222
+ sources,
223
+ targets,
224
+ mappings,
225
+ onChange: setMappings,
226
+ rowKey: 'property',
227
+ })
228
+ // mapper.rows, mapper.coverage, mapper.setSource, mapper.setTransform, …
88
229
  ```
89
230
 
231
+ Connecting a source to a Relation / List-of-Relation target sets `resolve: 'lookup'`.
232
+ Pass `renderLookup` to paint the host picker (Identity, etc.) on that row. Without
233
+ `renderLookup`, `onLookupsChange` still enables the package uid-text fallback.
234
+ `lookups` / `onLookupsChange` are deprecated — persist `lookup.entries` on the edge.
235
+
236
+ Row and JSON previews match sync `applyMapping` output. Lookup hits appear in the
237
+ bag; only extract/file stay under `_pendingResolve`. JSON sits behind a disclosure.
238
+
239
+ **Theming**
240
+
241
+ | Prop | Behavior |
242
+ |------|----------|
243
+ | `theme="default"` (default) | Injects structural + paint CSS once into `document.head` (`@layer seed-field-mapper`) with `--sfm-*` tokens (`--fm-*` aliases kept for one minor) |
244
+ | `theme="structural"` | Layout / spacing only — no color paint; inherits host colors |
245
+ | `theme="none"` | No injection — structural classes only (`fm-card`, `fm-row`, `fm-grid`, …); keeps `seed-field-mapper--unstyled` |
246
+ | `theme="unstyled"` | **Deprecated** shim for `none` |
247
+ | `classNames` | Per-slot host classes merged with package classes (`root`, `toolbar`, `row`, `rowAccessory`, …) |
248
+ | `components` | Optional host `Select` / `Button` / `Pill` (defaults are native controls) |
249
+ | `slots` | Toggle chrome: `autoMap`, `filter`, `inspector`, `preview` (`'row' \| 'json' \| 'both' \| false`), `lookups` (`'row' \| 'section' \| false`; rows layout defaults to `'row'`, wires to `'section'`) |
250
+ | `renderRowAccessory` | Optional `(row: FieldMapperRow) => ReactNode` for host chrome (e.g. extract/file **Preview**) in `.fm-row-accessory`. Unrelated to `slots.preview`. Package does not fetch. |
251
+ | `renderLookup` | Optional `(row: FieldMapperLookupRow) => ReactNode` for host lookup chrome on `resolve: 'lookup'` rows. Package does not fetch or list identities. |
252
+ | `showPreview` | **Deprecated** — prefer `slots.preview` |
253
+ | `connectionColors` | **Deprecated** optional stroke palette for wires; prefer `--sfm-map-*` / `--fm-map-*` / `--map-*` |
254
+ | `layout="wires"` | **Deprecated** — prefer `layout="rows"` (now the default) |
255
+
256
+ `SourceNode` may include optional `subtitle` (display-only). Prefer `subtitle` over stuffing a secondary fact into `label` (e.g. origin field name on a resolved well).
257
+
258
+ **DOM hooks for host CSS**
259
+
260
+ - Wires: mapped wells expose `data-mapping-index` / `data-pair={index % 8}` (kept for host CSS); pending `.active` only on the selected source.
261
+ - Rows: `data-state="empty|mapped|needsResolve|needsLookup|conflict"`, `data-property-name`, `data-resolve`, `data-required`, `data-source-kind`.
262
+
263
+ Sync `applyMapping` preview lists pending **extract/file** edges under `_pendingResolve`. Lookup refs from stored entries appear in the bag.
264
+
265
+ See [FIELD_MAPPER_REDESIGN.md](../../docs/FIELD_MAPPER_REDESIGN.md) for the row-layout design study (Phases 1–5 shipped). PermaPress: [FIELD_MAPPER_PERMAPRESS_HANDOFF.md](../../docs/FIELD_MAPPER_PERMAPRESS_HANDOFF.md).
266
+
90
267
  ## Relationship to `FeedFieldManifest`
91
268
 
92
269
  | Type | Package | Meaning |
93
270
  |------|---------|---------|
94
- | `FieldMapping` / `MappingDocument` | `@seedprotocol/mapping` | Source id → **model property name** |
95
- | `FeedFieldManifest` | `@seedprotocol/sdk` | Property/key → **role** (`image` / `html` / `text`) for `normalizeFeedItemFields` |
271
+ | `FieldMapping` / `MappingDocument` | `@seedprotocol/mapping` | Source id → **model property name** (1→N edges; optional `resolve`; optional `lookup.entries`) |
272
+ | `FeedFieldManifest` | `@seedprotocol/sdk` | Property/key → **role** (`image` / `audio` / `video` / `file` / `html` / `text`) for display |
96
273
 
97
- Compose them: map external fields into a plain item, then optionally normalize roles for media/HTML display.
274
+ Compose them: map + resolve into a plain item, then optionally normalize roles for media/HTML display. Storage stays `Image` / `File` schema types — no separate Audio/Video dataTypes.
98
275
 
99
- ## Phase 1 limits
276
+ ## Limits
100
277
 
101
278
  - Markdown: flat YAML frontmatter; `#` / `##` sections only
102
- - RSS: top-level item fields via `parseRssString` (no general XPath)
103
- - UI: self-contained dark theme; no desktop/Tailwind coupling
279
+ - RSS: top-level item fields via `parseRssString` (no general XPath); structured enclosure / media:content
280
+ - UI: self-contained dark theme (or `theme="none"` / `"structural"` for host paint); `layout` defaults to `rows` (`wires` deprecated); no desktop/Tailwind coupling; no media preview player
104
281
 
105
- See [MAPPING_PHASE2.md](../../docs/MAPPING_PHASE2.md) for consumer migration and hardening.
282
+ See [MAPPING_PHASE2.md](../../docs/MAPPING_PHASE2.md) for remaining consumer work (desktop migration, npm publish), [FIELD_MAPPER_REDESIGN.md](../../docs/FIELD_MAPPER_REDESIGN.md) for the row-layout study, and [FIELD_MAPPER_PERMAPRESS_HANDOFF.md](../../docs/FIELD_MAPPER_PERMAPRESS_HANDOFF.md) for PermaPress.
@@ -8,7 +8,8 @@ export type ParsedRssChannel = {
8
8
  };
9
9
  /**
10
10
  * Flatten a parsed RSS/Atom item into SourceNodes (one per string-coercible key).
11
- * Stable ids: `rss-{key}`.
11
+ * Enclosure / media:content become url + type in `meta` (not stringified objects).
12
+ * Stable ids: `rss-{key}`, `rss-enclosure-0`, …
12
13
  */
13
14
  export declare function rssItemToSources(item: Record<string, unknown>): SourceNode[];
14
15
  export type RssXmlSourcesResult = {
@@ -1 +1 @@
1
- {"version":3,"file":"rss.d.ts","sourceRoot":"","sources":["../../src/adapters/rss.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;CACjC,CAAA;AAiED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,EAAE,CAoB5E;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,CAAA;IACD,0DAA0D;IAC1D,KAAK,EAAE,UAAU,EAAE,EAAE,CAAA;CACtB,CAAA;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,mBAAmB,CAAC,CAU9B"}
1
+ {"version":3,"file":"rss.d.ts","sourceRoot":"","sources":["../../src/adapters/rss.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;CACjC,CAAA;AA8JD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,EAAE,CAoD5E;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,CAAA;IACD,0DAA0D;IAC1D,KAAK,EAAE,UAAU,EAAE,EAAE,CAAA;CACtB,CAAA;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,mBAAmB,CAAC,CAU9B"}
@@ -1,4 +1,4 @@
1
- import type { FieldMapping, PropertyBag, SourceNode, TargetProperty } from './types';
1
+ import type { ApplyMappingAsyncResult, FieldMapping, MappingLookups, PropertyBag, ResolveCallback, SourceNode, TargetProperty } from './types';
2
2
  /**
3
3
  * Coerce a raw string value to the Seed model property dataType.
4
4
  */
@@ -6,6 +6,25 @@ export declare function coerceValue(rawValue: string, dataType: string): unknown
6
6
  /**
7
7
  * Apply field mappings to produce a property bag for createItem / publish.
8
8
  * Missing sources or unknown target properties are skipped (no throw).
9
+ * `resolve: 'extract' | 'file'` edges are skipped — use `applyMappingAsync`.
10
+ * `resolve: 'lookup'` is applied from stored entries (or deprecated document lookups).
9
11
  */
10
- export declare function applyMapping(sources: SourceNode[], mappings: FieldMapping[], targets: TargetProperty[]): PropertyBag;
12
+ export declare function applyMapping(sources: SourceNode[], mappings: FieldMapping[], targets: TargetProperty[], options?: {
13
+ lookups?: MappingLookups;
14
+ }): PropertyBag;
15
+ export type ApplyMappingAsyncOptions = {
16
+ /** Required for extract/file edges. Not called for lookup. */
17
+ resolve?: ResolveCallback;
18
+ /**
19
+ * String → seed uid dictionaries keyed by property name.
20
+ * @deprecated Prefer `FieldMapping.lookup.entries`. Read as a fallback only.
21
+ */
22
+ lookups?: MappingLookups;
23
+ };
24
+ /**
25
+ * Async apply: copy+coerce plain edges; apply lookup entries; for extract/file
26
+ * call the host callback then coerce. Partial success — failed resolve edges
27
+ * are listed in `errors` and omitted from `properties`.
28
+ */
29
+ export declare function applyMappingAsync(sources: SourceNode[], mappings: FieldMapping[], targets: TargetProperty[], options?: ApplyMappingAsyncOptions): Promise<ApplyMappingAsyncResult>;
11
30
  //# sourceMappingURL=applyMapping.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"applyMapping.d.ts","sourceRoot":"","sources":["../src/applyMapping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,UAAU,EACV,cAAc,EACf,MAAM,SAAS,CAAA;AAEhB;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAuBvE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,UAAU,EAAE,EACrB,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,EAAE,cAAc,EAAE,GACxB,WAAW,CAeb"}
1
+ {"version":3,"file":"applyMapping.d.ts","sourceRoot":"","sources":["../src/applyMapping.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,uBAAuB,EACvB,YAAY,EACZ,cAAc,EACd,WAAW,EACX,eAAe,EAEf,UAAU,EACV,cAAc,EAEf,MAAM,SAAS,CAAA;AAEhB;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAuBvE;AA8DD;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,UAAU,EAAE,EACrB,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,EAAE,cAAc,EAAE,EACzB,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,cAAc,CAAA;CAAE,GACrC,WAAW,CA4Bb;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,8DAA8D;IAC9D,OAAO,CAAC,EAAE,eAAe,CAAA;IACzB;;;OAGG;IACH,OAAO,CAAC,EAAE,cAAc,CAAA;CACzB,CAAA;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,UAAU,EAAE,EACrB,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,EAAE,cAAc,EAAE,EACzB,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,uBAAuB,CAAC,CAwFlC"}