@artinstack/migrator 0.1.10 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +2 -1
  2. package/dist/{bundle-Do-9ikQv.d.ts → bundle-BgoXkWMy.d.ts} +1 -1
  3. package/dist/{chunk-3YJFSTYR.js → chunk-373TTCG7.js} +12 -1
  4. package/dist/chunk-373TTCG7.js.map +1 -0
  5. package/dist/chunk-4XZK55RW.js +837 -0
  6. package/dist/chunk-4XZK55RW.js.map +1 -0
  7. package/dist/{chunk-YLVPZ4M3.js → chunk-53VXTXGM.js} +97 -524
  8. package/dist/chunk-53VXTXGM.js.map +1 -0
  9. package/dist/{chunk-3A2PA4P3.js → chunk-5TRCJONX.js} +4 -232
  10. package/dist/chunk-5TRCJONX.js.map +1 -0
  11. package/dist/chunk-HUSXCKPI.js +357 -0
  12. package/dist/chunk-HUSXCKPI.js.map +1 -0
  13. package/dist/{chunk-LC7CGWDN.js → chunk-PD5AZX3B.js} +1 -1
  14. package/dist/chunk-PD5AZX3B.js.map +1 -0
  15. package/dist/{chunk-S4SUJT2D.js → chunk-VQ5HKNYP.js} +109 -4
  16. package/dist/chunk-VQ5HKNYP.js.map +1 -0
  17. package/dist/cli/index.js +5 -4
  18. package/dist/cli/index.js.map +1 -1
  19. package/dist/index.d.ts +3 -3
  20. package/dist/index.js +7 -6
  21. package/dist/normalizer/index.d.ts +106 -4
  22. package/dist/normalizer/index.js +2 -2
  23. package/dist/sinks/index.d.ts +2 -2
  24. package/dist/sinks/index.js +3 -3
  25. package/dist/transformers/index.d.ts +1 -1
  26. package/dist/transformers/index.js +3 -2
  27. package/dist/{types-TCHy3Oko.d.ts → types-Bicgdp4Z.d.ts} +15 -1
  28. package/package.json +1 -1
  29. package/dist/chunk-3A2PA4P3.js.map +0 -1
  30. package/dist/chunk-3YJFSTYR.js.map +0 -1
  31. package/dist/chunk-BONZ3U3I.js +0 -124
  32. package/dist/chunk-BONZ3U3I.js.map +0 -1
  33. package/dist/chunk-LC7CGWDN.js.map +0 -1
  34. package/dist/chunk-S4SUJT2D.js.map +0 -1
  35. package/dist/chunk-YLVPZ4M3.js.map +0 -1
@@ -0,0 +1,357 @@
1
+ import {
2
+ createMigrationMediaRefReplaceWith,
3
+ isMigrationMediaRef,
4
+ normalizeAssetUrl,
5
+ resolveMigrationMediaSourceId
6
+ } from "./chunk-S4GMDRGX.js";
7
+
8
+ // src/transformers/rewrite-inline-images.ts
9
+ import * as cheerio from "cheerio";
10
+ var BACKGROUND_IMAGE_URL_PATTERN = /background(?:-image)?\s*:[^;]*?url\s*\(\s*(['"]?)([^'")]+)\1\s*\)/gi;
11
+ function resolveRewriteOptions(options) {
12
+ const replaceWith = options.replaceWith ?? createMigrationMediaRefReplaceWith();
13
+ const requireUploaded = options.requireUploaded ?? Boolean(options.replaceWith);
14
+ return { replaceWith, requireUploaded };
15
+ }
16
+ function tryRewriteUrl(src, options, uploadedBySourceId, referencedSources, unresolved) {
17
+ const normalized = normalizeAssetUrl(src);
18
+ if (!normalized) return void 0;
19
+ if (isMigrationMediaRef(normalized)) {
20
+ referencedSources.add(normalized);
21
+ return normalized;
22
+ }
23
+ referencedSources.add(normalized);
24
+ const ref = options.resolveAsset(normalized);
25
+ if (!ref?.sourceAssetId) {
26
+ unresolved.add(normalized);
27
+ return void 0;
28
+ }
29
+ const { replaceWith, requireUploaded } = resolveRewriteOptions(options);
30
+ const uploaded = uploadedBySourceId.get(ref.sourceAssetId);
31
+ if (requireUploaded && !uploaded) {
32
+ unresolved.add(normalized);
33
+ return void 0;
34
+ }
35
+ return replaceWith(ref, uploaded);
36
+ }
37
+ function rewriteBackgroundUrlsInStyle(style, options, uploadedBySourceId, referencedSources, unresolved) {
38
+ return style.replace(BACKGROUND_IMAGE_URL_PATTERN, (full, quote, rawUrl) => {
39
+ const replaced = tryRewriteUrl(rawUrl.trim(), options, uploadedBySourceId, referencedSources, unresolved);
40
+ if (!replaced) return full;
41
+ const urlCall = quote ? `url(${quote}${replaced}${quote})` : `url(${replaced})`;
42
+ return full.replace(/url\s*\(\s*(['"]?)([^'")]+)\1\s*\)/i, urlCall);
43
+ });
44
+ }
45
+ function rewriteSrcset(srcset, options, uploadedBySourceId, referencedSources, unresolved) {
46
+ return srcset.split(",").map((entry) => {
47
+ const trimmed = entry.trim();
48
+ if (!trimmed) return entry;
49
+ const [urlPart, descriptor] = trimmed.split(/\s+/, 2);
50
+ const replaced = tryRewriteUrl(urlPart ?? "", options, uploadedBySourceId, referencedSources, unresolved);
51
+ if (!replaced) return entry;
52
+ return descriptor ? `${replaced} ${descriptor}` : replaced;
53
+ }).join(", ");
54
+ }
55
+ function rewriteInlineImages(html, options, uploadedBySourceId) {
56
+ if (!html.trim()) {
57
+ return { html, referencedSources: [], unresolved: [] };
58
+ }
59
+ const $ = cheerio.load(html, { xml: false });
60
+ const referencedSources = /* @__PURE__ */ new Set();
61
+ const unresolved = /* @__PURE__ */ new Set();
62
+ $("img").each((_, element) => {
63
+ const img = $(element);
64
+ const src = img.attr("src")?.trim();
65
+ if (src && !src.startsWith("data:")) {
66
+ const replaced = tryRewriteUrl(src, options, uploadedBySourceId, referencedSources, unresolved);
67
+ if (replaced) img.attr("src", replaced);
68
+ }
69
+ const srcset = img.attr("srcset")?.trim();
70
+ if (srcset) {
71
+ img.attr("srcset", rewriteSrcset(srcset, options, uploadedBySourceId, referencedSources, unresolved));
72
+ }
73
+ });
74
+ $("[data-bg-image]").each((_, element) => {
75
+ const node = $(element);
76
+ const bgImage = node.attr("data-bg-image")?.trim();
77
+ if (!bgImage || bgImage.startsWith("data:")) return;
78
+ const replaced = tryRewriteUrl(bgImage, options, uploadedBySourceId, referencedSources, unresolved);
79
+ if (replaced) node.attr("data-bg-image", replaced);
80
+ });
81
+ $("[style]").each((_, element) => {
82
+ const node = $(element);
83
+ const style = node.attr("style");
84
+ if (!style?.includes("background")) return;
85
+ const rewritten = rewriteBackgroundUrlsInStyle(
86
+ style,
87
+ options,
88
+ uploadedBySourceId,
89
+ referencedSources,
90
+ unresolved
91
+ );
92
+ if (rewritten !== style) node.attr("style", rewritten);
93
+ });
94
+ return {
95
+ html: $.root().html() ?? html,
96
+ referencedSources: [...referencedSources],
97
+ unresolved: [...unresolved]
98
+ };
99
+ }
100
+ function stampMigrationMediaRefs(html, options) {
101
+ return rewriteInlineImages(
102
+ html,
103
+ {
104
+ resolveAsset: (src) => {
105
+ const sourceAssetId = resolveMigrationMediaSourceId(
106
+ src,
107
+ options.urlToSourceId,
108
+ options.originUrlRewrite
109
+ );
110
+ if (!sourceAssetId) return void 0;
111
+ return { originalSrc: src, sourceAssetId };
112
+ },
113
+ replaceWith: options.replaceWith,
114
+ requireUploaded: options.requireUploaded ?? false
115
+ },
116
+ /* @__PURE__ */ new Map()
117
+ );
118
+ }
119
+
120
+ // src/parsers/wordpress/builders/registry.ts
121
+ function layoutEscapeRegExp(value) {
122
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
123
+ }
124
+ function shortcodeOpenRegex(token) {
125
+ return new RegExp(`\\[${layoutEscapeRegExp(token)}\\b([^\\]]*)\\]`, "gi");
126
+ }
127
+ function shortcodeCloseRegex(token) {
128
+ return new RegExp(`\\[\\/${layoutEscapeRegExp(token)}\\b[^\\]]*\\]`, "gi");
129
+ }
130
+ var FRACTIONAL_COLUMN_WIDTHS = {
131
+ one_col: "100%",
132
+ one_half: "50%",
133
+ one_third: "33.33%",
134
+ two_third: "66.67%",
135
+ two_thirds: "66.67%",
136
+ one_fourth: "25%",
137
+ three_fourth: "75%",
138
+ three_fourths: "75%"
139
+ };
140
+ function parseFractionalColumnWidth(token) {
141
+ return FRACTIONAL_COLUMN_WIDTHS[token];
142
+ }
143
+ function prefixedLayoutMap(config) {
144
+ return {
145
+ kind: "prefixed",
146
+ sectionRegex: shortcodeOpenRegex(config.section),
147
+ sectionCloseRegex: shortcodeCloseRegex(config.section),
148
+ rowRegex: shortcodeOpenRegex(config.row),
149
+ rowCloseRegex: shortcodeCloseRegex(config.row),
150
+ columnRegex: shortcodeOpenRegex(config.column),
151
+ columnCloseRegex: shortcodeCloseRegex(config.column),
152
+ bgParamName: config.bgParamName,
153
+ colsParamName: config.colsParamName
154
+ };
155
+ }
156
+ function extendedPrefixedLayoutMap(levels) {
157
+ return { kind: "extended-prefixed", levels };
158
+ }
159
+ function fractionalLayoutMap(config) {
160
+ const columnWidths = {};
161
+ for (const token of config.columns) {
162
+ const width = parseFractionalColumnWidth(token);
163
+ if (width) columnWidths[token] = width;
164
+ }
165
+ return {
166
+ kind: "fractional",
167
+ sectionRegex: shortcodeOpenRegex(config.section),
168
+ sectionCloseRegex: shortcodeCloseRegex(config.section),
169
+ rowRegex: shortcodeOpenRegex(config.row),
170
+ rowCloseRegex: shortcodeCloseRegex(config.row),
171
+ columnTokens: config.columns,
172
+ columnOpenRegexes: config.columns.map(shortcodeOpenRegex),
173
+ columnCloseRegexes: config.columns.map(shortcodeCloseRegex),
174
+ columnWidths,
175
+ bgParamName: config.bgParamName
176
+ };
177
+ }
178
+ var WP_WIDGET_PLACEHOLDER = "\u200B";
179
+ var WORDPRESS_WIDGET_REGISTRY = {
180
+ mapShortcodePrefixes: [
181
+ "blox_gmap",
182
+ "tatsu_gmap",
183
+ "tatsu_map",
184
+ "et_pb_map",
185
+ "vc_gmaps",
186
+ "vc_map"
187
+ ],
188
+ contactFormRules: [
189
+ { tag: "contact-form-7", source: "contact-form-7", idParam: "id" },
190
+ { tag: "contact_form", source: "contact-form-7", idParam: "id" },
191
+ { tag: "gravityform", source: "gravityforms", idParam: "id" },
192
+ { tag: "ninja_form", source: "ninja-forms", idParam: "id" },
193
+ { tag: "wpforms", source: "wpforms", idParam: "id" }
194
+ ],
195
+ videoShortcodePrefixes: [
196
+ "youtube",
197
+ "vimeo",
198
+ "embed",
199
+ "tatsu_video",
200
+ "et_pb_video",
201
+ "vc_video"
202
+ ],
203
+ portfolioShortcode: "portfolio",
204
+ blogShortcodeTags: ["blog", "recent_posts"],
205
+ testimonialsWrapperTags: ["testimonials"],
206
+ testimonialItemTag: "testimonial",
207
+ sliderShortcodeTags: ["rev_slider", "masterslider"],
208
+ galleryShortcode: "gallery",
209
+ idGalleryShortcodes: ["oshine_gallery", "vc_gallery", "nggallery"]
210
+ };
211
+ var UNRESOLVABLE_SHORTCODE_PREFIXES = [
212
+ "woocommerce_cart",
213
+ "woocommerce_checkout",
214
+ "woocommerce_my_account"
215
+ ];
216
+ var WORDPRESS_BUILDER_REGISTRY = [
217
+ {
218
+ id: "tatsu",
219
+ detect: /\[(?:\/)?tatsu_/i,
220
+ layoutMap: prefixedLayoutMap({
221
+ section: "tatsu_section",
222
+ row: "tatsu_row",
223
+ column: "tatsu_column",
224
+ bgParamName: "bg_image",
225
+ colsParamName: "layout"
226
+ }),
227
+ wrapperRules: [
228
+ { shortcodePrefix: "tatsu_text" },
229
+ { shortcodePrefix: "tatsu_inline_text" },
230
+ { shortcodePrefix: "tatsu_text_with_shortcodes" },
231
+ { shortcodePrefix: "tatsu_icon_group" }
232
+ ],
233
+ urlRules: [
234
+ { shortcodePrefix: "tatsu_image", urlParams: ["image", "url", "src"], tag: "img" },
235
+ { shortcodePrefix: "tatsu_single_image", urlParams: ["image", "url", "src"], tag: "img" }
236
+ ],
237
+ iconImageRules: [
238
+ { shortcodePrefix: "tatsu_icon", imageParam: "icon_image", hrefParam: "href" }
239
+ ],
240
+ scaffoldingPrefixes: ["tatsu_"]
241
+ },
242
+ {
243
+ id: "divi",
244
+ detect: /\[(?:\/)?et_pb_/i,
245
+ layoutMap: prefixedLayoutMap({
246
+ section: "et_pb_section",
247
+ row: "et_pb_row",
248
+ column: "et_pb_column",
249
+ bgParamName: "background_image"
250
+ }),
251
+ urlRules: [{ shortcodePrefix: "et_pb_image", urlParams: ["src", "url"], tag: "img" }],
252
+ scaffoldingPrefixes: ["et_pb_"]
253
+ },
254
+ {
255
+ id: "wpbakery",
256
+ detect: /\[(?:\/)?vc_/i,
257
+ layoutMap: extendedPrefixedLayoutMap([
258
+ { role: "section", tokens: ["vc_section"], bgParamName: "bg_image" },
259
+ { role: "row", tokens: ["vc_row"], colsParamName: "layout" },
260
+ { role: "column", tokens: ["vc_column_inner", "vc_column"], widthParamName: "width" }
261
+ ]),
262
+ urlRules: [
263
+ { shortcodePrefix: "vc_single_image", urlParams: ["image", "src", "url"], tag: "img" }
264
+ ],
265
+ scaffoldingPrefixes: ["vc_"]
266
+ },
267
+ {
268
+ id: "fusion",
269
+ detect: /\[(?:\/)?fusion_/i,
270
+ layoutMap: prefixedLayoutMap({
271
+ section: "fusion_builder_container",
272
+ row: "fusion_builder_row",
273
+ column: "fusion_builder_column",
274
+ bgParamName: "background_image"
275
+ }),
276
+ scaffoldingPrefixes: ["fusion_"]
277
+ },
278
+ {
279
+ id: "beaver",
280
+ detect: /\[(?:\/)?fl_(?:row|col|builder)/i,
281
+ layoutMap: extendedPrefixedLayoutMap([
282
+ { role: "row", tokens: ["fl_row"] },
283
+ { role: "column", tokens: ["fl_col"] }
284
+ ]),
285
+ scaffoldingPrefixes: ["fl_"]
286
+ },
287
+ {
288
+ id: "elementor",
289
+ detect: /\[(?:\/)?elementor[-_]/i,
290
+ urlRules: [
291
+ { shortcodePrefix: "elementor-widget", urlParams: ["url", "src", "image"], tag: "img" }
292
+ ],
293
+ scaffoldingPrefixes: ["elementor_"]
294
+ },
295
+ {
296
+ id: "oshine",
297
+ detect: /\[(?:special_sub_title|special_heading5|blox_\w+|grid_content|grids|testimonial\b|portfolio\b|recent_posts\b|animate_icon\w*|section\b|row\b|one_col|one_third|one_half|one_fourth|two_third|three_fourth|text\b)/i,
298
+ layoutMap: fractionalLayoutMap({
299
+ section: "section",
300
+ row: "row",
301
+ columns: [
302
+ "one_col",
303
+ "one_third",
304
+ "two_third",
305
+ "two_thirds",
306
+ "one_half",
307
+ "one_fourth",
308
+ "three_fourth",
309
+ "three_fourths"
310
+ ],
311
+ bgParamName: "bg_image"
312
+ }),
313
+ layoutMaps: [
314
+ extendedPrefixedLayoutMap([
315
+ { role: "section", tokens: ["blox_row"], bgParamName: "bg_image" },
316
+ { role: "row", tokens: ["blox_row_inner"], colsParamName: "columns" },
317
+ {
318
+ role: "column",
319
+ tokens: ["blox_column_inner", "blox_column"],
320
+ widthParamName: "width"
321
+ }
322
+ ])
323
+ ],
324
+ textRules: [
325
+ {
326
+ shortcodePrefix: "special_sub_title",
327
+ fields: [{ param: "title_content", tag: "p" }]
328
+ },
329
+ {
330
+ shortcodePrefix: "special_heading5",
331
+ fields: [
332
+ { param: "title_content", tag: "h2" },
333
+ { param: "caption_content", tag: "h4" }
334
+ ]
335
+ }
336
+ ],
337
+ wrapperRules: [
338
+ { shortcodePrefix: "grid_content" },
339
+ { shortcodePrefix: "blox_text" }
340
+ ],
341
+ urlRules: [
342
+ { shortcodePrefix: "blox_image", urlParams: ["image", "img", "src", "url"], tag: "img" }
343
+ ],
344
+ scaffoldingPrefixes: ["blox_", "animate_icon"],
345
+ legacyScaffoldingTokens: ["text", "icon", "linebreak", "grids", "testimonials"]
346
+ }
347
+ ];
348
+
349
+ export {
350
+ rewriteInlineImages,
351
+ stampMigrationMediaRefs,
352
+ WP_WIDGET_PLACEHOLDER,
353
+ WORDPRESS_WIDGET_REGISTRY,
354
+ UNRESOLVABLE_SHORTCODE_PREFIXES,
355
+ WORDPRESS_BUILDER_REGISTRY
356
+ };
357
+ //# sourceMappingURL=chunk-HUSXCKPI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/transformers/rewrite-inline-images.ts","../src/parsers/wordpress/builders/registry.ts"],"sourcesContent":["import * as cheerio from \"cheerio\";\n\nimport {\n buildMigrationMediaUrlIndex,\n createMigrationMediaRefReplaceWith,\n isMigrationMediaRef,\n normalizeAssetUrl,\n resolveMigrationMediaSourceId,\n type OriginUrlRewriteConfig,\n} from \"../lib/media-urls.js\";\n\nexport interface RewriteInlineImageRef {\n originalSrc: string;\n sourceAssetId?: string;\n}\n\nexport interface UploadedAssetRef {\n targetId: string;\n publicUrl?: string;\n}\n\nexport interface RewriteInlineImagesOptions {\n resolveAsset: (src: string) => RewriteInlineImageRef | undefined;\n /**\n * Replace a resolved source id with a migration ref or CDN URL.\n * When omitted, defaults to OSS-14 `artinstack-migration://asset/…` refs.\n */\n replaceWith?: (ref: RewriteInlineImageRef, uploaded?: UploadedAssetRef) => string;\n /**\n * When true, skip URLs that cannot be matched to an uploaded vault target.\n * Default: false when using migration refs; true when a custom `replaceWith` is supplied.\n */\n requireUploaded?: boolean;\n}\n\nexport interface RewriteInlineImagesResult {\n html: string;\n referencedSources: string[];\n unresolved: string[];\n}\n\n/** Inline CSS `background` / `background-image: url(…)` (quoted or bare). */\nconst BACKGROUND_IMAGE_URL_PATTERN =\n /background(?:-image)?\\s*:[^;]*?url\\s*\\(\\s*(['\"]?)([^'\")]+)\\1\\s*\\)/gi;\n\nfunction resolveRewriteOptions(\n options: RewriteInlineImagesOptions,\n): Required<Pick<RewriteInlineImagesOptions, \"replaceWith\" | \"requireUploaded\">> {\n const replaceWith = options.replaceWith ?? createMigrationMediaRefReplaceWith();\n const requireUploaded = options.requireUploaded ?? Boolean(options.replaceWith);\n return { replaceWith, requireUploaded };\n}\n\nfunction tryRewriteUrl(\n src: string,\n options: RewriteInlineImagesOptions,\n uploadedBySourceId: Map<string, UploadedAssetRef>,\n referencedSources: Set<string>,\n unresolved: Set<string>,\n): string | undefined {\n const normalized = normalizeAssetUrl(src);\n if (!normalized) return undefined;\n\n if (isMigrationMediaRef(normalized)) {\n referencedSources.add(normalized);\n return normalized;\n }\n\n referencedSources.add(normalized);\n const ref = options.resolveAsset(normalized);\n if (!ref?.sourceAssetId) {\n unresolved.add(normalized);\n return undefined;\n }\n\n const { replaceWith, requireUploaded } = resolveRewriteOptions(options);\n const uploaded = uploadedBySourceId.get(ref.sourceAssetId);\n if (requireUploaded && !uploaded) {\n unresolved.add(normalized);\n return undefined;\n }\n\n return replaceWith(ref, uploaded);\n}\n\nfunction rewriteBackgroundUrlsInStyle(\n style: string,\n options: RewriteInlineImagesOptions,\n uploadedBySourceId: Map<string, UploadedAssetRef>,\n referencedSources: Set<string>,\n unresolved: Set<string>,\n): string {\n return style.replace(BACKGROUND_IMAGE_URL_PATTERN, (full, quote: string, rawUrl: string) => {\n const replaced = tryRewriteUrl(rawUrl.trim(), options, uploadedBySourceId, referencedSources, unresolved);\n if (!replaced) return full;\n\n const urlCall = quote\n ? `url(${quote}${replaced}${quote})`\n : `url(${replaced})`;\n return full.replace(/url\\s*\\(\\s*(['\"]?)([^'\")]+)\\1\\s*\\)/i, urlCall);\n });\n}\n\nfunction rewriteSrcset(\n srcset: string,\n options: RewriteInlineImagesOptions,\n uploadedBySourceId: Map<string, UploadedAssetRef>,\n referencedSources: Set<string>,\n unresolved: Set<string>,\n): string {\n return srcset\n .split(\",\")\n .map((entry) => {\n const trimmed = entry.trim();\n if (!trimmed) return entry;\n const [urlPart, descriptor] = trimmed.split(/\\s+/, 2);\n const replaced = tryRewriteUrl(urlPart ?? \"\", options, uploadedBySourceId, referencedSources, unresolved);\n if (!replaced) return entry;\n return descriptor ? `${replaced} ${descriptor}` : replaced;\n })\n .join(\", \");\n}\n\n/** Rewrite `<img src>` / `srcset`, `data-bg-image`, and inline CSS backgrounds using uploaded asset targets. */\nexport function rewriteInlineImages(\n html: string,\n options: RewriteInlineImagesOptions,\n uploadedBySourceId: Map<string, UploadedAssetRef>,\n): RewriteInlineImagesResult {\n if (!html.trim()) {\n return { html, referencedSources: [], unresolved: [] };\n }\n\n const $ = cheerio.load(html, { xml: false });\n const referencedSources = new Set<string>();\n const unresolved = new Set<string>();\n\n $(\"img\").each((_, element) => {\n const img = $(element);\n const src = img.attr(\"src\")?.trim();\n if (src && !src.startsWith(\"data:\")) {\n const replaced = tryRewriteUrl(src, options, uploadedBySourceId, referencedSources, unresolved);\n if (replaced) img.attr(\"src\", replaced);\n }\n\n const srcset = img.attr(\"srcset\")?.trim();\n if (srcset) {\n img.attr(\"srcset\", rewriteSrcset(srcset, options, uploadedBySourceId, referencedSources, unresolved));\n }\n });\n\n $(\"[data-bg-image]\").each((_, element) => {\n const node = $(element);\n const bgImage = node.attr(\"data-bg-image\")?.trim();\n if (!bgImage || bgImage.startsWith(\"data:\")) return;\n const replaced = tryRewriteUrl(bgImage, options, uploadedBySourceId, referencedSources, unresolved);\n if (replaced) node.attr(\"data-bg-image\", replaced);\n });\n\n $(\"[style]\").each((_, element) => {\n const node = $(element);\n const style = node.attr(\"style\");\n if (!style?.includes(\"background\")) return;\n const rewritten = rewriteBackgroundUrlsInStyle(\n style,\n options,\n uploadedBySourceId,\n referencedSources,\n unresolved,\n );\n if (rewritten !== style) node.attr(\"style\", rewritten);\n });\n\n return {\n html: $.root().html() ?? html,\n referencedSources: [...referencedSources],\n unresolved: [...unresolved],\n };\n}\n\nexport interface StampMigrationMediaRefsOptions {\n /** Pre-built url/pathname → sourceId map (from attachments + inline assets). */\n urlToSourceId: Map<string, string>;\n /** Canonicalize lookup keys during stamp (OSS-15). */\n originUrlRewrite?: OriginUrlRewriteConfig;\n replaceWith?: RewriteInlineImagesOptions[\"replaceWith\"];\n requireUploaded?: boolean;\n}\n\n/**\n * OSS-14 — replace resolved `wp-content/uploads` URLs with `artinstack-migration://asset/…`\n * refs. Does not invent refs for unknown URLs (left unchanged + listed in `unresolved`).\n */\nexport function stampMigrationMediaRefs(\n html: string,\n options: StampMigrationMediaRefsOptions,\n): RewriteInlineImagesResult {\n return rewriteInlineImages(\n html,\n {\n resolveAsset: (src) => {\n const sourceAssetId = resolveMigrationMediaSourceId(\n src,\n options.urlToSourceId,\n options.originUrlRewrite,\n );\n if (!sourceAssetId) return undefined;\n return { originalSrc: src, sourceAssetId };\n },\n replaceWith: options.replaceWith,\n requireUploaded: options.requireUploaded ?? false,\n },\n new Map(),\n );\n}\n\n/** Build a url index from attachment rows and/or normalized assets. */\nexport { buildMigrationMediaUrlIndex };\n","export type BuilderHtmlTag = \"img\" | \"video\" | \"iframe\";\nexport type TextHtmlTag = \"p\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\";\n\n/** Bucket 1 — shortcodes with asset URL params → standard HTML. */\nexport interface BuilderUrlRule {\n shortcodePrefix: string;\n urlParams: string[];\n tag: BuilderHtmlTag;\n}\n\n/** Bucket 1 — shortcodes with text params → semantic HTML. */\nexport interface BuilderTextRule {\n shortcodePrefix: string;\n fields: { param: string; tag: TextHtmlTag }[];\n}\n\n/** Bucket 1 — shortcodes with inner HTML (+ optional image param). */\nexport interface BuilderWrapperRule {\n shortcodePrefix: string;\n urlParams?: string[];\n}\n\n/** Bucket 1 — image-based icon modules (`icon_image` param) → linked `<img>`. */\nexport interface BuilderIconImageRule {\n shortcodePrefix: string;\n imageParam: string;\n hrefParam?: string;\n}\n\n/** Bucket 1 — dynamic embeds replaced with a static migration placeholder. */\nexport interface BuilderPlaceholderRule {\n shortcodePrefix: string;\n html: string;\n}\n\n/** Profile A — prefixed namespace tokens (Tatsu, Divi, WPBakery, …). */\nexport interface PrefixedLayoutMap {\n kind: \"prefixed\";\n sectionRegex: RegExp;\n sectionCloseRegex: RegExp;\n rowRegex: RegExp;\n rowCloseRegex: RegExp;\n columnRegex: RegExp;\n columnCloseRegex: RegExp;\n bgParamName?: string;\n colsParamName?: string;\n}\n\n/** Profile B — legacy Blox fractional column tokens (`one_third`, `one_half`, …). */\nexport interface FractionalLayoutMap {\n kind: \"fractional\";\n sectionRegex: RegExp;\n sectionCloseRegex: RegExp;\n rowRegex: RegExp;\n rowCloseRegex: RegExp;\n columnTokens: string[];\n columnOpenRegexes: RegExp[];\n columnCloseRegexes: RegExp[];\n columnWidths: Record<string, string>;\n bgParamName?: string;\n}\n\n/** Profile C — multiple tokens per layout role (Blox prefixed, WPBakery inner columns, …). */\nexport interface ExtendedPrefixedLayoutLevel {\n role: \"section\" | \"row\" | \"column\";\n tokens: string[];\n bgParamName?: string;\n colsParamName?: string;\n widthParamName?: string;\n}\n\nexport interface ExtendedPrefixedLayoutMap {\n kind: \"extended-prefixed\";\n levels: ExtendedPrefixedLayoutLevel[];\n}\n\nexport type StructuralLayoutMap =\n | PrefixedLayoutMap\n | FractionalLayoutMap\n | ExtendedPrefixedLayoutMap;\n\nfunction layoutEscapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction shortcodeOpenRegex(token: string): RegExp {\n return new RegExp(`\\\\[${layoutEscapeRegExp(token)}\\\\b([^\\\\]]*)\\\\]`, \"gi\");\n}\n\nfunction shortcodeCloseRegex(token: string): RegExp {\n return new RegExp(`\\\\[\\\\/${layoutEscapeRegExp(token)}\\\\b[^\\\\]]*\\\\]`, \"gi\");\n}\n\nconst FRACTIONAL_COLUMN_WIDTHS: Record<string, string> = {\n one_col: \"100%\",\n one_half: \"50%\",\n one_third: \"33.33%\",\n two_third: \"66.67%\",\n two_thirds: \"66.67%\",\n one_fourth: \"25%\",\n three_fourth: \"75%\",\n three_fourths: \"75%\",\n};\n\nexport function parseFractionalColumnWidth(token: string): string | undefined {\n return FRACTIONAL_COLUMN_WIDTHS[token];\n}\n\n/** Profile A — section/row/column share a static namespace prefix. */\nexport function prefixedLayoutMap(config: {\n section: string;\n row: string;\n column: string;\n bgParamName?: string;\n colsParamName?: string;\n}): PrefixedLayoutMap {\n return {\n kind: \"prefixed\",\n sectionRegex: shortcodeOpenRegex(config.section),\n sectionCloseRegex: shortcodeCloseRegex(config.section),\n rowRegex: shortcodeOpenRegex(config.row),\n rowCloseRegex: shortcodeCloseRegex(config.row),\n columnRegex: shortcodeOpenRegex(config.column),\n columnCloseRegex: shortcodeCloseRegex(config.column),\n bgParamName: config.bgParamName,\n colsParamName: config.colsParamName,\n };\n}\n\n/** Profile B — legacy Blox/Oshine mathematical column shortcodes. */\n/** Profile C — map several shortcode names per section/row/column role (longest token first). */\nexport function extendedPrefixedLayoutMap(\n levels: ExtendedPrefixedLayoutLevel[],\n): ExtendedPrefixedLayoutMap {\n return { kind: \"extended-prefixed\", levels };\n}\n\nexport function fractionalLayoutMap(config: {\n section: string;\n row: string;\n columns: string[];\n bgParamName?: string;\n}): FractionalLayoutMap {\n const columnWidths: Record<string, string> = {};\n for (const token of config.columns) {\n const width = parseFractionalColumnWidth(token);\n if (width) columnWidths[token] = width;\n }\n\n return {\n kind: \"fractional\",\n sectionRegex: shortcodeOpenRegex(config.section),\n sectionCloseRegex: shortcodeCloseRegex(config.section),\n rowRegex: shortcodeOpenRegex(config.row),\n rowCloseRegex: shortcodeCloseRegex(config.row),\n columnTokens: config.columns,\n columnOpenRegexes: config.columns.map(shortcodeOpenRegex),\n columnCloseRegexes: config.columns.map(shortcodeCloseRegex),\n columnWidths,\n bgParamName: config.bgParamName,\n };\n}\n\n/** Per builder-family registry entry — declarative map executed by the flatten engine. */\nexport interface BuilderThemeConfig {\n id: string;\n detect: RegExp;\n layoutMap?: StructuralLayoutMap;\n /** Additional layout maps applied after `layoutMap` (e.g. Blox prefixed + legacy fractional). */\n layoutMaps?: StructuralLayoutMap[];\n urlRules?: BuilderUrlRule[];\n textRules?: BuilderTextRule[];\n wrapperRules?: BuilderWrapperRule[];\n iconImageRules?: BuilderIconImageRule[];\n placeholderRules?: BuilderPlaceholderRule[];\n scaffoldingPrefixes?: string[];\n legacyScaffoldingTokens?: string[];\n}\n\n/** @deprecated Alias — families not themes. */\nexport type BuilderFamilyConfig = BuilderThemeConfig;\n\n// ---------------------------------------------------------------------------\n// Widget registry (OSS-12 / OSS-16) — cross-builder; not BuilderThemeConfig\n// ---------------------------------------------------------------------------\n\n/** Keeps empty widget stubs from collapsing during cheerio text sweeps. */\nexport const WP_WIDGET_PLACEHOLDER = \"\\u200B\";\n\nexport interface WordPressContactFormWidgetRule {\n /** Shortcode tag name (e.g. `contact-form-7`). */\n tag: string;\n /** Value for `data-wp-form-source`. */\n source: string;\n /** Attribute holding the form id (e.g. `id`). */\n idParam: string;\n}\n\n/** Declarative widget tables consumed by `flattenWordPressWidgets()` in flatten.ts. */\nexport interface WordPressWidgetRegistry {\n mapShortcodePrefixes: readonly string[];\n contactFormRules: readonly WordPressContactFormWidgetRule[];\n videoShortcodePrefixes: readonly string[];\n /** Core / plugin portfolio dynamic shortcode tag. */\n portfolioShortcode: string;\n /** Builder blog roll modules (Tatsu/Oshine `[blog]`, etc.). */\n blogShortcodeTags: readonly string[];\n /** Testimonials carousel/list wrapper shortcodes (Oshine `[testimonials]`, etc.). */\n testimonialsWrapperTags: readonly string[];\n /** Inner testimonial item shortcode tag. */\n testimonialItemTag: string;\n /** In-body RevSlider / MasterSlider shortcodes → slider widget stub (alias only). */\n sliderShortcodeTags: readonly string[];\n /** WordPress core gallery shortcode tag (`ids=` split handled in engine). */\n galleryShortcode: string;\n /** Builder/plugin gallery shortcodes with explicit `ids=` attachment lists (OSS-12). */\n idGalleryShortcodes: readonly string[];\n}\n\nexport const WORDPRESS_WIDGET_REGISTRY: WordPressWidgetRegistry = {\n mapShortcodePrefixes: [\n \"blox_gmap\",\n \"tatsu_gmap\",\n \"tatsu_map\",\n \"et_pb_map\",\n \"vc_gmaps\",\n \"vc_map\",\n ],\n contactFormRules: [\n { tag: \"contact-form-7\", source: \"contact-form-7\", idParam: \"id\" },\n { tag: \"contact_form\", source: \"contact-form-7\", idParam: \"id\" },\n { tag: \"gravityform\", source: \"gravityforms\", idParam: \"id\" },\n { tag: \"ninja_form\", source: \"ninja-forms\", idParam: \"id\" },\n { tag: \"wpforms\", source: \"wpforms\", idParam: \"id\" },\n ],\n videoShortcodePrefixes: [\n \"youtube\",\n \"vimeo\",\n \"embed\",\n \"tatsu_video\",\n \"et_pb_video\",\n \"vc_video\",\n ],\n portfolioShortcode: \"portfolio\",\n blogShortcodeTags: [\"blog\", \"recent_posts\"],\n testimonialsWrapperTags: [\"testimonials\"],\n testimonialItemTag: \"testimonial\",\n sliderShortcodeTags: [\"rev_slider\", \"masterslider\"],\n galleryShortcode: \"gallery\",\n idGalleryShortcodes: [\"oshine_gallery\", \"vc_gallery\", \"nggallery\"],\n};\n\n/** Shortcodes that cannot become static HTML — reported in conflicts, never stripped. */\nexport const UNRESOLVABLE_SHORTCODE_PREFIXES = [\n \"woocommerce_cart\",\n \"woocommerce_checkout\",\n \"woocommerce_my_account\",\n] as const;\n\nexport const WORDPRESS_BUILDER_REGISTRY: BuilderThemeConfig[] = [\n {\n id: \"tatsu\",\n detect: /\\[(?:\\/)?tatsu_/i,\n layoutMap: prefixedLayoutMap({\n section: \"tatsu_section\",\n row: \"tatsu_row\",\n column: \"tatsu_column\",\n bgParamName: \"bg_image\",\n colsParamName: \"layout\",\n }),\n wrapperRules: [\n { shortcodePrefix: \"tatsu_text\" },\n { shortcodePrefix: \"tatsu_inline_text\" },\n { shortcodePrefix: \"tatsu_text_with_shortcodes\" },\n { shortcodePrefix: \"tatsu_icon_group\" },\n ],\n urlRules: [\n { shortcodePrefix: \"tatsu_image\", urlParams: [\"image\", \"url\", \"src\"], tag: \"img\" },\n { shortcodePrefix: \"tatsu_single_image\", urlParams: [\"image\", \"url\", \"src\"], tag: \"img\" },\n ],\n iconImageRules: [\n { shortcodePrefix: \"tatsu_icon\", imageParam: \"icon_image\", hrefParam: \"href\" },\n ],\n scaffoldingPrefixes: [\"tatsu_\"],\n },\n {\n id: \"divi\",\n detect: /\\[(?:\\/)?et_pb_/i,\n layoutMap: prefixedLayoutMap({\n section: \"et_pb_section\",\n row: \"et_pb_row\",\n column: \"et_pb_column\",\n bgParamName: \"background_image\",\n }),\n urlRules: [{ shortcodePrefix: \"et_pb_image\", urlParams: [\"src\", \"url\"], tag: \"img\" }],\n scaffoldingPrefixes: [\"et_pb_\"],\n },\n {\n id: \"wpbakery\",\n detect: /\\[(?:\\/)?vc_/i,\n layoutMap: extendedPrefixedLayoutMap([\n { role: \"section\", tokens: [\"vc_section\"], bgParamName: \"bg_image\" },\n { role: \"row\", tokens: [\"vc_row\"], colsParamName: \"layout\" },\n { role: \"column\", tokens: [\"vc_column_inner\", \"vc_column\"], widthParamName: \"width\" },\n ]),\n urlRules: [\n { shortcodePrefix: \"vc_single_image\", urlParams: [\"image\", \"src\", \"url\"], tag: \"img\" },\n ],\n scaffoldingPrefixes: [\"vc_\"],\n },\n {\n id: \"fusion\",\n detect: /\\[(?:\\/)?fusion_/i,\n layoutMap: prefixedLayoutMap({\n section: \"fusion_builder_container\",\n row: \"fusion_builder_row\",\n column: \"fusion_builder_column\",\n bgParamName: \"background_image\",\n }),\n scaffoldingPrefixes: [\"fusion_\"],\n },\n {\n id: \"beaver\",\n detect: /\\[(?:\\/)?fl_(?:row|col|builder)/i,\n layoutMap: extendedPrefixedLayoutMap([\n { role: \"row\", tokens: [\"fl_row\"] },\n { role: \"column\", tokens: [\"fl_col\"] },\n ]),\n scaffoldingPrefixes: [\"fl_\"],\n },\n {\n id: \"elementor\",\n detect: /\\[(?:\\/)?elementor[-_]/i,\n urlRules: [\n { shortcodePrefix: \"elementor-widget\", urlParams: [\"url\", \"src\", \"image\"], tag: \"img\" },\n ],\n scaffoldingPrefixes: [\"elementor_\"],\n },\n {\n id: \"oshine\",\n detect:\n /\\[(?:special_sub_title|special_heading5|blox_\\w+|grid_content|grids|testimonial\\b|portfolio\\b|recent_posts\\b|animate_icon\\w*|section\\b|row\\b|one_col|one_third|one_half|one_fourth|two_third|three_fourth|text\\b)/i,\n layoutMap: fractionalLayoutMap({\n section: \"section\",\n row: \"row\",\n columns: [\n \"one_col\",\n \"one_third\",\n \"two_third\",\n \"two_thirds\",\n \"one_half\",\n \"one_fourth\",\n \"three_fourth\",\n \"three_fourths\",\n ],\n bgParamName: \"bg_image\",\n }),\n layoutMaps: [\n extendedPrefixedLayoutMap([\n { role: \"section\", tokens: [\"blox_row\"], bgParamName: \"bg_image\" },\n { role: \"row\", tokens: [\"blox_row_inner\"], colsParamName: \"columns\" },\n {\n role: \"column\",\n tokens: [\"blox_column_inner\", \"blox_column\"],\n widthParamName: \"width\",\n },\n ]),\n ],\n textRules: [\n {\n shortcodePrefix: \"special_sub_title\",\n fields: [{ param: \"title_content\", tag: \"p\" }],\n },\n {\n shortcodePrefix: \"special_heading5\",\n fields: [\n { param: \"title_content\", tag: \"h2\" },\n { param: \"caption_content\", tag: \"h4\" },\n ],\n },\n ],\n wrapperRules: [\n { shortcodePrefix: \"grid_content\" },\n { shortcodePrefix: \"blox_text\" },\n ],\n urlRules: [\n { shortcodePrefix: \"blox_image\", urlParams: [\"image\", \"img\", \"src\", \"url\"], tag: \"img\" },\n ],\n scaffoldingPrefixes: [\"blox_\", \"animate_icon\"],\n legacyScaffoldingTokens: [\"text\", \"icon\", \"linebreak\", \"grids\", \"testimonials\"],\n },\n];\n\n/** @deprecated Use urlRules on BuilderThemeConfig — kept for type migration clarity. */\nexport type BuilderContentRule = BuilderUrlRule;\n"],"mappings":"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","names":[]}
@@ -99,4 +99,4 @@ export {
99
99
  bundleCounts,
100
100
  buildPortfolioMediaLinks
101
101
  };
102
- //# sourceMappingURL=chunk-LC7CGWDN.js.map
102
+ //# sourceMappingURL=chunk-PD5AZX3B.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/normalizer/types.ts","../src/normalizer/idempotency.ts","../src/normalizer/bundle.ts","../src/normalizer/portfolio-media.ts"],"sourcesContent":["export type MigrationPlatform = \"wordpress\" | \"smugmug\" | \"squarespace\" | \"wix\";\n\nexport type EntityType = \"post\" | \"page\" | \"asset\" | \"portfolio\" | \"category\" | \"tag\";\n\nexport type PublishStatus = \"draft\" | \"published\" | \"archived\";\n\nexport interface SourceMetadata {\n platform: MigrationPlatform;\n id: string;\n url?: string;\n path?: string;\n exportedAt?: string;\n /** WordPress `post_type` when the DTO shape differs (e.g. portfolio CPT emitted as `page`). */\n postType?: string;\n}\n\n/** Canonical post DTO — raw HTML; sanitize at host sink. */\nexport interface NormalizedPost {\n type: \"post\";\n source: SourceMetadata;\n sourceId: string;\n title: string;\n slug: string;\n excerpt?: string;\n contentHtml: string;\n publishedAt?: string;\n status: PublishStatus;\n categorySlugs?: string[];\n tagSlugs?: string[];\n /** WordPress attachment id before two-pass resolution. */\n sourceFeaturedMediaId?: string;\n featuredAssetSourceId?: string;\n seoTitle?: string;\n seoDescription?: string;\n}\n\n/** RevSlider / MasterSlider reference from theme hero meta (OSS-27) — alias only, no slide payloads in WXR. */\nexport interface PageHeroSliderHint {\n plugin: \"revslider\" | \"masterslider\";\n alias: string;\n slidertitle?: string;\n source?: \"meta-shortcode\" | \"meta-slider-field\" | \"tatsu-json\";\n}\n\n/** Non-body layout signals for host promotion (theme chrome, hero slots, …). */\nexport interface PageLayoutHints {\n heroSlider?: PageHeroSliderHint;\n}\n\n/** Canonical page DTO — raw HTML snapshot. */\nexport interface NormalizedPage {\n type: \"page\";\n source: SourceMetadata;\n sourceId: string;\n title: string;\n slug: string;\n contentHtml: string;\n contentCss?: string;\n isHomePage?: boolean;\n /** Site portfolio listing shell (distinct from portfolio CPT singles). */\n isPortfolioPage?: boolean;\n layoutHints?: PageLayoutHints;\n status: PublishStatus;\n seoTitle?: string;\n seoDescription?: string;\n}\n\n/** EXIF fields preserved from SmugMug / camera metadata when present. */\nexport interface NormalizedAssetExif {\n iso?: number;\n aperture?: number;\n shutter?: string;\n focalLength?: number;\n}\n\n/** Remote asset to stream into the host sink. */\nexport interface NormalizedAsset {\n type: \"asset\";\n source: SourceMetadata;\n sourceId: string;\n sourceUrl: string;\n filename: string;\n mimeType?: string;\n caption?: string;\n altText?: string;\n keywords?: string[];\n exif?: NormalizedAssetExif;\n portfolioSourceId?: string;\n sort?: number;\n}\n\n/** M2M index: portfolio ↔ asset membership and sort order. */\nexport interface PortfolioMediaLink {\n portfolioSourceId: string;\n assetSourceId: string;\n sort: number;\n}\n\nexport interface NormalizedPortfolio {\n type: \"portfolio\";\n source: SourceMetadata;\n sourceId: string;\n title: string;\n slug: string;\n description?: string;\n parentSourceId?: string;\n}\n\nexport interface NormalizedCategory {\n type: \"category\";\n source: SourceMetadata;\n sourceId: string;\n name: string;\n slug: string;\n}\n\nexport interface NormalizedTag {\n type: \"tag\";\n source: SourceMetadata;\n sourceId: string;\n name: string;\n slug: string;\n}\n\nexport type NormalizedEntity =\n | NormalizedPost\n | NormalizedPage\n | NormalizedAsset\n | NormalizedPortfolio\n | NormalizedCategory\n | NormalizedTag;\n\nexport interface ValidationIssue {\n code: string;\n message: string;\n path?: string;\n}\n\nexport interface ValidationResult {\n ok: boolean;\n issues: ValidationIssue[];\n summary?: {\n posts?: number;\n pages?: number;\n assets?: number;\n portfolios?: number;\n /** WordPress `post_type=portfolio` (and configured CPT slugs) in raw WXR. */\n portfolioCpt?: number;\n categories?: number;\n tags?: number;\n /** WXR rows the parser would emit (OSS-19). */\n importableItemCount?: number;\n unsupportedOnly?: boolean;\n skippedPostTypes?: Record<string, number>;\n };\n}\n\nexport interface WxrImportSummary {\n importableItemCount: number;\n unsupportedOnly: boolean;\n skippedPostTypes: Record<string, number>;\n skippedWooCommerceStubPages?: number;\n}\n\nexport interface AdapterContext {\n input: unknown;\n cursor?: MigrationCursor;\n}\n\nexport interface MigrationAdapter {\n platform: MigrationPlatform;\n validateInput(input: unknown): ValidationResult | Promise<ValidationResult>;\n enumerateEntities(ctx: AdapterContext): AsyncIterable<NormalizedEntity>;\n /** Platform-specific import accounting (e.g. WordPress skipped `post_type`s). */\n getImportSummary?(input: unknown): Promise<WxrImportSummary | undefined>;\n}\n\nexport interface MigrationCursor {\n lastEntityKey?: EntityKey;\n state?: Record<string, unknown>;\n}\n\nexport interface EntityKey {\n platform: MigrationPlatform;\n entityType: EntityType;\n sourceId: string;\n}\n\nexport function entityKey(entity: NormalizedEntity, platform: MigrationPlatform): EntityKey {\n return {\n platform,\n entityType: entity.type,\n sourceId: entity.sourceId,\n };\n}\n","import type { EntityKey, MigrationCursor } from \"./types.js\";\n\n/** Portable entity state for resume / idempotency (not Directus field names). */\nexport type EntityState = \"pending\" | \"done\" | \"failed\" | \"skipped\";\n\nexport interface TrackedEntity extends EntityKey {\n state: EntityState;\n targetId?: string;\n errorMessage?: string;\n}\n\nexport interface MigrationCheckpoint {\n jobId: string;\n cursor: MigrationCursor;\n entities: TrackedEntity[];\n updatedAt: string;\n}\n\nexport function isTerminalState(state: EntityState): boolean {\n return state === \"done\" || state === \"skipped\";\n}\n\nexport function shouldProcessEntity(\n key: EntityKey,\n entities: TrackedEntity[],\n): boolean {\n const existing = entities.find(\n (e) =>\n e.platform === key.platform &&\n e.entityType === key.entityType &&\n e.sourceId === key.sourceId,\n );\n return !existing || !isTerminalState(existing.state);\n}\n","import type {\n NormalizedAsset,\n NormalizedCategory,\n NormalizedEntity,\n NormalizedPage,\n NormalizedPortfolio,\n NormalizedPost,\n NormalizedTag,\n} from \"./types.js\";\n\nexport interface EntityBundle {\n posts: NormalizedPost[];\n pages: NormalizedPage[];\n media: NormalizedAsset[];\n portfolios: NormalizedPortfolio[];\n categories: NormalizedCategory[];\n tags: NormalizedTag[];\n}\n\nexport function emptyBundle(): EntityBundle {\n return {\n posts: [],\n pages: [],\n media: [],\n portfolios: [],\n categories: [],\n tags: [],\n };\n}\n\nexport async function collectEntities(\n entities: AsyncIterable<NormalizedEntity>,\n): Promise<EntityBundle> {\n const bundle = emptyBundle();\n\n for await (const entity of entities) {\n switch (entity.type) {\n case \"post\":\n bundle.posts.push(entity);\n break;\n case \"page\":\n bundle.pages.push(entity);\n break;\n case \"asset\":\n bundle.media.push(entity);\n break;\n case \"portfolio\":\n bundle.portfolios.push(entity);\n break;\n case \"category\":\n bundle.categories.push(entity);\n break;\n case \"tag\":\n bundle.tags.push(entity);\n break;\n default: {\n const _exhaustive: never = entity;\n throw new Error(`Unknown entity type: ${(_exhaustive as NormalizedEntity).type}`);\n }\n }\n }\n\n return bundle;\n}\n\nexport interface BundleCounts {\n posts: number;\n pages: number;\n assets: number;\n portfolios: number;\n categories: number;\n tags: number;\n}\n\nexport function bundleCounts(bundle: EntityBundle): BundleCounts {\n return {\n posts: bundle.posts.length,\n pages: bundle.pages.length,\n assets: bundle.media.length,\n portfolios: bundle.portfolios.length,\n categories: bundle.categories.length,\n tags: bundle.tags.length,\n };\n}\n","import type { EntityBundle } from \"./bundle.js\";\nimport type { PortfolioMediaLink } from \"./types.js\";\n\n/** Derive portfolio↔asset M2M rows from assets carrying `portfolioSourceId`. */\nexport function buildPortfolioMediaLinks(bundle: EntityBundle): PortfolioMediaLink[] {\n const links: PortfolioMediaLink[] = [];\n\n for (const asset of bundle.media) {\n if (!asset.portfolioSourceId) continue;\n links.push({\n portfolioSourceId: asset.portfolioSourceId,\n assetSourceId: asset.sourceId,\n sort: asset.sort ?? 0,\n });\n }\n\n links.sort((a, b) => {\n if (a.portfolioSourceId !== b.portfolioSourceId) {\n return a.portfolioSourceId.localeCompare(b.portfolioSourceId);\n }\n return a.sort - b.sort || a.assetSourceId.localeCompare(b.assetSourceId);\n });\n\n return links;\n}\n"],"mappings":";AA4LO,SAAS,UAAU,QAA0B,UAAwC;AAC1F,SAAO;AAAA,IACL;AAAA,IACA,YAAY,OAAO;AAAA,IACnB,UAAU,OAAO;AAAA,EACnB;AACF;;;AChLO,SAAS,gBAAgB,OAA6B;AAC3D,SAAO,UAAU,UAAU,UAAU;AACvC;AAEO,SAAS,oBACd,KACA,UACS;AACT,QAAM,WAAW,SAAS;AAAA,IACxB,CAAC,MACC,EAAE,aAAa,IAAI,YACnB,EAAE,eAAe,IAAI,cACrB,EAAE,aAAa,IAAI;AAAA,EACvB;AACA,SAAO,CAAC,YAAY,CAAC,gBAAgB,SAAS,KAAK;AACrD;;;ACdO,SAAS,cAA4B;AAC1C,SAAO;AAAA,IACL,OAAO,CAAC;AAAA,IACR,OAAO,CAAC;AAAA,IACR,OAAO,CAAC;AAAA,IACR,YAAY,CAAC;AAAA,IACb,YAAY,CAAC;AAAA,IACb,MAAM,CAAC;AAAA,EACT;AACF;AAEA,eAAsB,gBACpB,UACuB;AACvB,QAAM,SAAS,YAAY;AAE3B,mBAAiB,UAAU,UAAU;AACnC,YAAQ,OAAO,MAAM;AAAA,MACnB,KAAK;AACH,eAAO,MAAM,KAAK,MAAM;AACxB;AAAA,MACF,KAAK;AACH,eAAO,MAAM,KAAK,MAAM;AACxB;AAAA,MACF,KAAK;AACH,eAAO,MAAM,KAAK,MAAM;AACxB;AAAA,MACF,KAAK;AACH,eAAO,WAAW,KAAK,MAAM;AAC7B;AAAA,MACF,KAAK;AACH,eAAO,WAAW,KAAK,MAAM;AAC7B;AAAA,MACF,KAAK;AACH,eAAO,KAAK,KAAK,MAAM;AACvB;AAAA,MACF,SAAS;AACP,cAAM,cAAqB;AAC3B,cAAM,IAAI,MAAM,wBAAyB,YAAiC,IAAI,EAAE;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAWO,SAAS,aAAa,QAAoC;AAC/D,SAAO;AAAA,IACL,OAAO,OAAO,MAAM;AAAA,IACpB,OAAO,OAAO,MAAM;AAAA,IACpB,QAAQ,OAAO,MAAM;AAAA,IACrB,YAAY,OAAO,WAAW;AAAA,IAC9B,YAAY,OAAO,WAAW;AAAA,IAC9B,MAAM,OAAO,KAAK;AAAA,EACpB;AACF;;;AC/EO,SAAS,yBAAyB,QAA4C;AACnF,QAAM,QAA8B,CAAC;AAErC,aAAW,SAAS,OAAO,OAAO;AAChC,QAAI,CAAC,MAAM,kBAAmB;AAC9B,UAAM,KAAK;AAAA,MACT,mBAAmB,MAAM;AAAA,MACzB,eAAe,MAAM;AAAA,MACrB,MAAM,MAAM,QAAQ;AAAA,IACtB,CAAC;AAAA,EACH;AAEA,QAAM,KAAK,CAAC,GAAG,MAAM;AACnB,QAAI,EAAE,sBAAsB,EAAE,mBAAmB;AAC/C,aAAO,EAAE,kBAAkB,cAAc,EAAE,iBAAiB;AAAA,IAC9D;AACA,WAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,cAAc,EAAE,aAAa;AAAA,EACzE,CAAC;AAED,SAAO;AACT;","names":[]}
@@ -1,3 +1,6 @@
1
+ import {
2
+ normalizeVideoEmbedUrl
3
+ } from "./chunk-4XZK55RW.js";
1
4
  import {
2
5
  isMigrationMediaRef,
3
6
  parseMigrationMediaRef
@@ -131,6 +134,42 @@ function isPreservedEmbedIframe(tagName, attributes) {
131
134
  const src = attributes?.src ?? "";
132
135
  return EMBED_IFRAME_SRC.test(src);
133
136
  }
137
+ function isBlockLinkedImageAnchor($, $el) {
138
+ if (tagNameOf($el) !== "a") return false;
139
+ const href = $el.attr("href")?.trim();
140
+ if (!href || href === "#") return false;
141
+ let tagChildCount = 0;
142
+ let onlyImg = false;
143
+ $el.contents().each((_, node) => {
144
+ if (node.type === "text") {
145
+ if (String("data" in node ? node.data : "").trim()) {
146
+ tagChildCount = -1;
147
+ }
148
+ return;
149
+ }
150
+ if (node.type !== "tag") return;
151
+ tagChildCount += 1;
152
+ onlyImg = tagNameOf($(node)) === "img";
153
+ });
154
+ return tagChildCount === 1 && onlyImg;
155
+ }
156
+ function walkLinkedImageAnchor($, $el, options) {
157
+ const href = $el.attr("href").trim();
158
+ const $img = $el.children().first();
159
+ const meta = pickElementMeta($img);
160
+ const attributes = { ...meta.attributes ?? {}, href };
161
+ return applyElementMeta(
162
+ {
163
+ type: resolveComponentType("img", meta.classes, options),
164
+ tagName: "img",
165
+ void: true
166
+ },
167
+ {
168
+ attributes,
169
+ classes: meta.classes
170
+ }
171
+ );
172
+ }
134
173
  function layoutAttributesForComponent(attributes) {
135
174
  if (!attributes) return void 0;
136
175
  const { [LAYOUT_DATA_ATTR]: _layout, ...rest } = attributes;
@@ -234,13 +273,23 @@ function walkNode($, $el, options) {
234
273
  return component2;
235
274
  }
236
275
  if (isWpWidgetMarker(meta.attributes)) {
237
- return applyElementMeta(
276
+ const component2 = applyElementMeta(
238
277
  {
239
278
  type: resolveWidgetComponentType(options),
240
279
  tagName
241
280
  },
242
281
  meta
243
282
  );
283
+ if (meta.attributes?.[WP_WIDGET_ATTR]?.trim() === "testimonials") {
284
+ const children = walkChildren($, $el, options).filter((child) => {
285
+ if (child.type !== "textnode") return true;
286
+ return Boolean(child.content?.trim());
287
+ });
288
+ if (children.length > 0) {
289
+ component2.components = children;
290
+ }
291
+ }
292
+ return component2;
244
293
  }
245
294
  if (isPreservedEmbedIframe(tagName, meta.attributes)) {
246
295
  return applyElementMeta(
@@ -252,6 +301,9 @@ function walkNode($, $el, options) {
252
301
  meta
253
302
  );
254
303
  }
304
+ if (tagName === "a" && isBlockLinkedImageAnchor($, $el)) {
305
+ return walkLinkedImageAnchor($, $el, options);
306
+ }
255
307
  if (VOID_TAGS.has(tagName)) {
256
308
  return applyElementMeta(
257
309
  {
@@ -359,6 +411,45 @@ function serializeContentHtml($) {
359
411
  // src/transformers/html-to-tiptap/index.ts
360
412
  import * as cheerio2 from "cheerio";
361
413
 
414
+ // src/transformers/html-to-tiptap/video-embed.ts
415
+ var VIDEO_WIDGET = "video";
416
+ var VIDEO_IFRAME_SRC = /youtube\.com\/embed|youtube-nocookie\.com\/embed|youtu\.be|player\.vimeo\.com\/video/i;
417
+ function isVideoWidgetElement($el) {
418
+ return $el.attr("data-wp-widget")?.toLowerCase() === VIDEO_WIDGET;
419
+ }
420
+ function buildVideoEmbedNode(input) {
421
+ return {
422
+ type: "embed",
423
+ attrs: {
424
+ src: input.embedUrl,
425
+ provider: input.provider,
426
+ dataWpWidget: VIDEO_WIDGET,
427
+ dataEmbedUrl: input.embedUrl,
428
+ dataVideoProvider: input.provider
429
+ }
430
+ };
431
+ }
432
+ function videoEmbedNodeFromWidget($el) {
433
+ const embedUrl = $el.attr("data-embed-url")?.trim();
434
+ if (!embedUrl) return null;
435
+ const provider = $el.attr("data-video-provider")?.trim() || "external";
436
+ return buildVideoEmbedNode({ embedUrl, provider });
437
+ }
438
+ function videoEmbedNodeFromIframe($el) {
439
+ const src = $el.attr("src")?.trim();
440
+ if (!src || !VIDEO_IFRAME_SRC.test(src)) return null;
441
+ const normalized = normalizeVideoEmbedUrl(src);
442
+ return buildVideoEmbedNode({
443
+ embedUrl: normalized?.embedUrl ?? src,
444
+ provider: normalized?.provider ?? "external"
445
+ });
446
+ }
447
+ function isPreservedVideoIframe($el, tagName) {
448
+ if (tagName !== "iframe") return false;
449
+ const src = $el.attr("src")?.trim() ?? "";
450
+ return VIDEO_IFRAME_SRC.test(src);
451
+ }
452
+
362
453
  // src/transformers/html-to-tiptap/walk.ts
363
454
  var SKIP_TAGS2 = /* @__PURE__ */ new Set(["script", "style", "noscript", "template"]);
364
455
  var HEADING_TAGS = /* @__PURE__ */ new Set(["h1", "h2", "h3", "h4", "h5", "h6"]);
@@ -434,7 +525,7 @@ function hasBlockChild($, $el) {
434
525
  if ($child.get(0)?.type === "text") return;
435
526
  const childTag = tagNameOf2($child);
436
527
  if (!childTag) return;
437
- if (HEADING_TAGS.has(childTag) || childTag === "p" || childTag === "ul" || childTag === "ol" || childTag === "blockquote" || childTag === "pre" || childTag === "hr" || childTag === "img" || childTag === "table" || isLayoutMarker($child, { unwrapLayoutMarkers: true }) || UNWRAP_TAGS.has(childTag) && hasBlockChild($, $child)) {
528
+ if (HEADING_TAGS.has(childTag) || childTag === "p" || childTag === "ul" || childTag === "ol" || childTag === "blockquote" || childTag === "pre" || childTag === "hr" || childTag === "img" || childTag === "iframe" || childTag === "table" || isLayoutMarker($child, { unwrapLayoutMarkers: true }) || UNWRAP_TAGS.has(childTag) && hasBlockChild($, $child)) {
438
529
  hasBlock = true;
439
530
  }
440
531
  });
@@ -549,6 +640,12 @@ function parseMixedBlockContent($, $el, options) {
549
640
  if (image) blocks.push(image);
550
641
  return;
551
642
  }
643
+ if (tagName === "iframe" && isPreservedVideoIframe($child, tagName)) {
644
+ flushInline();
645
+ const embed = videoEmbedNodeFromIframe($child);
646
+ if (embed) blocks.push(embed);
647
+ return;
648
+ }
552
649
  if (INLINE_TAGS2.has(tagName)) {
553
650
  inlineBuffer.push(...parseInlineContent($, $child));
554
651
  return;
@@ -566,7 +663,7 @@ function walkListItem($, $el, options) {
566
663
  }
567
664
  const normalized = [];
568
665
  for (const block of blocks) {
569
- if (block.type === "paragraph" || block.type === "image" || block.type === "blockquote" || block.type === "bulletList" || block.type === "orderedList") {
666
+ if (block.type === "paragraph" || block.type === "image" || block.type === "embed" || block.type === "blockquote" || block.type === "bulletList" || block.type === "orderedList") {
570
667
  normalized.push(block);
571
668
  continue;
572
669
  }
@@ -587,6 +684,14 @@ function walkBlockNode($, $el, options) {
587
684
  if (isLayoutMarker($el, options)) {
588
685
  return walkBlockNodes($, $el, options);
589
686
  }
687
+ if (isVideoWidgetElement($el)) {
688
+ const embed = videoEmbedNodeFromWidget($el);
689
+ return embed ? [embed] : [];
690
+ }
691
+ if (isPreservedVideoIframe($el, tagName)) {
692
+ const embed = videoEmbedNodeFromIframe($el);
693
+ return embed ? [embed] : [];
694
+ }
590
695
  if (UNWRAP_TAGS.has(tagName)) {
591
696
  if (hasBlockChild($, $el)) {
592
697
  return walkBlockNodes($, $el, options);
@@ -961,4 +1066,4 @@ export {
961
1066
  validateTiptapDoc,
962
1067
  expandMigrationMediaRefs
963
1068
  };
964
- //# sourceMappingURL=chunk-S4SUJT2D.js.map
1069
+ //# sourceMappingURL=chunk-VQ5HKNYP.js.map