@ucptools/validator 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CLAUDE.md +109 -0
  2. package/CONTRIBUTING.md +113 -0
  3. package/LICENSE +21 -0
  4. package/README.md +203 -0
  5. package/api/analyze-feed.js +140 -0
  6. package/api/badge.js +185 -0
  7. package/api/benchmark.js +177 -0
  8. package/api/directory-stats.ts +29 -0
  9. package/api/directory.ts +73 -0
  10. package/api/generate-compliance.js +143 -0
  11. package/api/generate-schema.js +457 -0
  12. package/api/generate.js +132 -0
  13. package/api/security-scan.js +133 -0
  14. package/api/simulate.js +187 -0
  15. package/api/tsconfig.json +10 -0
  16. package/api/validate.js +1351 -0
  17. package/apify-actor/.actor/actor.json +68 -0
  18. package/apify-actor/.actor/input_schema.json +32 -0
  19. package/apify-actor/APIFY-STORE-LISTING.md +412 -0
  20. package/apify-actor/Dockerfile +8 -0
  21. package/apify-actor/README.md +166 -0
  22. package/apify-actor/main.ts +111 -0
  23. package/apify-actor/package.json +17 -0
  24. package/apify-actor/src/main.js +199 -0
  25. package/docs/BRAND-IDENTITY.md +238 -0
  26. package/docs/BRAND-STYLE-GUIDE.md +356 -0
  27. package/drizzle/0000_black_king_cobra.sql +39 -0
  28. package/drizzle/meta/0000_snapshot.json +309 -0
  29. package/drizzle/meta/_journal.json +13 -0
  30. package/drizzle.config.ts +10 -0
  31. package/examples/full-profile.json +70 -0
  32. package/examples/minimal-profile.json +23 -0
  33. package/package.json +69 -0
  34. package/public/.well-known/ucp +25 -0
  35. package/public/android-chrome-192x192.png +0 -0
  36. package/public/android-chrome-512x512.png +0 -0
  37. package/public/apple-touch-icon.png +0 -0
  38. package/public/brand.css +321 -0
  39. package/public/directory.html +701 -0
  40. package/public/favicon-16x16.png +0 -0
  41. package/public/favicon-32x32.png +0 -0
  42. package/public/favicon.ico +0 -0
  43. package/public/guides/bigcommerce.html +743 -0
  44. package/public/guides/fastucp.html +838 -0
  45. package/public/guides/magento.html +779 -0
  46. package/public/guides/shopify.html +726 -0
  47. package/public/guides/squarespace.html +749 -0
  48. package/public/guides/wix.html +747 -0
  49. package/public/guides/woocommerce.html +733 -0
  50. package/public/index.html +3835 -0
  51. package/public/learn.html +396 -0
  52. package/public/logo.jpeg +0 -0
  53. package/public/og-image-icon.png +0 -0
  54. package/public/og-image.png +0 -0
  55. package/public/robots.txt +6 -0
  56. package/public/site.webmanifest +31 -0
  57. package/public/sitemap.xml +69 -0
  58. package/public/social/linkedin-banner-1128x191.png +0 -0
  59. package/public/social/temp.PNG +0 -0
  60. package/public/social/x-header-1500x500.png +0 -0
  61. package/public/verify.html +410 -0
  62. package/scripts/generate-favicons.js +44 -0
  63. package/scripts/generate-ico.js +23 -0
  64. package/scripts/generate-og-image.js +45 -0
  65. package/scripts/reset-db.ts +77 -0
  66. package/scripts/seed-db.ts +71 -0
  67. package/scripts/setup-benchmark-db.js +70 -0
  68. package/src/api/server.ts +266 -0
  69. package/src/cli/index.ts +302 -0
  70. package/src/compliance/compliance-generator.ts +452 -0
  71. package/src/compliance/index.ts +28 -0
  72. package/src/compliance/templates.ts +338 -0
  73. package/src/compliance/types.ts +170 -0
  74. package/src/db/index.ts +28 -0
  75. package/src/db/schema.ts +84 -0
  76. package/src/feed-analyzer/feed-analyzer.ts +726 -0
  77. package/src/feed-analyzer/index.ts +34 -0
  78. package/src/feed-analyzer/types.ts +354 -0
  79. package/src/generator/index.ts +7 -0
  80. package/src/generator/key-generator.ts +124 -0
  81. package/src/generator/profile-builder.ts +402 -0
  82. package/src/hosting/artifacts-generator.ts +679 -0
  83. package/src/hosting/index.ts +6 -0
  84. package/src/index.ts +105 -0
  85. package/src/security/index.ts +15 -0
  86. package/src/security/security-scanner.ts +604 -0
  87. package/src/security/types.ts +55 -0
  88. package/src/services/directory.ts +434 -0
  89. package/src/simulator/agent-simulator.ts +941 -0
  90. package/src/simulator/index.ts +7 -0
  91. package/src/simulator/types.ts +170 -0
  92. package/src/types/generator.ts +140 -0
  93. package/src/types/index.ts +7 -0
  94. package/src/types/ucp-profile.ts +140 -0
  95. package/src/types/validation.ts +89 -0
  96. package/src/validator/index.ts +194 -0
  97. package/src/validator/network-validator.ts +417 -0
  98. package/src/validator/rules-validator.ts +297 -0
  99. package/src/validator/sdk-validator.ts +330 -0
  100. package/src/validator/structural-validator.ts +476 -0
  101. package/tests/fixtures/non-compliant-profile.json +25 -0
  102. package/tests/fixtures/official-sample-profile.json +75 -0
  103. package/tests/integration/benchmark.test.ts +207 -0
  104. package/tests/integration/database.test.ts +163 -0
  105. package/tests/integration/directory-api.test.ts +268 -0
  106. package/tests/integration/simulate-api.test.ts +230 -0
  107. package/tests/integration/validate-api.test.ts +269 -0
  108. package/tests/setup.ts +15 -0
  109. package/tests/unit/agent-simulator.test.ts +575 -0
  110. package/tests/unit/compliance-generator.test.ts +374 -0
  111. package/tests/unit/directory-service.test.ts +272 -0
  112. package/tests/unit/feed-analyzer.test.ts +517 -0
  113. package/tests/unit/lint-suggestions.test.ts +423 -0
  114. package/tests/unit/official-samples.test.ts +211 -0
  115. package/tests/unit/pdf-report.test.ts +390 -0
  116. package/tests/unit/sdk-validator.test.ts +531 -0
  117. package/tests/unit/security-scanner.test.ts +410 -0
  118. package/tests/unit/validation.test.ts +390 -0
  119. package/tsconfig.json +20 -0
  120. package/vercel.json +34 -0
  121. package/vitest.config.ts +22 -0
@@ -0,0 +1,679 @@
1
+ /**
2
+ * Hosting Artifacts Generator
3
+ * Generates installation artifacts for different hosting platforms
4
+ */
5
+
6
+ import type {
7
+ HostingConfig,
8
+ HostingMode,
9
+ HostingPlatform,
10
+ InstallArtifact,
11
+ } from '../types/generator.js';
12
+
13
+ const HOSTED_PROFILE_BASE_URL = 'https://profiles.ucp.tools';
14
+
15
+ /**
16
+ * Generate all installation artifacts for a hosting configuration
17
+ */
18
+ export function generateHostingArtifacts(
19
+ config: HostingConfig,
20
+ profileJson: string
21
+ ): InstallArtifact[] {
22
+ const artifacts: InstallArtifact[] = [];
23
+
24
+ // Always include the profile JSON
25
+ artifacts.push({
26
+ filename: 'ucp.json',
27
+ content: profileJson,
28
+ contentType: 'json',
29
+ description: 'UCP Business Profile JSON file',
30
+ });
31
+
32
+ // Generate platform-specific artifacts
33
+ switch (config.mode) {
34
+ case 'static':
35
+ artifacts.push(...generateStaticArtifacts(config));
36
+ break;
37
+ case 'edge-worker':
38
+ artifacts.push(...generateEdgeWorkerArtifacts(config));
39
+ break;
40
+ case 'reverse-proxy':
41
+ artifacts.push(...generateReverseProxyArtifacts(config));
42
+ break;
43
+ }
44
+
45
+ // Always include README
46
+ artifacts.push(generateReadme(config));
47
+
48
+ return artifacts;
49
+ }
50
+
51
+ /**
52
+ * Generate artifacts for static file hosting
53
+ */
54
+ function generateStaticArtifacts(config: HostingConfig): InstallArtifact[] {
55
+ const artifacts: InstallArtifact[] = [];
56
+ const platform = config.platform || 'generic';
57
+
58
+ switch (platform) {
59
+ case 'nginx':
60
+ artifacts.push(generateNginxStaticConfig(config));
61
+ break;
62
+ case 'apache':
63
+ artifacts.push(generateApacheConfig(config));
64
+ break;
65
+ case 'vercel':
66
+ artifacts.push(generateVercelConfig(config));
67
+ break;
68
+ case 'netlify':
69
+ artifacts.push(generateNetlifyConfig(config));
70
+ break;
71
+ case 'cloudflare-pages':
72
+ artifacts.push(generateCloudflarePagesFunctions(config));
73
+ break;
74
+ case 's3-cloudfront':
75
+ artifacts.push(generateS3Instructions(config));
76
+ break;
77
+ default:
78
+ artifacts.push(generateGenericStaticInstructions(config));
79
+ }
80
+
81
+ return artifacts;
82
+ }
83
+
84
+ /**
85
+ * Generate artifacts for edge worker hosting
86
+ */
87
+ function generateEdgeWorkerArtifacts(config: HostingConfig): InstallArtifact[] {
88
+ const artifacts: InstallArtifact[] = [];
89
+ const platform = config.platform || 'cloudflare-worker';
90
+
91
+ if (platform === 'cloudflare-worker') {
92
+ artifacts.push(generateCloudflareWorker(config));
93
+ artifacts.push(generateWranglerConfig(config));
94
+ }
95
+
96
+ return artifacts;
97
+ }
98
+
99
+ /**
100
+ * Generate artifacts for reverse proxy hosting
101
+ */
102
+ function generateReverseProxyArtifacts(config: HostingConfig): InstallArtifact[] {
103
+ const artifacts: InstallArtifact[] = [];
104
+
105
+ artifacts.push(generateNginxProxyConfig(config));
106
+
107
+ return artifacts;
108
+ }
109
+
110
+ /**
111
+ * Nginx static file configuration
112
+ */
113
+ function generateNginxStaticConfig(config: HostingConfig): InstallArtifact {
114
+ const content = `# Nginx configuration for UCP profile at ${config.merchantDomain}
115
+ # Add this to your server block
116
+
117
+ location = /.well-known/ucp {
118
+ alias /var/www/${config.merchantDomain}/ucp.json;
119
+ default_type application/json;
120
+
121
+ # CORS headers (required for cross-origin discovery)
122
+ add_header Access-Control-Allow-Origin "*" always;
123
+ add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
124
+ add_header Access-Control-Allow-Headers "Accept, Content-Type" always;
125
+
126
+ # Caching (1 hour recommended)
127
+ add_header Cache-Control "public, max-age=3600" always;
128
+
129
+ # Handle preflight requests
130
+ if ($request_method = OPTIONS) {
131
+ return 204;
132
+ }
133
+ }
134
+ `;
135
+
136
+ return {
137
+ filename: 'nginx-ucp.conf',
138
+ content,
139
+ contentType: 'nginx',
140
+ description: 'Nginx configuration snippet for static UCP profile',
141
+ };
142
+ }
143
+
144
+ /**
145
+ * Apache configuration
146
+ */
147
+ function generateApacheConfig(config: HostingConfig): InstallArtifact {
148
+ const content = `# Apache configuration for UCP profile at ${config.merchantDomain}
149
+ # Add this to your VirtualHost or .htaccess
150
+
151
+ <IfModule mod_rewrite.c>
152
+ RewriteEngine On
153
+ RewriteRule ^.well-known/ucp$ /ucp.json [L]
154
+ </IfModule>
155
+
156
+ <Files "ucp.json">
157
+ ForceType application/json
158
+
159
+ # CORS headers
160
+ Header set Access-Control-Allow-Origin "*"
161
+ Header set Access-Control-Allow-Methods "GET, OPTIONS"
162
+ Header set Access-Control-Allow-Headers "Accept, Content-Type"
163
+
164
+ # Caching
165
+ Header set Cache-Control "public, max-age=3600"
166
+ </Files>
167
+
168
+ <IfModule mod_headers.c>
169
+ # Handle preflight requests for .well-known/ucp
170
+ <LocationMatch "^/.well-known/ucp$">
171
+ Header always set Access-Control-Allow-Origin "*"
172
+ Header always set Access-Control-Allow-Methods "GET, OPTIONS"
173
+ </LocationMatch>
174
+ </IfModule>
175
+ `;
176
+
177
+ return {
178
+ filename: '.htaccess',
179
+ content,
180
+ contentType: 'apache',
181
+ description: 'Apache configuration for UCP profile',
182
+ };
183
+ }
184
+
185
+ /**
186
+ * Vercel configuration
187
+ */
188
+ function generateVercelConfig(config: HostingConfig): InstallArtifact {
189
+ const content = JSON.stringify({
190
+ rewrites: [
191
+ { source: '/.well-known/ucp', destination: '/ucp.json' }
192
+ ],
193
+ headers: [
194
+ {
195
+ source: '/.well-known/ucp',
196
+ headers: [
197
+ { key: 'Content-Type', value: 'application/json' },
198
+ { key: 'Access-Control-Allow-Origin', value: '*' },
199
+ { key: 'Access-Control-Allow-Methods', value: 'GET, OPTIONS' },
200
+ { key: 'Cache-Control', value: 'public, max-age=3600' }
201
+ ]
202
+ },
203
+ {
204
+ source: '/ucp.json',
205
+ headers: [
206
+ { key: 'Content-Type', value: 'application/json' },
207
+ { key: 'Access-Control-Allow-Origin', value: '*' },
208
+ { key: 'Cache-Control', value: 'public, max-age=3600' }
209
+ ]
210
+ }
211
+ ]
212
+ }, null, 2);
213
+
214
+ return {
215
+ filename: 'vercel.json',
216
+ content,
217
+ contentType: 'json',
218
+ description: 'Vercel configuration for UCP profile routing',
219
+ };
220
+ }
221
+
222
+ /**
223
+ * Netlify configuration
224
+ */
225
+ function generateNetlifyConfig(config: HostingConfig): InstallArtifact {
226
+ const redirects = `# Netlify redirects for UCP profile
227
+ /.well-known/ucp /ucp.json 200
228
+ `;
229
+
230
+ const headers = `# Netlify headers
231
+ /.well-known/ucp
232
+ Content-Type: application/json
233
+ Access-Control-Allow-Origin: *
234
+ Access-Control-Allow-Methods: GET, OPTIONS
235
+ Cache-Control: public, max-age=3600
236
+
237
+ /ucp.json
238
+ Content-Type: application/json
239
+ Access-Control-Allow-Origin: *
240
+ Cache-Control: public, max-age=3600
241
+ `;
242
+
243
+ return {
244
+ filename: '_redirects',
245
+ content: redirects + '\n' + headers.replace(/^/gm, '# '),
246
+ contentType: 'markdown',
247
+ description: 'Netlify redirects and headers (also create _headers file)',
248
+ };
249
+ }
250
+
251
+ /**
252
+ * Cloudflare Pages Functions
253
+ */
254
+ function generateCloudflarePagesFunctions(config: HostingConfig): InstallArtifact {
255
+ const content = `// Cloudflare Pages Function for /.well-known/ucp
256
+ // Place this file at: functions/.well-known/ucp.js
257
+
258
+ export async function onRequest(context) {
259
+ // Import the profile JSON (place ucp.json in your public folder)
260
+ const profileUrl = new URL('/ucp.json', context.request.url);
261
+
262
+ const response = await fetch(profileUrl);
263
+ const profile = await response.text();
264
+
265
+ return new Response(profile, {
266
+ headers: {
267
+ 'Content-Type': 'application/json',
268
+ 'Access-Control-Allow-Origin': '*',
269
+ 'Access-Control-Allow-Methods': 'GET, OPTIONS',
270
+ 'Cache-Control': 'public, max-age=3600',
271
+ },
272
+ });
273
+ }
274
+ `;
275
+
276
+ return {
277
+ filename: 'functions/.well-known/ucp.js',
278
+ content,
279
+ contentType: 'javascript',
280
+ description: 'Cloudflare Pages Function for UCP endpoint',
281
+ };
282
+ }
283
+
284
+ /**
285
+ * Cloudflare Worker for edge hosting
286
+ */
287
+ function generateCloudflareWorker(config: HostingConfig): InstallArtifact {
288
+ const hostedUrl = config.hostedProfileUrl ||
289
+ `${HOSTED_PROFILE_BASE_URL}/${config.merchantId}/ucp.json`;
290
+
291
+ const content = `// Cloudflare Worker for UCP Profile Proxy
292
+ // Route: ${config.merchantDomain}/.well-known/ucp*
293
+
294
+ export default {
295
+ async fetch(request, env, ctx) {
296
+ const url = new URL(request.url);
297
+
298
+ // Only handle /.well-known/ucp path
299
+ if (url.pathname !== '/.well-known/ucp') {
300
+ return new Response('Not Found', { status: 404 });
301
+ }
302
+
303
+ // Handle CORS preflight
304
+ if (request.method === 'OPTIONS') {
305
+ return new Response(null, {
306
+ status: 204,
307
+ headers: {
308
+ 'Access-Control-Allow-Origin': '*',
309
+ 'Access-Control-Allow-Methods': 'GET, OPTIONS',
310
+ 'Access-Control-Allow-Headers': 'Accept, Content-Type',
311
+ 'Access-Control-Max-Age': '86400',
312
+ },
313
+ });
314
+ }
315
+
316
+ // Fetch profile from hosted service
317
+ const profileUrl = '${hostedUrl}';
318
+
319
+ try {
320
+ const response = await fetch(profileUrl, {
321
+ cf: {
322
+ cacheTtl: 3600, // Cache for 1 hour
323
+ cacheEverything: true,
324
+ },
325
+ });
326
+
327
+ if (!response.ok) {
328
+ return new Response(
329
+ JSON.stringify({ error: 'Profile not found' }),
330
+ {
331
+ status: response.status,
332
+ headers: { 'Content-Type': 'application/json' }
333
+ }
334
+ );
335
+ }
336
+
337
+ const profile = await response.text();
338
+
339
+ return new Response(profile, {
340
+ headers: {
341
+ 'Content-Type': 'application/json',
342
+ 'Access-Control-Allow-Origin': '*',
343
+ 'Access-Control-Allow-Methods': 'GET, OPTIONS',
344
+ 'Cache-Control': 'public, max-age=3600',
345
+ 'X-UCP-Profile-Source': 'hosted',
346
+ },
347
+ });
348
+ } catch (error) {
349
+ return new Response(
350
+ JSON.stringify({ error: 'Failed to fetch profile' }),
351
+ {
352
+ status: 502,
353
+ headers: { 'Content-Type': 'application/json' }
354
+ }
355
+ );
356
+ }
357
+ },
358
+ };
359
+ `;
360
+
361
+ return {
362
+ filename: 'worker.js',
363
+ content,
364
+ contentType: 'javascript',
365
+ description: 'Cloudflare Worker for proxying UCP profile from hosted service',
366
+ };
367
+ }
368
+
369
+ /**
370
+ * Wrangler configuration for Cloudflare Worker
371
+ */
372
+ function generateWranglerConfig(config: HostingConfig): InstallArtifact {
373
+ const content = `# Cloudflare Worker configuration
374
+ # Run: npx wrangler deploy
375
+
376
+ name = "ucp-profile-${config.merchantId}"
377
+ main = "worker.js"
378
+ compatibility_date = "2024-01-01"
379
+
380
+ # Route configuration
381
+ # Update this with your actual domain
382
+ routes = [
383
+ { pattern = "${config.merchantDomain}/.well-known/ucp*", zone_name = "${config.merchantDomain}" }
384
+ ]
385
+
386
+ # Optional: Environment variables
387
+ # [vars]
388
+ # PROFILE_URL = "${config.hostedProfileUrl || `${HOSTED_PROFILE_BASE_URL}/${config.merchantId}/ucp.json`}"
389
+ `;
390
+
391
+ return {
392
+ filename: 'wrangler.toml',
393
+ content,
394
+ contentType: 'markdown',
395
+ description: 'Wrangler configuration for deploying Cloudflare Worker',
396
+ };
397
+ }
398
+
399
+ /**
400
+ * Nginx reverse proxy configuration
401
+ */
402
+ function generateNginxProxyConfig(config: HostingConfig): InstallArtifact {
403
+ const hostedUrl = config.hostedProfileUrl ||
404
+ `${HOSTED_PROFILE_BASE_URL}/${config.merchantId}/ucp.json`;
405
+
406
+ const content = `# Nginx reverse proxy configuration for UCP profile
407
+ # Add this to your server block
408
+
409
+ location = /.well-known/ucp {
410
+ # Proxy to hosted profile service
411
+ proxy_pass ${hostedUrl};
412
+ proxy_set_header Host profiles.ucp.tools;
413
+ proxy_set_header X-Real-IP $remote_addr;
414
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
415
+ proxy_set_header X-Forwarded-Proto $scheme;
416
+
417
+ # Caching
418
+ proxy_cache_valid 200 1h;
419
+ proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
420
+
421
+ # CORS headers
422
+ add_header Access-Control-Allow-Origin "*" always;
423
+ add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
424
+ add_header Cache-Control "public, max-age=3600" always;
425
+
426
+ # Handle preflight requests
427
+ if ($request_method = OPTIONS) {
428
+ add_header Access-Control-Allow-Origin "*";
429
+ add_header Access-Control-Allow-Methods "GET, OPTIONS";
430
+ add_header Access-Control-Max-Age 86400;
431
+ return 204;
432
+ }
433
+ }
434
+ `;
435
+
436
+ return {
437
+ filename: 'nginx-proxy.conf',
438
+ content,
439
+ contentType: 'nginx',
440
+ description: 'Nginx reverse proxy configuration for hosted UCP profile',
441
+ };
442
+ }
443
+
444
+ /**
445
+ * S3 + CloudFront instructions
446
+ */
447
+ function generateS3Instructions(config: HostingConfig): InstallArtifact {
448
+ const content = `# AWS S3 + CloudFront Setup for UCP Profile
449
+
450
+ ## 1. Upload Profile to S3
451
+
452
+ \`\`\`bash
453
+ # Create bucket (if needed)
454
+ aws s3 mb s3://${config.merchantDomain}-ucp
455
+
456
+ # Upload profile
457
+ aws s3 cp ucp.json s3://${config.merchantDomain}-ucp/ucp.json \\
458
+ --content-type "application/json" \\
459
+ --cache-control "public, max-age=3600"
460
+ \`\`\`
461
+
462
+ ## 2. Configure CloudFront
463
+
464
+ Create a CloudFront distribution with:
465
+
466
+ - Origin: S3 bucket \`${config.merchantDomain}-ucp\`
467
+ - Alternate domain: \`${config.merchantDomain}\`
468
+ - Behavior for \`/.well-known/ucp\`:
469
+ - Origin path: \`/ucp.json\`
470
+ - Allowed methods: GET, HEAD, OPTIONS
471
+ - Response headers policy: Add CORS headers
472
+
473
+ ## 3. CloudFront Function (for path rewrite)
474
+
475
+ \`\`\`javascript
476
+ function handler(event) {
477
+ var request = event.request;
478
+ if (request.uri === '/.well-known/ucp') {
479
+ request.uri = '/ucp.json';
480
+ }
481
+ return request;
482
+ }
483
+ \`\`\`
484
+
485
+ ## 4. CORS Configuration (S3 bucket policy)
486
+
487
+ \`\`\`json
488
+ {
489
+ "CORSRules": [
490
+ {
491
+ "AllowedHeaders": ["*"],
492
+ "AllowedMethods": ["GET"],
493
+ "AllowedOrigins": ["*"],
494
+ "ExposeHeaders": []
495
+ }
496
+ ]
497
+ }
498
+ \`\`\`
499
+
500
+ ## 5. Verification
501
+
502
+ \`\`\`bash
503
+ curl -I https://${config.merchantDomain}/.well-known/ucp
504
+ \`\`\`
505
+
506
+ Expected: HTTP 200 with Content-Type: application/json
507
+ `;
508
+
509
+ return {
510
+ filename: 'aws-setup.md',
511
+ content,
512
+ contentType: 'markdown',
513
+ description: 'AWS S3 + CloudFront setup instructions',
514
+ };
515
+ }
516
+
517
+ /**
518
+ * Generic static hosting instructions
519
+ */
520
+ function generateGenericStaticInstructions(config: HostingConfig): InstallArtifact {
521
+ const content = `# UCP Profile Static Hosting Instructions
522
+
523
+ ## Requirements
524
+
525
+ Your web server must serve the \`ucp.json\` file at:
526
+ \`\`\`
527
+ https://${config.merchantDomain}/.well-known/ucp
528
+ \`\`\`
529
+
530
+ ## Configuration Checklist
531
+
532
+ 1. **File Location**: Place \`ucp.json\` in your web root's \`.well-known\` directory
533
+ or configure a rewrite rule from \`/.well-known/ucp\` to your file location.
534
+
535
+ 2. **Content-Type**: Ensure the response has \`Content-Type: application/json\`
536
+
537
+ 3. **CORS Headers**: Add these headers for cross-origin discovery:
538
+ \`\`\`
539
+ Access-Control-Allow-Origin: *
540
+ Access-Control-Allow-Methods: GET, OPTIONS
541
+ Access-Control-Allow-Headers: Accept, Content-Type
542
+ \`\`\`
543
+
544
+ 4. **Caching**: Recommended cache headers:
545
+ \`\`\`
546
+ Cache-Control: public, max-age=3600
547
+ \`\`\`
548
+
549
+ 5. **HTTPS**: The endpoint MUST be served over HTTPS.
550
+
551
+ ## Verification
552
+
553
+ \`\`\`bash
554
+ # Test the endpoint
555
+ curl -I https://${config.merchantDomain}/.well-known/ucp
556
+
557
+ # Fetch and validate
558
+ curl https://${config.merchantDomain}/.well-known/ucp | jq .
559
+ \`\`\`
560
+
561
+ ## Validation
562
+
563
+ Run the UCP validator to check your deployment:
564
+ \`\`\`bash
565
+ ucp-validate --remote ${config.merchantDomain}
566
+ \`\`\`
567
+ `;
568
+
569
+ return {
570
+ filename: 'setup-instructions.md',
571
+ content,
572
+ contentType: 'markdown',
573
+ description: 'Generic static hosting setup instructions',
574
+ };
575
+ }
576
+
577
+ /**
578
+ * Generate README with all options
579
+ */
580
+ function generateReadme(config: HostingConfig): InstallArtifact {
581
+ const content = `# UCP Profile Installation - ${config.merchantDomain}
582
+
583
+ ## Profile Information
584
+
585
+ - **Merchant ID**: ${config.merchantId}
586
+ - **Domain**: ${config.merchantDomain}
587
+ - **Hosting Mode**: ${config.mode}
588
+ ${config.platform ? `- **Platform**: ${config.platform}` : ''}
589
+
590
+ ## Quick Start
591
+
592
+ Your UCP Business Profile must be accessible at:
593
+ \`\`\`
594
+ https://${config.merchantDomain}/.well-known/ucp
595
+ \`\`\`
596
+
597
+ ## Files Included
598
+
599
+ - \`ucp.json\` - Your UCP Business Profile
600
+ - Platform-specific configuration files (see below)
601
+
602
+ ## Installation Steps
603
+
604
+ ${getInstallationSteps(config)}
605
+
606
+ ## Verification
607
+
608
+ After installation, verify your profile is accessible:
609
+
610
+ \`\`\`bash
611
+ # Check endpoint responds
612
+ curl -I https://${config.merchantDomain}/.well-known/ucp
613
+
614
+ # Validate response content
615
+ curl https://${config.merchantDomain}/.well-known/ucp | jq .
616
+
617
+ # Run full validation
618
+ npx ucp-profile-manager validate --remote ${config.merchantDomain}
619
+ \`\`\`
620
+
621
+ ## Troubleshooting
622
+
623
+ ### Common Issues
624
+
625
+ 1. **404 Not Found**: Check file location and rewrite rules
626
+ 2. **CORS Errors**: Ensure Access-Control-Allow-Origin header is set
627
+ 3. **SSL Errors**: Profile must be served over HTTPS
628
+ 4. **Invalid JSON**: Validate JSON syntax with \`jq\`
629
+
630
+ ### Support
631
+
632
+ For assistance, contact: hello@ucp.tools
633
+
634
+ ---
635
+ Generated by UCP Profile Manager v1.0
636
+ `;
637
+
638
+ return {
639
+ filename: 'README.md',
640
+ content,
641
+ contentType: 'markdown',
642
+ description: 'Installation guide and documentation',
643
+ };
644
+ }
645
+
646
+ /**
647
+ * Get installation steps based on hosting config
648
+ */
649
+ function getInstallationSteps(config: HostingConfig): string {
650
+ switch (config.mode) {
651
+ case 'static':
652
+ return `### Static File Hosting
653
+
654
+ 1. Copy \`ucp.json\` to your web server
655
+ 2. Apply the configuration from the platform-specific config file
656
+ 3. Restart your web server if needed
657
+ 4. Test the endpoint`;
658
+
659
+ case 'edge-worker':
660
+ return `### Edge Worker Deployment
661
+
662
+ 1. Install Wrangler CLI: \`npm install -g wrangler\`
663
+ 2. Authenticate: \`wrangler login\`
664
+ 3. Update \`wrangler.toml\` with your zone settings
665
+ 4. Deploy: \`npx wrangler deploy\`
666
+ 5. Verify the route is active in Cloudflare dashboard`;
667
+
668
+ case 'reverse-proxy':
669
+ return `### Reverse Proxy Setup
670
+
671
+ 1. Add the nginx configuration snippet to your server block
672
+ 2. Test configuration: \`nginx -t\`
673
+ 3. Reload nginx: \`sudo systemctl reload nginx\`
674
+ 4. Verify the endpoint proxies correctly`;
675
+
676
+ default:
677
+ return `See the included configuration files for setup instructions.`;
678
+ }
679
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Hosting Module
3
+ * Generates installation artifacts for different platforms
4
+ */
5
+
6
+ export { generateHostingArtifacts } from './artifacts-generator.js';