@oaysus/cli 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.
Files changed (85) hide show
  1. package/README.md +35 -12
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +151 -85
  4. package/dist/cli.js.map +1 -1
  5. package/dist/components/App.d.ts +26 -10
  6. package/dist/components/App.d.ts.map +1 -1
  7. package/dist/components/App.js +23 -8
  8. package/dist/components/App.js.map +1 -1
  9. package/dist/components/SlashCommands.js +1 -1
  10. package/dist/lib/push.d.ts.map +1 -1
  11. package/dist/lib/push.js +29 -1
  12. package/dist/lib/push.js.map +1 -1
  13. package/dist/lib/shared/commands.d.ts +11 -1
  14. package/dist/lib/shared/commands.d.ts.map +1 -1
  15. package/dist/lib/shared/commands.js +55 -13
  16. package/dist/lib/shared/commands.js.map +1 -1
  17. package/dist/lib/shared/uploader.d.ts +1 -0
  18. package/dist/lib/shared/uploader.d.ts.map +1 -1
  19. package/dist/lib/shared/uploader.js +4 -2
  20. package/dist/lib/shared/uploader.js.map +1 -1
  21. package/dist/lib/site/asset-puller.d.ts +93 -0
  22. package/dist/lib/site/asset-puller.d.ts.map +1 -0
  23. package/dist/lib/site/asset-puller.js +376 -0
  24. package/dist/lib/site/asset-puller.js.map +1 -0
  25. package/dist/lib/site/asset-resolver.d.ts +44 -0
  26. package/dist/lib/site/asset-resolver.d.ts.map +1 -0
  27. package/dist/lib/site/asset-resolver.js +225 -0
  28. package/dist/lib/site/asset-resolver.js.map +1 -0
  29. package/dist/lib/site/asset-uploader.d.ts +58 -0
  30. package/dist/lib/site/asset-uploader.d.ts.map +1 -0
  31. package/dist/lib/site/asset-uploader.js +261 -0
  32. package/dist/lib/site/asset-uploader.js.map +1 -0
  33. package/dist/lib/site/index.d.ts +14 -0
  34. package/dist/lib/site/index.d.ts.map +1 -0
  35. package/dist/lib/site/index.js +15 -0
  36. package/dist/lib/site/index.js.map +1 -0
  37. package/dist/lib/site/metadata.d.ts +99 -0
  38. package/dist/lib/site/metadata.d.ts.map +1 -0
  39. package/dist/lib/site/metadata.js +470 -0
  40. package/dist/lib/site/metadata.js.map +1 -0
  41. package/dist/lib/site/page-publisher.d.ts +51 -0
  42. package/dist/lib/site/page-publisher.d.ts.map +1 -0
  43. package/dist/lib/site/page-publisher.js +460 -0
  44. package/dist/lib/site/page-publisher.js.map +1 -0
  45. package/dist/lib/site/page-puller.d.ts +93 -0
  46. package/dist/lib/site/page-puller.d.ts.map +1 -0
  47. package/dist/lib/site/page-puller.js +346 -0
  48. package/dist/lib/site/page-puller.js.map +1 -0
  49. package/dist/lib/site/page-validator.d.ts +29 -0
  50. package/dist/lib/site/page-validator.d.ts.map +1 -0
  51. package/dist/lib/site/page-validator.js +282 -0
  52. package/dist/lib/site/page-validator.js.map +1 -0
  53. package/dist/lib/site/project-loader.d.ts +45 -0
  54. package/dist/lib/site/project-loader.d.ts.map +1 -0
  55. package/dist/lib/site/project-loader.js +276 -0
  56. package/dist/lib/site/project-loader.js.map +1 -0
  57. package/dist/lib/site/shared-component-resolver.d.ts +89 -0
  58. package/dist/lib/site/shared-component-resolver.d.ts.map +1 -0
  59. package/dist/lib/site/shared-component-resolver.js +222 -0
  60. package/dist/lib/site/shared-component-resolver.js.map +1 -0
  61. package/dist/screens/WelcomeScreen.d.ts +26 -10
  62. package/dist/screens/WelcomeScreen.d.ts.map +1 -1
  63. package/dist/screens/WelcomeScreen.js +44 -21
  64. package/dist/screens/WelcomeScreen.js.map +1 -1
  65. package/dist/screens/site/SiteInitScreen.d.ts +12 -0
  66. package/dist/screens/site/SiteInitScreen.d.ts.map +1 -0
  67. package/dist/screens/site/SiteInitScreen.js +282 -0
  68. package/dist/screens/site/SiteInitScreen.js.map +1 -0
  69. package/dist/screens/site/SitePublishScreen.d.ts +15 -0
  70. package/dist/screens/site/SitePublishScreen.d.ts.map +1 -0
  71. package/dist/screens/site/SitePublishScreen.js +330 -0
  72. package/dist/screens/site/SitePublishScreen.js.map +1 -0
  73. package/dist/screens/site/SitePullScreen.d.ts +14 -0
  74. package/dist/screens/site/SitePullScreen.d.ts.map +1 -0
  75. package/dist/screens/site/SitePullScreen.js +455 -0
  76. package/dist/screens/site/SitePullScreen.js.map +1 -0
  77. package/dist/screens/site/SiteValidateScreen.d.ts +12 -0
  78. package/dist/screens/site/SiteValidateScreen.d.ts.map +1 -0
  79. package/dist/screens/site/SiteValidateScreen.js +201 -0
  80. package/dist/screens/site/SiteValidateScreen.js.map +1 -0
  81. package/dist/types/site.d.ts +348 -0
  82. package/dist/types/site.d.ts.map +1 -0
  83. package/dist/types/site.js +6 -0
  84. package/dist/types/site.js.map +1 -0
  85. package/package.json +1 -1
@@ -0,0 +1,346 @@
1
+ /**
2
+ * Page Puller
3
+ * Pulls pages from the Oaysus platform to local JSON files
4
+ */
5
+ import axios from 'axios';
6
+ import * as fs from 'fs/promises';
7
+ import * as path from 'path';
8
+ import { loadCredentials } from '../shared/auth.js';
9
+ import { SSO_BASE_URL, debug } from '../shared/config.js';
10
+ /**
11
+ * Convert a slug to a local filename
12
+ * / -> home.json (matches site init convention)
13
+ * /about -> about.json
14
+ * /services/training -> services-training.json
15
+ */
16
+ export function slugToFilename(slug) {
17
+ if (slug === '/') {
18
+ return 'home.json';
19
+ }
20
+ // Remove leading slash, replace remaining slashes with dashes
21
+ const name = slug.replace(/^\//, '').replace(/\//g, '-');
22
+ return `${name}.json`;
23
+ }
24
+ /**
25
+ * Convert server page to local PageDefinition format
26
+ * Strips server-only fields but preserves shared component relationships
27
+ */
28
+ function serverPageToLocal(serverPage) {
29
+ // Map components, preserving shared component info for round-trip
30
+ const localComponents = (serverPage.components || []).map(comp => {
31
+ const localComp = {
32
+ type: comp.type,
33
+ props: comp.props,
34
+ };
35
+ // Preserve optional fields
36
+ if (comp.id) {
37
+ localComp.id = comp.id;
38
+ }
39
+ if (comp.settings) {
40
+ localComp.settings = comp.settings;
41
+ }
42
+ // Preserve shared component info for round-trip support
43
+ if (comp.isGlobal && comp.globalId) {
44
+ localComp.shared = true;
45
+ localComp.globalId = comp.globalId;
46
+ }
47
+ return localComp;
48
+ });
49
+ return {
50
+ slug: serverPage.slug,
51
+ title: serverPage.title,
52
+ description: serverPage.description,
53
+ isHomePage: serverPage.isHomePage || false,
54
+ components: localComponents,
55
+ settings: serverPage.settings || {},
56
+ };
57
+ }
58
+ /**
59
+ * Check if a file exists
60
+ */
61
+ async function fileExists(filePath) {
62
+ try {
63
+ await fs.access(filePath);
64
+ return true;
65
+ }
66
+ catch {
67
+ return false;
68
+ }
69
+ }
70
+ /**
71
+ * Fetch all pages from the server
72
+ */
73
+ async function fetchPages(websiteId, jwt) {
74
+ const url = `${SSO_BASE_URL}/hosting/page-builder/pages`;
75
+ debug('Fetching pages from:', url);
76
+ try {
77
+ const response = await axios.get(url, {
78
+ params: { websiteId },
79
+ headers: {
80
+ Authorization: `Bearer ${jwt}`,
81
+ },
82
+ });
83
+ if (response.data.success && response.data.pages) {
84
+ return { success: true, pages: response.data.pages };
85
+ }
86
+ return { success: false, pages: [], error: response.data.error || 'Unknown error' };
87
+ }
88
+ catch (error) {
89
+ let errorMessage = 'Failed to fetch pages';
90
+ if (axios.isAxiosError(error)) {
91
+ if (error.response?.status === 401) {
92
+ errorMessage = 'Authentication failed. Please run "oaysus login"';
93
+ }
94
+ else {
95
+ errorMessage = error.response?.data?.error ||
96
+ error.response?.data?.detail?.error ||
97
+ error.message;
98
+ }
99
+ }
100
+ else if (error instanceof Error) {
101
+ errorMessage = error.message;
102
+ }
103
+ return { success: false, pages: [], error: errorMessage };
104
+ }
105
+ }
106
+ /**
107
+ * Preview what pages would be pulled without actually pulling them
108
+ * Use this to show the user what will happen before confirming
109
+ */
110
+ export async function previewPull(options) {
111
+ const { projectPath, config } = options;
112
+ // Get credentials if not provided
113
+ let websiteId = options.websiteId || config.websiteId;
114
+ let jwt = options.jwt;
115
+ if (!websiteId || !jwt) {
116
+ const credentials = await loadCredentials();
117
+ if (!credentials) {
118
+ return {
119
+ success: false,
120
+ error: 'Not logged in. Please run "oaysus login"',
121
+ pages: [],
122
+ newPages: [],
123
+ existingPages: [],
124
+ };
125
+ }
126
+ websiteId = websiteId || credentials.websiteId;
127
+ jwt = jwt || credentials.jwt;
128
+ }
129
+ if (!websiteId) {
130
+ return {
131
+ success: false,
132
+ error: 'No website ID found. Set websiteId in oaysus.website.json or run "oaysus login"',
133
+ pages: [],
134
+ newPages: [],
135
+ existingPages: [],
136
+ };
137
+ }
138
+ // Fetch pages from server
139
+ const fetchResult = await fetchPages(websiteId, jwt);
140
+ if (!fetchResult.success) {
141
+ return {
142
+ success: false,
143
+ error: fetchResult.error || 'Failed to fetch pages',
144
+ pages: [],
145
+ newPages: [],
146
+ existingPages: [],
147
+ };
148
+ }
149
+ const serverPages = fetchResult.pages;
150
+ const pagesDir = path.join(projectPath, 'pages');
151
+ const pages = [];
152
+ const newPages = [];
153
+ const existingPages = [];
154
+ for (const serverPage of serverPages) {
155
+ const filename = slugToFilename(serverPage.slug);
156
+ const filePath = path.join(pagesDir, filename);
157
+ const relativePath = path.join('pages', filename);
158
+ const exists = await fileExists(filePath);
159
+ const previewPage = {
160
+ slug: serverPage.slug,
161
+ title: serverPage.title,
162
+ file: relativePath,
163
+ existsLocally: exists,
164
+ };
165
+ pages.push(previewPage);
166
+ if (exists) {
167
+ existingPages.push(previewPage);
168
+ }
169
+ else {
170
+ newPages.push(previewPage);
171
+ }
172
+ }
173
+ return {
174
+ success: true,
175
+ pages,
176
+ newPages,
177
+ existingPages,
178
+ };
179
+ }
180
+ /**
181
+ * Pull all pages from the server to local files
182
+ */
183
+ export async function pullPages(options) {
184
+ const { projectPath, config, force = false, dryRun = false, onProgress } = options;
185
+ // Get credentials if not provided
186
+ let websiteId = options.websiteId || config.websiteId;
187
+ let jwt = options.jwt;
188
+ if (!websiteId || !jwt) {
189
+ const credentials = await loadCredentials();
190
+ if (!credentials) {
191
+ return {
192
+ success: false,
193
+ pages: [],
194
+ fetched: 0,
195
+ written: 0,
196
+ skipped: 0,
197
+ errors: ['Not logged in. Please run "oaysus login"'],
198
+ };
199
+ }
200
+ websiteId = websiteId || credentials.websiteId;
201
+ jwt = jwt || credentials.jwt;
202
+ }
203
+ if (!websiteId) {
204
+ return {
205
+ success: false,
206
+ pages: [],
207
+ fetched: 0,
208
+ written: 0,
209
+ skipped: 0,
210
+ errors: ['No website ID found. Set websiteId in oaysus.website.json or run "oaysus login"'],
211
+ };
212
+ }
213
+ // Fetch pages from server
214
+ if (onProgress) {
215
+ onProgress('fetching', 0, 1);
216
+ }
217
+ const fetchResult = await fetchPages(websiteId, jwt);
218
+ if (!fetchResult.success) {
219
+ return {
220
+ success: false,
221
+ pages: [],
222
+ fetched: 0,
223
+ written: 0,
224
+ skipped: 0,
225
+ errors: [fetchResult.error || 'Failed to fetch pages'],
226
+ };
227
+ }
228
+ const serverPages = fetchResult.pages;
229
+ if (serverPages.length === 0) {
230
+ return {
231
+ success: true,
232
+ pages: [],
233
+ fetched: 0,
234
+ written: 0,
235
+ skipped: 0,
236
+ errors: [],
237
+ };
238
+ }
239
+ if (onProgress) {
240
+ onProgress('fetching', 1, 1);
241
+ }
242
+ // Ensure pages directory exists
243
+ const pagesDir = path.join(projectPath, 'pages');
244
+ if (!dryRun) {
245
+ await fs.mkdir(pagesDir, { recursive: true });
246
+ }
247
+ // Write pages to local files
248
+ const results = [];
249
+ let written = 0;
250
+ let skipped = 0;
251
+ const errors = [];
252
+ for (let i = 0; i < serverPages.length; i++) {
253
+ const serverPage = serverPages[i];
254
+ const filename = slugToFilename(serverPage.slug);
255
+ const filePath = path.join(pagesDir, filename);
256
+ const relativePath = path.join('pages', filename);
257
+ if (onProgress) {
258
+ onProgress('writing', i + 1, serverPages.length, serverPage.slug);
259
+ }
260
+ // Check if file already exists
261
+ const exists = await fileExists(filePath);
262
+ if (exists && !force) {
263
+ results.push({
264
+ slug: serverPage.slug,
265
+ file: relativePath,
266
+ action: 'skipped',
267
+ });
268
+ skipped++;
269
+ continue;
270
+ }
271
+ // Convert to local format
272
+ const localPage = serverPageToLocal(serverPage);
273
+ // Write file (unless dry run)
274
+ if (!dryRun) {
275
+ try {
276
+ const content = JSON.stringify(localPage, null, 2);
277
+ await fs.writeFile(filePath, content, 'utf-8');
278
+ }
279
+ catch (error) {
280
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
281
+ errors.push(`Failed to write ${filename}: ${errorMessage}`);
282
+ continue;
283
+ }
284
+ }
285
+ results.push({
286
+ slug: serverPage.slug,
287
+ file: relativePath,
288
+ action: exists ? 'updated' : 'created',
289
+ });
290
+ written++;
291
+ }
292
+ return {
293
+ success: errors.length === 0,
294
+ pages: results,
295
+ fetched: serverPages.length,
296
+ written,
297
+ skipped,
298
+ errors,
299
+ };
300
+ }
301
+ /**
302
+ * Format pull results for display
303
+ */
304
+ export function formatPullResults(result) {
305
+ const lines = [];
306
+ if (result.fetched === 0) {
307
+ lines.push('No pages found on server');
308
+ return lines;
309
+ }
310
+ lines.push(`✓ Fetched ${result.fetched} page${result.fetched === 1 ? '' : 's'} from server`);
311
+ lines.push('');
312
+ if (result.written > 0 || result.skipped > 0) {
313
+ lines.push('Pages:');
314
+ for (const page of result.pages) {
315
+ const icon = page.action === 'created' ? '+' :
316
+ page.action === 'updated' ? '~' :
317
+ page.action === 'skipped' ? '-' : '?';
318
+ const suffix = page.action === 'skipped' ? ' (skipped, use --force to overwrite)' : '';
319
+ lines.push(` ${icon} ${page.file} (${page.slug})${suffix}`);
320
+ }
321
+ lines.push('');
322
+ }
323
+ if (result.errors.length > 0) {
324
+ lines.push('Errors:');
325
+ for (const error of result.errors) {
326
+ lines.push(` ✗ ${error}`);
327
+ }
328
+ lines.push('');
329
+ }
330
+ // Summary
331
+ const parts = [];
332
+ if (result.written > 0) {
333
+ parts.push(`${result.written} written`);
334
+ }
335
+ if (result.skipped > 0) {
336
+ parts.push(`${result.skipped} skipped`);
337
+ }
338
+ if (result.errors.length > 0) {
339
+ parts.push(`${result.errors.length} error${result.errors.length === 1 ? '' : 's'}`);
340
+ }
341
+ if (parts.length > 0) {
342
+ lines.push(`Summary: ${parts.join(', ')}`);
343
+ }
344
+ return lines;
345
+ }
346
+ //# sourceMappingURL=page-puller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-puller.js","sourceRoot":"","sources":["../../../src/lib/site/page-puller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Page Validator
3
+ * Validates pages against installed components on a website
4
+ */
5
+ import type { PageDefinition, LoadedProject, PageValidationResult, SiteValidationResult, InstalledComponent } from '../../types/site.js';
6
+ /**
7
+ * Fetch installed components for a website
8
+ */
9
+ export declare function fetchInstalledComponents(websiteId: string, jwt: string): Promise<InstalledComponent[]>;
10
+ /**
11
+ * Validate a single page against installed components
12
+ */
13
+ export declare function validatePage(pageFile: string, page: PageDefinition, installedComponents: InstalledComponent[], projectPath: string): Promise<PageValidationResult>;
14
+ /**
15
+ * Validate an entire website project
16
+ */
17
+ export declare function validateProject(project: LoadedProject, options?: {
18
+ websiteId?: string;
19
+ jwt?: string;
20
+ }): Promise<SiteValidationResult>;
21
+ /**
22
+ * Get list of missing components across all pages
23
+ */
24
+ export declare function getMissingComponents(result: SiteValidationResult): string[];
25
+ /**
26
+ * Format validation results for display
27
+ */
28
+ export declare function formatValidationResults(result: SiteValidationResult): string[];
29
+ //# sourceMappingURL=page-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-validator.d.ts","sourceRoot":"","sources":["../../../src/lib/site/page-validator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAEnB,MAAM,qBAAqB,CAAC;AAM7B;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAsC/B;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,cAAc,EACpB,mBAAmB,EAAE,kBAAkB,EAAE,EACzC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,oBAAoB,CAAC,CA6D/B;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE;IACP,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACT,GACL,OAAO,CAAC,oBAAoB,CAAC,CAqI/B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAQ3E;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,EAAE,CA6C9E"}
@@ -0,0 +1,282 @@
1
+ /**
2
+ * Page Validator
3
+ * Validates pages against installed components on a website
4
+ */
5
+ import axios from 'axios';
6
+ import { loadCredentials } from '../shared/auth.js';
7
+ import { SSO_BASE_URL, debug } from '../shared/config.js';
8
+ import { findAssetsInPage, assetExists } from './asset-resolver.js';
9
+ import { loadComponentCatalog, syncComponentCatalog, isCatalogStale } from './metadata.js';
10
+ /**
11
+ * Fetch installed components for a website
12
+ */
13
+ export async function fetchInstalledComponents(websiteId, jwt) {
14
+ const url = `${SSO_BASE_URL}/hosting/page-builder/components/custom`;
15
+ debug('Fetching installed components');
16
+ debug('API URL:', url);
17
+ debug('Website ID:', websiteId);
18
+ try {
19
+ const response = await axios.get(url, {
20
+ params: { websiteId },
21
+ headers: {
22
+ Authorization: `Bearer ${jwt}`,
23
+ },
24
+ });
25
+ if (response.data.success && response.data.components) {
26
+ return response.data.components.map((c) => ({
27
+ id: c.id,
28
+ name: c.name,
29
+ displayName: c.displayName,
30
+ sourceThemePackId: c.themePackId,
31
+ sourceThemePackName: c.themePack?.displayName,
32
+ schema: c.schema,
33
+ }));
34
+ }
35
+ return [];
36
+ }
37
+ catch (error) {
38
+ if (axios.isAxiosError(error)) {
39
+ debug('Fetch components failed:', error.response?.status, error.response?.data);
40
+ throw new Error(error.response?.data?.error ||
41
+ error.response?.data?.detail?.error ||
42
+ 'Failed to fetch installed components');
43
+ }
44
+ throw error;
45
+ }
46
+ }
47
+ /**
48
+ * Validate a single page against installed components
49
+ */
50
+ export async function validatePage(pageFile, page, installedComponents, projectPath) {
51
+ const errors = [];
52
+ const warnings = [];
53
+ const unresolvedComponents = [];
54
+ const localAssets = [];
55
+ // Build lookup set for installed component names
56
+ const installedNames = new Set(installedComponents.map(c => c.name));
57
+ // Validate each component
58
+ for (let i = 0; i < page.components.length; i++) {
59
+ const component = page.components[i];
60
+ // Check if component type is installed
61
+ if (!installedNames.has(component.type)) {
62
+ unresolvedComponents.push(component.type);
63
+ errors.push(`Component "${component.type}" is not installed on this website`);
64
+ }
65
+ // TODO: Validate props against component schema
66
+ // This would require fetching and parsing the component schemas
67
+ }
68
+ // Find local asset references
69
+ const assets = findAssetsInPage(page, pageFile);
70
+ localAssets.push(...assets);
71
+ // Verify local assets exist
72
+ for (const asset of assets) {
73
+ const exists = await assetExists(asset, projectPath);
74
+ if (!exists) {
75
+ errors.push(`Asset not found: ${asset.localPath} (referenced in ${pageFile})`);
76
+ }
77
+ }
78
+ // Validate slug format
79
+ if (!page.slug.startsWith('/')) {
80
+ errors.push(`Slug must start with "/": ${page.slug}`);
81
+ }
82
+ // Warn about empty descriptions
83
+ if (!page.description) {
84
+ warnings.push('Page is missing a description (recommended for SEO)');
85
+ }
86
+ // Warn if SEO settings are missing
87
+ if (!page.settings?.seo?.metaTitle) {
88
+ warnings.push('Page is missing metaTitle in SEO settings');
89
+ }
90
+ return {
91
+ valid: errors.length === 0,
92
+ pageFile,
93
+ slug: page.slug,
94
+ errors,
95
+ warnings,
96
+ unresolvedComponents,
97
+ localAssets,
98
+ };
99
+ }
100
+ /**
101
+ * Validate an entire website project
102
+ */
103
+ export async function validateProject(project, options = {}) {
104
+ // Get credentials if not provided
105
+ let websiteId = options.websiteId;
106
+ let jwt = options.jwt;
107
+ if (!websiteId || !jwt) {
108
+ const credentials = await loadCredentials();
109
+ if (!credentials) {
110
+ return {
111
+ valid: false,
112
+ pages: [],
113
+ totalErrors: 1,
114
+ totalWarnings: 0,
115
+ allAssets: [],
116
+ };
117
+ }
118
+ websiteId = websiteId || project.config.websiteId || credentials.websiteId;
119
+ jwt = jwt || credentials.jwt;
120
+ }
121
+ if (!websiteId) {
122
+ return {
123
+ valid: false,
124
+ pages: [{
125
+ valid: false,
126
+ pageFile: '',
127
+ slug: '',
128
+ errors: ['No website ID found. Set websiteId in oaysus.website.json or run "oaysus login"'],
129
+ warnings: [],
130
+ unresolvedComponents: [],
131
+ localAssets: [],
132
+ }],
133
+ totalErrors: 1,
134
+ totalWarnings: 0,
135
+ allAssets: [],
136
+ };
137
+ }
138
+ // Get installed components from local catalog or fetch from server
139
+ let installedComponents = [];
140
+ try {
141
+ // Check local catalog first
142
+ const catalog = await loadComponentCatalog(project.projectPath);
143
+ const isStale = await isCatalogStale(project.projectPath);
144
+ if (catalog && catalog.themePacks.length > 0 && !isStale) {
145
+ // Use local catalog
146
+ debug('Using local component catalog');
147
+ for (const pack of catalog.themePacks) {
148
+ for (const comp of pack.components) {
149
+ installedComponents.push({
150
+ id: `${pack.id}:${comp.type}`,
151
+ name: comp.type,
152
+ displayName: comp.displayName,
153
+ sourceThemePackId: pack.id,
154
+ sourceThemePackName: pack.name,
155
+ schema: comp.schema,
156
+ });
157
+ }
158
+ }
159
+ }
160
+ else {
161
+ // Sync catalog from server
162
+ debug('Syncing component catalog from server');
163
+ const syncResult = await syncComponentCatalog({
164
+ projectPath: project.projectPath,
165
+ websiteId,
166
+ jwt,
167
+ force: true,
168
+ });
169
+ if (syncResult.success && syncResult.catalog) {
170
+ for (const pack of syncResult.catalog.themePacks) {
171
+ for (const comp of pack.components) {
172
+ installedComponents.push({
173
+ id: `${pack.id}:${comp.type}`,
174
+ name: comp.type,
175
+ displayName: comp.displayName,
176
+ sourceThemePackId: pack.id,
177
+ sourceThemePackName: pack.name,
178
+ schema: comp.schema,
179
+ });
180
+ }
181
+ }
182
+ }
183
+ else {
184
+ // Fallback to direct API fetch if sync fails
185
+ installedComponents = await fetchInstalledComponents(websiteId, jwt);
186
+ }
187
+ }
188
+ }
189
+ catch (error) {
190
+ return {
191
+ valid: false,
192
+ pages: [{
193
+ valid: false,
194
+ pageFile: '',
195
+ slug: '',
196
+ errors: [`Failed to fetch installed components: ${error instanceof Error ? error.message : 'Unknown error'}`],
197
+ warnings: [],
198
+ unresolvedComponents: [],
199
+ localAssets: [],
200
+ }],
201
+ totalErrors: 1,
202
+ totalWarnings: 0,
203
+ allAssets: [],
204
+ };
205
+ }
206
+ // Validate each page
207
+ const pageResults = [];
208
+ const allAssets = [];
209
+ for (const page of project.pages) {
210
+ const result = await validatePage(page.file, page.definition, installedComponents, project.projectPath);
211
+ pageResults.push(result);
212
+ allAssets.push(...result.localAssets);
213
+ }
214
+ // Calculate totals
215
+ const totalErrors = pageResults.reduce((sum, p) => sum + p.errors.length, 0);
216
+ const totalWarnings = pageResults.reduce((sum, p) => sum + p.warnings.length, 0);
217
+ return {
218
+ valid: totalErrors === 0,
219
+ pages: pageResults,
220
+ totalErrors,
221
+ totalWarnings,
222
+ allAssets,
223
+ };
224
+ }
225
+ /**
226
+ * Get list of missing components across all pages
227
+ */
228
+ export function getMissingComponents(result) {
229
+ const missing = new Set();
230
+ for (const page of result.pages) {
231
+ for (const comp of page.unresolvedComponents) {
232
+ missing.add(comp);
233
+ }
234
+ }
235
+ return Array.from(missing);
236
+ }
237
+ /**
238
+ * Format validation results for display
239
+ */
240
+ export function formatValidationResults(result) {
241
+ const lines = [];
242
+ if (result.valid) {
243
+ lines.push('✓ All pages validated successfully');
244
+ lines.push(` ${result.pages.length} pages, ${result.allAssets.length} assets`);
245
+ }
246
+ else {
247
+ lines.push('✗ Validation failed');
248
+ lines.push('');
249
+ for (const page of result.pages) {
250
+ if (!page.valid) {
251
+ lines.push(` ${page.pageFile} (${page.slug})`);
252
+ for (const error of page.errors) {
253
+ lines.push(` ✗ ${error}`);
254
+ }
255
+ }
256
+ }
257
+ }
258
+ // Show warnings even if valid
259
+ const pagesWithWarnings = result.pages.filter(p => p.warnings.length > 0);
260
+ if (pagesWithWarnings.length > 0) {
261
+ lines.push('');
262
+ lines.push('Warnings:');
263
+ for (const page of pagesWithWarnings) {
264
+ for (const warning of page.warnings) {
265
+ lines.push(` ⚠ ${page.pageFile}: ${warning}`);
266
+ }
267
+ }
268
+ }
269
+ // Show missing components summary
270
+ const missing = getMissingComponents(result);
271
+ if (missing.length > 0) {
272
+ lines.push('');
273
+ lines.push('Missing components:');
274
+ for (const comp of missing) {
275
+ lines.push(` • ${comp}`);
276
+ }
277
+ lines.push('');
278
+ lines.push('Install these components via theme packs or upload them with "oaysus theme push"');
279
+ }
280
+ return lines;
281
+ }
282
+ //# sourceMappingURL=page-validator.js.map