@avocadostudio-ai/migration-sdk 0.1.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/LICENSE +201 -0
- package/dist/block-codegen.d.ts +5 -0
- package/dist/block-codegen.js +224 -0
- package/dist/design-tokens.d.ts +26 -0
- package/dist/design-tokens.js +733 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/scraper.d.ts +27 -0
- package/dist/scraper.js +1088 -0
- package/dist/section-extractor.d.ts +50 -0
- package/dist/section-extractor.js +911 -0
- package/dist/section-spec.d.ts +22 -0
- package/dist/section-spec.js +526 -0
- package/dist/types.d.ts +403 -0
- package/dist/types.js +2 -0
- package/package.json +47 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
export type FetchResult = {
|
|
2
|
+
html: string;
|
|
3
|
+
css: string;
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
title: string;
|
|
6
|
+
metaDescription: string;
|
|
7
|
+
};
|
|
8
|
+
export type ScreenshotResult = {
|
|
9
|
+
base64: string;
|
|
10
|
+
viewport: {
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export type DownloadedImage = {
|
|
16
|
+
localPath: string;
|
|
17
|
+
fileName: string;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
};
|
|
21
|
+
export type ColorUsage = "background" | "text" | "accent" | "border";
|
|
22
|
+
export type ExtractedColor = {
|
|
23
|
+
value: string;
|
|
24
|
+
usage: ColorUsage;
|
|
25
|
+
frequency: number;
|
|
26
|
+
/** CSS property where the color was found (e.g. "background-color", "color") */
|
|
27
|
+
property: string;
|
|
28
|
+
};
|
|
29
|
+
export type ExtractedFont = {
|
|
30
|
+
family: string;
|
|
31
|
+
usage: "heading" | "body" | "mono";
|
|
32
|
+
};
|
|
33
|
+
export type DesignTokens = {
|
|
34
|
+
colors: ExtractedColor[];
|
|
35
|
+
fonts: ExtractedFont[];
|
|
36
|
+
radii: string[];
|
|
37
|
+
};
|
|
38
|
+
/** Maps to the site's CSS custom properties (--bg-0, --brand, etc.) */
|
|
39
|
+
export type ThemeVariables = Record<string, string>;
|
|
40
|
+
export type FieldKind = "text" | "richtext" | "url" | "image" | "imageAlt" | "color" | "enum" | "number";
|
|
41
|
+
export type FieldSpec = {
|
|
42
|
+
name: string;
|
|
43
|
+
kind: FieldKind;
|
|
44
|
+
label: string;
|
|
45
|
+
required: boolean;
|
|
46
|
+
imageSpec?: {
|
|
47
|
+
aspectRatio: "landscape" | "square" | "portrait";
|
|
48
|
+
width: number;
|
|
49
|
+
height: number;
|
|
50
|
+
};
|
|
51
|
+
enumOptions?: string[];
|
|
52
|
+
};
|
|
53
|
+
export type ListFieldSpec = {
|
|
54
|
+
name: string;
|
|
55
|
+
label: string;
|
|
56
|
+
itemFields: FieldSpec[];
|
|
57
|
+
};
|
|
58
|
+
export type BlockCodegenInput = {
|
|
59
|
+
/** PascalCase block name, e.g. "PricingTable" */
|
|
60
|
+
name: string;
|
|
61
|
+
description: string;
|
|
62
|
+
category: "content" | "media" | "layout" | "conversion";
|
|
63
|
+
fields: FieldSpec[];
|
|
64
|
+
listFields?: ListFieldSpec[];
|
|
65
|
+
defaultProps: Record<string, unknown>;
|
|
66
|
+
/** CSS template using design system variables */
|
|
67
|
+
cssTemplate: string;
|
|
68
|
+
/** JSX body — inner content of the <section> wrapper */
|
|
69
|
+
rendererJsx: string;
|
|
70
|
+
/** Target directory for generated files, e.g. "apps/site/blocks" */
|
|
71
|
+
outputDir: string;
|
|
72
|
+
};
|
|
73
|
+
export type BlockCodegenResult = {
|
|
74
|
+
blockType: string;
|
|
75
|
+
filesCreated: string[];
|
|
76
|
+
manifestUpdated: string;
|
|
77
|
+
};
|
|
78
|
+
export type ExtractedSection = {
|
|
79
|
+
/** Position on page (0-based) */
|
|
80
|
+
index: number;
|
|
81
|
+
/** Original HTML tag (section, div, article, etc.) */
|
|
82
|
+
tag: string;
|
|
83
|
+
/** Element id attribute if present */
|
|
84
|
+
id?: string;
|
|
85
|
+
/** Semantic CSS class hints extracted from class list */
|
|
86
|
+
classHints: string[];
|
|
87
|
+
/** Heuristic block type suggestion based on class/content analysis */
|
|
88
|
+
suggestedBlockType?: string;
|
|
89
|
+
/** Structured content extracted from the section */
|
|
90
|
+
content: {
|
|
91
|
+
headings: Array<{
|
|
92
|
+
level: number;
|
|
93
|
+
text: string;
|
|
94
|
+
}>;
|
|
95
|
+
paragraphs: string[];
|
|
96
|
+
images: Array<{
|
|
97
|
+
src: string;
|
|
98
|
+
alt: string;
|
|
99
|
+
isLazy: boolean;
|
|
100
|
+
}>;
|
|
101
|
+
links: Array<{
|
|
102
|
+
href: string;
|
|
103
|
+
text: string;
|
|
104
|
+
}>;
|
|
105
|
+
lists: string[][];
|
|
106
|
+
};
|
|
107
|
+
/** Trimmed inner HTML (max ~5KB) */
|
|
108
|
+
rawHtml: string;
|
|
109
|
+
};
|
|
110
|
+
/** Compact page outline — captures full page structure in ~3KB regardless of HTML size */
|
|
111
|
+
export type PageOutline = {
|
|
112
|
+
/** All headings in document order */
|
|
113
|
+
headings: Array<{
|
|
114
|
+
level: number;
|
|
115
|
+
text: string;
|
|
116
|
+
}>;
|
|
117
|
+
/** Visual sections split at h1/h2 boundaries — each may contain sub-items from h3s */
|
|
118
|
+
sections: Array<{
|
|
119
|
+
/** Inferred section type */
|
|
120
|
+
type: "hero" | "features" | "cards" | "pricing" | "cta" | "text" | "gallery" | "faq" | "stats" | "contact" | "video" | "info-hub" | "unknown";
|
|
121
|
+
/** Section heading (from h1/h2) */
|
|
122
|
+
heading?: string;
|
|
123
|
+
/** First ~120 chars of text content */
|
|
124
|
+
contentSummary: string;
|
|
125
|
+
/** Sub-items from h3 headings within this section */
|
|
126
|
+
subItems?: string[];
|
|
127
|
+
imageCount: number;
|
|
128
|
+
linkCount: number;
|
|
129
|
+
listItemCount: number;
|
|
130
|
+
hasForm: boolean;
|
|
131
|
+
hasPricing: boolean;
|
|
132
|
+
hasVideo: boolean;
|
|
133
|
+
/** Elementor widget types found in this section */
|
|
134
|
+
widgetTypes?: string[];
|
|
135
|
+
/** Repeated element patterns detected visually */
|
|
136
|
+
repeatGroups?: RepeatGroup[];
|
|
137
|
+
/** How this section was detected */
|
|
138
|
+
detectedBy?: "heading" | "visual-gap" | "both";
|
|
139
|
+
}>;
|
|
140
|
+
totalImages: number;
|
|
141
|
+
totalLinks: number;
|
|
142
|
+
};
|
|
143
|
+
export type LayoutNode = {
|
|
144
|
+
tag: string;
|
|
145
|
+
depth: number;
|
|
146
|
+
rect: {
|
|
147
|
+
x: number;
|
|
148
|
+
y: number;
|
|
149
|
+
w: number;
|
|
150
|
+
h: number;
|
|
151
|
+
};
|
|
152
|
+
text: string;
|
|
153
|
+
childCount: number;
|
|
154
|
+
imgCount: number;
|
|
155
|
+
linkCount: number;
|
|
156
|
+
classes: string;
|
|
157
|
+
role: string;
|
|
158
|
+
widgetType: string;
|
|
159
|
+
};
|
|
160
|
+
export type VisualSection = {
|
|
161
|
+
y: number;
|
|
162
|
+
height: number;
|
|
163
|
+
nodes: LayoutNode[];
|
|
164
|
+
textLength: number;
|
|
165
|
+
imgCount: number;
|
|
166
|
+
linkCount: number;
|
|
167
|
+
};
|
|
168
|
+
export type RepeatGroup = {
|
|
169
|
+
signature: string;
|
|
170
|
+
count: number;
|
|
171
|
+
inferredType: "card" | "feature" | "pricing" | "stat" | "testimonial" | "unknown";
|
|
172
|
+
itemTexts: string[];
|
|
173
|
+
};
|
|
174
|
+
/** Extracted navigation structure from the source site */
|
|
175
|
+
export type NavExtraction = {
|
|
176
|
+
siteName?: string;
|
|
177
|
+
logoUrl?: string;
|
|
178
|
+
items: Array<{
|
|
179
|
+
label: string;
|
|
180
|
+
href?: string;
|
|
181
|
+
children?: Array<{
|
|
182
|
+
label: string;
|
|
183
|
+
href: string;
|
|
184
|
+
}>;
|
|
185
|
+
}>;
|
|
186
|
+
};
|
|
187
|
+
/** Embedded iframe (YouTube, Vimeo, Google Maps, etc.) */
|
|
188
|
+
export type ExtractedEmbed = {
|
|
189
|
+
src: string;
|
|
190
|
+
type: "youtube" | "vimeo" | "map" | "other";
|
|
191
|
+
/** Y position on the page (for correlating with visual sections) */
|
|
192
|
+
y: number;
|
|
193
|
+
width: number;
|
|
194
|
+
height: number;
|
|
195
|
+
};
|
|
196
|
+
/** Layered image composition — multiple positioned images forming one visual */
|
|
197
|
+
export type ImageComposition = {
|
|
198
|
+
layers: Array<{
|
|
199
|
+
src: string;
|
|
200
|
+
alt: string;
|
|
201
|
+
zIndex: number;
|
|
202
|
+
position: string;
|
|
203
|
+
bounds: {
|
|
204
|
+
x: number;
|
|
205
|
+
y: number;
|
|
206
|
+
w: number;
|
|
207
|
+
h: number;
|
|
208
|
+
};
|
|
209
|
+
}>;
|
|
210
|
+
compositeType: "stacked" | "overlay" | "single";
|
|
211
|
+
};
|
|
212
|
+
/** Extracted <video> element (background videos, hero videos, etc.) */
|
|
213
|
+
export type ExtractedVideo = {
|
|
214
|
+
src: string;
|
|
215
|
+
poster?: string;
|
|
216
|
+
autoplay: boolean;
|
|
217
|
+
loop: boolean;
|
|
218
|
+
muted: boolean;
|
|
219
|
+
/** Y position on the page (for correlating with visual sections) */
|
|
220
|
+
y: number;
|
|
221
|
+
width: number;
|
|
222
|
+
height: number;
|
|
223
|
+
};
|
|
224
|
+
/** Captured interaction state — styles before and after a trigger (click, scroll, hover) */
|
|
225
|
+
export type InteractionState = {
|
|
226
|
+
trigger: "click" | "scroll" | "hover";
|
|
227
|
+
/** Selector or description of the triggering element */
|
|
228
|
+
triggerTarget: string;
|
|
229
|
+
/** Key style properties that changed */
|
|
230
|
+
changedStyles: Record<string, {
|
|
231
|
+
before: string;
|
|
232
|
+
after: string;
|
|
233
|
+
}>;
|
|
234
|
+
/** Transition/animation duration if detected */
|
|
235
|
+
transitionDuration?: string;
|
|
236
|
+
};
|
|
237
|
+
/** Combined result from scrapeFullPage — one Playwright session */
|
|
238
|
+
export type FullPageScrape = {
|
|
239
|
+
content: FetchResult;
|
|
240
|
+
/** Desktop screenshot (1440px viewport) */
|
|
241
|
+
screenshot: ScreenshotResult | null;
|
|
242
|
+
/** Mobile screenshot (390px viewport) */
|
|
243
|
+
mobileScreenshot: ScreenshotResult | null;
|
|
244
|
+
sections: ExtractedSection[];
|
|
245
|
+
outline: PageOutline;
|
|
246
|
+
nav?: NavExtraction;
|
|
247
|
+
/** Per-section computed CSS styles (from getComputedStyle walker, keyed by visual section index) */
|
|
248
|
+
sectionStyles?: SectionStyles[];
|
|
249
|
+
/** Visual sections detected by bounding-box gap analysis (CMS-agnostic) */
|
|
250
|
+
visualSections?: VisualSection[];
|
|
251
|
+
/** Embedded iframes found on the page */
|
|
252
|
+
embeds?: ExtractedEmbed[];
|
|
253
|
+
/** Actual rendered fonts from getComputedStyle (more reliable than CSS regex) */
|
|
254
|
+
computedFonts?: {
|
|
255
|
+
heading: string | null;
|
|
256
|
+
body: string | null;
|
|
257
|
+
googleFontLinks: string[];
|
|
258
|
+
};
|
|
259
|
+
/** All <img> elements on the page with Y positions (for distributing to visual sections) */
|
|
260
|
+
pageImages?: Array<{
|
|
261
|
+
src: string;
|
|
262
|
+
alt: string;
|
|
263
|
+
y: number;
|
|
264
|
+
width: number;
|
|
265
|
+
height: number;
|
|
266
|
+
}>;
|
|
267
|
+
/** Fallback text content for visual sections where findElementForRange failed.
|
|
268
|
+
* Extracted by Y-range scan — has text content but no computed style tree. */
|
|
269
|
+
sectionFallbackContent?: Array<{
|
|
270
|
+
sectionIndex: number;
|
|
271
|
+
headings: Array<{
|
|
272
|
+
level: number;
|
|
273
|
+
text: string;
|
|
274
|
+
}>;
|
|
275
|
+
paragraphs: string[];
|
|
276
|
+
links: Array<{
|
|
277
|
+
href: string;
|
|
278
|
+
text: string;
|
|
279
|
+
}>;
|
|
280
|
+
lists: string[][];
|
|
281
|
+
}>;
|
|
282
|
+
/** Resolved CSS custom properties from :root/html (var(--name) → actual value).
|
|
283
|
+
* Used by design-tokens to resolve var() references to real colors/values. */
|
|
284
|
+
resolvedCssVars?: Record<string, string>;
|
|
285
|
+
/** <video> elements found on the page (hero background videos, etc.) */
|
|
286
|
+
videos?: ExtractedVideo[];
|
|
287
|
+
/** Detected scroll behavior library (Lenis, Locomotive, native smooth, etc.) */
|
|
288
|
+
scrollBehavior?: {
|
|
289
|
+
library: "lenis" | "locomotive" | "native-smooth" | "none";
|
|
290
|
+
scrollContainer?: string;
|
|
291
|
+
};
|
|
292
|
+
/** Interaction states captured during the interaction sweep (tabs, accordions, scroll triggers) */
|
|
293
|
+
interactionStates?: Array<{
|
|
294
|
+
sectionY: number;
|
|
295
|
+
states: InteractionState[];
|
|
296
|
+
}>;
|
|
297
|
+
/** Detected layered image compositions (overlapping positioned images) */
|
|
298
|
+
imageCompositions?: ImageComposition[];
|
|
299
|
+
};
|
|
300
|
+
/** A DOM node with its computed CSS styles extracted via getComputedStyle().
|
|
301
|
+
* The walker recurses 4 levels deep from each section root. */
|
|
302
|
+
export type ComputedStyleNode = {
|
|
303
|
+
tag: string;
|
|
304
|
+
depth: number;
|
|
305
|
+
/** Nth-child selector path for debugging (e.g. "section:nth-child(2) > div:nth-child(1) > h1") */
|
|
306
|
+
selector: string;
|
|
307
|
+
/** Filtered computed CSS properties — only non-default values */
|
|
308
|
+
styles: Record<string, string>;
|
|
309
|
+
/** Direct text content (first 200 chars, leaf nodes only) */
|
|
310
|
+
text: string | null;
|
|
311
|
+
/** Image info if this is an <img> element */
|
|
312
|
+
image: {
|
|
313
|
+
src: string;
|
|
314
|
+
alt: string;
|
|
315
|
+
naturalWidth: number;
|
|
316
|
+
naturalHeight: number;
|
|
317
|
+
} | null;
|
|
318
|
+
children: ComputedStyleNode[];
|
|
319
|
+
};
|
|
320
|
+
/** Computed styles for one page section, keyed by section index */
|
|
321
|
+
export type SectionStyles = {
|
|
322
|
+
sectionIndex: number;
|
|
323
|
+
root: ComputedStyleNode;
|
|
324
|
+
};
|
|
325
|
+
/** Block-type-agnostic section specification.
|
|
326
|
+
* Rich enough for the LLM to map to an existing block OR hand to block-coder
|
|
327
|
+
* for a custom block. No pre-mapped props — the LLM decides. */
|
|
328
|
+
export type SectionSpec = {
|
|
329
|
+
sectionIndex: number;
|
|
330
|
+
/** Content extracted from the section */
|
|
331
|
+
content: {
|
|
332
|
+
headings: Array<{
|
|
333
|
+
level: number;
|
|
334
|
+
text: string;
|
|
335
|
+
}>;
|
|
336
|
+
paragraphs: string[];
|
|
337
|
+
images: Array<{
|
|
338
|
+
src: string;
|
|
339
|
+
alt: string;
|
|
340
|
+
isBackground: boolean;
|
|
341
|
+
}>;
|
|
342
|
+
links: Array<{
|
|
343
|
+
href: string;
|
|
344
|
+
text: string;
|
|
345
|
+
}>;
|
|
346
|
+
lists: string[][];
|
|
347
|
+
};
|
|
348
|
+
/** DOM structure summary — what the section looks like structurally */
|
|
349
|
+
structure: {
|
|
350
|
+
/** e.g. "3-column grid of cards", "hero with side image", "accordion" */
|
|
351
|
+
pattern: string;
|
|
352
|
+
/** Number of repeated child groups (0 = no repetition) */
|
|
353
|
+
repeatCount: number;
|
|
354
|
+
/** Signature of repeated items (e.g. "img + h3 + p + a") */
|
|
355
|
+
repeatSignature?: string;
|
|
356
|
+
/** Total element count in the section */
|
|
357
|
+
elementCount: number;
|
|
358
|
+
/** Interaction model inferred from DOM */
|
|
359
|
+
interactionModel: "static" | "accordion" | "tabs" | "carousel" | "scroll-driven";
|
|
360
|
+
};
|
|
361
|
+
/** Exact computed CSS values from getComputedStyle() — the key data
|
|
362
|
+
* that lets the LLM or block-coder reproduce the visual design */
|
|
363
|
+
styles: {
|
|
364
|
+
/** Section container styles */
|
|
365
|
+
container: Record<string, string>;
|
|
366
|
+
/** Heading styles (first heading found) */
|
|
367
|
+
heading?: Record<string, string>;
|
|
368
|
+
/** Body text styles */
|
|
369
|
+
bodyText?: Record<string, string>;
|
|
370
|
+
/** Repeated item styles (if repeatCount > 0, styles of first item) */
|
|
371
|
+
repeatedItem?: Record<string, string>;
|
|
372
|
+
/** CTA/button styles (if links/buttons present) */
|
|
373
|
+
cta?: Record<string, string>;
|
|
374
|
+
};
|
|
375
|
+
/** Design characteristics derived from computed styles */
|
|
376
|
+
designNotes: {
|
|
377
|
+
backgroundColor: string;
|
|
378
|
+
textColor: string;
|
|
379
|
+
headingFont: string;
|
|
380
|
+
headingSize: string;
|
|
381
|
+
layout: string;
|
|
382
|
+
hasGradient: boolean;
|
|
383
|
+
hasShadow: boolean;
|
|
384
|
+
borderRadius: string;
|
|
385
|
+
};
|
|
386
|
+
/** Interaction states captured by clicking tabs/accordions and scrolling */
|
|
387
|
+
interactionStates?: InteractionState[];
|
|
388
|
+
/** Heuristic suggestion — NOT authoritative. LLM decides final block type. */
|
|
389
|
+
suggestedBlockType?: string;
|
|
390
|
+
/** Heuristic confidence (0-1) for the suggestion */
|
|
391
|
+
suggestedConfidence: number;
|
|
392
|
+
};
|
|
393
|
+
export type DiscoveredPage = {
|
|
394
|
+
url: string;
|
|
395
|
+
slug: string;
|
|
396
|
+
title?: string;
|
|
397
|
+
};
|
|
398
|
+
export type SiteStructure = {
|
|
399
|
+
origin: string;
|
|
400
|
+
pages: DiscoveredPage[];
|
|
401
|
+
source: "sitemap" | "robots" | "links" | "single";
|
|
402
|
+
totalFound: number;
|
|
403
|
+
};
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@avocadostudio-ai/migration-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/avocadostudio-ai/avocado.git",
|
|
10
|
+
"directory": "packages/migration-sdk"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"registry": "https://registry.npmjs.org",
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@avocadostudio-ai/shared": "0.1.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^22.13.10",
|
|
24
|
+
"tsx": "^4.19.3",
|
|
25
|
+
"typescript": "^5.7.3",
|
|
26
|
+
"playwright": "^1.58.2"
|
|
27
|
+
},
|
|
28
|
+
"description": "Utilities for migrating existing site content into the Avocado Studio PageDoc/BlockInstance shape",
|
|
29
|
+
"license": "Apache-2.0",
|
|
30
|
+
"homepage": "https://github.com/avocadostudio-ai/avocado/tree/main/packages/migration-sdk#readme",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/avocadostudio-ai/avocado/issues"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"playwright": "^1.58.2"
|
|
36
|
+
},
|
|
37
|
+
"peerDependenciesMeta": {
|
|
38
|
+
"playwright": {
|
|
39
|
+
"optional": true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc -p tsconfig.build.json",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"test": "NODE_ENV=test tsx --test --test-concurrency=4 src/**/*.test.ts"
|
|
46
|
+
}
|
|
47
|
+
}
|