@ucptools/validator 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CLAUDE.md +109 -0
  2. package/CONTRIBUTING.md +113 -0
  3. package/LICENSE +21 -0
  4. package/README.md +203 -0
  5. package/api/analyze-feed.js +140 -0
  6. package/api/badge.js +185 -0
  7. package/api/benchmark.js +177 -0
  8. package/api/directory-stats.ts +29 -0
  9. package/api/directory.ts +73 -0
  10. package/api/generate-compliance.js +143 -0
  11. package/api/generate-schema.js +457 -0
  12. package/api/generate.js +132 -0
  13. package/api/security-scan.js +133 -0
  14. package/api/simulate.js +187 -0
  15. package/api/tsconfig.json +10 -0
  16. package/api/validate.js +1351 -0
  17. package/apify-actor/.actor/actor.json +68 -0
  18. package/apify-actor/.actor/input_schema.json +32 -0
  19. package/apify-actor/APIFY-STORE-LISTING.md +412 -0
  20. package/apify-actor/Dockerfile +8 -0
  21. package/apify-actor/README.md +166 -0
  22. package/apify-actor/main.ts +111 -0
  23. package/apify-actor/package.json +17 -0
  24. package/apify-actor/src/main.js +199 -0
  25. package/docs/BRAND-IDENTITY.md +238 -0
  26. package/docs/BRAND-STYLE-GUIDE.md +356 -0
  27. package/drizzle/0000_black_king_cobra.sql +39 -0
  28. package/drizzle/meta/0000_snapshot.json +309 -0
  29. package/drizzle/meta/_journal.json +13 -0
  30. package/drizzle.config.ts +10 -0
  31. package/examples/full-profile.json +70 -0
  32. package/examples/minimal-profile.json +23 -0
  33. package/package.json +69 -0
  34. package/public/.well-known/ucp +25 -0
  35. package/public/android-chrome-192x192.png +0 -0
  36. package/public/android-chrome-512x512.png +0 -0
  37. package/public/apple-touch-icon.png +0 -0
  38. package/public/brand.css +321 -0
  39. package/public/directory.html +701 -0
  40. package/public/favicon-16x16.png +0 -0
  41. package/public/favicon-32x32.png +0 -0
  42. package/public/favicon.ico +0 -0
  43. package/public/guides/bigcommerce.html +743 -0
  44. package/public/guides/fastucp.html +838 -0
  45. package/public/guides/magento.html +779 -0
  46. package/public/guides/shopify.html +726 -0
  47. package/public/guides/squarespace.html +749 -0
  48. package/public/guides/wix.html +747 -0
  49. package/public/guides/woocommerce.html +733 -0
  50. package/public/index.html +3835 -0
  51. package/public/learn.html +396 -0
  52. package/public/logo.jpeg +0 -0
  53. package/public/og-image-icon.png +0 -0
  54. package/public/og-image.png +0 -0
  55. package/public/robots.txt +6 -0
  56. package/public/site.webmanifest +31 -0
  57. package/public/sitemap.xml +69 -0
  58. package/public/social/linkedin-banner-1128x191.png +0 -0
  59. package/public/social/temp.PNG +0 -0
  60. package/public/social/x-header-1500x500.png +0 -0
  61. package/public/verify.html +410 -0
  62. package/scripts/generate-favicons.js +44 -0
  63. package/scripts/generate-ico.js +23 -0
  64. package/scripts/generate-og-image.js +45 -0
  65. package/scripts/reset-db.ts +77 -0
  66. package/scripts/seed-db.ts +71 -0
  67. package/scripts/setup-benchmark-db.js +70 -0
  68. package/src/api/server.ts +266 -0
  69. package/src/cli/index.ts +302 -0
  70. package/src/compliance/compliance-generator.ts +452 -0
  71. package/src/compliance/index.ts +28 -0
  72. package/src/compliance/templates.ts +338 -0
  73. package/src/compliance/types.ts +170 -0
  74. package/src/db/index.ts +28 -0
  75. package/src/db/schema.ts +84 -0
  76. package/src/feed-analyzer/feed-analyzer.ts +726 -0
  77. package/src/feed-analyzer/index.ts +34 -0
  78. package/src/feed-analyzer/types.ts +354 -0
  79. package/src/generator/index.ts +7 -0
  80. package/src/generator/key-generator.ts +124 -0
  81. package/src/generator/profile-builder.ts +402 -0
  82. package/src/hosting/artifacts-generator.ts +679 -0
  83. package/src/hosting/index.ts +6 -0
  84. package/src/index.ts +105 -0
  85. package/src/security/index.ts +15 -0
  86. package/src/security/security-scanner.ts +604 -0
  87. package/src/security/types.ts +55 -0
  88. package/src/services/directory.ts +434 -0
  89. package/src/simulator/agent-simulator.ts +941 -0
  90. package/src/simulator/index.ts +7 -0
  91. package/src/simulator/types.ts +170 -0
  92. package/src/types/generator.ts +140 -0
  93. package/src/types/index.ts +7 -0
  94. package/src/types/ucp-profile.ts +140 -0
  95. package/src/types/validation.ts +89 -0
  96. package/src/validator/index.ts +194 -0
  97. package/src/validator/network-validator.ts +417 -0
  98. package/src/validator/rules-validator.ts +297 -0
  99. package/src/validator/sdk-validator.ts +330 -0
  100. package/src/validator/structural-validator.ts +476 -0
  101. package/tests/fixtures/non-compliant-profile.json +25 -0
  102. package/tests/fixtures/official-sample-profile.json +75 -0
  103. package/tests/integration/benchmark.test.ts +207 -0
  104. package/tests/integration/database.test.ts +163 -0
  105. package/tests/integration/directory-api.test.ts +268 -0
  106. package/tests/integration/simulate-api.test.ts +230 -0
  107. package/tests/integration/validate-api.test.ts +269 -0
  108. package/tests/setup.ts +15 -0
  109. package/tests/unit/agent-simulator.test.ts +575 -0
  110. package/tests/unit/compliance-generator.test.ts +374 -0
  111. package/tests/unit/directory-service.test.ts +272 -0
  112. package/tests/unit/feed-analyzer.test.ts +517 -0
  113. package/tests/unit/lint-suggestions.test.ts +423 -0
  114. package/tests/unit/official-samples.test.ts +211 -0
  115. package/tests/unit/pdf-report.test.ts +390 -0
  116. package/tests/unit/sdk-validator.test.ts +531 -0
  117. package/tests/unit/security-scanner.test.ts +410 -0
  118. package/tests/unit/validation.test.ts +390 -0
  119. package/tsconfig.json +20 -0
  120. package/vercel.json +34 -0
  121. package/vitest.config.ts +22 -0
@@ -0,0 +1,452 @@
1
+ /**
2
+ * GDPR/Privacy Compliance Generator
3
+ * Generates privacy policy addendums and consent language for agentic commerce
4
+ */
5
+
6
+ import type {
7
+ ComplianceGeneratorInput,
8
+ ComplianceGeneratorOutput,
9
+ ComplianceDocument,
10
+ ComplianceSection,
11
+ DataProcessor,
12
+ ComplianceRegion,
13
+ } from './types.js';
14
+ import {
15
+ AI_PLATFORM_PROCESSORS,
16
+ REGION_NAMES,
17
+ LAWFUL_BASIS_DESCRIPTIONS,
18
+ } from './types.js';
19
+ import {
20
+ generateAiCommerceSection,
21
+ generateProcessorDisclosures,
22
+ generateConsentLanguage,
23
+ generateMarketingOptIn,
24
+ generateDataSubjectRights,
25
+ generateTrackingNotice,
26
+ generateDisclaimer,
27
+ } from './templates.js';
28
+
29
+ /**
30
+ * Generate complete compliance documentation
31
+ */
32
+ export function generateComplianceDocuments(
33
+ input: ComplianceGeneratorInput
34
+ ): ComplianceGeneratorOutput {
35
+ // Validate input
36
+ validateInput(input);
37
+
38
+ // Build list of data processors
39
+ const processors = buildProcessorList(input);
40
+
41
+ // Generate all sections
42
+ const aiCommerceSection = generateAiCommerceSection(
43
+ input.companyName,
44
+ processors,
45
+ input.lawfulBasis,
46
+ input.regions
47
+ );
48
+
49
+ const processorDisclosures = generateProcessorDisclosures(
50
+ processors,
51
+ input.regions
52
+ );
53
+
54
+ const consentLanguage = generateConsentLanguage(
55
+ input.companyName,
56
+ input.lawfulBasis,
57
+ input.includeMarketingConsent || false
58
+ );
59
+
60
+ const marketingOptIn = input.includeMarketingConsent
61
+ ? generateMarketingOptIn(input.companyName)
62
+ : undefined;
63
+
64
+ const dataSubjectRights = generateDataSubjectRights(
65
+ input.companyName,
66
+ input.companyEmail || 'privacy@yourcompany.com',
67
+ input.dpoEmail,
68
+ input.regions
69
+ );
70
+
71
+ const trackingNotice = generateTrackingNotice(
72
+ input.companyName,
73
+ input.regions
74
+ );
75
+
76
+ const disclaimer = generateDisclaimer();
77
+
78
+ // Build the full privacy addendum document
79
+ const privacyAddendum = buildPrivacyAddendum(
80
+ input,
81
+ aiCommerceSection,
82
+ processorDisclosures,
83
+ dataSubjectRights,
84
+ trackingNotice,
85
+ disclaimer
86
+ );
87
+
88
+ // Generate embed HTML
89
+ const embedHtml = generateEmbedHtml(
90
+ input.companyName,
91
+ aiCommerceSection,
92
+ processorDisclosures,
93
+ dataSubjectRights,
94
+ trackingNotice,
95
+ disclaimer
96
+ );
97
+
98
+ // Generate plain text
99
+ const plainText = generatePlainText(
100
+ input.companyName,
101
+ aiCommerceSection,
102
+ processorDisclosures,
103
+ consentLanguage,
104
+ marketingOptIn,
105
+ dataSubjectRights,
106
+ trackingNotice,
107
+ disclaimer
108
+ );
109
+
110
+ return {
111
+ privacyAddendum,
112
+ snippets: {
113
+ aiCommerceSection,
114
+ processorDisclosures,
115
+ consentLanguage,
116
+ marketingOptIn,
117
+ dataSubjectRights,
118
+ trackingNotice,
119
+ },
120
+ embedHtml,
121
+ plainText,
122
+ generatedAt: new Date().toISOString(),
123
+ lawfulBasis: input.lawfulBasis,
124
+ regions: input.regions,
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Validate generator input
130
+ */
131
+ function validateInput(input: ComplianceGeneratorInput): void {
132
+ if (!input.companyName || input.companyName.trim() === '') {
133
+ throw new Error('Company name is required');
134
+ }
135
+
136
+ if (!input.regions || input.regions.length === 0) {
137
+ throw new Error('At least one region must be selected');
138
+ }
139
+
140
+ if (!input.platforms || input.platforms.length === 0) {
141
+ throw new Error('At least one AI platform must be selected');
142
+ }
143
+
144
+ if (!input.lawfulBasis) {
145
+ throw new Error('Lawful basis must be specified');
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Build the list of data processors from input
151
+ */
152
+ function buildProcessorList(input: ComplianceGeneratorInput): DataProcessor[] {
153
+ const processors: DataProcessor[] = [];
154
+
155
+ // Add AI platform processors
156
+ for (const platform of input.platforms) {
157
+ if (platform !== 'other') {
158
+ processors.push(AI_PLATFORM_PROCESSORS[platform]);
159
+ }
160
+ }
161
+
162
+ // Add custom processors
163
+ if (input.additionalProcessors) {
164
+ processors.push(...input.additionalProcessors);
165
+ }
166
+
167
+ return processors;
168
+ }
169
+
170
+ /**
171
+ * Build the full privacy addendum document
172
+ */
173
+ function buildPrivacyAddendum(
174
+ input: ComplianceGeneratorInput,
175
+ aiCommerceSection: string,
176
+ processorDisclosures: string,
177
+ dataSubjectRights: string,
178
+ trackingNotice: string,
179
+ disclaimer: string
180
+ ): ComplianceDocument {
181
+ const sections: ComplianceSection[] = [
182
+ {
183
+ id: 'ai-commerce',
184
+ title: 'AI-Powered Shopping and Agentic Commerce',
185
+ content: aiCommerceSection,
186
+ required: true,
187
+ applicableRegions: ['eu', 'uk', 'california', 'global'],
188
+ },
189
+ {
190
+ id: 'processors',
191
+ title: 'Third-Party Data Processors',
192
+ content: processorDisclosures,
193
+ required: true,
194
+ applicableRegions: ['eu', 'uk', 'california', 'global'],
195
+ },
196
+ {
197
+ id: 'rights',
198
+ title: 'Your Data Rights',
199
+ content: dataSubjectRights,
200
+ required: true,
201
+ applicableRegions: ['eu', 'uk', 'california', 'global'],
202
+ },
203
+ {
204
+ id: 'tracking',
205
+ title: 'Tracking and Cookies',
206
+ content: trackingNotice,
207
+ required: false,
208
+ applicableRegions: ['eu', 'uk', 'global'],
209
+ },
210
+ ];
211
+
212
+ // Add data retention section if requested
213
+ if (input.includeDataRetention) {
214
+ const retentionYears = input.retentionPeriodYears || 7;
215
+ sections.push({
216
+ id: 'retention',
217
+ title: 'Data Retention',
218
+ content: generateDataRetentionSection(input.companyName, retentionYears, input.regions),
219
+ required: false,
220
+ applicableRegions: ['eu', 'uk', 'california', 'global'],
221
+ });
222
+ }
223
+
224
+ return {
225
+ title: `Privacy Policy Addendum: AI Commerce - ${input.companyName}`,
226
+ sections,
227
+ disclaimer,
228
+ generatedAt: new Date().toISOString(),
229
+ regions: input.regions,
230
+ };
231
+ }
232
+
233
+ /**
234
+ * Generate data retention section
235
+ */
236
+ function generateDataRetentionSection(
237
+ companyName: string,
238
+ retentionYears: number,
239
+ regions: ComplianceRegion[]
240
+ ): string {
241
+ let section = `## Data Retention for AI Commerce Transactions
242
+
243
+ ${companyName} retains personal data from AI agent transactions for the following periods:
244
+
245
+ | Data Category | Retention Period | Purpose |
246
+ |--------------|------------------|---------|
247
+ | Order Information | ${retentionYears} years | Legal/tax compliance, warranty claims |
248
+ | Transaction Records | ${retentionYears} years | Financial records, dispute resolution |
249
+ | Delivery Information | 2 years | Customer service, delivery issues |
250
+ | Communication Records | 3 years | Customer support, quality assurance |
251
+ | Payment Confirmations | ${retentionYears} years | Financial audit requirements |
252
+
253
+ ### Retention Principles
254
+
255
+ - Data is retained only as long as necessary for the stated purposes
256
+ - We regularly review and delete data that is no longer needed
257
+ - You may request earlier deletion, subject to legal retention requirements
258
+ `;
259
+
260
+ if (regions.includes('eu') || regions.includes('uk')) {
261
+ section += `
262
+ ### GDPR Storage Limitation
263
+
264
+ In accordance with Article 5(1)(e) of the GDPR, we do not keep personal data for longer than necessary. Retention periods are based on:
265
+ - Contractual obligations
266
+ - Legal requirements (tax, accounting)
267
+ - Legitimate business needs
268
+ - Applicable limitation periods for legal claims
269
+ `;
270
+ }
271
+
272
+ return section;
273
+ }
274
+
275
+ /**
276
+ * Generate embeddable HTML
277
+ */
278
+ function generateEmbedHtml(
279
+ companyName: string,
280
+ aiCommerceSection: string,
281
+ processorDisclosures: string,
282
+ dataSubjectRights: string,
283
+ trackingNotice: string,
284
+ disclaimer: string
285
+ ): string {
286
+ // Convert markdown to simple HTML
287
+ const convertMarkdown = (md: string): string => {
288
+ return md
289
+ // Headers
290
+ .replace(/^### (.+)$/gm, '<h4>$1</h4>')
291
+ .replace(/^## (.+)$/gm, '<h3>$1</h3>')
292
+ .replace(/^# (.+)$/gm, '<h2>$1</h2>')
293
+ // Bold
294
+ .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
295
+ // Lists
296
+ .replace(/^- (.+)$/gm, '<li>$1</li>')
297
+ // Blockquotes
298
+ .replace(/^> (.+)$/gm, '<blockquote>$1</blockquote>')
299
+ // Paragraphs
300
+ .replace(/\n\n/g, '</p><p>')
301
+ // Tables (basic)
302
+ .replace(/\|(.+)\|/g, (match) => {
303
+ const cells = match.split('|').filter(c => c.trim());
304
+ return '<tr>' + cells.map(c => `<td>${c.trim()}</td>`).join('') + '</tr>';
305
+ })
306
+ // Links
307
+ .replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
308
+ };
309
+
310
+ return `<!-- AI Commerce Privacy Policy Addendum - Generated by UCP.tools -->
311
+ <div class="ucp-privacy-addendum">
312
+ <style>
313
+ .ucp-privacy-addendum {
314
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
315
+ line-height: 1.6;
316
+ color: #333;
317
+ max-width: 800px;
318
+ }
319
+ .ucp-privacy-addendum h2 { font-size: 1.5em; margin-top: 2em; border-bottom: 2px solid #2E86AB; padding-bottom: 0.5em; }
320
+ .ucp-privacy-addendum h3 { font-size: 1.2em; margin-top: 1.5em; color: #2E86AB; }
321
+ .ucp-privacy-addendum h4 { font-size: 1em; margin-top: 1em; }
322
+ .ucp-privacy-addendum table { width: 100%; border-collapse: collapse; margin: 1em 0; }
323
+ .ucp-privacy-addendum th, .ucp-privacy-addendum td { border: 1px solid #ddd; padding: 8px; text-align: left; }
324
+ .ucp-privacy-addendum th { background: #f5f5f5; }
325
+ .ucp-privacy-addendum blockquote { border-left: 4px solid #2E86AB; margin: 1em 0; padding: 0.5em 1em; background: #f9f9f9; }
326
+ .ucp-privacy-addendum .disclaimer { margin-top: 2em; padding: 1em; background: #fff3cd; border: 1px solid #ffc107; border-radius: 4px; font-size: 0.9em; }
327
+ </style>
328
+
329
+ <h2>Privacy Policy Addendum: AI Commerce</h2>
330
+ <p><em>Effective Date: ${new Date().toLocaleDateString()}</em></p>
331
+ <p>This addendum describes how ${companyName} handles personal data when you make purchases through AI shopping agents.</p>
332
+
333
+ <div class="section">
334
+ ${convertMarkdown(aiCommerceSection)}
335
+ </div>
336
+
337
+ <div class="section">
338
+ ${convertMarkdown(processorDisclosures)}
339
+ </div>
340
+
341
+ <div class="section">
342
+ ${convertMarkdown(dataSubjectRights)}
343
+ </div>
344
+
345
+ <div class="section">
346
+ ${convertMarkdown(trackingNotice)}
347
+ </div>
348
+
349
+ <div class="disclaimer">
350
+ ${convertMarkdown(disclaimer)}
351
+ </div>
352
+ </div>
353
+ <!-- End AI Commerce Privacy Policy Addendum -->`;
354
+ }
355
+
356
+ /**
357
+ * Generate plain text version
358
+ */
359
+ function generatePlainText(
360
+ companyName: string,
361
+ aiCommerceSection: string,
362
+ processorDisclosures: string,
363
+ consentLanguage: string,
364
+ marketingOptIn: string | undefined,
365
+ dataSubjectRights: string,
366
+ trackingNotice: string,
367
+ disclaimer: string
368
+ ): string {
369
+ const divider = '='.repeat(60);
370
+
371
+ let text = `${divider}
372
+ PRIVACY POLICY ADDENDUM: AI COMMERCE
373
+ ${companyName}
374
+ Generated: ${new Date().toISOString()}
375
+ ${divider}
376
+
377
+ ${aiCommerceSection}
378
+
379
+ ${divider}
380
+
381
+ ${processorDisclosures}
382
+
383
+ ${divider}
384
+
385
+ ${consentLanguage}
386
+ `;
387
+
388
+ if (marketingOptIn) {
389
+ text += `
390
+ ${divider}
391
+
392
+ ${marketingOptIn}
393
+ `;
394
+ }
395
+
396
+ text += `
397
+ ${divider}
398
+
399
+ ${dataSubjectRights}
400
+
401
+ ${divider}
402
+
403
+ ${trackingNotice}
404
+
405
+ ${divider}
406
+
407
+ ${disclaimer}
408
+ `;
409
+
410
+ return text;
411
+ }
412
+
413
+ /**
414
+ * Get available regions with descriptions
415
+ */
416
+ export function getAvailableRegions(): Array<{ id: ComplianceRegion; name: string }> {
417
+ return Object.entries(REGION_NAMES).map(([id, name]) => ({
418
+ id: id as ComplianceRegion,
419
+ name,
420
+ }));
421
+ }
422
+
423
+ /**
424
+ * Get lawful basis options with descriptions
425
+ */
426
+ export function getLawfulBasisOptions(): Array<{
427
+ id: string;
428
+ title: string;
429
+ description: string;
430
+ gdprArticle: string;
431
+ }> {
432
+ return Object.entries(LAWFUL_BASIS_DESCRIPTIONS).map(([id, info]) => ({
433
+ id,
434
+ ...info,
435
+ }));
436
+ }
437
+
438
+ /**
439
+ * Get AI platform options
440
+ */
441
+ export function getAiPlatformOptions(): Array<{
442
+ id: string;
443
+ name: string;
444
+ description: string;
445
+ }> {
446
+ return [
447
+ { id: 'openai', name: 'OpenAI (ChatGPT Shopping)', description: 'ChatGPT-powered shopping assistant' },
448
+ { id: 'google', name: 'Google (AI Mode / Gemini)', description: 'Google AI shopping agent' },
449
+ { id: 'microsoft', name: 'Microsoft (Copilot)', description: 'Microsoft Copilot checkout' },
450
+ { id: 'other', name: 'Other AI Platforms', description: 'Other AI shopping agents' },
451
+ ];
452
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * GDPR/Privacy Compliance Generator Module
3
+ * Generates privacy policy addendums and consent language for agentic commerce
4
+ */
5
+
6
+ export {
7
+ generateComplianceDocuments,
8
+ getAvailableRegions,
9
+ getLawfulBasisOptions,
10
+ getAiPlatformOptions,
11
+ } from './compliance-generator.js';
12
+
13
+ export type {
14
+ ComplianceRegion,
15
+ LawfulBasis,
16
+ AgentPlatform,
17
+ ComplianceGeneratorInput,
18
+ ComplianceGeneratorOutput,
19
+ ComplianceDocument,
20
+ ComplianceSection,
21
+ DataProcessor,
22
+ } from './types.js';
23
+
24
+ export {
25
+ AI_PLATFORM_PROCESSORS,
26
+ REGION_NAMES,
27
+ LAWFUL_BASIS_DESCRIPTIONS,
28
+ } from './types.js';