@primeuicom/mcp 0.1.13 → 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 +8 -7
- package/dist/service.js +289 -152
- package/dist/service.js.map +1 -1
- package/package.json +1 -1
package/dist/service.js
CHANGED
|
@@ -138,15 +138,45 @@ var pageSchema = z2.object({
|
|
|
138
138
|
"PrimeUI page descriptor used by project and export tools. Paths are always relative to export project root."
|
|
139
139
|
);
|
|
140
140
|
var exportStatusSchema = z2.enum(["in_progress", "completed", "failed"]).describe(
|
|
141
|
-
"Export lifecycle state. Use 'completed' before calling
|
|
141
|
+
"Export lifecycle state. Use 'completed' before calling download_export."
|
|
142
142
|
);
|
|
143
143
|
var exportItemSchema = z2.object({
|
|
144
144
|
id: z2.string().describe(
|
|
145
|
-
"Export identifier. Pass this value to
|
|
145
|
+
"Export identifier. Pass this value to download_export input.id."
|
|
146
146
|
),
|
|
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.
|
|
219
|
-
2.
|
|
220
|
-
3.
|
|
221
|
-
4.
|
|
222
|
-
5.
|
|
223
|
-
6.
|
|
224
|
-
7.
|
|
225
|
-
8.
|
|
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,25 +263,31 @@ ${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.
|
|
236
|
-
-
|
|
266
|
+
- Always call clear_temp at the very beginning of a new import flow to avoid stale export state.
|
|
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:
|
|
239
270
|
- PrimeUI + local page exists -> candidate for update/review.
|
|
240
271
|
- PrimeUI exists, local missing -> import candidate.
|
|
241
272
|
- Local exists, PrimeUI missing -> custom local page, no action unless user asks.
|
|
242
273
|
- Both exist but path mismatch -> ambiguous, export + compare before deciding.
|
|
243
|
-
- For targeted component-level analysis on a specific page, call
|
|
244
|
-
- After
|
|
274
|
+
- For targeted component-level analysis on a specific page, call inspect_page before export/copy.
|
|
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
|
-
- Always call
|
|
250
|
-
-
|
|
284
|
+
- Always call copy_page for each confirmed page instead of manual file copy.
|
|
285
|
+
- copy_page performs safe copy only:
|
|
251
286
|
- new files are copied,
|
|
252
287
|
- identical files are reported,
|
|
253
288
|
- conflicting files are NEVER overwritten and are returned with diff.
|
|
254
|
-
-
|
|
289
|
+
- copy_page reads and validates files only from 'pages[].manifest.files' in local sidecar manifest.
|
|
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).
|
|
257
293
|
- If integration pattern is obvious, apply it and explicitly mark it in the report with "\u26A0 integration update".
|
|
@@ -274,7 +310,7 @@ AFTER CALLING:
|
|
|
274
310
|
- If any requested page is not found or not ready, report this immediately and ask for replacement page choices.
|
|
275
311
|
- Ask the user which pages to add or update now (specific page list or all missing pages) and keep this decision in thread context.
|
|
276
312
|
- Import works per page. Multiple pages are allowed, but each page transfer is handled explicitly.
|
|
277
|
-
- Only after user decision is clear, proceed to
|
|
313
|
+
- Only after user decision is clear, proceed to create_export.
|
|
278
314
|
|
|
279
315
|
${WORKFLOW_SUMMARY}`,
|
|
280
316
|
inputSchema: {},
|
|
@@ -304,7 +340,7 @@ var toolInspectPage = {
|
|
|
304
340
|
description: `Targeted inspection tool for one PrimeUI page. Returns page details, active variant (if present), raw components payload, and a formatted report table.
|
|
305
341
|
|
|
306
342
|
WHEN TO USE:
|
|
307
|
-
- Call this after
|
|
343
|
+
- Call this after get_project_info when user wants component-level analysis for a specific page.
|
|
308
344
|
- Use this before export/copy when user asks to compare or selectively update page sections.
|
|
309
345
|
|
|
310
346
|
AFTER CALLING:
|
|
@@ -346,16 +382,16 @@ ${WORKFLOW_SUMMARY}`,
|
|
|
346
382
|
};
|
|
347
383
|
var toolListExports = {
|
|
348
384
|
title: "PrimeUI Export List",
|
|
349
|
-
description: `Auxiliary tool to list previously created exports with their IDs, statuses, and creation dates. This is NOT the starting point for page import - start with
|
|
385
|
+
description: `Auxiliary tool to list previously created exports with their IDs, statuses, and creation dates. This is NOT the starting point for page import - start with get_project_info instead.
|
|
350
386
|
|
|
351
387
|
WHEN TO USE: This is an OPTIONAL helper tool, NOT part of the main import flow. Use it only when:
|
|
352
388
|
- Something went wrong with an export and you need to check its status.
|
|
353
389
|
- The user explicitly references a previous export by date or context.
|
|
354
390
|
- You need to verify whether an export initiated via PrimeUI Studio UI has completed.
|
|
355
391
|
|
|
356
|
-
Note: exports can be created both through
|
|
392
|
+
Note: exports can be created both through create_export AND by the user directly in PrimeUI Studio UI. This tool shows all of them.
|
|
357
393
|
|
|
358
|
-
Do NOT use this tool as a substitute for
|
|
394
|
+
Do NOT use this tool as a substitute for create_export. Always create a fresh export unless the user explicitly asks to use a previous one.
|
|
359
395
|
|
|
360
396
|
${WORKFLOW_SUMMARY}`,
|
|
361
397
|
inputSchema: {},
|
|
@@ -373,27 +409,39 @@ ${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
|
|
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
|
-
WHEN TO USE: Call this AFTER the user has confirmed which pages they want to import via
|
|
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
|
|
|
380
416
|
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.
|
|
384
|
-
-
|
|
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.
|
|
422
|
+
- Use the returned export ID to call download_export.
|
|
385
423
|
|
|
386
424
|
${WORKFLOW_SUMMARY}`,
|
|
387
425
|
inputSchema: {},
|
|
388
426
|
outputSchema: {
|
|
389
427
|
export: z2.object({
|
|
390
428
|
id: z2.string().describe(
|
|
391
|
-
"Freshly created export identifier. Use this as
|
|
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(
|
|
396
|
-
"
|
|
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: {
|
|
@@ -407,26 +455,26 @@ var toolDownloadExport = {
|
|
|
407
455
|
title: "PrimeUI Download Export",
|
|
408
456
|
description: `Download a completed export into .primeui/temp/exports/[exportId]. Downloads and extracts the full export as a standalone Next.js project into the temp directory. This does NOT modify the user's project - files land only in .primeui/temp/.
|
|
409
457
|
|
|
410
|
-
Do NOT call this as the first step. Requires export ID from
|
|
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
|
|
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
|
|
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.
|
|
420
|
-
- For each selected page, call
|
|
467
|
+
- Use manifestPath from this response as the contract for page slug/path mapping and manifest file lists in copy operations.
|
|
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
|
-
-
|
|
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: {
|
|
428
476
|
id: z2.string().describe(
|
|
429
|
-
"Export identifier to download. Prefer export.id from
|
|
477
|
+
"Export identifier to download. Prefer export.id from create_export; use list_exports only on explicit user request."
|
|
430
478
|
)
|
|
431
479
|
},
|
|
432
480
|
outputSchema: {
|
|
@@ -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)
|
|
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(
|
|
443
|
-
"Page descriptors
|
|
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: {
|
|
@@ -455,13 +503,13 @@ var toolCopyPage = {
|
|
|
455
503
|
description: `Safely copy one page from downloaded export into the user's local project. Works only with local files in .primeui/temp/exports and does NOT call PrimeUI API.
|
|
456
504
|
|
|
457
505
|
WHEN TO USE:
|
|
458
|
-
- Call this AFTER
|
|
506
|
+
- Call this AFTER download_export.
|
|
459
507
|
- Call once per selected page.
|
|
460
508
|
- Use actualPageSlug only when user wants to remap route during import.
|
|
461
509
|
|
|
462
510
|
BEHAVIOR:
|
|
463
511
|
- Reads export sidecar manifest to resolve originPageSlug -> pagePath/componentsPath.
|
|
464
|
-
- Copies
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
|
|
@@ -571,79 +621,79 @@ function createPrimeUiMcpServer(source) {
|
|
|
571
621
|
}
|
|
572
622
|
);
|
|
573
623
|
server.registerTool(
|
|
574
|
-
"
|
|
624
|
+
"get_project_info",
|
|
575
625
|
toolGetProjectInfo,
|
|
576
626
|
async () => {
|
|
577
627
|
try {
|
|
578
628
|
const info = await source.getProjectInfo();
|
|
579
|
-
return okResult("
|
|
629
|
+
return okResult("get_project_info", info);
|
|
580
630
|
} catch (error) {
|
|
581
631
|
return errorResult(error);
|
|
582
632
|
}
|
|
583
633
|
}
|
|
584
634
|
);
|
|
585
635
|
server.registerTool(
|
|
586
|
-
"
|
|
636
|
+
"inspect_page",
|
|
587
637
|
toolInspectPage,
|
|
588
638
|
async ({ pageSlug }) => {
|
|
589
639
|
try {
|
|
590
640
|
const result = await source.inspectPage(pageSlug);
|
|
591
|
-
return okResult("
|
|
641
|
+
return okResult("inspect_page", result);
|
|
592
642
|
} catch (error) {
|
|
593
643
|
return errorResult(error);
|
|
594
644
|
}
|
|
595
645
|
}
|
|
596
646
|
);
|
|
597
647
|
server.registerTool(
|
|
598
|
-
"
|
|
648
|
+
"list_exports",
|
|
599
649
|
toolListExports,
|
|
600
650
|
async () => {
|
|
601
651
|
try {
|
|
602
652
|
const exportsList = await source.listExports();
|
|
603
|
-
return okResult("
|
|
653
|
+
return okResult("list_exports", { exports: exportsList });
|
|
604
654
|
} catch (error) {
|
|
605
655
|
return errorResult(error);
|
|
606
656
|
}
|
|
607
657
|
}
|
|
608
658
|
);
|
|
609
659
|
server.registerTool(
|
|
610
|
-
"
|
|
660
|
+
"create_export",
|
|
611
661
|
toolCreateExport,
|
|
612
662
|
async () => {
|
|
613
663
|
try {
|
|
614
664
|
const result = await source.createExport();
|
|
615
|
-
return okResult("
|
|
665
|
+
return okResult("create_export", result);
|
|
616
666
|
} catch (error) {
|
|
617
667
|
return errorResult(error);
|
|
618
668
|
}
|
|
619
669
|
}
|
|
620
670
|
);
|
|
621
671
|
server.registerTool(
|
|
622
|
-
"
|
|
672
|
+
"download_export",
|
|
623
673
|
toolDownloadExport,
|
|
624
674
|
async ({ id }) => {
|
|
625
675
|
try {
|
|
626
676
|
const result = await source.downloadExportById(id);
|
|
627
|
-
return okResult("
|
|
677
|
+
return okResult("download_export", result);
|
|
628
678
|
} catch (error) {
|
|
629
679
|
return errorResult(error);
|
|
630
680
|
}
|
|
631
681
|
}
|
|
632
682
|
);
|
|
633
683
|
server.registerTool(
|
|
634
|
-
"
|
|
684
|
+
"clear_temp",
|
|
635
685
|
toolClearTemp,
|
|
636
686
|
async () => {
|
|
637
687
|
try {
|
|
638
688
|
await source.clearTemp();
|
|
639
|
-
return okResult("
|
|
689
|
+
return okResult("clear_temp", { success: true });
|
|
640
690
|
} catch (error) {
|
|
641
691
|
return errorResult(error);
|
|
642
692
|
}
|
|
643
693
|
}
|
|
644
694
|
);
|
|
645
695
|
server.registerTool(
|
|
646
|
-
"
|
|
696
|
+
"copy_page",
|
|
647
697
|
toolCopyPage,
|
|
648
698
|
async ({ originPageSlug, actualPageSlug }) => {
|
|
649
699
|
try {
|
|
@@ -651,7 +701,7 @@ function createPrimeUiMcpServer(source) {
|
|
|
651
701
|
originPageSlug,
|
|
652
702
|
actualPageSlug
|
|
653
703
|
);
|
|
654
|
-
return okResult("
|
|
704
|
+
return okResult("copy_page", result);
|
|
655
705
|
} catch (error) {
|
|
656
706
|
return errorResult(error);
|
|
657
707
|
}
|
|
@@ -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,65 +1231,107 @@ async function fileExists2(filePath) {
|
|
|
1180
1231
|
return false;
|
|
1181
1232
|
}
|
|
1182
1233
|
}
|
|
1183
|
-
function
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
1282
|
+
if (!maybe.pages.every(isExportPageRecord)) {
|
|
1199
1283
|
throw new Error("Export manifest pages payload is invalid.");
|
|
1200
1284
|
}
|
|
1201
1285
|
return {
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
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
|
|
1210
|
-
const
|
|
1211
|
-
const
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
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
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
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
|
|
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 });
|
|
1233
1326
|
const exportDirectories = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
|
|
1234
1327
|
if (exportDirectories.length === 0) {
|
|
1235
1328
|
throw new Error(
|
|
1236
|
-
"No downloaded exports found in .primeui/temp/exports. Run
|
|
1329
|
+
"No downloaded exports found in .primeui/temp/exports. Run download_export first."
|
|
1237
1330
|
);
|
|
1238
1331
|
}
|
|
1239
1332
|
if (exportDirectories.length > 1) {
|
|
1240
1333
|
throw new Error(
|
|
1241
|
-
"Multiple export folders found in .primeui/temp/exports. Clear temp with
|
|
1334
|
+
"Multiple export folders found in .primeui/temp/exports. Clear temp with clear_temp and download one export."
|
|
1242
1335
|
);
|
|
1243
1336
|
}
|
|
1244
1337
|
const exportId = exportDirectories[0] ?? "";
|
|
@@ -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
|
|
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.
|
|
1436
|
+
if (manifest.export.id !== exportId) {
|
|
1344
1437
|
throw new Error(
|
|
1345
|
-
`Manifest
|
|
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
|
|
1386
|
-
|
|
1387
|
-
|
|
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:
|
|
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
|
|
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
|
|
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.
|
|
1728
|
+
return typeof maybe.total === "number" && typeof maybe.successful === "number" && typeof maybe.failed === "number";
|
|
1632
1729
|
}
|
|
1633
|
-
function
|
|
1634
|
-
|
|
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
|
|
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
|
|
1757
|
+
throw new Error("Export manifest is invalid.");
|
|
1639
1758
|
}
|
|
1640
1759
|
const maybe = value;
|
|
1641
|
-
|
|
1642
|
-
|
|
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(
|
|
1645
|
-
throw new Error("Export pages
|
|
1771
|
+
if (!maybe.pages.every(isExportPage)) {
|
|
1772
|
+
throw new Error("Export manifest pages payload is invalid.");
|
|
1646
1773
|
}
|
|
1647
1774
|
return {
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
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
|
|
1681
|
-
|
|
1682
|
-
|
|
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
|
-
|
|
1707
|
-
|
|
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
|
-
|
|
1714
|
-
await readFile4(
|
|
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
|
|
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 (
|
|
1838
|
+
if (manifest.export.id !== id) {
|
|
1724
1839
|
throw new Error(
|
|
1725
|
-
`Export
|
|
1840
|
+
`Export manifest mismatch for id "${id}". Run create_export and retry download.`
|
|
1726
1841
|
);
|
|
1727
1842
|
}
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
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(
|
|
1976
|
+
pages: z3.array(exportPageSchema2)
|
|
1840
1977
|
});
|
|
1841
1978
|
var PrimeUiApiContractError = class extends Error {
|
|
1842
1979
|
constructor(endpoint, details) {
|