@nymbalhq/importers 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +5 -2
- package/dist/index.js +23 -20
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AiAdapter, ReviewsAdapter } from '@nymbalhq/types';
|
|
1
|
+
import { AiAdapter, ReviewsAdapter, MediaStorage } from '@nymbalhq/types';
|
|
2
2
|
import { ServiceRegistry, Repositories } from '@nymbalhq/platform';
|
|
3
3
|
|
|
4
4
|
interface WooCommerceCredentials {
|
|
@@ -73,10 +73,13 @@ type ProgressEvent = {
|
|
|
73
73
|
interface WooCommerceImporterDeps {
|
|
74
74
|
services: ServiceRegistry;
|
|
75
75
|
repos: Repositories;
|
|
76
|
+
/** `ai` is null when the store's ai provider is `none`, and enrichment is skipped. */
|
|
76
77
|
adapters: {
|
|
77
|
-
ai: AiAdapter;
|
|
78
|
+
ai: AiAdapter | null;
|
|
78
79
|
reviews: ReviewsAdapter;
|
|
79
80
|
};
|
|
81
|
+
/** The store's media storage (`app.platform.media`): every product image is put here. */
|
|
82
|
+
media: MediaStorage;
|
|
80
83
|
onProgress?: ProgressCallback;
|
|
81
84
|
}
|
|
82
85
|
declare function runWooCommerceImport(opts: WooCommerceImportOptions, deps: WooCommerceImporterDeps): Promise<ImportReport>;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
// src/woocommerce/importer.ts
|
|
2
|
-
import { createLocalFsMediaStorage } from "@nymbalhq/platform";
|
|
3
|
-
import { join as join3 } from "path";
|
|
4
|
-
|
|
5
1
|
// src/state.ts
|
|
6
2
|
import { readFile, writeFile, unlink } from "fs/promises";
|
|
7
3
|
var EMPTY_STATE = {
|
|
@@ -460,6 +456,8 @@ var SUPPORTED_TYPES = {
|
|
|
460
456
|
"image/gif": "gif"
|
|
461
457
|
};
|
|
462
458
|
async function downloadImage(src, altText, storage, subdir) {
|
|
459
|
+
let body;
|
|
460
|
+
let contentType;
|
|
463
461
|
try {
|
|
464
462
|
const controller = new AbortController();
|
|
465
463
|
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
@@ -471,22 +469,28 @@ async function downloadImage(src, altText, storage, subdir) {
|
|
|
471
469
|
}
|
|
472
470
|
if (!res.ok) return null;
|
|
473
471
|
const rawContentType = res.headers.get("content-type") ?? "image/jpeg";
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const hash = createHash2("sha256").update(src).digest("hex").slice(0, 16);
|
|
477
|
-
const key = join2(subdir, `${hash}.${ext}`);
|
|
478
|
-
const buffer = await res.arrayBuffer();
|
|
479
|
-
const { url } = await storage.put(key, new Uint8Array(buffer), contentType);
|
|
480
|
-
return { url, altText: altText || "" };
|
|
472
|
+
contentType = (rawContentType.split(";")[0] ?? "image/jpeg").trim();
|
|
473
|
+
body = new Uint8Array(await res.arrayBuffer());
|
|
481
474
|
} catch {
|
|
482
475
|
return null;
|
|
483
476
|
}
|
|
477
|
+
const ext = SUPPORTED_TYPES[contentType] ?? "jpg";
|
|
478
|
+
const hash = createHash2("sha256").update(src).digest("hex").slice(0, 16);
|
|
479
|
+
const key = join2(subdir, `${hash}.${ext}`);
|
|
480
|
+
const { url } = await storage.put(key, body, contentType);
|
|
481
|
+
return { url, altText: altText || "" };
|
|
484
482
|
}
|
|
485
|
-
async function downloadProductImages(images, productSlug, storage) {
|
|
483
|
+
async function downloadProductImages(images, productSlug, storage, onStoreError) {
|
|
486
484
|
const results = [];
|
|
487
485
|
for (const img of images) {
|
|
488
486
|
if (!img.src) continue;
|
|
489
|
-
|
|
487
|
+
let downloaded;
|
|
488
|
+
try {
|
|
489
|
+
downloaded = await downloadImage(img.src, img.alt, storage, `products/${productSlug}`);
|
|
490
|
+
} catch (error) {
|
|
491
|
+
onStoreError(error);
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
490
494
|
if (downloaded) {
|
|
491
495
|
results.push(downloaded);
|
|
492
496
|
}
|
|
@@ -580,13 +584,9 @@ async function pMap(items, fn, concurrency) {
|
|
|
580
584
|
}
|
|
581
585
|
async function runWooCommerceImport(opts, deps) {
|
|
582
586
|
const startMs = Date.now();
|
|
583
|
-
const { services, adapters, onProgress } = deps;
|
|
587
|
+
const { services, adapters, media, onProgress } = deps;
|
|
584
588
|
const emit = onProgress ?? (() => void 0);
|
|
585
589
|
const client = new WooCommerceClient(opts.credentials);
|
|
586
|
-
const storage = createLocalFsMediaStorage({
|
|
587
|
-
root: join3(opts.projectRoot, "public", "imported-media"),
|
|
588
|
-
urlPrefix: "/imported-media"
|
|
589
|
-
});
|
|
590
590
|
let state = opts.fresh ? freshState() : await loadState(opts.statePath) ?? freshState();
|
|
591
591
|
const checkpoint = async () => saveState(opts.statePath, state);
|
|
592
592
|
emit({ kind: "phase", phase: "connect" });
|
|
@@ -646,7 +646,10 @@ async function runWooCommerceImport(opts, deps) {
|
|
|
646
646
|
const slug = wc.slug || wc.name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
647
647
|
const suffix = seenSlugs.has(slug) ? String(wc.id) : "";
|
|
648
648
|
seenSlugs.add(slug);
|
|
649
|
-
const mediaItems = await downloadProductImages(wc.images, slug,
|
|
649
|
+
const mediaItems = await downloadProductImages(wc.images, slug, media, (err) => {
|
|
650
|
+
state.errors.push({ phase: "media", id: String(wc.id), error: String(err) });
|
|
651
|
+
emit({ kind: "error", phase: "media", id: String(wc.id), message: String(err) });
|
|
652
|
+
});
|
|
650
653
|
try {
|
|
651
654
|
const snap = await services.product.create(
|
|
652
655
|
mapProduct(wc, variations, categoryIds, mediaItems, suffix)
|
|
@@ -759,7 +762,7 @@ async function runWooCommerceImport(opts, deps) {
|
|
|
759
762
|
}
|
|
760
763
|
}
|
|
761
764
|
const enrichStats = { enriched: 0, skipped: 0, failed: 0 };
|
|
762
|
-
if (opts.ai && state.importedIds.products.length > 0) {
|
|
765
|
+
if (opts.ai && adapters.ai !== null && state.importedIds.products.length > 0) {
|
|
763
766
|
emit({ kind: "phase", phase: "enrich" });
|
|
764
767
|
const stats = await runEnrichment(
|
|
765
768
|
state.importedIds.products,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/woocommerce/importer.ts","../src/state.ts","../src/report.ts","../src/woocommerce/client.ts","../src/woocommerce/mappers/category.ts","../src/woocommerce/mappers/product.ts","../src/woocommerce/mappers/customer.ts","../src/woocommerce/mappers/order.ts","../src/woocommerce/mappers/review.ts","../src/woocommerce/media.ts","../src/woocommerce/enrichment.ts"],"sourcesContent":["import type { AiAdapter } from '@nymbalhq/types'\nimport type {\n ServiceRegistry,\n Repositories,\n} from '@nymbalhq/platform'\nimport { createLocalFsMediaStorage } from '@nymbalhq/platform'\nimport { join } from 'node:path'\nimport type { WooCommerceImportOptions, ImportState, ProgressCallback } from '../types.js'\nimport { loadState, freshState, saveState } from '../state.js'\nimport { buildReport } from '../report.js'\nimport { WooCommerceClient, WooCommerceApiError } from './client.js'\nimport type { WcCategory, WcProduct, WcVariation, WcCustomer, WcOrder, WcReview } from './client.js'\nimport { mapCategory } from './mappers/category.js'\nimport { mapProduct } from './mappers/product.js'\nimport { mapCustomer } from './mappers/customer.js'\nimport { mapOrder } from './mappers/order.js'\nimport { mapReview } from './mappers/review.js'\nimport { downloadProductImages } from './media.js'\nimport { runEnrichment } from './enrichment.js'\nimport type { ReviewsAdapter } from '@nymbalhq/types'\n\nexport interface WooCommerceImporterDeps {\n services: ServiceRegistry\n repos: Repositories\n adapters: { ai: AiAdapter; reviews: ReviewsAdapter }\n onProgress?: ProgressCallback\n}\n\nasync function pMap<T, U>(\n items: T[],\n fn: (item: T, index: number) => Promise<U>,\n concurrency: number,\n): Promise<Array<U | Error>> {\n const results: Array<U | Error> = new Array(items.length)\n let idx = 0\n async function worker() {\n while (idx < items.length) {\n const i = idx++\n const item = items[i]\n if (item === undefined) continue\n try {\n results[i] = await fn(item, i)\n } catch (err) {\n results[i] = err instanceof Error ? err : new Error(String(err))\n }\n }\n }\n await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, worker))\n return results\n}\n\nexport async function runWooCommerceImport(\n opts: WooCommerceImportOptions,\n deps: WooCommerceImporterDeps,\n) {\n const startMs = Date.now()\n const { services, adapters, onProgress } = deps\n const emit = onProgress ?? (() => undefined)\n\n const client = new WooCommerceClient(opts.credentials)\n const storage = createLocalFsMediaStorage({\n root: join(opts.projectRoot, 'public', 'imported-media'),\n urlPrefix: '/imported-media',\n })\n\n // Load or create state\n let state: ImportState = opts.fresh\n ? freshState()\n : ((await loadState(opts.statePath)) ?? freshState())\n\n const checkpoint = async () => saveState(opts.statePath, state)\n\n // ── 1. Connect ──────────────────────────────────────────────────────────────\n emit({ kind: 'phase', phase: 'connect' })\n try {\n await client.healthCheck()\n } catch (err) {\n if (err instanceof WooCommerceApiError) throw err\n throw new Error(`Cannot connect to ${opts.credentials.url}: ${String(err)}`)\n }\n\n // ── 2. Categories ────────────────────────────────────────────────────────────\n if (opts.entities.includes('categories') && state.phase !== 'done') {\n emit({ kind: 'phase', phase: 'categories' })\n state.phase = 'categories'\n\n const startPage = state.cursors['categories']?.page ?? 1\n let done = state.importedIds.categories.length\n let total = 0\n\n for await (const page of client.paginate<WcCategory>('/products/categories', {}, startPage)) {\n total = page.total\n\n for (const wc of page.items) {\n if (state.wcToNymbalCategory[wc.id]) continue\n const nymbalParentId = wc.parent ? state.wcToNymbalCategory[wc.parent] : undefined\n try {\n const category = await services.category.create(mapCategory(wc, nymbalParentId))\n state.wcToNymbalCategory[wc.id] = category.id\n state.importedIds.categories.push(category.id)\n state.eventsEmitted++\n } catch (err) {\n state.errors.push({ phase: 'categories', id: String(wc.id), error: String(err) })\n }\n done++\n emit({ kind: 'progress', entity: 'categories', done, total })\n }\n\n state.cursors['categories'] = { page: state.cursors['categories']?.page ?? 1 }\n await checkpoint()\n }\n }\n\n // ── 3. Products ──────────────────────────────────────────────────────────────\n if (opts.entities.includes('products') && state.phase !== 'done') {\n emit({ kind: 'phase', phase: 'products' })\n state.phase = 'products'\n\n const startPage = state.cursors['products']?.page ?? 1\n let done = state.importedIds.products.length\n let total = 0\n\n const seenSlugs = new Set<string>()\n\n for await (const page of client.paginate<WcProduct>('/products', {}, startPage)) {\n total = page.total\n\n await pMap(\n page.items,\n async (wc) => {\n if (state.wcToNymbalProduct[wc.id]) return\n\n let variations: WcVariation[] = []\n if (wc.type === 'variable' && wc.variations.length > 0) {\n try {\n variations = await client.getProductVariations(wc.id)\n } catch {\n // proceed with no variations\n }\n }\n\n const categoryIds = wc.categories\n .map((c) => state.wcToNymbalCategory[c.id])\n .filter((id): id is string => Boolean(id))\n\n const slug = wc.slug || wc.name.toLowerCase().replace(/[^a-z0-9]+/g, '-')\n const suffix = seenSlugs.has(slug) ? String(wc.id) : ''\n seenSlugs.add(slug)\n\n const mediaItems = await downloadProductImages(wc.images, slug, storage)\n\n try {\n const snap = await services.product.create(\n mapProduct(wc, variations, categoryIds, mediaItems, suffix),\n )\n state.wcToNymbalProduct[wc.id] = snap.id\n state.importedIds.products.push(snap.id)\n state.eventsEmitted++\n } catch (err) {\n state.errors.push({ phase: 'products', id: String(wc.id), error: String(err) })\n }\n },\n opts.concurrency,\n )\n\n done += page.items.length\n emit({ kind: 'progress', entity: 'products', done, total })\n state.cursors['products'] = { page: (state.cursors['products']?.page ?? 1) + 1 }\n await checkpoint()\n }\n }\n\n // ── 4. Customers ─────────────────────────────────────────────────────────────\n if (opts.entities.includes('customers') && state.phase !== 'done') {\n emit({ kind: 'phase', phase: 'customers' })\n state.phase = 'customers'\n\n const startPage = state.cursors['customers']?.page ?? 1\n let done = state.importedIds.customers.length\n let total = 0\n\n for await (const page of client.paginate<WcCustomer>('/customers', {}, startPage)) {\n total = page.total\n\n await pMap(\n page.items,\n async (wc) => {\n if (state.wcToNymbalCustomer[wc.id]) return\n try {\n const customer = await services.auth.importCustomer(mapCustomer(wc, opts.gdpr))\n state.wcToNymbalCustomer[wc.id] = customer.id\n state.importedIds.customers.push(customer.id)\n state.eventsEmitted++\n } catch (err) {\n state.errors.push({ phase: 'customers', id: String(wc.id), error: String(err) })\n }\n },\n opts.concurrency,\n )\n\n done += page.items.length\n emit({ kind: 'progress', entity: 'customers', done, total })\n state.cursors['customers'] = { page: (state.cursors['customers']?.page ?? 1) + 1 }\n await checkpoint()\n }\n }\n\n // ── 5. Orders ────────────────────────────────────────────────────────────────\n if (opts.entities.includes('orders') && state.phase !== 'done') {\n emit({ kind: 'phase', phase: 'orders' })\n state.phase = 'orders'\n\n const startPage = state.cursors['orders']?.page ?? 1\n let done = state.importedIds.orders.length\n let total = 0\n\n for await (const page of client.paginate<WcOrder>('/orders', {}, startPage)) {\n total = page.total\n\n await pMap(\n page.items,\n async (wc) => {\n if (state.wcToNymbalProduct[wc.id]) return\n const nymbalCustomerId = wc.customer_id\n ? (state.wcToNymbalCustomer[wc.customer_id] ?? null)\n : null\n const productIdMap: Record<number, string> = {}\n for (const li of wc.line_items) {\n const nid = state.wcToNymbalProduct[li.product_id]\n if (nid) productIdMap[li.product_id] = nid\n }\n try {\n const order = await services.order.importOrder(\n mapOrder(wc, nymbalCustomerId, productIdMap, opts.currency),\n )\n state.importedIds.orders.push(order.id)\n state.eventsEmitted++\n } catch (err) {\n state.errors.push({ phase: 'orders', id: String(wc.id), error: String(err) })\n }\n },\n opts.concurrency,\n )\n\n done += page.items.length\n emit({ kind: 'progress', entity: 'orders', done, total })\n state.cursors['orders'] = { page: (state.cursors['orders']?.page ?? 1) + 1 }\n await checkpoint()\n }\n }\n\n // ── 6. Reviews ───────────────────────────────────────────────────────────────\n if (opts.entities.includes('reviews') && state.phase !== 'done') {\n emit({ kind: 'phase', phase: 'reviews' })\n state.phase = 'reviews'\n\n const startPage = state.cursors['reviews']?.page ?? 1\n let done = state.importedIds.reviews.length\n let total = 0\n\n for await (const page of client.paginate<WcReview>('/products/reviews', {}, startPage)) {\n total = page.total\n\n for (const wc of page.items) {\n const nymbalProductId = state.wcToNymbalProduct[wc.product_id]\n if (!nymbalProductId) continue\n try {\n const result = await adapters.reviews.submitReview(\n mapReview(wc, nymbalProductId, null),\n )\n state.importedIds.reviews.push(result.review.id)\n state.eventsEmitted++\n } catch (err) {\n state.errors.push({ phase: 'reviews', id: String(wc.id), error: String(err) })\n }\n done++\n emit({ kind: 'progress', entity: 'reviews', done, total })\n }\n\n state.cursors['reviews'] = { page: (state.cursors['reviews']?.page ?? 1) + 1 }\n await checkpoint()\n }\n }\n\n // ── 7. AI Enrichment ─────────────────────────────────────────────────────────\n const enrichStats = { enriched: 0, skipped: 0, failed: 0 }\n if (opts.ai && state.importedIds.products.length > 0) {\n emit({ kind: 'phase', phase: 'enrich' })\n const stats = await runEnrichment(\n state.importedIds.products,\n adapters.ai,\n services.product,\n (done, total) => emit({ kind: 'progress', entity: 'enrichment', done, total }),\n )\n Object.assign(enrichStats, stats)\n state.eventsEmitted += enrichStats.enriched\n }\n\n state.phase = 'done'\n await checkpoint()\n\n const report = buildReport(state, startMs)\n report.enriched = enrichStats\n return report\n}\n","import { readFile, writeFile, unlink } from 'node:fs/promises'\nimport type { ImportState } from './types.js'\n\nconst EMPTY_STATE: Omit<ImportState, 'startedAt' | 'statePath'> = {\n source: 'woocommerce',\n phase: 'connect',\n cursors: {},\n importedIds: { categories: [], products: [], customers: [], orders: [], reviews: [] },\n wcToNymbalCustomer: {},\n wcToNymbalProduct: {},\n wcToNymbalCategory: {},\n errors: [],\n eventsEmitted: 0,\n}\n\nexport async function loadState(statePath: string): Promise<ImportState | null> {\n try {\n const raw = await readFile(statePath, 'utf-8')\n return JSON.parse(raw) as ImportState\n } catch {\n return null\n }\n}\n\nexport function freshState(): ImportState {\n return { ...structuredClone(EMPTY_STATE), startedAt: new Date().toISOString() }\n}\n\nexport async function saveState(statePath: string, state: ImportState): Promise<void> {\n await writeFile(statePath, JSON.stringify(state, null, 2), 'utf-8')\n}\n\nexport async function deleteState(statePath: string): Promise<void> {\n try {\n await unlink(statePath)\n } catch {\n // file may not exist\n }\n}\n","import { writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { ImportReport, ImportState } from './types.js'\n\nexport function buildReport(state: ImportState, startMs: number): ImportReport {\n return {\n imported: {\n categories: state.importedIds.categories.length,\n products: state.importedIds.products.length,\n customers: state.importedIds.customers.length,\n orders: state.importedIds.orders.length,\n reviews: state.importedIds.reviews.length,\n },\n enriched: { enriched: 0, skipped: 0, failed: 0 },\n errors: state.errors,\n eventsEmitted: state.eventsEmitted,\n durationMs: Date.now() - startMs,\n }\n}\n\nexport function formatReport(report: ImportReport, storeName: string): string {\n const mins = Math.round(report.durationMs / 60000)\n const secs = Math.round((report.durationMs % 60000) / 1000)\n const duration = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`\n\n const lines: string[] = [\n `# Nymbal Migration Report — ${storeName}`,\n `_Generated ${new Date().toISOString()}_`,\n '',\n '## Import Summary',\n '',\n `| Entity | Imported |`,\n `|---|---|`,\n `| Categories | ${report.imported.categories} |`,\n `| Products | ${report.imported.products} |`,\n `| Customers | ${report.imported.customers} |`,\n `| Orders | ${report.imported.orders} |`,\n `| Reviews | ${report.imported.reviews} |`,\n '',\n '## AI Enrichment',\n '',\n `- Products enriched: **${report.enriched.enriched}**`,\n `- Products skipped (content sufficient): ${report.enriched.skipped}`,\n `- Enrichment failures: ${report.enriched.failed}`,\n '',\n '## Pipeline Stats',\n '',\n `- Events emitted: ${report.eventsEmitted}`,\n `- Duration: ${duration}`,\n '',\n ]\n\n if (report.errors.length > 0) {\n lines.push('## Errors Requiring Review', '')\n for (const e of report.errors) {\n lines.push(`- **${e.phase}**${e.id ? ` (${e.id})` : ''}: ${e.error}`)\n }\n lines.push('')\n }\n\n lines.push(\n '## Next Steps',\n '',\n '1. Run `nymbal dev` to preview your imported store',\n '2. Run `nymbal test:visual --update-baselines` to capture visual baselines',\n '3. Review any items listed under Errors above',\n ...(report.enriched.skipped > 0\n ? ['4. Products with sufficient content were skipped — review originals if needed']\n : []),\n )\n\n return lines.join('\\n')\n}\n\nexport async function writeReport(report: ImportReport, projectRoot: string, storeName: string): Promise<string> {\n const content = formatReport(report, storeName)\n const path = join(projectRoot, 'nymbal-migration-report.md')\n await writeFile(path, content, 'utf-8')\n return path\n}\n","import type { WooCommerceCredentials } from '../types.js'\n\nexport class WooCommerceApiError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message)\n this.name = 'WooCommerceApiError'\n }\n}\n\nexport interface WcPage<T> {\n items: T[]\n total: number\n totalPages: number\n}\n\nexport interface WcProduct {\n id: number\n name: string\n slug: string\n type: 'simple' | 'variable' | 'grouped' | 'external'\n status: string\n description: string\n short_description: string\n sku: string\n regular_price: string\n sale_price: string\n categories: Array<{ id: number; name: string; slug: string }>\n images: Array<{ id: number; src: string; name: string; alt: string }>\n attributes: Array<{ id: number; name: string; options: string[] }>\n variations: number[]\n meta_data: Array<{ key: string; value: unknown }>\n downloadable: boolean\n virtual: boolean\n weight: string\n dimensions: { length: string; width: string; height: string }\n yoast_head_json?: { title?: string; description?: string }\n}\n\nexport interface WcVariation {\n id: number\n sku: string\n regular_price: string\n sale_price: string\n stock_quantity: number | null\n status: string\n attributes: Array<{ id: number; name: string; option: string }>\n weight: string\n dimensions: { length: string; width: string; height: string }\n}\n\nexport interface WcCategory {\n id: number\n name: string\n slug: string\n parent: number\n description: string\n count: number\n}\n\nexport interface WcCustomer {\n id: number\n email: string\n first_name: string\n last_name: string\n username: string\n billing: WcAddress\n shipping: WcAddress\n meta_data: Array<{ key: string; value: unknown }>\n date_created: string\n}\n\nexport interface WcAddress {\n first_name: string\n last_name: string\n company: string\n address_1: string\n address_2: string\n city: string\n state: string\n postcode: string\n country: string\n phone: string\n email?: string\n}\n\nexport interface WcOrder {\n id: number\n number: string\n status: string\n currency: string\n date_created: string\n customer_id: number\n billing: WcAddress\n shipping: WcAddress\n line_items: Array<{\n id: number\n product_id: number\n variation_id: number\n name: string\n quantity: number\n price: number\n subtotal: string\n total: string\n sku: string\n }>\n shipping_lines: Array<{ method_title: string; total: string }>\n tax_lines: Array<{ label: string; tax_total: string }>\n discount_total: string\n shipping_total: string\n total_tax: string\n total: string\n customer_note: string\n meta_data: Array<{ key: string; value: unknown }>\n payment_method: string\n}\n\nexport interface WcReview {\n id: number\n product_id: number\n reviewer: string\n reviewer_email: string\n review: string\n rating: number\n verified: boolean\n date_created: string\n}\n\nexport class WooCommerceClient {\n private readonly base: string\n private readonly auth: string\n\n constructor(creds: WooCommerceCredentials) {\n this.base = creds.url.replace(/\\/$/, '') + '/wp-json/wc/v3'\n this.auth = `Basic ${Buffer.from(`${creds.consumerKey}:${creds.consumerSecret}`).toString('base64')}`\n }\n\n async healthCheck(): Promise<void> {\n const res = await this.fetchWithRetry(this.base + '/system_status')\n if (!res.ok) {\n throw new WooCommerceApiError(res.status, `Connection failed: ${res.status} ${res.statusText}`)\n }\n }\n\n async getTotal(resource: string, params: Record<string, string> = {}): Promise<number> {\n const url = this.buildUrl(resource, { ...params, per_page: '1', page: '1' })\n const res = await this.fetchWithRetry(url)\n return parseInt(res.headers.get('X-WP-Total') ?? '0', 10)\n }\n\n async *paginate<T>(\n resource: string,\n params: Record<string, string> = {},\n startPage = 1,\n ): AsyncGenerator<WcPage<T>> {\n let page = startPage\n while (true) {\n const url = this.buildUrl(resource, {\n ...params,\n per_page: '100',\n page: String(page),\n orderby: 'id',\n order: 'asc',\n })\n const res = await this.fetchWithRetry(url)\n if (!res.ok) {\n throw new WooCommerceApiError(res.status, `${resource} page ${page}: ${res.statusText}`)\n }\n const items = (await res.json()) as T[]\n const total = parseInt(res.headers.get('X-WP-Total') ?? '0', 10)\n const totalPages = parseInt(res.headers.get('X-WP-TotalPages') ?? '1', 10)\n yield { items, total, totalPages }\n if (page >= totalPages) break\n page++\n }\n }\n\n async getProductVariations(productId: number): Promise<WcVariation[]> {\n const results: WcVariation[] = []\n for await (const page of this.paginate<WcVariation>(`/products/${productId}/variations`)) {\n results.push(...page.items)\n }\n return results\n }\n\n private buildUrl(path: string, params: Record<string, string>): string {\n const url = new URL(this.base + path)\n for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v)\n return url.toString()\n }\n\n private async request(path: string): Promise<Response> {\n return fetch(this.base + path, { headers: { Authorization: this.auth } })\n }\n\n private async fetchWithRetry(url: string, attempt = 0): Promise<Response> {\n const res = await fetch(url, { headers: { Authorization: this.auth } })\n if (res.status === 429 && attempt < 3) {\n const retryAfter = parseInt(res.headers.get('Retry-After') ?? '5', 10)\n await sleep(retryAfter * 1000)\n return this.fetchWithRetry(url, attempt + 1)\n }\n if (res.status >= 500 && attempt < 2) {\n await sleep(1000 * (attempt + 1))\n return this.fetchWithRetry(url, attempt + 1)\n }\n return res\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms))\n}\n","import type { CategoryCreateInput } from '@nymbalhq/platform'\nimport type { WcCategory } from '../client.js'\n\nexport function mapCategory(\n wc: WcCategory,\n nymbalParentId: string | undefined,\n): CategoryCreateInput {\n const result: CategoryCreateInput = {\n slug: slugify(wc.slug || wc.name),\n name: wc.name,\n parentId: nymbalParentId ?? null,\n position: 0,\n }\n if (wc.description) result.description = wc.description\n return result\n}\n\nexport function slugify(s: string): string {\n return s\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 80)\n}\n","import type { ProductCreateInput } from '@nymbalhq/platform'\nimport type { WcProduct, WcVariation } from '../client.js'\nimport { slugify } from './category.js'\n\nconst PRICE_MINOR_FACTOR = 100\n\nfunction parsePriceMinor(price: string): number {\n const n = parseFloat(price)\n return isNaN(n) ? 0 : Math.round(n * PRICE_MINOR_FACTOR)\n}\n\nfunction parseWeightGrams(weight: string): number | null {\n const n = parseFloat(weight)\n return isNaN(n) || n <= 0 ? null : Math.round(n * 1000)\n}\n\nfunction parseDimensions(d: { length: string; width: string; height: string }) {\n const l = parseFloat(d.length)\n const w = parseFloat(d.width)\n const h = parseFloat(d.height)\n if ([l, w, h].some(isNaN)) return null\n // WooCommerce dimensions are in cm; Nymbal stores in mm\n return { lengthMm: Math.round(l * 10), widthMm: Math.round(w * 10), heightMm: Math.round(h * 10) }\n}\n\nfunction wcStatusToNymbal(status: string): 'active' | 'draft' {\n return status === 'publish' ? 'active' : 'draft'\n}\n\nexport function mapProduct(\n wc: WcProduct,\n variations: WcVariation[],\n categoryIds: string[],\n mediaUrls: Array<{ url: string; altText: string }>,\n slugSuffix: string,\n): ProductCreateInput {\n const slug = slugSuffix ? `${slugify(wc.slug || wc.name)}-${slugSuffix}` : slugify(wc.slug || wc.name)\n\n const seoTitle = wc.yoast_head_json?.title ?? wc.name\n const seoDescription = wc.yoast_head_json?.description ?? wc.short_description?.replace(/<[^>]+>/g, '') ?? ''\n\n const media = mediaUrls.map((m, i) => ({ url: m.url, altText: m.altText, position: i }))\n\n const metadata: Record<string, unknown> = {\n woocommerce: {\n id: wc.id,\n sku: wc.sku,\n syncedAt: new Date().toISOString(),\n virtual: wc.virtual,\n downloadable: wc.downloadable,\n },\n }\n\n if (wc.type === 'variable' && variations.length > 0) {\n const mappedVariants = variations.map((v) => {\n const optionName = v.attributes.map((a) => a.option).join(' / ')\n return {\n sku: v.sku || `${wc.sku || slug}-${v.id}`,\n name: optionName || `Variant ${v.id}`,\n priceMinor: parsePriceMinor(v.regular_price),\n compareAtPriceMinor: v.sale_price ? parsePriceMinor(v.regular_price) : null,\n weightGrams: parseWeightGrams(v.weight),\n dimensions: parseDimensions(v.dimensions),\n stock: v.stock_quantity ?? 0,\n lowStockThreshold: 5,\n options: v.attributes.map((a) => ({ name: a.name, value: a.option })),\n status: (v.status === 'publish' ? 'active' : 'inactive') as 'active' | 'inactive',\n }\n })\n return {\n slug,\n name: wc.name,\n description: stripHtml(wc.description),\n shortDescription: stripHtml(wc.short_description),\n status: wcStatusToNymbal(wc.status),\n type: 'variable',\n seoTitle,\n seoDescription,\n media,\n metadata,\n categoryIds,\n variants: mappedVariants,\n }\n }\n\n return {\n slug,\n name: wc.name,\n description: stripHtml(wc.description),\n shortDescription: stripHtml(wc.short_description),\n status: wcStatusToNymbal(wc.status),\n type: 'simple',\n seoTitle,\n seoDescription,\n media,\n metadata,\n categoryIds,\n variants: [\n {\n sku: wc.sku || slug,\n name: wc.name,\n priceMinor: parsePriceMinor(wc.regular_price),\n compareAtPriceMinor: wc.sale_price ? parsePriceMinor(wc.regular_price) : null,\n weightGrams: parseWeightGrams(wc.weight),\n dimensions: parseDimensions(wc.dimensions),\n stock: 0,\n lowStockThreshold: 5,\n options: [],\n status: 'active' as const,\n },\n ],\n }\n}\n\nexport function isThinContent(description: string): boolean {\n const text = description.replace(/<[^>]+>/g, '').trim()\n const wordCount = text.split(/\\s+/).filter(Boolean).length\n return wordCount < 50\n}\n\nfunction stripHtml(html: string): string {\n return html.replace(/<[^>]+>/g, '').trim()\n}\n","import { createHash } from 'node:crypto'\nimport { v7 as uuidv7 } from 'uuid'\nimport type { CustomerAddress } from '@nymbalhq/types'\nimport type { ImportCustomerInput } from '@nymbalhq/platform'\nimport type { WcCustomer, WcAddress } from '../client.js'\n\nfunction mapAddress(a: WcAddress): CustomerAddress {\n const addr: CustomerAddress = {\n id: uuidv7(),\n firstName: a.first_name,\n lastName: a.last_name,\n addressLine1: a.address_1 || '—',\n city: a.city,\n region: a.state,\n postalCode: a.postcode,\n country: a.country || 'US',\n isDefaultBilling: true,\n isDefaultShipping: false,\n }\n if (a.address_2) addr.addressLine2 = a.address_2\n if (a.phone) addr.phone = a.phone\n if (a.company) addr.company = a.company\n return addr\n}\n\nexport function mapCustomer(wc: WcCustomer, gdpr: boolean): ImportCustomerInput {\n const wcMeta: Record<string, unknown> = { customerId: wc.id, syncedAt: new Date().toISOString() }\n\n if (gdpr) {\n const hash = createHash('sha256').update(String(wc.id)).digest('hex').slice(0, 12)\n return {\n email: `customer-${hash}@imported.nymbal.local`,\n metadata: { woocommerce: wcMeta },\n }\n }\n\n const input: ImportCustomerInput = {\n email: wc.email,\n metadata: { woocommerce: wcMeta },\n }\n if (wc.first_name) input.firstName = wc.first_name\n if (wc.last_name) input.lastName = wc.last_name\n if (wc.billing.address_1) input.addresses = [mapAddress(wc.billing)]\n return input\n}\n","import type { Address, OrderLineItem, OrderStatus } from '@nymbalhq/types'\nimport type { OrderImportInput } from '@nymbalhq/platform'\nimport type { WcOrder, WcAddress } from '../client.js'\nimport { v7 as uuidv7 } from 'uuid'\n\nconst PRICE_MINOR_FACTOR = 100\n\nfunction toMinor(value: string | number): number {\n const n = typeof value === 'string' ? parseFloat(value) : value\n return isNaN(n) ? 0 : Math.round(n * PRICE_MINOR_FACTOR)\n}\n\nconst STATUS_MAP: Record<string, OrderStatus> = {\n pending: 'pending',\n processing: 'processing',\n 'on-hold': 'pending',\n completed: 'delivered',\n cancelled: 'cancelled',\n refunded: 'refunded',\n failed: 'cancelled',\n}\n\n/** WooCommerce statuses whose order was paid; a refunded order was paid, then refunded in full. */\nconst PAID_STATUSES = new Set(['processing', 'completed', 'refunded'])\n\nfunction mapAddress(a: WcAddress): Address {\n const addr: Address = {\n firstName: a.first_name,\n lastName: a.last_name,\n addressLine1: a.address_1 || '—',\n city: a.city || '—',\n region: a.state,\n postalCode: a.postcode,\n country: a.country || 'US',\n }\n if (a.address_2) addr.addressLine2 = a.address_2\n if (a.phone) addr.phone = a.phone\n if (a.company) addr.company = a.company\n return addr\n}\n\nexport function mapOrder(\n wc: WcOrder,\n nymbalCustomerId: string | null,\n nymbalProductIds: Record<number, string>,\n currency: string,\n): OrderImportInput {\n const status: OrderStatus = STATUS_MAP[wc.status] ?? 'pending'\n\n const lineItems: OrderLineItem[] = wc.line_items.map((li) => ({\n variantId: nymbalProductIds[li.product_id] ?? 'unknown',\n productId: nymbalProductIds[li.product_id] ?? 'unknown',\n productName: li.name,\n variantName: li.name,\n sku: li.sku,\n qty: li.quantity,\n unitPriceMinor: toMinor(li.price),\n lineSubtotalMinor: toMinor(li.subtotal),\n lineTaxMinor: 0,\n lineTotalMinor: toMinor(li.total),\n imageUrl: '',\n }))\n\n const shippingTotal = wc.shipping_lines.reduce((sum, s) => sum + toMinor(s.total), 0)\n const taxTotal = toMinor(wc.total_tax)\n const discountTotal = toMinor(wc.discount_total)\n const totalMinor = toMinor(wc.total)\n const subtotalMinor = totalMinor - taxTotal - shippingTotal + discountTotal\n\n const billing = mapAddress(wc.billing)\n const shipping = wc.shipping.address_1 ? mapAddress(wc.shipping) : billing\n const email = wc.billing.email ?? `wc-${wc.id}@unknown.nymbal.local`\n\n const result: OrderImportInput = {\n orderId: uuidv7(),\n orderNumber: `WC-${wc.number}`,\n status,\n customerId: nymbalCustomerId,\n email,\n billingAddress: billing,\n shippingAddress: shipping,\n lineItems,\n subtotalMinor,\n taxTotalMinor: taxTotal,\n shippingTotalMinor: shippingTotal,\n discountTotalMinor: discountTotal,\n totalMinor,\n currency: wc.currency || currency,\n metadata: {\n woocommerce: {\n orderId: wc.id,\n orderNumber: wc.number,\n originalStatus: wc.status,\n paymentMethod: wc.payment_method,\n syncedAt: new Date().toISOString(),\n },\n },\n createdAt: new Date(wc.date_created),\n }\n // A free order took no money: nothing captured.\n if (PAID_STATUSES.has(wc.status) && totalMinor > 0) {\n result.capturedMinor = totalMinor\n result.refundedMinor = wc.status === 'refunded' ? totalMinor : 0\n result.captureMethod = 'import'\n }\n if (wc.customer_note) result.notes = wc.customer_note\n return result\n}\n","import type { ReviewSubmission } from '@nymbalhq/types'\nimport type { WcReview } from '../client.js'\n\nexport function mapReview(\n wc: WcReview,\n nymbalProductId: string,\n nymbalCustomerId: string | null,\n): ReviewSubmission {\n return {\n productId: nymbalProductId,\n customerId: nymbalCustomerId,\n orderId: null,\n rating: Math.min(5, Math.max(1, wc.rating)),\n title: '',\n body: wc.review.replace(/<[^>]+>/g, '').trim(),\n }\n}\n","import { createHash } from 'node:crypto'\nimport { join } from 'node:path'\nimport type { MediaStorage } from '@nymbalhq/platform'\n\nconst TIMEOUT_MS = 15_000\nconst SUPPORTED_TYPES: Record<string, string> = {\n 'image/jpeg': 'jpg',\n 'image/png': 'png',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n}\n\nexport interface DownloadedMedia {\n url: string\n altText: string\n}\n\nexport async function downloadImage(\n src: string,\n altText: string,\n storage: MediaStorage,\n subdir: string,\n): Promise<DownloadedMedia | null> {\n try {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), TIMEOUT_MS)\n let res: Response\n try {\n res = await fetch(src, { signal: controller.signal })\n } finally {\n clearTimeout(timer)\n }\n if (!res.ok) return null\n\n const rawContentType = res.headers.get('content-type') ?? 'image/jpeg'\n const contentType = (rawContentType.split(';')[0] ?? 'image/jpeg').trim()\n const ext = SUPPORTED_TYPES[contentType] ?? 'jpg'\n const hash = createHash('sha256').update(src).digest('hex').slice(0, 16)\n const key = join(subdir, `${hash}.${ext}`)\n\n const buffer = await res.arrayBuffer()\n const { url } = await storage.put(key, new Uint8Array(buffer), contentType)\n return { url, altText: altText || '' }\n } catch {\n return null\n }\n}\n\nexport async function downloadProductImages(\n images: Array<{ src: string; alt: string }>,\n productSlug: string,\n storage: MediaStorage,\n): Promise<DownloadedMedia[]> {\n const results: DownloadedMedia[] = []\n for (const img of images) {\n if (!img.src) continue\n const downloaded = await downloadImage(img.src, img.alt, storage, `products/${productSlug}`)\n if (downloaded) {\n results.push(downloaded)\n }\n }\n return results\n}\n","import type { AiAdapter } from '@nymbalhq/types'\nimport type { ProductService } from '@nymbalhq/platform'\nimport { isThinContent } from './mappers/product.js'\n\nexport interface EnrichmentStats {\n enriched: number\n skipped: number\n failed: number\n}\n\nconst SYSTEM_PROMPT = `You are an expert e-commerce copywriter. Given a product name and any existing description, write compelling, accurate product copy. Return ONLY a JSON object with these exact fields:\n{\n \"description\": \"2-3 paragraph description (plain text, no HTML)\",\n \"shortDescription\": \"1-2 sentence summary\",\n \"seoTitle\": \"SEO-optimised title (50-60 chars)\",\n \"seoDescription\": \"Meta description (140-160 chars)\"\n}\nDo not add information you cannot infer. Be honest about the product.`\n\nexport async function enrichProduct(\n productId: string,\n productName: string,\n existingDescription: string,\n aiAdapter: AiAdapter,\n productService: ProductService,\n): Promise<boolean> {\n const prompt = `Product name: ${productName}\nExisting description: ${existingDescription || '(none)'}\n\nGenerate copy for this product.`\n\n let raw: string\n try {\n raw = await aiAdapter.complete(`${SYSTEM_PROMPT}\\n\\n${prompt}`, { maxTokens: 800 })\n } catch {\n return false\n }\n\n let parsed: Record<string, string>\n try {\n const match = /\\{[\\s\\S]*\\}/.exec(raw)\n if (!match?.[0]) return false\n parsed = JSON.parse(match[0]) as Record<string, string>\n } catch {\n return false\n }\n\n await productService.update(productId, {\n description: parsed['description'] ?? existingDescription,\n shortDescription: parsed['shortDescription'] ?? '',\n seoTitle: parsed['seoTitle'] ?? productName,\n seoDescription: parsed['seoDescription'] ?? '',\n metadata: {\n woocommerce: { enriched: true, enrichedAt: new Date().toISOString() },\n original: { description: existingDescription },\n },\n })\n return true\n}\n\nexport async function runEnrichment(\n productIds: string[],\n aiAdapter: AiAdapter,\n productService: ProductService,\n onProgress: (done: number, total: number) => void,\n): Promise<EnrichmentStats> {\n const stats: EnrichmentStats = { enriched: 0, skipped: 0, failed: 0 }\n let done = 0\n\n for (const id of productIds) {\n try {\n const snapshot = await productService.getById(id)\n const description = snapshot.description ?? ''\n\n if (!isThinContent(description)) {\n stats.skipped++\n } else {\n const ok = await enrichProduct(id, snapshot.name, description, aiAdapter, productService)\n if (ok) stats.enriched++\n else stats.failed++\n }\n } catch {\n stats.failed++\n }\n done++\n onProgress(done, productIds.length)\n }\n\n return stats\n}\n"],"mappings":"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","names":["join","writeFile","uuidv7","PRICE_MINOR_FACTOR","mapAddress","createHash","join","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/state.ts","../src/report.ts","../src/woocommerce/client.ts","../src/woocommerce/mappers/category.ts","../src/woocommerce/mappers/product.ts","../src/woocommerce/mappers/customer.ts","../src/woocommerce/mappers/order.ts","../src/woocommerce/mappers/review.ts","../src/woocommerce/media.ts","../src/woocommerce/enrichment.ts","../src/woocommerce/importer.ts"],"sourcesContent":["import { readFile, writeFile, unlink } from 'node:fs/promises'\nimport type { ImportState } from './types.js'\n\nconst EMPTY_STATE: Omit<ImportState, 'startedAt' | 'statePath'> = {\n source: 'woocommerce',\n phase: 'connect',\n cursors: {},\n importedIds: { categories: [], products: [], customers: [], orders: [], reviews: [] },\n wcToNymbalCustomer: {},\n wcToNymbalProduct: {},\n wcToNymbalCategory: {},\n errors: [],\n eventsEmitted: 0,\n}\n\nexport async function loadState(statePath: string): Promise<ImportState | null> {\n try {\n const raw = await readFile(statePath, 'utf-8')\n return JSON.parse(raw) as ImportState\n } catch {\n return null\n }\n}\n\nexport function freshState(): ImportState {\n return { ...structuredClone(EMPTY_STATE), startedAt: new Date().toISOString() }\n}\n\nexport async function saveState(statePath: string, state: ImportState): Promise<void> {\n await writeFile(statePath, JSON.stringify(state, null, 2), 'utf-8')\n}\n\nexport async function deleteState(statePath: string): Promise<void> {\n try {\n await unlink(statePath)\n } catch {\n // file may not exist\n }\n}\n","import { writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { ImportReport, ImportState } from './types.js'\n\nexport function buildReport(state: ImportState, startMs: number): ImportReport {\n return {\n imported: {\n categories: state.importedIds.categories.length,\n products: state.importedIds.products.length,\n customers: state.importedIds.customers.length,\n orders: state.importedIds.orders.length,\n reviews: state.importedIds.reviews.length,\n },\n enriched: { enriched: 0, skipped: 0, failed: 0 },\n errors: state.errors,\n eventsEmitted: state.eventsEmitted,\n durationMs: Date.now() - startMs,\n }\n}\n\nexport function formatReport(report: ImportReport, storeName: string): string {\n const mins = Math.round(report.durationMs / 60000)\n const secs = Math.round((report.durationMs % 60000) / 1000)\n const duration = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`\n\n const lines: string[] = [\n `# Nymbal Migration Report — ${storeName}`,\n `_Generated ${new Date().toISOString()}_`,\n '',\n '## Import Summary',\n '',\n `| Entity | Imported |`,\n `|---|---|`,\n `| Categories | ${report.imported.categories} |`,\n `| Products | ${report.imported.products} |`,\n `| Customers | ${report.imported.customers} |`,\n `| Orders | ${report.imported.orders} |`,\n `| Reviews | ${report.imported.reviews} |`,\n '',\n '## AI Enrichment',\n '',\n `- Products enriched: **${report.enriched.enriched}**`,\n `- Products skipped (content sufficient): ${report.enriched.skipped}`,\n `- Enrichment failures: ${report.enriched.failed}`,\n '',\n '## Pipeline Stats',\n '',\n `- Events emitted: ${report.eventsEmitted}`,\n `- Duration: ${duration}`,\n '',\n ]\n\n if (report.errors.length > 0) {\n lines.push('## Errors Requiring Review', '')\n for (const e of report.errors) {\n lines.push(`- **${e.phase}**${e.id ? ` (${e.id})` : ''}: ${e.error}`)\n }\n lines.push('')\n }\n\n lines.push(\n '## Next Steps',\n '',\n '1. Run `nymbal dev` to preview your imported store',\n '2. Run `nymbal test:visual --update-baselines` to capture visual baselines',\n '3. Review any items listed under Errors above',\n ...(report.enriched.skipped > 0\n ? ['4. Products with sufficient content were skipped — review originals if needed']\n : []),\n )\n\n return lines.join('\\n')\n}\n\nexport async function writeReport(report: ImportReport, projectRoot: string, storeName: string): Promise<string> {\n const content = formatReport(report, storeName)\n const path = join(projectRoot, 'nymbal-migration-report.md')\n await writeFile(path, content, 'utf-8')\n return path\n}\n","import type { WooCommerceCredentials } from '../types.js'\n\nexport class WooCommerceApiError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message)\n this.name = 'WooCommerceApiError'\n }\n}\n\nexport interface WcPage<T> {\n items: T[]\n total: number\n totalPages: number\n}\n\nexport interface WcProduct {\n id: number\n name: string\n slug: string\n type: 'simple' | 'variable' | 'grouped' | 'external'\n status: string\n description: string\n short_description: string\n sku: string\n regular_price: string\n sale_price: string\n categories: Array<{ id: number; name: string; slug: string }>\n images: Array<{ id: number; src: string; name: string; alt: string }>\n attributes: Array<{ id: number; name: string; options: string[] }>\n variations: number[]\n meta_data: Array<{ key: string; value: unknown }>\n downloadable: boolean\n virtual: boolean\n weight: string\n dimensions: { length: string; width: string; height: string }\n yoast_head_json?: { title?: string; description?: string }\n}\n\nexport interface WcVariation {\n id: number\n sku: string\n regular_price: string\n sale_price: string\n stock_quantity: number | null\n status: string\n attributes: Array<{ id: number; name: string; option: string }>\n weight: string\n dimensions: { length: string; width: string; height: string }\n}\n\nexport interface WcCategory {\n id: number\n name: string\n slug: string\n parent: number\n description: string\n count: number\n}\n\nexport interface WcCustomer {\n id: number\n email: string\n first_name: string\n last_name: string\n username: string\n billing: WcAddress\n shipping: WcAddress\n meta_data: Array<{ key: string; value: unknown }>\n date_created: string\n}\n\nexport interface WcAddress {\n first_name: string\n last_name: string\n company: string\n address_1: string\n address_2: string\n city: string\n state: string\n postcode: string\n country: string\n phone: string\n email?: string\n}\n\nexport interface WcOrder {\n id: number\n number: string\n status: string\n currency: string\n date_created: string\n customer_id: number\n billing: WcAddress\n shipping: WcAddress\n line_items: Array<{\n id: number\n product_id: number\n variation_id: number\n name: string\n quantity: number\n price: number\n subtotal: string\n total: string\n sku: string\n }>\n shipping_lines: Array<{ method_title: string; total: string }>\n tax_lines: Array<{ label: string; tax_total: string }>\n discount_total: string\n shipping_total: string\n total_tax: string\n total: string\n customer_note: string\n meta_data: Array<{ key: string; value: unknown }>\n payment_method: string\n}\n\nexport interface WcReview {\n id: number\n product_id: number\n reviewer: string\n reviewer_email: string\n review: string\n rating: number\n verified: boolean\n date_created: string\n}\n\nexport class WooCommerceClient {\n private readonly base: string\n private readonly auth: string\n\n constructor(creds: WooCommerceCredentials) {\n this.base = creds.url.replace(/\\/$/, '') + '/wp-json/wc/v3'\n this.auth = `Basic ${Buffer.from(`${creds.consumerKey}:${creds.consumerSecret}`).toString('base64')}`\n }\n\n async healthCheck(): Promise<void> {\n const res = await this.fetchWithRetry(this.base + '/system_status')\n if (!res.ok) {\n throw new WooCommerceApiError(res.status, `Connection failed: ${res.status} ${res.statusText}`)\n }\n }\n\n async getTotal(resource: string, params: Record<string, string> = {}): Promise<number> {\n const url = this.buildUrl(resource, { ...params, per_page: '1', page: '1' })\n const res = await this.fetchWithRetry(url)\n return parseInt(res.headers.get('X-WP-Total') ?? '0', 10)\n }\n\n async *paginate<T>(\n resource: string,\n params: Record<string, string> = {},\n startPage = 1,\n ): AsyncGenerator<WcPage<T>> {\n let page = startPage\n while (true) {\n const url = this.buildUrl(resource, {\n ...params,\n per_page: '100',\n page: String(page),\n orderby: 'id',\n order: 'asc',\n })\n const res = await this.fetchWithRetry(url)\n if (!res.ok) {\n throw new WooCommerceApiError(res.status, `${resource} page ${page}: ${res.statusText}`)\n }\n const items = (await res.json()) as T[]\n const total = parseInt(res.headers.get('X-WP-Total') ?? '0', 10)\n const totalPages = parseInt(res.headers.get('X-WP-TotalPages') ?? '1', 10)\n yield { items, total, totalPages }\n if (page >= totalPages) break\n page++\n }\n }\n\n async getProductVariations(productId: number): Promise<WcVariation[]> {\n const results: WcVariation[] = []\n for await (const page of this.paginate<WcVariation>(`/products/${productId}/variations`)) {\n results.push(...page.items)\n }\n return results\n }\n\n private buildUrl(path: string, params: Record<string, string>): string {\n const url = new URL(this.base + path)\n for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v)\n return url.toString()\n }\n\n private async request(path: string): Promise<Response> {\n return fetch(this.base + path, { headers: { Authorization: this.auth } })\n }\n\n private async fetchWithRetry(url: string, attempt = 0): Promise<Response> {\n const res = await fetch(url, { headers: { Authorization: this.auth } })\n if (res.status === 429 && attempt < 3) {\n const retryAfter = parseInt(res.headers.get('Retry-After') ?? '5', 10)\n await sleep(retryAfter * 1000)\n return this.fetchWithRetry(url, attempt + 1)\n }\n if (res.status >= 500 && attempt < 2) {\n await sleep(1000 * (attempt + 1))\n return this.fetchWithRetry(url, attempt + 1)\n }\n return res\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms))\n}\n","import type { CategoryCreateInput } from '@nymbalhq/platform'\nimport type { WcCategory } from '../client.js'\n\nexport function mapCategory(\n wc: WcCategory,\n nymbalParentId: string | undefined,\n): CategoryCreateInput {\n const result: CategoryCreateInput = {\n slug: slugify(wc.slug || wc.name),\n name: wc.name,\n parentId: nymbalParentId ?? null,\n position: 0,\n }\n if (wc.description) result.description = wc.description\n return result\n}\n\nexport function slugify(s: string): string {\n return s\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 80)\n}\n","import type { ProductCreateInput } from '@nymbalhq/platform'\nimport type { WcProduct, WcVariation } from '../client.js'\nimport { slugify } from './category.js'\n\nconst PRICE_MINOR_FACTOR = 100\n\nfunction parsePriceMinor(price: string): number {\n const n = parseFloat(price)\n return isNaN(n) ? 0 : Math.round(n * PRICE_MINOR_FACTOR)\n}\n\nfunction parseWeightGrams(weight: string): number | null {\n const n = parseFloat(weight)\n return isNaN(n) || n <= 0 ? null : Math.round(n * 1000)\n}\n\nfunction parseDimensions(d: { length: string; width: string; height: string }) {\n const l = parseFloat(d.length)\n const w = parseFloat(d.width)\n const h = parseFloat(d.height)\n if ([l, w, h].some(isNaN)) return null\n // WooCommerce dimensions are in cm; Nymbal stores in mm\n return { lengthMm: Math.round(l * 10), widthMm: Math.round(w * 10), heightMm: Math.round(h * 10) }\n}\n\nfunction wcStatusToNymbal(status: string): 'active' | 'draft' {\n return status === 'publish' ? 'active' : 'draft'\n}\n\nexport function mapProduct(\n wc: WcProduct,\n variations: WcVariation[],\n categoryIds: string[],\n mediaUrls: Array<{ url: string; altText: string }>,\n slugSuffix: string,\n): ProductCreateInput {\n const slug = slugSuffix ? `${slugify(wc.slug || wc.name)}-${slugSuffix}` : slugify(wc.slug || wc.name)\n\n const seoTitle = wc.yoast_head_json?.title ?? wc.name\n const seoDescription = wc.yoast_head_json?.description ?? wc.short_description?.replace(/<[^>]+>/g, '') ?? ''\n\n const media = mediaUrls.map((m, i) => ({ url: m.url, altText: m.altText, position: i }))\n\n const metadata: Record<string, unknown> = {\n woocommerce: {\n id: wc.id,\n sku: wc.sku,\n syncedAt: new Date().toISOString(),\n virtual: wc.virtual,\n downloadable: wc.downloadable,\n },\n }\n\n if (wc.type === 'variable' && variations.length > 0) {\n const mappedVariants = variations.map((v) => {\n const optionName = v.attributes.map((a) => a.option).join(' / ')\n return {\n sku: v.sku || `${wc.sku || slug}-${v.id}`,\n name: optionName || `Variant ${v.id}`,\n priceMinor: parsePriceMinor(v.regular_price),\n compareAtPriceMinor: v.sale_price ? parsePriceMinor(v.regular_price) : null,\n weightGrams: parseWeightGrams(v.weight),\n dimensions: parseDimensions(v.dimensions),\n stock: v.stock_quantity ?? 0,\n lowStockThreshold: 5,\n options: v.attributes.map((a) => ({ name: a.name, value: a.option })),\n status: (v.status === 'publish' ? 'active' : 'inactive') as 'active' | 'inactive',\n }\n })\n return {\n slug,\n name: wc.name,\n description: stripHtml(wc.description),\n shortDescription: stripHtml(wc.short_description),\n status: wcStatusToNymbal(wc.status),\n type: 'variable',\n seoTitle,\n seoDescription,\n media,\n metadata,\n categoryIds,\n variants: mappedVariants,\n }\n }\n\n return {\n slug,\n name: wc.name,\n description: stripHtml(wc.description),\n shortDescription: stripHtml(wc.short_description),\n status: wcStatusToNymbal(wc.status),\n type: 'simple',\n seoTitle,\n seoDescription,\n media,\n metadata,\n categoryIds,\n variants: [\n {\n sku: wc.sku || slug,\n name: wc.name,\n priceMinor: parsePriceMinor(wc.regular_price),\n compareAtPriceMinor: wc.sale_price ? parsePriceMinor(wc.regular_price) : null,\n weightGrams: parseWeightGrams(wc.weight),\n dimensions: parseDimensions(wc.dimensions),\n stock: 0,\n lowStockThreshold: 5,\n options: [],\n status: 'active' as const,\n },\n ],\n }\n}\n\nexport function isThinContent(description: string): boolean {\n const text = description.replace(/<[^>]+>/g, '').trim()\n const wordCount = text.split(/\\s+/).filter(Boolean).length\n return wordCount < 50\n}\n\nfunction stripHtml(html: string): string {\n return html.replace(/<[^>]+>/g, '').trim()\n}\n","import { createHash } from 'node:crypto'\nimport { v7 as uuidv7 } from 'uuid'\nimport type { CustomerAddress } from '@nymbalhq/types'\nimport type { ImportCustomerInput } from '@nymbalhq/platform'\nimport type { WcCustomer, WcAddress } from '../client.js'\n\nfunction mapAddress(a: WcAddress): CustomerAddress {\n const addr: CustomerAddress = {\n id: uuidv7(),\n firstName: a.first_name,\n lastName: a.last_name,\n addressLine1: a.address_1 || '—',\n city: a.city,\n region: a.state,\n postalCode: a.postcode,\n country: a.country || 'US',\n isDefaultBilling: true,\n isDefaultShipping: false,\n }\n if (a.address_2) addr.addressLine2 = a.address_2\n if (a.phone) addr.phone = a.phone\n if (a.company) addr.company = a.company\n return addr\n}\n\nexport function mapCustomer(wc: WcCustomer, gdpr: boolean): ImportCustomerInput {\n const wcMeta: Record<string, unknown> = { customerId: wc.id, syncedAt: new Date().toISOString() }\n\n if (gdpr) {\n const hash = createHash('sha256').update(String(wc.id)).digest('hex').slice(0, 12)\n return {\n email: `customer-${hash}@imported.nymbal.local`,\n metadata: { woocommerce: wcMeta },\n }\n }\n\n const input: ImportCustomerInput = {\n email: wc.email,\n metadata: { woocommerce: wcMeta },\n }\n if (wc.first_name) input.firstName = wc.first_name\n if (wc.last_name) input.lastName = wc.last_name\n if (wc.billing.address_1) input.addresses = [mapAddress(wc.billing)]\n return input\n}\n","import type { Address, OrderLineItem, OrderStatus } from '@nymbalhq/types'\nimport type { OrderImportInput } from '@nymbalhq/platform'\nimport type { WcOrder, WcAddress } from '../client.js'\nimport { v7 as uuidv7 } from 'uuid'\n\nconst PRICE_MINOR_FACTOR = 100\n\nfunction toMinor(value: string | number): number {\n const n = typeof value === 'string' ? parseFloat(value) : value\n return isNaN(n) ? 0 : Math.round(n * PRICE_MINOR_FACTOR)\n}\n\nconst STATUS_MAP: Record<string, OrderStatus> = {\n pending: 'pending',\n processing: 'processing',\n 'on-hold': 'pending',\n completed: 'delivered',\n cancelled: 'cancelled',\n refunded: 'refunded',\n failed: 'cancelled',\n}\n\n/** WooCommerce statuses whose order was paid; a refunded order was paid, then refunded in full. */\nconst PAID_STATUSES = new Set(['processing', 'completed', 'refunded'])\n\nfunction mapAddress(a: WcAddress): Address {\n const addr: Address = {\n firstName: a.first_name,\n lastName: a.last_name,\n addressLine1: a.address_1 || '—',\n city: a.city || '—',\n region: a.state,\n postalCode: a.postcode,\n country: a.country || 'US',\n }\n if (a.address_2) addr.addressLine2 = a.address_2\n if (a.phone) addr.phone = a.phone\n if (a.company) addr.company = a.company\n return addr\n}\n\nexport function mapOrder(\n wc: WcOrder,\n nymbalCustomerId: string | null,\n nymbalProductIds: Record<number, string>,\n currency: string,\n): OrderImportInput {\n const status: OrderStatus = STATUS_MAP[wc.status] ?? 'pending'\n\n const lineItems: OrderLineItem[] = wc.line_items.map((li) => ({\n variantId: nymbalProductIds[li.product_id] ?? 'unknown',\n productId: nymbalProductIds[li.product_id] ?? 'unknown',\n productName: li.name,\n variantName: li.name,\n sku: li.sku,\n qty: li.quantity,\n unitPriceMinor: toMinor(li.price),\n lineSubtotalMinor: toMinor(li.subtotal),\n lineTaxMinor: 0,\n lineTotalMinor: toMinor(li.total),\n imageUrl: '',\n }))\n\n const shippingTotal = wc.shipping_lines.reduce((sum, s) => sum + toMinor(s.total), 0)\n const taxTotal = toMinor(wc.total_tax)\n const discountTotal = toMinor(wc.discount_total)\n const totalMinor = toMinor(wc.total)\n const subtotalMinor = totalMinor - taxTotal - shippingTotal + discountTotal\n\n const billing = mapAddress(wc.billing)\n const shipping = wc.shipping.address_1 ? mapAddress(wc.shipping) : billing\n const email = wc.billing.email ?? `wc-${wc.id}@unknown.nymbal.local`\n\n const result: OrderImportInput = {\n orderId: uuidv7(),\n orderNumber: `WC-${wc.number}`,\n status,\n customerId: nymbalCustomerId,\n email,\n billingAddress: billing,\n shippingAddress: shipping,\n lineItems,\n subtotalMinor,\n taxTotalMinor: taxTotal,\n shippingTotalMinor: shippingTotal,\n discountTotalMinor: discountTotal,\n totalMinor,\n currency: wc.currency || currency,\n metadata: {\n woocommerce: {\n orderId: wc.id,\n orderNumber: wc.number,\n originalStatus: wc.status,\n paymentMethod: wc.payment_method,\n syncedAt: new Date().toISOString(),\n },\n },\n createdAt: new Date(wc.date_created),\n }\n // A free order took no money: nothing captured.\n if (PAID_STATUSES.has(wc.status) && totalMinor > 0) {\n result.capturedMinor = totalMinor\n result.refundedMinor = wc.status === 'refunded' ? totalMinor : 0\n result.captureMethod = 'import'\n }\n if (wc.customer_note) result.notes = wc.customer_note\n return result\n}\n","import type { ReviewSubmission } from '@nymbalhq/types'\nimport type { WcReview } from '../client.js'\n\nexport function mapReview(\n wc: WcReview,\n nymbalProductId: string,\n nymbalCustomerId: string | null,\n): ReviewSubmission {\n return {\n productId: nymbalProductId,\n customerId: nymbalCustomerId,\n orderId: null,\n rating: Math.min(5, Math.max(1, wc.rating)),\n title: '',\n body: wc.review.replace(/<[^>]+>/g, '').trim(),\n }\n}\n","import { createHash } from 'node:crypto'\nimport { join } from 'node:path'\nimport type { MediaStorage } from '@nymbalhq/types'\n\nconst TIMEOUT_MS = 15_000\nconst SUPPORTED_TYPES: Record<string, string> = {\n 'image/jpeg': 'jpg',\n 'image/png': 'png',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n}\n\nexport interface DownloadedMedia {\n url: string\n altText: string\n}\n\n/**\n * The image at `src`, stored. A source that cannot be fetched or answers non-2xx is skipped (null); a `put` that rejects\n * is the store's failure, so it is thrown for the caller to report.\n */\nexport async function downloadImage(\n src: string,\n altText: string,\n storage: MediaStorage,\n subdir: string,\n): Promise<DownloadedMedia | null> {\n let body: Uint8Array\n let contentType: string\n try {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), TIMEOUT_MS)\n let res: Response\n try {\n res = await fetch(src, { signal: controller.signal })\n } finally {\n clearTimeout(timer)\n }\n if (!res.ok) return null\n\n const rawContentType = res.headers.get('content-type') ?? 'image/jpeg'\n contentType = (rawContentType.split(';')[0] ?? 'image/jpeg').trim()\n body = new Uint8Array(await res.arrayBuffer())\n } catch {\n return null\n }\n const ext = SUPPORTED_TYPES[contentType] ?? 'jpg'\n const hash = createHash('sha256').update(src).digest('hex').slice(0, 16)\n const key = join(subdir, `${hash}.${ext}`)\n\n const { url } = await storage.put(key, body, contentType)\n return { url, altText: altText || '' }\n}\n\n/** Each image stored, in order; one whose `put` rejects is left out and passed to `onStoreError`. */\nexport async function downloadProductImages(\n images: Array<{ src: string; alt: string }>,\n productSlug: string,\n storage: MediaStorage,\n onStoreError: (error: unknown) => void,\n): Promise<DownloadedMedia[]> {\n const results: DownloadedMedia[] = []\n for (const img of images) {\n if (!img.src) continue\n let downloaded: DownloadedMedia | null\n try {\n downloaded = await downloadImage(img.src, img.alt, storage, `products/${productSlug}`)\n } catch (error) {\n onStoreError(error)\n continue\n }\n if (downloaded) {\n results.push(downloaded)\n }\n }\n return results\n}\n","import type { AiAdapter } from '@nymbalhq/types'\nimport type { ProductService } from '@nymbalhq/platform'\nimport { isThinContent } from './mappers/product.js'\n\nexport interface EnrichmentStats {\n enriched: number\n skipped: number\n failed: number\n}\n\nconst SYSTEM_PROMPT = `You are an expert e-commerce copywriter. Given a product name and any existing description, write compelling, accurate product copy. Return ONLY a JSON object with these exact fields:\n{\n \"description\": \"2-3 paragraph description (plain text, no HTML)\",\n \"shortDescription\": \"1-2 sentence summary\",\n \"seoTitle\": \"SEO-optimised title (50-60 chars)\",\n \"seoDescription\": \"Meta description (140-160 chars)\"\n}\nDo not add information you cannot infer. Be honest about the product.`\n\nexport async function enrichProduct(\n productId: string,\n productName: string,\n existingDescription: string,\n aiAdapter: AiAdapter,\n productService: ProductService,\n): Promise<boolean> {\n const prompt = `Product name: ${productName}\nExisting description: ${existingDescription || '(none)'}\n\nGenerate copy for this product.`\n\n let raw: string\n try {\n raw = await aiAdapter.complete(`${SYSTEM_PROMPT}\\n\\n${prompt}`, { maxTokens: 800 })\n } catch {\n return false\n }\n\n let parsed: Record<string, string>\n try {\n const match = /\\{[\\s\\S]*\\}/.exec(raw)\n if (!match?.[0]) return false\n parsed = JSON.parse(match[0]) as Record<string, string>\n } catch {\n return false\n }\n\n await productService.update(productId, {\n description: parsed['description'] ?? existingDescription,\n shortDescription: parsed['shortDescription'] ?? '',\n seoTitle: parsed['seoTitle'] ?? productName,\n seoDescription: parsed['seoDescription'] ?? '',\n metadata: {\n woocommerce: { enriched: true, enrichedAt: new Date().toISOString() },\n original: { description: existingDescription },\n },\n })\n return true\n}\n\nexport async function runEnrichment(\n productIds: string[],\n aiAdapter: AiAdapter,\n productService: ProductService,\n onProgress: (done: number, total: number) => void,\n): Promise<EnrichmentStats> {\n const stats: EnrichmentStats = { enriched: 0, skipped: 0, failed: 0 }\n let done = 0\n\n for (const id of productIds) {\n try {\n const snapshot = await productService.getById(id)\n const description = snapshot.description ?? ''\n\n if (!isThinContent(description)) {\n stats.skipped++\n } else {\n const ok = await enrichProduct(id, snapshot.name, description, aiAdapter, productService)\n if (ok) stats.enriched++\n else stats.failed++\n }\n } catch {\n stats.failed++\n }\n done++\n onProgress(done, productIds.length)\n }\n\n return stats\n}\n","import type { AiAdapter, MediaStorage } from '@nymbalhq/types'\nimport type {\n ServiceRegistry,\n Repositories,\n} from '@nymbalhq/platform'\nimport type { WooCommerceImportOptions, ImportState, ProgressCallback } from '../types.js'\nimport { loadState, freshState, saveState } from '../state.js'\nimport { buildReport } from '../report.js'\nimport { WooCommerceClient, WooCommerceApiError } from './client.js'\nimport type { WcCategory, WcProduct, WcVariation, WcCustomer, WcOrder, WcReview } from './client.js'\nimport { mapCategory } from './mappers/category.js'\nimport { mapProduct } from './mappers/product.js'\nimport { mapCustomer } from './mappers/customer.js'\nimport { mapOrder } from './mappers/order.js'\nimport { mapReview } from './mappers/review.js'\nimport { downloadProductImages } from './media.js'\nimport { runEnrichment } from './enrichment.js'\nimport type { ReviewsAdapter } from '@nymbalhq/types'\n\nexport interface WooCommerceImporterDeps {\n services: ServiceRegistry\n repos: Repositories\n /** `ai` is null when the store's ai provider is `none`, and enrichment is skipped. */\n adapters: { ai: AiAdapter | null; reviews: ReviewsAdapter }\n /** The store's media storage (`app.platform.media`): every product image is put here. */\n media: MediaStorage\n onProgress?: ProgressCallback\n}\n\nasync function pMap<T, U>(\n items: T[],\n fn: (item: T, index: number) => Promise<U>,\n concurrency: number,\n): Promise<Array<U | Error>> {\n const results: Array<U | Error> = new Array(items.length)\n let idx = 0\n async function worker() {\n while (idx < items.length) {\n const i = idx++\n const item = items[i]\n if (item === undefined) continue\n try {\n results[i] = await fn(item, i)\n } catch (err) {\n results[i] = err instanceof Error ? err : new Error(String(err))\n }\n }\n }\n await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, worker))\n return results\n}\n\nexport async function runWooCommerceImport(\n opts: WooCommerceImportOptions,\n deps: WooCommerceImporterDeps,\n) {\n const startMs = Date.now()\n const { services, adapters, media, onProgress } = deps\n const emit = onProgress ?? (() => undefined)\n\n const client = new WooCommerceClient(opts.credentials)\n\n // Load or create state\n let state: ImportState = opts.fresh\n ? freshState()\n : ((await loadState(opts.statePath)) ?? freshState())\n\n const checkpoint = async () => saveState(opts.statePath, state)\n\n // ── 1. Connect ──────────────────────────────────────────────────────────────\n emit({ kind: 'phase', phase: 'connect' })\n try {\n await client.healthCheck()\n } catch (err) {\n if (err instanceof WooCommerceApiError) throw err\n throw new Error(`Cannot connect to ${opts.credentials.url}: ${String(err)}`)\n }\n\n // ── 2. Categories ────────────────────────────────────────────────────────────\n if (opts.entities.includes('categories') && state.phase !== 'done') {\n emit({ kind: 'phase', phase: 'categories' })\n state.phase = 'categories'\n\n const startPage = state.cursors['categories']?.page ?? 1\n let done = state.importedIds.categories.length\n let total = 0\n\n for await (const page of client.paginate<WcCategory>('/products/categories', {}, startPage)) {\n total = page.total\n\n for (const wc of page.items) {\n if (state.wcToNymbalCategory[wc.id]) continue\n const nymbalParentId = wc.parent ? state.wcToNymbalCategory[wc.parent] : undefined\n try {\n const category = await services.category.create(mapCategory(wc, nymbalParentId))\n state.wcToNymbalCategory[wc.id] = category.id\n state.importedIds.categories.push(category.id)\n state.eventsEmitted++\n } catch (err) {\n state.errors.push({ phase: 'categories', id: String(wc.id), error: String(err) })\n }\n done++\n emit({ kind: 'progress', entity: 'categories', done, total })\n }\n\n state.cursors['categories'] = { page: state.cursors['categories']?.page ?? 1 }\n await checkpoint()\n }\n }\n\n // ── 3. Products ──────────────────────────────────────────────────────────────\n if (opts.entities.includes('products') && state.phase !== 'done') {\n emit({ kind: 'phase', phase: 'products' })\n state.phase = 'products'\n\n const startPage = state.cursors['products']?.page ?? 1\n let done = state.importedIds.products.length\n let total = 0\n\n const seenSlugs = new Set<string>()\n\n for await (const page of client.paginate<WcProduct>('/products', {}, startPage)) {\n total = page.total\n\n await pMap(\n page.items,\n async (wc) => {\n if (state.wcToNymbalProduct[wc.id]) return\n\n let variations: WcVariation[] = []\n if (wc.type === 'variable' && wc.variations.length > 0) {\n try {\n variations = await client.getProductVariations(wc.id)\n } catch {\n // proceed with no variations\n }\n }\n\n const categoryIds = wc.categories\n .map((c) => state.wcToNymbalCategory[c.id])\n .filter((id): id is string => Boolean(id))\n\n const slug = wc.slug || wc.name.toLowerCase().replace(/[^a-z0-9]+/g, '-')\n const suffix = seenSlugs.has(slug) ? String(wc.id) : ''\n seenSlugs.add(slug)\n\n const mediaItems = await downloadProductImages(wc.images, slug, media, (err) => {\n state.errors.push({ phase: 'media', id: String(wc.id), error: String(err) })\n emit({ kind: 'error', phase: 'media', id: String(wc.id), message: String(err) })\n })\n\n try {\n const snap = await services.product.create(\n mapProduct(wc, variations, categoryIds, mediaItems, suffix),\n )\n state.wcToNymbalProduct[wc.id] = snap.id\n state.importedIds.products.push(snap.id)\n state.eventsEmitted++\n } catch (err) {\n state.errors.push({ phase: 'products', id: String(wc.id), error: String(err) })\n }\n },\n opts.concurrency,\n )\n\n done += page.items.length\n emit({ kind: 'progress', entity: 'products', done, total })\n state.cursors['products'] = { page: (state.cursors['products']?.page ?? 1) + 1 }\n await checkpoint()\n }\n }\n\n // ── 4. Customers ─────────────────────────────────────────────────────────────\n if (opts.entities.includes('customers') && state.phase !== 'done') {\n emit({ kind: 'phase', phase: 'customers' })\n state.phase = 'customers'\n\n const startPage = state.cursors['customers']?.page ?? 1\n let done = state.importedIds.customers.length\n let total = 0\n\n for await (const page of client.paginate<WcCustomer>('/customers', {}, startPage)) {\n total = page.total\n\n await pMap(\n page.items,\n async (wc) => {\n if (state.wcToNymbalCustomer[wc.id]) return\n try {\n const customer = await services.auth.importCustomer(mapCustomer(wc, opts.gdpr))\n state.wcToNymbalCustomer[wc.id] = customer.id\n state.importedIds.customers.push(customer.id)\n state.eventsEmitted++\n } catch (err) {\n state.errors.push({ phase: 'customers', id: String(wc.id), error: String(err) })\n }\n },\n opts.concurrency,\n )\n\n done += page.items.length\n emit({ kind: 'progress', entity: 'customers', done, total })\n state.cursors['customers'] = { page: (state.cursors['customers']?.page ?? 1) + 1 }\n await checkpoint()\n }\n }\n\n // ── 5. Orders ────────────────────────────────────────────────────────────────\n if (opts.entities.includes('orders') && state.phase !== 'done') {\n emit({ kind: 'phase', phase: 'orders' })\n state.phase = 'orders'\n\n const startPage = state.cursors['orders']?.page ?? 1\n let done = state.importedIds.orders.length\n let total = 0\n\n for await (const page of client.paginate<WcOrder>('/orders', {}, startPage)) {\n total = page.total\n\n await pMap(\n page.items,\n async (wc) => {\n if (state.wcToNymbalProduct[wc.id]) return\n const nymbalCustomerId = wc.customer_id\n ? (state.wcToNymbalCustomer[wc.customer_id] ?? null)\n : null\n const productIdMap: Record<number, string> = {}\n for (const li of wc.line_items) {\n const nid = state.wcToNymbalProduct[li.product_id]\n if (nid) productIdMap[li.product_id] = nid\n }\n try {\n const order = await services.order.importOrder(\n mapOrder(wc, nymbalCustomerId, productIdMap, opts.currency),\n )\n state.importedIds.orders.push(order.id)\n state.eventsEmitted++\n } catch (err) {\n state.errors.push({ phase: 'orders', id: String(wc.id), error: String(err) })\n }\n },\n opts.concurrency,\n )\n\n done += page.items.length\n emit({ kind: 'progress', entity: 'orders', done, total })\n state.cursors['orders'] = { page: (state.cursors['orders']?.page ?? 1) + 1 }\n await checkpoint()\n }\n }\n\n // ── 6. Reviews ───────────────────────────────────────────────────────────────\n if (opts.entities.includes('reviews') && state.phase !== 'done') {\n emit({ kind: 'phase', phase: 'reviews' })\n state.phase = 'reviews'\n\n const startPage = state.cursors['reviews']?.page ?? 1\n let done = state.importedIds.reviews.length\n let total = 0\n\n for await (const page of client.paginate<WcReview>('/products/reviews', {}, startPage)) {\n total = page.total\n\n for (const wc of page.items) {\n const nymbalProductId = state.wcToNymbalProduct[wc.product_id]\n if (!nymbalProductId) continue\n try {\n const result = await adapters.reviews.submitReview(\n mapReview(wc, nymbalProductId, null),\n )\n state.importedIds.reviews.push(result.review.id)\n state.eventsEmitted++\n } catch (err) {\n state.errors.push({ phase: 'reviews', id: String(wc.id), error: String(err) })\n }\n done++\n emit({ kind: 'progress', entity: 'reviews', done, total })\n }\n\n state.cursors['reviews'] = { page: (state.cursors['reviews']?.page ?? 1) + 1 }\n await checkpoint()\n }\n }\n\n // ── 7. AI Enrichment ─────────────────────────────────────────────────────────\n const enrichStats = { enriched: 0, skipped: 0, failed: 0 }\n if (opts.ai && adapters.ai !== null && state.importedIds.products.length > 0) {\n emit({ kind: 'phase', phase: 'enrich' })\n const stats = await runEnrichment(\n state.importedIds.products,\n adapters.ai,\n services.product,\n (done, total) => emit({ kind: 'progress', entity: 'enrichment', done, total }),\n )\n Object.assign(enrichStats, stats)\n state.eventsEmitted += enrichStats.enriched\n }\n\n state.phase = 'done'\n await checkpoint()\n\n const report = buildReport(state, startMs)\n report.enriched = enrichStats\n return report\n}\n"],"mappings":"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","names":["writeFile","uuidv7","PRICE_MINOR_FACTOR","mapAddress","createHash","join"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nymbalhq/importers",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Source migration importers for Nymbal (WooCommerce, etc.)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"uuid": "^10.0.0",
|
|
31
|
-
"@nymbalhq/config": "
|
|
32
|
-
"@nymbalhq/platform": "
|
|
33
|
-
"@nymbalhq/types": "
|
|
31
|
+
"@nymbalhq/config": "1.1.0",
|
|
32
|
+
"@nymbalhq/platform": "1.1.0",
|
|
33
|
+
"@nymbalhq/types": "1.1.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/uuid": "^10.0.0",
|