@primeuicom/mcp 0.1.14 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -59,9 +59,9 @@ Available tools:
59
59
  | `get_project_info` | Returns project metadata and page list, including export readiness and source paths. | none |
60
60
  | `inspect_page` | Returns details for one page by slug, raw active-variant components (or `null`), and a formatted component report table for local comparison. | `pageSlug` |
61
61
  | `list_exports` | Lists existing exports with ID, status, and creation time. | none |
62
- | `create_export` | Starts a new export and returns created export info plus page snapshot. | none |
63
- | `download_export` | Downloads and extracts a completed export into `.primeui/temp/exports/<exportId>/` and writes sidecar manifest `.primeui/temp/exports/<exportId>.manifest.json`. | `id` (export ID) |
64
- | `copy_page` | Safely copies one page from downloaded export into user project (new files only, conflicts as diff, add-only missing deps). | `originPageSlug`, `actualPageSlug?` |
62
+ | `create_export` | Starts a new export and returns strict API manifest payload (`pages[].manifest.files` + `export.components`), then saves sidecar manifest `.primeui/temp/exports/<exportId>.manifest.json`. | none |
63
+ | `download_export` | Downloads and extracts a completed export into `.primeui/temp/exports/<exportId>/` and validates existing local sidecar manifest for this export ID. | `id` (export ID) |
64
+ | `copy_page` | Safely copies one page from downloaded export into user project using only `pages[].manifest.files` (new files only, conflicts as diff, add-only missing deps). | `originPageSlug`, `actualPageSlug?` |
65
65
  | `clear_temp` | Clears `.primeui/temp/` and recreates baseline temp layout. | none |
66
66
 
67
67
  ## Getting started
@@ -196,6 +196,7 @@ Use the same JSON server configuration shown in Getting started:
196
196
  - otherwise MCP searches `.primeui/project.json` upward from current working directory.
197
197
  - Missing or invalid `targetProjectPath` is treated as a configuration error.
198
198
  - Downloaded archives are validated as ZIP payloads before extraction.
199
+ - `create_export` is the source of truth for local sidecar manifest creation in `.primeui/temp/exports/<exportId>.manifest.json`.
199
200
  - Temporary export files are written under `.primeui/temp/` in user project folder
200
201
 
201
202
  ## Local development and testing
package/dist/service.js CHANGED
@@ -147,6 +147,36 @@ var exportItemSchema = z2.object({
147
147
  status: exportStatusSchema,
148
148
  createdAt: z2.string().describe("Export creation timestamp in ISO-8601 UTC format.")
149
149
  }).describe("Single export record from PrimeUI export history.");
150
+ var exportSummarySchema = z2.object({
151
+ total: z2.number().describe("Total pages processed in this export run."),
152
+ successful: z2.number().describe("Number of pages exported successfully."),
153
+ failed: z2.number().describe("Number of pages that failed export.")
154
+ });
155
+ var exportComponentSchema = z2.object({
156
+ componentKey: z2.string().describe("Global component key from export manifest."),
157
+ enabled: z2.boolean().describe("True when this global component was exported."),
158
+ files: z2.array(z2.string()).describe("Files associated with this global component."),
159
+ message: z2.string().describe("Export status message for this global component.")
160
+ });
161
+ var exportPageManifestSchema = z2.object({
162
+ success: z2.boolean().describe("True when page export succeeded in this export run."),
163
+ message: z2.string().describe("Export status message for this page."),
164
+ files: z2.array(z2.string()).describe(
165
+ "Authoritative list of page-related files from PrimeUI export manifest, including shared project files required by this page."
166
+ )
167
+ });
168
+ var exportPageSchema = z2.object({
169
+ id: z2.string().describe("Stable PrimeUI page identifier for this export entry."),
170
+ title: z2.string().optional().describe(
171
+ "Optional page title from export manifest. Can be undefined for some legacy records."
172
+ ),
173
+ slug: z2.string().describe("PrimeUI page slug included in this export."),
174
+ pageType: z2.string().describe("PrimeUI page type for routing/path resolution."),
175
+ isReadyToExport: z2.literal(true).describe("Always true for pages included in export payload."),
176
+ pagePath: z2.string().describe("Page source file path relative to export root."),
177
+ componentsPath: z2.string().describe("Page components directory path relative to export root."),
178
+ manifest: exportPageManifestSchema
179
+ });
150
180
  var fileTransferSchema = z2.object({
151
181
  sourcePath: z2.string().describe("Relative source file path inside downloaded export root."),
152
182
  targetPath: z2.string().describe("Relative target file path inside user project root.")
@@ -215,14 +245,14 @@ var TRIGGER_PHRASES = [
215
245
  ].join(", ");
216
246
  var WORKFLOW_SUMMARY = `
217
247
  WORKFLOW ORDER (always follow this sequence):
218
- 1. get_project_info -> discover PrimeUI pages
219
- 2. Reconcile PrimeUI pages with local project pages/routes/paths, then confirm target pages with user
220
- 3. inspect_page -> inspect selected page components and produce a structured comparison table
221
- 4. create_export -> generate export snapshot for agreed pages/analysis scope
222
- 5. download_export -> download to temp directory
223
- 6. copy_page -> copy one page safely (repeat for each selected page)
224
- 7. Reconcile integration points and resolve reported conflicts, if any
225
- 8. clear_temp -> cleanup temp files
248
+ 1. clear_temp -> cleanup stale temp files from previous runs before starting new import flow
249
+ 2. get_project_info -> discover PrimeUI pages
250
+ 3. Reconcile PrimeUI pages with local project pages/routes/paths, then confirm target pages with user
251
+ 4. inspect_page -> inspect selected page components and produce a structured comparison table
252
+ 5. create_export -> generate export snapshot and local manifest with page file lists + global components
253
+ 6. download_export -> download to temp directory
254
+ 7. copy_page -> copy one page safely (repeat for each selected page)
255
+ 8. Reconcile integration points and resolve reported conflicts, if any
226
256
  `.trim();
227
257
  var initialInstructions = `
228
258
  PrimeUI MCP enables importing pages from PrimeUI Studio into your local project.
@@ -233,6 +263,7 @@ ${WORKFLOW_SUMMARY}
233
263
 
234
264
  CRITICAL RULES FOR PAGE IMPORT:
235
265
  - Import is PAGE-BY-PAGE only. Never import the entire project at once.
266
+ - Always call clear_temp at the very beginning of a new import flow to avoid stale export state.
236
267
  - After get_project_info, always compare PrimeUI pages against the user's existing local project pages before proposing imports.
237
268
  - Reconciliation report MUST include: page presence match, slug/pagePath/componentsPath path match, and proposed action per page.
238
269
  - Decision matrix:
@@ -244,6 +275,10 @@ CRITICAL RULES FOR PAGE IMPORT:
244
275
  - After inspect_page returns report table, supplement that table with local project page state:
245
276
  mark what already exists, what is missing, and what differs.
246
277
  - Before creating export, ask user which pages to add/update (specific pages or all missing pages) and keep that choice in thread context.
278
+ - create_export response includes:
279
+ - page-level manifest files per page ('pages[].manifest.files'),
280
+ - global components list ('export.components').
281
+ Mention both to user after create_export, because this is the source of truth for copy decisions.
247
282
  - The downloaded export in .primeui/temp/ contains a full standalone Next.js project.
248
283
  Do NOT copy it wholesale.
249
284
  - Always call copy_page for each confirmed page instead of manual file copy.
@@ -251,6 +286,7 @@ CRITICAL RULES FOR PAGE IMPORT:
251
286
  - new files are copied,
252
287
  - identical files are reported,
253
288
  - conflicting files are NEVER overwritten and are returned with diff.
289
+ - copy_page reads and validates files only from 'pages[].manifest.files' in local sidecar manifest.
254
290
  - copy_page may add only missing dependencies to user's package.json.
255
291
  It never upgrades existing dependency versions and never runs dependency installation.
256
292
  - After page copy operations, inspect integration points (navigation configs, link menus, route registries, page indexes).
@@ -373,7 +409,7 @@ ${WORKFLOW_SUMMARY}`,
373
409
  };
374
410
  var toolCreateExport = {
375
411
  title: "PrimeUI Create Export",
376
- description: `Create a new export snapshot from the current PrimeUI project state and wait for completion. Returns export ID and the list of pages included in the export.
412
+ description: `Create a new export snapshot from the current PrimeUI project state and wait for completion. Returns strict export manifest payload from API, including page-level file manifests and global components.
377
413
 
378
414
  WHEN TO USE: Call this AFTER the user has confirmed which pages they want to import via get_project_info. This tool is REQUIRED before download_export, unless the user explicitly and directly asked to download a specific previous export.
379
415
 
@@ -381,6 +417,8 @@ AFTER CALLING:
381
417
  - Verify that all user-selected pages are present in the export result AND have isReadyToExport: true.
382
418
  - If path mismatch pages were flagged during project-info reconciliation, ensure they are included for deeper comparison after download.
383
419
  - If any required page is missing or not ready, report and pause for user decision before proceeding.
420
+ - Mention that global components are available in export.components and can be transferred separately if needed.
421
+ - Local sidecar manifest is saved to .primeui/temp/exports/[exportId].manifest.json from this API payload.
384
422
  - Use the returned export ID to call download_export.
385
423
 
386
424
  ${WORKFLOW_SUMMARY}`,
@@ -390,10 +428,20 @@ ${WORKFLOW_SUMMARY}`,
390
428
  id: z2.string().describe(
391
429
  "Freshly created export identifier. Use this as download_export input.id."
392
430
  ),
393
- status: exportStatusSchema
431
+ status: exportStatusSchema,
432
+ createdAt: z2.string().describe("Export creation timestamp in ISO-8601 UTC format."),
433
+ expiresAt: z2.string().nullable().describe(
434
+ "Export expiration timestamp in ISO-8601 UTC format, or null when export has no explicit expiration."
435
+ ),
436
+ summary: exportSummarySchema.describe(
437
+ "Export run summary with total/successful/failed counters."
438
+ ),
439
+ components: z2.array(exportComponentSchema).describe(
440
+ "Global component export manifest. These components are independent from page-level manifests."
441
+ )
394
442
  }).describe("Created export summary."),
395
- pages: z2.array(pageSchema).describe(
396
- "Page snapshot associated with this export operation. Validate requested pages here before download/import."
443
+ pages: z2.array(exportPageSchema).describe(
444
+ "Strict page payload associated with this export, including per-page manifest files list."
397
445
  )
398
446
  },
399
447
  annotations: {
@@ -409,19 +457,19 @@ var toolDownloadExport = {
409
457
 
410
458
  Do NOT call this as the first step. Requires export ID from create_export. Start with get_project_info instead.
411
459
 
412
- WHEN TO USE: Call this AFTER create_export has returned a completed export. Requires export ID from create_export so MCP can use the matching local pages snapshot for sidecar manifest generation.
460
+ WHEN TO USE: Call this AFTER create_export has returned a completed export. Requires export ID from create_export so MCP can use the matching local sidecar manifest.
413
461
 
414
462
  NOTE:
415
- - Download of an export ID without local pages snapshot may fail.
463
+ - Download of an export ID without local sidecar manifest will fail.
416
464
  - For previous exports, create a fresh export first when possible.
417
465
 
418
466
  AFTER DOWNLOAD:
419
- - Use manifestPath from this response as the contract for page slug/path mapping in copy operations.
467
+ - Use manifestPath from this response as the contract for page slug/path mapping and manifest file lists in copy operations.
420
468
  - For each selected page, call copy_page with:
421
469
  - originPageSlug (required),
422
470
  - actualPageSlug (optional, if route remap is needed).
423
471
  - If there are unresolved conflicts in copy report, stop and ask the user.
424
- - Only after all requested pages are copied and reconciled, call clear_temp.
472
+ - Do not force cleanup at flow end; keep downloaded export files available for validation and follow-up checks.
425
473
 
426
474
  ${WORKFLOW_SUMMARY}`,
427
475
  inputSchema: {
@@ -437,10 +485,10 @@ AFTER DOWNLOAD:
437
485
  "Absolute local path to extracted export root in .primeui/temp/exports/[exportId]. Read files from here only."
438
486
  ),
439
487
  manifestPath: z2.string().describe(
440
- "Absolute path to sidecar export manifest file (.primeui/temp/exports/[exportId].manifest.json). copy_page depends on this file."
488
+ "Absolute path to sidecar export manifest file (.primeui/temp/exports/[exportId].manifest.json) saved by create_export and required by copy_page."
441
489
  ),
442
- pages: z2.array(pageSchema).describe(
443
- "Page descriptors for import decisions. Use pagePath/componentsPath from each item when copying code into user project."
490
+ pages: z2.array(exportPageSchema).describe(
491
+ "Page descriptors loaded from local sidecar export manifest for import decisions."
444
492
  )
445
493
  },
446
494
  annotations: {
@@ -461,7 +509,7 @@ WHEN TO USE:
461
509
 
462
510
  BEHAVIOR:
463
511
  - Reads export sidecar manifest to resolve originPageSlug -> pagePath/componentsPath.
464
- - Copies page file + full page components folder + recursively discovered internal dependencies.
512
+ - Copies and validates files strictly from pages[].manifest.files for the selected page.
465
513
  - Never overwrites existing conflicting files.
466
514
  - Reports:
467
515
  a) copied new files,
@@ -506,7 +554,9 @@ ${WORKFLOW_SUMMARY}`,
506
554
  "Dependencies with version mismatch between export and user project. Report only."
507
555
  ),
508
556
  summary: z2.object({
509
- totalCandidateFiles: z2.number().describe("Total number of candidate files considered for page copy."),
557
+ totalCandidateFiles: z2.number().describe(
558
+ "Total number of candidate files considered for page copy from pages[].manifest.files."
559
+ ),
510
560
  copiedFiles: z2.number().describe("Number of files copied as new."),
511
561
  identicalFiles: z2.number().describe("Number of files detected as identical."),
512
562
  conflictFiles: z2.number().describe("Number of conflicting files not copied."),
@@ -525,9 +575,9 @@ var toolClearTemp = {
525
575
  title: "PrimeUI Clear Temp",
526
576
  description: `Delete all files in .primeui/temp/ and recreate empty temp structure.
527
577
 
528
- Do NOT call this unless ALL requested pages have been successfully imported into the user's project.
578
+ Call this at the START of a new import flow, before get_project_info/create_export, to reset stale temp state from previous runs.
529
579
 
530
- WHEN TO USE: Call this as the FINAL step after ALL requested pages have been successfully imported into the user's project. Do not call this between page imports - wait until all pages are done.
580
+ WHEN TO USE: Call once at the beginning of each new import flow. Do not call this between page imports in the same flow.
531
581
 
532
582
  Safe to call multiple times. Only affects .primeui/temp/ directory - never touches the user's project files.
533
583
 
@@ -837,6 +887,7 @@ function shouldParseFile(filePath) {
837
887
  return PARSEABLE_EXTENSIONS.has(path3.extname(filePath).toLowerCase());
838
888
  }
839
889
  async function buildImportGraph(input) {
890
+ const shouldFollowInternalImports = input.followInternalImports ?? true;
840
891
  const projectRoot = path3.resolve(input.projectRoot);
841
892
  const queue = input.entryFiles.map((filePath) => path3.resolve(filePath));
842
893
  const visited = /* @__PURE__ */ new Set();
@@ -866,7 +917,7 @@ async function buildImportGraph(input) {
866
917
  specifier
867
918
  );
868
919
  if (resolved.kind === "internal") {
869
- if (!visited.has(resolved.filePath)) {
920
+ if (shouldFollowInternalImports && !visited.has(resolved.filePath)) {
870
921
  queue.push(resolved.filePath);
871
922
  }
872
923
  continue;
@@ -1180,53 +1231,95 @@ async function fileExists2(filePath) {
1180
1231
  return false;
1181
1232
  }
1182
1233
  }
1183
- function isPageRecord(value) {
1234
+ function isExportPageManifestRecord(value) {
1235
+ if (!value || typeof value !== "object") {
1236
+ return false;
1237
+ }
1238
+ const maybe = value;
1239
+ return typeof maybe.success === "boolean" && typeof maybe.message === "string" && Array.isArray(maybe.files) && maybe.files.every((item) => typeof item === "string");
1240
+ }
1241
+ function isExportStatus(value) {
1242
+ return value === "in_progress" || value === "completed" || value === "failed";
1243
+ }
1244
+ function isExportSummaryRecord(value) {
1245
+ if (!value || typeof value !== "object") {
1246
+ return false;
1247
+ }
1248
+ const maybe = value;
1249
+ return typeof maybe.total === "number" && typeof maybe.successful === "number" && typeof maybe.failed === "number";
1250
+ }
1251
+ function isExportedComponentRecord(value) {
1252
+ if (!value || typeof value !== "object") {
1253
+ return false;
1254
+ }
1255
+ const maybe = value;
1256
+ return typeof maybe.componentKey === "string" && typeof maybe.enabled === "boolean" && Array.isArray(maybe.files) && maybe.files.every((item) => typeof item === "string") && typeof maybe.message === "string";
1257
+ }
1258
+ function isExportPageRecord(value) {
1184
1259
  if (!value || typeof value !== "object") {
1185
1260
  return false;
1186
1261
  }
1187
1262
  const maybe = value;
1188
- return typeof maybe.id === "string" && typeof maybe.title === "string" && typeof maybe.slug === "string" && typeof maybe.pageType === "string" && typeof maybe.isReadyToExport === "boolean" && typeof maybe.pagePath === "string" && typeof maybe.componentsPath === "string";
1263
+ const title = maybe.title;
1264
+ return typeof maybe.id === "string" && (typeof title === "string" || typeof title === "undefined") && typeof maybe.slug === "string" && typeof maybe.pageType === "string" && maybe.isReadyToExport === true && typeof maybe.pagePath === "string" && typeof maybe.componentsPath === "string" && isExportPageManifestRecord(maybe.manifest);
1189
1265
  }
1190
1266
  function parseManifest(value) {
1191
1267
  if (!value || typeof value !== "object") {
1192
1268
  throw new Error("Invalid export manifest format.");
1193
1269
  }
1194
1270
  const maybe = value;
1195
- if (maybe.schemaVersion !== 1 || typeof maybe.generatedAt !== "string" || typeof maybe.exportId !== "string" || typeof maybe.projectPath !== "string" || !Array.isArray(maybe.pages)) {
1271
+ const exportPayload = maybe.export;
1272
+ if (!exportPayload || typeof exportPayload !== "object") {
1273
+ throw new Error("Export manifest export payload is invalid.");
1274
+ }
1275
+ const exportObject = exportPayload;
1276
+ const status = exportObject.status;
1277
+ const summary = exportObject.summary;
1278
+ const components = exportObject.components;
1279
+ if (typeof exportObject.id !== "string" || !isExportStatus(status) || typeof exportObject.createdAt !== "string" || !("expiresAt" in exportObject) || !(typeof exportObject.expiresAt === "string" || exportObject.expiresAt === null) || !isExportSummaryRecord(summary) || !Array.isArray(components) || !components.every(isExportedComponentRecord) || !Array.isArray(maybe.pages)) {
1196
1280
  throw new Error("Export manifest does not match expected schema.");
1197
1281
  }
1198
- if (!maybe.pages.every(isPageRecord)) {
1282
+ if (!maybe.pages.every(isExportPageRecord)) {
1199
1283
  throw new Error("Export manifest pages payload is invalid.");
1200
1284
  }
1201
1285
  return {
1202
- schemaVersion: 1,
1203
- generatedAt: maybe.generatedAt,
1204
- exportId: maybe.exportId,
1205
- projectPath: maybe.projectPath,
1286
+ export: {
1287
+ id: exportObject.id,
1288
+ status,
1289
+ createdAt: exportObject.createdAt,
1290
+ expiresAt: exportObject.expiresAt,
1291
+ summary,
1292
+ components
1293
+ },
1206
1294
  pages: maybe.pages
1207
1295
  };
1208
1296
  }
1209
- async function listFilesRecursively(dirPath) {
1210
- const files = [];
1211
- const stack = [dirPath];
1212
- while (stack.length > 0) {
1213
- const current = stack.pop();
1214
- if (!current) {
1215
- continue;
1297
+ async function resolveManifestCandidateFiles(input) {
1298
+ const result = /* @__PURE__ */ new Set();
1299
+ for (const rawPath of input.files) {
1300
+ const trimmed = rawPath.trim();
1301
+ if (!trimmed) {
1302
+ throw new Error(
1303
+ `Export manifest for page "${input.pageSlug}" contains an empty file path.`
1304
+ );
1216
1305
  }
1217
- const entries = await readdir(current, { withFileTypes: true });
1218
- for (const entry of entries) {
1219
- const absolutePath = path4.join(current, entry.name);
1220
- if (entry.isDirectory()) {
1221
- stack.push(absolutePath);
1222
- continue;
1223
- }
1224
- if (entry.isFile()) {
1225
- files.push(absolutePath);
1226
- }
1306
+ const normalizedRelative = toPosixPath(trimmed).replace(/^\.\/+/, "");
1307
+ const absolutePath = path4.resolve(input.exportPath, normalizedRelative);
1308
+ const relative = path4.relative(input.exportPath, absolutePath);
1309
+ if (relative.startsWith("..") || path4.isAbsolute(relative)) {
1310
+ throw new Error(
1311
+ `Export manifest for page "${input.pageSlug}" contains path outside export root: ${trimmed}`
1312
+ );
1227
1313
  }
1314
+ const stats = await stat3(absolutePath).catch(() => null);
1315
+ if (!stats?.isFile()) {
1316
+ throw new Error(
1317
+ `Export manifest file is missing for page "${input.pageSlug}": ${normalizedRelative}`
1318
+ );
1319
+ }
1320
+ result.add(absolutePath);
1228
1321
  }
1229
- return files;
1322
+ return [...result].sort((a, b) => a.localeCompare(b));
1230
1323
  }
1231
1324
  async function resolveSingleExportDirectory(exportsRoot) {
1232
1325
  const entries = await readdir(exportsRoot, { withFileTypes: true });
@@ -1336,13 +1429,13 @@ async function copyPageFromExport(input) {
1336
1429
  );
1337
1430
  if (!await fileExists2(manifestPath)) {
1338
1431
  throw new Error(
1339
- `Export manifest is missing at ${manifestPath}. Run download_export to regenerate it.`
1432
+ `Export manifest is missing at ${manifestPath}. Run create_export and download_export to regenerate it.`
1340
1433
  );
1341
1434
  }
1342
1435
  const manifest = parseManifest(await readJsonFile(manifestPath));
1343
- if (manifest.exportId !== exportId) {
1436
+ if (manifest.export.id !== exportId) {
1344
1437
  throw new Error(
1345
- `Manifest exportId mismatch: expected ${exportId}, got ${manifest.exportId}. Re-download export.`
1438
+ `Manifest export id mismatch: expected ${exportId}, got ${manifest.export.id}. Re-run create_export and download_export.`
1346
1439
  );
1347
1440
  }
1348
1441
  const page = manifest.pages.find(
@@ -1382,15 +1475,16 @@ async function copyPageFromExport(input) {
1382
1475
  const importRewritePlan = isSlugRemapped ? buildImportRewritePlan(page.componentsPath, targetPaths.componentsPath) : null;
1383
1476
  ensureSafeTargetPath(input.projectRoot, targetPagePath);
1384
1477
  ensureSafeTargetPath(input.projectRoot, targetComponentsPath);
1385
- const componentFiles = await listFilesRecursively(sourceComponentsPath);
1386
- const seedFiles = [sourcePagePath, ...componentFiles];
1387
- const importGraph = await buildImportGraph({
1478
+ const candidateFiles = await resolveManifestCandidateFiles({
1479
+ exportPath,
1480
+ pageSlug: normalizedOriginSlug,
1481
+ files: page.manifest.files
1482
+ });
1483
+ const dependencyGraph = await buildImportGraph({
1388
1484
  projectRoot: exportPath,
1389
- entryFiles: seedFiles
1485
+ entryFiles: candidateFiles,
1486
+ followInternalImports: false
1390
1487
  });
1391
- const candidateFiles = [...new Set(importGraph.internalFiles)].sort(
1392
- (a, b) => a.localeCompare(b)
1393
- );
1394
1488
  const newFiles = [];
1395
1489
  const identicalFiles = [];
1396
1490
  const conflictFiles = [];
@@ -1463,7 +1557,7 @@ async function copyPageFromExport(input) {
1463
1557
  const userPackageJson = await readJsonFile(userPackageJsonPath);
1464
1558
  const addedDependencies = [];
1465
1559
  const dependenciesVersionConflicts = [];
1466
- for (const packageName of importGraph.externalPackages) {
1560
+ for (const packageName of dependencyGraph.externalPackages) {
1467
1561
  const exportDependency = getDependencyVersion(
1468
1562
  exportPackageJson,
1469
1563
  packageName
@@ -1623,31 +1717,69 @@ function buildInspectPageReport(input) {
1623
1717
  }
1624
1718
 
1625
1719
  // src/services/project-sync-service.ts
1626
- function isProjectPage(value) {
1720
+ function isExportStatus2(value) {
1721
+ return value === "in_progress" || value === "completed" || value === "failed";
1722
+ }
1723
+ function isExportSummary(value) {
1627
1724
  if (!value || typeof value !== "object") {
1628
1725
  return false;
1629
1726
  }
1630
1727
  const maybe = value;
1631
- return typeof maybe.id === "string" && typeof maybe.title === "string" && typeof maybe.slug === "string" && typeof maybe.pageType === "string" && typeof maybe.isReadyToExport === "boolean" && typeof maybe.pagePath === "string" && typeof maybe.componentsPath === "string";
1728
+ return typeof maybe.total === "number" && typeof maybe.successful === "number" && typeof maybe.failed === "number";
1632
1729
  }
1633
- function buildPagesSnapshotPath(exportsRoot, exportId) {
1634
- return path5.join(exportsRoot, `${exportId}.pages.snapshot.json`);
1730
+ function isExportedComponent(value) {
1731
+ if (!value || typeof value !== "object") {
1732
+ return false;
1733
+ }
1734
+ const maybe = value;
1735
+ return typeof maybe.componentKey === "string" && typeof maybe.enabled === "boolean" && Array.isArray(maybe.files) && maybe.files.every((item) => typeof item === "string") && typeof maybe.message === "string";
1736
+ }
1737
+ function isExportPageManifest(value) {
1738
+ if (!value || typeof value !== "object") {
1739
+ return false;
1740
+ }
1741
+ const maybe = value;
1742
+ return typeof maybe.success === "boolean" && typeof maybe.message === "string" && Array.isArray(maybe.files) && maybe.files.every((item) => typeof item === "string");
1743
+ }
1744
+ function isExportPage(value) {
1745
+ if (!value || typeof value !== "object") {
1746
+ return false;
1747
+ }
1748
+ const maybe = value;
1749
+ const title = maybe.title;
1750
+ return typeof maybe.id === "string" && (typeof title === "string" || typeof title === "undefined") && typeof maybe.slug === "string" && typeof maybe.pageType === "string" && maybe.isReadyToExport === true && typeof maybe.pagePath === "string" && typeof maybe.componentsPath === "string" && isExportPageManifest(maybe.manifest);
1635
1751
  }
1636
- function parsePagesSnapshot(value) {
1752
+ function buildManifestPath(exportsRoot, exportId) {
1753
+ return path5.join(exportsRoot, `${exportId}.manifest.json`);
1754
+ }
1755
+ function parseExportManifest(value) {
1637
1756
  if (!value || typeof value !== "object") {
1638
- throw new Error("Export pages snapshot is invalid.");
1757
+ throw new Error("Export manifest is invalid.");
1639
1758
  }
1640
1759
  const maybe = value;
1641
- if (maybe.schemaVersion !== 1 || typeof maybe.generatedAt !== "string" || typeof maybe.exportId !== "string" || !Array.isArray(maybe.pages)) {
1642
- throw new Error("Export pages snapshot does not match expected schema.");
1760
+ const exportPayload = maybe.export;
1761
+ if (!exportPayload || typeof exportPayload !== "object") {
1762
+ throw new Error("Export manifest export payload is invalid.");
1763
+ }
1764
+ const exportObject = exportPayload;
1765
+ const status = exportObject.status;
1766
+ const summary = exportObject.summary;
1767
+ const components = exportObject.components;
1768
+ if (typeof exportObject.id !== "string" || !isExportStatus2(status) || typeof exportObject.createdAt !== "string" || !("expiresAt" in exportObject) || !(typeof exportObject.expiresAt === "string" || exportObject.expiresAt === null) || !isExportSummary(summary) || !Array.isArray(components) || !components.every(isExportedComponent) || !Array.isArray(maybe.pages)) {
1769
+ throw new Error("Export manifest does not match expected schema.");
1643
1770
  }
1644
- if (!maybe.pages.every(isProjectPage)) {
1645
- throw new Error("Export pages snapshot has invalid pages payload.");
1771
+ if (!maybe.pages.every(isExportPage)) {
1772
+ throw new Error("Export manifest pages payload is invalid.");
1646
1773
  }
1647
1774
  return {
1648
- schemaVersion: 1,
1649
- generatedAt: maybe.generatedAt,
1650
- exportId: maybe.exportId,
1775
+ export: {
1776
+ id: exportObject.id,
1777
+ status,
1778
+ createdAt: exportObject.createdAt,
1779
+ expiresAt: exportObject.expiresAt,
1780
+ summary,
1781
+ components
1782
+ },
1651
1783
  pages: maybe.pages
1652
1784
  };
1653
1785
  }
@@ -1677,21 +1809,9 @@ var ProjectSyncService = class {
1677
1809
  async createExport() {
1678
1810
  await this.ensureTempLayout();
1679
1811
  const result = await this.provider.createExport();
1680
- const pagesSnapshotPath = buildPagesSnapshotPath(
1681
- this.exportsRoot,
1682
- result.export.id
1683
- );
1684
- const snapshot = {
1685
- schemaVersion: 1,
1686
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1687
- exportId: result.export.id,
1688
- pages: result.pages
1689
- };
1690
- await writeUtf8(
1691
- pagesSnapshotPath,
1692
- `${JSON.stringify(snapshot, null, 2)}
1693
- `
1694
- );
1812
+ const manifestPath = buildManifestPath(this.exportsRoot, result.export.id);
1813
+ await writeUtf8(manifestPath, `${JSON.stringify(result, null, 2)}
1814
+ `);
1695
1815
  return result;
1696
1816
  }
1697
1817
  async downloadExportById(id) {
@@ -1703,39 +1823,28 @@ var ProjectSyncService = class {
1703
1823
  }
1704
1824
  const targetZipPath = path5.join(this.exportsRoot, `${id}.zip`);
1705
1825
  const targetProjectPath = path5.join(this.exportsRoot, id);
1706
- await ensureDir(this.exportsRoot);
1707
- await this.provider.downloadExportArchive(id, targetZipPath);
1708
- await resetDir(targetProjectPath);
1709
- await extractZip(targetZipPath, targetProjectPath);
1710
- const pagesSnapshotPath = buildPagesSnapshotPath(this.exportsRoot, id);
1711
- let pagesSnapshot;
1826
+ const manifestPath = buildManifestPath(this.exportsRoot, id);
1827
+ let manifest;
1712
1828
  try {
1713
- const snapshotRaw = JSON.parse(
1714
- await readFile4(pagesSnapshotPath, "utf-8")
1829
+ manifest = parseExportManifest(
1830
+ JSON.parse(await readFile4(manifestPath, "utf-8"))
1715
1831
  );
1716
- pagesSnapshot = parsePagesSnapshot(snapshotRaw);
1717
1832
  } catch (error) {
1718
1833
  const reason = error instanceof Error ? error.message : String(error);
1719
1834
  throw new Error(
1720
- `Export pages snapshot is required for download id "${id}". Run create_export for this export before downloading. Details: ${reason}`
1835
+ `Export manifest is required for download id "${id}". Run create_export for this export before downloading. Details: ${reason}`
1721
1836
  );
1722
1837
  }
1723
- if (pagesSnapshot.exportId !== id) {
1838
+ if (manifest.export.id !== id) {
1724
1839
  throw new Error(
1725
- `Export pages snapshot mismatch for id "${id}". Run create_export and retry download.`
1840
+ `Export manifest mismatch for id "${id}". Run create_export and retry download.`
1726
1841
  );
1727
1842
  }
1728
- const pages = pagesSnapshot.pages;
1729
- const manifestPath = path5.join(this.exportsRoot, `${id}.manifest.json`);
1730
- const manifest = {
1731
- schemaVersion: 1,
1732
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1733
- exportId: id,
1734
- projectPath: targetProjectPath,
1735
- pages
1736
- };
1737
- await writeUtf8(manifestPath, `${JSON.stringify(manifest, null, 2)}
1738
- `);
1843
+ await ensureDir(this.exportsRoot);
1844
+ await this.provider.downloadExportArchive(id, targetZipPath);
1845
+ await resetDir(targetProjectPath);
1846
+ await extractZip(targetZipPath, targetProjectPath);
1847
+ const pages = manifest.pages;
1739
1848
  return {
1740
1849
  exportId: id,
1741
1850
  projectPath: targetProjectPath,
@@ -1764,14 +1873,12 @@ var ProjectSyncService = class {
1764
1873
  }
1765
1874
  async clearTemp() {
1766
1875
  await resetDir(this.tempRoot);
1767
- await writeUtf8(path5.join(this.tempRoot, ".gitkeep"), "");
1768
1876
  await ensureDir(this.exportsRoot);
1769
1877
  }
1770
1878
  async ensureTempLayout() {
1771
1879
  await ensureDir(this.primeUiRoot);
1772
1880
  await ensureDir(this.tempRoot);
1773
1881
  await ensureDir(this.exportsRoot);
1774
- await writeUtf8(path5.join(this.tempRoot, ".gitkeep"), "");
1775
1882
  }
1776
1883
  };
1777
1884
 
@@ -1799,6 +1906,32 @@ var projectPageObjectSchema = z3.object({
1799
1906
  componentsPath: z3.string()
1800
1907
  });
1801
1908
  var projectPageSchema = projectPageObjectSchema;
1909
+ var exportSummarySchema2 = z3.object({
1910
+ total: z3.number(),
1911
+ successful: z3.number(),
1912
+ failed: z3.number()
1913
+ });
1914
+ var exportedComponentSchema = z3.object({
1915
+ componentKey: z3.string(),
1916
+ enabled: z3.boolean(),
1917
+ files: z3.array(z3.string()),
1918
+ message: z3.string()
1919
+ });
1920
+ var exportPageManifestSchema2 = z3.object({
1921
+ success: z3.boolean(),
1922
+ message: z3.string(),
1923
+ files: z3.array(z3.string())
1924
+ });
1925
+ var exportPageSchema2 = z3.object({
1926
+ id: z3.string(),
1927
+ title: z3.string().optional(),
1928
+ slug: z3.string(),
1929
+ pageType: z3.string(),
1930
+ isReadyToExport: z3.literal(true),
1931
+ pagePath: z3.string(),
1932
+ componentsPath: z3.string(),
1933
+ manifest: exportPageManifestSchema2
1934
+ });
1802
1935
  var projectInfoSchema = z3.object({
1803
1936
  projectId: z3.string(),
1804
1937
  projectName: z3.string(),
@@ -1834,9 +1967,13 @@ var exportsResponseSchema = z3.object({
1834
1967
  var createExportResponseSchema = z3.object({
1835
1968
  export: z3.object({
1836
1969
  id: z3.string(),
1837
- status: exportStatusSchema2
1970
+ status: exportStatusSchema2,
1971
+ createdAt: z3.string().datetime({ offset: true }),
1972
+ expiresAt: z3.string().datetime({ offset: true }).nullable(),
1973
+ summary: exportSummarySchema2,
1974
+ components: z3.array(exportedComponentSchema)
1838
1975
  }),
1839
- pages: z3.array(projectPageSchema)
1976
+ pages: z3.array(exportPageSchema2)
1840
1977
  });
1841
1978
  var PrimeUiApiContractError = class extends Error {
1842
1979
  constructor(endpoint, details) {