@ucptools/validator 1.0.0 → 1.1.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 (330) hide show
  1. package/dist/auth/config.d.ts +20 -0
  2. package/dist/auth/config.d.ts.map +1 -0
  3. package/dist/auth/config.js +114 -0
  4. package/dist/auth/config.js.map +1 -0
  5. package/dist/auth/index.d.ts +5 -0
  6. package/dist/auth/index.d.ts.map +1 -0
  7. package/dist/auth/index.js +17 -0
  8. package/dist/auth/index.js.map +1 -0
  9. package/dist/auth/middleware.d.ts +45 -0
  10. package/dist/auth/middleware.d.ts.map +1 -0
  11. package/dist/auth/middleware.js +170 -0
  12. package/dist/auth/middleware.js.map +1 -0
  13. package/dist/auth/service.d.ts +80 -0
  14. package/dist/auth/service.d.ts.map +1 -0
  15. package/dist/auth/service.js +298 -0
  16. package/dist/auth/service.js.map +1 -0
  17. package/dist/cli/index.d.ts +6 -0
  18. package/dist/cli/index.d.ts.map +1 -0
  19. package/dist/cli/index.js +375 -0
  20. package/dist/cli/index.js.map +1 -0
  21. package/dist/cli/mock-server.d.ts +20 -0
  22. package/dist/cli/mock-server.d.ts.map +1 -0
  23. package/dist/cli/mock-server.js +261 -0
  24. package/dist/cli/mock-server.js.map +1 -0
  25. package/dist/compliance/compliance-generator.d.ts +34 -0
  26. package/dist/compliance/compliance-generator.d.ts.map +1 -0
  27. package/dist/compliance/compliance-generator.js +320 -0
  28. package/dist/compliance/compliance-generator.js.map +1 -0
  29. package/dist/compliance/index.d.ts +8 -0
  30. package/dist/compliance/index.d.ts.map +1 -0
  31. package/dist/compliance/index.js +17 -0
  32. package/dist/compliance/index.js.map +1 -0
  33. package/dist/compliance/templates.d.ts +34 -0
  34. package/dist/compliance/templates.d.ts.map +1 -0
  35. package/{src/compliance/templates.ts → dist/compliance/templates.js} +117 -155
  36. package/dist/compliance/templates.js.map +1 -0
  37. package/dist/compliance/types.d.ts +64 -0
  38. package/dist/compliance/types.d.ts.map +1 -0
  39. package/dist/compliance/types.js +64 -0
  40. package/dist/compliance/types.js.map +1 -0
  41. package/dist/db/index.d.ts +17 -0
  42. package/dist/db/index.d.ts.map +1 -0
  43. package/dist/db/index.js +80 -0
  44. package/dist/db/index.js.map +1 -0
  45. package/dist/db/schema.d.ts +3886 -0
  46. package/dist/db/schema.d.ts.map +1 -0
  47. package/dist/db/schema.js +425 -0
  48. package/dist/db/schema.js.map +1 -0
  49. package/dist/db/utils.d.ts +252 -0
  50. package/dist/db/utils.d.ts.map +1 -0
  51. package/dist/db/utils.js +295 -0
  52. package/dist/db/utils.js.map +1 -0
  53. package/dist/feed-analyzer/feed-analyzer.d.ts +26 -0
  54. package/dist/feed-analyzer/feed-analyzer.d.ts.map +1 -0
  55. package/{src/feed-analyzer/feed-analyzer.ts → dist/feed-analyzer/feed-analyzer.js} +856 -726
  56. package/dist/feed-analyzer/feed-analyzer.js.map +1 -0
  57. package/dist/feed-analyzer/index.d.ts +8 -0
  58. package/dist/feed-analyzer/index.d.ts.map +1 -0
  59. package/dist/feed-analyzer/index.js +19 -0
  60. package/dist/feed-analyzer/index.js.map +1 -0
  61. package/dist/feed-analyzer/types.d.ts +285 -0
  62. package/dist/feed-analyzer/types.d.ts.map +1 -0
  63. package/dist/feed-analyzer/types.js +175 -0
  64. package/dist/feed-analyzer/types.js.map +1 -0
  65. package/{src/generator/index.ts → dist/generator/index.d.ts} +1 -1
  66. package/dist/generator/index.d.ts.map +1 -0
  67. package/dist/generator/index.js +13 -0
  68. package/dist/generator/index.js.map +1 -0
  69. package/dist/generator/key-generator.d.ts +24 -0
  70. package/dist/generator/key-generator.d.ts.map +1 -0
  71. package/dist/generator/key-generator.js +144 -0
  72. package/dist/generator/key-generator.js.map +1 -0
  73. package/dist/generator/profile-builder.d.ts +15 -0
  74. package/dist/generator/profile-builder.d.ts.map +1 -0
  75. package/dist/generator/profile-builder.js +338 -0
  76. package/dist/generator/profile-builder.js.map +1 -0
  77. package/dist/hosting/artifacts-generator.d.ts +10 -0
  78. package/dist/hosting/artifacts-generator.d.ts.map +1 -0
  79. package/{src/hosting/artifacts-generator.ts → dist/hosting/artifacts-generator.js} +191 -241
  80. package/dist/hosting/artifacts-generator.js.map +1 -0
  81. package/{src/hosting/index.ts → dist/hosting/index.d.ts} +1 -1
  82. package/dist/hosting/index.d.ts.map +1 -0
  83. package/dist/hosting/index.js +10 -0
  84. package/dist/hosting/index.js.map +1 -0
  85. package/dist/index.d.ts +18 -0
  86. package/dist/index.d.ts.map +1 -0
  87. package/dist/index.js +78 -0
  88. package/dist/index.js.map +1 -0
  89. package/dist/lib/analytics.d.ts +337 -0
  90. package/dist/lib/analytics.d.ts.map +1 -0
  91. package/dist/lib/analytics.js +188 -0
  92. package/dist/lib/analytics.js.map +1 -0
  93. package/{src/security/index.ts → dist/security/index.d.ts} +8 -15
  94. package/dist/security/index.d.ts.map +1 -0
  95. package/dist/security/index.js +12 -0
  96. package/dist/security/index.js.map +1 -0
  97. package/dist/security/security-scanner.d.ts +10 -0
  98. package/dist/security/security-scanner.d.ts.map +1 -0
  99. package/dist/security/security-scanner.js +669 -0
  100. package/dist/security/security-scanner.js.map +1 -0
  101. package/dist/security/types.d.ts +80 -0
  102. package/dist/security/types.d.ts.map +1 -0
  103. package/dist/security/types.js +21 -0
  104. package/dist/security/types.js.map +1 -0
  105. package/dist/services/analytics.d.ts +114 -0
  106. package/dist/services/analytics.d.ts.map +1 -0
  107. package/dist/services/analytics.js +862 -0
  108. package/dist/services/analytics.js.map +1 -0
  109. package/dist/services/badge.d.ts +31 -0
  110. package/dist/services/badge.d.ts.map +1 -0
  111. package/dist/services/badge.js +152 -0
  112. package/dist/services/badge.js.map +1 -0
  113. package/dist/services/cron.d.ts +125 -0
  114. package/dist/services/cron.d.ts.map +1 -0
  115. package/dist/services/cron.js +613 -0
  116. package/dist/services/cron.js.map +1 -0
  117. package/dist/services/directory.d.ts +106 -0
  118. package/dist/services/directory.d.ts.map +1 -0
  119. package/dist/services/directory.js +351 -0
  120. package/dist/services/directory.js.map +1 -0
  121. package/dist/services/email.d.ts +112 -0
  122. package/dist/services/email.d.ts.map +1 -0
  123. package/dist/services/email.js +772 -0
  124. package/dist/services/email.js.map +1 -0
  125. package/dist/services/hosted-profiles.d.ts +77 -0
  126. package/dist/services/hosted-profiles.d.ts.map +1 -0
  127. package/dist/services/hosted-profiles.js +433 -0
  128. package/dist/services/hosted-profiles.js.map +1 -0
  129. package/dist/services/latency.d.ts +67 -0
  130. package/dist/services/latency.d.ts.map +1 -0
  131. package/dist/services/latency.js +274 -0
  132. package/dist/services/latency.js.map +1 -0
  133. package/dist/services/manifest-compliance.d.ts +64 -0
  134. package/dist/services/manifest-compliance.d.ts.map +1 -0
  135. package/dist/services/manifest-compliance.js +271 -0
  136. package/dist/services/manifest-compliance.js.map +1 -0
  137. package/dist/services/monitoring-diff.d.ts +31 -0
  138. package/dist/services/monitoring-diff.d.ts.map +1 -0
  139. package/dist/services/monitoring-diff.js +189 -0
  140. package/dist/services/monitoring-diff.js.map +1 -0
  141. package/dist/services/notifications.d.ts +46 -0
  142. package/dist/services/notifications.d.ts.map +1 -0
  143. package/dist/services/notifications.js +88 -0
  144. package/dist/services/notifications.js.map +1 -0
  145. package/dist/services/stripe.d.ts +93 -0
  146. package/dist/services/stripe.d.ts.map +1 -0
  147. package/dist/services/stripe.js +490 -0
  148. package/dist/services/stripe.js.map +1 -0
  149. package/dist/services/validation-history.d.ts +99 -0
  150. package/dist/services/validation-history.d.ts.map +1 -0
  151. package/dist/services/validation-history.js +344 -0
  152. package/dist/services/validation-history.js.map +1 -0
  153. package/dist/services/validation-logging.d.ts +103 -0
  154. package/dist/services/validation-logging.d.ts.map +1 -0
  155. package/dist/services/validation-logging.js +210 -0
  156. package/dist/services/validation-logging.js.map +1 -0
  157. package/dist/services/validation.d.ts +119 -0
  158. package/dist/services/validation.d.ts.map +1 -0
  159. package/dist/services/validation.js +1185 -0
  160. package/dist/services/validation.js.map +1 -0
  161. package/dist/simulator/agent-simulator.d.ts +69 -0
  162. package/dist/simulator/agent-simulator.d.ts.map +1 -0
  163. package/dist/simulator/agent-simulator.js +870 -0
  164. package/dist/simulator/agent-simulator.js.map +1 -0
  165. package/{src/simulator/index.ts → dist/simulator/index.d.ts} +7 -7
  166. package/dist/simulator/index.d.ts.map +1 -0
  167. package/dist/simulator/index.js +23 -0
  168. package/dist/simulator/index.js.map +1 -0
  169. package/{src/simulator/types.ts → dist/simulator/types.d.ts} +171 -170
  170. package/dist/simulator/types.d.ts.map +1 -0
  171. package/dist/simulator/types.js +18 -0
  172. package/dist/simulator/types.js.map +1 -0
  173. package/dist/types/acp-validation.d.ts +87 -0
  174. package/dist/types/acp-validation.d.ts.map +1 -0
  175. package/dist/types/acp-validation.js +40 -0
  176. package/dist/types/acp-validation.js.map +1 -0
  177. package/dist/types/analytics.d.ts +182 -0
  178. package/dist/types/analytics.d.ts.map +1 -0
  179. package/dist/types/analytics.js +7 -0
  180. package/dist/types/analytics.js.map +1 -0
  181. package/dist/types/generator.d.ts +106 -0
  182. package/dist/types/generator.d.ts.map +1 -0
  183. package/dist/types/generator.js +6 -0
  184. package/dist/types/generator.js.map +1 -0
  185. package/{src/types/index.ts → dist/types/index.d.ts} +1 -1
  186. package/dist/types/index.d.ts.map +1 -0
  187. package/dist/types/index.js +23 -0
  188. package/dist/types/index.js.map +1 -0
  189. package/dist/types/ucp-profile.d.ts +111 -0
  190. package/dist/types/ucp-profile.d.ts.map +1 -0
  191. package/dist/types/ucp-profile.js +45 -0
  192. package/dist/types/ucp-profile.js.map +1 -0
  193. package/dist/types/validation.d.ts +76 -0
  194. package/dist/types/validation.d.ts.map +1 -0
  195. package/dist/types/validation.js +42 -0
  196. package/dist/types/validation.js.map +1 -0
  197. package/dist/validator/acp/index.d.ts +31 -0
  198. package/dist/validator/acp/index.d.ts.map +1 -0
  199. package/dist/validator/acp/index.js +574 -0
  200. package/dist/validator/acp/index.js.map +1 -0
  201. package/dist/validator/index.d.ts +26 -0
  202. package/dist/validator/index.d.ts.map +1 -0
  203. package/dist/validator/index.js +161 -0
  204. package/dist/validator/index.js.map +1 -0
  205. package/dist/validator/network-validator.d.ts +28 -0
  206. package/dist/validator/network-validator.d.ts.map +1 -0
  207. package/dist/validator/network-validator.js +319 -0
  208. package/dist/validator/network-validator.js.map +1 -0
  209. package/dist/validator/rules-validator.d.ts +19 -0
  210. package/dist/validator/rules-validator.d.ts.map +1 -0
  211. package/dist/validator/rules-validator.js +306 -0
  212. package/dist/validator/rules-validator.js.map +1 -0
  213. package/dist/validator/sdk-validator.d.ts +58 -0
  214. package/dist/validator/sdk-validator.d.ts.map +1 -0
  215. package/{src/validator/sdk-validator.ts → dist/validator/sdk-validator.js} +273 -330
  216. package/dist/validator/sdk-validator.js.map +1 -0
  217. package/dist/validator/structural-validator.d.ts +11 -0
  218. package/dist/validator/structural-validator.d.ts.map +1 -0
  219. package/dist/validator/structural-validator.js +549 -0
  220. package/dist/validator/structural-validator.js.map +1 -0
  221. package/dist/validator/utils.d.ts +51 -0
  222. package/dist/validator/utils.d.ts.map +1 -0
  223. package/dist/validator/utils.js +132 -0
  224. package/dist/validator/utils.js.map +1 -0
  225. package/package.json +44 -12
  226. package/CLAUDE.md +0 -109
  227. package/api/analyze-feed.js +0 -140
  228. package/api/badge.js +0 -185
  229. package/api/benchmark.js +0 -177
  230. package/api/directory-stats.ts +0 -29
  231. package/api/directory.ts +0 -73
  232. package/api/generate-compliance.js +0 -143
  233. package/api/generate-schema.js +0 -457
  234. package/api/generate.js +0 -132
  235. package/api/security-scan.js +0 -133
  236. package/api/simulate.js +0 -187
  237. package/api/tsconfig.json +0 -10
  238. package/api/validate.js +0 -1351
  239. package/apify-actor/.actor/actor.json +0 -68
  240. package/apify-actor/.actor/input_schema.json +0 -32
  241. package/apify-actor/APIFY-STORE-LISTING.md +0 -412
  242. package/apify-actor/Dockerfile +0 -8
  243. package/apify-actor/README.md +0 -166
  244. package/apify-actor/main.ts +0 -111
  245. package/apify-actor/package.json +0 -17
  246. package/apify-actor/src/main.js +0 -199
  247. package/docs/BRAND-IDENTITY.md +0 -238
  248. package/docs/BRAND-STYLE-GUIDE.md +0 -356
  249. package/drizzle/0000_black_king_cobra.sql +0 -39
  250. package/drizzle/meta/0000_snapshot.json +0 -309
  251. package/drizzle/meta/_journal.json +0 -13
  252. package/drizzle.config.ts +0 -10
  253. package/public/.well-known/ucp +0 -25
  254. package/public/android-chrome-192x192.png +0 -0
  255. package/public/android-chrome-512x512.png +0 -0
  256. package/public/apple-touch-icon.png +0 -0
  257. package/public/brand.css +0 -321
  258. package/public/directory.html +0 -701
  259. package/public/favicon-16x16.png +0 -0
  260. package/public/favicon-32x32.png +0 -0
  261. package/public/favicon.ico +0 -0
  262. package/public/guides/bigcommerce.html +0 -743
  263. package/public/guides/fastucp.html +0 -838
  264. package/public/guides/magento.html +0 -779
  265. package/public/guides/shopify.html +0 -726
  266. package/public/guides/squarespace.html +0 -749
  267. package/public/guides/wix.html +0 -747
  268. package/public/guides/woocommerce.html +0 -733
  269. package/public/index.html +0 -3835
  270. package/public/learn.html +0 -396
  271. package/public/logo.jpeg +0 -0
  272. package/public/og-image-icon.png +0 -0
  273. package/public/og-image.png +0 -0
  274. package/public/robots.txt +0 -6
  275. package/public/site.webmanifest +0 -31
  276. package/public/sitemap.xml +0 -69
  277. package/public/social/linkedin-banner-1128x191.png +0 -0
  278. package/public/social/temp.PNG +0 -0
  279. package/public/social/x-header-1500x500.png +0 -0
  280. package/public/verify.html +0 -410
  281. package/scripts/generate-favicons.js +0 -44
  282. package/scripts/generate-ico.js +0 -23
  283. package/scripts/generate-og-image.js +0 -45
  284. package/scripts/reset-db.ts +0 -77
  285. package/scripts/seed-db.ts +0 -71
  286. package/scripts/setup-benchmark-db.js +0 -70
  287. package/src/api/server.ts +0 -266
  288. package/src/cli/index.ts +0 -302
  289. package/src/compliance/compliance-generator.ts +0 -452
  290. package/src/compliance/index.ts +0 -28
  291. package/src/compliance/types.ts +0 -170
  292. package/src/db/index.ts +0 -28
  293. package/src/db/schema.ts +0 -84
  294. package/src/feed-analyzer/index.ts +0 -34
  295. package/src/feed-analyzer/types.ts +0 -354
  296. package/src/generator/key-generator.ts +0 -124
  297. package/src/generator/profile-builder.ts +0 -402
  298. package/src/index.ts +0 -105
  299. package/src/security/security-scanner.ts +0 -604
  300. package/src/security/types.ts +0 -55
  301. package/src/services/directory.ts +0 -434
  302. package/src/simulator/agent-simulator.ts +0 -941
  303. package/src/types/generator.ts +0 -140
  304. package/src/types/ucp-profile.ts +0 -140
  305. package/src/types/validation.ts +0 -89
  306. package/src/validator/index.ts +0 -194
  307. package/src/validator/network-validator.ts +0 -417
  308. package/src/validator/rules-validator.ts +0 -297
  309. package/src/validator/structural-validator.ts +0 -476
  310. package/tests/fixtures/non-compliant-profile.json +0 -25
  311. package/tests/fixtures/official-sample-profile.json +0 -75
  312. package/tests/integration/benchmark.test.ts +0 -207
  313. package/tests/integration/database.test.ts +0 -163
  314. package/tests/integration/directory-api.test.ts +0 -268
  315. package/tests/integration/simulate-api.test.ts +0 -230
  316. package/tests/integration/validate-api.test.ts +0 -269
  317. package/tests/setup.ts +0 -15
  318. package/tests/unit/agent-simulator.test.ts +0 -575
  319. package/tests/unit/compliance-generator.test.ts +0 -374
  320. package/tests/unit/directory-service.test.ts +0 -272
  321. package/tests/unit/feed-analyzer.test.ts +0 -517
  322. package/tests/unit/lint-suggestions.test.ts +0 -423
  323. package/tests/unit/official-samples.test.ts +0 -211
  324. package/tests/unit/pdf-report.test.ts +0 -390
  325. package/tests/unit/sdk-validator.test.ts +0 -531
  326. package/tests/unit/security-scanner.test.ts +0 -410
  327. package/tests/unit/validation.test.ts +0 -390
  328. package/tsconfig.json +0 -20
  329. package/vercel.json +0 -34
  330. package/vitest.config.ts +0 -22
@@ -1,476 +0,0 @@
1
- /**
2
- * Structural Validator
3
- * Validates UCP Profile JSON structure (no network calls)
4
- */
5
-
6
- import type { UcpProfile, UcpService, UcpCapability } from '../types/ucp-profile.js';
7
- import type { ValidationIssue } from '../types/validation.js';
8
- import { ValidationErrorCodes } from '../types/validation.js';
9
-
10
- /**
11
- * Version format regex (YYYY-MM-DD)
12
- */
13
- const VERSION_REGEX = /^\d{4}-\d{2}-\d{2}$/;
14
-
15
- /**
16
- * Validate the structural integrity of a UCP profile
17
- * Returns an array of validation issues
18
- */
19
- export function validateStructure(profile: unknown): ValidationIssue[] {
20
- const issues: ValidationIssue[] = [];
21
-
22
- // Check if it's an object
23
- if (!profile || typeof profile !== 'object') {
24
- issues.push({
25
- severity: 'error',
26
- code: ValidationErrorCodes.MISSING_UCP_OBJECT,
27
- path: '$',
28
- message: 'Profile must be a JSON object',
29
- hint: 'Ensure your profile is valid JSON and contains a root object',
30
- });
31
- return issues;
32
- }
33
-
34
- const profileObj = profile as Record<string, unknown>;
35
-
36
- // Check for ucp root object
37
- if (!profileObj.ucp || typeof profileObj.ucp !== 'object') {
38
- issues.push({
39
- severity: 'error',
40
- code: ValidationErrorCodes.MISSING_UCP_OBJECT,
41
- path: '$.ucp',
42
- message: 'Missing required "ucp" object at root level',
43
- hint: 'Add a "ucp" object containing version, services, and capabilities',
44
- });
45
- return issues;
46
- }
47
-
48
- const ucp = profileObj.ucp as Record<string, unknown>;
49
-
50
- // Validate version
51
- issues.push(...validateVersion(ucp));
52
-
53
- // Validate services
54
- issues.push(...validateServices(ucp));
55
-
56
- // Validate capabilities
57
- issues.push(...validateCapabilities(ucp));
58
-
59
- // Validate signing_keys if present
60
- if (profileObj.signing_keys !== undefined) {
61
- issues.push(...validateSigningKeys(profileObj.signing_keys));
62
- }
63
-
64
- return issues;
65
- }
66
-
67
- /**
68
- * Validate UCP version field
69
- */
70
- function validateVersion(ucp: Record<string, unknown>): ValidationIssue[] {
71
- const issues: ValidationIssue[] = [];
72
-
73
- if (!ucp.version) {
74
- issues.push({
75
- severity: 'error',
76
- code: ValidationErrorCodes.MISSING_VERSION,
77
- path: '$.ucp.version',
78
- message: 'Missing required "version" field in ucp object',
79
- hint: 'Add version field with format "YYYY-MM-DD" (e.g., "2026-01-11")',
80
- });
81
- } else if (typeof ucp.version !== 'string') {
82
- issues.push({
83
- severity: 'error',
84
- code: ValidationErrorCodes.INVALID_VERSION_FORMAT,
85
- path: '$.ucp.version',
86
- message: 'Version must be a string',
87
- hint: 'Use format "YYYY-MM-DD" (e.g., "2026-01-11")',
88
- });
89
- } else if (!VERSION_REGEX.test(ucp.version)) {
90
- issues.push({
91
- severity: 'error',
92
- code: ValidationErrorCodes.INVALID_VERSION_FORMAT,
93
- path: '$.ucp.version',
94
- message: `Invalid version format: "${ucp.version}"`,
95
- hint: 'Use format "YYYY-MM-DD" (e.g., "2026-01-11")',
96
- });
97
- }
98
-
99
- return issues;
100
- }
101
-
102
- /**
103
- * Validate services object
104
- */
105
- function validateServices(ucp: Record<string, unknown>): ValidationIssue[] {
106
- const issues: ValidationIssue[] = [];
107
-
108
- if (!ucp.services) {
109
- issues.push({
110
- severity: 'error',
111
- code: ValidationErrorCodes.MISSING_SERVICES,
112
- path: '$.ucp.services',
113
- message: 'Missing required "services" field in ucp object',
114
- hint: 'Add a services object with at least one service definition',
115
- });
116
- return issues;
117
- }
118
-
119
- if (typeof ucp.services !== 'object' || Array.isArray(ucp.services)) {
120
- issues.push({
121
- severity: 'error',
122
- code: ValidationErrorCodes.INVALID_SERVICE_STRUCTURE,
123
- path: '$.ucp.services',
124
- message: 'Services must be an object (not an array)',
125
- hint: 'Use format: { "dev.ucp.shopping": { ... } }',
126
- });
127
- return issues;
128
- }
129
-
130
- const services = ucp.services as Record<string, unknown>;
131
-
132
- // Check for at least one service
133
- if (Object.keys(services).length === 0) {
134
- issues.push({
135
- severity: 'warn',
136
- code: ValidationErrorCodes.MISSING_SERVICES,
137
- path: '$.ucp.services',
138
- message: 'Services object is empty',
139
- hint: 'Add at least one service (e.g., "dev.ucp.shopping")',
140
- });
141
- }
142
-
143
- // Validate each service
144
- for (const [serviceName, service] of Object.entries(services)) {
145
- issues.push(...validateService(serviceName, service));
146
- }
147
-
148
- return issues;
149
- }
150
-
151
- /**
152
- * Validate individual service definition
153
- */
154
- function validateService(name: string, service: unknown): ValidationIssue[] {
155
- const issues: ValidationIssue[] = [];
156
- const path = `$.ucp.services["${name}"]`;
157
-
158
- if (!service || typeof service !== 'object') {
159
- issues.push({
160
- severity: 'error',
161
- code: ValidationErrorCodes.INVALID_SERVICE_STRUCTURE,
162
- path,
163
- message: `Service "${name}" must be an object`,
164
- });
165
- return issues;
166
- }
167
-
168
- const svc = service as Record<string, unknown>;
169
-
170
- // Check required version
171
- if (!svc.version) {
172
- issues.push({
173
- severity: 'error',
174
- code: ValidationErrorCodes.INVALID_SERVICE_STRUCTURE,
175
- path: `${path}.version`,
176
- message: `Service "${name}" missing required "version" field`,
177
- });
178
- } else if (typeof svc.version === 'string' && !VERSION_REGEX.test(svc.version)) {
179
- issues.push({
180
- severity: 'error',
181
- code: ValidationErrorCodes.INVALID_VERSION_FORMAT,
182
- path: `${path}.version`,
183
- message: `Invalid version format in service "${name}"`,
184
- hint: 'Use format "YYYY-MM-DD"',
185
- });
186
- }
187
-
188
- // Check required spec
189
- if (!svc.spec) {
190
- issues.push({
191
- severity: 'error',
192
- code: ValidationErrorCodes.INVALID_SERVICE_STRUCTURE,
193
- path: `${path}.spec`,
194
- message: `Service "${name}" missing required "spec" field`,
195
- });
196
- }
197
-
198
- // Check for at least one transport
199
- const hasTransport = svc.rest || svc.mcp || svc.a2a || svc.embedded;
200
- if (!hasTransport) {
201
- issues.push({
202
- severity: 'warn',
203
- code: ValidationErrorCodes.INVALID_SERVICE_STRUCTURE,
204
- path,
205
- message: `Service "${name}" has no transport bindings`,
206
- hint: 'Add at least one transport: rest, mcp, a2a, or embedded',
207
- });
208
- }
209
-
210
- // Validate REST transport if present
211
- if (svc.rest) {
212
- issues.push(...validateRestTransport(svc.rest, `${path}.rest`));
213
- }
214
-
215
- // Validate MCP transport if present
216
- if (svc.mcp) {
217
- issues.push(...validateMcpTransport(svc.mcp, `${path}.mcp`));
218
- }
219
-
220
- return issues;
221
- }
222
-
223
- /**
224
- * Validate REST transport binding
225
- */
226
- function validateRestTransport(transport: unknown, path: string): ValidationIssue[] {
227
- const issues: ValidationIssue[] = [];
228
-
229
- if (!transport || typeof transport !== 'object') {
230
- issues.push({
231
- severity: 'error',
232
- code: ValidationErrorCodes.INVALID_SERVICE_STRUCTURE,
233
- path,
234
- message: 'REST transport must be an object',
235
- });
236
- return issues;
237
- }
238
-
239
- const rest = transport as Record<string, unknown>;
240
-
241
- if (!rest.schema) {
242
- issues.push({
243
- severity: 'error',
244
- code: ValidationErrorCodes.INVALID_SERVICE_STRUCTURE,
245
- path: `${path}.schema`,
246
- message: 'REST transport missing required "schema" field',
247
- });
248
- }
249
-
250
- if (!rest.endpoint) {
251
- issues.push({
252
- severity: 'error',
253
- code: ValidationErrorCodes.INVALID_SERVICE_STRUCTURE,
254
- path: `${path}.endpoint`,
255
- message: 'REST transport missing required "endpoint" field',
256
- });
257
- }
258
-
259
- return issues;
260
- }
261
-
262
- /**
263
- * Validate MCP transport binding
264
- */
265
- function validateMcpTransport(transport: unknown, path: string): ValidationIssue[] {
266
- const issues: ValidationIssue[] = [];
267
-
268
- if (!transport || typeof transport !== 'object') {
269
- issues.push({
270
- severity: 'error',
271
- code: ValidationErrorCodes.INVALID_SERVICE_STRUCTURE,
272
- path,
273
- message: 'MCP transport must be an object',
274
- });
275
- return issues;
276
- }
277
-
278
- const mcp = transport as Record<string, unknown>;
279
-
280
- if (!mcp.schema) {
281
- issues.push({
282
- severity: 'error',
283
- code: ValidationErrorCodes.INVALID_SERVICE_STRUCTURE,
284
- path: `${path}.schema`,
285
- message: 'MCP transport missing required "schema" field',
286
- });
287
- }
288
-
289
- if (!mcp.endpoint) {
290
- issues.push({
291
- severity: 'error',
292
- code: ValidationErrorCodes.INVALID_SERVICE_STRUCTURE,
293
- path: `${path}.endpoint`,
294
- message: 'MCP transport missing required "endpoint" field',
295
- });
296
- }
297
-
298
- return issues;
299
- }
300
-
301
- /**
302
- * Validate capabilities array
303
- */
304
- function validateCapabilities(ucp: Record<string, unknown>): ValidationIssue[] {
305
- const issues: ValidationIssue[] = [];
306
-
307
- if (!ucp.capabilities) {
308
- issues.push({
309
- severity: 'error',
310
- code: ValidationErrorCodes.MISSING_CAPABILITIES,
311
- path: '$.ucp.capabilities',
312
- message: 'Missing required "capabilities" field in ucp object',
313
- hint: 'Add a capabilities array with at least one capability',
314
- });
315
- return issues;
316
- }
317
-
318
- if (!Array.isArray(ucp.capabilities)) {
319
- issues.push({
320
- severity: 'error',
321
- code: ValidationErrorCodes.INVALID_CAPABILITY_STRUCTURE,
322
- path: '$.ucp.capabilities',
323
- message: 'Capabilities must be an array',
324
- });
325
- return issues;
326
- }
327
-
328
- if (ucp.capabilities.length === 0) {
329
- issues.push({
330
- severity: 'warn',
331
- code: ValidationErrorCodes.MISSING_CAPABILITIES,
332
- path: '$.ucp.capabilities',
333
- message: 'Capabilities array is empty',
334
- hint: 'Add at least one capability (e.g., dev.ucp.shopping.checkout)',
335
- });
336
- }
337
-
338
- // Validate each capability
339
- for (let i = 0; i < ucp.capabilities.length; i++) {
340
- issues.push(...validateCapability(ucp.capabilities[i], i));
341
- }
342
-
343
- return issues;
344
- }
345
-
346
- /**
347
- * Validate individual capability
348
- */
349
- function validateCapability(capability: unknown, index: number): ValidationIssue[] {
350
- const issues: ValidationIssue[] = [];
351
- const path = `$.ucp.capabilities[${index}]`;
352
-
353
- if (!capability || typeof capability !== 'object') {
354
- issues.push({
355
- severity: 'error',
356
- code: ValidationErrorCodes.INVALID_CAPABILITY_STRUCTURE,
357
- path,
358
- message: `Capability at index ${index} must be an object`,
359
- });
360
- return issues;
361
- }
362
-
363
- const cap = capability as Record<string, unknown>;
364
-
365
- // Check required fields
366
- if (!cap.name) {
367
- issues.push({
368
- severity: 'error',
369
- code: ValidationErrorCodes.INVALID_CAPABILITY_STRUCTURE,
370
- path: `${path}.name`,
371
- message: 'Capability missing required "name" field',
372
- });
373
- }
374
-
375
- if (!cap.version) {
376
- issues.push({
377
- severity: 'error',
378
- code: ValidationErrorCodes.INVALID_CAPABILITY_STRUCTURE,
379
- path: `${path}.version`,
380
- message: 'Capability missing required "version" field',
381
- });
382
- } else if (typeof cap.version === 'string' && !VERSION_REGEX.test(cap.version)) {
383
- issues.push({
384
- severity: 'error',
385
- code: ValidationErrorCodes.INVALID_VERSION_FORMAT,
386
- path: `${path}.version`,
387
- message: `Invalid version format: "${cap.version}"`,
388
- hint: 'Use format "YYYY-MM-DD"',
389
- });
390
- }
391
-
392
- if (!cap.spec) {
393
- issues.push({
394
- severity: 'error',
395
- code: ValidationErrorCodes.INVALID_CAPABILITY_STRUCTURE,
396
- path: `${path}.spec`,
397
- message: 'Capability missing required "spec" field',
398
- });
399
- }
400
-
401
- if (!cap.schema) {
402
- issues.push({
403
- severity: 'error',
404
- code: ValidationErrorCodes.INVALID_CAPABILITY_STRUCTURE,
405
- path: `${path}.schema`,
406
- message: 'Capability missing required "schema" field',
407
- });
408
- }
409
-
410
- return issues;
411
- }
412
-
413
- /**
414
- * Validate signing_keys structure
415
- * Accepts signing_keys as a direct array of JWKs (official UCP format)
416
- */
417
- function validateSigningKeys(signingKeys: unknown): ValidationIssue[] {
418
- const issues: ValidationIssue[] = [];
419
- const path = '$.signing_keys';
420
-
421
- // signing_keys should be an array of JWKs
422
- if (!Array.isArray(signingKeys)) {
423
- issues.push({
424
- severity: 'error',
425
- code: ValidationErrorCodes.INVALID_SIGNING_KEY,
426
- path,
427
- message: 'signing_keys must be an array of JWK objects',
428
- });
429
- return issues;
430
- }
431
-
432
- for (let i = 0; i < signingKeys.length; i++) {
433
- issues.push(...validateJwk(signingKeys[i], `${path}[${i}]`));
434
- }
435
-
436
- return issues;
437
- }
438
-
439
- /**
440
- * Validate JWK structure
441
- */
442
- function validateJwk(jwk: unknown, path: string): ValidationIssue[] {
443
- const issues: ValidationIssue[] = [];
444
-
445
- if (!jwk || typeof jwk !== 'object') {
446
- issues.push({
447
- severity: 'error',
448
- code: ValidationErrorCodes.INVALID_SIGNING_KEY,
449
- path,
450
- message: 'JWK must be an object',
451
- });
452
- return issues;
453
- }
454
-
455
- const key = jwk as Record<string, unknown>;
456
-
457
- if (!key.kty) {
458
- issues.push({
459
- severity: 'error',
460
- code: ValidationErrorCodes.INVALID_SIGNING_KEY,
461
- path: `${path}.kty`,
462
- message: 'JWK missing required "kty" field',
463
- });
464
- }
465
-
466
- if (!key.kid) {
467
- issues.push({
468
- severity: 'error',
469
- code: ValidationErrorCodes.INVALID_SIGNING_KEY,
470
- path: `${path}.kid`,
471
- message: 'JWK missing required "kid" field',
472
- });
473
- }
474
-
475
- return issues;
476
- }
@@ -1,25 +0,0 @@
1
- {
2
- "ucp": {
3
- "version": "1.0",
4
- "services": {
5
- "checkout": "/api/ucp/checkout",
6
- "orders": "/api/ucp/orders",
7
- "products": "/api/ucp/products"
8
- },
9
- "capabilities": [
10
- "dev.ucp.shopping.checkout",
11
- "dev.ucp.shopping.catalog",
12
- "dev.ucp.shopping.fulfillment"
13
- ],
14
- "merchant": {
15
- "name": "Test Merchant",
16
- "description": "A non-compliant but working UCP profile",
17
- "website": "https://example.com"
18
- },
19
- "sandbox": true
20
- },
21
- "payment": {
22
- "sandbox_mode": true,
23
- "accepted_tokens": ["sandbox_*", "test"]
24
- }
25
- }
@@ -1,75 +0,0 @@
1
- {
2
- "ucp": {
3
- "version": "2026-01-11",
4
- "services": {
5
- "dev.ucp.shopping": {
6
- "version": "2026-01-11",
7
- "spec": "https://ucp.dev/specs/shopping",
8
- "rest": {
9
- "schema": "https://ucp.dev/services/shopping/openapi.json",
10
- "endpoint": "https://example.com/api"
11
- }
12
- }
13
- },
14
- "capabilities": [
15
- {
16
- "name": "dev.ucp.shopping.checkout",
17
- "version": "2026-01-11",
18
- "spec": "https://ucp.dev/specs/shopping/checkout",
19
- "schema": "https://ucp.dev/schemas/shopping/checkout.json"
20
- },
21
- {
22
- "name": "dev.ucp.shopping.order",
23
- "version": "2026-01-11",
24
- "spec": "https://ucp.dev/specs/shopping/order",
25
- "schema": "https://ucp.dev/schemas/shopping/order.json",
26
- "extends": "dev.ucp.shopping.checkout"
27
- },
28
- {
29
- "name": "dev.ucp.shopping.fulfillment",
30
- "version": "2026-01-11",
31
- "spec": "https://ucp.dev/specs/shopping/fulfillment",
32
- "schema": "https://ucp.dev/schemas/shopping/fulfillment.json",
33
- "extends": "dev.ucp.shopping.order"
34
- },
35
- {
36
- "name": "dev.ucp.shopping.discount",
37
- "version": "2026-01-11",
38
- "spec": "https://ucp.dev/specs/shopping/discount",
39
- "schema": "https://ucp.dev/schemas/shopping/discount.json",
40
- "extends": "dev.ucp.shopping.checkout"
41
- },
42
- {
43
- "name": "dev.ucp.shopping.buyer_consent",
44
- "version": "2026-01-11",
45
- "spec": "https://ucp.dev/specs/shopping/buyer_consent",
46
- "schema": "https://ucp.dev/schemas/shopping/buyer_consent.json",
47
- "extends": "dev.ucp.shopping.checkout"
48
- }
49
- ]
50
- },
51
- "signing_keys": [
52
- {
53
- "kty": "EC",
54
- "crv": "P-256",
55
- "x": "test-x-coordinate",
56
- "y": "test-y-coordinate",
57
- "kid": "key-2026-01"
58
- }
59
- ],
60
- "payment": {
61
- "handlers": [
62
- {
63
- "id": "mock_payment",
64
- "name": "com.example.mock_payment",
65
- "version": "2026-01-11",
66
- "spec": "https://example.com/payment/spec",
67
- "config_schema": "https://example.com/payment/config.json",
68
- "instrument_schemas": ["https://example.com/payment/instrument.json"],
69
- "config": {
70
- "mode": "sandbox"
71
- }
72
- }
73
- ]
74
- }
75
- }