@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
package/src/db/schema.ts DELETED
@@ -1,84 +0,0 @@
1
- import {
2
- pgTable,
3
- uuid,
4
- varchar,
5
- text,
6
- integer,
7
- timestamp,
8
- boolean,
9
- index,
10
- decimal,
11
- } from 'drizzle-orm/pg-core';
12
-
13
- /**
14
- * Merchants table - stores UCP-enabled merchant directory entries
15
- */
16
- export const merchants = pgTable(
17
- 'merchants',
18
- {
19
- id: uuid('id').primaryKey().defaultRandom(),
20
- // The merchant's primary domain (e.g., "example.com")
21
- domain: varchar('domain', { length: 255 }).notNull().unique(),
22
- // Display name for the merchant
23
- displayName: varchar('display_name', { length: 255 }).notNull(),
24
- // Optional description
25
- description: text('description'),
26
- // Logo URL
27
- logoUrl: varchar('logo_url', { length: 512 }),
28
- // Website URL (full URL including protocol)
29
- websiteUrl: varchar('website_url', { length: 512 }),
30
- // Category (e-commerce, saas, marketplace, etc.)
31
- category: varchar('category', { length: 100 }),
32
- // Country code (ISO 3166-1 alpha-2)
33
- countryCode: varchar('country_code', { length: 2 }),
34
- // UCP readiness score (0-100)
35
- ucpScore: integer('ucp_score'),
36
- // UCP grade (A, B, C, D, F)
37
- ucpGrade: varchar('ucp_grade', { length: 2 }),
38
- // Supported transports (comma-separated: REST, MCP, A2A, Embedded)
39
- transports: varchar('transports', { length: 255 }),
40
- // Whether the merchant has opted into the directory
41
- isPublic: boolean('is_public').default(true).notNull(),
42
- // Whether the merchant has been verified
43
- isVerified: boolean('is_verified').default(false).notNull(),
44
- // Last validation timestamp
45
- lastValidatedAt: timestamp('last_validated_at'),
46
- // Record timestamps
47
- createdAt: timestamp('created_at').defaultNow().notNull(),
48
- updatedAt: timestamp('updated_at').defaultNow().notNull(),
49
- },
50
- (table) => [
51
- index('idx_merchants_domain').on(table.domain),
52
- index('idx_merchants_category').on(table.category),
53
- index('idx_merchants_country').on(table.countryCode),
54
- index('idx_merchants_score').on(table.ucpScore),
55
- index('idx_merchants_public').on(table.isPublic),
56
- ]
57
- );
58
-
59
- /**
60
- * Benchmark stats table - stores aggregate validation statistics
61
- * (migrated from setup script)
62
- */
63
- export const benchmarkStats = pgTable('benchmark_stats', {
64
- id: integer('id').primaryKey().generatedAlwaysAsIdentity(),
65
- scoreBucket: integer('score_bucket').notNull().unique(),
66
- count: integer('count').default(0).notNull(),
67
- });
68
-
69
- /**
70
- * Benchmark summary table - quick aggregate stats
71
- * (migrated from setup script)
72
- */
73
- export const benchmarkSummary = pgTable('benchmark_summary', {
74
- id: integer('id').primaryKey().default(1),
75
- totalValidations: integer('total_validations').default(0).notNull(),
76
- avgScore: decimal('avg_score', { precision: 5, scale: 2 }).default('0'),
77
- updatedAt: timestamp('updated_at').defaultNow().notNull(),
78
- });
79
-
80
- // Type exports for use in application code
81
- export type Merchant = typeof merchants.$inferSelect;
82
- export type NewMerchant = typeof merchants.$inferInsert;
83
- export type BenchmarkStat = typeof benchmarkStats.$inferSelect;
84
- export type BenchmarkSummary = typeof benchmarkSummary.$inferSelect;
@@ -1,34 +0,0 @@
1
- /**
2
- * Product Feed Quality Analyzer Module
3
- * Deep analysis of product data quality for AI agent visibility
4
- */
5
-
6
- export {
7
- analyzeProductFeed,
8
- analyzeProductFeedFromHtml,
9
- analyzeProduct,
10
- extractProductsFromHtml,
11
- validateGtin,
12
- } from './feed-analyzer.js';
13
-
14
- export type {
15
- ProductData,
16
- ProductOffer,
17
- ProductAnalysis,
18
- QualityCheck,
19
- FeedAnalysisResult,
20
- FeedAnalysisInput,
21
- CategoryScores,
22
- Recommendation,
23
- FeedSummary,
24
- GtinValidation,
25
- IssueSeverity,
26
- CheckCategory,
27
- } from './types.js';
28
-
29
- export {
30
- QUALITY_CHECKS,
31
- VALID_AVAILABILITY_VALUES,
32
- CATEGORY_WEIGHTS,
33
- GRADE_THRESHOLDS,
34
- } from './types.js';
@@ -1,354 +0,0 @@
1
- /**
2
- * Product Feed Quality Analyzer Types
3
- * Deep analysis of product data quality for AI agent visibility
4
- */
5
-
6
- /**
7
- * Severity levels for feed issues
8
- */
9
- export type IssueSeverity = 'critical' | 'warning' | 'info';
10
-
11
- /**
12
- * Categories of feed quality checks
13
- */
14
- export type CheckCategory =
15
- | 'completeness'
16
- | 'identifiers'
17
- | 'images'
18
- | 'pricing'
19
- | 'descriptions'
20
- | 'categories'
21
- | 'availability';
22
-
23
- /**
24
- * Individual product data from Schema.org JSON-LD
25
- */
26
- export interface ProductData {
27
- name?: string;
28
- description?: string;
29
- sku?: string;
30
- gtin?: string;
31
- gtin8?: string;
32
- gtin12?: string;
33
- gtin13?: string;
34
- gtin14?: string;
35
- mpn?: string;
36
- brand?: string | { name?: string };
37
- image?: string | string[] | { url?: string }[];
38
- offers?: ProductOffer | ProductOffer[];
39
- category?: string;
40
- color?: string;
41
- size?: string;
42
- material?: string;
43
- weight?: string | { value?: number; unitCode?: string };
44
- aggregateRating?: {
45
- ratingValue?: number;
46
- reviewCount?: number;
47
- ratingCount?: number;
48
- };
49
- review?: unknown[];
50
- url?: string;
51
- '@type'?: string;
52
- }
53
-
54
- /**
55
- * Product offer/pricing data
56
- */
57
- export interface ProductOffer {
58
- '@type'?: string;
59
- price?: number | string;
60
- priceCurrency?: string;
61
- availability?: string;
62
- url?: string;
63
- seller?: { name?: string };
64
- priceValidUntil?: string;
65
- itemCondition?: string;
66
- shippingDetails?: unknown;
67
- }
68
-
69
- /**
70
- * Individual quality check result
71
- */
72
- export interface QualityCheck {
73
- id: string;
74
- name: string;
75
- category: CheckCategory;
76
- passed: boolean;
77
- severity: IssueSeverity;
78
- message: string;
79
- details?: string;
80
- recommendation?: string;
81
- affectedProducts?: string[];
82
- }
83
-
84
- /**
85
- * Product-level analysis result
86
- */
87
- export interface ProductAnalysis {
88
- name: string;
89
- url?: string;
90
- sku?: string;
91
- score: number;
92
- issues: QualityCheck[];
93
- attributes: {
94
- hasName: boolean;
95
- hasDescription: boolean;
96
- hasSku: boolean;
97
- hasGtin: boolean;
98
- hasBrand: boolean;
99
- hasImage: boolean;
100
- hasPrice: boolean;
101
- hasAvailability: boolean;
102
- hasCategory: boolean;
103
- descriptionLength: number;
104
- imageCount: number;
105
- };
106
- }
107
-
108
- /**
109
- * Category scores breakdown
110
- */
111
- export interface CategoryScores {
112
- completeness: number;
113
- identifiers: number;
114
- images: number;
115
- pricing: number;
116
- descriptions: number;
117
- categories: number;
118
- availability: number;
119
- }
120
-
121
- /**
122
- * Overall feed analysis result
123
- */
124
- export interface FeedAnalysisResult {
125
- url: string;
126
- analyzedAt: string;
127
- productsFound: number;
128
- productsAnalyzed: number;
129
- overallScore: number;
130
- agentVisibilityScore: number;
131
- grade: 'A' | 'B' | 'C' | 'D' | 'F';
132
- categoryScores: CategoryScores;
133
- issues: QualityCheck[];
134
- topIssues: QualityCheck[];
135
- products: ProductAnalysis[];
136
- recommendations: Recommendation[];
137
- summary: FeedSummary;
138
- }
139
-
140
- /**
141
- * Recommendation for improving feed quality
142
- */
143
- export interface Recommendation {
144
- priority: 'high' | 'medium' | 'low';
145
- category: CheckCategory;
146
- title: string;
147
- description: string;
148
- impact: string;
149
- affectedCount: number;
150
- }
151
-
152
- /**
153
- * Summary statistics for the feed
154
- */
155
- export interface FeedSummary {
156
- totalProducts: number;
157
- withName: number;
158
- withDescription: number;
159
- withSku: number;
160
- withGtin: number;
161
- withBrand: number;
162
- withImages: number;
163
- withPrice: number;
164
- withAvailability: number;
165
- withCategory: number;
166
- averageDescriptionLength: number;
167
- averageImageCount: number;
168
- }
169
-
170
- /**
171
- * Input options for feed analysis
172
- */
173
- export interface FeedAnalysisInput {
174
- url: string;
175
- maxProducts?: number;
176
- includeProductDetails?: boolean;
177
- }
178
-
179
- /**
180
- * GTIN validation result
181
- */
182
- export interface GtinValidation {
183
- isValid: boolean;
184
- type?: 'GTIN-8' | 'GTIN-12' | 'GTIN-13' | 'GTIN-14' | 'UPC' | 'EAN';
185
- error?: string;
186
- }
187
-
188
- /**
189
- * Quality check definitions
190
- */
191
- export const QUALITY_CHECKS: Record<string, {
192
- name: string;
193
- category: CheckCategory;
194
- severity: IssueSeverity;
195
- description: string;
196
- }> = {
197
- // Completeness checks
198
- 'missing-name': {
199
- name: 'Missing Product Name',
200
- category: 'completeness',
201
- severity: 'critical',
202
- description: 'Product name is required for AI agents to identify products',
203
- },
204
- 'missing-description': {
205
- name: 'Missing Description',
206
- category: 'completeness',
207
- severity: 'warning',
208
- description: 'Product description helps AI agents understand and recommend products',
209
- },
210
- 'short-description': {
211
- name: 'Short Description',
212
- category: 'descriptions',
213
- severity: 'info',
214
- description: 'Longer descriptions provide better context for AI agents',
215
- },
216
- 'missing-brand': {
217
- name: 'Missing Brand',
218
- category: 'completeness',
219
- severity: 'warning',
220
- description: 'Brand information helps with product identification and search',
221
- },
222
-
223
- // Identifier checks
224
- 'missing-sku': {
225
- name: 'Missing SKU',
226
- category: 'identifiers',
227
- severity: 'warning',
228
- description: 'SKU helps uniquely identify products in your catalog',
229
- },
230
- 'missing-gtin': {
231
- name: 'Missing GTIN/UPC/EAN',
232
- category: 'identifiers',
233
- severity: 'warning',
234
- description: 'Global identifiers enable cross-platform product matching',
235
- },
236
- 'invalid-gtin': {
237
- name: 'Invalid GTIN Format',
238
- category: 'identifiers',
239
- severity: 'critical',
240
- description: 'GTIN has invalid format or check digit',
241
- },
242
-
243
- // Image checks
244
- 'missing-image': {
245
- name: 'Missing Product Image',
246
- category: 'images',
247
- severity: 'critical',
248
- description: 'Product images are essential for AI shopping experiences',
249
- },
250
- 'single-image': {
251
- name: 'Single Image Only',
252
- category: 'images',
253
- severity: 'info',
254
- description: 'Multiple images improve product presentation',
255
- },
256
- 'missing-image-alt': {
257
- name: 'Missing Image Alt Text',
258
- category: 'images',
259
- severity: 'info',
260
- description: 'Alt text helps with accessibility and SEO',
261
- },
262
-
263
- // Pricing checks
264
- 'missing-price': {
265
- name: 'Missing Price',
266
- category: 'pricing',
267
- severity: 'critical',
268
- description: 'Price is required for AI agents to complete purchases',
269
- },
270
- 'missing-currency': {
271
- name: 'Missing Currency',
272
- category: 'pricing',
273
- severity: 'warning',
274
- description: 'Currency must be specified for international commerce',
275
- },
276
- 'invalid-price': {
277
- name: 'Invalid Price Format',
278
- category: 'pricing',
279
- severity: 'critical',
280
- description: 'Price must be a valid number',
281
- },
282
-
283
- // Availability checks
284
- 'missing-availability': {
285
- name: 'Missing Availability',
286
- category: 'availability',
287
- severity: 'warning',
288
- description: 'Availability status helps AI agents make purchase decisions',
289
- },
290
- 'invalid-availability': {
291
- name: 'Invalid Availability Value',
292
- category: 'availability',
293
- severity: 'warning',
294
- description: 'Availability should use Schema.org ItemAvailability values',
295
- },
296
-
297
- // Category checks
298
- 'missing-category': {
299
- name: 'Missing Category',
300
- category: 'categories',
301
- severity: 'info',
302
- description: 'Product categorization improves discoverability',
303
- },
304
- };
305
-
306
- /**
307
- * Valid Schema.org availability values
308
- */
309
- export const VALID_AVAILABILITY_VALUES = [
310
- 'https://schema.org/InStock',
311
- 'https://schema.org/OutOfStock',
312
- 'https://schema.org/PreOrder',
313
- 'https://schema.org/PreSale',
314
- 'https://schema.org/SoldOut',
315
- 'https://schema.org/InStoreOnly',
316
- 'https://schema.org/OnlineOnly',
317
- 'https://schema.org/LimitedAvailability',
318
- 'https://schema.org/Discontinued',
319
- 'https://schema.org/BackOrder',
320
- 'InStock',
321
- 'OutOfStock',
322
- 'PreOrder',
323
- 'PreSale',
324
- 'SoldOut',
325
- 'InStoreOnly',
326
- 'OnlineOnly',
327
- 'LimitedAvailability',
328
- 'Discontinued',
329
- 'BackOrder',
330
- ];
331
-
332
- /**
333
- * Scoring weights for different categories
334
- */
335
- export const CATEGORY_WEIGHTS: Record<CheckCategory, number> = {
336
- completeness: 25,
337
- identifiers: 15,
338
- images: 20,
339
- pricing: 20,
340
- descriptions: 10,
341
- categories: 5,
342
- availability: 5,
343
- };
344
-
345
- /**
346
- * Grade thresholds (aligned with all other scoring models)
347
- */
348
- export const GRADE_THRESHOLDS = {
349
- A: 90,
350
- B: 80,
351
- C: 70,
352
- D: 60,
353
- F: 0,
354
- };
@@ -1,124 +0,0 @@
1
- /**
2
- * Signing Key Generator for UCP Webhook Signing
3
- * Generates EC or RSA key pairs in JWK format
4
- */
5
-
6
- import * as jose from 'jose';
7
- import { nanoid } from 'nanoid';
8
- import type { JwkKey } from '../types/ucp-profile.js';
9
-
10
- export type KeyAlgorithm = 'ES256' | 'RS256';
11
-
12
- export interface KeyPairResult {
13
- publicKey: JwkKey;
14
- privateKey: string; // PEM format for merchant storage
15
- }
16
-
17
- /**
18
- * Generate a new signing key pair
19
- */
20
- export async function generateSigningKeyPair(
21
- algorithm: KeyAlgorithm = 'ES256'
22
- ): Promise<KeyPairResult> {
23
- const keyId = `ucp-${nanoid(12)}`;
24
-
25
- if (algorithm === 'ES256') {
26
- return generateECKeyPair(keyId);
27
- } else {
28
- return generateRSAKeyPair(keyId);
29
- }
30
- }
31
-
32
- /**
33
- * Generate EC (P-256) key pair
34
- */
35
- async function generateECKeyPair(keyId: string): Promise<KeyPairResult> {
36
- const { publicKey, privateKey } = await jose.generateKeyPair('ES256', {
37
- extractable: true,
38
- });
39
-
40
- // Export public key as JWK
41
- const publicJwk = await jose.exportJWK(publicKey);
42
-
43
- // Export private key as PEM
44
- const privatePem = await jose.exportPKCS8(privateKey);
45
-
46
- return {
47
- publicKey: {
48
- kty: 'EC',
49
- kid: keyId,
50
- use: 'sig',
51
- alg: 'ES256',
52
- crv: publicJwk.crv as string,
53
- x: publicJwk.x as string,
54
- y: publicJwk.y as string,
55
- },
56
- privateKey: privatePem,
57
- };
58
- }
59
-
60
- /**
61
- * Generate RSA (2048-bit) key pair
62
- */
63
- async function generateRSAKeyPair(keyId: string): Promise<KeyPairResult> {
64
- const { publicKey, privateKey } = await jose.generateKeyPair('RS256', {
65
- extractable: true,
66
- modulusLength: 2048,
67
- });
68
-
69
- // Export public key as JWK
70
- const publicJwk = await jose.exportJWK(publicKey);
71
-
72
- // Export private key as PEM
73
- const privatePem = await jose.exportPKCS8(privateKey);
74
-
75
- return {
76
- publicKey: {
77
- kty: 'RSA',
78
- kid: keyId,
79
- use: 'sig',
80
- alg: 'RS256',
81
- n: publicJwk.n as string,
82
- e: publicJwk.e as string,
83
- },
84
- privateKey: privatePem,
85
- };
86
- }
87
-
88
- /**
89
- * Validate a JWK public key structure
90
- */
91
- export function validatePublicKey(key: JwkKey): string[] {
92
- const errors: string[] = [];
93
-
94
- if (!key.kty) {
95
- errors.push('Missing required field: kty');
96
- }
97
-
98
- if (!key.kid) {
99
- errors.push('Missing required field: kid');
100
- }
101
-
102
- if (key.kty === 'EC') {
103
- if (!key.crv) errors.push('EC key missing curve (crv)');
104
- if (!key.x) errors.push('EC key missing x coordinate');
105
- if (!key.y) errors.push('EC key missing y coordinate');
106
- if (key.crv && !['P-256', 'P-384', 'P-521'].includes(key.crv)) {
107
- errors.push(`Unsupported EC curve: ${key.crv}`);
108
- }
109
- } else if (key.kty === 'RSA') {
110
- if (!key.n) errors.push('RSA key missing modulus (n)');
111
- if (!key.e) errors.push('RSA key missing exponent (e)');
112
- } else if (key.kty) {
113
- errors.push(`Unsupported key type: ${key.kty}`);
114
- }
115
-
116
- return errors;
117
- }
118
-
119
- /**
120
- * Import and validate a public key from JWK
121
- */
122
- export async function importPublicKey(jwk: JwkKey): Promise<jose.KeyLike | Uint8Array> {
123
- return jose.importJWK(jwk as jose.JWK, jwk.alg);
124
- }