@contentrain/types 1.1.0 → 1.3.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 +13 -0
- package/dist/index.d.mts +649 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +107 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,495 @@
|
|
|
1
|
+
//#region src/migration.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Version stamped into every migration document (`version` field on each root).
|
|
4
|
+
* The four contracts evolve together with this package; a reader that sees a
|
|
5
|
+
* higher version than it knows should refuse rather than guess.
|
|
6
|
+
*/
|
|
7
|
+
declare const MIGRATION_CONTRACT_VERSION = 1;
|
|
8
|
+
/**
|
|
9
|
+
* How the WordPress site's data was reached, ordered from least to most
|
|
10
|
+
* complete. Measured field coverage rises with each rung (roughly 34% for
|
|
11
|
+
* public REST, 57% with an Application Password, 82% from a WXR export, 100%
|
|
12
|
+
* via the Bridge plugin) — which is why provenance is recorded on every RawIR:
|
|
13
|
+
* two documents for the same site are only comparable if their rungs are.
|
|
14
|
+
*/
|
|
15
|
+
type SourceAccessKind = 'rest_public' | 'rest_auth' | 'wxr' | 'bridge';
|
|
16
|
+
/** The ladder in ascending completeness order. */
|
|
17
|
+
declare const SOURCE_ACCESS_LADDER: readonly ["rest_public", "rest_auth", "wxr", "bridge"];
|
|
18
|
+
interface RawProvenance {
|
|
19
|
+
kind: SourceAccessKind;
|
|
20
|
+
/** ISO 8601 UTC time the extraction ran. */
|
|
21
|
+
fetched_at?: string;
|
|
22
|
+
/** Name/version of the producing tool (e.g. "wordpress-bridge/1.0"). */
|
|
23
|
+
tool?: string;
|
|
24
|
+
}
|
|
25
|
+
interface RawSite {
|
|
26
|
+
url: string;
|
|
27
|
+
title?: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
/** WXR distinguishes the WP install URL from the public site URL. */
|
|
30
|
+
base_site_url?: string;
|
|
31
|
+
base_blog_url?: string;
|
|
32
|
+
/** Site language as reported (e.g. "en-US"); multilingual detail lives in `language_pairs`. */
|
|
33
|
+
language?: string | null;
|
|
34
|
+
/** Generator string (WordPress version) when the source exposed it. */
|
|
35
|
+
generator?: string | null;
|
|
36
|
+
/** WXR export timestamp, when the source was a WXR file. */
|
|
37
|
+
export_date?: string | null;
|
|
38
|
+
wxr_version?: string | null;
|
|
39
|
+
}
|
|
40
|
+
interface RawAuthor {
|
|
41
|
+
id: number | null;
|
|
42
|
+
login: string;
|
|
43
|
+
email?: string | null;
|
|
44
|
+
display_name: string;
|
|
45
|
+
first_name?: string | null;
|
|
46
|
+
last_name?: string | null;
|
|
47
|
+
}
|
|
48
|
+
interface RawTerm {
|
|
49
|
+
id: number | null;
|
|
50
|
+
taxonomy: string;
|
|
51
|
+
slug: string;
|
|
52
|
+
name: string;
|
|
53
|
+
/** Parent term slug within the same taxonomy, when hierarchical. */
|
|
54
|
+
parent?: string | null;
|
|
55
|
+
/** Whether `parent` names a term present in this document. */
|
|
56
|
+
parent_resolved?: boolean | null;
|
|
57
|
+
description?: string;
|
|
58
|
+
}
|
|
59
|
+
/** A post's reference to a term — kept even when the term itself is absent. */
|
|
60
|
+
interface RawTermRef {
|
|
61
|
+
taxonomy: string;
|
|
62
|
+
slug: string;
|
|
63
|
+
name: string;
|
|
64
|
+
/** Whether the referenced term exists in this document's `terms`. */
|
|
65
|
+
resolved: boolean;
|
|
66
|
+
}
|
|
67
|
+
/** An ACF field value paired with its field-definition key (`field_…`). */
|
|
68
|
+
interface RawAcfValue {
|
|
69
|
+
value: unknown;
|
|
70
|
+
field_key: string;
|
|
71
|
+
}
|
|
72
|
+
interface RawPost {
|
|
73
|
+
id: number;
|
|
74
|
+
/** WordPress post type — "post", "page", or a custom post type slug. */
|
|
75
|
+
type: string;
|
|
76
|
+
/** WordPress status verbatim ("publish", "draft", "future", "pending", …). */
|
|
77
|
+
status: string;
|
|
78
|
+
slug: string;
|
|
79
|
+
title: string;
|
|
80
|
+
/** Public permalink, when known. Permalink *structure* is a site setting; the link is the fact. */
|
|
81
|
+
link?: string | null;
|
|
82
|
+
guid?: string | null;
|
|
83
|
+
/** Author login; resolve against `RawIR.authors`. */
|
|
84
|
+
author: string | null;
|
|
85
|
+
/** ISO 8601 UTC, null when the source carried no usable date. */
|
|
86
|
+
date: string | null;
|
|
87
|
+
modified: string | null;
|
|
88
|
+
/** Rendered/exported HTML body, untransformed. */
|
|
89
|
+
content: string;
|
|
90
|
+
excerpt: string;
|
|
91
|
+
parent?: number | null;
|
|
92
|
+
menu_order?: number;
|
|
93
|
+
sticky?: boolean;
|
|
94
|
+
password?: string | null;
|
|
95
|
+
comment_status?: string | null;
|
|
96
|
+
ping_status?: string | null;
|
|
97
|
+
terms: RawTermRef[];
|
|
98
|
+
/**
|
|
99
|
+
* Post meta, PHP-serialized values already decoded where possible.
|
|
100
|
+
* Core WP keys (underscore-prefixed) are included: which keys matter is a
|
|
101
|
+
* downstream decision.
|
|
102
|
+
*/
|
|
103
|
+
meta: Record<string, unknown>;
|
|
104
|
+
/** Meta keys whose values were PHP-serialized in the source (decoded above). */
|
|
105
|
+
serialized_keys?: string[];
|
|
106
|
+
/** ACF fields recovered by pairing `foo` with `_foo = "field_…"`. */
|
|
107
|
+
acf?: Record<string, RawAcfValue>;
|
|
108
|
+
}
|
|
109
|
+
interface RawAttachment {
|
|
110
|
+
id: number;
|
|
111
|
+
title: string;
|
|
112
|
+
slug: string;
|
|
113
|
+
/** Original file URL. Downloading/re-hosting is a consumer concern. */
|
|
114
|
+
url: string | null;
|
|
115
|
+
alt?: string;
|
|
116
|
+
caption?: string;
|
|
117
|
+
description?: string;
|
|
118
|
+
/** Relative upload path (`_wp_attached_file`), when present. */
|
|
119
|
+
file?: string | null;
|
|
120
|
+
/** `_wp_attachment_metadata` verbatim (sizes, EXIF, …). */
|
|
121
|
+
image_meta?: unknown;
|
|
122
|
+
mime?: string | null;
|
|
123
|
+
parent?: number | null;
|
|
124
|
+
parent_resolved?: boolean | null;
|
|
125
|
+
author?: string | null;
|
|
126
|
+
date?: string | null;
|
|
127
|
+
status?: string;
|
|
128
|
+
meta?: Record<string, unknown>;
|
|
129
|
+
}
|
|
130
|
+
/** Where a menu item points. `resolved` says whether the target exists in this document. */
|
|
131
|
+
type RawMenuTarget = {
|
|
132
|
+
kind: 'url';
|
|
133
|
+
url: string;
|
|
134
|
+
resolved: true;
|
|
135
|
+
} | {
|
|
136
|
+
kind: 'post';
|
|
137
|
+
post_type: string;
|
|
138
|
+
id: number | null;
|
|
139
|
+
slug: string | null;
|
|
140
|
+
resolved: boolean;
|
|
141
|
+
} | {
|
|
142
|
+
kind: 'term';
|
|
143
|
+
taxonomy: string;
|
|
144
|
+
id: number | null;
|
|
145
|
+
slug: string | null;
|
|
146
|
+
resolved: boolean;
|
|
147
|
+
} | {
|
|
148
|
+
kind: 'archive';
|
|
149
|
+
post_type: string;
|
|
150
|
+
resolved: true;
|
|
151
|
+
} | {
|
|
152
|
+
kind: 'unknown';
|
|
153
|
+
resolved: false;
|
|
154
|
+
};
|
|
155
|
+
interface RawMenuItem {
|
|
156
|
+
id: number;
|
|
157
|
+
title: string;
|
|
158
|
+
order?: number;
|
|
159
|
+
/** Parent menu-item id for nested menus. */
|
|
160
|
+
parent?: number | null;
|
|
161
|
+
parent_unresolved?: boolean;
|
|
162
|
+
url?: string | null;
|
|
163
|
+
target: RawMenuTarget;
|
|
164
|
+
/** `target` attribute for the rendered link (e.g. "_blank"). */
|
|
165
|
+
target_attr?: string | null;
|
|
166
|
+
classes?: string[];
|
|
167
|
+
description?: string;
|
|
168
|
+
status?: string;
|
|
169
|
+
}
|
|
170
|
+
interface RawMenu {
|
|
171
|
+
id: number | null;
|
|
172
|
+
slug: string;
|
|
173
|
+
name: string;
|
|
174
|
+
items: RawMenuItem[];
|
|
175
|
+
}
|
|
176
|
+
interface RawComment {
|
|
177
|
+
id: number;
|
|
178
|
+
/** Post id this comment belongs to. */
|
|
179
|
+
post: number;
|
|
180
|
+
post_type?: string;
|
|
181
|
+
parent?: number | null;
|
|
182
|
+
parent_resolved?: boolean | null;
|
|
183
|
+
author: string;
|
|
184
|
+
email?: string | null;
|
|
185
|
+
url?: string | null;
|
|
186
|
+
date: string | null;
|
|
187
|
+
content: string;
|
|
188
|
+
/** WordPress approval flag verbatim ("1", "0", "spam", …). */
|
|
189
|
+
approved?: string;
|
|
190
|
+
type?: string;
|
|
191
|
+
user_id?: number | null;
|
|
192
|
+
meta?: Record<string, unknown>;
|
|
193
|
+
}
|
|
194
|
+
/** A redirect rule (e.g. from the Redirection plugin — visible from the authenticated rung up). */
|
|
195
|
+
interface RawRedirect {
|
|
196
|
+
from: string;
|
|
197
|
+
to: string;
|
|
198
|
+
status?: number;
|
|
199
|
+
/** Which plugin/table produced the rule. */
|
|
200
|
+
source?: string;
|
|
201
|
+
}
|
|
202
|
+
/** Translation grouping for multilingual sites (Polylang/WPML — bridge rung). */
|
|
203
|
+
interface RawLanguagePair {
|
|
204
|
+
post: number;
|
|
205
|
+
/** locale → post id of the translation. */
|
|
206
|
+
translations: Record<string, number>;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* The complete raw extraction of one WordPress site at one access rung.
|
|
210
|
+
* This is the boundary document between extraction (Bridge/importers) and
|
|
211
|
+
* everything downstream — content conversion, capability analysis, migration.
|
|
212
|
+
*/
|
|
213
|
+
interface RawIR {
|
|
214
|
+
version: number;
|
|
215
|
+
provenance: RawProvenance;
|
|
216
|
+
site: RawSite;
|
|
217
|
+
authors: RawAuthor[];
|
|
218
|
+
terms: RawTerm[];
|
|
219
|
+
posts: RawPost[];
|
|
220
|
+
attachments: RawAttachment[];
|
|
221
|
+
menus?: RawMenu[];
|
|
222
|
+
comments?: RawComment[];
|
|
223
|
+
redirects?: RawRedirect[];
|
|
224
|
+
language_pairs?: RawLanguagePair[];
|
|
225
|
+
/** Site options (bridge rung), verbatim. */
|
|
226
|
+
options?: Record<string, unknown>;
|
|
227
|
+
}
|
|
228
|
+
declare const CAPABILITY_KEYS: readonly ["seo", "forms", "comments", "search", "newsletter", "analytics", "consent", "share", "video", "ads", "builder", "blocks", "i18n", "cache", "security", "acf", "ecommerce", "jetpack", "redirects", "membership", "accessibility", "media", "scheduling"];
|
|
229
|
+
type CapabilityKey = (typeof CAPABILITY_KEYS)[number];
|
|
230
|
+
interface CapabilityDetection {
|
|
231
|
+
present: boolean;
|
|
232
|
+
/** Identified plugin/provider, when known (e.g. "yoast", "contact-form-7"). */
|
|
233
|
+
plugin?: string | null;
|
|
234
|
+
/** How it was detected: "rest", "dom", "slug", "headers", "meta", … */
|
|
235
|
+
evidence?: string[];
|
|
236
|
+
detail?: string;
|
|
237
|
+
}
|
|
238
|
+
interface CapabilityManifest {
|
|
239
|
+
version: number;
|
|
240
|
+
site_url: string;
|
|
241
|
+
access: {
|
|
242
|
+
html_status?: number | null;
|
|
243
|
+
rest_status?: number | null; /** Highest source-access rung actually achieved for this site. */
|
|
244
|
+
achieved: SourceAccessKind;
|
|
245
|
+
};
|
|
246
|
+
generator?: string | null;
|
|
247
|
+
theme?: string | null;
|
|
248
|
+
plugins?: string[];
|
|
249
|
+
rest_namespaces?: string[];
|
|
250
|
+
custom_post_types?: Array<{
|
|
251
|
+
slug: string; /** Whether the CPT is exposed in public REST (`rest: false` CPTs need a higher rung). */
|
|
252
|
+
rest_visible: boolean;
|
|
253
|
+
count?: number | null;
|
|
254
|
+
}>;
|
|
255
|
+
/** Comments get first-class treatment: they are a launch-critical capability. */
|
|
256
|
+
comments: {
|
|
257
|
+
active: boolean;
|
|
258
|
+
form_status?: 'open' | 'closed' | 'mixed' | null;
|
|
259
|
+
rest_total?: number | null;
|
|
260
|
+
plugin?: string | null;
|
|
261
|
+
};
|
|
262
|
+
capabilities: Partial<Record<CapabilityKey, CapabilityDetection>>;
|
|
263
|
+
/** Dynamic front-end behaviors observed in the DOM ("carousel", "modal", "search-overlay", …). */
|
|
264
|
+
behaviors?: string[];
|
|
265
|
+
languages?: string[];
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* How the legacy CSS for a family was packaged.
|
|
269
|
+
* - `purge_set`: rules kept if used by any page in the family's page set —
|
|
270
|
+
* small file, safe against per-page rule loss (single-page purging is not).
|
|
271
|
+
* - `localcss`: the site's stylesheets localized wholesale — larger, zero risk.
|
|
272
|
+
*/
|
|
273
|
+
type CssStrategy = 'purge_set' | 'localcss';
|
|
274
|
+
/**
|
|
275
|
+
* Cascade layer name the emitter quarantines legacy CSS under, so a modern
|
|
276
|
+
* utility layer (e.g. Tailwind) can coexist with pixel-faithful migrated pages.
|
|
277
|
+
*/
|
|
278
|
+
declare const LEGACY_CSS_LAYER = "legacy";
|
|
279
|
+
/** Design tokens extracted from the source site, e.g. for a Tailwind `@theme` block. */
|
|
280
|
+
interface DesignTokens {
|
|
281
|
+
colors?: Record<string, string>;
|
|
282
|
+
font_families?: Record<string, string>;
|
|
283
|
+
font_sizes?: Record<string, string>;
|
|
284
|
+
spacing?: Record<string, string>;
|
|
285
|
+
breakpoints?: Record<string, string>;
|
|
286
|
+
}
|
|
287
|
+
type RouteKind = 'front' | 'single' | 'page' | 'archive' | 'term' | 'author' | 'date' | 'search' | 'not_found' | 'custom';
|
|
288
|
+
interface RouteParamDef {
|
|
289
|
+
name: string;
|
|
290
|
+
source: 'post_slug' | 'term_slug' | 'author_slug' | 'page_number' | 'custom';
|
|
291
|
+
}
|
|
292
|
+
/** Selects a family/component variant when a route parameter matches. */
|
|
293
|
+
interface RouteVariantRule {
|
|
294
|
+
param: string;
|
|
295
|
+
in: string[];
|
|
296
|
+
variant: string;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* One route pattern → one layout family. Pagination is a parameter of a route
|
|
300
|
+
* (`/news/page/2` is `/news` with `page_number = 2`), never a separate family —
|
|
301
|
+
* across a 30-site corpus, page 2 never produced a new layout.
|
|
302
|
+
*/
|
|
303
|
+
interface RouteModel {
|
|
304
|
+
id: string;
|
|
305
|
+
/** URL pattern with `:param` placeholders, e.g. "/category/:term", "/news/page/:page". */
|
|
306
|
+
pattern: string;
|
|
307
|
+
kind: RouteKind;
|
|
308
|
+
/** LayoutFamily id. */
|
|
309
|
+
family: string;
|
|
310
|
+
params?: RouteParamDef[];
|
|
311
|
+
/** QueryBinding id feeding this route's list, when it renders one. */
|
|
312
|
+
query?: string;
|
|
313
|
+
variant_rules?: RouteVariantRule[];
|
|
314
|
+
}
|
|
315
|
+
type SlotKind = 'body' | 'title' | 'date' | 'excerpt' | 'author_name' | 'author_link' | 'featured_image' | 'term_list' | 'self_link' | 'custom';
|
|
316
|
+
/** Where a content field lands in a family's markup. */
|
|
317
|
+
interface SlotBinding {
|
|
318
|
+
kind: SlotKind;
|
|
319
|
+
/** CSS selector of the slot's container, when one could be determined. */
|
|
320
|
+
selector?: string;
|
|
321
|
+
/** Content-model field backing a `custom` slot. */
|
|
322
|
+
field?: string;
|
|
323
|
+
/** Date rendering format for `date` slots, as observed on the source site. */
|
|
324
|
+
date_format?: string;
|
|
325
|
+
}
|
|
326
|
+
/** A rendered, asset-rewritten chunk of site chrome the emitter injects verbatim. */
|
|
327
|
+
interface ChromeChunk {
|
|
328
|
+
id: string;
|
|
329
|
+
position: 'head' | 'before_body' | 'after_body';
|
|
330
|
+
html: string;
|
|
331
|
+
}
|
|
332
|
+
interface ComponentPlacement {
|
|
333
|
+
/** ComponentDef id. */
|
|
334
|
+
component: string;
|
|
335
|
+
variant?: string;
|
|
336
|
+
selector?: string;
|
|
337
|
+
}
|
|
338
|
+
interface FamilyVariant {
|
|
339
|
+
key: string;
|
|
340
|
+
description?: string;
|
|
341
|
+
}
|
|
342
|
+
interface LayoutFamily {
|
|
343
|
+
id: string;
|
|
344
|
+
name?: string;
|
|
345
|
+
kind?: RouteKind;
|
|
346
|
+
chrome?: ChromeChunk[];
|
|
347
|
+
slots?: SlotBinding[];
|
|
348
|
+
components?: ComponentPlacement[];
|
|
349
|
+
css: {
|
|
350
|
+
strategy: CssStrategy; /** Emitted stylesheet paths, relative to the generated project. */
|
|
351
|
+
files?: string[];
|
|
352
|
+
};
|
|
353
|
+
/** Observed column counts per viewport class (e.g. desktop 4 → mobile 1). */
|
|
354
|
+
columns?: Partial<Record<'desktop' | 'mobile', number>>;
|
|
355
|
+
variants?: FamilyVariant[];
|
|
356
|
+
/** Verification trail: which pages formed the family, held-out fidelity score. */
|
|
357
|
+
evidence?: {
|
|
358
|
+
pages?: string[];
|
|
359
|
+
holdout_score?: number;
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Component semantic vocabulary. `source` records where its content can come
|
|
364
|
+
* from: `rest` (derivable from content data — author box, term list),
|
|
365
|
+
* `runtime` (needs a live service — comments form, search), `chrome` (carried
|
|
366
|
+
* as rendered markup — nav, ads).
|
|
367
|
+
*/
|
|
368
|
+
declare const COMPONENT_TYPES: readonly ["nav", "related", "comments", "ads", "chrome", "taxonomy", "author", "share", "breadcrumb", "meta", "pagination", "card", "custom"];
|
|
369
|
+
type ComponentType = (typeof COMPONENT_TYPES)[number];
|
|
370
|
+
type ComponentSource = 'rest' | 'runtime' | 'chrome';
|
|
371
|
+
interface ComponentVariantDef {
|
|
372
|
+
key: string;
|
|
373
|
+
conditions?: RouteVariantRule[];
|
|
374
|
+
description?: string;
|
|
375
|
+
}
|
|
376
|
+
interface ComponentDef {
|
|
377
|
+
id: string;
|
|
378
|
+
type: ComponentType;
|
|
379
|
+
source: ComponentSource;
|
|
380
|
+
name?: string;
|
|
381
|
+
selector?: string;
|
|
382
|
+
/** e.g. a card component with `news`, `compact`, `featured` variants. */
|
|
383
|
+
variants?: ComponentVariantDef[];
|
|
384
|
+
}
|
|
385
|
+
type ExcerptSource = 'excerpt' | 'content_first_paragraph' | 'none';
|
|
386
|
+
type PaginationKind = 'numbered' | 'infinite' | 'none';
|
|
387
|
+
/**
|
|
388
|
+
* The query behind a rendered list: which content, filtered how, in what
|
|
389
|
+
* order, how many. Inferred by matching rendered items against candidate
|
|
390
|
+
* queries — order matters, so the sequence is part of the contract.
|
|
391
|
+
*/
|
|
392
|
+
interface QueryBinding {
|
|
393
|
+
id: string;
|
|
394
|
+
/** Content source: "posts", "pages", a CPT slug, or a content-model id. */
|
|
395
|
+
source: string;
|
|
396
|
+
taxonomy?: {
|
|
397
|
+
taxonomy: string; /** Fixed term slug, or the route param carrying it. */
|
|
398
|
+
term?: string;
|
|
399
|
+
term_param?: string;
|
|
400
|
+
};
|
|
401
|
+
author?: {
|
|
402
|
+
slug?: string;
|
|
403
|
+
param?: string;
|
|
404
|
+
};
|
|
405
|
+
order: {
|
|
406
|
+
by: 'date' | 'title' | 'menu_order' | 'custom';
|
|
407
|
+
direction: 'asc' | 'desc';
|
|
408
|
+
};
|
|
409
|
+
per_page: number | null;
|
|
410
|
+
pagination: PaginationKind;
|
|
411
|
+
excerpt_source?: ExcerptSource;
|
|
412
|
+
/** WordPress image size name the list renders (e.g. "medium_large"). */
|
|
413
|
+
image_size?: string;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* - `responsive`: one build, CSS handles viewports.
|
|
417
|
+
* - `split`: desktop and mobile variants are produced separately and served
|
|
418
|
+
* by device — for sites whose mobile DOM differs beyond CSS.
|
|
419
|
+
*/
|
|
420
|
+
type ViewportStrategy = 'responsive' | 'split';
|
|
421
|
+
interface ProjectIR {
|
|
422
|
+
version: number;
|
|
423
|
+
site: {
|
|
424
|
+
url: string;
|
|
425
|
+
title?: string;
|
|
426
|
+
locales?: string[];
|
|
427
|
+
};
|
|
428
|
+
routes: RouteModel[];
|
|
429
|
+
families: LayoutFamily[];
|
|
430
|
+
components?: ComponentDef[];
|
|
431
|
+
queries?: QueryBinding[];
|
|
432
|
+
tokens?: DesignTokens;
|
|
433
|
+
/** Default CSS packaging for families that don't override it. */
|
|
434
|
+
css_default: CssStrategy;
|
|
435
|
+
viewport_strategy?: ViewportStrategy;
|
|
436
|
+
/** Content-model ids this project's queries and slots refer to. */
|
|
437
|
+
content_models?: string[];
|
|
438
|
+
}
|
|
439
|
+
type CapabilityDisposition = 'migrated_static' | 'archived' | 'needs_runtime' | 'external_adapter' | 'kept_on_wordpress' | 'dropped';
|
|
440
|
+
interface HandoffCapability {
|
|
441
|
+
key: CapabilityKey;
|
|
442
|
+
disposition: CapabilityDisposition;
|
|
443
|
+
detail?: string;
|
|
444
|
+
/** e.g. comments: { migrated: 412, spam_skipped: 60 } */
|
|
445
|
+
counts?: Record<string, number>;
|
|
446
|
+
}
|
|
447
|
+
type OfferProvider = 'studio_managed' | 'adapter' | 'keep_wordpress';
|
|
448
|
+
interface CostEstimate {
|
|
449
|
+
currency: string;
|
|
450
|
+
monthly: number;
|
|
451
|
+
assumptions?: string[];
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* The comparison shown when a runtime capability is offered: what running the
|
|
455
|
+
* old server costs (maintenance, security, hosting) against the managed
|
|
456
|
+
* option — so declining is an informed choice, not a default.
|
|
457
|
+
*/
|
|
458
|
+
interface CostComparison {
|
|
459
|
+
self_host?: CostEstimate;
|
|
460
|
+
managed?: CostEstimate;
|
|
461
|
+
}
|
|
462
|
+
interface HandoffOffer {
|
|
463
|
+
capability: CapabilityKey;
|
|
464
|
+
provider: OfferProvider;
|
|
465
|
+
/** Adapter identifier when `provider` is `adapter`. */
|
|
466
|
+
adapter?: string;
|
|
467
|
+
cost_comparison?: CostComparison;
|
|
468
|
+
/** e.g. "keeping comments on WordPress means the WordPress server stays live". */
|
|
469
|
+
warning?: string;
|
|
470
|
+
}
|
|
471
|
+
interface MigrationHandoff {
|
|
472
|
+
version: number;
|
|
473
|
+
site_url: string;
|
|
474
|
+
/** ISO 8601 UTC. */
|
|
475
|
+
generated_at: string;
|
|
476
|
+
repository?: {
|
|
477
|
+
provider: 'github' | 'gitlab';
|
|
478
|
+
owner: string;
|
|
479
|
+
name: string;
|
|
480
|
+
default_branch: string;
|
|
481
|
+
};
|
|
482
|
+
preview_url?: string;
|
|
483
|
+
content_summary?: {
|
|
484
|
+
models: number;
|
|
485
|
+
entries: number;
|
|
486
|
+
locales?: string[];
|
|
487
|
+
};
|
|
488
|
+
capabilities: HandoffCapability[];
|
|
489
|
+
offers?: HandoffOffer[];
|
|
490
|
+
notes?: string[];
|
|
491
|
+
}
|
|
492
|
+
//#endregion
|
|
1
493
|
//#region src/provider.d.ts
|
|
2
494
|
/**
|
|
3
495
|
* A single file change within a plan.
|
|
@@ -38,6 +530,13 @@ interface ProviderCapabilities {
|
|
|
38
530
|
pullRequestFallback: boolean;
|
|
39
531
|
/** Provider can execute AST scanners against source files. Implies local disk access. */
|
|
40
532
|
astScan: boolean;
|
|
533
|
+
/**
|
|
534
|
+
* Provider can write a two-parent merge commit (see
|
|
535
|
+
* `RepoProvider.createMergeCommit`). Absent = false. Optional so that
|
|
536
|
+
* existing external `implements` code keeps compiling: a required member
|
|
537
|
+
* here would be a breaking change for every custom provider.
|
|
538
|
+
*/
|
|
539
|
+
mergeCommit?: boolean;
|
|
41
540
|
}
|
|
42
541
|
/** Capability set for the LocalProvider (simple-git + worktree). */
|
|
43
542
|
declare const LOCAL_CAPABILITIES: ProviderCapabilities;
|
|
@@ -95,6 +594,15 @@ interface Commit {
|
|
|
95
594
|
sync?: SyncResult;
|
|
96
595
|
/** Non-fatal problem encountered while completing the write. */
|
|
97
596
|
warning?: string;
|
|
597
|
+
/**
|
|
598
|
+
* Whether the base branch advanced to the contentrain tip after an
|
|
599
|
+
* auto-merge. Only providers that own the base advance (a local worktree)
|
|
600
|
+
* report it. `blocked_diverged` is a partial success: the content is on
|
|
601
|
+
* the contentrain branch; only the fast-forward is pending.
|
|
602
|
+
*/
|
|
603
|
+
base_advance?: BaseAdvance;
|
|
604
|
+
/** Outcome of pushing the contentrain branch, when the provider pushes. */
|
|
605
|
+
remote_push?: RemotePush;
|
|
98
606
|
}
|
|
99
607
|
/**
|
|
100
608
|
* Input to `applyPlan`. Represents a single atomic commit: all changes land
|
|
@@ -175,6 +683,86 @@ interface MergeResult {
|
|
|
175
683
|
warning?: string;
|
|
176
684
|
};
|
|
177
685
|
}
|
|
686
|
+
/**
|
|
687
|
+
* What happened to the base branch after content landed on the
|
|
688
|
+
* content-tracking branch.
|
|
689
|
+
*
|
|
690
|
+
* `advanced` — the base branch fast-forwarded to the contentrain tip.
|
|
691
|
+
* `blocked_diverged` — the base branch holds commits that are not in
|
|
692
|
+
* contentrain, so fast-forwarding was impossible. The content itself is
|
|
693
|
+
* safe on the contentrain branch; only the advance is pending until the
|
|
694
|
+
* branches are reconciled.
|
|
695
|
+
*
|
|
696
|
+
* These two values are shared vocabulary across the ecosystem — Studio
|
|
697
|
+
* reports the same enum for its main-advance state. A pull request is an
|
|
698
|
+
* attachment (`pullRequestUrl`), never a third state: "PR opened" is
|
|
699
|
+
* `blocked_diverged` with a non-null URL.
|
|
700
|
+
*/
|
|
701
|
+
type BaseAdvance = 'advanced' | 'blocked_diverged';
|
|
702
|
+
/**
|
|
703
|
+
* Outcome of pushing the content-tracking branch to the configured remote.
|
|
704
|
+
*
|
|
705
|
+
* `pushed` — the remote accepted the push (possibly after one
|
|
706
|
+
* fetch-merge-retry). `rejected` — the push failed even after the retry;
|
|
707
|
+
* local and remote have diverged and need reconciling. `no-remote` — the
|
|
708
|
+
* repository has no configured remote, so nothing was attempted.
|
|
709
|
+
*/
|
|
710
|
+
type RemotePush = 'pushed' | 'rejected' | 'no-remote';
|
|
711
|
+
/**
|
|
712
|
+
* Machine-readable conflict kinds produced by the content-aware three-way
|
|
713
|
+
* reconcile planner. CLOSED SET: consumers (Studio) key their localized
|
|
714
|
+
* editor questions on these values, so a free string would drift. Adding a
|
|
715
|
+
* value here is a minor version bump and MUST carry a changelog entry;
|
|
716
|
+
* renaming or removing one is breaking.
|
|
717
|
+
*/
|
|
718
|
+
type ConflictCode = 'field_value_conflict' | 'dictionary_value_conflict' | 'vocabulary_value_conflict' | 'model_key_conflict' | 'meta_status_conflict' | 'document_body_conflict' | 'frontmatter_value_conflict' | 'delete_edit_conflict' | 'file_conflict';
|
|
719
|
+
/**
|
|
720
|
+
* One surviving conflict from a three-way reconcile: base (merge-base),
|
|
721
|
+
* ours (content branch), theirs (base branch) disagree in a way the policy
|
|
722
|
+
* table cannot resolve mechanically. Everything mechanical has already been
|
|
723
|
+
* merged; only these items need a decision from an editor or agent.
|
|
724
|
+
*
|
|
725
|
+
* `id` is derived from the position AND the three values (see
|
|
726
|
+
* `conflictId`), so a resolution made against a stale dry-run no longer
|
|
727
|
+
* matches once any side changes — compare-and-set for free.
|
|
728
|
+
*/
|
|
729
|
+
interface ConflictItem {
|
|
730
|
+
id: string;
|
|
731
|
+
/** Content-root-relative path of the file the conflict lives in. */
|
|
732
|
+
path: string;
|
|
733
|
+
model?: string;
|
|
734
|
+
kind: 'collection' | 'singleton' | 'document' | 'dictionary' | 'vocabulary' | 'model' | 'meta' | 'file';
|
|
735
|
+
/** Entry ID, dictionary key, vocabulary term, or model schema key. */
|
|
736
|
+
key?: string;
|
|
737
|
+
/** Field inside the entry / frontmatter key, when the conflict is field-level. */
|
|
738
|
+
field?: string;
|
|
739
|
+
locale?: string;
|
|
740
|
+
/** The three values in question — for display; omitted for `kind: 'file'`. */
|
|
741
|
+
base?: unknown;
|
|
742
|
+
ours?: unknown;
|
|
743
|
+
theirs?: unknown;
|
|
744
|
+
code: ConflictCode;
|
|
745
|
+
/** Ready-to-display English sentence naming the conflict. */
|
|
746
|
+
message: string;
|
|
747
|
+
/**
|
|
748
|
+
* Policy-table suggestion, never auto-applied. Present only where the
|
|
749
|
+
* approved policy names a preferred side (model schema keys → `theirs`).
|
|
750
|
+
*/
|
|
751
|
+
suggested?: 'ours' | 'theirs';
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* A decision for one conflict, keyed by `ConflictItem.id`. `choose` picks a
|
|
755
|
+
* side (absence of the item on that side means deletion); `value` supplies
|
|
756
|
+
* a hand-authored replacement. A resolution whose `id` no longer matches a
|
|
757
|
+
* live conflict is dropped and the conflict re-reported with a fresh id.
|
|
758
|
+
*/
|
|
759
|
+
type ConflictResolution = {
|
|
760
|
+
id: string;
|
|
761
|
+
choose: 'ours' | 'theirs';
|
|
762
|
+
} | {
|
|
763
|
+
id: string;
|
|
764
|
+
value: unknown;
|
|
765
|
+
};
|
|
178
766
|
/**
|
|
179
767
|
* One media asset as the provider's media stack reports it. `path` is the
|
|
180
768
|
* repo-relative storage path content fields reference (`media/...`); `url`
|
|
@@ -310,6 +898,40 @@ interface RepoProvider extends RepoReader, RepoWriter {
|
|
|
310
898
|
}): Promise<MergeResult>;
|
|
311
899
|
isMerged(branch: string, into?: string): Promise<boolean>;
|
|
312
900
|
getDefaultBranch(): Promise<string>;
|
|
901
|
+
/**
|
|
902
|
+
* The merge-base commit of two refs — the `base` input of a three-way
|
|
903
|
+
* reconcile.
|
|
904
|
+
*
|
|
905
|
+
* Reconciling a diverged content branch needs the last common ancestor,
|
|
906
|
+
* and every backend can name it (git `merge-base`, GitHub's compare API,
|
|
907
|
+
* GitLab's merge_base endpoint) — but not every existing implementation
|
|
908
|
+
* does yet, and demanding it would break external `implements` code.
|
|
909
|
+
*
|
|
910
|
+
* Optional: a provider that omits it cannot drive a reconcile; the caller
|
|
911
|
+
* falls back to a pull-request flow. Returns `null` when the refs share
|
|
912
|
+
* no history.
|
|
913
|
+
*/
|
|
914
|
+
getMergeBase?(refA: string, refB: string): Promise<string | null>;
|
|
915
|
+
/**
|
|
916
|
+
* Write a set of resolved changes as a TWO-PARENT merge commit on
|
|
917
|
+
* `branch`, joining `ours` and `theirs` so that git history records the
|
|
918
|
+
* reconcile — after it, `theirs` is an ancestor of `branch` and a plain
|
|
919
|
+
* fast-forward advance works again. `changes` are applied on top of the
|
|
920
|
+
* `ours` tree; for content-owned paths the planner's output is
|
|
921
|
+
* authoritative, never a textual auto-merge.
|
|
922
|
+
*
|
|
923
|
+
* Optional, mirrored by `capabilities.mergeCommit`: a backend whose
|
|
924
|
+
* commits API cannot express two parents (GitLab) omits it and the
|
|
925
|
+
* caller falls back to a merge-request flow.
|
|
926
|
+
*/
|
|
927
|
+
createMergeCommit?(input: {
|
|
928
|
+
/** Branch whose tip becomes the merge commit (the content branch). */branch: string; /** First parent — the current tip of `branch`. */
|
|
929
|
+
ours: string; /** Second parent — the ref being reconciled in. */
|
|
930
|
+
theirs: string;
|
|
931
|
+
changes: FileChange[];
|
|
932
|
+
message: string;
|
|
933
|
+
author: CommitAuthor;
|
|
934
|
+
}): Promise<Commit>;
|
|
313
935
|
}
|
|
314
936
|
//#endregion
|
|
315
937
|
//#region src/index.d.ts
|
|
@@ -825,6 +1447,32 @@ declare function sortKeys(obj: unknown, fieldOrder?: readonly string[]): unknown
|
|
|
825
1447
|
declare function canonicalStringify(data: unknown, fieldOrder?: readonly string[]): string;
|
|
826
1448
|
/** Generate a 12-character hex entry ID */
|
|
827
1449
|
declare function generateEntryId(): string;
|
|
1450
|
+
/**
|
|
1451
|
+
* FNV-1a 64-bit over the input's UTF-8 code points, returned as 16 hex
|
|
1452
|
+
* chars. Deliberately dependency-free (no `node:crypto` — this package has
|
|
1453
|
+
* zero runtime imports and runs anywhere). Not cryptographic; the use case
|
|
1454
|
+
* is change detection, not tamper resistance.
|
|
1455
|
+
*/
|
|
1456
|
+
declare function stableHash(input: string): string;
|
|
1457
|
+
/**
|
|
1458
|
+
* Identity of a reconcile conflict: position AND values. Hashing the three
|
|
1459
|
+
* values into the id makes every resolution a compare-and-set — if any side
|
|
1460
|
+
* changes between the dry-run that produced the conflict and the apply that
|
|
1461
|
+
* carries the decision, the id no longer matches, the stale resolution is
|
|
1462
|
+
* dropped, and the conflict is re-reported with a fresh id. Values are
|
|
1463
|
+
* normalized through `sortKeys` first so semantically equal objects hash
|
|
1464
|
+
* equal regardless of key order; the positional array keeps absent and
|
|
1465
|
+
* null distinguishable per slot.
|
|
1466
|
+
*/
|
|
1467
|
+
declare function conflictId(conflict: {
|
|
1468
|
+
path: string;
|
|
1469
|
+
key?: string;
|
|
1470
|
+
field?: string;
|
|
1471
|
+
locale?: string;
|
|
1472
|
+
base?: unknown;
|
|
1473
|
+
ours?: unknown;
|
|
1474
|
+
theirs?: unknown;
|
|
1475
|
+
}): string;
|
|
828
1476
|
/** Parse markdown frontmatter — extracts YAML frontmatter and body from a markdown string */
|
|
829
1477
|
declare function parseMarkdownFrontmatter(content: string): {
|
|
830
1478
|
frontmatter: Record<string, unknown>;
|
|
@@ -833,5 +1481,5 @@ declare function parseMarkdownFrontmatter(content: string): {
|
|
|
833
1481
|
/** Serialize data + body into markdown frontmatter format */
|
|
834
1482
|
declare function serializeMarkdownFrontmatter(data: Record<string, unknown>, body: string): string;
|
|
835
1483
|
//#endregion
|
|
836
|
-
export { type ApplyPlanInput, AssetEntry, type Branch, CANONICAL_JSON, CONTENTRAIN_BRANCH, CONTENTRAIN_DIR, CollectionContentFile, CollectionContentOutput, CollectionEntry, CollectionMeta, CollectionRuntimeFormat, type Commit, type CommitAuthor, ContentSource, ContentStatus, ContentrainConfig, ContentrainError, ContextJson, ContextSource, DICTIONARY_TITLE_FIELD, DictionaryContentFile, DictionaryMeta, DocumentContentOutput, DocumentEntry, DocumentMeta, DuplicateGroup, ENTRY_ID_PATTERN, EntryMeta, FieldDef, FieldType, FileCategory, type FileChange, type FileDiff, FileFramework, GraphNode, LOCALE_PATTERN, LOCAL_CAPABILITIES, LocaleStrategy, MODEL_FIELD_ORDER, type MediaAsset, type MediaIngestInput, type MediaListOptions, type MediaListResult, type MediaProvider, type MediaUpdateInput, type MergeResult, ModelDefinition, ModelKind, ModelSummary, NormalizePlan, NormalizePlanExtraction, NormalizePlanModel, NormalizePlanPatch, PATH_PATTERNS, Platform, PolymorphicRelationRef, ProjectGraph, type ProviderCapabilities, type RepoProvider, type RepoReader, type RepoWriter, SECRET_PATTERNS, SLUG_PATTERN, ScaffoldTemplate, ScanCandidate, ScanCandidatesResult, ScanSummaryResult, SingletonContentFile, SingletonMeta, StackType, StringContext, SyncResult, TITLE_FIELD_TYPES, ValidationError, ValidationResult, Vocabulary, WorkflowMode, type WriteReadiness, canonicalStringify, detectSecrets, generateEntryId, isMediaType, isTitleFieldType, orderedFieldNames, parseMarkdownFrontmatter, resolveFieldLabel, serializeMarkdownFrontmatter, sortKeys, validateAccept, validateEntryId, validateFieldValue, validateLocale, validateSemanticType, validateSlug };
|
|
1484
|
+
export { type ApplyPlanInput, AssetEntry, type BaseAdvance, type Branch, CANONICAL_JSON, CAPABILITY_KEYS, COMPONENT_TYPES, CONTENTRAIN_BRANCH, CONTENTRAIN_DIR, CapabilityDetection, CapabilityDisposition, CapabilityKey, CapabilityManifest, ChromeChunk, CollectionContentFile, CollectionContentOutput, CollectionEntry, CollectionMeta, CollectionRuntimeFormat, type Commit, type CommitAuthor, ComponentDef, ComponentPlacement, ComponentSource, ComponentType, ComponentVariantDef, type ConflictCode, type ConflictItem, type ConflictResolution, ContentSource, ContentStatus, ContentrainConfig, ContentrainError, ContextJson, ContextSource, CostComparison, CostEstimate, CssStrategy, DICTIONARY_TITLE_FIELD, DesignTokens, DictionaryContentFile, DictionaryMeta, DocumentContentOutput, DocumentEntry, DocumentMeta, DuplicateGroup, ENTRY_ID_PATTERN, EntryMeta, ExcerptSource, FamilyVariant, FieldDef, FieldType, FileCategory, type FileChange, type FileDiff, FileFramework, GraphNode, HandoffCapability, HandoffOffer, LEGACY_CSS_LAYER, LOCALE_PATTERN, LOCAL_CAPABILITIES, LayoutFamily, LocaleStrategy, MIGRATION_CONTRACT_VERSION, MODEL_FIELD_ORDER, type MediaAsset, type MediaIngestInput, type MediaListOptions, type MediaListResult, type MediaProvider, type MediaUpdateInput, type MergeResult, MigrationHandoff, ModelDefinition, ModelKind, ModelSummary, NormalizePlan, NormalizePlanExtraction, NormalizePlanModel, NormalizePlanPatch, OfferProvider, PATH_PATTERNS, PaginationKind, Platform, PolymorphicRelationRef, ProjectGraph, ProjectIR, type ProviderCapabilities, QueryBinding, RawAcfValue, RawAttachment, RawAuthor, RawComment, RawIR, RawLanguagePair, RawMenu, RawMenuItem, RawMenuTarget, RawPost, RawProvenance, RawRedirect, RawSite, RawTerm, RawTermRef, type RemotePush, type RepoProvider, type RepoReader, type RepoWriter, RouteKind, RouteModel, RouteParamDef, RouteVariantRule, SECRET_PATTERNS, SLUG_PATTERN, SOURCE_ACCESS_LADDER, ScaffoldTemplate, ScanCandidate, ScanCandidatesResult, ScanSummaryResult, SingletonContentFile, SingletonMeta, SlotBinding, SlotKind, SourceAccessKind, StackType, StringContext, SyncResult, TITLE_FIELD_TYPES, ValidationError, ValidationResult, ViewportStrategy, Vocabulary, WorkflowMode, type WriteReadiness, canonicalStringify, conflictId, detectSecrets, generateEntryId, isMediaType, isTitleFieldType, orderedFieldNames, parseMarkdownFrontmatter, resolveFieldLabel, serializeMarkdownFrontmatter, sortKeys, stableHash, validateAccept, validateEntryId, validateFieldValue, validateLocale, validateSemanticType, validateSlug };
|
|
837
1485
|
//# sourceMappingURL=index.d.mts.map
|