@artinstack/migrator 0.1.3 → 0.1.5

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/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  squarespaceAdapter,
16
16
  wixAdapter,
17
17
  wordpressAdapter
18
- } from "./chunk-QEXTXHFG.js";
18
+ } from "./chunk-XUBCG3IA.js";
19
19
  import {
20
20
  normalizedAssetExifSchema,
21
21
  normalizedAssetSchema,
@@ -53,13 +53,12 @@ import {
53
53
  hasWarnings,
54
54
  mapJsonPrettyWire,
55
55
  portfolioMediaMatchesBundle,
56
- rewriteInlineImages,
57
56
  runDryRun,
58
57
  runMigration,
59
58
  runMigrationFromBundle,
60
59
  staleUrlsFromEstimate,
61
60
  writeFilesystemExport
62
- } from "./chunk-HH7666MQ.js";
61
+ } from "./chunk-YLFVYPB3.js";
63
62
  import {
64
63
  buildPortfolioMediaLinks,
65
64
  bundleCounts,
@@ -69,6 +68,22 @@ import {
69
68
  isTerminalState,
70
69
  shouldProcessEntity
71
70
  } from "./chunk-HI7JHWZU.js";
71
+ import {
72
+ cssToStyles,
73
+ grapesComponentSchema,
74
+ grapesProjectSnapshotSchema,
75
+ grapesStyleRuleSchema,
76
+ htmlToGrapes,
77
+ htmlToTiptap,
78
+ tiptapDocSchema,
79
+ tiptapMarkSchema,
80
+ tiptapNodeSchema,
81
+ validateGrapesProjectSnapshot,
82
+ validateTiptapDoc
83
+ } from "./chunk-CIOYDRY5.js";
84
+ import {
85
+ rewriteInlineImages
86
+ } from "./chunk-S7TRWILI.js";
72
87
  import {
73
88
  discoverContentAssetUrls,
74
89
  discoverRawImgSrcs,
@@ -76,354 +91,6 @@ import {
76
91
  isLikelyImageUrl,
77
92
  normalizeAssetUrl
78
93
  } from "./chunk-2PNSVE5Y.js";
79
-
80
- // src/transformers/html-to-grapes/index.ts
81
- import * as cheerio from "cheerio";
82
-
83
- // src/transformers/css-to-styles/index.ts
84
- function stripCssComments(css) {
85
- return css.replace(/\/\*[\s\S]*?\*\//g, "");
86
- }
87
- function parseDeclarations(block) {
88
- const style = {};
89
- for (const declaration of block.split(";")) {
90
- const trimmed = declaration.trim();
91
- if (!trimmed) continue;
92
- const separator = trimmed.indexOf(":");
93
- if (separator === -1) continue;
94
- const property = trimmed.slice(0, separator).trim();
95
- const value = trimmed.slice(separator + 1).trim();
96
- if (!property || !value) continue;
97
- style[property] = value;
98
- }
99
- return style;
100
- }
101
- function cssToStyles(css) {
102
- const cleaned = stripCssComments(css);
103
- const rules = [];
104
- const rulePattern = /([^{]+)\{([^}]*)\}/g;
105
- for (const match of cleaned.matchAll(rulePattern)) {
106
- const selectorText = match[1]?.trim() ?? "";
107
- const declarationBlock = match[2] ?? "";
108
- if (!selectorText || selectorText.startsWith("@")) continue;
109
- const style = parseDeclarations(declarationBlock);
110
- if (Object.keys(style).length === 0) continue;
111
- const selectors = selectorText.split(",").map((selector) => selector.trim()).filter(Boolean);
112
- if (selectors.length === 0) continue;
113
- rules.push({ selectors, style });
114
- }
115
- return rules;
116
- }
117
-
118
- // src/transformers/html-to-grapes/walk.ts
119
- var INLINE_TAGS = /* @__PURE__ */ new Set([
120
- "a",
121
- "abbr",
122
- "b",
123
- "br",
124
- "cite",
125
- "code",
126
- "del",
127
- "em",
128
- "i",
129
- "img",
130
- "ins",
131
- "mark",
132
- "q",
133
- "s",
134
- "small",
135
- "span",
136
- "strong",
137
- "sub",
138
- "sup",
139
- "u",
140
- "wbr"
141
- ]);
142
- var VOID_TAGS = /* @__PURE__ */ new Set([
143
- "area",
144
- "base",
145
- "br",
146
- "col",
147
- "embed",
148
- "hr",
149
- "img",
150
- "input",
151
- "link",
152
- "meta",
153
- "param",
154
- "source",
155
- "track",
156
- "wbr"
157
- ]);
158
- var TEXT_CONTAINER_TAGS = /* @__PURE__ */ new Set([
159
- "blockquote",
160
- "figcaption",
161
- "h1",
162
- "h2",
163
- "h3",
164
- "h4",
165
- "h5",
166
- "h6",
167
- "label",
168
- "li",
169
- "p",
170
- "pre",
171
- "td",
172
- "th"
173
- ]);
174
- var SKIP_TAGS = /* @__PURE__ */ new Set(["script", "style", "noscript", "template"]);
175
- var DEFAULT_TYPES = {
176
- a: "link",
177
- img: "image"
178
- };
179
- function tagNameOf($el) {
180
- const raw = $el.prop("tagName");
181
- return typeof raw === "string" ? raw.toLowerCase() : void 0;
182
- }
183
- function applyElementMeta(component, meta) {
184
- if (meta.attributes) component.attributes = meta.attributes;
185
- if (meta.classes) component.classes = meta.classes;
186
- return component;
187
- }
188
- function pickElementMeta($el) {
189
- const attributes = {};
190
- const classes = [];
191
- if (typeof $el.attr() === "object") {
192
- for (const [key, value] of Object.entries($el.attr() ?? {})) {
193
- if (key === "class") {
194
- classes.push(...value.split(/\s+/).filter(Boolean));
195
- continue;
196
- }
197
- attributes[key] = value;
198
- }
199
- }
200
- return {
201
- attributes: Object.keys(attributes).length > 0 ? attributes : void 0,
202
- classes: classes.length > 0 ? classes : void 0
203
- };
204
- }
205
- function resolveComponentType(tagName, classes, options) {
206
- if (options.componentMap && classes) {
207
- for (const className of classes) {
208
- const mapped = options.componentMap[className];
209
- if (mapped) return mapped;
210
- }
211
- }
212
- return DEFAULT_TYPES[tagName] ?? "default";
213
- }
214
- function hasOnlyInlineContent($, $el) {
215
- let inlineOnly = true;
216
- $el.contents().each((_, node) => {
217
- if (!inlineOnly) return;
218
- const $child = $(node);
219
- if ($child.get(0)?.type === "text") return;
220
- const childTag = tagNameOf($child);
221
- if (!childTag || !INLINE_TAGS.has(childTag)) {
222
- inlineOnly = false;
223
- return;
224
- }
225
- if (!hasOnlyInlineContent($, $child)) {
226
- inlineOnly = false;
227
- }
228
- });
229
- return inlineOnly;
230
- }
231
- function walkChildren($, $el, options) {
232
- const components = [];
233
- $el.contents().each((_, node) => {
234
- const walked = walkNode($, $(node), options);
235
- if (!walked) return;
236
- if (Array.isArray(walked)) {
237
- components.push(...walked);
238
- } else {
239
- components.push(walked);
240
- }
241
- });
242
- return components;
243
- }
244
- function walkNode($, $el, options) {
245
- const node = $el.get(0);
246
- if (!node) return null;
247
- if (node.type === "text") {
248
- const text = "data" in node ? String(node.data ?? "") : "";
249
- if (!text.trim()) return null;
250
- return { type: "textnode", content: text };
251
- }
252
- if (node.type !== "tag") return null;
253
- const tagName = tagNameOf($el);
254
- if (!tagName || SKIP_TAGS.has(tagName)) return null;
255
- const meta = pickElementMeta($el);
256
- if (VOID_TAGS.has(tagName)) {
257
- return applyElementMeta(
258
- {
259
- type: resolveComponentType(tagName, meta.classes, options),
260
- tagName,
261
- void: true
262
- },
263
- meta
264
- );
265
- }
266
- if (TEXT_CONTAINER_TAGS.has(tagName) && hasOnlyInlineContent($, $el)) {
267
- return applyElementMeta(
268
- {
269
- type: "text",
270
- tagName,
271
- content: $el.html() ?? ""
272
- },
273
- meta
274
- );
275
- }
276
- if (INLINE_TAGS.has(tagName)) {
277
- return applyElementMeta(
278
- {
279
- type: "text",
280
- content: $.html($el) ?? ""
281
- },
282
- meta
283
- );
284
- }
285
- const components = walkChildren($, $el, options);
286
- const component = applyElementMeta(
287
- {
288
- type: resolveComponentType(tagName, meta.classes, options),
289
- tagName
290
- },
291
- meta
292
- );
293
- if (components.length > 0) {
294
- component.components = components;
295
- }
296
- return component;
297
- }
298
- function appendWalked(content, walked) {
299
- if (!walked) return;
300
- if (Array.isArray(walked)) {
301
- content.push(...walked);
302
- return;
303
- }
304
- content.push(walked);
305
- }
306
- function walkNodes($, $nodes, content, options) {
307
- $nodes.each((_, node) => {
308
- appendWalked(content, walkNode($, $(node), options));
309
- });
310
- }
311
- function walkHtmlToComponents($, options = {}) {
312
- const content = [];
313
- const body = $("body");
314
- if (body.length) {
315
- walkNodes($, body.contents(), content, options);
316
- return content;
317
- }
318
- const children = $.root().children();
319
- if (children.length) {
320
- walkNodes($, children, content, options);
321
- } else {
322
- walkNodes($, $.root().contents(), content, options);
323
- }
324
- return content;
325
- }
326
-
327
- // src/transformers/html-to-grapes/index.ts
328
- function htmlToGrapes(html, options = {}) {
329
- const trimmed = html.trim();
330
- if (!trimmed) {
331
- return { content: [], styles: [] };
332
- }
333
- const $ = cheerio.load(trimmed, { xml: false });
334
- const styleBlocks = [];
335
- $("style").each((_, element) => {
336
- styleBlocks.push($(element).html() ?? "");
337
- $(element).remove();
338
- });
339
- const contentCss = styleBlocks.join("\n").trim();
340
- const styles = cssToStyles(contentCss);
341
- const content = walkHtmlToComponents($, options);
342
- const contentHtml = serializeContentHtml($);
343
- return {
344
- content,
345
- styles,
346
- ...contentHtml ? { contentHtml } : {},
347
- ...contentCss ? { contentCss } : {}
348
- };
349
- }
350
- function serializeContentHtml($) {
351
- const body = $("body");
352
- if (body.length) {
353
- const html = body.html()?.trim();
354
- return html || void 0;
355
- }
356
- const rootHtml = $.root().html()?.trim();
357
- return rootHtml || void 0;
358
- }
359
-
360
- // src/transformers/validate-snapshot.ts
361
- import { z } from "zod";
362
- var grapesStyleRuleSchema = z.object({
363
- selectors: z.array(z.string().min(1)).min(1),
364
- style: z.record(z.string(), z.string())
365
- });
366
- var grapesComponentSchema = z.lazy(
367
- () => z.object({
368
- type: z.string().min(1),
369
- tagName: z.string().optional(),
370
- attributes: z.record(z.string(), z.string()).optional(),
371
- classes: z.array(z.string()).optional(),
372
- components: z.array(grapesComponentSchema).optional(),
373
- content: z.string().optional(),
374
- void: z.boolean().optional()
375
- })
376
- );
377
- var grapesProjectSnapshotSchema = z.object({
378
- content: z.array(grapesComponentSchema),
379
- styles: z.array(grapesStyleRuleSchema),
380
- contentHtml: z.string().optional(),
381
- contentCss: z.string().optional()
382
- });
383
- function zodIssuesToValidationIssues(issues) {
384
- return issues.map((issue) => ({
385
- code: issue.code,
386
- message: issue.message,
387
- path: issue.path.length > 0 ? issue.path.join(".") : void 0
388
- }));
389
- }
390
- function collectComponentTypes(components) {
391
- const types = [];
392
- for (const component of components) {
393
- types.push(component.type);
394
- if (component.components?.length) {
395
- types.push(...collectComponentTypes(component.components));
396
- }
397
- }
398
- return types;
399
- }
400
- function validateAllowedComponentTypes(snapshot, allowedComponentTypes) {
401
- const allowlist = new Set(allowedComponentTypes);
402
- const issues = [];
403
- for (const componentType of collectComponentTypes(snapshot.content)) {
404
- if (!allowlist.has(componentType)) {
405
- issues.push({
406
- code: "invalid_component_type",
407
- message: `Component type "${componentType}" is not in allowedComponentTypes`,
408
- path: "content"
409
- });
410
- }
411
- }
412
- return issues;
413
- }
414
- function validateGrapesProjectSnapshot(snapshot, options = {}) {
415
- const result = grapesProjectSnapshotSchema.safeParse(snapshot);
416
- if (!result.success) {
417
- return { ok: false, issues: zodIssuesToValidationIssues(result.error.issues) };
418
- }
419
- if (options.allowedComponentTypes?.length) {
420
- const typeIssues = validateAllowedComponentTypes(result.data, options.allowedComponentTypes);
421
- if (typeIssues.length > 0) {
422
- return { ok: false, issues: typeIssues };
423
- }
424
- }
425
- return { ok: true, issues: [] };
426
- }
427
94
  export {
428
95
  FALLBACK_ASSET_BYTES,
429
96
  FilesystemMigrationSink,
@@ -462,6 +129,7 @@ export {
462
129
  hasBlockingConflicts,
463
130
  hasWarnings,
464
131
  htmlToGrapes,
132
+ htmlToTiptap,
465
133
  isLikelyImageUrl,
466
134
  isTerminalState,
467
135
  mapJsonPrettyWire,
@@ -488,6 +156,9 @@ export {
488
156
  sourceMetadataSchema,
489
157
  squarespaceAdapter,
490
158
  staleUrlsFromEstimate,
159
+ tiptapDocSchema,
160
+ tiptapMarkSchema,
161
+ tiptapNodeSchema,
491
162
  validateGrapesProjectSnapshot,
492
163
  validateNormalizedAsset,
493
164
  validateNormalizedCategory,
@@ -496,6 +167,7 @@ export {
496
167
  validateNormalizedPortfolio,
497
168
  validateNormalizedPost,
498
169
  validateNormalizedTag,
170
+ validateTiptapDoc,
499
171
  wixAdapter,
500
172
  wordpressAdapter,
501
173
  writeFilesystemExport
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/transformers/html-to-grapes/index.ts","../src/transformers/css-to-styles/index.ts","../src/transformers/html-to-grapes/walk.ts","../src/transformers/validate-snapshot.ts"],"sourcesContent":["import * as cheerio from \"cheerio\";\n\nimport { cssToStyles } from \"../css-to-styles/index.js\";\nimport type { GrapesProjectSnapshot, HtmlToGrapesOptions } from \"./types.js\";\nimport { walkHtmlToComponents } from \"./walk.js\";\n\nexport type {\n GrapesComponent,\n GrapesProjectSnapshot,\n GrapesStyleRule,\n HtmlToGrapesOptions,\n} from \"./types.js\";\n\n/** Cheerio HTML walk → Grapes `content` + root `styles`. */\nexport function htmlToGrapes(html: string, options: HtmlToGrapesOptions = {}): GrapesProjectSnapshot {\n const trimmed = html.trim();\n if (!trimmed) {\n return { content: [], styles: [] };\n }\n\n const $ = cheerio.load(trimmed, { xml: false });\n const styleBlocks: string[] = [];\n\n $(\"style\").each((_, element) => {\n styleBlocks.push($(element).html() ?? \"\");\n $(element).remove();\n });\n\n const contentCss = styleBlocks.join(\"\\n\").trim();\n const styles = cssToStyles(contentCss);\n const content = walkHtmlToComponents($, options);\n const contentHtml = serializeContentHtml($);\n\n return {\n content,\n styles,\n ...(contentHtml ? { contentHtml } : {}),\n ...(contentCss ? { contentCss } : {}),\n };\n}\n\nfunction serializeContentHtml($: cheerio.CheerioAPI): string | undefined {\n const body = $(\"body\");\n if (body.length) {\n const html = body.html()?.trim();\n return html || undefined;\n }\n\n const rootHtml = $.root().html()?.trim();\n return rootHtml || undefined;\n}\n","import type { GrapesStyleRule } from \"../html-to-grapes/types.js\";\n\nfunction stripCssComments(css: string): string {\n return css.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n}\n\nfunction parseDeclarations(block: string): Record<string, string> {\n const style: Record<string, string> = {};\n for (const declaration of block.split(\";\")) {\n const trimmed = declaration.trim();\n if (!trimmed) continue;\n const separator = trimmed.indexOf(\":\");\n if (separator === -1) continue;\n const property = trimmed.slice(0, separator).trim();\n const value = trimmed.slice(separator + 1).trim();\n if (!property || !value) continue;\n style[property] = value;\n }\n return style;\n}\n\n/** Parse `<style>` blocks and class rules into Grapes root `styles[]`. */\nexport function cssToStyles(css: string): GrapesStyleRule[] {\n const cleaned = stripCssComments(css);\n const rules: GrapesStyleRule[] = [];\n const rulePattern = /([^{]+)\\{([^}]*)\\}/g;\n\n for (const match of cleaned.matchAll(rulePattern)) {\n const selectorText = match[1]?.trim() ?? \"\";\n const declarationBlock = match[2] ?? \"\";\n if (!selectorText || selectorText.startsWith(\"@\")) continue;\n\n const style = parseDeclarations(declarationBlock);\n if (Object.keys(style).length === 0) continue;\n\n const selectors = selectorText\n .split(\",\")\n .map((selector) => selector.trim())\n .filter(Boolean);\n\n if (selectors.length === 0) continue;\n rules.push({ selectors, style });\n }\n\n return rules;\n}\n","import type { Cheerio, CheerioAPI } from \"cheerio\";\nimport type { AnyNode } from \"domhandler\";\n\nimport type { GrapesComponent, HtmlToGrapesOptions } from \"./types.js\";\n\ntype CheerioSelection = Cheerio<AnyNode>;\n\nconst INLINE_TAGS = new Set([\n \"a\",\n \"abbr\",\n \"b\",\n \"br\",\n \"cite\",\n \"code\",\n \"del\",\n \"em\",\n \"i\",\n \"img\",\n \"ins\",\n \"mark\",\n \"q\",\n \"s\",\n \"small\",\n \"span\",\n \"strong\",\n \"sub\",\n \"sup\",\n \"u\",\n \"wbr\",\n]);\n\nconst VOID_TAGS = new Set([\n \"area\",\n \"base\",\n \"br\",\n \"col\",\n \"embed\",\n \"hr\",\n \"img\",\n \"input\",\n \"link\",\n \"meta\",\n \"param\",\n \"source\",\n \"track\",\n \"wbr\",\n]);\n\nconst TEXT_CONTAINER_TAGS = new Set([\n \"blockquote\",\n \"figcaption\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"label\",\n \"li\",\n \"p\",\n \"pre\",\n \"td\",\n \"th\",\n]);\n\nconst SKIP_TAGS = new Set([\"script\", \"style\", \"noscript\", \"template\"]);\n\nconst DEFAULT_TYPES: Record<string, string> = {\n a: \"link\",\n img: \"image\",\n};\n\nfunction tagNameOf($el: CheerioSelection): string | undefined {\n const raw = $el.prop(\"tagName\");\n return typeof raw === \"string\" ? raw.toLowerCase() : undefined;\n}\n\nfunction applyElementMeta(\n component: GrapesComponent,\n meta: Pick<GrapesComponent, \"attributes\" | \"classes\">,\n): GrapesComponent {\n if (meta.attributes) component.attributes = meta.attributes;\n if (meta.classes) component.classes = meta.classes;\n return component;\n}\n\nfunction pickElementMeta($el: CheerioSelection): Pick<GrapesComponent, \"attributes\" | \"classes\"> {\n const attributes: Record<string, string> = {};\n const classes: string[] = [];\n\n if (typeof $el.attr() === \"object\") {\n for (const [key, value] of Object.entries($el.attr() ?? {})) {\n if (key === \"class\") {\n classes.push(...value.split(/\\s+/).filter(Boolean));\n continue;\n }\n attributes[key] = value;\n }\n }\n\n return {\n attributes: Object.keys(attributes).length > 0 ? attributes : undefined,\n classes: classes.length > 0 ? classes : undefined,\n };\n}\n\nfunction resolveComponentType(\n tagName: string,\n classes: string[] | undefined,\n options: HtmlToGrapesOptions,\n): string {\n if (options.componentMap && classes) {\n for (const className of classes) {\n const mapped = options.componentMap[className];\n if (mapped) return mapped;\n }\n }\n return DEFAULT_TYPES[tagName] ?? \"default\";\n}\n\nfunction hasOnlyInlineContent($: CheerioAPI, $el: CheerioSelection): boolean {\n let inlineOnly = true;\n\n $el.contents().each((_, node) => {\n if (!inlineOnly) return;\n const $child = $(node);\n if ($child.get(0)?.type === \"text\") return;\n const childTag = tagNameOf($child);\n if (!childTag || !INLINE_TAGS.has(childTag)) {\n inlineOnly = false;\n return;\n }\n if (!hasOnlyInlineContent($, $child)) {\n inlineOnly = false;\n }\n });\n\n return inlineOnly;\n}\n\nfunction walkChildren(\n $: CheerioAPI,\n $el: CheerioSelection,\n options: HtmlToGrapesOptions,\n): GrapesComponent[] {\n const components: GrapesComponent[] = [];\n\n $el.contents().each((_, node) => {\n const walked = walkNode($, $(node), options);\n if (!walked) return;\n if (Array.isArray(walked)) {\n components.push(...walked);\n } else {\n components.push(walked);\n }\n });\n\n return components;\n}\n\nfunction walkNode(\n $: CheerioAPI,\n $el: CheerioSelection,\n options: HtmlToGrapesOptions,\n): GrapesComponent | GrapesComponent[] | null {\n const node = $el.get(0);\n if (!node) return null;\n\n if (node.type === \"text\") {\n const text = \"data\" in node ? String(node.data ?? \"\") : \"\";\n if (!text.trim()) return null;\n return { type: \"textnode\", content: text };\n }\n\n if (node.type !== \"tag\") return null;\n\n const tagName = tagNameOf($el);\n if (!tagName || SKIP_TAGS.has(tagName)) return null;\n\n const meta = pickElementMeta($el);\n\n if (VOID_TAGS.has(tagName)) {\n return applyElementMeta(\n {\n type: resolveComponentType(tagName, meta.classes, options),\n tagName,\n void: true,\n },\n meta,\n );\n }\n\n if (TEXT_CONTAINER_TAGS.has(tagName) && hasOnlyInlineContent($, $el)) {\n return applyElementMeta(\n {\n type: \"text\",\n tagName,\n content: $el.html() ?? \"\",\n },\n meta,\n );\n }\n\n if (INLINE_TAGS.has(tagName)) {\n return applyElementMeta(\n {\n type: \"text\",\n content: $.html($el) ?? \"\",\n },\n meta,\n );\n }\n\n const components = walkChildren($, $el, options);\n const component = applyElementMeta(\n {\n type: resolveComponentType(tagName, meta.classes, options),\n tagName,\n },\n meta,\n );\n\n if (components.length > 0) {\n component.components = components;\n }\n\n return component;\n}\n\nfunction appendWalked(\n content: GrapesComponent[],\n walked: GrapesComponent | GrapesComponent[] | null,\n): void {\n if (!walked) return;\n if (Array.isArray(walked)) {\n content.push(...walked);\n return;\n }\n content.push(walked);\n}\n\nfunction walkNodes(\n $: CheerioAPI,\n $nodes: CheerioSelection,\n content: GrapesComponent[],\n options: HtmlToGrapesOptions,\n): void {\n $nodes.each((_, node) => {\n appendWalked(content, walkNode($, $(node), options));\n });\n}\n\nexport function walkHtmlToComponents(\n $: CheerioAPI,\n options: HtmlToGrapesOptions = {},\n): GrapesComponent[] {\n const content: GrapesComponent[] = [];\n const body = $(\"body\");\n\n if (body.length) {\n walkNodes($, body.contents(), content, options);\n return content;\n }\n\n const children = $.root().children();\n if (children.length) {\n walkNodes($, children, content, options);\n } else {\n walkNodes($, $.root().contents(), content, options);\n }\n\n return content;\n}\n","import { z } from \"zod\";\n\nimport type { ValidationIssue, ValidationResult } from \"../normalizer/types.js\";\nimport type { GrapesComponent, GrapesProjectSnapshot } from \"./html-to-grapes/types.js\";\n\nexport const grapesStyleRuleSchema = z.object({\n selectors: z.array(z.string().min(1)).min(1),\n style: z.record(z.string(), z.string()),\n});\n\nexport const grapesComponentSchema: z.ZodType<GrapesComponent> = z.lazy(() =>\n z.object({\n type: z.string().min(1),\n tagName: z.string().optional(),\n attributes: z.record(z.string(), z.string()).optional(),\n classes: z.array(z.string()).optional(),\n components: z.array(grapesComponentSchema).optional(),\n content: z.string().optional(),\n void: z.boolean().optional(),\n }),\n);\n\nexport const grapesProjectSnapshotSchema = z.object({\n content: z.array(grapesComponentSchema),\n styles: z.array(grapesStyleRuleSchema),\n contentHtml: z.string().optional(),\n contentCss: z.string().optional(),\n});\n\nexport interface ValidateGrapesProjectSnapshotOptions {\n /** When set, every component `type` in the tree must be in this allowlist. */\n allowedComponentTypes?: string[];\n}\n\nfunction zodIssuesToValidationIssues(issues: z.ZodIssue[]): ValidationIssue[] {\n return issues.map((issue) => ({\n code: issue.code,\n message: issue.message,\n path: issue.path.length > 0 ? issue.path.join(\".\") : undefined,\n }));\n}\n\nfunction collectComponentTypes(components: GrapesComponent[]): string[] {\n const types: string[] = [];\n for (const component of components) {\n types.push(component.type);\n if (component.components?.length) {\n types.push(...collectComponentTypes(component.components));\n }\n }\n return types;\n}\n\nfunction validateAllowedComponentTypes(\n snapshot: GrapesProjectSnapshot,\n allowedComponentTypes: string[],\n): ValidationIssue[] {\n const allowlist = new Set(allowedComponentTypes);\n const issues: ValidationIssue[] = [];\n\n for (const componentType of collectComponentTypes(snapshot.content)) {\n if (!allowlist.has(componentType)) {\n issues.push({\n code: \"invalid_component_type\",\n message: `Component type \"${componentType}\" is not in allowedComponentTypes`,\n path: \"content\",\n });\n }\n }\n\n return issues;\n}\n\n/**\n * Opt-in structural check for a Grapes project snapshot (not a full Grapes editor project file).\n * Does not validate host-specific component registries unless `allowedComponentTypes` is passed.\n */\nexport function validateGrapesProjectSnapshot(\n snapshot: unknown,\n options: ValidateGrapesProjectSnapshotOptions = {},\n): ValidationResult {\n const result = grapesProjectSnapshotSchema.safeParse(snapshot);\n if (!result.success) {\n return { ok: false, issues: zodIssuesToValidationIssues(result.error.issues) };\n }\n\n if (options.allowedComponentTypes?.length) {\n const typeIssues = validateAllowedComponentTypes(result.data, options.allowedComponentTypes);\n if (typeIssues.length > 0) {\n return { ok: false, issues: typeIssues };\n }\n }\n\n return { ok: true, issues: [] };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,5 +1,7 @@
1
- import { d as MigrationCursor, b as EntityKey, E as EntityBundle, P as PortfolioMediaLink, V as ValidationResult } from '../bundle-DfM_jKbq.js';
2
- export { A as AdapterContext, B as BundleCounts, c as EntityType, M as MigrationAdapter, a as MigrationPlatform, N as NormalizedAsset, e as NormalizedAssetExif, f as NormalizedCategory, g as NormalizedEntity, h as NormalizedPage, i as NormalizedPortfolio, j as NormalizedPost, k as NormalizedTag, l as PublishStatus, S as SourceMetadata, m as ValidationIssue, n as bundleCounts, o as collectEntities, p as emptyBundle, q as entityKey } from '../bundle-DfM_jKbq.js';
1
+ import { i as MigrationCursor, E as EntityKey, P as PortfolioMediaLink, l as ValidationResult } from '../types-DWOP8Dcy.js';
2
+ export { A as AdapterContext, h as EntityType, M as MigrationAdapter, g as MigrationPlatform, b as NormalizedAsset, j as NormalizedAssetExif, d as NormalizedCategory, f as NormalizedEntity, a as NormalizedPage, c as NormalizedPortfolio, N as NormalizedPost, e as NormalizedTag, k as PublishStatus, S as SourceMetadata, V as ValidationIssue, m as entityKey } from '../types-DWOP8Dcy.js';
3
+ import { E as EntityBundle } from '../bundle-uAAHehbv.js';
4
+ export { B as BundleCounts, b as bundleCounts, c as collectEntities, e as emptyBundle } from '../bundle-uAAHehbv.js';
3
5
  import { z } from 'zod';
4
6
 
5
7
  /** Portable entity state for resume / idempotency (not Directus field names). */
@@ -0,0 +1,21 @@
1
+ interface RewriteInlineImageRef {
2
+ originalSrc: string;
3
+ sourceAssetId?: string;
4
+ }
5
+ interface UploadedAssetRef {
6
+ targetId: string;
7
+ publicUrl?: string;
8
+ }
9
+ interface RewriteInlineImagesOptions {
10
+ resolveAsset: (src: string) => RewriteInlineImageRef | undefined;
11
+ replaceWith: (ref: RewriteInlineImageRef, uploaded: UploadedAssetRef) => string;
12
+ }
13
+ interface RewriteInlineImagesResult {
14
+ html: string;
15
+ referencedSources: string[];
16
+ unresolved: string[];
17
+ }
18
+ /** Rewrite `<img src>` / `srcset` using uploaded asset targets supplied by the host sink. */
19
+ declare function rewriteInlineImages(html: string, options: RewriteInlineImagesOptions, uploadedBySourceId: Map<string, UploadedAssetRef>): RewriteInlineImagesResult;
20
+
21
+ export { type RewriteInlineImageRef as R, type UploadedAssetRef as U, type RewriteInlineImagesOptions as a, type RewriteInlineImagesResult as b, rewriteInlineImages as r };