@ucptools/validator 1.0.0 → 1.0.1

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 (236) hide show
  1. package/.claude/settings.local.json +60 -0
  2. package/.vercel/README.txt +11 -0
  3. package/.vercel/project.json +1 -0
  4. package/dist/cli/index.d.ts +6 -0
  5. package/dist/cli/index.d.ts.map +1 -0
  6. package/dist/cli/index.js +279 -0
  7. package/dist/cli/index.js.map +1 -0
  8. package/dist/compliance/compliance-generator.d.ts +34 -0
  9. package/dist/compliance/compliance-generator.d.ts.map +1 -0
  10. package/dist/compliance/compliance-generator.js +320 -0
  11. package/dist/compliance/compliance-generator.js.map +1 -0
  12. package/dist/compliance/index.d.ts +8 -0
  13. package/dist/compliance/index.d.ts.map +1 -0
  14. package/dist/compliance/index.js +17 -0
  15. package/dist/compliance/index.js.map +1 -0
  16. package/dist/compliance/templates.d.ts +34 -0
  17. package/dist/compliance/templates.d.ts.map +1 -0
  18. package/{src/compliance/templates.ts → dist/compliance/templates.js} +117 -155
  19. package/dist/compliance/templates.js.map +1 -0
  20. package/dist/compliance/types.d.ts +64 -0
  21. package/dist/compliance/types.d.ts.map +1 -0
  22. package/dist/compliance/types.js +64 -0
  23. package/dist/compliance/types.js.map +1 -0
  24. package/dist/db/index.d.ts +11 -0
  25. package/dist/db/index.d.ts.map +1 -0
  26. package/dist/db/index.js +63 -0
  27. package/dist/db/index.js.map +1 -0
  28. package/dist/db/schema.d.ts +444 -0
  29. package/dist/db/schema.d.ts.map +1 -0
  30. package/dist/db/schema.js +65 -0
  31. package/dist/db/schema.js.map +1 -0
  32. package/dist/feed-analyzer/feed-analyzer.d.ts +26 -0
  33. package/dist/feed-analyzer/feed-analyzer.d.ts.map +1 -0
  34. package/{src/feed-analyzer/feed-analyzer.ts → dist/feed-analyzer/feed-analyzer.js} +642 -726
  35. package/dist/feed-analyzer/feed-analyzer.js.map +1 -0
  36. package/dist/feed-analyzer/index.d.ts +8 -0
  37. package/dist/feed-analyzer/index.d.ts.map +1 -0
  38. package/dist/feed-analyzer/index.js +19 -0
  39. package/dist/feed-analyzer/index.js.map +1 -0
  40. package/dist/feed-analyzer/types.d.ts +204 -0
  41. package/dist/feed-analyzer/types.d.ts.map +1 -0
  42. package/dist/feed-analyzer/types.js +162 -0
  43. package/dist/feed-analyzer/types.js.map +1 -0
  44. package/{src/generator/index.ts → dist/generator/index.d.ts} +1 -1
  45. package/dist/generator/index.d.ts.map +1 -0
  46. package/dist/generator/index.js +13 -0
  47. package/dist/generator/index.js.map +1 -0
  48. package/dist/generator/key-generator.d.ts +24 -0
  49. package/dist/generator/key-generator.d.ts.map +1 -0
  50. package/dist/generator/key-generator.js +144 -0
  51. package/dist/generator/key-generator.js.map +1 -0
  52. package/dist/generator/profile-builder.d.ts +15 -0
  53. package/dist/generator/profile-builder.d.ts.map +1 -0
  54. package/dist/generator/profile-builder.js +338 -0
  55. package/dist/generator/profile-builder.js.map +1 -0
  56. package/dist/hosting/artifacts-generator.d.ts +10 -0
  57. package/dist/hosting/artifacts-generator.d.ts.map +1 -0
  58. package/{src/hosting/artifacts-generator.ts → dist/hosting/artifacts-generator.js} +191 -241
  59. package/dist/hosting/artifacts-generator.js.map +1 -0
  60. package/{src/hosting/index.ts → dist/hosting/index.d.ts} +1 -1
  61. package/dist/hosting/index.d.ts.map +1 -0
  62. package/dist/hosting/index.js +10 -0
  63. package/dist/hosting/index.js.map +1 -0
  64. package/dist/index.d.ts +18 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +78 -0
  67. package/dist/index.js.map +1 -0
  68. package/{src/security/index.ts → dist/security/index.d.ts} +8 -15
  69. package/dist/security/index.d.ts.map +1 -0
  70. package/dist/security/index.js +12 -0
  71. package/dist/security/index.js.map +1 -0
  72. package/dist/security/security-scanner.d.ts +10 -0
  73. package/dist/security/security-scanner.d.ts.map +1 -0
  74. package/dist/security/security-scanner.js +541 -0
  75. package/dist/security/security-scanner.js.map +1 -0
  76. package/dist/security/types.d.ts +48 -0
  77. package/dist/security/types.d.ts.map +1 -0
  78. package/dist/security/types.js +21 -0
  79. package/dist/security/types.js.map +1 -0
  80. package/dist/services/directory.d.ts +104 -0
  81. package/dist/services/directory.d.ts.map +1 -0
  82. package/dist/services/directory.js +333 -0
  83. package/dist/services/directory.js.map +1 -0
  84. package/dist/simulator/agent-simulator.d.ts +69 -0
  85. package/dist/simulator/agent-simulator.d.ts.map +1 -0
  86. package/{src/simulator/agent-simulator.ts → dist/simulator/agent-simulator.js} +650 -941
  87. package/dist/simulator/agent-simulator.js.map +1 -0
  88. package/{src/simulator/index.ts → dist/simulator/index.d.ts} +7 -7
  89. package/dist/simulator/index.d.ts.map +1 -0
  90. package/dist/simulator/index.js +23 -0
  91. package/dist/simulator/index.js.map +1 -0
  92. package/{src/simulator/types.ts → dist/simulator/types.d.ts} +145 -170
  93. package/dist/simulator/types.d.ts.map +1 -0
  94. package/dist/simulator/types.js +18 -0
  95. package/dist/simulator/types.js.map +1 -0
  96. package/dist/types/generator.d.ts +106 -0
  97. package/dist/types/generator.d.ts.map +1 -0
  98. package/dist/types/generator.js +6 -0
  99. package/dist/types/generator.js.map +1 -0
  100. package/{src/types/index.ts → dist/types/index.d.ts} +1 -1
  101. package/dist/types/index.d.ts.map +1 -0
  102. package/dist/types/index.js +23 -0
  103. package/dist/types/index.js.map +1 -0
  104. package/dist/types/ucp-profile.d.ts +103 -0
  105. package/dist/types/ucp-profile.d.ts.map +1 -0
  106. package/dist/types/ucp-profile.js +45 -0
  107. package/dist/types/ucp-profile.js.map +1 -0
  108. package/dist/types/validation.d.ts +68 -0
  109. package/dist/types/validation.d.ts.map +1 -0
  110. package/dist/types/validation.js +32 -0
  111. package/dist/types/validation.js.map +1 -0
  112. package/dist/validator/index.d.ts +26 -0
  113. package/dist/validator/index.d.ts.map +1 -0
  114. package/dist/validator/index.js +161 -0
  115. package/dist/validator/index.js.map +1 -0
  116. package/dist/validator/network-validator.d.ts +28 -0
  117. package/dist/validator/network-validator.d.ts.map +1 -0
  118. package/dist/validator/network-validator.js +319 -0
  119. package/dist/validator/network-validator.js.map +1 -0
  120. package/dist/validator/rules-validator.d.ts +11 -0
  121. package/dist/validator/rules-validator.d.ts.map +1 -0
  122. package/dist/validator/rules-validator.js +257 -0
  123. package/dist/validator/rules-validator.js.map +1 -0
  124. package/dist/validator/sdk-validator.d.ts +58 -0
  125. package/dist/validator/sdk-validator.d.ts.map +1 -0
  126. package/{src/validator/sdk-validator.ts → dist/validator/sdk-validator.js} +273 -330
  127. package/dist/validator/sdk-validator.js.map +1 -0
  128. package/dist/validator/structural-validator.d.ts +11 -0
  129. package/dist/validator/structural-validator.d.ts.map +1 -0
  130. package/dist/validator/structural-validator.js +415 -0
  131. package/dist/validator/structural-validator.js.map +1 -0
  132. package/package.json +1 -1
  133. package/publish-output.txt +0 -0
  134. package/CLAUDE.md +0 -109
  135. package/api/analyze-feed.js +0 -140
  136. package/api/badge.js +0 -185
  137. package/api/benchmark.js +0 -177
  138. package/api/directory-stats.ts +0 -29
  139. package/api/directory.ts +0 -73
  140. package/api/generate-compliance.js +0 -143
  141. package/api/generate-schema.js +0 -457
  142. package/api/generate.js +0 -132
  143. package/api/security-scan.js +0 -133
  144. package/api/simulate.js +0 -187
  145. package/api/tsconfig.json +0 -10
  146. package/api/validate.js +0 -1351
  147. package/apify-actor/.actor/actor.json +0 -68
  148. package/apify-actor/.actor/input_schema.json +0 -32
  149. package/apify-actor/APIFY-STORE-LISTING.md +0 -412
  150. package/apify-actor/Dockerfile +0 -8
  151. package/apify-actor/README.md +0 -166
  152. package/apify-actor/main.ts +0 -111
  153. package/apify-actor/package.json +0 -17
  154. package/apify-actor/src/main.js +0 -199
  155. package/docs/BRAND-IDENTITY.md +0 -238
  156. package/docs/BRAND-STYLE-GUIDE.md +0 -356
  157. package/drizzle/0000_black_king_cobra.sql +0 -39
  158. package/drizzle/meta/0000_snapshot.json +0 -309
  159. package/drizzle/meta/_journal.json +0 -13
  160. package/drizzle.config.ts +0 -10
  161. package/public/.well-known/ucp +0 -25
  162. package/public/android-chrome-192x192.png +0 -0
  163. package/public/android-chrome-512x512.png +0 -0
  164. package/public/apple-touch-icon.png +0 -0
  165. package/public/brand.css +0 -321
  166. package/public/directory.html +0 -701
  167. package/public/favicon-16x16.png +0 -0
  168. package/public/favicon-32x32.png +0 -0
  169. package/public/favicon.ico +0 -0
  170. package/public/guides/bigcommerce.html +0 -743
  171. package/public/guides/fastucp.html +0 -838
  172. package/public/guides/magento.html +0 -779
  173. package/public/guides/shopify.html +0 -726
  174. package/public/guides/squarespace.html +0 -749
  175. package/public/guides/wix.html +0 -747
  176. package/public/guides/woocommerce.html +0 -733
  177. package/public/index.html +0 -3835
  178. package/public/learn.html +0 -396
  179. package/public/logo.jpeg +0 -0
  180. package/public/og-image-icon.png +0 -0
  181. package/public/og-image.png +0 -0
  182. package/public/robots.txt +0 -6
  183. package/public/site.webmanifest +0 -31
  184. package/public/sitemap.xml +0 -69
  185. package/public/social/linkedin-banner-1128x191.png +0 -0
  186. package/public/social/temp.PNG +0 -0
  187. package/public/social/x-header-1500x500.png +0 -0
  188. package/public/verify.html +0 -410
  189. package/scripts/generate-favicons.js +0 -44
  190. package/scripts/generate-ico.js +0 -23
  191. package/scripts/generate-og-image.js +0 -45
  192. package/scripts/reset-db.ts +0 -77
  193. package/scripts/seed-db.ts +0 -71
  194. package/scripts/setup-benchmark-db.js +0 -70
  195. package/src/api/server.ts +0 -266
  196. package/src/cli/index.ts +0 -302
  197. package/src/compliance/compliance-generator.ts +0 -452
  198. package/src/compliance/index.ts +0 -28
  199. package/src/compliance/types.ts +0 -170
  200. package/src/db/index.ts +0 -28
  201. package/src/db/schema.ts +0 -84
  202. package/src/feed-analyzer/index.ts +0 -34
  203. package/src/feed-analyzer/types.ts +0 -354
  204. package/src/generator/key-generator.ts +0 -124
  205. package/src/generator/profile-builder.ts +0 -402
  206. package/src/index.ts +0 -105
  207. package/src/security/security-scanner.ts +0 -604
  208. package/src/security/types.ts +0 -55
  209. package/src/services/directory.ts +0 -434
  210. package/src/types/generator.ts +0 -140
  211. package/src/types/ucp-profile.ts +0 -140
  212. package/src/types/validation.ts +0 -89
  213. package/src/validator/index.ts +0 -194
  214. package/src/validator/network-validator.ts +0 -417
  215. package/src/validator/rules-validator.ts +0 -297
  216. package/src/validator/structural-validator.ts +0 -476
  217. package/tests/fixtures/non-compliant-profile.json +0 -25
  218. package/tests/fixtures/official-sample-profile.json +0 -75
  219. package/tests/integration/benchmark.test.ts +0 -207
  220. package/tests/integration/database.test.ts +0 -163
  221. package/tests/integration/directory-api.test.ts +0 -268
  222. package/tests/integration/simulate-api.test.ts +0 -230
  223. package/tests/integration/validate-api.test.ts +0 -269
  224. package/tests/setup.ts +0 -15
  225. package/tests/unit/agent-simulator.test.ts +0 -575
  226. package/tests/unit/compliance-generator.test.ts +0 -374
  227. package/tests/unit/directory-service.test.ts +0 -272
  228. package/tests/unit/feed-analyzer.test.ts +0 -517
  229. package/tests/unit/lint-suggestions.test.ts +0 -423
  230. package/tests/unit/official-samples.test.ts +0 -211
  231. package/tests/unit/pdf-report.test.ts +0 -390
  232. package/tests/unit/sdk-validator.test.ts +0 -531
  233. package/tests/unit/security-scanner.test.ts +0 -410
  234. package/tests/unit/validation.test.ts +0 -390
  235. package/vercel.json +0 -34
  236. package/vitest.config.ts +0 -22
@@ -1,70 +0,0 @@
1
- /**
2
- * Setup script for benchmark database tables
3
- * Run with: node scripts/setup-benchmark-db.js
4
- */
5
-
6
- import pg from 'pg';
7
-
8
- const { Pool } = pg;
9
-
10
- async function setup() {
11
- const pool = new Pool({
12
- connectionString: process.env.DATABASE_URL,
13
- ssl: { rejectUnauthorized: false }
14
- });
15
-
16
- try {
17
- console.log('Creating benchmark tables...');
18
-
19
- // Create benchmark_stats table for aggregate statistics
20
- await pool.query(`
21
- CREATE TABLE IF NOT EXISTS benchmark_stats (
22
- id SERIAL PRIMARY KEY,
23
- score_bucket INT NOT NULL CHECK (score_bucket >= 0 AND score_bucket <= 100),
24
- count INT DEFAULT 0,
25
- UNIQUE(score_bucket)
26
- );
27
- `);
28
-
29
- // Initialize score buckets (0-10, 10-20, ..., 90-100)
30
- for (let bucket = 0; bucket <= 100; bucket += 10) {
31
- await pool.query(`
32
- INSERT INTO benchmark_stats (score_bucket, count)
33
- VALUES ($1, 0)
34
- ON CONFLICT (score_bucket) DO NOTHING;
35
- `, [bucket]);
36
- }
37
-
38
- // Create summary table for quick stats
39
- await pool.query(`
40
- CREATE TABLE IF NOT EXISTS benchmark_summary (
41
- id INT PRIMARY KEY DEFAULT 1,
42
- total_validations INT DEFAULT 0,
43
- avg_score DECIMAL(5,2) DEFAULT 0,
44
- updated_at TIMESTAMP DEFAULT NOW(),
45
- CHECK (id = 1)
46
- );
47
- `);
48
-
49
- // Initialize summary row
50
- await pool.query(`
51
- INSERT INTO benchmark_summary (id, total_validations, avg_score)
52
- VALUES (1, 0, 0)
53
- ON CONFLICT (id) DO NOTHING;
54
- `);
55
-
56
- console.log('✓ Benchmark tables created successfully');
57
-
58
- // Verify setup
59
- const result = await pool.query('SELECT * FROM benchmark_stats ORDER BY score_bucket');
60
- console.log('Score buckets:', result.rows.length);
61
-
62
- } catch (error) {
63
- console.error('Error setting up database:', error);
64
- process.exit(1);
65
- } finally {
66
- await pool.end();
67
- }
68
- }
69
-
70
- setup();
package/src/api/server.ts DELETED
@@ -1,266 +0,0 @@
1
- /**
2
- * UCP Profile Manager API Server
3
- */
4
-
5
- import express from 'express';
6
- import type { Request, Response, NextFunction } from 'express';
7
- import { nanoid } from 'nanoid';
8
- import pino from 'pino';
9
- import { buildProfile, generateMinimalProfile } from '../generator/index.js';
10
- import { validateProfile, validateRemote, validateQuick, validateJsonString } from '../validator/index.js';
11
- import { generateHostingArtifacts } from '../hosting/index.js';
12
- import type { GeneratorInput, HostingConfig } from '../types/generator.js';
13
- import type { ValidationOptions } from '../types/validation.js';
14
-
15
- // Logger
16
- const logger = pino({
17
- level: process.env.LOG_LEVEL || 'info',
18
- transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty' } : undefined,
19
- });
20
-
21
- const app = express();
22
- app.use(express.json({ limit: '1mb' }));
23
-
24
- // Request logging middleware
25
- app.use((req: Request, res: Response, next: NextFunction) => {
26
- const requestId = nanoid(8);
27
- (req as any).requestId = requestId;
28
- logger.info({ requestId, method: req.method, path: req.path }, 'Request received');
29
- next();
30
- });
31
-
32
- // CORS middleware
33
- app.use((req: Request, res: Response, next: NextFunction) => {
34
- res.header('Access-Control-Allow-Origin', '*');
35
- res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
36
- res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
37
- if (req.method === 'OPTIONS') {
38
- return res.sendStatus(204);
39
- }
40
- next();
41
- });
42
-
43
- // Health check
44
- app.get('/health', (req: Request, res: Response) => {
45
- res.json({ status: 'ok', version: '1.0.0' });
46
- });
47
-
48
- // API version prefix
49
- const api = express.Router();
50
-
51
- /**
52
- * Generate a UCP profile
53
- * POST /v1/profiles/generate
54
- */
55
- api.post('/profiles/generate', async (req: Request, res: Response) => {
56
- try {
57
- const input: GeneratorInput = req.body;
58
-
59
- // Validate required fields
60
- if (!input.merchant?.merchantId || !input.merchant?.primaryDomain) {
61
- return res.status(400).json({
62
- error: 'Missing required fields: merchant.merchantId and merchant.primaryDomain',
63
- });
64
- }
65
-
66
- if (!input.transport?.rest?.endpoint) {
67
- return res.status(400).json({
68
- error: 'Missing required field: transport.rest.endpoint',
69
- });
70
- }
71
-
72
- // Set defaults
73
- input.capabilities = input.capabilities || { checkout: true, order: false, fulfillment: false, discount: false };
74
-
75
- const result = await buildProfile(input);
76
-
77
- res.json({
78
- profile: result.profile,
79
- signing_key_pair: result.signingKeyPair ? {
80
- public_key: result.signingKeyPair.publicKey,
81
- private_key_pem: result.signingKeyPair.privateKey,
82
- } : undefined,
83
- });
84
- } catch (error) {
85
- logger.error({ error }, 'Profile generation failed');
86
- res.status(500).json({ error: 'Failed to generate profile' });
87
- }
88
- });
89
-
90
- /**
91
- * Generate minimal profile
92
- * POST /v1/profiles/generate-minimal
93
- */
94
- api.post('/profiles/generate-minimal', (req: Request, res: Response) => {
95
- try {
96
- const { endpoint, version } = req.body;
97
-
98
- if (!endpoint) {
99
- return res.status(400).json({ error: 'Missing required field: endpoint' });
100
- }
101
-
102
- const profile = generateMinimalProfile(endpoint, version);
103
- res.json({ profile });
104
- } catch (error) {
105
- logger.error({ error }, 'Minimal profile generation failed');
106
- res.status(500).json({ error: 'Failed to generate minimal profile' });
107
- }
108
- });
109
-
110
- /**
111
- * Validate a profile (from request body)
112
- * POST /v1/profiles/validate
113
- */
114
- api.post('/profiles/validate', async (req: Request, res: Response) => {
115
- try {
116
- const { profile, options } = req.body as {
117
- profile: unknown;
118
- options?: ValidationOptions;
119
- };
120
-
121
- if (!profile) {
122
- return res.status(400).json({ error: 'Missing required field: profile' });
123
- }
124
-
125
- const report = await validateProfile(profile, options || {});
126
- res.json(report);
127
- } catch (error) {
128
- logger.error({ error }, 'Profile validation failed');
129
- res.status(500).json({ error: 'Validation failed' });
130
- }
131
- });
132
-
133
- /**
134
- * Quick validate (structural + rules only)
135
- * POST /v1/profiles/validate-quick
136
- */
137
- api.post('/profiles/validate-quick', (req: Request, res: Response) => {
138
- try {
139
- const { profile } = req.body;
140
-
141
- if (!profile) {
142
- return res.status(400).json({ error: 'Missing required field: profile' });
143
- }
144
-
145
- const report = validateQuick(profile);
146
- res.json(report);
147
- } catch (error) {
148
- logger.error({ error }, 'Quick validation failed');
149
- res.status(500).json({ error: 'Validation failed' });
150
- }
151
- });
152
-
153
- /**
154
- * Validate remote profile
155
- * POST /v1/profiles/validate-remote
156
- */
157
- api.post('/profiles/validate-remote', async (req: Request, res: Response) => {
158
- try {
159
- const { domain, options } = req.body as {
160
- domain: string;
161
- options?: ValidationOptions;
162
- };
163
-
164
- if (!domain) {
165
- return res.status(400).json({ error: 'Missing required field: domain' });
166
- }
167
-
168
- const report = await validateRemote(domain, options || {});
169
- res.json(report);
170
- } catch (error) {
171
- logger.error({ error }, 'Remote validation failed');
172
- res.status(500).json({ error: 'Remote validation failed' });
173
- }
174
- });
175
-
176
- /**
177
- * Validate JSON string
178
- * POST /v1/profiles/validate-json
179
- */
180
- api.post('/profiles/validate-json', async (req: Request, res: Response) => {
181
- try {
182
- const { json, options } = req.body as {
183
- json: string;
184
- options?: ValidationOptions;
185
- };
186
-
187
- if (!json) {
188
- return res.status(400).json({ error: 'Missing required field: json' });
189
- }
190
-
191
- const report = await validateJsonString(json, options || {});
192
- res.json(report);
193
- } catch (error) {
194
- logger.error({ error }, 'JSON validation failed');
195
- res.status(500).json({ error: 'Validation failed' });
196
- }
197
- });
198
-
199
- /**
200
- * Generate hosting artifacts
201
- * POST /v1/hosting/artifacts
202
- */
203
- api.post('/hosting/artifacts', (req: Request, res: Response) => {
204
- try {
205
- const { config, profile_json } = req.body as {
206
- config: HostingConfig;
207
- profile_json: string;
208
- };
209
-
210
- if (!config?.merchantId || !config?.merchantDomain || !config?.mode) {
211
- return res.status(400).json({
212
- error: 'Missing required config fields: merchantId, merchantDomain, mode',
213
- });
214
- }
215
-
216
- if (!profile_json) {
217
- return res.status(400).json({ error: 'Missing required field: profile_json' });
218
- }
219
-
220
- const artifacts = generateHostingArtifacts(config, profile_json);
221
- res.json({ artifacts });
222
- } catch (error) {
223
- logger.error({ error }, 'Artifact generation failed');
224
- res.status(500).json({ error: 'Failed to generate artifacts' });
225
- }
226
- });
227
-
228
- /**
229
- * Hosted profile endpoint (for edge worker proxying)
230
- * GET /hosted/:merchantId/ucp.json
231
- */
232
- api.get('/hosted/:merchantId/ucp.json', async (req: Request, res: Response) => {
233
- const { merchantId } = req.params;
234
-
235
- // TODO: Implement database lookup for stored profiles
236
- // For now, return 404 as this requires database integration
237
- res.status(404).json({
238
- error: 'Profile not found',
239
- hint: 'This endpoint requires database integration. Use /v1/profiles/generate to create profiles.',
240
- });
241
- });
242
-
243
- // Mount API routes
244
- app.use('/v1', api);
245
-
246
- // Error handler
247
- app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
248
- logger.error({ error: err }, 'Unhandled error');
249
- res.status(500).json({ error: 'Internal server error' });
250
- });
251
-
252
- // 404 handler
253
- app.use((req: Request, res: Response) => {
254
- res.status(404).json({ error: 'Not found' });
255
- });
256
-
257
- // Start server
258
- const PORT = process.env.PORT || 3000;
259
-
260
- if (process.env.NODE_ENV !== 'test') {
261
- app.listen(PORT, () => {
262
- logger.info({ port: PORT }, 'UCP Profile Manager API started');
263
- });
264
- }
265
-
266
- export { app };
package/src/cli/index.ts DELETED
@@ -1,302 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * UCP Profile Validator CLI
4
- */
5
-
6
- import { program } from 'commander';
7
- import { readFileSync, writeFileSync, existsSync } from 'fs';
8
- import { resolve } from 'path';
9
- import { validateProfile, validateRemote, validateQuick } from '../validator/index.js';
10
- import { buildProfile, generateMinimalProfile } from '../generator/index.js';
11
- import { generateHostingArtifacts } from '../hosting/index.js';
12
- import type { ValidationReport } from '../types/validation.js';
13
- import type { GeneratorInput, HostingConfig, HostingMode, HostingPlatform } from '../types/generator.js';
14
-
15
- // Colors for terminal output (simple implementation without chalk for ESM)
16
- const colors = {
17
- red: (s: string) => `\x1b[31m${s}\x1b[0m`,
18
- green: (s: string) => `\x1b[32m${s}\x1b[0m`,
19
- yellow: (s: string) => `\x1b[33m${s}\x1b[0m`,
20
- blue: (s: string) => `\x1b[34m${s}\x1b[0m`,
21
- gray: (s: string) => `\x1b[90m${s}\x1b[0m`,
22
- bold: (s: string) => `\x1b[1m${s}\x1b[0m`,
23
- };
24
-
25
- program
26
- .name('ucp-validate')
27
- .description('UCP Profile Validator and Generator CLI')
28
- .version('1.0.0');
29
-
30
- /**
31
- * Validate command
32
- */
33
- program
34
- .command('validate')
35
- .description('Validate a UCP profile')
36
- .option('-f, --file <path>', 'Path to local JSON file')
37
- .option('-r, --remote <domain>', 'Remote domain to fetch profile from')
38
- .option('-q, --quick', 'Quick validation (no network checks)')
39
- .option('-o, --output <path>', 'Output report to file')
40
- .option('--json', 'Output as JSON')
41
- .action(async (options) => {
42
- try {
43
- let report: ValidationReport;
44
-
45
- if (options.remote) {
46
- console.log(colors.blue(`Fetching profile from https://${options.remote}/.well-known/ucp...`));
47
- report = await validateRemote(options.remote, {
48
- skipNetworkChecks: options.quick,
49
- });
50
- } else if (options.file) {
51
- const filePath = resolve(options.file);
52
- if (!existsSync(filePath)) {
53
- console.error(colors.red(`File not found: ${filePath}`));
54
- process.exit(1);
55
- }
56
- const content = readFileSync(filePath, 'utf-8');
57
- const profile = JSON.parse(content);
58
-
59
- if (options.quick) {
60
- report = validateQuick(profile);
61
- } else {
62
- report = await validateProfile(profile);
63
- }
64
- } else {
65
- console.error(colors.red('Please specify --file or --remote'));
66
- process.exit(1);
67
- }
68
-
69
- // Output
70
- if (options.json) {
71
- console.log(JSON.stringify(report, null, 2));
72
- } else {
73
- printValidationReport(report);
74
- }
75
-
76
- if (options.output) {
77
- writeFileSync(options.output, JSON.stringify(report, null, 2));
78
- console.log(colors.gray(`\nReport saved to: ${options.output}`));
79
- }
80
-
81
- process.exit(report.ok ? 0 : 1);
82
- } catch (error) {
83
- console.error(colors.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
84
- process.exit(1);
85
- }
86
- });
87
-
88
- /**
89
- * Generate command
90
- */
91
- program
92
- .command('generate')
93
- .description('Generate a new UCP profile')
94
- .requiredOption('-d, --domain <domain>', 'Primary domain (e.g., merchant.com)')
95
- .requiredOption('-e, --endpoint <url>', 'REST API endpoint URL')
96
- .option('-i, --id <id>', 'Merchant ID (auto-generated if not provided)')
97
- .option('--order', 'Enable Order capability')
98
- .option('--fulfillment', 'Enable Fulfillment capability')
99
- .option('--discount', 'Enable Discount capability')
100
- .option('-o, --output <path>', 'Output file path', 'ucp.json')
101
- .option('--json', 'Output to stdout as JSON')
102
- .action(async (options) => {
103
- try {
104
- const input: GeneratorInput = {
105
- merchant: {
106
- merchantId: options.id || `merchant-${Date.now()}`,
107
- primaryDomain: options.domain,
108
- },
109
- transport: {
110
- rest: {
111
- endpoint: options.endpoint,
112
- },
113
- },
114
- capabilities: {
115
- checkout: true,
116
- order: options.order || false,
117
- fulfillment: options.fulfillment || false,
118
- discount: options.discount || false,
119
- },
120
- security: {
121
- generateSigningKeys: options.order || false,
122
- },
123
- };
124
-
125
- const result = await buildProfile(input);
126
-
127
- if (options.json) {
128
- console.log(result.profileJson);
129
- } else {
130
- writeFileSync(options.output, result.profileJson);
131
- console.log(colors.green(`✓ Profile generated: ${options.output}`));
132
-
133
- if (result.signingKeyPair) {
134
- const keyFile = options.output.replace('.json', '-private-key.pem');
135
- writeFileSync(keyFile, result.signingKeyPair.privateKey);
136
- console.log(colors.yellow(`⚠ Private key saved: ${keyFile}`));
137
- console.log(colors.gray(' Keep this file secure! Do not commit to version control.'));
138
- }
139
-
140
- console.log(colors.blue('\nNext steps:'));
141
- console.log(' 1. Review the generated profile');
142
- console.log(' 2. Run: ucp-validate validate -f ' + options.output);
143
- console.log(' 3. Deploy to https://' + options.domain + '/.well-known/ucp');
144
- }
145
- } catch (error) {
146
- console.error(colors.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
147
- process.exit(1);
148
- }
149
- });
150
-
151
- /**
152
- * Generate minimal profile
153
- */
154
- program
155
- .command('generate-minimal')
156
- .description('Generate a minimal starter profile')
157
- .requiredOption('-e, --endpoint <url>', 'REST API endpoint URL')
158
- .option('-o, --output <path>', 'Output file path', 'ucp.json')
159
- .action((options) => {
160
- try {
161
- const profile = generateMinimalProfile(options.endpoint);
162
- const json = JSON.stringify(profile, null, 2);
163
-
164
- writeFileSync(options.output, json);
165
- console.log(colors.green(`✓ Minimal profile generated: ${options.output}`));
166
- } catch (error) {
167
- console.error(colors.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
168
- process.exit(1);
169
- }
170
- });
171
-
172
- /**
173
- * Generate hosting artifacts
174
- */
175
- program
176
- .command('hosting')
177
- .description('Generate hosting configuration files')
178
- .requiredOption('-f, --file <path>', 'Path to profile JSON file')
179
- .requiredOption('-d, --domain <domain>', 'Merchant domain')
180
- .requiredOption('-m, --mode <mode>', 'Hosting mode: static, edge-worker, reverse-proxy')
181
- .option('-p, --platform <platform>', 'Platform: nginx, apache, vercel, netlify, cloudflare-worker, s3-cloudfront')
182
- .option('-i, --id <id>', 'Merchant ID')
183
- .option('-o, --output <dir>', 'Output directory', './ucp-hosting')
184
- .action((options) => {
185
- try {
186
- const filePath = resolve(options.file);
187
- if (!existsSync(filePath)) {
188
- console.error(colors.red(`File not found: ${filePath}`));
189
- process.exit(1);
190
- }
191
-
192
- const profileJson = readFileSync(filePath, 'utf-8');
193
-
194
- const config: HostingConfig = {
195
- mode: options.mode as HostingMode,
196
- platform: options.platform as HostingPlatform,
197
- merchantId: options.id || `merchant-${Date.now()}`,
198
- merchantDomain: options.domain,
199
- };
200
-
201
- const artifacts = generateHostingArtifacts(config, profileJson);
202
-
203
- // Create output directory
204
- const outputDir = resolve(options.output);
205
- if (!existsSync(outputDir)) {
206
- const { mkdirSync } = require('fs');
207
- mkdirSync(outputDir, { recursive: true });
208
- }
209
-
210
- // Write artifacts
211
- for (const artifact of artifacts) {
212
- const artifactPath = resolve(outputDir, artifact.filename);
213
- // Create subdirectories if needed
214
- const dir = artifactPath.substring(0, artifactPath.lastIndexOf('/'));
215
- if (dir && !existsSync(dir)) {
216
- const { mkdirSync } = require('fs');
217
- mkdirSync(dir, { recursive: true });
218
- }
219
- writeFileSync(artifactPath, artifact.content);
220
- console.log(colors.green(`✓ ${artifact.filename}`));
221
- }
222
-
223
- console.log(colors.blue(`\nArtifacts generated in: ${outputDir}`));
224
- console.log(colors.gray('See README.md for installation instructions.'));
225
- } catch (error) {
226
- console.error(colors.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
227
- process.exit(1);
228
- }
229
- });
230
-
231
- /**
232
- * Print validation report to console
233
- */
234
- function printValidationReport(report: ValidationReport): void {
235
- console.log('\n' + colors.bold('UCP Profile Validation Report'));
236
- console.log('═'.repeat(50));
237
-
238
- if (report.profile_url) {
239
- console.log(colors.gray(`Profile: ${report.profile_url}`));
240
- }
241
- if (report.ucp_version) {
242
- console.log(colors.gray(`UCP Version: ${report.ucp_version}`));
243
- }
244
- console.log(colors.gray(`Validated: ${report.validated_at}`));
245
- console.log(colors.gray(`Mode: ${report.validation_mode}`));
246
- console.log('');
247
-
248
- // Summary
249
- if (report.ok) {
250
- console.log(colors.green('✓ Validation PASSED'));
251
- } else {
252
- console.log(colors.red('✗ Validation FAILED'));
253
- }
254
-
255
- // Issue counts
256
- const errors = report.issues.filter(i => i.severity === 'error');
257
- const warnings = report.issues.filter(i => i.severity === 'warn');
258
- const info = report.issues.filter(i => i.severity === 'info');
259
-
260
- if (report.issues.length === 0) {
261
- console.log(colors.green('\nNo issues found!'));
262
- } else {
263
- console.log(`\n${errors.length} error(s), ${warnings.length} warning(s), ${info.length} info`);
264
- }
265
-
266
- // Print issues
267
- if (errors.length > 0) {
268
- console.log('\n' + colors.red('Errors:'));
269
- for (const issue of errors) {
270
- printIssue(issue);
271
- }
272
- }
273
-
274
- if (warnings.length > 0) {
275
- console.log('\n' + colors.yellow('Warnings:'));
276
- for (const issue of warnings) {
277
- printIssue(issue);
278
- }
279
- }
280
-
281
- if (info.length > 0) {
282
- console.log('\n' + colors.blue('Info:'));
283
- for (const issue of info) {
284
- printIssue(issue);
285
- }
286
- }
287
-
288
- console.log('');
289
- }
290
-
291
- function printIssue(issue: { severity: string; code: string; path: string; message: string; hint?: string }): void {
292
- const icon = issue.severity === 'error' ? '✗' : issue.severity === 'warn' ? '⚠' : 'ℹ';
293
- const color = issue.severity === 'error' ? colors.red : issue.severity === 'warn' ? colors.yellow : colors.blue;
294
-
295
- console.log(` ${color(icon)} [${issue.code}] ${issue.message}`);
296
- console.log(colors.gray(` Path: ${issue.path}`));
297
- if (issue.hint) {
298
- console.log(colors.gray(` Hint: ${issue.hint}`));
299
- }
300
- }
301
-
302
- program.parse();