@contentrain/wp-import 0.4.5 → 0.5.0
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.
- package/README.md +34 -0
- package/dist/index.d.mts +45 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +291 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ WordPress importers for the Contentrain migration pipeline — ported from a cor
|
|
|
6
6
|
WXR file or REST API ──► RawIR (source-faithful, provenance-stamped)
|
|
7
7
|
RawIR ──► .contentrain content store (pure file map) + EntrySourceMap
|
|
8
8
|
RawIR + EntrySourceMap ──► CommentsExport (live-service intake payload)
|
|
9
|
+
SourceDeltaPlan + store (+ incoming export) ──► placed SourceDeltaPlan
|
|
9
10
|
```
|
|
10
11
|
|
|
11
12
|
## Usage
|
|
@@ -37,6 +38,28 @@ const commentsExport = buildCommentsExport(raw, entry_source_map)
|
|
|
37
38
|
|
|
38
39
|
Streaming WXR parse (sax): a 100 MB export holds only its records in memory.
|
|
39
40
|
|
|
41
|
+
## Source deltas
|
|
42
|
+
|
|
43
|
+
A bridge reports what changed at the origin since the last delivery as a `SourceDeltaPlan`: `created`, `updated`, `moved` and `deleted` records by WordPress id. `planSourceDelta` works out what each change means for the store:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { planSourceDelta, formatSourceDeltaReport } from '@contentrain/wp-import'
|
|
47
|
+
|
|
48
|
+
const plan = planSourceDelta({
|
|
49
|
+
delta, // the bridge's SourceDeltaPlan
|
|
50
|
+
store: { files, entry_source_map }, // the repository's .contentrain, as it is now
|
|
51
|
+
incoming: { files: next, entry_source_map: nextMap }, // the new export
|
|
52
|
+
})
|
|
53
|
+
console.log(formatSourceDeltaReport(plan))
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- **Placement.** A post-type record is looked up in the `EntrySourceMap`. A term or media record is found by its `wp_id` in its own model (`wp-tax-<taxonomy>` / `categories` / `tags`, `wp-media` / `media`). The map lists post ids only, and post 6 and category 6 are different records. A record that cannot be placed gets `unmapped` with a reason: `not-in-source-map`, `no-model-for-type` or `entry-not-found`.
|
|
57
|
+
- **`updated` / `moved`** list `fields_changed`, comparing the stored entry with the incoming one (frontmatter keys and `body` for a document). A `moved` record whose address changed adds a 301 to `redirects`. If the store derives entry ids from slugs, the new id is in `entry_id_after`.
|
|
58
|
+
- **`deleted`** is a tombstone. `trashed` (can come back) and `purged` (gone) stay distinct.
|
|
59
|
+
- **Conflicts.** If the entry's meta says something other than the importer wrote it last (`source` is not `import`, or `updated_by` is not an importer), the record gets `conflict: true` and `repo_edit: { updated_by, updated_at?, source }`. Nothing is overwritten, and a person decides.
|
|
60
|
+
|
|
61
|
+
The planner is pure and plan-only. Applying a plan is a governed write, and it goes through review. Pass `taxonomies` for custom taxonomies the store has no model of its own for; otherwise their term ids would be read as post ids.
|
|
62
|
+
|
|
40
63
|
## Multilingual sites
|
|
41
64
|
|
|
42
65
|
A post's language is read from the plugin — Polylang's `lang` (REST) or its
|
|
@@ -55,6 +78,17 @@ stay per-store; the `language` / `post_translations` bookkeeping taxonomies
|
|
|
55
78
|
never become models. `report.locales` and `report.translation_groups` say
|
|
56
79
|
what happened. A monolingual site is unchanged (`i18n: false`, `data.json`).
|
|
57
80
|
|
|
81
|
+
Few sites are translated evenly. A post type whose content exists in some of the
|
|
82
|
+
site's languages gets `model.locales` — the locales it actually has posts in,
|
|
83
|
+
with the default locale always included, since a post that carries no language
|
|
84
|
+
tag belongs to it and its absence would be the importer's uncertainty rather
|
|
85
|
+
than a fact about the site. A type translated into every locale gets no
|
|
86
|
+
`locales` key at all: absent already means "all", and a redundant list would
|
|
87
|
+
have to be maintained as locales are added. `contentrain validate` checks
|
|
88
|
+
parity against that subset, so pages that only ever existed in one language
|
|
89
|
+
stop failing as missing translations. See
|
|
90
|
+
[Locale Coverage](https://ai.contentrain.io/reference/model-kinds#locale-coverage).
|
|
91
|
+
|
|
58
92
|
## REST limits and completeness
|
|
59
93
|
|
|
60
94
|
`fetchRestRawIR` accepts `concurrency` (default 4), `maxPages` (optional cap per
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CommentsExport, EntrySourceMap, HandoffComments, RawIR } from "@contentrain/types";
|
|
1
|
+
import { CommentsExport, EntrySourceMap, HandoffComments, RawIR, SourceDeltaPlan } from "@contentrain/types";
|
|
2
2
|
import { Readable } from "node:stream";
|
|
3
3
|
|
|
4
4
|
//#region src/wxr.d.ts
|
|
@@ -121,5 +121,48 @@ declare const PLUGIN_META: RegExp;
|
|
|
121
121
|
declare const taxModelId: (t: string) => string;
|
|
122
122
|
declare const slugify: (s: unknown) => string;
|
|
123
123
|
//#endregion
|
|
124
|
-
|
|
124
|
+
//#region src/delta.d.ts
|
|
125
|
+
/** A `.contentrain` store as a file map (repo-relative path → file text), with the source map it was built from. */
|
|
126
|
+
interface DeltaStore {
|
|
127
|
+
files: Record<string, string>;
|
|
128
|
+
entry_source_map: EntrySourceMap;
|
|
129
|
+
}
|
|
130
|
+
interface PlanSourceDeltaInput {
|
|
131
|
+
/** The origin delta, as the bridge wrote it. */
|
|
132
|
+
delta: SourceDeltaPlan;
|
|
133
|
+
/** The repository's store, as it is now — including edits made since the import. */
|
|
134
|
+
store: DeltaStore;
|
|
135
|
+
/** The new export the delta describes. Without it the plan has no field-level differences and no address for a created record. */
|
|
136
|
+
incoming?: DeltaStore;
|
|
137
|
+
/**
|
|
138
|
+
* Origin types that are taxonomies. Their ids are term ids, which the
|
|
139
|
+
* source map (post ids) must never be consulted for: post 6 and category 6
|
|
140
|
+
* are different records. Defaults to WordPress core's.
|
|
141
|
+
*/
|
|
142
|
+
taxonomies?: string[];
|
|
143
|
+
/**
|
|
144
|
+
* `updated_by` values that mean "written by an import". Meta with any other
|
|
145
|
+
* writer — or with a `source` other than `import` — is a repository edit.
|
|
146
|
+
*/
|
|
147
|
+
importers?: string[];
|
|
148
|
+
}
|
|
149
|
+
declare const DEFAULT_TAXONOMIES: readonly ["category", "post_tag", "nav_menu", "post_format"];
|
|
150
|
+
declare const DEFAULT_IMPORTERS: readonly ["contentrain-bridge", "@contentrain/wp-import"];
|
|
151
|
+
/**
|
|
152
|
+
* Place a bridge's source delta in the repository's store.
|
|
153
|
+
*
|
|
154
|
+
* Every entry keeps the bridge's facts (op, ids, fingerprints, paths,
|
|
155
|
+
* `deleted_kind`) and gains its store address — or `unmapped` with the reason.
|
|
156
|
+
* `updated` and `moved` gain `fields_changed` against the incoming export; a
|
|
157
|
+
* record the repository also edited gets `conflict: true` and `repo_edit`;
|
|
158
|
+
* every `moved` whose address changed adds a 301 to `redirects`.
|
|
159
|
+
*
|
|
160
|
+
* Nothing is written and nothing is decided: a conflict is reported for a
|
|
161
|
+
* person to resolve, and applying the plan is a separate, governed step.
|
|
162
|
+
*/
|
|
163
|
+
declare function planSourceDelta(input: PlanSourceDeltaInput): SourceDeltaPlan;
|
|
164
|
+
/** The plan as text, for a dry run: one line per record, then conflicts, unmapped records, redirects and warnings. */
|
|
165
|
+
declare function formatSourceDeltaReport(plan: SourceDeltaPlan): string;
|
|
166
|
+
//#endregion
|
|
167
|
+
export { type ContentrainResult, DEFAULT_IMPORTERS, DEFAULT_TAXONOMIES, type DeltaStore, type ImportReport, PLUGIN_META, type PlanSourceDeltaInput, type RestImportOptions, type RestImportResult, SKIP_TYPES, type UnserializeResult, type WxrParseResult, type WxrStats, buildCommentsExport, canon, fetchRestRawIR, formatSourceDeltaReport, hexId, looksSerialized, parseWxr, phpUnserialize, planSourceDelta, rawToContentrain, slugify, strip, summarizeComments, taxModelId, tryUnserialize };
|
|
125
168
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/wxr.ts","../src/rest.ts","../src/contentrain.ts","../src/comments.ts","../src/php-unserialize.ts","../src/core.ts"],"mappings":";;;;UA2BiB,QAAA;EACf,UAAA;IAAc,QAAA;IAAkB,OAAA;IAAiB,MAAA;IAAgB,IAAA;EAAA;EACjE,UAAA;EACA,gBAAA;EACA,MAAA,EAAQ,MAAA;AAAA;AAAA,UAGO,cAAA;EACf,GAAA,EAAK,KAAA;EACL,KAAA,EAAO,QAAA;AAAA;AAAA,iBAiBa,QAAA,CAAS,KAAA,WAAgB,QAAA,EAAU,IAAA;EAAS,IAAA;AAAA,IAAkB,OAAA,CAAQ,cAAA;;;UCtC3E,iBAAA;EACf,MAAA;;EAEA,SAAA,UAAmB,KAAA;EDSI;ECPvB,IAAA;IAAS,IAAA;IAAc,WAAA;EAAA;EACvB,OAAA;EDOiD;;;;;ECDjD,WAAA;EDIc;;AAGhB;;ECFE,QAAA;EACA,IAAA;AAAA;AAAA,UAGe,gBAAA;EACf,GAAA,EAAK,KAAA;EACL,QAAA;AAAA;AAAA,iBAqCoB,cAAA,CAAe,OAAA,EAAS,iBAAA,GAAoB,OAAA,CAAQ,gBAAA;;;UChDzD,YAAA;EACf,cAAA;EACA,aAAA;EACA,cAAA;EACA,WAAA,EAAa,MAAA;EACb,UAAA,EAAY,MAAA;EACZ,aAAA;EACA,iBAAA;EACA,MAAA,EAAQ,MAAA;IAAiB,IAAA;IAAc,MAAA;IAAgB,MAAA;IAAgB,OAAA;EAAA;EFJvE;EEMA,OAAA;EFNc;EEQd,kBAAA;AAAA;AAAA,UAGe,iBAAA;EACf,KAAA,EAAO,MAAA;EACP,gBAAA,EAAkB,cAAA;EAClB,MAAA,EAAQ,YAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/wxr.ts","../src/rest.ts","../src/contentrain.ts","../src/comments.ts","../src/php-unserialize.ts","../src/core.ts","../src/delta.ts"],"mappings":";;;;UA2BiB,QAAA;EACf,UAAA;IAAc,QAAA;IAAkB,OAAA;IAAiB,MAAA;IAAgB,IAAA;EAAA;EACjE,UAAA;EACA,gBAAA;EACA,MAAA,EAAQ,MAAA;AAAA;AAAA,UAGO,cAAA;EACf,GAAA,EAAK,KAAA;EACL,KAAA,EAAO,QAAA;AAAA;AAAA,iBAiBa,QAAA,CAAS,KAAA,WAAgB,QAAA,EAAU,IAAA;EAAS,IAAA;AAAA,IAAkB,OAAA,CAAQ,cAAA;;;UCtC3E,iBAAA;EACf,MAAA;;EAEA,SAAA,UAAmB,KAAA;EDSI;ECPvB,IAAA;IAAS,IAAA;IAAc,WAAA;EAAA;EACvB,OAAA;EDOiD;;;;;ECDjD,WAAA;EDIc;;AAGhB;;ECFE,QAAA;EACA,IAAA;AAAA;AAAA,UAGe,gBAAA;EACf,GAAA,EAAK,KAAA;EACL,QAAA;AAAA;AAAA,iBAqCoB,cAAA,CAAe,OAAA,EAAS,iBAAA,GAAoB,OAAA,CAAQ,gBAAA;;;UChDzD,YAAA;EACf,cAAA;EACA,aAAA;EACA,cAAA;EACA,WAAA,EAAa,MAAA;EACb,UAAA,EAAY,MAAA;EACZ,aAAA;EACA,iBAAA;EACA,MAAA,EAAQ,MAAA;IAAiB,IAAA;IAAc,MAAA;IAAgB,MAAA;IAAgB,OAAA;EAAA;EFJvE;EEMA,OAAA;EFNc;EEQd,kBAAA;AAAA;AAAA,UAGe,iBAAA;EACf,KAAA,EAAO,MAAA;EACP,gBAAA,EAAkB,cAAA;EAClB,MAAA,EAAQ,YAAA;AAAA;AAAA,iBAoCM,gBAAA,CAAiB,GAAA,EAAK,KAAA,EAAO,IAAA;EAAS,SAAA;AAAA,IAAuB,iBAAA;;;iBCzE7D,mBAAA,CAAoB,GAAA,EAAK,KAAA,EAAO,OAAA,EAAS,cAAA,EAAgB,IAAA;EAAS,YAAA;AAAA,IAA0B,cAAA;AHmB5G;AAAA,iBGJgB,iBAAA,CAAkB,GAAA,EAAK,cAAA,GAAiB,eAAA;;;UCnBvC,iBAAA;EACf,EAAA;EACA,UAAA;EACA,KAAA;EACA,KAAA;AAAA;AAAA,iBAGc,eAAA,CAAgB,CAAA,YAAa,CAAA;AAAA,iBAI7B,cAAA,CAAe,GAAA;;iBAyFf,cAAA,CAAe,CAAA,WAAY,iBAAA;;;cC9F9B,KAAA,GAAS,CAAA;;cAST,KAAA,GAAS,CAAA;;cAcT,KAAA,GAAS,CAAA;;;;;;;;;;cA4BT,UAAA,EAAU,MAAA;AL3BvB;AAAA,cK+Ba,WAAA,EAAW,MAAA;;cAOX,UAAA,GAAc,CAAA;AAAA,cAGd,OAAA,GAAW,CAAA;;;;UCxDP,UAAA;EACf,KAAA,EAAO,MAAA;EACP,gBAAA,EAAkB,cAAA;AAAA;AAAA,UAGH,oBAAA;ENOD;EMLd,KAAA,EAAO,eAAA;ENEO;EMAd,KAAA,EAAO,UAAA;ENA0C;EMEjD,QAAA,GAAW,UAAA;ENDX;;;;;EMOA,UAAA;ENFe;;;;EMOf,SAAA;AAAA;AAAA,cAGW,kBAAA;AAAA,cACA,iBAAA;;;ANQb;;;;;;;;;;iBM8NgB,eAAA,CAAgB,KAAA,EAAO,oBAAA,GAAuB,eAAA;;iBAkC9C,uBAAA,CAAwB,IAAA,EAAM,eAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -904,6 +904,24 @@ const LANGUAGE_TAXONOMIES = new Set([
|
|
|
904
904
|
"term_language",
|
|
905
905
|
"term_translations"
|
|
906
906
|
]);
|
|
907
|
+
/**
|
|
908
|
+
* A relation field over `targets`: its declared model, and how a reference into
|
|
909
|
+
* it is stored.
|
|
910
|
+
*
|
|
911
|
+
* With several targets the id alone does not say which model's entry it is, so
|
|
912
|
+
* the reference is the pair `{ model, ref }`. With one there is nothing to
|
|
913
|
+
* choose between: the field is single-target and stores the entry id. Writing
|
|
914
|
+
* the pair there fails the same validator that requires it when there are
|
|
915
|
+
* several — so the choice is made in one place, from the same list the model
|
|
916
|
+
* declares.
|
|
917
|
+
*/
|
|
918
|
+
function relationOver(targets) {
|
|
919
|
+
const single = targets.length === 1;
|
|
920
|
+
return {
|
|
921
|
+
model: single ? targets[0] : targets,
|
|
922
|
+
ref: (entry) => single ? entry.ref : entry
|
|
923
|
+
};
|
|
924
|
+
}
|
|
907
925
|
function rawToContentrain(raw, opts) {
|
|
908
926
|
const updatedBy = opts?.updatedBy ?? "@contentrain/wp-import";
|
|
909
927
|
const locale = normLocale(raw.site.language) || "en";
|
|
@@ -1125,6 +1143,15 @@ function rawToContentrain(raw, opts) {
|
|
|
1125
1143
|
};
|
|
1126
1144
|
metas[mid][id] ??= importMeta("published");
|
|
1127
1145
|
}
|
|
1146
|
+
const postTypes = [...new Set(raw.posts.map((p) => p.type))].filter((t) => {
|
|
1147
|
+
if (SKIP_TYPES.test(t)) {
|
|
1148
|
+
report.skipped_types.push(t);
|
|
1149
|
+
return false;
|
|
1150
|
+
}
|
|
1151
|
+
return true;
|
|
1152
|
+
});
|
|
1153
|
+
const contentModelIds = postTypes.map(typeModelId);
|
|
1154
|
+
const toContent = relationOver(contentModelIds);
|
|
1128
1155
|
addModel({
|
|
1129
1156
|
id: "media",
|
|
1130
1157
|
name: "Media",
|
|
@@ -1189,7 +1216,7 @@ function rawToContentrain(raw, opts) {
|
|
|
1189
1216
|
},
|
|
1190
1217
|
parent: {
|
|
1191
1218
|
type: "relation",
|
|
1192
|
-
model:
|
|
1219
|
+
model: toContent.model,
|
|
1193
1220
|
label: "Attached to",
|
|
1194
1221
|
order: 110
|
|
1195
1222
|
},
|
|
@@ -1211,8 +1238,9 @@ function rawToContentrain(raw, opts) {
|
|
|
1211
1238
|
let slug = slugify(decodeSlug(a.slug)) || `media-${a.id}`;
|
|
1212
1239
|
if (mediaSlugs.has(slug)) slug = `${slug}-${a.id}`;
|
|
1213
1240
|
mediaSlugs.add(slug);
|
|
1214
|
-
const
|
|
1215
|
-
if (a.parent && !
|
|
1241
|
+
const target = a.parent ? postEntry.get(a.parent) : void 0;
|
|
1242
|
+
if (a.parent && !target) report.dropped_relations++;
|
|
1243
|
+
const parent = target ? toContent.ref(target) : null;
|
|
1216
1244
|
const im = a.image_meta ?? null;
|
|
1217
1245
|
contents.media[id] = pick({
|
|
1218
1246
|
title: strip(a.title) || slug,
|
|
@@ -1245,15 +1273,6 @@ function rawToContentrain(raw, opts) {
|
|
|
1245
1273
|
]));
|
|
1246
1274
|
metas.media[id] = importMeta("published");
|
|
1247
1275
|
}
|
|
1248
|
-
const postTypes = [...new Set(raw.posts.map((p) => p.type))].filter((t) => {
|
|
1249
|
-
if (SKIP_TYPES.test(t)) {
|
|
1250
|
-
report.skipped_types.push(t);
|
|
1251
|
-
return false;
|
|
1252
|
-
}
|
|
1253
|
-
return true;
|
|
1254
|
-
});
|
|
1255
|
-
const contentModelIds = postTypes.map(typeModelId);
|
|
1256
|
-
models.media.fields.parent.model = contentModelIds;
|
|
1257
1276
|
for (const type of postTypes) {
|
|
1258
1277
|
const typeItems = raw.posts.filter((p) => p.type === type);
|
|
1259
1278
|
const mid = typeModelId(type);
|
|
@@ -1356,12 +1375,14 @@ function rawToContentrain(raw, opts) {
|
|
|
1356
1375
|
report.acf_fields[k] = t;
|
|
1357
1376
|
}
|
|
1358
1377
|
}
|
|
1378
|
+
const covered = locales.filter((l) => l === locale || typeItems.some((p) => postLocale(p) === l));
|
|
1359
1379
|
addModel({
|
|
1360
1380
|
id: mid,
|
|
1361
1381
|
name: type === "post" ? "Posts" : type === "page" ? "Pages" : mid,
|
|
1362
1382
|
kind: "collection",
|
|
1363
1383
|
domain: domainOf(type),
|
|
1364
1384
|
i18n: multilingual,
|
|
1385
|
+
...multilingual && covered.length < locales.length ? { locales: covered } : {},
|
|
1365
1386
|
title_field: "title",
|
|
1366
1387
|
fields
|
|
1367
1388
|
});
|
|
@@ -1443,6 +1464,7 @@ function rawToContentrain(raw, opts) {
|
|
|
1443
1464
|
}
|
|
1444
1465
|
}
|
|
1445
1466
|
});
|
|
1467
|
+
const toTarget = relationOver([...contentModelIds, ...taxonomies.map(taxModelId)]);
|
|
1446
1468
|
addModel({
|
|
1447
1469
|
id: "menu-items",
|
|
1448
1470
|
name: "Menu items",
|
|
@@ -1493,7 +1515,7 @@ function rawToContentrain(raw, opts) {
|
|
|
1493
1515
|
},
|
|
1494
1516
|
target: {
|
|
1495
1517
|
type: "relation",
|
|
1496
|
-
model:
|
|
1518
|
+
model: toTarget.model,
|
|
1497
1519
|
label: "Target",
|
|
1498
1520
|
order: 70
|
|
1499
1521
|
},
|
|
@@ -1543,13 +1565,13 @@ function rawToContentrain(raw, opts) {
|
|
|
1543
1565
|
if (i.target.kind === "url" && i.url) e.url = i.url;
|
|
1544
1566
|
if (i.target.kind === "post" && i.target.resolved && i.target.id != null) {
|
|
1545
1567
|
const pe = postEntry.get(i.target.id);
|
|
1546
|
-
if (pe) e.target = pe;
|
|
1568
|
+
if (pe) e.target = toTarget.ref(pe);
|
|
1547
1569
|
else report.dropped_relations++;
|
|
1548
1570
|
}
|
|
1549
|
-
if (i.target.kind === "term" && i.target.resolved && i.target.slug && models[taxModelId(i.target.taxonomy)]) e.target = {
|
|
1571
|
+
if (i.target.kind === "term" && i.target.resolved && i.target.slug && models[taxModelId(i.target.taxonomy)]) e.target = toTarget.ref({
|
|
1550
1572
|
model: taxModelId(i.target.taxonomy),
|
|
1551
1573
|
ref: termId(i.target.taxonomy, i.target.slug)
|
|
1552
|
-
};
|
|
1574
|
+
});
|
|
1553
1575
|
if (i.classes?.length) e.classes = i.classes;
|
|
1554
1576
|
if (i.description) e.description = strip(i.description);
|
|
1555
1577
|
contents["menu-items"][itemRef(i.id)] = e;
|
|
@@ -1595,7 +1617,7 @@ function rawToContentrain(raw, opts) {
|
|
|
1595
1617
|
},
|
|
1596
1618
|
post: {
|
|
1597
1619
|
type: "relation",
|
|
1598
|
-
model:
|
|
1620
|
+
model: toContent.model,
|
|
1599
1621
|
required: true,
|
|
1600
1622
|
label: "Post",
|
|
1601
1623
|
order: 60
|
|
@@ -1638,7 +1660,7 @@ function rawToContentrain(raw, opts) {
|
|
|
1638
1660
|
url: c.url,
|
|
1639
1661
|
body: c.content,
|
|
1640
1662
|
published_at: c.date,
|
|
1641
|
-
post,
|
|
1663
|
+
post: toContent.ref(post),
|
|
1642
1664
|
parent: c.parent && c.parent_resolved ? cRef(c.parent) : null,
|
|
1643
1665
|
type: c.type,
|
|
1644
1666
|
user: c.user_id ? userById.get(c.user_id) : null,
|
|
@@ -1804,6 +1826,256 @@ function summarizeComments(exp) {
|
|
|
1804
1826
|
};
|
|
1805
1827
|
}
|
|
1806
1828
|
//#endregion
|
|
1807
|
-
|
|
1829
|
+
//#region src/delta.ts
|
|
1830
|
+
const DEFAULT_TAXONOMIES = [
|
|
1831
|
+
"category",
|
|
1832
|
+
"post_tag",
|
|
1833
|
+
"nav_menu",
|
|
1834
|
+
"post_format"
|
|
1835
|
+
];
|
|
1836
|
+
const DEFAULT_IMPORTERS = ["contentrain-bridge", "@contentrain/wp-import"];
|
|
1837
|
+
const parseJson = (text) => {
|
|
1838
|
+
if (text === void 0) return void 0;
|
|
1839
|
+
try {
|
|
1840
|
+
return JSON.parse(text);
|
|
1841
|
+
} catch {
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
};
|
|
1845
|
+
const asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1846
|
+
function readModel(files, id) {
|
|
1847
|
+
const model = asRecord(parseJson(files[`.contentrain/models/${id}.json`]));
|
|
1848
|
+
return model && model.id === id ? model : void 0;
|
|
1849
|
+
}
|
|
1850
|
+
function defaultLocale(files) {
|
|
1851
|
+
const locales = asRecord(asRecord(parseJson(files[".contentrain/config.json"]))?.locales);
|
|
1852
|
+
return typeof locales?.default === "string" ? locales.default : "en";
|
|
1853
|
+
}
|
|
1854
|
+
/** The model's own directory. A custom `content_path` or locale strategy is not read — the entry is reported as not found. */
|
|
1855
|
+
const contentDir = (model) => model.content_path || model.locale_strategy && model.locale_strategy !== "file" ? void 0 : `.contentrain/content/${model.domain}/${model.id}`;
|
|
1856
|
+
/**
|
|
1857
|
+
* A markdown document's fields as raw text: each top-level frontmatter key
|
|
1858
|
+
* with its value lines (a list's items included), and `body`. Compared as
|
|
1859
|
+
* written — the store is canonical, so equal values are equal text.
|
|
1860
|
+
*/
|
|
1861
|
+
function documentFields(text) {
|
|
1862
|
+
const fields = {};
|
|
1863
|
+
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(text);
|
|
1864
|
+
if (!match) return { body: text };
|
|
1865
|
+
let key;
|
|
1866
|
+
for (const line of match[1].split("\n")) {
|
|
1867
|
+
const top = /^([^\s:#][^:]*):(.*)$/.exec(line);
|
|
1868
|
+
if (top) {
|
|
1869
|
+
key = top[1].trim();
|
|
1870
|
+
fields[key] = top[2].trim();
|
|
1871
|
+
} else if (key !== void 0) fields[key] += `\n${line}`;
|
|
1872
|
+
}
|
|
1873
|
+
fields.body = match[2];
|
|
1874
|
+
return fields;
|
|
1875
|
+
}
|
|
1876
|
+
function readEntry(files, at) {
|
|
1877
|
+
const { model, entry_id: id, locale } = at;
|
|
1878
|
+
const dir = contentDir(model);
|
|
1879
|
+
if (!dir) return void 0;
|
|
1880
|
+
if (model.kind === "document") {
|
|
1881
|
+
const text = files[model.i18n ? `${dir}/${id}/${locale}.md` : `${dir}/${id}.md`];
|
|
1882
|
+
if (text === void 0) return void 0;
|
|
1883
|
+
const meta = asRecord(parseJson(files[`.contentrain/meta/${model.id}/${id}/${locale}.json`]));
|
|
1884
|
+
return {
|
|
1885
|
+
fields: documentFields(text),
|
|
1886
|
+
meta
|
|
1887
|
+
};
|
|
1888
|
+
}
|
|
1889
|
+
if (model.kind !== "collection") return void 0;
|
|
1890
|
+
const entry = asRecord(asRecord(parseJson(files[`${dir}/${model.i18n ? locale : "data"}.json`]))?.[id]);
|
|
1891
|
+
if (!entry) return void 0;
|
|
1892
|
+
const metas = asRecord(parseJson(files[`.contentrain/meta/${model.id}/${locale}.json`]));
|
|
1893
|
+
return {
|
|
1894
|
+
fields: Object.fromEntries(Object.entries(entry).map(([key, value]) => [key, canon(value)])),
|
|
1895
|
+
meta: asRecord(metas?.[id])
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
/** The entry in `model` whose `wp_id` is `wpId`, for records the source map does not list (terms, media). */
|
|
1899
|
+
function findByWpId(files, model, wpId, locale) {
|
|
1900
|
+
const dir = contentDir(model);
|
|
1901
|
+
if (!dir) return void 0;
|
|
1902
|
+
if (model.kind === "collection") {
|
|
1903
|
+
const entries = asRecord(parseJson(files[`${dir}/${model.i18n ? locale : "data"}.json`])) ?? {};
|
|
1904
|
+
return Object.keys(entries).find((id) => asRecord(entries[id])?.wp_id === wpId);
|
|
1905
|
+
}
|
|
1906
|
+
if (model.kind !== "document") return void 0;
|
|
1907
|
+
const suffix = model.i18n ? `/${locale}.md` : ".md";
|
|
1908
|
+
for (const [path, text] of Object.entries(files)) {
|
|
1909
|
+
if (!path.startsWith(`${dir}/`) || !path.endsWith(suffix)) continue;
|
|
1910
|
+
if (documentFields(text).wp_id === String(wpId)) {
|
|
1911
|
+
const rest = path.slice(dir.length + 1, -suffix.length);
|
|
1912
|
+
if (!rest.includes("/")) return rest;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
/** Store models that can hold records of an origin type the source map does not cover. */
|
|
1917
|
+
function typeModelIds(wpType) {
|
|
1918
|
+
if (wpType === "attachment") return ["wp-media", "media"];
|
|
1919
|
+
return [`wp-tax-${wpType.replace(/_/g, "-")}`, taxModelId(wpType)];
|
|
1920
|
+
}
|
|
1921
|
+
function place(store, wpType, wpId, taxonomies) {
|
|
1922
|
+
const { files } = store;
|
|
1923
|
+
const locale = defaultLocale(files);
|
|
1924
|
+
const models = wpType ? typeModelIds(wpType).map((id) => readModel(files, id)).filter((model) => model !== void 0) : [];
|
|
1925
|
+
if (wpType && (wpType === "attachment" || taxonomies.has(wpType) || models.length)) {
|
|
1926
|
+
if (!models.length) return { unmapped: "no-model-for-type" };
|
|
1927
|
+
for (const model of models) {
|
|
1928
|
+
const entryId = findByWpId(files, model, wpId, locale);
|
|
1929
|
+
if (entryId) return {
|
|
1930
|
+
model,
|
|
1931
|
+
entry_id: entryId,
|
|
1932
|
+
locale
|
|
1933
|
+
};
|
|
1934
|
+
}
|
|
1935
|
+
return { unmapped: "entry-not-found" };
|
|
1936
|
+
}
|
|
1937
|
+
const ref = store.entry_source_map[String(wpId)];
|
|
1938
|
+
if (!ref) return { unmapped: "not-in-source-map" };
|
|
1939
|
+
const model = readModel(files, ref.model_id);
|
|
1940
|
+
if (!model) return { unmapped: "no-model-for-type" };
|
|
1941
|
+
return {
|
|
1942
|
+
model,
|
|
1943
|
+
entry_id: ref.entry_id,
|
|
1944
|
+
locale: ref.locale ?? locale
|
|
1945
|
+
};
|
|
1946
|
+
}
|
|
1947
|
+
const isPlaced = (placement) => "model" in placement;
|
|
1948
|
+
function repoEdit(meta, importers) {
|
|
1949
|
+
if (!meta) return void 0;
|
|
1950
|
+
if (meta.source === "import" && importers.has(meta.updated_by)) return void 0;
|
|
1951
|
+
return {
|
|
1952
|
+
updated_by: meta.updated_by,
|
|
1953
|
+
...meta.updated_at ? { updated_at: meta.updated_at } : {},
|
|
1954
|
+
source: meta.source
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
function changedFields(before, after) {
|
|
1958
|
+
return [...new Set([...Object.keys(before.fields), ...Object.keys(after.fields)])].filter((key) => before.fields[key] !== after.fields[key]).toSorted();
|
|
1959
|
+
}
|
|
1960
|
+
const DELETED_DETAIL = {
|
|
1961
|
+
trashed: "tombstone: trashed at the origin — it can still come back",
|
|
1962
|
+
purged: "tombstone: purged from the origin"
|
|
1963
|
+
};
|
|
1964
|
+
function planEntry(entry, input, taxonomies, importers) {
|
|
1965
|
+
const { store, incoming } = input;
|
|
1966
|
+
const out = { ...entry };
|
|
1967
|
+
const notes = entry.detail && entry.detail !== entry.deleted_kind ? [entry.detail] : [];
|
|
1968
|
+
const target = entry.op === "created" ? incoming ? place(incoming, entry.wp_type, entry.wp_id, taxonomies) : { unmapped: "not-in-source-map" } : place(store, entry.wp_type, entry.wp_id, taxonomies);
|
|
1969
|
+
if (!isPlaced(target)) {
|
|
1970
|
+
out.unmapped = target.unmapped;
|
|
1971
|
+
if (entry.op === "created" && !incoming) notes.push("no incoming export to place it");
|
|
1972
|
+
if (entry.op === "deleted" && entry.deleted_kind) notes.push(DELETED_DETAIL[entry.deleted_kind]);
|
|
1973
|
+
out.detail = notes.join("; ");
|
|
1974
|
+
return out;
|
|
1975
|
+
}
|
|
1976
|
+
out.model = target.model.id;
|
|
1977
|
+
out.entry_id = target.entry_id;
|
|
1978
|
+
out.locale = target.locale;
|
|
1979
|
+
const stored = readEntry(store.files, target);
|
|
1980
|
+
if (entry.op === "created") {
|
|
1981
|
+
if (stored) notes.push("the store already has an entry at this address");
|
|
1982
|
+
} else if (!stored) out.unmapped = "entry-not-found";
|
|
1983
|
+
if (entry.op === "deleted" && entry.deleted_kind) notes.push(DELETED_DETAIL[entry.deleted_kind]);
|
|
1984
|
+
if ((entry.op === "updated" || entry.op === "moved") && stored) {
|
|
1985
|
+
const after = incoming ? place(incoming, entry.wp_type, entry.wp_id, taxonomies) : void 0;
|
|
1986
|
+
const next = after && isPlaced(after) ? readEntry(incoming.files, after) : void 0;
|
|
1987
|
+
if (next) {
|
|
1988
|
+
out.fields_changed = changedFields(stored, next);
|
|
1989
|
+
if (isPlaced(after) && after.entry_id !== target.entry_id) out.entry_id_after = after.entry_id;
|
|
1990
|
+
} else notes.push(incoming ? "not in the incoming export" : "no incoming export to compare with");
|
|
1991
|
+
}
|
|
1992
|
+
const edit = repoEdit(stored?.meta, importers);
|
|
1993
|
+
if (edit) {
|
|
1994
|
+
out.conflict = true;
|
|
1995
|
+
out.repo_edit = edit;
|
|
1996
|
+
notes.push(`also edited in the repository by ${edit.updated_by}${edit.updated_at ? ` at ${edit.updated_at}` : ""} — not overwritten`);
|
|
1997
|
+
}
|
|
1998
|
+
if (notes.length) out.detail = notes.join("; ");
|
|
1999
|
+
return out;
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* Place a bridge's source delta in the repository's store.
|
|
2003
|
+
*
|
|
2004
|
+
* Every entry keeps the bridge's facts (op, ids, fingerprints, paths,
|
|
2005
|
+
* `deleted_kind`) and gains its store address — or `unmapped` with the reason.
|
|
2006
|
+
* `updated` and `moved` gain `fields_changed` against the incoming export; a
|
|
2007
|
+
* record the repository also edited gets `conflict: true` and `repo_edit`;
|
|
2008
|
+
* every `moved` whose address changed adds a 301 to `redirects`.
|
|
2009
|
+
*
|
|
2010
|
+
* Nothing is written and nothing is decided: a conflict is reported for a
|
|
2011
|
+
* person to resolve, and applying the plan is a separate, governed step.
|
|
2012
|
+
*/
|
|
2013
|
+
function planSourceDelta(input) {
|
|
2014
|
+
const taxonomies = new Set(input.taxonomies ?? DEFAULT_TAXONOMIES);
|
|
2015
|
+
const importers = new Set(input.importers ?? DEFAULT_IMPORTERS);
|
|
2016
|
+
const { delta } = input;
|
|
2017
|
+
const entries = delta.entries.map((entry) => planEntry(entry, input, taxonomies, importers));
|
|
2018
|
+
const redirects = [...delta.redirects ?? []];
|
|
2019
|
+
const seen = new Set(redirects.map((redirect) => redirect.from));
|
|
2020
|
+
for (const entry of entries) {
|
|
2021
|
+
if (entry.op !== "moved" || !entry.path_before || !entry.path_after || entry.path_before === entry.path_after) continue;
|
|
2022
|
+
if (seen.has(entry.path_before)) continue;
|
|
2023
|
+
seen.add(entry.path_before);
|
|
2024
|
+
redirects.push({
|
|
2025
|
+
from: entry.path_before,
|
|
2026
|
+
to: entry.path_after,
|
|
2027
|
+
status: 301
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
const warnings = [...delta.warnings ?? []];
|
|
2031
|
+
if (!delta.deletions_detectable) warnings.push("deletions could not be determined from this cursor; the absence of a deleted entry does not mean nothing was deleted");
|
|
2032
|
+
if (delta.deletions_undetectable_types?.length) warnings.push(`deletions could not be determined for: ${delta.deletions_undetectable_types.join(", ")}`);
|
|
2033
|
+
if (!input.incoming) warnings.push("no incoming export: no field-level differences and no address for created records");
|
|
2034
|
+
return {
|
|
2035
|
+
...delta,
|
|
2036
|
+
entries,
|
|
2037
|
+
...redirects.length ? { redirects } : {},
|
|
2038
|
+
...warnings.length ? { warnings } : {}
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
2041
|
+
const address = (entry) => entry.model && entry.entry_id ? `${entry.model}/${entry.entry_id}${entry.entry_id_after ? ` → ${entry.entry_id_after}` : ""}` : `unmapped (${entry.unmapped})`;
|
|
2042
|
+
/** The plan as text, for a dry run: one line per record, then conflicts, unmapped records, redirects and warnings. */
|
|
2043
|
+
function formatSourceDeltaReport(plan) {
|
|
2044
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2045
|
+
for (const entry of plan.entries) counts.set(entry.op, (counts.get(entry.op) ?? 0) + 1);
|
|
2046
|
+
const summary = [...counts].map(([op, n]) => `${n} ${op}`).join(", ") || "no changes";
|
|
2047
|
+
const { cursor } = plan;
|
|
2048
|
+
const lines = [
|
|
2049
|
+
`Source delta: ${plan.entries.length} records (${summary})`,
|
|
2050
|
+
`Cursor: ${[
|
|
2051
|
+
cursor.kind,
|
|
2052
|
+
cursor.inventory_hash ?? cursor.modified_after ?? cursor.export_id,
|
|
2053
|
+
`(${cursor.taken_at})`
|
|
2054
|
+
].filter(Boolean).join(" ")}`,
|
|
2055
|
+
`Deletions: ${plan.deletions_detectable ? "detectable" : "NOT detectable"}`,
|
|
2056
|
+
""
|
|
2057
|
+
];
|
|
2058
|
+
for (const entry of plan.entries) {
|
|
2059
|
+
const parts = [`${entry.op.padEnd(8)} ${entry.wp_type ?? "?"} ${entry.wp_id}`, address(entry)];
|
|
2060
|
+
if (entry.op === "moved" && entry.path_before) parts.push(`${entry.path_before} → ${entry.path_after}`);
|
|
2061
|
+
if (entry.deleted_kind) parts.push(entry.deleted_kind);
|
|
2062
|
+
if (entry.fields_changed) parts.push(`fields: ${entry.fields_changed.join(", ") || "none"}`);
|
|
2063
|
+
if (entry.conflict) parts.push("CONFLICT");
|
|
2064
|
+
lines.push(` ${parts.join(" ")}`);
|
|
2065
|
+
}
|
|
2066
|
+
const conflicts = plan.entries.filter((entry) => entry.conflict);
|
|
2067
|
+
const unmapped = plan.entries.filter((entry) => entry.unmapped);
|
|
2068
|
+
lines.push("", `Conflicts (${conflicts.length})${conflicts.length ? ":" : ""}`);
|
|
2069
|
+
for (const entry of conflicts) lines.push(` ${entry.wp_type} ${entry.wp_id} → ${address(entry)}: edited by ${entry.repo_edit?.updated_by}${entry.repo_edit?.updated_at ? ` at ${entry.repo_edit.updated_at}` : ""}`);
|
|
2070
|
+
lines.push(`Unmapped (${unmapped.length})${unmapped.length ? ":" : ""}`);
|
|
2071
|
+
for (const entry of unmapped) lines.push(` ${entry.op} ${entry.wp_type} ${entry.wp_id}: ${entry.unmapped}`);
|
|
2072
|
+
lines.push(`Redirects (${plan.redirects?.length ?? 0})${plan.redirects?.length ? ":" : ""}`);
|
|
2073
|
+
for (const redirect of plan.redirects ?? []) lines.push(` ${redirect.from} → ${redirect.to} (${redirect.status})`);
|
|
2074
|
+
for (const warning of plan.warnings ?? []) lines.push(`Warning: ${warning}`);
|
|
2075
|
+
lines.push("", "Dry run: nothing was written. Applying a plan goes through review.");
|
|
2076
|
+
return `${lines.join("\n")}\n`;
|
|
2077
|
+
}
|
|
2078
|
+
//#endregion
|
|
2079
|
+
export { DEFAULT_IMPORTERS, DEFAULT_TAXONOMIES, PLUGIN_META, SKIP_TYPES, buildCommentsExport, canon, fetchRestRawIR, formatSourceDeltaReport, hexId, looksSerialized, parseWxr, phpUnserialize, planSourceDelta, rawToContentrain, slugify, strip, summarizeComments, taxModelId, tryUnserialize };
|
|
1808
2080
|
|
|
1809
2081
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["iso"],"sources":["../src/php-unserialize.ts","../src/core.ts","../src/wxr.ts","../src/rest.ts","../src/contentrain.ts","../src/comments.ts"],"sourcesContent":["// Small PHP serialize() decoder, ported from the measured import chain.\n// Supported: s (byte-length strings), i, d, b, N, a (array → list or object), O (object → { __class, … }).\n// PHP string lengths are BYTES, so the walk happens over a Buffer, not a JS string.\n\nexport interface UnserializeResult {\n ok: boolean\n serialized: boolean\n value: unknown\n error?: string\n}\n\nexport function looksSerialized(s: unknown): s is string {\n return typeof s === 'string' && /^(a|O):\\d+:[{\"]|^(s|i|d|b):[^;]+;?$|^N;$/.test(s)\n}\n\nexport function phpUnserialize(str: string): unknown {\n const buf = Buffer.from(str, 'utf8')\n let p = 0\n const fail = (m: string): never => {\n throw new Error(`unserialize: ${m} at byte ${p}`)\n }\n const readUntil = (ch: string): string => {\n const i = buf.indexOf(ch, p)\n if (i < 0) fail(`expected ${ch}`)\n const s = buf.toString('utf8', p, i)\n p = i + 1\n return s\n }\n const expect = (ch: string): void => {\n if (buf[p] !== ch.charCodeAt(0)) fail(`expected '${ch}' got '${String.fromCharCode(buf[p] ?? 0)}'`)\n p++\n }\n const value = (): unknown => {\n const t = String.fromCharCode(buf[p] ?? 0)\n p++\n switch (t) {\n case 'N':\n expect(';')\n return null\n case 'b': {\n expect(':')\n return readUntil(';') === '1'\n }\n case 'i': {\n expect(':')\n return Number(readUntil(';'))\n }\n case 'd': {\n expect(':')\n const v = readUntil(';')\n return v === 'INF' ? Infinity : v === '-INF' ? -Infinity : v === 'NAN' ? Number.NaN : Number(v)\n }\n case 's': {\n expect(':')\n const n = Number(readUntil(':'))\n expect('\"')\n const s = buf.toString('utf8', p, p + n)\n p += n\n expect('\"')\n expect(';')\n return s\n }\n case 'a': {\n expect(':')\n const n = Number(readUntil(':'))\n expect('{')\n const entries: Array<[unknown, unknown]> = []\n for (let i = 0; i < n; i++) {\n const k = value()\n const v = value()\n entries.push([k, v])\n }\n expect('}')\n // consecutive 0..n-1 integer keys → JSON list; anything else → object\n const isList = entries.every(([k], i) => k === i)\n return isList ? entries.map(([, v]) => v) : Object.fromEntries(entries.map(([k, v]) => [String(k), v]))\n }\n case 'O': {\n expect(':')\n readUntil(':')\n expect('\"')\n const cls = readUntil('\"')\n expect(':')\n const n = Number(readUntil(':'))\n expect('{')\n const o: Record<string, unknown> = { __class: cls }\n for (let i = 0; i < n; i++) {\n const k = value()\n const v = value()\n o[String(k).split('\\u0000').at(-1) ?? String(k)] = v\n }\n expect('}')\n return o\n }\n default:\n return fail(`unknown type '${t}'`)\n }\n }\n const v = value()\n if (p !== buf.length) fail(`trailing ${buf.length - p} bytes`)\n return v\n}\n\n/** Attempt a decode; on failure the value stays exactly as it was. */\nexport function tryUnserialize(s: string): UnserializeResult {\n if (!looksSerialized(s)) return { ok: false, serialized: false, value: s }\n try {\n return { ok: true, serialized: true, value: phpUnserialize(s) }\n } catch (e) {\n return { ok: false, serialized: true, value: s, error: e instanceof Error ? e.message : String(e) }\n }\n}\n","// Shared conversion helpers, ported from the measured induction core.\n// The identity formulas here are load-bearing: hexId('posts:' + slug) must\n// produce the same entry id whether the site arrived via WXR or REST, or a\n// later delta import would duplicate every entry.\n\nimport { createHash } from 'node:crypto'\nimport type { FieldDef } from '@contentrain/types'\n\nexport const norm = (s: unknown): string => String(s ?? '').replace(/\\s+/g, ' ').trim()\n\nexport const strip = (s: unknown): string =>\n norm(\n String(s ?? '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/&#(\\d+);/g, (_m, d: string) => String.fromCodePoint(Number(d)))\n .replace(/&/g, '&'),\n )\n\n/** Entry id: deterministic hex from a stable key (model:slug style). */\nexport const hexId = (s: string): string => createHash('sha1').update(s).digest('hex').slice(0, 24)\n\nconst sortDeep = (v: unknown): unknown =>\n Array.isArray(v)\n ? v.map(sortDeep)\n : v && typeof v === 'object'\n ? Object.fromEntries(\n Object.keys(v as Record<string, unknown>)\n .toSorted()\n .map((k) => [k, sortDeep((v as Record<string, unknown>)[k])]),\n )\n : v\n\n/** Canonical serialization: sorted keys, 2-space indent, trailing newline. */\nexport const canon = (o: unknown): string => `${JSON.stringify(sortDeep(o), null, 2)}\\n`\n\n/** Field-type inference from a value (open meta / ACF fields). */\nexport const byValue = (v: unknown): string | null => {\n if (typeof v === 'boolean') return 'boolean'\n if (typeof v === 'number') return Number.isInteger(v) ? 'integer' : 'decimal'\n if (Array.isArray(v)) return 'array'\n if (v && typeof v === 'object') return 'object'\n const s = String(v ?? '').trim()\n if (!s) return null\n if (/^https?:\\/\\/\\S+\\.(mp3|m4a|wav|pdf|zip|docx?)(\\?|$)/i.test(s)) return 'file'\n if (/^https?:\\/\\/\\S+\\.(jpe?g|png|gif|webp|avif|svg)(\\?|$)/i.test(s)) return 'image'\n if (/^https?:\\/\\//.test(s)) return 'url'\n if (/^[^@\\s]+@[^@\\s]+\\.[a-z]{2,}$/i.test(s)) return 'email'\n if (/^\\d{4}-\\d{2}-\\d{2}([T ]\\d{2}:\\d{2})/.test(s)) return 'datetime'\n if (/<[a-z][\\s\\S]*>/i.test(s)) return 'richtext'\n return s.length > 200 ? 'text' : 'string'\n}\n\n/**\n * WP post types that never become content models: core plumbing (`wp_*`\n * covers `wp_global_styles`, `wp_navigation`, `wp_block`, `wp_template*`),\n * Jetpack, and theme/page-builder internals that store design, not content —\n * GeneratePress elements, GenerateBlocks styles, Elementor templates and\n * Contact Form 7 / WPForms definitions. ACF field/group records stay in:\n * their cross-model parent links are part of the lossless import contract.\n * Whether a stored record gets a public route is the route producer's decision.\n */\nexport const SKIP_TYPES =\n /^(attachment|nav_menu_item|wp_|jp_|pattern|revision|oembed_cache|customize_changeset|user_request|custom_css|gblocks_|gblocks-|gp_elements|gp-elements|elementor_library|e-landing-page|wpcf7_contact_form|wpforms)/\n\n/** Open meta keys that are plugin plumbing, not editorial fields. */\nexport const PLUGIN_META = /^(jetpack|wpdc|discourse|footnotes|inline_featured|spay_|advanced_seo|rank_math|yoast)/i\n\n/** WP core/plumbing meta keys (never editorial). */\nexport const CORE_META =\n /^(_edit_last|_edit_lock|_thumbnail_id|_wp_page_template|_wp_old_slug|_wp_attached_file|_wp_attachment_metadata|_wp_attachment_image_alt|_wp_attachment_backup_sizes|_wp_desired_post_slug|_wp_trash_meta_.*|_menu_item_.*|_oembed_.*|_publicize_.*|_wpas_.*|_pingme|_encloseme|enclosure|_last_editor_used_jetpack|_rest_api_.*|_wp_attachment_context|_wp_attachment_is_custom_header|_wp_attachment_is_custom_background|_customize_.*|_elementor_.*|_wpcom_.*|_wp_suggested_privacy_policy_content|geo_.*|_jetpack_.*|_links_to.*|_format_.*)$/\n\n/** Taxonomy → model id. */\nexport const taxModelId = (t: string): string =>\n t === 'post_tag' ? 'tags' : t === 'category' ? 'categories' : t.replace(/_/g, '-')\n\nexport const slugify = (s: unknown): string =>\n String(s ?? '')\n .normalize('NFKD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n\nexport const decodeSlug = (s: string): string => {\n try {\n return decodeURIComponent(s)\n } catch {\n return s\n }\n}\n\n/** The measured core field skeleton for content models (same order/labels). */\nexport function coreFields(opts: {\n excerpt: boolean\n body: boolean\n date: boolean\n author: boolean\n taxes: string[]\n cover: boolean\n}): { fields: Record<string, FieldDef>; order: number } {\n const fields: Record<string, FieldDef> = {\n title: { type: 'string', required: true, label: 'Title', order: 10 },\n slug: { type: 'slug', required: true, unique: true, label: 'Slug', order: 20 },\n }\n let o = 30\n if (opts.excerpt) fields.excerpt = { type: 'text', label: 'Excerpt', order: (o += 10) }\n if (opts.body) fields.body = { type: 'richtext', label: 'Body', order: (o += 10) }\n if (opts.date) fields.published_at = { type: 'datetime', label: 'Published at', order: (o += 10) }\n if (opts.author) fields.author = { type: 'relation', model: 'authors', label: 'Author', order: (o += 10) }\n for (const t of opts.taxes) fields[taxModelId(t)] = { type: 'relations', model: taxModelId(t), label: t, order: (o += 10) }\n if (opts.cover) fields.cover = { type: 'relation', model: 'media', label: 'Cover', order: (o += 10) }\n return { fields, order: o }\n}\n","// WXR (WordPress eXtended RSS) → RawIR.\n//\n// Streaming (sax) parse: a 100 MB export holds only its records in memory,\n// never the XML. Ported from the measured import chain — the state machine,\n// the meta decoding, and the resolution passes are the parts three sessions\n// of corpus work already debugged; they are transliterated, not redesigned.\n\nimport sax from 'sax'\nimport type { Readable } from 'node:stream'\nimport type {\n RawIR,\n RawPost,\n RawLanguagePair,\n RawAttachment,\n RawAuthor,\n RawComment,\n RawMenu,\n RawMenuItem,\n RawMenuTarget,\n RawTerm,\n RawTermRef,\n RawAcfValue,\n} from '@contentrain/types'\nimport { MIGRATION_CONTRACT_VERSION } from '@contentrain/types'\nimport { tryUnserialize } from './php-unserialize.js'\nimport { strip } from './core.js'\n\nexport interface WxrStats {\n serialized: { detected: number; decoded: number; failed: number; keys: string[] }\n acf_fields: number\n unresolved_terms: number\n counts: Record<string, number>\n}\n\nexport interface WxrParseResult {\n raw: RawIR\n stats: WxrStats\n}\n\ninterface XItem {\n [k: string]: unknown\n terms: Array<{ taxonomy: string; slug: string; name?: string }>\n postmeta: Array<{ key?: string; value?: string }>\n comments: Array<Record<string, string | Array<{ key?: string; value?: string }>>>\n}\n\nconst ts = (s: string | undefined): string | null =>\n !s || s.startsWith('0000-00-00') ? null : s.replace(' ', 'T')\nconst iso = (local: string | undefined, gmt: string | undefined): string | null =>\n gmt && !gmt.startsWith('0000') ? `${ts(gmt)}Z` : ts(local)\nconst num = (s: unknown): number => (s === '' || s == null ? 0 : Number(s))\nconst termKey = (tax: string, slug: string): string => `${tax}:${slug}`\n\nexport async function parseWxr(input: string | Readable, opts?: { tool?: string }): Promise<WxrParseResult> {\n const site: Record<string, unknown> = {}\n const authors: Array<Record<string, string>> = []\n const terms: Array<Record<string, string>> = []\n const items: XItem[] = []\n const stats: WxrStats = {\n serialized: { detected: 0, decoded: 0, failed: 0, keys: [] },\n acf_fields: 0,\n unresolved_terms: 0,\n counts: {},\n }\n const serializedKeySet = new Set<string>()\n\n // ── sax state machine (ported verbatim) ──\n const parser = sax.createStream(true, { trim: false })\n const path: string[] = []\n let text = ''\n let item: XItem | null = null\n let meta: { key?: string; value?: string } | null = null\n let comment: Record<string, string | Array<{ key?: string; value?: string }>> | null = null\n let cmeta: { key?: string; value?: string } | null = null\n let author: Record<string, string> | null = null\n let term: (Record<string, string> & { _tag?: string }) | null = null\n const channelText: Record<string, string> = {}\n\n const leaf = (name: string, into: Record<string, unknown>, map: Record<string, string>): void => {\n const target = map[name]\n if (target) into[target] = text\n }\n parser.on('opentag', (node) => {\n path.push(node.name)\n text = ''\n const n = node.name\n if (n === 'item') item = { terms: [], postmeta: [], comments: [] }\n else if (n === 'wp:postmeta') meta = {}\n else if (n === 'wp:comment') comment = { meta: [] }\n else if (n === 'wp:commentmeta') cmeta = {}\n else if (n === 'wp:author') author = {}\n else if (n === 'wp:category' || n === 'wp:tag' || n === 'wp:term') term = { _tag: n }\n else if (n === 'category' && item) {\n const attrs = node.attributes as Record<string, string>\n item.terms.push({ taxonomy: attrs.domain ?? '', slug: attrs.nicename ?? '' })\n }\n })\n parser.on('text', (t) => {\n text += t\n })\n parser.on('cdata', (t) => {\n text += t\n })\n parser.on('comment', (c) => {\n const m = /generator=\"([^\"]*)\"\\s+created=\"([^\"]*)\"/.exec(c)\n if (m) {\n site.generator = m[1]\n site.export_date = m[2]\n }\n })\n parser.on('closetag', (n) => {\n const parent = path[path.length - 2]\n if (cmeta) {\n if (n === 'wp:meta_key') cmeta.key = text\n else if (n === 'wp:meta_value') cmeta.value = text\n else if (n === 'wp:commentmeta') {\n ;(comment!.meta as Array<{ key?: string; value?: string }>).push(cmeta)\n cmeta = null\n }\n } else if (comment) {\n const map: Record<string, string> = {\n 'wp:comment_id': 'id',\n 'wp:comment_author': 'author',\n 'wp:comment_author_email': 'email',\n 'wp:comment_author_url': 'url',\n 'wp:comment_date': 'date',\n 'wp:comment_date_gmt': 'date_gmt',\n 'wp:comment_content': 'content',\n 'wp:comment_approved': 'approved',\n 'wp:comment_type': 'type',\n 'wp:comment_parent': 'parent',\n 'wp:comment_user_id': 'user_id',\n }\n leaf(n, comment, map)\n if (n === 'wp:comment') {\n item!.comments.push(comment)\n comment = null\n }\n } else if (meta) {\n if (n === 'wp:meta_key') meta.key = text\n else if (n === 'wp:meta_value') meta.value = text\n else if (n === 'wp:postmeta') {\n item!.postmeta.push(meta)\n meta = null\n }\n } else if (item) {\n if (n === 'category') {\n const t = item.terms.at(-1)\n if (t) t.name = text\n } else {\n const map: Record<string, string> = {\n title: 'title',\n link: 'link',\n 'dc:creator': 'author',\n guid: 'guid',\n description: 'description',\n 'content:encoded': 'content',\n 'excerpt:encoded': 'excerpt',\n 'wp:post_id': 'id',\n 'wp:post_date': 'date',\n 'wp:post_date_gmt': 'date_gmt',\n 'wp:post_modified': 'modified',\n 'wp:post_modified_gmt': 'modified_gmt',\n 'wp:comment_status': 'comment_status',\n 'wp:ping_status': 'ping_status',\n 'wp:post_name': 'slug',\n 'wp:status': 'status',\n 'wp:post_parent': 'parent',\n 'wp:menu_order': 'menu_order',\n 'wp:post_type': 'type',\n 'wp:post_password': 'password',\n 'wp:is_sticky': 'sticky',\n 'wp:attachment_url': 'attachment_url',\n }\n leaf(n, item, map)\n if (n === 'item') {\n items.push(item)\n item = null\n }\n }\n } else if (author) {\n leaf(n, author, {\n 'wp:author_id': 'id',\n 'wp:author_login': 'login',\n 'wp:author_email': 'email',\n 'wp:author_display_name': 'display_name',\n 'wp:author_first_name': 'first_name',\n 'wp:author_last_name': 'last_name',\n })\n if (n === 'wp:author') {\n authors.push(author)\n author = null\n }\n } else if (term) {\n leaf(n, term, {\n 'wp:term_id': 'id',\n 'wp:category_nicename': 'slug',\n 'wp:category_parent': 'parent',\n 'wp:cat_name': 'name',\n 'wp:category_description': 'description',\n 'wp:tag_slug': 'slug',\n 'wp:tag_name': 'name',\n 'wp:tag_description': 'description',\n 'wp:term_taxonomy': 'taxonomy',\n 'wp:term_slug': 'slug',\n 'wp:term_parent': 'parent',\n 'wp:term_name': 'name',\n 'wp:term_description': 'description',\n })\n if (n === term._tag) {\n term.taxonomy = term._tag === 'wp:category' ? 'category' : term._tag === 'wp:tag' ? 'post_tag' : (term.taxonomy ?? '')\n delete term._tag\n terms.push(term)\n term = null\n }\n } else if (parent === 'channel') {\n leaf(n, channelText, {\n title: 'title',\n link: 'link',\n description: 'description',\n language: 'language',\n 'wp:wxr_version': 'wxr_version',\n 'wp:base_site_url': 'base_site_url',\n 'wp:base_blog_url': 'base_blog_url',\n })\n }\n path.pop()\n text = ''\n })\n\n await new Promise<void>((resolve, reject) => {\n parser.on('end', () => resolve())\n parser.on('error', reject)\n if (typeof input === 'string') {\n parser.end(input)\n } else {\n input.pipe(parser)\n }\n })\n Object.assign(site, channelText)\n\n // ── post-processing: meta decode, ACF pairing, entity split, resolution flags ──\n const decodeMeta = (list: Array<{ key?: string; value?: string }>): { meta: Record<string, unknown>; serializedKeys: string[] } => {\n const out: Record<string, unknown> = {}\n const serializedKeys: string[] = []\n for (const { key, value } of list) {\n if (!key) continue\n const r = tryUnserialize(value ?? '')\n if (r.serialized) {\n stats.serialized.detected++\n serializedKeySet.add(key)\n if (r.ok) {\n stats.serialized.decoded++\n serializedKeys.push(key)\n } else stats.serialized.failed++\n }\n const v = r.ok ? r.value : (value ?? '')\n out[key] = key in out ? ([] as unknown[]).concat(out[key] as unknown[], [v]) : v\n }\n return { meta: out, serializedKeys }\n }\n const extractAcf = (metaObj: Record<string, unknown>): Record<string, RawAcfValue> => {\n const acf: Record<string, RawAcfValue> = {}\n for (const [k, v] of Object.entries(metaObj)) {\n if (k.startsWith('_') && typeof v === 'string' && /^field_[0-9a-f]+$/.test(v) && k.slice(1) in metaObj) {\n acf[k.slice(1)] = { value: metaObj[k.slice(1)], field_key: v }\n }\n }\n return acf\n }\n\n const termSet = new Set(terms.map((t) => termKey(t.taxonomy ?? '', t.slug ?? '')))\n\n interface Rec extends RawPost {\n attachment_url: string | null\n _comments: RawComment[]\n }\n const records: Rec[] = items.map((it) => {\n const { meta: m, serializedKeys } = decodeMeta(it.postmeta)\n const acf = extractAcf(m)\n stats.acf_fields += Object.keys(acf).length\n const id = num(it.id)\n const termRefs: RawTermRef[] = it.terms.map((t) => ({\n taxonomy: t.taxonomy,\n slug: t.slug,\n name: t.name ?? '',\n resolved: termSet.has(termKey(t.taxonomy, t.slug)),\n }))\n const comments: RawComment[] = it.comments.map((c) => ({\n id: num(c.id),\n post: id,\n post_type: (it.type as string) || 'post',\n parent: num(c.parent) || null,\n parent_resolved: null,\n author: (c.author as string) ?? '',\n email: (c.email as string) || null,\n url: (c.url as string) || null,\n date: iso(c.date as string, c.date_gmt as string),\n date_gmt: ts(c.date_gmt as string),\n content: (c.content as string) ?? '',\n approved: (c.approved as string | undefined) ?? '1',\n type: (c.type as string) || 'comment',\n user_id: num(c.user_id) || null,\n meta: decodeMeta((c.meta as Array<{ key?: string; value?: string }>) ?? []).meta,\n }))\n return {\n id,\n type: (it.type as string) || 'post',\n status: (it.status as string) || 'publish',\n slug: (it.slug as string) || '',\n title: (it.title as string) ?? '',\n link: (it.link as string) || null,\n guid: (it.guid as string) || null,\n author: (it.author as string) || null,\n date: iso(it.date as string, it.date_gmt as string),\n modified: iso(it.modified as string, it.modified_gmt as string),\n content: (it.content as string) ?? '',\n excerpt: (it.excerpt as string) ?? '',\n parent: num(it.parent) || null,\n menu_order: num(it.menu_order),\n sticky: it.sticky === '1',\n password: (it.password as string) || null,\n comment_status: (it.comment_status as string) || null,\n ping_status: (it.ping_status as string) || null,\n terms: termRefs,\n meta: m,\n serialized_keys: serializedKeys,\n acf,\n // Polylang exports a post's language as a `language` taxonomy term.\n lang: termRefs.find((t) => t.taxonomy === 'language')?.slug || null,\n attachment_url: (it.attachment_url as string) || null,\n _comments: comments,\n }\n })\n for (const r of records) {\n for (const t of r.terms) if (!t.resolved && r.type !== 'nav_menu_item') stats.unresolved_terms++\n }\n\n const byId = new Map(records.map((r) => [r.id, r]))\n const posts: RawPost[] = records\n .filter((r) => r.type !== 'attachment' && r.type !== 'nav_menu_item')\n .map(({ attachment_url: _a, _comments: _c, ...p }) => p)\n // Polylang groups translations under a shared `post_translations` term\n // (`pll_…`); every post in a group is a translation of the others.\n const groups = new Map<string, RawPost[]>()\n for (const p of posts) {\n const group = p.terms.find((t) => t.taxonomy === 'post_translations')?.slug\n if (!group) continue\n groups.set(group, [...(groups.get(group) ?? []), p])\n }\n const languagePairs: RawLanguagePair[] = []\n for (const members of groups.values()) {\n const translations: Record<string, number> = {}\n for (const m of members) if (m.lang) translations[m.lang] = m.id\n if (Object.keys(translations).length > 1) languagePairs.push({ post: members[0]!.id, translations })\n }\n const attachments: RawAttachment[] = records\n .filter((r) => r.type === 'attachment')\n .map((r) => ({\n id: r.id,\n title: r.title,\n slug: r.slug,\n url: r.attachment_url ?? r.guid ?? null,\n alt: typeof r.meta._wp_attachment_image_alt === 'string' ? r.meta._wp_attachment_image_alt : '',\n caption: r.excerpt,\n description: r.content,\n file: typeof r.meta._wp_attached_file === 'string' ? r.meta._wp_attached_file : null,\n image_meta: r.meta._wp_attachment_metadata ?? null,\n mime: guessMime(r.attachment_url ?? r.guid ?? ''),\n parent: r.parent,\n parent_resolved: r.parent ? byId.has(r.parent) : null,\n author: r.author,\n date: r.date,\n status: r.status,\n meta: r.meta,\n }))\n\n // ── menus: nav_menu terms + nav_menu_item posts (targets from meta) ──\n const postById = new Map(posts.map((p) => [p.id, p]))\n const termByIdNum = new Map(terms.map((t) => [num(t.id), t]))\n const menus: RawMenu[] = terms\n .filter((t) => t.taxonomy === 'nav_menu')\n .map((mt) => ({ id: num(mt.id) || null, slug: mt.slug ?? '', name: mt.name ?? '', items: [] as RawMenuItem[] }))\n const menuBySlug = new Map(menus.map((m) => [m.slug, m]))\n for (const r of records.filter((x) => x.type === 'nav_menu_item')) {\n const m = r.meta\n const type = m._menu_item_type as string | undefined\n const object = m._menu_item_object as string | undefined\n const objectId = num(m._menu_item_object_id)\n let target: RawMenuTarget\n if (type === 'custom') target = { kind: 'url', url: String(m._menu_item_url ?? ''), resolved: true }\n else if (type === 'post_type') {\n const p = postById.get(objectId)\n target = { kind: 'post', post_type: object ?? '', id: objectId || null, slug: p?.slug ?? null, resolved: !!p }\n } else if (type === 'taxonomy') {\n const t = termByIdNum.get(objectId)\n target = { kind: 'term', taxonomy: object ?? '', id: objectId || null, slug: t?.slug ?? null, resolved: !!t }\n } else if (type === 'post_type_archive') target = { kind: 'archive', post_type: object ?? '', resolved: true }\n else target = { kind: 'unknown', resolved: false }\n const parent = num(m._menu_item_menu_item_parent) || null\n const itemRec: RawMenuItem = {\n id: r.id,\n title: r.title || (('slug' in target && target.slug) || '') || '',\n order: r.menu_order,\n parent,\n url: (m._menu_item_url as string) || null,\n target,\n target_attr: (m._menu_item_target as string) || null,\n classes: ([] as string[]).concat((m._menu_item_classes as string[]) ?? []).filter(Boolean),\n description: r.content,\n status: r.status,\n }\n const menuSlug = r.terms.find((t) => t.taxonomy === 'nav_menu')?.slug\n const menu = menuSlug ? menuBySlug.get(menuSlug) : undefined\n if (menu) menu.items.push(itemRec)\n }\n const allMenuItemIds = new Set(menus.flatMap((m) => m.items.map((i) => i.id)))\n for (const m of menus) {\n for (const i of m.items) if (i.parent && !allMenuItemIds.has(i.parent)) i.parent_unresolved = true\n }\n\n // ── comments: threaded, parent resolution within the same post ──\n const comments: RawComment[] = []\n for (const r of records) {\n const ids = new Set(r._comments.map((c) => c.id))\n for (const c of r._comments) comments.push({ ...c, parent_resolved: c.parent ? ids.has(c.parent) : null })\n }\n\n const rawAuthors: RawAuthor[] = authors.map((a) => ({\n id: num(a.id) || null,\n login: a.login ?? '',\n email: a.email || null,\n display_name: a.display_name || a.login || '',\n first_name: a.first_name || null,\n last_name: a.last_name || null,\n }))\n const rawTerms: RawTerm[] = terms\n .filter((t) => t.taxonomy !== 'nav_menu')\n .map((t) => ({\n id: num(t.id) || null,\n taxonomy: t.taxonomy ?? '',\n slug: t.slug ?? '',\n name: t.name ?? '',\n parent: t.parent || null,\n parent_resolved: t.parent ? termSet.has(termKey(t.taxonomy ?? '', t.parent)) : null,\n description: t.description || '',\n }))\n\n stats.serialized.keys = [...serializedKeySet].toSorted()\n stats.counts = {\n items: records.length,\n posts: posts.length,\n attachments: attachments.length,\n terms: rawTerms.length,\n authors: rawAuthors.length,\n menus: menus.length,\n comments: comments.length,\n }\n\n const raw: RawIR = {\n version: MIGRATION_CONTRACT_VERSION,\n provenance: { kind: 'wxr', tool: opts?.tool ?? '@contentrain/wp-import' },\n site: {\n url: String(site.link ?? channelText.link ?? ''),\n title: strip(channelText.title),\n description: strip(channelText.description) || undefined,\n base_site_url: channelText.base_site_url,\n base_blog_url: channelText.base_blog_url,\n language: channelText.language ?? null,\n generator: (site.generator as string) ?? null,\n export_date: (site.export_date as string) ?? null,\n wxr_version: channelText.wxr_version ?? null,\n },\n authors: rawAuthors,\n terms: rawTerms,\n posts,\n attachments,\n menus,\n comments,\n ...(languagePairs.length ? { language_pairs: languagePairs } : {}),\n }\n return { raw, stats }\n}\n\nconst MIME: Record<string, string> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n gif: 'image/gif',\n webp: 'image/webp',\n svg: 'image/svg+xml',\n mp3: 'audio/mpeg',\n mp4: 'video/mp4',\n pdf: 'application/pdf',\n}\nfunction guessMime(u: string): string | null {\n const e = (u.split('?')[0]?.split('.').pop() ?? '').toLowerCase()\n return MIME[e] ?? null\n}\n","// Public/authenticated WordPress REST → RawIR.\n//\n// The lowest rungs of the source-access ladder. What REST cannot see (menus,\n// rest:false CPTs, unregistered meta) is simply absent from the result —\n// absence at a low rung is information, not an error, and the manifest layer\n// decides what to recommend about it.\n\nimport type { RawIR, RawAttachment, RawComment, RawLanguagePair, RawPost, RawTerm, RawTermRef, SourceAccessKind } from '@contentrain/types'\nimport { MIGRATION_CONTRACT_VERSION } from '@contentrain/types'\nimport { strip, SKIP_TYPES } from './core.js'\n\nconst iso = (gmt: string | undefined): string | null => (gmt ? `${gmt}Z` : null)\nconst approvedOf = (status: string | undefined): RawComment['approved'] =>\n status === 'approved' ? '1' : status === 'hold' ? '0' : status === 'spam' ? 'spam' : status === 'trash' ? 'trash' : (status ?? '1')\n\nexport interface RestImportOptions {\n origin: string\n /** Injectable for tests and for hosts that need custom dispatch. */\n fetchImpl?: typeof fetch\n /** WordPress Application Password credentials — lifts the rung to rest_auth. */\n auth?: { user: string; appPassword: string }\n perPage?: number\n /**\n * Requests in flight at once, across every collection. Default 4. A site\n * with 74 pages of posts, 40 of media and 37 of comments used to receive\n * ~150 simultaneous requests — enough to trip a host's rate limit or WAF.\n */\n concurrency?: number\n /**\n * Pages fetched per collection at most. Unset = all. A truncated collection\n * is named in `warnings` (with what was skipped), never silently shortened.\n */\n maxPages?: number\n tool?: string\n}\n\nexport interface RestImportResult {\n raw: RawIR\n warnings: string[]\n}\n\ninterface RestPost {\n id: number\n slug: string\n status?: string\n type?: string\n link?: string\n title?: { rendered?: string }\n content?: { rendered?: string }\n excerpt?: { rendered?: string }\n date_gmt?: string\n modified_gmt?: string\n author?: number\n parent?: number\n menu_order?: number\n sticky?: boolean\n comment_status?: string\n ping_status?: string\n featured_media?: number\n categories?: number[]\n tags?: number[]\n meta?: Record<string, unknown>\n /** Polylang: language slug of the post and its translation group (`{ en: 12, tr: 34 }`, self included). */\n lang?: string\n translations?: Record<string, number>\n /** WPML: locale of the post and its other translations. */\n wpml_current_locale?: string\n wpml_translations?: Array<{ locale?: string; id?: number }>\n}\n\nconst positiveInteger = (name: string, value: number): number => {\n if (!Number.isSafeInteger(value) || value < 1) throw new RangeError(`${name} must be a positive safe integer`)\n return value\n}\n\nexport async function fetchRestRawIR(options: RestImportOptions): Promise<RestImportResult> {\n const origin = options.origin.replace(/\\/$/, '')\n const doFetch = options.fetchImpl ?? fetch\n const perPage = positiveInteger('perPage', options.perPage ?? 100)\n if (perPage > 100) throw new RangeError('perPage must be at most 100')\n const concurrency = positiveInteger('concurrency', options.concurrency ?? 4)\n const maxPages = options.maxPages !== undefined ? positiveInteger('maxPages', options.maxPages) : undefined\n const warnings: string[] = []\n // One pool for the whole import: page fetches of every collection queue here,\n // so the cap holds even though collections are requested \"in parallel\".\n const queue: Array<() => Promise<void>> = []\n let running = 0\n const schedule = <T>(task: () => Promise<T>): Promise<T> =>\n new Promise<T>((resolve, reject) => {\n queue.push(() => task().then(resolve, reject))\n pump()\n })\n const pump = (): void => {\n while (running < concurrency && queue.length) {\n const task = queue.shift()!\n running++\n void task().finally(() => {\n running--\n pump()\n })\n }\n }\n const headers: Record<string, string> = { accept: 'application/json' }\n if (options.auth) {\n headers.authorization = `Basic ${Buffer.from(`${options.auth.user}:${options.auth.appPassword}`).toString('base64')}`\n }\n\n const getAll = async <T>(path: string): Promise<T[]> => {\n const url = (page: number) => `${origin}/wp-json/wp/v2/${path}${path.includes('?') ? '&' : '?'}per_page=${perPage}&page=${page}`\n // Hold the slot until the body is consumed, not merely until headers arrive.\n const pageData = (page: number) => schedule(async () => {\n const response = await doFetch(url(page), { headers })\n if (!response.ok) {\n warnings.push(`${path}: page ${page} HTTP ${response.status} — skipped`)\n await response.body?.cancel()\n return { items: [] as T[], pages: 1 }\n }\n const pages = Number(response.headers.get('x-wp-totalpages') ?? '1')\n if (!Number.isSafeInteger(pages) || pages < 0) throw new Error(`${path}: invalid x-wp-totalpages`)\n const items = await response.json() as T[]\n if (!Array.isArray(items)) throw new Error(`${path}: page ${page} is not a collection`)\n return { items, pages: Math.max(1, pages) }\n })\n const first = await pageData(1)\n const totalPages = first.pages\n const firstPage = first.items\n const wanted = maxPages !== undefined ? Math.min(totalPages, maxPages) : totalPages\n if (wanted < totalPages) {\n warnings.push(`${path}: ${totalPages} pages, fetched ${wanted} (maxPages) — ${totalPages - wanted} pages skipped`)\n }\n if (wanted <= 1) return firstPage\n const rest = await Promise.all(\n Array.from({ length: wanted - 1 }, (_v, i) =>\n pageData(i + 2).then((r) => r.items),\n ),\n )\n return firstPage.concat(...rest)\n }\n\n interface RestType { slug: string; rest_base?: string }\n const typesResp = await doFetch(`${origin}/wp-json/wp/v2/types`, { headers })\n const types: Record<string, RestType> = typesResp.ok ? ((await typesResp.json()) as Record<string, RestType>) : {}\n if (!typesResp.ok) warnings.push(`types: HTTP ${typesResp.status} — importing posts and pages only`)\n const postTypes = Object.values(types).filter(\n (t) => t.rest_base && !SKIP_TYPES.test(t.slug) && t.slug !== 'attachment',\n )\n const bases = postTypes.length ? postTypes.map((t) => ({ slug: t.slug, base: t.rest_base! })) : [\n { slug: 'post', base: 'posts' },\n { slug: 'page', base: 'pages' },\n ]\n\n interface RestTerm { id: number; slug: string; name: string; taxonomy?: string; parent?: number; description?: string }\n interface RestUser { id: number; slug: string; name: string }\n interface RestMedia {\n id: number\n slug: string\n title?: { rendered?: string }\n source_url?: string\n alt_text?: string\n caption?: { rendered?: string }\n mime_type?: string\n media_details?: { width?: number; height?: number; file?: string }\n post?: number | null\n date_gmt?: string\n }\n interface RestComment {\n id: number\n post: number\n parent?: number\n author?: number\n author_name?: string\n author_url?: string\n date_gmt?: string\n content?: { rendered?: string }\n status?: string\n type?: string\n }\n\n const [categories, tags, users, media, restComments, ...postLists] = await Promise.all([\n getAll<RestTerm>('categories'),\n getAll<RestTerm>('tags'),\n getAll<RestUser>('users'),\n getAll<RestMedia>('media'),\n getAll<RestComment>('comments'),\n ...bases.map((b) => getAll<RestPost>(b.base)),\n ])\n\n const termsById = new Map<number, RawTerm>()\n for (const [tax, list] of [\n ['category', categories],\n ['post_tag', tags],\n ] as const) {\n for (const t of list) {\n termsById.set(t.id, {\n id: t.id,\n taxonomy: tax,\n slug: t.slug,\n name: strip(t.name),\n parent: t.parent ? (list.find((x) => x.id === t.parent)?.slug ?? null) : null,\n parent_resolved: t.parent ? list.some((x) => x.id === t.parent) : null,\n description: t.description ?? '',\n })\n }\n }\n const userById = new Map(users.map((u) => [u.id, u]))\n\n const posts: RawPost[] = []\n // Translation groups, one pair per group. Polylang's `translations` already\n // includes the post itself; WPML lists the OTHER translations, so add self.\n const languagePairs: RawLanguagePair[] = []\n const seenGroups = new Set<string>()\n const recordGroup = (self: RestPost, lang: string | null) => {\n const translations: Record<string, number> = {}\n if (self.translations && typeof self.translations === 'object') {\n for (const [code, id] of Object.entries(self.translations)) if (typeof id === 'number') translations[code] = id\n } else if (Array.isArray(self.wpml_translations)) {\n for (const t of self.wpml_translations) if (t.locale && typeof t.id === 'number') translations[t.locale] = t.id\n if (lang) translations[lang] = self.id\n }\n const ids = Object.values(translations)\n if (ids.length < 2) return\n const key = [...ids].toSorted((a, b) => a - b).join(',')\n if (seenGroups.has(key)) return\n seenGroups.add(key)\n languagePairs.push({ post: self.id, translations })\n }\n for (const [i, base] of bases.entries()) {\n for (const p of postLists[i] ?? []) {\n const termRefs: RawTermRef[] = [...(p.categories ?? []), ...(p.tags ?? [])].map((id) => {\n const t = termsById.get(id)\n return t\n ? { taxonomy: t.taxonomy, slug: t.slug, name: t.name, resolved: true }\n : { taxonomy: 'category', slug: String(id), name: String(id), resolved: false }\n })\n const author = p.author ? (userById.get(p.author)?.slug ?? null) : null\n const meta: Record<string, unknown> = { ...(p.meta && typeof p.meta === 'object' ? p.meta : {}) }\n if (p.featured_media) meta._thumbnail_id = String(p.featured_media)\n const lang = (typeof p.lang === 'string' && p.lang) || (typeof p.wpml_current_locale === 'string' && p.wpml_current_locale) || null\n recordGroup(p, lang)\n posts.push({\n ...(lang ? { lang } : {}),\n id: p.id,\n type: base.slug,\n status: p.status ?? 'publish',\n slug: p.slug,\n title: p.title?.rendered ?? '',\n link: p.link ?? null,\n guid: null,\n author,\n date: iso(p.date_gmt),\n modified: iso(p.modified_gmt),\n content: p.content?.rendered ?? '',\n excerpt: p.excerpt?.rendered ?? '',\n parent: p.parent || null,\n menu_order: p.menu_order ?? 0,\n sticky: p.sticky ?? false,\n password: null,\n comment_status: p.comment_status ?? null,\n ping_status: p.ping_status ?? null,\n terms: termRefs,\n meta,\n })\n }\n }\n\n const attachments: RawAttachment[] = media.map((m) => ({\n id: m.id,\n title: strip(m.title?.rendered) || m.slug,\n slug: m.slug,\n url: m.source_url ?? null,\n alt: m.alt_text ?? '',\n caption: strip(m.caption?.rendered),\n file: m.media_details?.file ?? null,\n image_meta: m.media_details ?? null,\n mime: m.mime_type ?? null,\n parent: m.post ?? null,\n parent_resolved: m.post ? posts.some((p) => p.id === m.post) : null,\n date: iso(m.date_gmt),\n }))\n\n const postIds = new Set(posts.map((p) => p.id))\n const commentIds = new Set(restComments.map((c) => c.id))\n const comments: RawComment[] = restComments.map((c) => ({\n id: c.id,\n post: c.post,\n post_type: posts.find((p) => p.id === c.post)?.type,\n parent: c.parent || null,\n parent_resolved: c.parent ? commentIds.has(c.parent) : null,\n author: strip(c.author_name) || 'anonymous',\n email: null,\n url: c.author_url || null,\n date: iso(c.date_gmt),\n date_gmt: c.date_gmt ?? null,\n content: c.content?.rendered ?? '',\n approved: approvedOf(c.status),\n type: c.type ?? 'comment',\n user_id: c.author || null,\n }))\n for (const c of comments) if (!postIds.has(c.post)) warnings.push(`comment ${c.id}: post ${c.post} not in fetched set`)\n\n const kind: SourceAccessKind = options.auth ? 'rest_auth' : 'rest_public'\n const raw: RawIR = {\n version: MIGRATION_CONTRACT_VERSION,\n provenance: { kind, tool: options.tool ?? '@contentrain/wp-import' },\n site: { url: origin },\n authors: users.map((u) => ({ id: u.id, login: u.slug, display_name: strip(u.name) || u.slug, email: null })),\n terms: [...termsById.values()],\n posts,\n attachments,\n comments,\n ...(languagePairs.length ? { language_pairs: languagePairs } : {}),\n }\n return { raw, warnings }\n}\n","// RawIR → .contentrain content store, as a pure file map.\n//\n// Ported from the measured import chain (the \"deviations from r4i\" it carried\n// are already folded in: PATH_PATTERNS-conformant layout, ContentrainConfig\n// shape, meta files per locale). Two properties are load-bearing:\n//\n// 1. Identity formulas — hexId('posts:' + slug), hexId('category:' + slug) —\n// are shared with the REST path, so the same site imported twice (or via a\n// later delta) lands on the same entry ids.\n// 2. The EntrySourceMap (wp post id → entry address) is produced HERE and only\n// here: this is the one place that knows both sides of the mapping, and the\n// comments intake downstream cannot exist without it.\n\nimport type { EntrySourceMap, FieldDef, ModelDefinition, RawIR, RawPost } from '@contentrain/types'\nimport {\n byValue,\n canon,\n coreFields,\n decodeSlug,\n hexId,\n PLUGIN_META,\n SKIP_TYPES,\n slugify,\n strip,\n taxModelId,\n} from './core.js'\n\nexport interface ImportReport {\n slug_rewritten: number\n slug_fallback: number\n title_fallback: number\n meta_fields: Record<string, string>\n acf_fields: Record<string, string>\n skipped_types: string[]\n dropped_relations: number\n models: Record<string, { kind: string; domain: string; fields: number; entries: number }>\n /** Default first, then every other language seen on a post. One entry on a monolingual site. */\n locales: string[]\n /** Translation groups that were folded into one entry id across locales. */\n translation_groups: number\n}\n\nexport interface ContentrainResult {\n files: Record<string, string>\n entry_source_map: EntrySourceMap\n report: ImportReport\n}\n\ntype Entry = Record<string, unknown>\ntype Meta = { status: string; source: string; updated_by: string; publish_at?: string }\n\nconst pick = (o: Record<string, unknown>, keys: Set<string>): Entry =>\n Object.fromEntries(Object.entries(o).filter(([k, v]) => keys.has(k) && v !== undefined && v !== null && v !== ''))\nconst typeModelId = (t: string): string => (t === 'post' ? 'posts' : t === 'page' ? 'pages' : t.replace(/_/g, '-'))\nconst domainOf = (t: string): string => (t === 'post' ? 'blog' : t === 'page' ? 'site' : 'custom')\n\n/** `en_US` / `tr-TR` / `en` → `en`: the store's locale is the primary subtag. */\nconst normLocale = (code: string | null | undefined): string => (code ?? '').split(/[-_]/)[0]?.toLowerCase() ?? ''\n\n/** Multilingual-plugin taxonomies that carry language bookkeeping, not content. */\nconst LANGUAGE_TAXONOMIES = new Set(['language', 'post_translations', 'term_language', 'term_translations'])\n\nexport function rawToContentrain(raw: RawIR, opts?: { updatedBy?: string }): ContentrainResult {\n const updatedBy = opts?.updatedBy ?? '@contentrain/wp-import'\n const locale = normLocale(raw.site.language) || 'en'\n const models: Record<string, ModelDefinition> = {}\n const contents: Record<string, Record<string, Entry>> = {}\n const metas: Record<string, Record<string, Meta>> = {}\n // Per-locale buckets for i18n models (post types on a multilingual site).\n const localeContents: Record<string, Record<string, Record<string, Entry>>> = {}\n const localeMetas: Record<string, Record<string, Record<string, Meta>>> = {}\n\n // ── languages ──\n // A post's language comes from the plugin (`lang`) or, in WXR, from the\n // `language` taxonomy term Polylang attaches. Posts without one belong to the\n // site's default locale. More than one locale seen → post-type models are\n // i18n and content is written per locale.\n const postLocale = (p: RawPost): string =>\n normLocale(p.lang ?? p.terms.find((t) => t.taxonomy === 'language')?.slug) || locale\n const locales = [locale, ...[...new Set(raw.posts.filter((p) => !SKIP_TYPES.test(p.type)).map(postLocale))].filter((l) => l !== locale).toSorted()]\n const multilingual = locales.length > 1\n // Translation groups → one canonical post per group. Its entry id is shared\n // by every translation, which is how an i18n collection addresses one entry\n // in several locales. The canonical member is the default-locale post when\n // there is one, else the lowest id — deterministic either way.\n const postById = new Map(raw.posts.map((p) => [p.id, p]))\n const canonicalOf = new Map<number, number>()\n let translationGroups = 0\n for (const pair of raw.language_pairs ?? []) {\n const members = [...new Set(Object.values(pair.translations))].filter((id) => postById.has(id) && !SKIP_TYPES.test(postById.get(id)!.type))\n if (members.length < 2) continue\n const memberLocales = members.map((id) => postLocale(postById.get(id)!))\n if (new Set(memberLocales).size !== members.length)\n throw new Error(`Translation group ${pair.post} has multiple posts in the same normalized locale; refusing to overwrite a translation`)\n const canonical = members.find((id) => postLocale(postById.get(id)!) === locale) ?? Math.min(...members)\n for (const id of members) canonicalOf.set(id, canonical)\n translationGroups++\n }\n let siteEntry: Entry = {}\n let siteMeta: Meta | null = null\n const report: ImportReport = {\n slug_rewritten: 0,\n slug_fallback: 0,\n title_fallback: 0,\n meta_fields: {},\n acf_fields: {},\n skipped_types: [],\n dropped_relations: 0,\n models: {},\n locales,\n translation_groups: translationGroups,\n }\n const importMeta = (status: string, extra: Partial<Meta> = {}): Meta => ({\n status,\n source: 'import',\n updated_by: updatedBy,\n ...extra,\n })\n const mapStatus = (p: RawPost): Meta => {\n if (p.status === 'future' && (!p.date || !Number.isFinite(Date.parse(p.date))))\n throw new Error(`Scheduled WordPress post ${p.id} has no valid publication date`)\n return p.status === 'publish' || p.status === 'inherit'\n ? importMeta('published')\n : p.status === 'future'\n ? importMeta('published', { publish_at: p.date! })\n : p.status === 'pending'\n ? importMeta('in_review')\n : p.status === 'trash'\n ? importMeta('archived')\n : importMeta('draft')\n }\n const addModel = (m: ModelDefinition): void => {\n models[m.id] = m\n contents[m.id] ??= {}\n metas[m.id] ??= {}\n }\n\n // ── identities (shared formulas — REST and WXR must agree) ──\n const authorId = (login: string): string => hexId(`authors:${login}`)\n const termId = (tax: string, slug: string): string => hexId(`${tax}:${slug}`)\n const usedSlugs = new Map<string, number>()\n const postSlug = (p: RawPost): string => {\n let s = slugify(decodeSlug(p.slug))\n if (!s) {\n s = `${p.type}-${p.id}`\n report.slug_fallback++\n } else if (s !== p.slug) report.slug_rewritten++\n // Slugs are unique per type — and per locale on a multilingual site, where\n // `/about/` and `/tr/about/` are different entries with the same slug.\n const key = multilingual ? `${p.type}:${postLocale(p)}:${s}` : `${p.type}:${s}`\n const n = usedSlugs.get(key) ?? 0\n usedSlugs.set(key, n + 1)\n return n ? `${s}-${p.id}` : s\n }\n const slugOf = new Map<number, string>()\n const postEntry = new Map<number, { model: string; ref: string }>()\n for (const p of raw.posts.toSorted((a, b) => a.id - b.id)) {\n // Only exported entries may be relation targets or appear in the source map.\n if (SKIP_TYPES.test(p.type)) continue\n slugOf.set(p.id, postSlug(p))\n }\n // Keep established slug identities unless distinct translation groups collide.\n // Prefer the default-locale group, then the lowest WP id, independently of\n // REST pagination/WXR order. A colliding group gets a WP-id namespace.\n const identityOwner = new Map<string, number>()\n const exported = raw.posts.filter((p) => !SKIP_TYPES.test(p.type))\n const canonicalPosts = exported.filter((p) => (canonicalOf.get(p.id) ?? p.id) === p.id)\n .toSorted((a, b) => Number(postLocale(b) === locale) - Number(postLocale(a) === locale) || a.id - b.id)\n for (const p of canonicalPosts) {\n const key = `${typeModelId(p.type)}:${slugOf.get(p.id)!}`\n if (!identityOwner.has(key)) identityOwner.set(key, p.id)\n }\n for (const p of exported) {\n const canonical = canonicalOf.get(p.id) ?? p.id\n const mid = typeModelId(p.type)\n const key = `${mid}:${slugOf.get(canonical) ?? slugOf.get(p.id)!}`\n postEntry.set(p.id, { model: mid, ref: hexId(identityOwner.get(key) === canonical ? key : `${mid}:wp:${canonical}`) })\n }\n const mediaRef = (id: number): string | null => (raw.attachments.some((a) => a.id === id) ? hexId(`media:${id}`) : null)\n const titleOf = (p: RawPost): string => {\n const t = strip(p.title)\n if (t) return t\n report.title_fallback++\n return slugOf.get(p.id) ?? `#${p.id}`\n }\n\n // ── authors ──\n addModel({\n id: 'authors', name: 'Authors', kind: 'collection', domain: 'blog', i18n: false, title_field: 'name',\n fields: {\n name: { type: 'string', required: true, label: 'Name', order: 10 },\n slug: { type: 'slug', required: true, unique: true, label: 'Slug', order: 20 },\n email: { type: 'email', label: 'Email', order: 30 },\n wp_id: { type: 'integer', label: 'WP user ID', order: 40 },\n },\n })\n for (const a of raw.authors) {\n const id = authorId(slugify(a.login))\n contents.authors![id] = pick(\n { name: strip(a.display_name), slug: slugify(a.login), email: a.email, wp_id: a.id },\n new Set(['name', 'slug', 'email', 'wp_id']),\n )\n metas.authors![id] = importMeta('published')\n }\n for (const p of raw.posts) {\n if (p.author && !contents.authors![authorId(slugify(p.author))]) {\n contents.authors![authorId(slugify(p.author))] = { name: p.author, slug: slugify(p.author) }\n metas.authors![authorId(slugify(p.author))] = importMeta('published')\n }\n }\n\n // ── taxonomies (nav_menu and post_format excluded; post_format → select field) ──\n const taxonomies = [...new Set(raw.terms.map((t) => t.taxonomy))].filter(\n (t) => t !== 'nav_menu' && t !== 'post_format' && !LANGUAGE_TAXONOMIES.has(t),\n )\n for (const tax of taxonomies) {\n const mid = taxModelId(tax)\n addModel({\n id: mid, name: mid, kind: 'collection', domain: 'blog', i18n: false, title_field: 'name',\n fields: {\n name: { type: 'string', required: true, label: 'Name', order: 10 },\n slug: { type: 'slug', required: true, unique: true, label: 'Slug', order: 20 },\n description: { type: 'text', label: 'Description', order: 30 },\n parent: { type: 'relation', model: mid, label: 'Parent', order: 40 },\n wp_id: { type: 'integer', label: 'WP term ID', order: 50 },\n },\n })\n for (const t of raw.terms.filter((x) => x.taxonomy === tax)) {\n const id = termId(tax, t.slug)\n contents[mid]![id] = pick(\n {\n name: strip(t.name),\n slug: slugify(decodeSlug(t.slug)) || `term-${t.id}`,\n description: strip(t.description),\n parent: t.parent && t.parent_resolved ? termId(tax, t.parent) : null,\n wp_id: t.id,\n },\n new Set(['name', 'slug', 'description', 'parent', 'wp_id']),\n )\n metas[mid]![id] = importMeta('published')\n }\n }\n // referenced-but-unlisted terms join the pool (marked resolved:false upstream, never dropped)\n for (const p of raw.posts) {\n for (const t of p.terms) {\n if (t.taxonomy === 'nav_menu' || t.taxonomy === 'post_format' || t.resolved) continue\n const mid = taxModelId(t.taxonomy)\n if (!models[mid]) continue\n const id = termId(t.taxonomy, t.slug)\n contents[mid]![id] ??= { name: strip(t.name), slug: slugify(t.slug) }\n metas[mid]![id] ??= importMeta('published')\n }\n }\n\n // ── media ──\n addModel({\n id: 'media', name: 'Media', kind: 'collection', domain: 'assets', i18n: false, title_field: 'title',\n fields: {\n title: { type: 'string', required: true, label: 'Title', order: 10 },\n slug: { type: 'slug', required: true, unique: true, label: 'Slug', order: 20 },\n url: { type: 'url', required: true, label: 'Source URL', order: 30 },\n alt: { type: 'string', label: 'Alt text', order: 40 },\n caption: { type: 'text', label: 'Caption', order: 50 },\n description: { type: 'text', label: 'Description', order: 60 },\n mime: { type: 'string', label: 'MIME type', order: 70 },\n width: { type: 'integer', label: 'Width', order: 80 },\n height: { type: 'integer', label: 'Height', order: 90 },\n file: { type: 'string', label: 'Upload path', order: 100 },\n parent: { type: 'relation', model: [], label: 'Attached to', order: 110 },\n wp_id: { type: 'integer', label: 'WP attachment ID', order: 120 },\n uploaded_at: { type: 'datetime', label: 'Uploaded at', order: 130 },\n },\n })\n const mediaSlugs = new Set<string>()\n for (const a of raw.attachments) {\n const id = hexId(`media:${a.id}`)\n let slug = slugify(decodeSlug(a.slug)) || `media-${a.id}`\n if (mediaSlugs.has(slug)) slug = `${slug}-${a.id}`\n mediaSlugs.add(slug)\n const parent = a.parent ? (postEntry.get(a.parent) ?? null) : null\n if (a.parent && !parent) report.dropped_relations++\n const im = (a.image_meta ?? null) as { width?: number; height?: number } | null\n contents.media![id] = pick(\n {\n title: strip(a.title) || slug,\n slug,\n url: a.url,\n alt: strip(a.alt),\n caption: strip(a.caption),\n description: strip(a.description),\n mime: a.mime,\n width: im?.width,\n height: im?.height,\n file: a.file,\n parent,\n wp_id: a.id,\n uploaded_at: a.date,\n },\n new Set(['title', 'slug', 'url', 'alt', 'caption', 'description', 'mime', 'width', 'height', 'file', 'parent', 'wp_id', 'uploaded_at']),\n )\n metas.media![id] = importMeta('published')\n }\n\n // ── post types (post, page, CPTs) ──\n const postTypes = [...new Set(raw.posts.map((p) => p.type))].filter((t) => {\n if (SKIP_TYPES.test(t)) {\n report.skipped_types.push(t)\n return false\n }\n return true\n })\n const contentModelIds = postTypes.map(typeModelId)\n ;(models.media!.fields!.parent as FieldDef).model = contentModelIds\n for (const type of postTypes) {\n const typeItems = raw.posts.filter((p) => p.type === type)\n const mid = typeModelId(type)\n const taxes = [...new Set(typeItems.flatMap((p) => p.terms.map((t) => t.taxonomy)))].filter((t) => models[taxModelId(t)])\n const { fields, order } = coreFields({\n excerpt: typeItems.some((p) => p.excerpt),\n body: typeItems.some((p) => p.content),\n date: typeItems.some((p) => p.date),\n author: typeItems.some((p) => p.author),\n taxes,\n cover: typeItems.some((p) => p.meta._thumbnail_id),\n })\n let o = order\n fields.wp_id = { type: 'integer', required: true, unique: true, label: 'WP post ID', order: (o += 10) }\n fields.modified_at = { type: 'datetime', label: 'Modified at', order: (o += 10) }\n fields.link = { type: 'url', label: 'Original permalink', order: (o += 10) }\n const parentModels = [...new Set(typeItems.flatMap((p) => {\n const parent = p.parent ? postEntry.get(p.parent) : undefined\n return parent ? [parent.model] : []\n }))].toSorted()\n if (typeItems.some((p) => p.parent)) fields.parent = {\n type: 'relation',\n model: parentModels.length > 1 ? parentModels : (parentModels[0] ?? mid),\n label: 'Parent', order: (o += 10),\n }\n if (typeItems.some((p) => p.menu_order)) fields.menu_order = { type: 'integer', label: 'Menu order', order: (o += 10) }\n if (typeItems.some((p) => p.sticky)) fields.sticky = { type: 'boolean', label: 'Sticky', order: (o += 10) }\n if (typeItems.some((p) => typeof p.meta._wp_page_template === 'string' && p.meta._wp_page_template !== 'default'))\n fields.template = { type: 'string', label: 'Page template', order: (o += 10) }\n const formats = [\n ...new Set(\n typeItems.flatMap((p) =>\n p.terms.filter((t) => t.taxonomy === 'post_format').map((t) => t.slug.replace(/^post-format-/, '')),\n ),\n ),\n ].toSorted()\n if (formats.length) fields.format = { type: 'select', options: formats, label: 'Post format', order: (o += 10) }\n if (typeItems.some((p) => p.status === 'private' || p.password))\n fields.visibility = { type: 'select', options: ['public', 'private', 'password'], label: 'Visibility', order: (o += 10) }\n if (typeItems.some((p) => p.comment_status))\n fields.comments_open = { type: 'boolean', label: 'Comments open', order: (o += 10) }\n for (const p of typeItems) {\n for (const [k, v] of Object.entries(p.meta)) {\n if (k.startsWith('_') || fields[k] || PLUGIN_META.test(k) || k === 'enclosure') continue\n const t = byValue(v)\n if (t && t !== 'object') {\n fields[k] = { type: t as FieldDef['type'], label: k, order: (o += 10) }\n report.meta_fields[k] = t\n }\n }\n for (const [k, acf] of Object.entries(p.acf ?? {})) {\n if (fields[k]) continue\n const t = byValue(acf.value) ?? 'string'\n fields[k] = { type: (t === 'object' ? 'object' : t) as FieldDef['type'], label: k, description: 'ACF', order: (o += 10) }\n report.acf_fields[k] = t\n }\n }\n addModel({\n id: mid,\n name: type === 'post' ? 'Posts' : type === 'page' ? 'Pages' : mid,\n kind: 'collection',\n domain: domainOf(type),\n i18n: multilingual,\n title_field: 'title',\n fields,\n })\n for (const p of typeItems) {\n const id = postEntry.get(p.id)!.ref\n const loc = postLocale(p)\n const contentBucket = multilingual ? ((localeContents[mid] ??= {})[loc] ??= {}) : contents[mid]!\n const metaBucket = multilingual ? ((localeMetas[mid] ??= {})[loc] ??= {}) : metas[mid]!\n const e: Entry = { title: titleOf(p), slug: slugOf.get(p.id), wp_id: p.id }\n if (fields.excerpt) e.excerpt = strip(p.excerpt)\n if (fields.body) e.body = p.content // Gutenberg comments and shortcodes verbatim (known gap)\n if (fields.published_at && p.date) e.published_at = p.date\n if (p.modified) e.modified_at = p.modified\n if (p.link) e.link = p.link\n if (fields.author && p.author) e.author = authorId(slugify(p.author))\n for (const t of taxes) {\n e[taxModelId(t)] = p.terms\n .filter((x) => x.taxonomy === t && contents[taxModelId(t)]![termId(t, x.slug)])\n .map((x) => termId(t, x.slug))\n }\n if (fields.cover && p.meta._thumbnail_id) {\n const m = mediaRef(Number(p.meta._thumbnail_id))\n if (m) e.cover = m\n else report.dropped_relations++\n }\n if (fields.parent && p.parent) {\n const pe = postEntry.get(p.parent)\n if (pe) e.parent = parentModels.length > 1 ? pe : pe.ref\n else report.dropped_relations++\n }\n if (fields.menu_order && p.menu_order) e.menu_order = p.menu_order\n if (fields.sticky) e.sticky = p.sticky ?? false\n if (fields.template && typeof p.meta._wp_page_template === 'string' && p.meta._wp_page_template !== 'default')\n e.template = p.meta._wp_page_template\n if (fields.format) {\n const f = p.terms.find((t) => t.taxonomy === 'post_format')\n if (f) e.format = f.slug.replace(/^post-format-/, '')\n }\n if (fields.visibility) e.visibility = p.password ? 'password' : p.status === 'private' ? 'private' : 'public'\n if (fields.comments_open) e.comments_open = p.comment_status === 'open'\n for (const k of Object.keys(fields)) if (k in p.meta && !(k in e) && !k.startsWith('_')) e[k] = p.meta[k]\n for (const [k, acf] of Object.entries(p.acf ?? {})) if (fields[k] && !(k in e)) e[k] = acf.value\n contentBucket[id] = e\n metaBucket[id] = mapStatus(p)\n }\n }\n\n // ── menus ──\n const menus = raw.menus ?? []\n if (menus.length) {\n addModel({\n id: 'menus', name: 'Menus', kind: 'collection', domain: 'site', i18n: false, title_field: 'name',\n fields: {\n name: { type: 'string', required: true, label: 'Name', order: 10 },\n slug: { type: 'slug', required: true, unique: true, label: 'Slug', order: 20 },\n items: { type: 'relations', model: 'menu-items', label: 'Items', order: 30 },\n wp_id: { type: 'integer', label: 'WP term ID', order: 40 },\n },\n })\n addModel({\n id: 'menu-items', name: 'Menu items', kind: 'collection', domain: 'site', i18n: false, title_field: 'title',\n fields: {\n title: { type: 'string', required: true, label: 'Label', order: 10 },\n menu: { type: 'relation', model: 'menus', required: true, label: 'Menu', order: 20 },\n order: { type: 'integer', label: 'Order', order: 30 },\n parent: { type: 'relation', model: 'menu-items', label: 'Parent item', order: 40 },\n type: { type: 'select', options: ['custom', 'post_type', 'taxonomy', 'post_type_archive'], label: 'Type', order: 50 },\n url: { type: 'url', label: 'URL', order: 60 },\n target: { type: 'relation', model: [...contentModelIds, ...taxonomies.map(taxModelId)], label: 'Target', order: 70 },\n open_in_new_tab: { type: 'boolean', label: 'Open in new tab', order: 80 },\n classes: { type: 'array', items: 'string', label: 'CSS classes', order: 90 },\n description: { type: 'text', label: 'Description', order: 100 },\n wp_id: { type: 'integer', label: 'WP post ID', order: 110 },\n },\n })\n const itemRef = (id: number): string => hexId(`menu-items:${id}`)\n for (const m of menus) {\n const mid = hexId(`menus:${m.slug}`)\n contents.menus![mid] = {\n name: strip(m.name),\n slug: slugify(m.slug) || `menu-${m.id}`,\n items: m.items.map((i) => itemRef(i.id)),\n wp_id: m.id,\n }\n metas.menus![mid] = importMeta('published')\n for (const i of m.items) {\n const e: Entry = {\n title: strip(i.title) || i.url || `item-${i.id}`,\n menu: mid,\n order: i.order,\n type: i.target.kind === 'url' ? 'custom' : i.target.kind === 'post' ? 'post_type' : i.target.kind === 'term' ? 'taxonomy' : i.target.kind === 'archive' ? 'post_type_archive' : 'custom',\n wp_id: i.id,\n open_in_new_tab: i.target_attr === '_blank',\n }\n if (i.parent && !i.parent_unresolved) e.parent = itemRef(i.parent)\n if (i.target.kind === 'url' && i.url) e.url = i.url\n if (i.target.kind === 'post' && i.target.resolved && i.target.id != null) {\n const pe = postEntry.get(i.target.id)\n if (pe) e.target = pe\n else report.dropped_relations++\n }\n if (i.target.kind === 'term' && i.target.resolved && i.target.slug && models[taxModelId(i.target.taxonomy)])\n e.target = { model: taxModelId(i.target.taxonomy), ref: termId(i.target.taxonomy, i.target.slug) }\n if (i.classes?.length) e.classes = i.classes\n if (i.description) e.description = strip(i.description)\n contents['menu-items']![itemRef(i.id)] = e\n metas['menu-items']![itemRef(i.id)] = importMeta(i.status === 'publish' ? 'published' : 'draft')\n }\n }\n }\n\n // ── comments (frozen archive collection; the live-service path is CommentsExport) ──\n const comments = raw.comments ?? []\n if (comments.length) {\n addModel({\n id: 'comments', name: 'Comments', kind: 'collection', domain: 'blog', i18n: false, title_field: 'author',\n fields: {\n author: { type: 'string', required: true, label: 'Author', order: 10 },\n email: { type: 'email', label: 'Email', order: 20 },\n url: { type: 'url', label: 'Website', order: 30 },\n body: { type: 'richtext', label: 'Body', order: 40 },\n published_at: { type: 'datetime', label: 'Date', order: 50 },\n post: { type: 'relation', model: contentModelIds, required: true, label: 'Post', order: 60 },\n parent: { type: 'relation', model: 'comments', label: 'In reply to', order: 70 },\n type: { type: 'string', label: 'Type', order: 80 },\n user: { type: 'relation', model: 'authors', label: 'User', order: 90 },\n wp_id: { type: 'integer', label: 'WP comment ID', order: 100 },\n },\n })\n const cRef = (id: number): string => hexId(`comments:${id}`)\n const userById = new Map(raw.authors.filter((a) => a.id != null).map((a) => [a.id, authorId(slugify(a.login))]))\n for (const c of comments) {\n const post = postEntry.get(c.post)\n if (!post) {\n report.dropped_relations++\n continue\n }\n contents.comments![cRef(c.id)] = pick(\n {\n author: strip(c.author) || 'anonymous',\n email: c.email,\n url: c.url,\n body: c.content,\n published_at: c.date,\n post,\n parent: c.parent && c.parent_resolved ? cRef(c.parent) : null,\n type: c.type,\n user: c.user_id ? userById.get(c.user_id) : null,\n wp_id: c.id,\n },\n new Set(['author', 'email', 'url', 'body', 'published_at', 'post', 'parent', 'type', 'user', 'wp_id']),\n )\n metas.comments![cRef(c.id)] = importMeta(\n c.approved === '1' ? 'published' : c.approved === 'spam' || c.approved === 'trash' ? 'archived' : 'in_review',\n )\n }\n }\n\n // ── site singleton + vocabulary ──\n addModel({\n id: 'site', name: 'Site', kind: 'singleton', domain: 'site', i18n: false, title_field: 'title',\n fields: {\n title: { type: 'string', required: true, label: 'Site title', order: 10 },\n tagline: { type: 'string', label: 'Tagline', order: 20 },\n url: { type: 'url', label: 'URL', order: 30 },\n language: { type: 'string', label: 'Language', order: 40 },\n },\n })\n siteEntry = pick(\n { title: strip(raw.site.title) || 'Site', tagline: raw.site.description, url: raw.site.url, language: raw.site.language },\n new Set(['title', 'tagline', 'url', 'language']),\n )\n siteMeta = importMeta('published')\n const vocabTerms: Record<string, Record<string, string>> = {}\n for (const m of menus) {\n for (const i of m.items) {\n const k = slugify(strip(i.title))\n if (k && !vocabTerms[k]) vocabTerms[k] = { [locale]: strip(i.title) }\n }\n }\n\n // ── file map (canonical serialization; PATH_PATTERNS layout) ──\n const files: Record<string, string> = {}\n const domains = [...new Set(Object.values(models).map((m) => m.domain))].toSorted()\n for (const m of Object.values(models)) {\n files[`.contentrain/models/${m.id}.json`] = canon(m)\n if (m.i18n) {\n // One content + meta file per locale; an entry id recurs across the\n // locales it has a translation in.\n const perLocale = localeContents[m.id] ?? {}\n const ids = new Set<string>()\n for (const loc of Object.keys(perLocale).toSorted()) {\n files[`.contentrain/content/${m.domain}/${m.id}/${loc}.json`] = canon(perLocale[loc])\n files[`.contentrain/meta/${m.id}/${loc}.json`] = canon(localeMetas[m.id]?.[loc] ?? {})\n for (const id of Object.keys(perLocale[loc]!)) ids.add(id)\n }\n report.models[m.id] = { kind: m.kind, domain: m.domain, fields: Object.keys(m.fields ?? {}).length, entries: ids.size }\n continue\n }\n const content = m.kind === 'singleton' ? siteEntry : contents[m.id]!\n const meta = m.kind === 'singleton' ? siteMeta : metas[m.id]!\n files[`.contentrain/content/${m.domain}/${m.id}/data.json`] = canon(content)\n files[`.contentrain/meta/${m.id}/${locale}.json`] = canon(meta)\n report.models[m.id] = {\n kind: m.kind,\n domain: m.domain,\n fields: Object.keys(m.fields ?? {}).length,\n entries: m.kind === 'singleton' ? 1 : Object.keys(contents[m.id]!).length,\n }\n }\n files['.contentrain/config.json'] = canon({\n version: 1,\n stack: 'astro',\n platform: 'web',\n workflow: 'review',\n locales: { default: locale, supported: locales },\n domains,\n })\n files['.contentrain/vocabulary.json'] = canon({ version: 1, terms: vocabTerms })\n\n const entrySourceMap: EntrySourceMap = {}\n for (const [wpId, ref] of postEntry) {\n entrySourceMap[String(wpId)] = { model_id: ref.model, entry_id: ref.ref, locale: postLocale(postById.get(wpId)!) }\n }\n\n files['import-report.json'] = canon(report)\n return { files, entry_source_map: entrySourceMap, report }\n}\n","// CommentsExport builder — the bridge from a migrated static site to a live\n// comments service. Everything the receiving side needs travels here: the\n// entry addresses (WordPress ids mean nothing after migration), the verbatim\n// comments, and which threads were closed at the source.\n\nimport type { CommentsExport, EntrySourceMap, HandoffComments, RawIR } from '@contentrain/types'\nimport { COMMENTS_EXPORT_FORMAT, MIGRATION_CONTRACT_VERSION } from '@contentrain/types'\n\nexport function buildCommentsExport(raw: RawIR, entries: EntrySourceMap, opts?: { generated_at?: string }): CommentsExport {\n const threadsClosed = raw.posts.filter((p) => p.comment_status && p.comment_status !== 'open').map((p) => p.id)\n return {\n version: MIGRATION_CONTRACT_VERSION,\n format: COMMENTS_EXPORT_FORMAT,\n source: raw.provenance,\n site_url: raw.site.url || undefined,\n generated_at: opts?.generated_at ?? new Date().toISOString(),\n entries,\n threads_closed: threadsClosed.length ? threadsClosed : undefined,\n comments: raw.comments ?? [],\n }\n}\n\n/** Handoff summary for the export: counts + closed threads + what could not be addressed. */\nexport function summarizeComments(exp: CommentsExport): HandoffComments {\n const byStatus: Record<string, number> = {}\n const byType: Record<string, number> = {}\n const unresolved: Array<{ comment_id: number; post: number; reason: string }> = []\n for (const c of exp.comments) {\n byStatus[c.approved ?? '1'] = (byStatus[c.approved ?? '1'] ?? 0) + 1\n byType[c.type ?? 'comment'] = (byType[c.type ?? 'comment'] ?? 0) + 1\n if (!exp.entries[String(c.post)]) unresolved.push({ comment_id: c.id, post: c.post, reason: 'post has no entry mapping' })\n }\n return {\n total: exp.comments.length,\n by_status: byStatus,\n types: byType,\n export: { format: exp.format },\n threads_closed: exp.threads_closed,\n unresolved: unresolved.length ? unresolved : undefined,\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["iso"],"sources":["../src/php-unserialize.ts","../src/core.ts","../src/wxr.ts","../src/rest.ts","../src/contentrain.ts","../src/comments.ts","../src/delta.ts"],"sourcesContent":["// Small PHP serialize() decoder, ported from the measured import chain.\n// Supported: s (byte-length strings), i, d, b, N, a (array → list or object), O (object → { __class, … }).\n// PHP string lengths are BYTES, so the walk happens over a Buffer, not a JS string.\n\nexport interface UnserializeResult {\n ok: boolean\n serialized: boolean\n value: unknown\n error?: string\n}\n\nexport function looksSerialized(s: unknown): s is string {\n return typeof s === 'string' && /^(a|O):\\d+:[{\"]|^(s|i|d|b):[^;]+;?$|^N;$/.test(s)\n}\n\nexport function phpUnserialize(str: string): unknown {\n const buf = Buffer.from(str, 'utf8')\n let p = 0\n const fail = (m: string): never => {\n throw new Error(`unserialize: ${m} at byte ${p}`)\n }\n const readUntil = (ch: string): string => {\n const i = buf.indexOf(ch, p)\n if (i < 0) fail(`expected ${ch}`)\n const s = buf.toString('utf8', p, i)\n p = i + 1\n return s\n }\n const expect = (ch: string): void => {\n if (buf[p] !== ch.charCodeAt(0)) fail(`expected '${ch}' got '${String.fromCharCode(buf[p] ?? 0)}'`)\n p++\n }\n const value = (): unknown => {\n const t = String.fromCharCode(buf[p] ?? 0)\n p++\n switch (t) {\n case 'N':\n expect(';')\n return null\n case 'b': {\n expect(':')\n return readUntil(';') === '1'\n }\n case 'i': {\n expect(':')\n return Number(readUntil(';'))\n }\n case 'd': {\n expect(':')\n const v = readUntil(';')\n return v === 'INF' ? Infinity : v === '-INF' ? -Infinity : v === 'NAN' ? Number.NaN : Number(v)\n }\n case 's': {\n expect(':')\n const n = Number(readUntil(':'))\n expect('\"')\n const s = buf.toString('utf8', p, p + n)\n p += n\n expect('\"')\n expect(';')\n return s\n }\n case 'a': {\n expect(':')\n const n = Number(readUntil(':'))\n expect('{')\n const entries: Array<[unknown, unknown]> = []\n for (let i = 0; i < n; i++) {\n const k = value()\n const v = value()\n entries.push([k, v])\n }\n expect('}')\n // consecutive 0..n-1 integer keys → JSON list; anything else → object\n const isList = entries.every(([k], i) => k === i)\n return isList ? entries.map(([, v]) => v) : Object.fromEntries(entries.map(([k, v]) => [String(k), v]))\n }\n case 'O': {\n expect(':')\n readUntil(':')\n expect('\"')\n const cls = readUntil('\"')\n expect(':')\n const n = Number(readUntil(':'))\n expect('{')\n const o: Record<string, unknown> = { __class: cls }\n for (let i = 0; i < n; i++) {\n const k = value()\n const v = value()\n o[String(k).split('\\u0000').at(-1) ?? String(k)] = v\n }\n expect('}')\n return o\n }\n default:\n return fail(`unknown type '${t}'`)\n }\n }\n const v = value()\n if (p !== buf.length) fail(`trailing ${buf.length - p} bytes`)\n return v\n}\n\n/** Attempt a decode; on failure the value stays exactly as it was. */\nexport function tryUnserialize(s: string): UnserializeResult {\n if (!looksSerialized(s)) return { ok: false, serialized: false, value: s }\n try {\n return { ok: true, serialized: true, value: phpUnserialize(s) }\n } catch (e) {\n return { ok: false, serialized: true, value: s, error: e instanceof Error ? e.message : String(e) }\n }\n}\n","// Shared conversion helpers, ported from the measured induction core.\n// The identity formulas here are load-bearing: hexId('posts:' + slug) must\n// produce the same entry id whether the site arrived via WXR or REST, or a\n// later delta import would duplicate every entry.\n\nimport { createHash } from 'node:crypto'\nimport type { FieldDef } from '@contentrain/types'\n\nexport const norm = (s: unknown): string => String(s ?? '').replace(/\\s+/g, ' ').trim()\n\nexport const strip = (s: unknown): string =>\n norm(\n String(s ?? '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/&#(\\d+);/g, (_m, d: string) => String.fromCodePoint(Number(d)))\n .replace(/&/g, '&'),\n )\n\n/** Entry id: deterministic hex from a stable key (model:slug style). */\nexport const hexId = (s: string): string => createHash('sha1').update(s).digest('hex').slice(0, 24)\n\nconst sortDeep = (v: unknown): unknown =>\n Array.isArray(v)\n ? v.map(sortDeep)\n : v && typeof v === 'object'\n ? Object.fromEntries(\n Object.keys(v as Record<string, unknown>)\n .toSorted()\n .map((k) => [k, sortDeep((v as Record<string, unknown>)[k])]),\n )\n : v\n\n/** Canonical serialization: sorted keys, 2-space indent, trailing newline. */\nexport const canon = (o: unknown): string => `${JSON.stringify(sortDeep(o), null, 2)}\\n`\n\n/** Field-type inference from a value (open meta / ACF fields). */\nexport const byValue = (v: unknown): string | null => {\n if (typeof v === 'boolean') return 'boolean'\n if (typeof v === 'number') return Number.isInteger(v) ? 'integer' : 'decimal'\n if (Array.isArray(v)) return 'array'\n if (v && typeof v === 'object') return 'object'\n const s = String(v ?? '').trim()\n if (!s) return null\n if (/^https?:\\/\\/\\S+\\.(mp3|m4a|wav|pdf|zip|docx?)(\\?|$)/i.test(s)) return 'file'\n if (/^https?:\\/\\/\\S+\\.(jpe?g|png|gif|webp|avif|svg)(\\?|$)/i.test(s)) return 'image'\n if (/^https?:\\/\\//.test(s)) return 'url'\n if (/^[^@\\s]+@[^@\\s]+\\.[a-z]{2,}$/i.test(s)) return 'email'\n if (/^\\d{4}-\\d{2}-\\d{2}([T ]\\d{2}:\\d{2})/.test(s)) return 'datetime'\n if (/<[a-z][\\s\\S]*>/i.test(s)) return 'richtext'\n return s.length > 200 ? 'text' : 'string'\n}\n\n/**\n * WP post types that never become content models: core plumbing (`wp_*`\n * covers `wp_global_styles`, `wp_navigation`, `wp_block`, `wp_template*`),\n * Jetpack, and theme/page-builder internals that store design, not content —\n * GeneratePress elements, GenerateBlocks styles, Elementor templates and\n * Contact Form 7 / WPForms definitions. ACF field/group records stay in:\n * their cross-model parent links are part of the lossless import contract.\n * Whether a stored record gets a public route is the route producer's decision.\n */\nexport const SKIP_TYPES =\n /^(attachment|nav_menu_item|wp_|jp_|pattern|revision|oembed_cache|customize_changeset|user_request|custom_css|gblocks_|gblocks-|gp_elements|gp-elements|elementor_library|e-landing-page|wpcf7_contact_form|wpforms)/\n\n/** Open meta keys that are plugin plumbing, not editorial fields. */\nexport const PLUGIN_META = /^(jetpack|wpdc|discourse|footnotes|inline_featured|spay_|advanced_seo|rank_math|yoast)/i\n\n/** WP core/plumbing meta keys (never editorial). */\nexport const CORE_META =\n /^(_edit_last|_edit_lock|_thumbnail_id|_wp_page_template|_wp_old_slug|_wp_attached_file|_wp_attachment_metadata|_wp_attachment_image_alt|_wp_attachment_backup_sizes|_wp_desired_post_slug|_wp_trash_meta_.*|_menu_item_.*|_oembed_.*|_publicize_.*|_wpas_.*|_pingme|_encloseme|enclosure|_last_editor_used_jetpack|_rest_api_.*|_wp_attachment_context|_wp_attachment_is_custom_header|_wp_attachment_is_custom_background|_customize_.*|_elementor_.*|_wpcom_.*|_wp_suggested_privacy_policy_content|geo_.*|_jetpack_.*|_links_to.*|_format_.*)$/\n\n/** Taxonomy → model id. */\nexport const taxModelId = (t: string): string =>\n t === 'post_tag' ? 'tags' : t === 'category' ? 'categories' : t.replace(/_/g, '-')\n\nexport const slugify = (s: unknown): string =>\n String(s ?? '')\n .normalize('NFKD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n\nexport const decodeSlug = (s: string): string => {\n try {\n return decodeURIComponent(s)\n } catch {\n return s\n }\n}\n\n/** The measured core field skeleton for content models (same order/labels). */\nexport function coreFields(opts: {\n excerpt: boolean\n body: boolean\n date: boolean\n author: boolean\n taxes: string[]\n cover: boolean\n}): { fields: Record<string, FieldDef>; order: number } {\n const fields: Record<string, FieldDef> = {\n title: { type: 'string', required: true, label: 'Title', order: 10 },\n slug: { type: 'slug', required: true, unique: true, label: 'Slug', order: 20 },\n }\n let o = 30\n if (opts.excerpt) fields.excerpt = { type: 'text', label: 'Excerpt', order: (o += 10) }\n if (opts.body) fields.body = { type: 'richtext', label: 'Body', order: (o += 10) }\n if (opts.date) fields.published_at = { type: 'datetime', label: 'Published at', order: (o += 10) }\n if (opts.author) fields.author = { type: 'relation', model: 'authors', label: 'Author', order: (o += 10) }\n for (const t of opts.taxes) fields[taxModelId(t)] = { type: 'relations', model: taxModelId(t), label: t, order: (o += 10) }\n if (opts.cover) fields.cover = { type: 'relation', model: 'media', label: 'Cover', order: (o += 10) }\n return { fields, order: o }\n}\n","// WXR (WordPress eXtended RSS) → RawIR.\n//\n// Streaming (sax) parse: a 100 MB export holds only its records in memory,\n// never the XML. Ported from the measured import chain — the state machine,\n// the meta decoding, and the resolution passes are the parts three sessions\n// of corpus work already debugged; they are transliterated, not redesigned.\n\nimport sax from 'sax'\nimport type { Readable } from 'node:stream'\nimport type {\n RawIR,\n RawPost,\n RawLanguagePair,\n RawAttachment,\n RawAuthor,\n RawComment,\n RawMenu,\n RawMenuItem,\n RawMenuTarget,\n RawTerm,\n RawTermRef,\n RawAcfValue,\n} from '@contentrain/types'\nimport { MIGRATION_CONTRACT_VERSION } from '@contentrain/types'\nimport { tryUnserialize } from './php-unserialize.js'\nimport { strip } from './core.js'\n\nexport interface WxrStats {\n serialized: { detected: number; decoded: number; failed: number; keys: string[] }\n acf_fields: number\n unresolved_terms: number\n counts: Record<string, number>\n}\n\nexport interface WxrParseResult {\n raw: RawIR\n stats: WxrStats\n}\n\ninterface XItem {\n [k: string]: unknown\n terms: Array<{ taxonomy: string; slug: string; name?: string }>\n postmeta: Array<{ key?: string; value?: string }>\n comments: Array<Record<string, string | Array<{ key?: string; value?: string }>>>\n}\n\nconst ts = (s: string | undefined): string | null =>\n !s || s.startsWith('0000-00-00') ? null : s.replace(' ', 'T')\nconst iso = (local: string | undefined, gmt: string | undefined): string | null =>\n gmt && !gmt.startsWith('0000') ? `${ts(gmt)}Z` : ts(local)\nconst num = (s: unknown): number => (s === '' || s == null ? 0 : Number(s))\nconst termKey = (tax: string, slug: string): string => `${tax}:${slug}`\n\nexport async function parseWxr(input: string | Readable, opts?: { tool?: string }): Promise<WxrParseResult> {\n const site: Record<string, unknown> = {}\n const authors: Array<Record<string, string>> = []\n const terms: Array<Record<string, string>> = []\n const items: XItem[] = []\n const stats: WxrStats = {\n serialized: { detected: 0, decoded: 0, failed: 0, keys: [] },\n acf_fields: 0,\n unresolved_terms: 0,\n counts: {},\n }\n const serializedKeySet = new Set<string>()\n\n // ── sax state machine (ported verbatim) ──\n const parser = sax.createStream(true, { trim: false })\n const path: string[] = []\n let text = ''\n let item: XItem | null = null\n let meta: { key?: string; value?: string } | null = null\n let comment: Record<string, string | Array<{ key?: string; value?: string }>> | null = null\n let cmeta: { key?: string; value?: string } | null = null\n let author: Record<string, string> | null = null\n let term: (Record<string, string> & { _tag?: string }) | null = null\n const channelText: Record<string, string> = {}\n\n const leaf = (name: string, into: Record<string, unknown>, map: Record<string, string>): void => {\n const target = map[name]\n if (target) into[target] = text\n }\n parser.on('opentag', (node) => {\n path.push(node.name)\n text = ''\n const n = node.name\n if (n === 'item') item = { terms: [], postmeta: [], comments: [] }\n else if (n === 'wp:postmeta') meta = {}\n else if (n === 'wp:comment') comment = { meta: [] }\n else if (n === 'wp:commentmeta') cmeta = {}\n else if (n === 'wp:author') author = {}\n else if (n === 'wp:category' || n === 'wp:tag' || n === 'wp:term') term = { _tag: n }\n else if (n === 'category' && item) {\n const attrs = node.attributes as Record<string, string>\n item.terms.push({ taxonomy: attrs.domain ?? '', slug: attrs.nicename ?? '' })\n }\n })\n parser.on('text', (t) => {\n text += t\n })\n parser.on('cdata', (t) => {\n text += t\n })\n parser.on('comment', (c) => {\n const m = /generator=\"([^\"]*)\"\\s+created=\"([^\"]*)\"/.exec(c)\n if (m) {\n site.generator = m[1]\n site.export_date = m[2]\n }\n })\n parser.on('closetag', (n) => {\n const parent = path[path.length - 2]\n if (cmeta) {\n if (n === 'wp:meta_key') cmeta.key = text\n else if (n === 'wp:meta_value') cmeta.value = text\n else if (n === 'wp:commentmeta') {\n ;(comment!.meta as Array<{ key?: string; value?: string }>).push(cmeta)\n cmeta = null\n }\n } else if (comment) {\n const map: Record<string, string> = {\n 'wp:comment_id': 'id',\n 'wp:comment_author': 'author',\n 'wp:comment_author_email': 'email',\n 'wp:comment_author_url': 'url',\n 'wp:comment_date': 'date',\n 'wp:comment_date_gmt': 'date_gmt',\n 'wp:comment_content': 'content',\n 'wp:comment_approved': 'approved',\n 'wp:comment_type': 'type',\n 'wp:comment_parent': 'parent',\n 'wp:comment_user_id': 'user_id',\n }\n leaf(n, comment, map)\n if (n === 'wp:comment') {\n item!.comments.push(comment)\n comment = null\n }\n } else if (meta) {\n if (n === 'wp:meta_key') meta.key = text\n else if (n === 'wp:meta_value') meta.value = text\n else if (n === 'wp:postmeta') {\n item!.postmeta.push(meta)\n meta = null\n }\n } else if (item) {\n if (n === 'category') {\n const t = item.terms.at(-1)\n if (t) t.name = text\n } else {\n const map: Record<string, string> = {\n title: 'title',\n link: 'link',\n 'dc:creator': 'author',\n guid: 'guid',\n description: 'description',\n 'content:encoded': 'content',\n 'excerpt:encoded': 'excerpt',\n 'wp:post_id': 'id',\n 'wp:post_date': 'date',\n 'wp:post_date_gmt': 'date_gmt',\n 'wp:post_modified': 'modified',\n 'wp:post_modified_gmt': 'modified_gmt',\n 'wp:comment_status': 'comment_status',\n 'wp:ping_status': 'ping_status',\n 'wp:post_name': 'slug',\n 'wp:status': 'status',\n 'wp:post_parent': 'parent',\n 'wp:menu_order': 'menu_order',\n 'wp:post_type': 'type',\n 'wp:post_password': 'password',\n 'wp:is_sticky': 'sticky',\n 'wp:attachment_url': 'attachment_url',\n }\n leaf(n, item, map)\n if (n === 'item') {\n items.push(item)\n item = null\n }\n }\n } else if (author) {\n leaf(n, author, {\n 'wp:author_id': 'id',\n 'wp:author_login': 'login',\n 'wp:author_email': 'email',\n 'wp:author_display_name': 'display_name',\n 'wp:author_first_name': 'first_name',\n 'wp:author_last_name': 'last_name',\n })\n if (n === 'wp:author') {\n authors.push(author)\n author = null\n }\n } else if (term) {\n leaf(n, term, {\n 'wp:term_id': 'id',\n 'wp:category_nicename': 'slug',\n 'wp:category_parent': 'parent',\n 'wp:cat_name': 'name',\n 'wp:category_description': 'description',\n 'wp:tag_slug': 'slug',\n 'wp:tag_name': 'name',\n 'wp:tag_description': 'description',\n 'wp:term_taxonomy': 'taxonomy',\n 'wp:term_slug': 'slug',\n 'wp:term_parent': 'parent',\n 'wp:term_name': 'name',\n 'wp:term_description': 'description',\n })\n if (n === term._tag) {\n term.taxonomy = term._tag === 'wp:category' ? 'category' : term._tag === 'wp:tag' ? 'post_tag' : (term.taxonomy ?? '')\n delete term._tag\n terms.push(term)\n term = null\n }\n } else if (parent === 'channel') {\n leaf(n, channelText, {\n title: 'title',\n link: 'link',\n description: 'description',\n language: 'language',\n 'wp:wxr_version': 'wxr_version',\n 'wp:base_site_url': 'base_site_url',\n 'wp:base_blog_url': 'base_blog_url',\n })\n }\n path.pop()\n text = ''\n })\n\n await new Promise<void>((resolve, reject) => {\n parser.on('end', () => resolve())\n parser.on('error', reject)\n if (typeof input === 'string') {\n parser.end(input)\n } else {\n input.pipe(parser)\n }\n })\n Object.assign(site, channelText)\n\n // ── post-processing: meta decode, ACF pairing, entity split, resolution flags ──\n const decodeMeta = (list: Array<{ key?: string; value?: string }>): { meta: Record<string, unknown>; serializedKeys: string[] } => {\n const out: Record<string, unknown> = {}\n const serializedKeys: string[] = []\n for (const { key, value } of list) {\n if (!key) continue\n const r = tryUnserialize(value ?? '')\n if (r.serialized) {\n stats.serialized.detected++\n serializedKeySet.add(key)\n if (r.ok) {\n stats.serialized.decoded++\n serializedKeys.push(key)\n } else stats.serialized.failed++\n }\n const v = r.ok ? r.value : (value ?? '')\n out[key] = key in out ? ([] as unknown[]).concat(out[key] as unknown[], [v]) : v\n }\n return { meta: out, serializedKeys }\n }\n const extractAcf = (metaObj: Record<string, unknown>): Record<string, RawAcfValue> => {\n const acf: Record<string, RawAcfValue> = {}\n for (const [k, v] of Object.entries(metaObj)) {\n if (k.startsWith('_') && typeof v === 'string' && /^field_[0-9a-f]+$/.test(v) && k.slice(1) in metaObj) {\n acf[k.slice(1)] = { value: metaObj[k.slice(1)], field_key: v }\n }\n }\n return acf\n }\n\n const termSet = new Set(terms.map((t) => termKey(t.taxonomy ?? '', t.slug ?? '')))\n\n interface Rec extends RawPost {\n attachment_url: string | null\n _comments: RawComment[]\n }\n const records: Rec[] = items.map((it) => {\n const { meta: m, serializedKeys } = decodeMeta(it.postmeta)\n const acf = extractAcf(m)\n stats.acf_fields += Object.keys(acf).length\n const id = num(it.id)\n const termRefs: RawTermRef[] = it.terms.map((t) => ({\n taxonomy: t.taxonomy,\n slug: t.slug,\n name: t.name ?? '',\n resolved: termSet.has(termKey(t.taxonomy, t.slug)),\n }))\n const comments: RawComment[] = it.comments.map((c) => ({\n id: num(c.id),\n post: id,\n post_type: (it.type as string) || 'post',\n parent: num(c.parent) || null,\n parent_resolved: null,\n author: (c.author as string) ?? '',\n email: (c.email as string) || null,\n url: (c.url as string) || null,\n date: iso(c.date as string, c.date_gmt as string),\n date_gmt: ts(c.date_gmt as string),\n content: (c.content as string) ?? '',\n approved: (c.approved as string | undefined) ?? '1',\n type: (c.type as string) || 'comment',\n user_id: num(c.user_id) || null,\n meta: decodeMeta((c.meta as Array<{ key?: string; value?: string }>) ?? []).meta,\n }))\n return {\n id,\n type: (it.type as string) || 'post',\n status: (it.status as string) || 'publish',\n slug: (it.slug as string) || '',\n title: (it.title as string) ?? '',\n link: (it.link as string) || null,\n guid: (it.guid as string) || null,\n author: (it.author as string) || null,\n date: iso(it.date as string, it.date_gmt as string),\n modified: iso(it.modified as string, it.modified_gmt as string),\n content: (it.content as string) ?? '',\n excerpt: (it.excerpt as string) ?? '',\n parent: num(it.parent) || null,\n menu_order: num(it.menu_order),\n sticky: it.sticky === '1',\n password: (it.password as string) || null,\n comment_status: (it.comment_status as string) || null,\n ping_status: (it.ping_status as string) || null,\n terms: termRefs,\n meta: m,\n serialized_keys: serializedKeys,\n acf,\n // Polylang exports a post's language as a `language` taxonomy term.\n lang: termRefs.find((t) => t.taxonomy === 'language')?.slug || null,\n attachment_url: (it.attachment_url as string) || null,\n _comments: comments,\n }\n })\n for (const r of records) {\n for (const t of r.terms) if (!t.resolved && r.type !== 'nav_menu_item') stats.unresolved_terms++\n }\n\n const byId = new Map(records.map((r) => [r.id, r]))\n const posts: RawPost[] = records\n .filter((r) => r.type !== 'attachment' && r.type !== 'nav_menu_item')\n .map(({ attachment_url: _a, _comments: _c, ...p }) => p)\n // Polylang groups translations under a shared `post_translations` term\n // (`pll_…`); every post in a group is a translation of the others.\n const groups = new Map<string, RawPost[]>()\n for (const p of posts) {\n const group = p.terms.find((t) => t.taxonomy === 'post_translations')?.slug\n if (!group) continue\n groups.set(group, [...(groups.get(group) ?? []), p])\n }\n const languagePairs: RawLanguagePair[] = []\n for (const members of groups.values()) {\n const translations: Record<string, number> = {}\n for (const m of members) if (m.lang) translations[m.lang] = m.id\n if (Object.keys(translations).length > 1) languagePairs.push({ post: members[0]!.id, translations })\n }\n const attachments: RawAttachment[] = records\n .filter((r) => r.type === 'attachment')\n .map((r) => ({\n id: r.id,\n title: r.title,\n slug: r.slug,\n url: r.attachment_url ?? r.guid ?? null,\n alt: typeof r.meta._wp_attachment_image_alt === 'string' ? r.meta._wp_attachment_image_alt : '',\n caption: r.excerpt,\n description: r.content,\n file: typeof r.meta._wp_attached_file === 'string' ? r.meta._wp_attached_file : null,\n image_meta: r.meta._wp_attachment_metadata ?? null,\n mime: guessMime(r.attachment_url ?? r.guid ?? ''),\n parent: r.parent,\n parent_resolved: r.parent ? byId.has(r.parent) : null,\n author: r.author,\n date: r.date,\n status: r.status,\n meta: r.meta,\n }))\n\n // ── menus: nav_menu terms + nav_menu_item posts (targets from meta) ──\n const postById = new Map(posts.map((p) => [p.id, p]))\n const termByIdNum = new Map(terms.map((t) => [num(t.id), t]))\n const menus: RawMenu[] = terms\n .filter((t) => t.taxonomy === 'nav_menu')\n .map((mt) => ({ id: num(mt.id) || null, slug: mt.slug ?? '', name: mt.name ?? '', items: [] as RawMenuItem[] }))\n const menuBySlug = new Map(menus.map((m) => [m.slug, m]))\n for (const r of records.filter((x) => x.type === 'nav_menu_item')) {\n const m = r.meta\n const type = m._menu_item_type as string | undefined\n const object = m._menu_item_object as string | undefined\n const objectId = num(m._menu_item_object_id)\n let target: RawMenuTarget\n if (type === 'custom') target = { kind: 'url', url: String(m._menu_item_url ?? ''), resolved: true }\n else if (type === 'post_type') {\n const p = postById.get(objectId)\n target = { kind: 'post', post_type: object ?? '', id: objectId || null, slug: p?.slug ?? null, resolved: !!p }\n } else if (type === 'taxonomy') {\n const t = termByIdNum.get(objectId)\n target = { kind: 'term', taxonomy: object ?? '', id: objectId || null, slug: t?.slug ?? null, resolved: !!t }\n } else if (type === 'post_type_archive') target = { kind: 'archive', post_type: object ?? '', resolved: true }\n else target = { kind: 'unknown', resolved: false }\n const parent = num(m._menu_item_menu_item_parent) || null\n const itemRec: RawMenuItem = {\n id: r.id,\n title: r.title || (('slug' in target && target.slug) || '') || '',\n order: r.menu_order,\n parent,\n url: (m._menu_item_url as string) || null,\n target,\n target_attr: (m._menu_item_target as string) || null,\n classes: ([] as string[]).concat((m._menu_item_classes as string[]) ?? []).filter(Boolean),\n description: r.content,\n status: r.status,\n }\n const menuSlug = r.terms.find((t) => t.taxonomy === 'nav_menu')?.slug\n const menu = menuSlug ? menuBySlug.get(menuSlug) : undefined\n if (menu) menu.items.push(itemRec)\n }\n const allMenuItemIds = new Set(menus.flatMap((m) => m.items.map((i) => i.id)))\n for (const m of menus) {\n for (const i of m.items) if (i.parent && !allMenuItemIds.has(i.parent)) i.parent_unresolved = true\n }\n\n // ── comments: threaded, parent resolution within the same post ──\n const comments: RawComment[] = []\n for (const r of records) {\n const ids = new Set(r._comments.map((c) => c.id))\n for (const c of r._comments) comments.push({ ...c, parent_resolved: c.parent ? ids.has(c.parent) : null })\n }\n\n const rawAuthors: RawAuthor[] = authors.map((a) => ({\n id: num(a.id) || null,\n login: a.login ?? '',\n email: a.email || null,\n display_name: a.display_name || a.login || '',\n first_name: a.first_name || null,\n last_name: a.last_name || null,\n }))\n const rawTerms: RawTerm[] = terms\n .filter((t) => t.taxonomy !== 'nav_menu')\n .map((t) => ({\n id: num(t.id) || null,\n taxonomy: t.taxonomy ?? '',\n slug: t.slug ?? '',\n name: t.name ?? '',\n parent: t.parent || null,\n parent_resolved: t.parent ? termSet.has(termKey(t.taxonomy ?? '', t.parent)) : null,\n description: t.description || '',\n }))\n\n stats.serialized.keys = [...serializedKeySet].toSorted()\n stats.counts = {\n items: records.length,\n posts: posts.length,\n attachments: attachments.length,\n terms: rawTerms.length,\n authors: rawAuthors.length,\n menus: menus.length,\n comments: comments.length,\n }\n\n const raw: RawIR = {\n version: MIGRATION_CONTRACT_VERSION,\n provenance: { kind: 'wxr', tool: opts?.tool ?? '@contentrain/wp-import' },\n site: {\n url: String(site.link ?? channelText.link ?? ''),\n title: strip(channelText.title),\n description: strip(channelText.description) || undefined,\n base_site_url: channelText.base_site_url,\n base_blog_url: channelText.base_blog_url,\n language: channelText.language ?? null,\n generator: (site.generator as string) ?? null,\n export_date: (site.export_date as string) ?? null,\n wxr_version: channelText.wxr_version ?? null,\n },\n authors: rawAuthors,\n terms: rawTerms,\n posts,\n attachments,\n menus,\n comments,\n ...(languagePairs.length ? { language_pairs: languagePairs } : {}),\n }\n return { raw, stats }\n}\n\nconst MIME: Record<string, string> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n gif: 'image/gif',\n webp: 'image/webp',\n svg: 'image/svg+xml',\n mp3: 'audio/mpeg',\n mp4: 'video/mp4',\n pdf: 'application/pdf',\n}\nfunction guessMime(u: string): string | null {\n const e = (u.split('?')[0]?.split('.').pop() ?? '').toLowerCase()\n return MIME[e] ?? null\n}\n","// Public/authenticated WordPress REST → RawIR.\n//\n// The lowest rungs of the source-access ladder. What REST cannot see (menus,\n// rest:false CPTs, unregistered meta) is simply absent from the result —\n// absence at a low rung is information, not an error, and the manifest layer\n// decides what to recommend about it.\n\nimport type { RawIR, RawAttachment, RawComment, RawLanguagePair, RawPost, RawTerm, RawTermRef, SourceAccessKind } from '@contentrain/types'\nimport { MIGRATION_CONTRACT_VERSION } from '@contentrain/types'\nimport { strip, SKIP_TYPES } from './core.js'\n\nconst iso = (gmt: string | undefined): string | null => (gmt ? `${gmt}Z` : null)\nconst approvedOf = (status: string | undefined): RawComment['approved'] =>\n status === 'approved' ? '1' : status === 'hold' ? '0' : status === 'spam' ? 'spam' : status === 'trash' ? 'trash' : (status ?? '1')\n\nexport interface RestImportOptions {\n origin: string\n /** Injectable for tests and for hosts that need custom dispatch. */\n fetchImpl?: typeof fetch\n /** WordPress Application Password credentials — lifts the rung to rest_auth. */\n auth?: { user: string; appPassword: string }\n perPage?: number\n /**\n * Requests in flight at once, across every collection. Default 4. A site\n * with 74 pages of posts, 40 of media and 37 of comments used to receive\n * ~150 simultaneous requests — enough to trip a host's rate limit or WAF.\n */\n concurrency?: number\n /**\n * Pages fetched per collection at most. Unset = all. A truncated collection\n * is named in `warnings` (with what was skipped), never silently shortened.\n */\n maxPages?: number\n tool?: string\n}\n\nexport interface RestImportResult {\n raw: RawIR\n warnings: string[]\n}\n\ninterface RestPost {\n id: number\n slug: string\n status?: string\n type?: string\n link?: string\n title?: { rendered?: string }\n content?: { rendered?: string }\n excerpt?: { rendered?: string }\n date_gmt?: string\n modified_gmt?: string\n author?: number\n parent?: number\n menu_order?: number\n sticky?: boolean\n comment_status?: string\n ping_status?: string\n featured_media?: number\n categories?: number[]\n tags?: number[]\n meta?: Record<string, unknown>\n /** Polylang: language slug of the post and its translation group (`{ en: 12, tr: 34 }`, self included). */\n lang?: string\n translations?: Record<string, number>\n /** WPML: locale of the post and its other translations. */\n wpml_current_locale?: string\n wpml_translations?: Array<{ locale?: string; id?: number }>\n}\n\nconst positiveInteger = (name: string, value: number): number => {\n if (!Number.isSafeInteger(value) || value < 1) throw new RangeError(`${name} must be a positive safe integer`)\n return value\n}\n\nexport async function fetchRestRawIR(options: RestImportOptions): Promise<RestImportResult> {\n const origin = options.origin.replace(/\\/$/, '')\n const doFetch = options.fetchImpl ?? fetch\n const perPage = positiveInteger('perPage', options.perPage ?? 100)\n if (perPage > 100) throw new RangeError('perPage must be at most 100')\n const concurrency = positiveInteger('concurrency', options.concurrency ?? 4)\n const maxPages = options.maxPages !== undefined ? positiveInteger('maxPages', options.maxPages) : undefined\n const warnings: string[] = []\n // One pool for the whole import: page fetches of every collection queue here,\n // so the cap holds even though collections are requested \"in parallel\".\n const queue: Array<() => Promise<void>> = []\n let running = 0\n const schedule = <T>(task: () => Promise<T>): Promise<T> =>\n new Promise<T>((resolve, reject) => {\n queue.push(() => task().then(resolve, reject))\n pump()\n })\n const pump = (): void => {\n while (running < concurrency && queue.length) {\n const task = queue.shift()!\n running++\n void task().finally(() => {\n running--\n pump()\n })\n }\n }\n const headers: Record<string, string> = { accept: 'application/json' }\n if (options.auth) {\n headers.authorization = `Basic ${Buffer.from(`${options.auth.user}:${options.auth.appPassword}`).toString('base64')}`\n }\n\n const getAll = async <T>(path: string): Promise<T[]> => {\n const url = (page: number) => `${origin}/wp-json/wp/v2/${path}${path.includes('?') ? '&' : '?'}per_page=${perPage}&page=${page}`\n // Hold the slot until the body is consumed, not merely until headers arrive.\n const pageData = (page: number) => schedule(async () => {\n const response = await doFetch(url(page), { headers })\n if (!response.ok) {\n warnings.push(`${path}: page ${page} HTTP ${response.status} — skipped`)\n await response.body?.cancel()\n return { items: [] as T[], pages: 1 }\n }\n const pages = Number(response.headers.get('x-wp-totalpages') ?? '1')\n if (!Number.isSafeInteger(pages) || pages < 0) throw new Error(`${path}: invalid x-wp-totalpages`)\n const items = await response.json() as T[]\n if (!Array.isArray(items)) throw new Error(`${path}: page ${page} is not a collection`)\n return { items, pages: Math.max(1, pages) }\n })\n const first = await pageData(1)\n const totalPages = first.pages\n const firstPage = first.items\n const wanted = maxPages !== undefined ? Math.min(totalPages, maxPages) : totalPages\n if (wanted < totalPages) {\n warnings.push(`${path}: ${totalPages} pages, fetched ${wanted} (maxPages) — ${totalPages - wanted} pages skipped`)\n }\n if (wanted <= 1) return firstPage\n const rest = await Promise.all(\n Array.from({ length: wanted - 1 }, (_v, i) =>\n pageData(i + 2).then((r) => r.items),\n ),\n )\n return firstPage.concat(...rest)\n }\n\n interface RestType { slug: string; rest_base?: string }\n const typesResp = await doFetch(`${origin}/wp-json/wp/v2/types`, { headers })\n const types: Record<string, RestType> = typesResp.ok ? ((await typesResp.json()) as Record<string, RestType>) : {}\n if (!typesResp.ok) warnings.push(`types: HTTP ${typesResp.status} — importing posts and pages only`)\n const postTypes = Object.values(types).filter(\n (t) => t.rest_base && !SKIP_TYPES.test(t.slug) && t.slug !== 'attachment',\n )\n const bases = postTypes.length ? postTypes.map((t) => ({ slug: t.slug, base: t.rest_base! })) : [\n { slug: 'post', base: 'posts' },\n { slug: 'page', base: 'pages' },\n ]\n\n interface RestTerm { id: number; slug: string; name: string; taxonomy?: string; parent?: number; description?: string }\n interface RestUser { id: number; slug: string; name: string }\n interface RestMedia {\n id: number\n slug: string\n title?: { rendered?: string }\n source_url?: string\n alt_text?: string\n caption?: { rendered?: string }\n mime_type?: string\n media_details?: { width?: number; height?: number; file?: string }\n post?: number | null\n date_gmt?: string\n }\n interface RestComment {\n id: number\n post: number\n parent?: number\n author?: number\n author_name?: string\n author_url?: string\n date_gmt?: string\n content?: { rendered?: string }\n status?: string\n type?: string\n }\n\n const [categories, tags, users, media, restComments, ...postLists] = await Promise.all([\n getAll<RestTerm>('categories'),\n getAll<RestTerm>('tags'),\n getAll<RestUser>('users'),\n getAll<RestMedia>('media'),\n getAll<RestComment>('comments'),\n ...bases.map((b) => getAll<RestPost>(b.base)),\n ])\n\n const termsById = new Map<number, RawTerm>()\n for (const [tax, list] of [\n ['category', categories],\n ['post_tag', tags],\n ] as const) {\n for (const t of list) {\n termsById.set(t.id, {\n id: t.id,\n taxonomy: tax,\n slug: t.slug,\n name: strip(t.name),\n parent: t.parent ? (list.find((x) => x.id === t.parent)?.slug ?? null) : null,\n parent_resolved: t.parent ? list.some((x) => x.id === t.parent) : null,\n description: t.description ?? '',\n })\n }\n }\n const userById = new Map(users.map((u) => [u.id, u]))\n\n const posts: RawPost[] = []\n // Translation groups, one pair per group. Polylang's `translations` already\n // includes the post itself; WPML lists the OTHER translations, so add self.\n const languagePairs: RawLanguagePair[] = []\n const seenGroups = new Set<string>()\n const recordGroup = (self: RestPost, lang: string | null) => {\n const translations: Record<string, number> = {}\n if (self.translations && typeof self.translations === 'object') {\n for (const [code, id] of Object.entries(self.translations)) if (typeof id === 'number') translations[code] = id\n } else if (Array.isArray(self.wpml_translations)) {\n for (const t of self.wpml_translations) if (t.locale && typeof t.id === 'number') translations[t.locale] = t.id\n if (lang) translations[lang] = self.id\n }\n const ids = Object.values(translations)\n if (ids.length < 2) return\n const key = [...ids].toSorted((a, b) => a - b).join(',')\n if (seenGroups.has(key)) return\n seenGroups.add(key)\n languagePairs.push({ post: self.id, translations })\n }\n for (const [i, base] of bases.entries()) {\n for (const p of postLists[i] ?? []) {\n const termRefs: RawTermRef[] = [...(p.categories ?? []), ...(p.tags ?? [])].map((id) => {\n const t = termsById.get(id)\n return t\n ? { taxonomy: t.taxonomy, slug: t.slug, name: t.name, resolved: true }\n : { taxonomy: 'category', slug: String(id), name: String(id), resolved: false }\n })\n const author = p.author ? (userById.get(p.author)?.slug ?? null) : null\n const meta: Record<string, unknown> = { ...(p.meta && typeof p.meta === 'object' ? p.meta : {}) }\n if (p.featured_media) meta._thumbnail_id = String(p.featured_media)\n const lang = (typeof p.lang === 'string' && p.lang) || (typeof p.wpml_current_locale === 'string' && p.wpml_current_locale) || null\n recordGroup(p, lang)\n posts.push({\n ...(lang ? { lang } : {}),\n id: p.id,\n type: base.slug,\n status: p.status ?? 'publish',\n slug: p.slug,\n title: p.title?.rendered ?? '',\n link: p.link ?? null,\n guid: null,\n author,\n date: iso(p.date_gmt),\n modified: iso(p.modified_gmt),\n content: p.content?.rendered ?? '',\n excerpt: p.excerpt?.rendered ?? '',\n parent: p.parent || null,\n menu_order: p.menu_order ?? 0,\n sticky: p.sticky ?? false,\n password: null,\n comment_status: p.comment_status ?? null,\n ping_status: p.ping_status ?? null,\n terms: termRefs,\n meta,\n })\n }\n }\n\n const attachments: RawAttachment[] = media.map((m) => ({\n id: m.id,\n title: strip(m.title?.rendered) || m.slug,\n slug: m.slug,\n url: m.source_url ?? null,\n alt: m.alt_text ?? '',\n caption: strip(m.caption?.rendered),\n file: m.media_details?.file ?? null,\n image_meta: m.media_details ?? null,\n mime: m.mime_type ?? null,\n parent: m.post ?? null,\n parent_resolved: m.post ? posts.some((p) => p.id === m.post) : null,\n date: iso(m.date_gmt),\n }))\n\n const postIds = new Set(posts.map((p) => p.id))\n const commentIds = new Set(restComments.map((c) => c.id))\n const comments: RawComment[] = restComments.map((c) => ({\n id: c.id,\n post: c.post,\n post_type: posts.find((p) => p.id === c.post)?.type,\n parent: c.parent || null,\n parent_resolved: c.parent ? commentIds.has(c.parent) : null,\n author: strip(c.author_name) || 'anonymous',\n email: null,\n url: c.author_url || null,\n date: iso(c.date_gmt),\n date_gmt: c.date_gmt ?? null,\n content: c.content?.rendered ?? '',\n approved: approvedOf(c.status),\n type: c.type ?? 'comment',\n user_id: c.author || null,\n }))\n for (const c of comments) if (!postIds.has(c.post)) warnings.push(`comment ${c.id}: post ${c.post} not in fetched set`)\n\n const kind: SourceAccessKind = options.auth ? 'rest_auth' : 'rest_public'\n const raw: RawIR = {\n version: MIGRATION_CONTRACT_VERSION,\n provenance: { kind, tool: options.tool ?? '@contentrain/wp-import' },\n site: { url: origin },\n authors: users.map((u) => ({ id: u.id, login: u.slug, display_name: strip(u.name) || u.slug, email: null })),\n terms: [...termsById.values()],\n posts,\n attachments,\n comments,\n ...(languagePairs.length ? { language_pairs: languagePairs } : {}),\n }\n return { raw, warnings }\n}\n","// RawIR → .contentrain content store, as a pure file map.\n//\n// Ported from the measured import chain (the \"deviations from r4i\" it carried\n// are already folded in: PATH_PATTERNS-conformant layout, ContentrainConfig\n// shape, meta files per locale). Two properties are load-bearing:\n//\n// 1. Identity formulas — hexId('posts:' + slug), hexId('category:' + slug) —\n// are shared with the REST path, so the same site imported twice (or via a\n// later delta) lands on the same entry ids.\n// 2. The EntrySourceMap (wp post id → entry address) is produced HERE and only\n// here: this is the one place that knows both sides of the mapping, and the\n// comments intake downstream cannot exist without it.\n\nimport type { EntrySourceMap, FieldDef, ModelDefinition, RawIR, RawPost } from '@contentrain/types'\nimport {\n byValue,\n canon,\n coreFields,\n decodeSlug,\n hexId,\n PLUGIN_META,\n SKIP_TYPES,\n slugify,\n strip,\n taxModelId,\n} from './core.js'\n\nexport interface ImportReport {\n slug_rewritten: number\n slug_fallback: number\n title_fallback: number\n meta_fields: Record<string, string>\n acf_fields: Record<string, string>\n skipped_types: string[]\n dropped_relations: number\n models: Record<string, { kind: string; domain: string; fields: number; entries: number }>\n /** Default first, then every other language seen on a post. One entry on a monolingual site. */\n locales: string[]\n /** Translation groups that were folded into one entry id across locales. */\n translation_groups: number\n}\n\nexport interface ContentrainResult {\n files: Record<string, string>\n entry_source_map: EntrySourceMap\n report: ImportReport\n}\n\ntype Entry = Record<string, unknown>\ntype Meta = { status: string; source: string; updated_by: string; publish_at?: string }\n\nconst pick = (o: Record<string, unknown>, keys: Set<string>): Entry =>\n Object.fromEntries(Object.entries(o).filter(([k, v]) => keys.has(k) && v !== undefined && v !== null && v !== ''))\nconst typeModelId = (t: string): string => (t === 'post' ? 'posts' : t === 'page' ? 'pages' : t.replace(/_/g, '-'))\nconst domainOf = (t: string): string => (t === 'post' ? 'blog' : t === 'page' ? 'site' : 'custom')\n\n/** `en_US` / `tr-TR` / `en` → `en`: the store's locale is the primary subtag. */\nconst normLocale = (code: string | null | undefined): string => (code ?? '').split(/[-_]/)[0]?.toLowerCase() ?? ''\n\n/** Multilingual-plugin taxonomies that carry language bookkeeping, not content. */\nconst LANGUAGE_TAXONOMIES = new Set(['language', 'post_translations', 'term_language', 'term_translations'])\n\n/**\n * A relation field over `targets`: its declared model, and how a reference into\n * it is stored.\n *\n * With several targets the id alone does not say which model's entry it is, so\n * the reference is the pair `{ model, ref }`. With one there is nothing to\n * choose between: the field is single-target and stores the entry id. Writing\n * the pair there fails the same validator that requires it when there are\n * several — so the choice is made in one place, from the same list the model\n * declares.\n */\nfunction relationOver(targets: string[]): {\n model: string | string[]\n ref: (entry: { model: string; ref: string }) => string | { model: string; ref: string }\n} {\n const single = targets.length === 1\n return { model: single ? targets[0]! : targets, ref: (entry) => (single ? entry.ref : entry) }\n}\n\nexport function rawToContentrain(raw: RawIR, opts?: { updatedBy?: string }): ContentrainResult {\n const updatedBy = opts?.updatedBy ?? '@contentrain/wp-import'\n const locale = normLocale(raw.site.language) || 'en'\n const models: Record<string, ModelDefinition> = {}\n const contents: Record<string, Record<string, Entry>> = {}\n const metas: Record<string, Record<string, Meta>> = {}\n // Per-locale buckets for i18n models (post types on a multilingual site).\n const localeContents: Record<string, Record<string, Record<string, Entry>>> = {}\n const localeMetas: Record<string, Record<string, Record<string, Meta>>> = {}\n\n // ── languages ──\n // A post's language comes from the plugin (`lang`) or, in WXR, from the\n // `language` taxonomy term Polylang attaches. Posts without one belong to the\n // site's default locale. More than one locale seen → post-type models are\n // i18n and content is written per locale.\n const postLocale = (p: RawPost): string =>\n normLocale(p.lang ?? p.terms.find((t) => t.taxonomy === 'language')?.slug) || locale\n const locales = [locale, ...[...new Set(raw.posts.filter((p) => !SKIP_TYPES.test(p.type)).map(postLocale))].filter((l) => l !== locale).toSorted()]\n const multilingual = locales.length > 1\n // Translation groups → one canonical post per group. Its entry id is shared\n // by every translation, which is how an i18n collection addresses one entry\n // in several locales. The canonical member is the default-locale post when\n // there is one, else the lowest id — deterministic either way.\n const postById = new Map(raw.posts.map((p) => [p.id, p]))\n const canonicalOf = new Map<number, number>()\n let translationGroups = 0\n for (const pair of raw.language_pairs ?? []) {\n const members = [...new Set(Object.values(pair.translations))].filter((id) => postById.has(id) && !SKIP_TYPES.test(postById.get(id)!.type))\n if (members.length < 2) continue\n const memberLocales = members.map((id) => postLocale(postById.get(id)!))\n if (new Set(memberLocales).size !== members.length)\n throw new Error(`Translation group ${pair.post} has multiple posts in the same normalized locale; refusing to overwrite a translation`)\n const canonical = members.find((id) => postLocale(postById.get(id)!) === locale) ?? Math.min(...members)\n for (const id of members) canonicalOf.set(id, canonical)\n translationGroups++\n }\n let siteEntry: Entry = {}\n let siteMeta: Meta | null = null\n const report: ImportReport = {\n slug_rewritten: 0,\n slug_fallback: 0,\n title_fallback: 0,\n meta_fields: {},\n acf_fields: {},\n skipped_types: [],\n dropped_relations: 0,\n models: {},\n locales,\n translation_groups: translationGroups,\n }\n const importMeta = (status: string, extra: Partial<Meta> = {}): Meta => ({\n status,\n source: 'import',\n updated_by: updatedBy,\n ...extra,\n })\n const mapStatus = (p: RawPost): Meta => {\n if (p.status === 'future' && (!p.date || !Number.isFinite(Date.parse(p.date))))\n throw new Error(`Scheduled WordPress post ${p.id} has no valid publication date`)\n return p.status === 'publish' || p.status === 'inherit'\n ? importMeta('published')\n : p.status === 'future'\n ? importMeta('published', { publish_at: p.date! })\n : p.status === 'pending'\n ? importMeta('in_review')\n : p.status === 'trash'\n ? importMeta('archived')\n : importMeta('draft')\n }\n const addModel = (m: ModelDefinition): void => {\n models[m.id] = m\n contents[m.id] ??= {}\n metas[m.id] ??= {}\n }\n\n // ── identities (shared formulas — REST and WXR must agree) ──\n const authorId = (login: string): string => hexId(`authors:${login}`)\n const termId = (tax: string, slug: string): string => hexId(`${tax}:${slug}`)\n const usedSlugs = new Map<string, number>()\n const postSlug = (p: RawPost): string => {\n let s = slugify(decodeSlug(p.slug))\n if (!s) {\n s = `${p.type}-${p.id}`\n report.slug_fallback++\n } else if (s !== p.slug) report.slug_rewritten++\n // Slugs are unique per type — and per locale on a multilingual site, where\n // `/about/` and `/tr/about/` are different entries with the same slug.\n const key = multilingual ? `${p.type}:${postLocale(p)}:${s}` : `${p.type}:${s}`\n const n = usedSlugs.get(key) ?? 0\n usedSlugs.set(key, n + 1)\n return n ? `${s}-${p.id}` : s\n }\n const slugOf = new Map<number, string>()\n const postEntry = new Map<number, { model: string; ref: string }>()\n for (const p of raw.posts.toSorted((a, b) => a.id - b.id)) {\n // Only exported entries may be relation targets or appear in the source map.\n if (SKIP_TYPES.test(p.type)) continue\n slugOf.set(p.id, postSlug(p))\n }\n // Keep established slug identities unless distinct translation groups collide.\n // Prefer the default-locale group, then the lowest WP id, independently of\n // REST pagination/WXR order. A colliding group gets a WP-id namespace.\n const identityOwner = new Map<string, number>()\n const exported = raw.posts.filter((p) => !SKIP_TYPES.test(p.type))\n const canonicalPosts = exported.filter((p) => (canonicalOf.get(p.id) ?? p.id) === p.id)\n .toSorted((a, b) => Number(postLocale(b) === locale) - Number(postLocale(a) === locale) || a.id - b.id)\n for (const p of canonicalPosts) {\n const key = `${typeModelId(p.type)}:${slugOf.get(p.id)!}`\n if (!identityOwner.has(key)) identityOwner.set(key, p.id)\n }\n for (const p of exported) {\n const canonical = canonicalOf.get(p.id) ?? p.id\n const mid = typeModelId(p.type)\n const key = `${mid}:${slugOf.get(canonical) ?? slugOf.get(p.id)!}`\n postEntry.set(p.id, { model: mid, ref: hexId(identityOwner.get(key) === canonical ? key : `${mid}:wp:${canonical}`) })\n }\n const mediaRef = (id: number): string | null => (raw.attachments.some((a) => a.id === id) ? hexId(`media:${id}`) : null)\n const titleOf = (p: RawPost): string => {\n const t = strip(p.title)\n if (t) return t\n report.title_fallback++\n return slugOf.get(p.id) ?? `#${p.id}`\n }\n\n // ── authors ──\n addModel({\n id: 'authors', name: 'Authors', kind: 'collection', domain: 'blog', i18n: false, title_field: 'name',\n fields: {\n name: { type: 'string', required: true, label: 'Name', order: 10 },\n slug: { type: 'slug', required: true, unique: true, label: 'Slug', order: 20 },\n email: { type: 'email', label: 'Email', order: 30 },\n wp_id: { type: 'integer', label: 'WP user ID', order: 40 },\n },\n })\n for (const a of raw.authors) {\n const id = authorId(slugify(a.login))\n contents.authors![id] = pick(\n { name: strip(a.display_name), slug: slugify(a.login), email: a.email, wp_id: a.id },\n new Set(['name', 'slug', 'email', 'wp_id']),\n )\n metas.authors![id] = importMeta('published')\n }\n for (const p of raw.posts) {\n if (p.author && !contents.authors![authorId(slugify(p.author))]) {\n contents.authors![authorId(slugify(p.author))] = { name: p.author, slug: slugify(p.author) }\n metas.authors![authorId(slugify(p.author))] = importMeta('published')\n }\n }\n\n // ── taxonomies (nav_menu and post_format excluded; post_format → select field) ──\n const taxonomies = [...new Set(raw.terms.map((t) => t.taxonomy))].filter(\n (t) => t !== 'nav_menu' && t !== 'post_format' && !LANGUAGE_TAXONOMIES.has(t),\n )\n for (const tax of taxonomies) {\n const mid = taxModelId(tax)\n addModel({\n id: mid, name: mid, kind: 'collection', domain: 'blog', i18n: false, title_field: 'name',\n fields: {\n name: { type: 'string', required: true, label: 'Name', order: 10 },\n slug: { type: 'slug', required: true, unique: true, label: 'Slug', order: 20 },\n description: { type: 'text', label: 'Description', order: 30 },\n parent: { type: 'relation', model: mid, label: 'Parent', order: 40 },\n wp_id: { type: 'integer', label: 'WP term ID', order: 50 },\n },\n })\n for (const t of raw.terms.filter((x) => x.taxonomy === tax)) {\n const id = termId(tax, t.slug)\n contents[mid]![id] = pick(\n {\n name: strip(t.name),\n slug: slugify(decodeSlug(t.slug)) || `term-${t.id}`,\n description: strip(t.description),\n parent: t.parent && t.parent_resolved ? termId(tax, t.parent) : null,\n wp_id: t.id,\n },\n new Set(['name', 'slug', 'description', 'parent', 'wp_id']),\n )\n metas[mid]![id] = importMeta('published')\n }\n }\n // referenced-but-unlisted terms join the pool (marked resolved:false upstream, never dropped)\n for (const p of raw.posts) {\n for (const t of p.terms) {\n if (t.taxonomy === 'nav_menu' || t.taxonomy === 'post_format' || t.resolved) continue\n const mid = taxModelId(t.taxonomy)\n if (!models[mid]) continue\n const id = termId(t.taxonomy, t.slug)\n contents[mid]![id] ??= { name: strip(t.name), slug: slugify(t.slug) }\n metas[mid]![id] ??= importMeta('published')\n }\n }\n\n const postTypes = [...new Set(raw.posts.map((p) => p.type))].filter((t) => {\n if (SKIP_TYPES.test(t)) {\n report.skipped_types.push(t)\n return false\n }\n return true\n })\n const contentModelIds = postTypes.map(typeModelId)\n const toContent = relationOver(contentModelIds)\n\n // ── media ──\n addModel({\n id: 'media', name: 'Media', kind: 'collection', domain: 'assets', i18n: false, title_field: 'title',\n fields: {\n title: { type: 'string', required: true, label: 'Title', order: 10 },\n slug: { type: 'slug', required: true, unique: true, label: 'Slug', order: 20 },\n url: { type: 'url', required: true, label: 'Source URL', order: 30 },\n alt: { type: 'string', label: 'Alt text', order: 40 },\n caption: { type: 'text', label: 'Caption', order: 50 },\n description: { type: 'text', label: 'Description', order: 60 },\n mime: { type: 'string', label: 'MIME type', order: 70 },\n width: { type: 'integer', label: 'Width', order: 80 },\n height: { type: 'integer', label: 'Height', order: 90 },\n file: { type: 'string', label: 'Upload path', order: 100 },\n parent: { type: 'relation', model: toContent.model, label: 'Attached to', order: 110 },\n wp_id: { type: 'integer', label: 'WP attachment ID', order: 120 },\n uploaded_at: { type: 'datetime', label: 'Uploaded at', order: 130 },\n },\n })\n const mediaSlugs = new Set<string>()\n for (const a of raw.attachments) {\n const id = hexId(`media:${a.id}`)\n let slug = slugify(decodeSlug(a.slug)) || `media-${a.id}`\n if (mediaSlugs.has(slug)) slug = `${slug}-${a.id}`\n mediaSlugs.add(slug)\n const target = a.parent ? postEntry.get(a.parent) : undefined\n if (a.parent && !target) report.dropped_relations++\n const parent = target ? toContent.ref(target) : null\n const im = (a.image_meta ?? null) as { width?: number; height?: number } | null\n contents.media![id] = pick(\n {\n title: strip(a.title) || slug,\n slug,\n url: a.url,\n alt: strip(a.alt),\n caption: strip(a.caption),\n description: strip(a.description),\n mime: a.mime,\n width: im?.width,\n height: im?.height,\n file: a.file,\n parent,\n wp_id: a.id,\n uploaded_at: a.date,\n },\n new Set(['title', 'slug', 'url', 'alt', 'caption', 'description', 'mime', 'width', 'height', 'file', 'parent', 'wp_id', 'uploaded_at']),\n )\n metas.media![id] = importMeta('published')\n }\n\n // ── post types (post, page, CPTs) ──\n for (const type of postTypes) {\n const typeItems = raw.posts.filter((p) => p.type === type)\n const mid = typeModelId(type)\n const taxes = [...new Set(typeItems.flatMap((p) => p.terms.map((t) => t.taxonomy)))].filter((t) => models[taxModelId(t)])\n const { fields, order } = coreFields({\n excerpt: typeItems.some((p) => p.excerpt),\n body: typeItems.some((p) => p.content),\n date: typeItems.some((p) => p.date),\n author: typeItems.some((p) => p.author),\n taxes,\n cover: typeItems.some((p) => p.meta._thumbnail_id),\n })\n let o = order\n fields.wp_id = { type: 'integer', required: true, unique: true, label: 'WP post ID', order: (o += 10) }\n fields.modified_at = { type: 'datetime', label: 'Modified at', order: (o += 10) }\n fields.link = { type: 'url', label: 'Original permalink', order: (o += 10) }\n const parentModels = [...new Set(typeItems.flatMap((p) => {\n const parent = p.parent ? postEntry.get(p.parent) : undefined\n return parent ? [parent.model] : []\n }))].toSorted()\n if (typeItems.some((p) => p.parent)) fields.parent = {\n type: 'relation',\n model: parentModels.length > 1 ? parentModels : (parentModels[0] ?? mid),\n label: 'Parent', order: (o += 10),\n }\n if (typeItems.some((p) => p.menu_order)) fields.menu_order = { type: 'integer', label: 'Menu order', order: (o += 10) }\n if (typeItems.some((p) => p.sticky)) fields.sticky = { type: 'boolean', label: 'Sticky', order: (o += 10) }\n if (typeItems.some((p) => typeof p.meta._wp_page_template === 'string' && p.meta._wp_page_template !== 'default'))\n fields.template = { type: 'string', label: 'Page template', order: (o += 10) }\n const formats = [\n ...new Set(\n typeItems.flatMap((p) =>\n p.terms.filter((t) => t.taxonomy === 'post_format').map((t) => t.slug.replace(/^post-format-/, '')),\n ),\n ),\n ].toSorted()\n if (formats.length) fields.format = { type: 'select', options: formats, label: 'Post format', order: (o += 10) }\n if (typeItems.some((p) => p.status === 'private' || p.password))\n fields.visibility = { type: 'select', options: ['public', 'private', 'password'], label: 'Visibility', order: (o += 10) }\n if (typeItems.some((p) => p.comment_status))\n fields.comments_open = { type: 'boolean', label: 'Comments open', order: (o += 10) }\n for (const p of typeItems) {\n for (const [k, v] of Object.entries(p.meta)) {\n if (k.startsWith('_') || fields[k] || PLUGIN_META.test(k) || k === 'enclosure') continue\n const t = byValue(v)\n if (t && t !== 'object') {\n fields[k] = { type: t as FieldDef['type'], label: k, order: (o += 10) }\n report.meta_fields[k] = t\n }\n }\n for (const [k, acf] of Object.entries(p.acf ?? {})) {\n if (fields[k]) continue\n const t = byValue(acf.value) ?? 'string'\n fields[k] = { type: (t === 'object' ? 'object' : t) as FieldDef['type'], label: k, description: 'ACF', order: (o += 10) }\n report.acf_fields[k] = t\n }\n }\n // The locales this type actually has content in. A partially-translated\n // site — pages in one language, posts in three — used to fail validation on\n // every untranslated entry, because parity was checked against the whole\n // project list. Declaring the real coverage says so once, in the model.\n //\n // The default locale is always in: it is the site's source of truth, and a\n // post that carries no language tag belongs to it (`postLocale`), so its\n // absence here would be the importer's uncertainty, not a fact about the\n // site. Full coverage writes nothing — absent already means \"all\", and a\n // redundant list would then have to be maintained as locales are added.\n const covered = locales.filter(l => l === locale || typeItems.some((p) => postLocale(p) === l))\n addModel({\n id: mid,\n name: type === 'post' ? 'Posts' : type === 'page' ? 'Pages' : mid,\n kind: 'collection',\n domain: domainOf(type),\n i18n: multilingual,\n ...(multilingual && covered.length < locales.length ? { locales: covered } : {}),\n title_field: 'title',\n fields,\n })\n for (const p of typeItems) {\n const id = postEntry.get(p.id)!.ref\n const loc = postLocale(p)\n const contentBucket = multilingual ? ((localeContents[mid] ??= {})[loc] ??= {}) : contents[mid]!\n const metaBucket = multilingual ? ((localeMetas[mid] ??= {})[loc] ??= {}) : metas[mid]!\n const e: Entry = { title: titleOf(p), slug: slugOf.get(p.id), wp_id: p.id }\n if (fields.excerpt) e.excerpt = strip(p.excerpt)\n if (fields.body) e.body = p.content // Gutenberg comments and shortcodes verbatim (known gap)\n if (fields.published_at && p.date) e.published_at = p.date\n if (p.modified) e.modified_at = p.modified\n if (p.link) e.link = p.link\n if (fields.author && p.author) e.author = authorId(slugify(p.author))\n for (const t of taxes) {\n e[taxModelId(t)] = p.terms\n .filter((x) => x.taxonomy === t && contents[taxModelId(t)]![termId(t, x.slug)])\n .map((x) => termId(t, x.slug))\n }\n if (fields.cover && p.meta._thumbnail_id) {\n const m = mediaRef(Number(p.meta._thumbnail_id))\n if (m) e.cover = m\n else report.dropped_relations++\n }\n if (fields.parent && p.parent) {\n const pe = postEntry.get(p.parent)\n if (pe) e.parent = parentModels.length > 1 ? pe : pe.ref\n else report.dropped_relations++\n }\n if (fields.menu_order && p.menu_order) e.menu_order = p.menu_order\n if (fields.sticky) e.sticky = p.sticky ?? false\n if (fields.template && typeof p.meta._wp_page_template === 'string' && p.meta._wp_page_template !== 'default')\n e.template = p.meta._wp_page_template\n if (fields.format) {\n const f = p.terms.find((t) => t.taxonomy === 'post_format')\n if (f) e.format = f.slug.replace(/^post-format-/, '')\n }\n if (fields.visibility) e.visibility = p.password ? 'password' : p.status === 'private' ? 'private' : 'public'\n if (fields.comments_open) e.comments_open = p.comment_status === 'open'\n for (const k of Object.keys(fields)) if (k in p.meta && !(k in e) && !k.startsWith('_')) e[k] = p.meta[k]\n for (const [k, acf] of Object.entries(p.acf ?? {})) if (fields[k] && !(k in e)) e[k] = acf.value\n contentBucket[id] = e\n metaBucket[id] = mapStatus(p)\n }\n }\n\n // ── menus ──\n const menus = raw.menus ?? []\n if (menus.length) {\n addModel({\n id: 'menus', name: 'Menus', kind: 'collection', domain: 'site', i18n: false, title_field: 'name',\n fields: {\n name: { type: 'string', required: true, label: 'Name', order: 10 },\n slug: { type: 'slug', required: true, unique: true, label: 'Slug', order: 20 },\n items: { type: 'relations', model: 'menu-items', label: 'Items', order: 30 },\n wp_id: { type: 'integer', label: 'WP term ID', order: 40 },\n },\n })\n const toTarget = relationOver([...contentModelIds, ...taxonomies.map(taxModelId)])\n addModel({\n id: 'menu-items', name: 'Menu items', kind: 'collection', domain: 'site', i18n: false, title_field: 'title',\n fields: {\n title: { type: 'string', required: true, label: 'Label', order: 10 },\n menu: { type: 'relation', model: 'menus', required: true, label: 'Menu', order: 20 },\n order: { type: 'integer', label: 'Order', order: 30 },\n parent: { type: 'relation', model: 'menu-items', label: 'Parent item', order: 40 },\n type: { type: 'select', options: ['custom', 'post_type', 'taxonomy', 'post_type_archive'], label: 'Type', order: 50 },\n url: { type: 'url', label: 'URL', order: 60 },\n target: { type: 'relation', model: toTarget.model, label: 'Target', order: 70 },\n open_in_new_tab: { type: 'boolean', label: 'Open in new tab', order: 80 },\n classes: { type: 'array', items: 'string', label: 'CSS classes', order: 90 },\n description: { type: 'text', label: 'Description', order: 100 },\n wp_id: { type: 'integer', label: 'WP post ID', order: 110 },\n },\n })\n const itemRef = (id: number): string => hexId(`menu-items:${id}`)\n for (const m of menus) {\n const mid = hexId(`menus:${m.slug}`)\n contents.menus![mid] = {\n name: strip(m.name),\n slug: slugify(m.slug) || `menu-${m.id}`,\n items: m.items.map((i) => itemRef(i.id)),\n wp_id: m.id,\n }\n metas.menus![mid] = importMeta('published')\n for (const i of m.items) {\n const e: Entry = {\n title: strip(i.title) || i.url || `item-${i.id}`,\n menu: mid,\n order: i.order,\n type: i.target.kind === 'url' ? 'custom' : i.target.kind === 'post' ? 'post_type' : i.target.kind === 'term' ? 'taxonomy' : i.target.kind === 'archive' ? 'post_type_archive' : 'custom',\n wp_id: i.id,\n open_in_new_tab: i.target_attr === '_blank',\n }\n if (i.parent && !i.parent_unresolved) e.parent = itemRef(i.parent)\n if (i.target.kind === 'url' && i.url) e.url = i.url\n if (i.target.kind === 'post' && i.target.resolved && i.target.id != null) {\n const pe = postEntry.get(i.target.id)\n if (pe) e.target = toTarget.ref(pe)\n else report.dropped_relations++\n }\n if (i.target.kind === 'term' && i.target.resolved && i.target.slug && models[taxModelId(i.target.taxonomy)])\n e.target = toTarget.ref({ model: taxModelId(i.target.taxonomy), ref: termId(i.target.taxonomy, i.target.slug) })\n if (i.classes?.length) e.classes = i.classes\n if (i.description) e.description = strip(i.description)\n contents['menu-items']![itemRef(i.id)] = e\n metas['menu-items']![itemRef(i.id)] = importMeta(i.status === 'publish' ? 'published' : 'draft')\n }\n }\n }\n\n // ── comments (frozen archive collection; the live-service path is CommentsExport) ──\n const comments = raw.comments ?? []\n if (comments.length) {\n addModel({\n id: 'comments', name: 'Comments', kind: 'collection', domain: 'blog', i18n: false, title_field: 'author',\n fields: {\n author: { type: 'string', required: true, label: 'Author', order: 10 },\n email: { type: 'email', label: 'Email', order: 20 },\n url: { type: 'url', label: 'Website', order: 30 },\n body: { type: 'richtext', label: 'Body', order: 40 },\n published_at: { type: 'datetime', label: 'Date', order: 50 },\n post: { type: 'relation', model: toContent.model, required: true, label: 'Post', order: 60 },\n parent: { type: 'relation', model: 'comments', label: 'In reply to', order: 70 },\n type: { type: 'string', label: 'Type', order: 80 },\n user: { type: 'relation', model: 'authors', label: 'User', order: 90 },\n wp_id: { type: 'integer', label: 'WP comment ID', order: 100 },\n },\n })\n const cRef = (id: number): string => hexId(`comments:${id}`)\n const userById = new Map(raw.authors.filter((a) => a.id != null).map((a) => [a.id, authorId(slugify(a.login))]))\n for (const c of comments) {\n const post = postEntry.get(c.post)\n if (!post) {\n report.dropped_relations++\n continue\n }\n contents.comments![cRef(c.id)] = pick(\n {\n author: strip(c.author) || 'anonymous',\n email: c.email,\n url: c.url,\n body: c.content,\n published_at: c.date,\n post: toContent.ref(post),\n parent: c.parent && c.parent_resolved ? cRef(c.parent) : null,\n type: c.type,\n user: c.user_id ? userById.get(c.user_id) : null,\n wp_id: c.id,\n },\n new Set(['author', 'email', 'url', 'body', 'published_at', 'post', 'parent', 'type', 'user', 'wp_id']),\n )\n metas.comments![cRef(c.id)] = importMeta(\n c.approved === '1' ? 'published' : c.approved === 'spam' || c.approved === 'trash' ? 'archived' : 'in_review',\n )\n }\n }\n\n // ── site singleton + vocabulary ──\n addModel({\n id: 'site', name: 'Site', kind: 'singleton', domain: 'site', i18n: false, title_field: 'title',\n fields: {\n title: { type: 'string', required: true, label: 'Site title', order: 10 },\n tagline: { type: 'string', label: 'Tagline', order: 20 },\n url: { type: 'url', label: 'URL', order: 30 },\n language: { type: 'string', label: 'Language', order: 40 },\n },\n })\n siteEntry = pick(\n { title: strip(raw.site.title) || 'Site', tagline: raw.site.description, url: raw.site.url, language: raw.site.language },\n new Set(['title', 'tagline', 'url', 'language']),\n )\n siteMeta = importMeta('published')\n const vocabTerms: Record<string, Record<string, string>> = {}\n for (const m of menus) {\n for (const i of m.items) {\n const k = slugify(strip(i.title))\n if (k && !vocabTerms[k]) vocabTerms[k] = { [locale]: strip(i.title) }\n }\n }\n\n // ── file map (canonical serialization; PATH_PATTERNS layout) ──\n const files: Record<string, string> = {}\n const domains = [...new Set(Object.values(models).map((m) => m.domain))].toSorted()\n for (const m of Object.values(models)) {\n files[`.contentrain/models/${m.id}.json`] = canon(m)\n if (m.i18n) {\n // One content + meta file per locale; an entry id recurs across the\n // locales it has a translation in.\n const perLocale = localeContents[m.id] ?? {}\n const ids = new Set<string>()\n for (const loc of Object.keys(perLocale).toSorted()) {\n files[`.contentrain/content/${m.domain}/${m.id}/${loc}.json`] = canon(perLocale[loc])\n files[`.contentrain/meta/${m.id}/${loc}.json`] = canon(localeMetas[m.id]?.[loc] ?? {})\n for (const id of Object.keys(perLocale[loc]!)) ids.add(id)\n }\n report.models[m.id] = { kind: m.kind, domain: m.domain, fields: Object.keys(m.fields ?? {}).length, entries: ids.size }\n continue\n }\n const content = m.kind === 'singleton' ? siteEntry : contents[m.id]!\n const meta = m.kind === 'singleton' ? siteMeta : metas[m.id]!\n files[`.contentrain/content/${m.domain}/${m.id}/data.json`] = canon(content)\n files[`.contentrain/meta/${m.id}/${locale}.json`] = canon(meta)\n report.models[m.id] = {\n kind: m.kind,\n domain: m.domain,\n fields: Object.keys(m.fields ?? {}).length,\n entries: m.kind === 'singleton' ? 1 : Object.keys(contents[m.id]!).length,\n }\n }\n files['.contentrain/config.json'] = canon({\n version: 1,\n stack: 'astro',\n platform: 'web',\n workflow: 'review',\n locales: { default: locale, supported: locales },\n domains,\n })\n files['.contentrain/vocabulary.json'] = canon({ version: 1, terms: vocabTerms })\n\n const entrySourceMap: EntrySourceMap = {}\n for (const [wpId, ref] of postEntry) {\n entrySourceMap[String(wpId)] = { model_id: ref.model, entry_id: ref.ref, locale: postLocale(postById.get(wpId)!) }\n }\n\n files['import-report.json'] = canon(report)\n return { files, entry_source_map: entrySourceMap, report }\n}\n","// CommentsExport builder — the bridge from a migrated static site to a live\n// comments service. Everything the receiving side needs travels here: the\n// entry addresses (WordPress ids mean nothing after migration), the verbatim\n// comments, and which threads were closed at the source.\n\nimport type { CommentsExport, EntrySourceMap, HandoffComments, RawIR } from '@contentrain/types'\nimport { COMMENTS_EXPORT_FORMAT, MIGRATION_CONTRACT_VERSION } from '@contentrain/types'\n\nexport function buildCommentsExport(raw: RawIR, entries: EntrySourceMap, opts?: { generated_at?: string }): CommentsExport {\n const threadsClosed = raw.posts.filter((p) => p.comment_status && p.comment_status !== 'open').map((p) => p.id)\n return {\n version: MIGRATION_CONTRACT_VERSION,\n format: COMMENTS_EXPORT_FORMAT,\n source: raw.provenance,\n site_url: raw.site.url || undefined,\n generated_at: opts?.generated_at ?? new Date().toISOString(),\n entries,\n threads_closed: threadsClosed.length ? threadsClosed : undefined,\n comments: raw.comments ?? [],\n }\n}\n\n/** Handoff summary for the export: counts + closed threads + what could not be addressed. */\nexport function summarizeComments(exp: CommentsExport): HandoffComments {\n const byStatus: Record<string, number> = {}\n const byType: Record<string, number> = {}\n const unresolved: Array<{ comment_id: number; post: number; reason: string }> = []\n for (const c of exp.comments) {\n byStatus[c.approved ?? '1'] = (byStatus[c.approved ?? '1'] ?? 0) + 1\n byType[c.type ?? 'comment'] = (byType[c.type ?? 'comment'] ?? 0) + 1\n if (!exp.entries[String(c.post)]) unresolved.push({ comment_id: c.id, post: c.post, reason: 'post has no entry mapping' })\n }\n return {\n total: exp.comments.length,\n by_status: byStatus,\n types: byType,\n export: { format: exp.format },\n threads_closed: exp.threads_closed,\n unresolved: unresolved.length ? unresolved : undefined,\n }\n}\n","// SourceDeltaPlan (as a bridge produces it) + the repository's store → a plan\n// a person can review: every origin record placed in the store, or said to be\n// unplaceable and why.\n//\n// The bridge knows what changed at the origin and nothing about the store; the\n// store knows its entries and nothing about the origin. The EntrySourceMap\n// written at import time is the only link, so this module lives next to the\n// code that writes it. Pure over file maps, like `rawToContentrain`: no git,\n// no provider, and nothing is written — applying a plan is a governed write\n// that belongs to whoever reviews it.\n//\n// What the planner refuses to do is decide. A record the repository also\n// edited is flagged, not overwritten; a trashed record stays distinguishable\n// from a purged one; a record it cannot place says why instead of vanishing.\n\nimport type { EntryMeta, EntrySourceMap, ModelDefinition, SourceDeltaEntry, SourceDeltaPlan } from '@contentrain/types'\nimport { canon, taxModelId } from './core.js'\n\n/** A `.contentrain` store as a file map (repo-relative path → file text), with the source map it was built from. */\nexport interface DeltaStore {\n files: Record<string, string>\n entry_source_map: EntrySourceMap\n}\n\nexport interface PlanSourceDeltaInput {\n /** The origin delta, as the bridge wrote it. */\n delta: SourceDeltaPlan\n /** The repository's store, as it is now — including edits made since the import. */\n store: DeltaStore\n /** The new export the delta describes. Without it the plan has no field-level differences and no address for a created record. */\n incoming?: DeltaStore\n /**\n * Origin types that are taxonomies. Their ids are term ids, which the\n * source map (post ids) must never be consulted for: post 6 and category 6\n * are different records. Defaults to WordPress core's.\n */\n taxonomies?: string[]\n /**\n * `updated_by` values that mean \"written by an import\". Meta with any other\n * writer — or with a `source` other than `import` — is a repository edit.\n */\n importers?: string[]\n}\n\nexport const DEFAULT_TAXONOMIES = ['category', 'post_tag', 'nav_menu', 'post_format'] as const\nexport const DEFAULT_IMPORTERS = ['contentrain-bridge', '@contentrain/wp-import'] as const\n\ninterface Placed {\n model: ModelDefinition\n entry_id: string\n locale: string\n}\n\ntype Placement = Placed | { unmapped: NonNullable<SourceDeltaEntry['unmapped']> }\n\n/** A stored entry, reduced to what a comparison needs: each field's value as text, and its meta. */\ninterface StoredEntry {\n fields: Record<string, string>\n meta?: EntryMeta\n}\n\n// ─── Store reading ───\n\nconst parseJson = (text: string | undefined): unknown => {\n if (text === undefined) return undefined\n try {\n return JSON.parse(text)\n }\n catch {\n return undefined\n }\n}\n\nconst asRecord = (value: unknown): Record<string, unknown> | undefined =>\n value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined\n\nfunction readModel(files: Record<string, string>, id: string): ModelDefinition | undefined {\n const model = asRecord(parseJson(files[`.contentrain/models/${id}.json`]))\n return model && model.id === id ? (model as unknown as ModelDefinition) : undefined\n}\n\nfunction defaultLocale(files: Record<string, string>): string {\n const config = asRecord(parseJson(files['.contentrain/config.json']))\n const locales = asRecord(config?.locales)\n return typeof locales?.default === 'string' ? locales.default : 'en'\n}\n\n/** The model's own directory. A custom `content_path` or locale strategy is not read — the entry is reported as not found. */\nconst contentDir = (model: ModelDefinition): string | undefined =>\n model.content_path || (model.locale_strategy && model.locale_strategy !== 'file')\n ? undefined\n : `.contentrain/content/${model.domain}/${model.id}`\n\n/**\n * A markdown document's fields as raw text: each top-level frontmatter key\n * with its value lines (a list's items included), and `body`. Compared as\n * written — the store is canonical, so equal values are equal text.\n */\nfunction documentFields(text: string): Record<string, string> {\n const fields: Record<string, string> = {}\n const match = /^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/.exec(text)\n if (!match) return { body: text }\n let key: string | undefined\n for (const line of match[1]!.split('\\n')) {\n const top = /^([^\\s:#][^:]*):(.*)$/.exec(line)\n if (top) {\n key = top[1]!.trim()\n fields[key] = top[2]!.trim()\n }\n else if (key !== undefined) {\n fields[key] += `\\n${line}`\n }\n }\n fields.body = match[2]!\n return fields\n}\n\nfunction readEntry(files: Record<string, string>, at: Placed): StoredEntry | undefined {\n const { model, entry_id: id, locale } = at\n const dir = contentDir(model)\n if (!dir) return undefined\n if (model.kind === 'document') {\n const text = files[model.i18n ? `${dir}/${id}/${locale}.md` : `${dir}/${id}.md`]\n if (text === undefined) return undefined\n const meta = asRecord(parseJson(files[`.contentrain/meta/${model.id}/${id}/${locale}.json`]))\n return { fields: documentFields(text), meta: meta as EntryMeta | undefined }\n }\n if (model.kind !== 'collection') return undefined\n const entries = asRecord(parseJson(files[`${dir}/${model.i18n ? locale : 'data'}.json`]))\n const entry = asRecord(entries?.[id])\n if (!entry) return undefined\n const metas = asRecord(parseJson(files[`.contentrain/meta/${model.id}/${locale}.json`]))\n const fields = Object.fromEntries(Object.entries(entry).map(([key, value]) => [key, canon(value)]))\n return { fields, meta: asRecord(metas?.[id]) as EntryMeta | undefined }\n}\n\n/** The entry in `model` whose `wp_id` is `wpId`, for records the source map does not list (terms, media). */\nfunction findByWpId(files: Record<string, string>, model: ModelDefinition, wpId: number, locale: string): string | undefined {\n const dir = contentDir(model)\n if (!dir) return undefined\n if (model.kind === 'collection') {\n const entries = asRecord(parseJson(files[`${dir}/${model.i18n ? locale : 'data'}.json`])) ?? {}\n return Object.keys(entries).find(id => asRecord(entries[id])?.wp_id === wpId)\n }\n if (model.kind !== 'document') return undefined\n const suffix = model.i18n ? `/${locale}.md` : '.md'\n for (const [path, text] of Object.entries(files)) {\n if (!path.startsWith(`${dir}/`) || !path.endsWith(suffix)) continue\n if (documentFields(text).wp_id === String(wpId)) {\n const rest = path.slice(dir.length + 1, -suffix.length)\n if (!rest.includes('/')) return rest\n }\n }\n return undefined\n}\n\n/** Store models that can hold records of an origin type the source map does not cover. */\nfunction typeModelIds(wpType: string): string[] {\n if (wpType === 'attachment') return ['wp-media', 'media']\n return [`wp-tax-${wpType.replace(/_/g, '-')}`, taxModelId(wpType)]\n}\n\nfunction place(store: DeltaStore, wpType: string | undefined, wpId: number, taxonomies: Set<string>): Placement {\n const { files } = store\n const locale = defaultLocale(files)\n const models = wpType ? typeModelIds(wpType).map(id => readModel(files, id)).filter(model => model !== undefined) : []\n // A listed taxonomy, media, or any type the store has a model of its own for\n // is found by `wp_id` in that model. Only what is left falls to the map.\n if (wpType && (wpType === 'attachment' || taxonomies.has(wpType) || models.length)) {\n if (!models.length) return { unmapped: 'no-model-for-type' }\n for (const model of models) {\n const entryId = findByWpId(files, model, wpId, locale)\n if (entryId) return { model, entry_id: entryId, locale }\n }\n return { unmapped: 'entry-not-found' }\n }\n const ref = store.entry_source_map[String(wpId)]\n if (!ref) return { unmapped: 'not-in-source-map' }\n const model = readModel(files, ref.model_id)\n if (!model) return { unmapped: 'no-model-for-type' }\n return { model, entry_id: ref.entry_id, locale: ref.locale ?? locale }\n}\n\n// ─── Planning ───\n\nconst isPlaced = (placement: Placement): placement is Placed => 'model' in placement\n\nfunction repoEdit(meta: EntryMeta | undefined, importers: Set<string>): SourceDeltaEntry['repo_edit'] {\n if (!meta) return undefined\n if (meta.source === 'import' && importers.has(meta.updated_by)) return undefined\n return {\n updated_by: meta.updated_by,\n ...(meta.updated_at ? { updated_at: meta.updated_at } : {}),\n source: meta.source,\n }\n}\n\nfunction changedFields(before: StoredEntry, after: StoredEntry): string[] {\n const keys = new Set([...Object.keys(before.fields), ...Object.keys(after.fields)])\n return [...keys].filter(key => before.fields[key] !== after.fields[key]).toSorted()\n}\n\nconst DELETED_DETAIL = {\n trashed: 'tombstone: trashed at the origin — it can still come back',\n purged: 'tombstone: purged from the origin',\n} as const\n\nfunction planEntry(entry: SourceDeltaEntry, input: PlanSourceDeltaInput, taxonomies: Set<string>, importers: Set<string>): SourceDeltaEntry {\n const { store, incoming } = input\n const out: SourceDeltaEntry = { ...entry }\n // The bridge's detail for a deletion is the kind itself; the tombstone note below says it in full.\n const notes: string[] = entry.detail && entry.detail !== entry.deleted_kind ? [entry.detail] : []\n\n // A created record is not in the store yet; its address comes from the export that carries it.\n const target = entry.op === 'created'\n ? (incoming ? place(incoming, entry.wp_type, entry.wp_id, taxonomies) : { unmapped: 'not-in-source-map' as const })\n : place(store, entry.wp_type, entry.wp_id, taxonomies)\n\n if (!isPlaced(target)) {\n out.unmapped = target.unmapped\n if (entry.op === 'created' && !incoming) notes.push('no incoming export to place it')\n if (entry.op === 'deleted' && entry.deleted_kind) notes.push(DELETED_DETAIL[entry.deleted_kind])\n out.detail = notes.join('; ')\n return out\n }\n\n out.model = target.model.id\n out.entry_id = target.entry_id\n out.locale = target.locale\n const stored = readEntry(store.files, target)\n\n if (entry.op === 'created') {\n if (stored) notes.push('the store already has an entry at this address')\n }\n else if (!stored) {\n out.unmapped = 'entry-not-found'\n }\n\n if (entry.op === 'deleted' && entry.deleted_kind) notes.push(DELETED_DETAIL[entry.deleted_kind])\n\n if ((entry.op === 'updated' || entry.op === 'moved') && stored) {\n const after = incoming ? place(incoming, entry.wp_type, entry.wp_id, taxonomies) : undefined\n const next = after && isPlaced(after) ? readEntry(incoming!.files, after) : undefined\n if (next) {\n out.fields_changed = changedFields(stored, next)\n if (isPlaced(after!) && after.entry_id !== target.entry_id) out.entry_id_after = after.entry_id\n }\n else {\n notes.push(incoming ? 'not in the incoming export' : 'no incoming export to compare with')\n }\n }\n\n const edit = repoEdit(stored?.meta, importers)\n if (edit) {\n out.conflict = true\n out.repo_edit = edit\n notes.push(`also edited in the repository by ${edit.updated_by}${edit.updated_at ? ` at ${edit.updated_at}` : ''} — not overwritten`)\n }\n\n if (notes.length) out.detail = notes.join('; ')\n return out\n}\n\n/**\n * Place a bridge's source delta in the repository's store.\n *\n * Every entry keeps the bridge's facts (op, ids, fingerprints, paths,\n * `deleted_kind`) and gains its store address — or `unmapped` with the reason.\n * `updated` and `moved` gain `fields_changed` against the incoming export; a\n * record the repository also edited gets `conflict: true` and `repo_edit`;\n * every `moved` whose address changed adds a 301 to `redirects`.\n *\n * Nothing is written and nothing is decided: a conflict is reported for a\n * person to resolve, and applying the plan is a separate, governed step.\n */\nexport function planSourceDelta(input: PlanSourceDeltaInput): SourceDeltaPlan {\n const taxonomies = new Set<string>(input.taxonomies ?? DEFAULT_TAXONOMIES)\n const importers = new Set<string>(input.importers ?? DEFAULT_IMPORTERS)\n const { delta } = input\n const entries = delta.entries.map(entry => planEntry(entry, input, taxonomies, importers))\n\n const redirects = [...(delta.redirects ?? [])]\n const seen = new Set(redirects.map(redirect => redirect.from))\n for (const entry of entries) {\n if (entry.op !== 'moved' || !entry.path_before || !entry.path_after || entry.path_before === entry.path_after) continue\n if (seen.has(entry.path_before)) continue\n seen.add(entry.path_before)\n redirects.push({ from: entry.path_before, to: entry.path_after, status: 301 })\n }\n\n const warnings = [...(delta.warnings ?? [])]\n if (!delta.deletions_detectable) warnings.push('deletions could not be determined from this cursor; the absence of a deleted entry does not mean nothing was deleted')\n if (delta.deletions_undetectable_types?.length) warnings.push(`deletions could not be determined for: ${delta.deletions_undetectable_types.join(', ')}`)\n if (!input.incoming) warnings.push('no incoming export: no field-level differences and no address for created records')\n\n return {\n ...delta,\n entries,\n ...(redirects.length ? { redirects } : {}),\n ...(warnings.length ? { warnings } : {}),\n }\n}\n\n// ─── Report ───\n\nconst address = (entry: SourceDeltaEntry): string =>\n entry.model && entry.entry_id ? `${entry.model}/${entry.entry_id}${entry.entry_id_after ? ` → ${entry.entry_id_after}` : ''}` : `unmapped (${entry.unmapped})`\n\n/** The plan as text, for a dry run: one line per record, then conflicts, unmapped records, redirects and warnings. */\nexport function formatSourceDeltaReport(plan: SourceDeltaPlan): string {\n const counts = new Map<string, number>()\n for (const entry of plan.entries) counts.set(entry.op, (counts.get(entry.op) ?? 0) + 1)\n const summary = [...counts].map(([op, n]) => `${n} ${op}`).join(', ') || 'no changes'\n const { cursor } = plan\n const lines = [\n `Source delta: ${plan.entries.length} records (${summary})`,\n `Cursor: ${[cursor.kind, cursor.inventory_hash ?? cursor.modified_after ?? cursor.export_id, `(${cursor.taken_at})`].filter(Boolean).join(' ')}`,\n `Deletions: ${plan.deletions_detectable ? 'detectable' : 'NOT detectable'}`,\n '',\n ]\n for (const entry of plan.entries) {\n const parts = [`${entry.op.padEnd(8)} ${entry.wp_type ?? '?'} ${entry.wp_id}`, address(entry)]\n if (entry.op === 'moved' && entry.path_before) parts.push(`${entry.path_before} → ${entry.path_after}`)\n if (entry.deleted_kind) parts.push(entry.deleted_kind)\n if (entry.fields_changed) parts.push(`fields: ${entry.fields_changed.join(', ') || 'none'}`)\n if (entry.conflict) parts.push('CONFLICT')\n lines.push(` ${parts.join(' ')}`)\n }\n const conflicts = plan.entries.filter(entry => entry.conflict)\n const unmapped = plan.entries.filter(entry => entry.unmapped)\n lines.push('', `Conflicts (${conflicts.length})${conflicts.length ? ':' : ''}`)\n for (const entry of conflicts) lines.push(` ${entry.wp_type} ${entry.wp_id} → ${address(entry)}: edited by ${entry.repo_edit?.updated_by}${entry.repo_edit?.updated_at ? ` at ${entry.repo_edit.updated_at}` : ''}`)\n lines.push(`Unmapped (${unmapped.length})${unmapped.length ? ':' : ''}`)\n for (const entry of unmapped) lines.push(` ${entry.op} ${entry.wp_type} ${entry.wp_id}: ${entry.unmapped}`)\n lines.push(`Redirects (${plan.redirects?.length ?? 0})${plan.redirects?.length ? ':' : ''}`)\n for (const redirect of plan.redirects ?? []) lines.push(` ${redirect.from} → ${redirect.to} (${redirect.status})`)\n for (const warning of plan.warnings ?? []) lines.push(`Warning: ${warning}`)\n lines.push('', 'Dry run: nothing was written. Applying a plan goes through review.')\n return `${lines.join('\\n')}\\n`\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentrain/wp-import",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WordPress importers for Contentrain — WXR and REST to RawIR, RawIR to a .contentrain content store",
|
|
6
6
|
"type": "module",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"sax": "^1.4.1",
|
|
41
|
-
"@contentrain/types": "1.
|
|
41
|
+
"@contentrain/types": "1.18.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/node": "^22.10.0",
|