@uniforge/testing 0.1.0-alpha.2
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.
- package/dist/auth/index.d.cts +177 -0
- package/dist/auth/index.d.ts +177 -0
- package/dist/auth/index.js +459 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/index.mjs +418 -0
- package/dist/auth/index.mjs.map +1 -0
- package/dist/billing/index.d.cts +27 -0
- package/dist/billing/index.d.ts +27 -0
- package/dist/billing/index.js +208 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/billing/index.mjs +178 -0
- package/dist/billing/index.mjs.map +1 -0
- package/dist/database/index.d.cts +399 -0
- package/dist/database/index.d.ts +399 -0
- package/dist/database/index.js +19054 -0
- package/dist/database/index.js.map +1 -0
- package/dist/database/index.mjs +19046 -0
- package/dist/database/index.mjs.map +1 -0
- package/dist/graphql/index.d.cts +23 -0
- package/dist/graphql/index.d.ts +23 -0
- package/dist/graphql/index.js +18511 -0
- package/dist/graphql/index.js.map +1 -0
- package/dist/graphql/index.mjs +18505 -0
- package/dist/graphql/index.mjs.map +1 -0
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +6 -0
- package/dist/index.mjs.map +1 -0
- package/dist/multi-store/index.d.cts +66 -0
- package/dist/multi-store/index.d.ts +66 -0
- package/dist/multi-store/index.js +319 -0
- package/dist/multi-store/index.js.map +1 -0
- package/dist/multi-store/index.mjs +287 -0
- package/dist/multi-store/index.mjs.map +1 -0
- package/dist/multi-tenant/index.d.cts +15 -0
- package/dist/multi-tenant/index.d.ts +15 -0
- package/dist/multi-tenant/index.js +87 -0
- package/dist/multi-tenant/index.js.map +1 -0
- package/dist/multi-tenant/index.mjs +57 -0
- package/dist/multi-tenant/index.mjs.map +1 -0
- package/dist/performance/index.d.cts +60 -0
- package/dist/performance/index.d.ts +60 -0
- package/dist/performance/index.js +280 -0
- package/dist/performance/index.js.map +1 -0
- package/dist/performance/index.mjs +246 -0
- package/dist/performance/index.mjs.map +1 -0
- package/dist/platform/index.d.cts +71 -0
- package/dist/platform/index.d.ts +71 -0
- package/dist/platform/index.js +435 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/index.mjs +396 -0
- package/dist/platform/index.mjs.map +1 -0
- package/dist/rbac/index.d.cts +21 -0
- package/dist/rbac/index.d.ts +21 -0
- package/dist/rbac/index.js +178 -0
- package/dist/rbac/index.js.map +1 -0
- package/dist/rbac/index.mjs +150 -0
- package/dist/rbac/index.mjs.map +1 -0
- package/dist/security/index.d.cts +73 -0
- package/dist/security/index.d.ts +73 -0
- package/dist/security/index.js +246 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/index.mjs +211 -0
- package/dist/security/index.mjs.map +1 -0
- package/dist/shopify-api/index.d.cts +139 -0
- package/dist/shopify-api/index.d.ts +139 -0
- package/dist/shopify-api/index.js +469 -0
- package/dist/shopify-api/index.js.map +1 -0
- package/dist/shopify-api/index.mjs +439 -0
- package/dist/shopify-api/index.mjs.map +1 -0
- package/dist/shopify-compliance/index.d.cts +85 -0
- package/dist/shopify-compliance/index.d.ts +85 -0
- package/dist/shopify-compliance/index.js +287 -0
- package/dist/shopify-compliance/index.js.map +1 -0
- package/dist/shopify-compliance/index.mjs +259 -0
- package/dist/shopify-compliance/index.mjs.map +1 -0
- package/dist/webhooks/index.d.cts +127 -0
- package/dist/webhooks/index.d.ts +127 -0
- package/dist/webhooks/index.js +18934 -0
- package/dist/webhooks/index.js.map +1 -0
- package/dist/webhooks/index.mjs +18916 -0
- package/dist/webhooks/index.mjs.map +1 -0
- package/package.json +112 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/shopify-compliance/index.ts
|
|
21
|
+
var shopify_compliance_exports = {};
|
|
22
|
+
__export(shopify_compliance_exports, {
|
|
23
|
+
BUILT_FOR_SHOPIFY_CHECKS: () => BUILT_FOR_SHOPIFY_CHECKS,
|
|
24
|
+
ComplianceChecker: () => ComplianceChecker
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(shopify_compliance_exports);
|
|
27
|
+
|
|
28
|
+
// src/shopify-compliance/compliance-checks.ts
|
|
29
|
+
function createCheck(id, name, category, severity, configKey, passMessage, failMessage) {
|
|
30
|
+
return {
|
|
31
|
+
id,
|
|
32
|
+
name,
|
|
33
|
+
category,
|
|
34
|
+
severity,
|
|
35
|
+
check(config) {
|
|
36
|
+
const passed = config[configKey] === true;
|
|
37
|
+
return {
|
|
38
|
+
id,
|
|
39
|
+
name,
|
|
40
|
+
category,
|
|
41
|
+
severity,
|
|
42
|
+
passed,
|
|
43
|
+
message: passed ? passMessage : failMessage
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
var BUILT_FOR_SHOPIFY_CHECKS = [
|
|
49
|
+
// Required - authentication
|
|
50
|
+
createCheck(
|
|
51
|
+
"auth-offline-tokens",
|
|
52
|
+
"App uses offline access tokens",
|
|
53
|
+
"authentication",
|
|
54
|
+
"required",
|
|
55
|
+
"hasOfflineTokens",
|
|
56
|
+
"App correctly uses offline access tokens",
|
|
57
|
+
"App must use offline access tokens for background operations"
|
|
58
|
+
),
|
|
59
|
+
createCheck(
|
|
60
|
+
"auth-session-management",
|
|
61
|
+
"App has session management",
|
|
62
|
+
"authentication",
|
|
63
|
+
"required",
|
|
64
|
+
"hasSessionManagement",
|
|
65
|
+
"App has proper session management",
|
|
66
|
+
"App must implement session management for user authentication"
|
|
67
|
+
),
|
|
68
|
+
// Required - webhooks
|
|
69
|
+
createCheck(
|
|
70
|
+
"webhook-app-uninstall",
|
|
71
|
+
"Handles APP_UNINSTALLED webhook",
|
|
72
|
+
"webhooks",
|
|
73
|
+
"required",
|
|
74
|
+
"hasAppUninstallHandler",
|
|
75
|
+
"App handles APP_UNINSTALLED webhook correctly",
|
|
76
|
+
"App must handle APP_UNINSTALLED webhook to clean up data"
|
|
77
|
+
),
|
|
78
|
+
{
|
|
79
|
+
id: "webhook-gdpr",
|
|
80
|
+
name: "Has GDPR compliance endpoints",
|
|
81
|
+
category: "gdpr",
|
|
82
|
+
severity: "required",
|
|
83
|
+
check(config) {
|
|
84
|
+
const passed = config.hasGDPREndpoints === true;
|
|
85
|
+
return {
|
|
86
|
+
id: this.id,
|
|
87
|
+
name: this.name,
|
|
88
|
+
category: this.category,
|
|
89
|
+
severity: this.severity,
|
|
90
|
+
passed,
|
|
91
|
+
message: passed ? "App has GDPR compliance endpoints (customers/data_request, customers/redact, shop/redact)" : "App must implement GDPR endpoints: customers/data_request, customers/redact, shop/redact"
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
createCheck(
|
|
96
|
+
"webhook-handlers",
|
|
97
|
+
"Has webhook handlers registered",
|
|
98
|
+
"webhooks",
|
|
99
|
+
"required",
|
|
100
|
+
"hasWebhookHandlers",
|
|
101
|
+
"App has webhook handlers registered",
|
|
102
|
+
"App must register webhook handlers for required events"
|
|
103
|
+
),
|
|
104
|
+
// Required - security
|
|
105
|
+
createCheck(
|
|
106
|
+
"security-headers",
|
|
107
|
+
"Uses security headers",
|
|
108
|
+
"security",
|
|
109
|
+
"required",
|
|
110
|
+
"hasSecurityHeaders",
|
|
111
|
+
"App uses security headers (HSTS, X-Content-Type-Options, etc.)",
|
|
112
|
+
"App must use security headers (HSTS, X-Content-Type-Options, X-Frame-Options)"
|
|
113
|
+
),
|
|
114
|
+
createCheck(
|
|
115
|
+
"security-input-validation",
|
|
116
|
+
"Validates user input",
|
|
117
|
+
"security",
|
|
118
|
+
"required",
|
|
119
|
+
"hasInputValidation",
|
|
120
|
+
"App validates user input properly",
|
|
121
|
+
"App must validate and sanitize all user input"
|
|
122
|
+
),
|
|
123
|
+
createCheck(
|
|
124
|
+
"security-csp",
|
|
125
|
+
"Has Content Security Policy",
|
|
126
|
+
"security",
|
|
127
|
+
"required",
|
|
128
|
+
"hasContentSecurityPolicy",
|
|
129
|
+
"App has Content Security Policy configured",
|
|
130
|
+
"App must implement Content Security Policy headers"
|
|
131
|
+
),
|
|
132
|
+
// Required - api_usage
|
|
133
|
+
createCheck(
|
|
134
|
+
"api-rate-limiting",
|
|
135
|
+
"Implements rate limiting",
|
|
136
|
+
"api_usage",
|
|
137
|
+
"required",
|
|
138
|
+
"hasRateLimiting",
|
|
139
|
+
"App implements rate limiting and respects Shopify API limits",
|
|
140
|
+
"App must implement rate limiting and respect Shopify API rate limits"
|
|
141
|
+
),
|
|
142
|
+
createCheck(
|
|
143
|
+
"api-retry-logic",
|
|
144
|
+
"Has retry logic for API calls",
|
|
145
|
+
"api_usage",
|
|
146
|
+
"required",
|
|
147
|
+
"hasRetryLogic",
|
|
148
|
+
"App has retry logic with exponential backoff for API calls",
|
|
149
|
+
"App must implement retry logic with backoff for transient API failures"
|
|
150
|
+
),
|
|
151
|
+
createCheck(
|
|
152
|
+
"api-error-handling",
|
|
153
|
+
"Has proper error handling",
|
|
154
|
+
"api_usage",
|
|
155
|
+
"required",
|
|
156
|
+
"hasErrorHandling",
|
|
157
|
+
"App has proper error handling for API responses",
|
|
158
|
+
"App must handle API errors gracefully and provide user feedback"
|
|
159
|
+
),
|
|
160
|
+
// Recommended - billing
|
|
161
|
+
createCheck(
|
|
162
|
+
"billing-integration",
|
|
163
|
+
"Has billing/subscription integration",
|
|
164
|
+
"billing",
|
|
165
|
+
"recommended",
|
|
166
|
+
"hasBillingIntegration",
|
|
167
|
+
"App has billing/subscription integration with Shopify",
|
|
168
|
+
"App should integrate with Shopify billing API for subscriptions"
|
|
169
|
+
),
|
|
170
|
+
// Recommended - performance
|
|
171
|
+
{
|
|
172
|
+
id: "perf-response-time",
|
|
173
|
+
name: "Average response time under 500ms",
|
|
174
|
+
category: "performance",
|
|
175
|
+
severity: "recommended",
|
|
176
|
+
check(config) {
|
|
177
|
+
const avgTime = config.avgResponseTimeMs;
|
|
178
|
+
const passed = avgTime !== void 0 && avgTime < 500;
|
|
179
|
+
const result = {
|
|
180
|
+
id: this.id,
|
|
181
|
+
name: this.name,
|
|
182
|
+
category: this.category,
|
|
183
|
+
severity: this.severity,
|
|
184
|
+
passed,
|
|
185
|
+
message: passed ? `Average response time (${avgTime}ms) is under 500ms` : "Average response time should be under 500ms"
|
|
186
|
+
};
|
|
187
|
+
if (avgTime !== void 0) {
|
|
188
|
+
result.details = `Current average: ${avgTime}ms`;
|
|
189
|
+
}
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
createCheck(
|
|
194
|
+
"perf-optimization",
|
|
195
|
+
"Has performance optimization",
|
|
196
|
+
"performance",
|
|
197
|
+
"recommended",
|
|
198
|
+
"hasPerformanceOptimization",
|
|
199
|
+
"App has performance optimizations (caching, lazy loading, etc.)",
|
|
200
|
+
"App should implement performance optimizations such as caching"
|
|
201
|
+
),
|
|
202
|
+
// Optional - ux
|
|
203
|
+
createCheck(
|
|
204
|
+
"ux-i18n",
|
|
205
|
+
"Supports multiple languages",
|
|
206
|
+
"ux",
|
|
207
|
+
"optional",
|
|
208
|
+
"supportsMultipleLanguages",
|
|
209
|
+
"App supports multiple languages for international merchants",
|
|
210
|
+
"Consider adding multi-language support for broader reach"
|
|
211
|
+
),
|
|
212
|
+
createCheck(
|
|
213
|
+
"ux-accessibility",
|
|
214
|
+
"Has accessibility compliance",
|
|
215
|
+
"ux",
|
|
216
|
+
"optional",
|
|
217
|
+
"hasAccessibilityCompliance",
|
|
218
|
+
"App meets accessibility standards (WCAG)",
|
|
219
|
+
"Consider implementing accessibility features (WCAG compliance)"
|
|
220
|
+
)
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
// src/shopify-compliance/compliance-checker.ts
|
|
224
|
+
var ComplianceChecker = class {
|
|
225
|
+
checks;
|
|
226
|
+
constructor(checks) {
|
|
227
|
+
this.checks = checks ?? BUILT_FOR_SHOPIFY_CHECKS;
|
|
228
|
+
}
|
|
229
|
+
runAll(config) {
|
|
230
|
+
const results = this.checks.map((c) => c.check(config));
|
|
231
|
+
const score = this.generateScore(results);
|
|
232
|
+
const summary = this.buildSummary(results);
|
|
233
|
+
return {
|
|
234
|
+
appName: config.appName,
|
|
235
|
+
checkedAt: /* @__PURE__ */ new Date(),
|
|
236
|
+
totalChecks: results.length,
|
|
237
|
+
passed: results.filter((r) => r.passed).length,
|
|
238
|
+
failed: results.filter((r) => !r.passed && r.severity === "required").length,
|
|
239
|
+
warnings: results.filter((r) => !r.passed && r.severity !== "required").length,
|
|
240
|
+
score,
|
|
241
|
+
checks: results,
|
|
242
|
+
summary
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
runCategory(config, category) {
|
|
246
|
+
return this.checks.filter((c) => c.category === category).map((c) => c.check(config));
|
|
247
|
+
}
|
|
248
|
+
getEligibility(config) {
|
|
249
|
+
const results = this.checks.map((c) => c.check(config));
|
|
250
|
+
const blockers = results.filter((r) => r.severity === "required" && !r.passed);
|
|
251
|
+
return {
|
|
252
|
+
eligible: blockers.length === 0,
|
|
253
|
+
blockers
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
generateScore(results) {
|
|
257
|
+
const required = results.filter((r) => r.severity === "required");
|
|
258
|
+
const recommended = results.filter((r) => r.severity === "recommended");
|
|
259
|
+
const optional = results.filter((r) => r.severity === "optional");
|
|
260
|
+
const requiredScore = required.length > 0 ? required.filter((r) => r.passed).length / required.length * 60 : 60;
|
|
261
|
+
const recommendedScore = recommended.length > 0 ? recommended.filter((r) => r.passed).length / recommended.length * 30 : 30;
|
|
262
|
+
const optionalScore = optional.length > 0 ? optional.filter((r) => r.passed).length / optional.length * 10 : 10;
|
|
263
|
+
return Math.round(requiredScore + recommendedScore + optionalScore);
|
|
264
|
+
}
|
|
265
|
+
buildSummary(results) {
|
|
266
|
+
const required = results.filter((r) => r.severity === "required");
|
|
267
|
+
const recommended = results.filter((r) => r.severity === "recommended");
|
|
268
|
+
const optional = results.filter((r) => r.severity === "optional");
|
|
269
|
+
const requiredPassed = required.filter((r) => r.passed).length;
|
|
270
|
+
const requiredTotal = required.length;
|
|
271
|
+
return {
|
|
272
|
+
requiredPassed,
|
|
273
|
+
requiredTotal,
|
|
274
|
+
recommendedPassed: recommended.filter((r) => r.passed).length,
|
|
275
|
+
recommendedTotal: recommended.length,
|
|
276
|
+
optionalPassed: optional.filter((r) => r.passed).length,
|
|
277
|
+
optionalTotal: optional.length,
|
|
278
|
+
isEligible: requiredPassed === requiredTotal
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
283
|
+
0 && (module.exports = {
|
|
284
|
+
BUILT_FOR_SHOPIFY_CHECKS,
|
|
285
|
+
ComplianceChecker
|
|
286
|
+
});
|
|
287
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/shopify-compliance/index.ts","../../src/shopify-compliance/compliance-checks.ts","../../src/shopify-compliance/compliance-checker.ts"],"sourcesContent":["/**\n * @uniforge/testing - Shopify Compliance\n *\n * Built for Shopify compliance checker and verification utilities.\n */\n\nexport type {\n ComplianceCategory,\n ComplianceCheckConfig,\n ComplianceCheckResult,\n ComplianceReport,\n ComplianceSeverity,\n ComplianceSummary,\n} from './types';\n\nexport type { ComplianceCheck } from './compliance-checks';\nexport { BUILT_FOR_SHOPIFY_CHECKS } from './compliance-checks';\n\nexport { ComplianceChecker } from './compliance-checker';\n","/**\n * Built for Shopify compliance checks.\n */\n\nimport type {\n ComplianceCategory,\n ComplianceCheckConfig,\n ComplianceCheckResult,\n ComplianceSeverity,\n} from './types';\n\nexport interface ComplianceCheck {\n id: string;\n name: string;\n category: ComplianceCategory;\n severity: ComplianceSeverity;\n check: (config: ComplianceCheckConfig) => ComplianceCheckResult;\n}\n\nfunction createCheck(\n id: string,\n name: string,\n category: ComplianceCategory,\n severity: ComplianceSeverity,\n configKey: keyof ComplianceCheckConfig,\n passMessage: string,\n failMessage: string,\n): ComplianceCheck {\n return {\n id,\n name,\n category,\n severity,\n check(config: ComplianceCheckConfig): ComplianceCheckResult {\n const passed = config[configKey] === true;\n return {\n id,\n name,\n category,\n severity,\n passed,\n message: passed ? passMessage : failMessage,\n };\n },\n };\n}\n\nexport const BUILT_FOR_SHOPIFY_CHECKS: ComplianceCheck[] = [\n // Required - authentication\n createCheck(\n 'auth-offline-tokens',\n 'App uses offline access tokens',\n 'authentication',\n 'required',\n 'hasOfflineTokens',\n 'App correctly uses offline access tokens',\n 'App must use offline access tokens for background operations',\n ),\n createCheck(\n 'auth-session-management',\n 'App has session management',\n 'authentication',\n 'required',\n 'hasSessionManagement',\n 'App has proper session management',\n 'App must implement session management for user authentication',\n ),\n\n // Required - webhooks\n createCheck(\n 'webhook-app-uninstall',\n 'Handles APP_UNINSTALLED webhook',\n 'webhooks',\n 'required',\n 'hasAppUninstallHandler',\n 'App handles APP_UNINSTALLED webhook correctly',\n 'App must handle APP_UNINSTALLED webhook to clean up data',\n ),\n {\n id: 'webhook-gdpr',\n name: 'Has GDPR compliance endpoints',\n category: 'gdpr',\n severity: 'required',\n check(config: ComplianceCheckConfig): ComplianceCheckResult {\n const passed = config.hasGDPREndpoints === true;\n return {\n id: this.id,\n name: this.name,\n category: this.category,\n severity: this.severity,\n passed,\n message: passed\n ? 'App has GDPR compliance endpoints (customers/data_request, customers/redact, shop/redact)'\n : 'App must implement GDPR endpoints: customers/data_request, customers/redact, shop/redact',\n };\n },\n },\n createCheck(\n 'webhook-handlers',\n 'Has webhook handlers registered',\n 'webhooks',\n 'required',\n 'hasWebhookHandlers',\n 'App has webhook handlers registered',\n 'App must register webhook handlers for required events',\n ),\n\n // Required - security\n createCheck(\n 'security-headers',\n 'Uses security headers',\n 'security',\n 'required',\n 'hasSecurityHeaders',\n 'App uses security headers (HSTS, X-Content-Type-Options, etc.)',\n 'App must use security headers (HSTS, X-Content-Type-Options, X-Frame-Options)',\n ),\n createCheck(\n 'security-input-validation',\n 'Validates user input',\n 'security',\n 'required',\n 'hasInputValidation',\n 'App validates user input properly',\n 'App must validate and sanitize all user input',\n ),\n createCheck(\n 'security-csp',\n 'Has Content Security Policy',\n 'security',\n 'required',\n 'hasContentSecurityPolicy',\n 'App has Content Security Policy configured',\n 'App must implement Content Security Policy headers',\n ),\n\n // Required - api_usage\n createCheck(\n 'api-rate-limiting',\n 'Implements rate limiting',\n 'api_usage',\n 'required',\n 'hasRateLimiting',\n 'App implements rate limiting and respects Shopify API limits',\n 'App must implement rate limiting and respect Shopify API rate limits',\n ),\n createCheck(\n 'api-retry-logic',\n 'Has retry logic for API calls',\n 'api_usage',\n 'required',\n 'hasRetryLogic',\n 'App has retry logic with exponential backoff for API calls',\n 'App must implement retry logic with backoff for transient API failures',\n ),\n createCheck(\n 'api-error-handling',\n 'Has proper error handling',\n 'api_usage',\n 'required',\n 'hasErrorHandling',\n 'App has proper error handling for API responses',\n 'App must handle API errors gracefully and provide user feedback',\n ),\n\n // Recommended - billing\n createCheck(\n 'billing-integration',\n 'Has billing/subscription integration',\n 'billing',\n 'recommended',\n 'hasBillingIntegration',\n 'App has billing/subscription integration with Shopify',\n 'App should integrate with Shopify billing API for subscriptions',\n ),\n\n // Recommended - performance\n {\n id: 'perf-response-time',\n name: 'Average response time under 500ms',\n category: 'performance',\n severity: 'recommended',\n check(config: ComplianceCheckConfig): ComplianceCheckResult {\n const avgTime = config.avgResponseTimeMs;\n const passed = avgTime !== undefined && avgTime < 500;\n const result: ComplianceCheckResult = {\n id: this.id,\n name: this.name,\n category: this.category,\n severity: this.severity,\n passed,\n message: passed\n ? `Average response time (${avgTime}ms) is under 500ms`\n : 'Average response time should be under 500ms',\n };\n if (avgTime !== undefined) {\n result.details = `Current average: ${avgTime}ms`;\n }\n return result;\n },\n },\n createCheck(\n 'perf-optimization',\n 'Has performance optimization',\n 'performance',\n 'recommended',\n 'hasPerformanceOptimization',\n 'App has performance optimizations (caching, lazy loading, etc.)',\n 'App should implement performance optimizations such as caching',\n ),\n\n // Optional - ux\n createCheck(\n 'ux-i18n',\n 'Supports multiple languages',\n 'ux',\n 'optional',\n 'supportsMultipleLanguages',\n 'App supports multiple languages for international merchants',\n 'Consider adding multi-language support for broader reach',\n ),\n createCheck(\n 'ux-accessibility',\n 'Has accessibility compliance',\n 'ux',\n 'optional',\n 'hasAccessibilityCompliance',\n 'App meets accessibility standards (WCAG)',\n 'Consider implementing accessibility features (WCAG compliance)',\n ),\n];\n","/**\n * Compliance checker for Built for Shopify requirements.\n */\n\nimport type { ComplianceCheck } from './compliance-checks';\nimport { BUILT_FOR_SHOPIFY_CHECKS } from './compliance-checks';\nimport type {\n ComplianceCategory,\n ComplianceCheckConfig,\n ComplianceCheckResult,\n ComplianceReport,\n ComplianceSummary,\n} from './types';\n\nexport class ComplianceChecker {\n private readonly checks: ComplianceCheck[];\n\n constructor(checks?: ComplianceCheck[]) {\n this.checks = checks ?? BUILT_FOR_SHOPIFY_CHECKS;\n }\n\n runAll(config: ComplianceCheckConfig): ComplianceReport {\n const results = this.checks.map((c) => c.check(config));\n const score = this.generateScore(results);\n const summary = this.buildSummary(results);\n\n return {\n appName: config.appName,\n checkedAt: new Date(),\n totalChecks: results.length,\n passed: results.filter((r) => r.passed).length,\n failed: results.filter((r) => !r.passed && r.severity === 'required').length,\n warnings: results.filter((r) => !r.passed && r.severity !== 'required').length,\n score,\n checks: results,\n summary,\n };\n }\n\n runCategory(\n config: ComplianceCheckConfig,\n category: ComplianceCategory,\n ): ComplianceCheckResult[] {\n return this.checks\n .filter((c) => c.category === category)\n .map((c) => c.check(config));\n }\n\n getEligibility(config: ComplianceCheckConfig): {\n eligible: boolean;\n blockers: ComplianceCheckResult[];\n } {\n const results = this.checks.map((c) => c.check(config));\n const blockers = results.filter((r) => r.severity === 'required' && !r.passed);\n return {\n eligible: blockers.length === 0,\n blockers,\n };\n }\n\n generateScore(results: ComplianceCheckResult[]): number {\n const required = results.filter((r) => r.severity === 'required');\n const recommended = results.filter((r) => r.severity === 'recommended');\n const optional = results.filter((r) => r.severity === 'optional');\n\n const requiredScore =\n required.length > 0\n ? (required.filter((r) => r.passed).length / required.length) * 60\n : 60;\n const recommendedScore =\n recommended.length > 0\n ? (recommended.filter((r) => r.passed).length / recommended.length) * 30\n : 30;\n const optionalScore =\n optional.length > 0\n ? (optional.filter((r) => r.passed).length / optional.length) * 10\n : 10;\n\n return Math.round(requiredScore + recommendedScore + optionalScore);\n }\n\n private buildSummary(results: ComplianceCheckResult[]): ComplianceSummary {\n const required = results.filter((r) => r.severity === 'required');\n const recommended = results.filter((r) => r.severity === 'recommended');\n const optional = results.filter((r) => r.severity === 'optional');\n\n const requiredPassed = required.filter((r) => r.passed).length;\n const requiredTotal = required.length;\n\n return {\n requiredPassed,\n requiredTotal,\n recommendedPassed: recommended.filter((r) => r.passed).length,\n recommendedTotal: recommended.length,\n optionalPassed: optional.filter((r) => r.passed).length,\n optionalTotal: optional.length,\n isEligible: requiredPassed === requiredTotal,\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
// src/shopify-compliance/compliance-checks.ts
|
|
2
|
+
function createCheck(id, name, category, severity, configKey, passMessage, failMessage) {
|
|
3
|
+
return {
|
|
4
|
+
id,
|
|
5
|
+
name,
|
|
6
|
+
category,
|
|
7
|
+
severity,
|
|
8
|
+
check(config) {
|
|
9
|
+
const passed = config[configKey] === true;
|
|
10
|
+
return {
|
|
11
|
+
id,
|
|
12
|
+
name,
|
|
13
|
+
category,
|
|
14
|
+
severity,
|
|
15
|
+
passed,
|
|
16
|
+
message: passed ? passMessage : failMessage
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
var BUILT_FOR_SHOPIFY_CHECKS = [
|
|
22
|
+
// Required - authentication
|
|
23
|
+
createCheck(
|
|
24
|
+
"auth-offline-tokens",
|
|
25
|
+
"App uses offline access tokens",
|
|
26
|
+
"authentication",
|
|
27
|
+
"required",
|
|
28
|
+
"hasOfflineTokens",
|
|
29
|
+
"App correctly uses offline access tokens",
|
|
30
|
+
"App must use offline access tokens for background operations"
|
|
31
|
+
),
|
|
32
|
+
createCheck(
|
|
33
|
+
"auth-session-management",
|
|
34
|
+
"App has session management",
|
|
35
|
+
"authentication",
|
|
36
|
+
"required",
|
|
37
|
+
"hasSessionManagement",
|
|
38
|
+
"App has proper session management",
|
|
39
|
+
"App must implement session management for user authentication"
|
|
40
|
+
),
|
|
41
|
+
// Required - webhooks
|
|
42
|
+
createCheck(
|
|
43
|
+
"webhook-app-uninstall",
|
|
44
|
+
"Handles APP_UNINSTALLED webhook",
|
|
45
|
+
"webhooks",
|
|
46
|
+
"required",
|
|
47
|
+
"hasAppUninstallHandler",
|
|
48
|
+
"App handles APP_UNINSTALLED webhook correctly",
|
|
49
|
+
"App must handle APP_UNINSTALLED webhook to clean up data"
|
|
50
|
+
),
|
|
51
|
+
{
|
|
52
|
+
id: "webhook-gdpr",
|
|
53
|
+
name: "Has GDPR compliance endpoints",
|
|
54
|
+
category: "gdpr",
|
|
55
|
+
severity: "required",
|
|
56
|
+
check(config) {
|
|
57
|
+
const passed = config.hasGDPREndpoints === true;
|
|
58
|
+
return {
|
|
59
|
+
id: this.id,
|
|
60
|
+
name: this.name,
|
|
61
|
+
category: this.category,
|
|
62
|
+
severity: this.severity,
|
|
63
|
+
passed,
|
|
64
|
+
message: passed ? "App has GDPR compliance endpoints (customers/data_request, customers/redact, shop/redact)" : "App must implement GDPR endpoints: customers/data_request, customers/redact, shop/redact"
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
createCheck(
|
|
69
|
+
"webhook-handlers",
|
|
70
|
+
"Has webhook handlers registered",
|
|
71
|
+
"webhooks",
|
|
72
|
+
"required",
|
|
73
|
+
"hasWebhookHandlers",
|
|
74
|
+
"App has webhook handlers registered",
|
|
75
|
+
"App must register webhook handlers for required events"
|
|
76
|
+
),
|
|
77
|
+
// Required - security
|
|
78
|
+
createCheck(
|
|
79
|
+
"security-headers",
|
|
80
|
+
"Uses security headers",
|
|
81
|
+
"security",
|
|
82
|
+
"required",
|
|
83
|
+
"hasSecurityHeaders",
|
|
84
|
+
"App uses security headers (HSTS, X-Content-Type-Options, etc.)",
|
|
85
|
+
"App must use security headers (HSTS, X-Content-Type-Options, X-Frame-Options)"
|
|
86
|
+
),
|
|
87
|
+
createCheck(
|
|
88
|
+
"security-input-validation",
|
|
89
|
+
"Validates user input",
|
|
90
|
+
"security",
|
|
91
|
+
"required",
|
|
92
|
+
"hasInputValidation",
|
|
93
|
+
"App validates user input properly",
|
|
94
|
+
"App must validate and sanitize all user input"
|
|
95
|
+
),
|
|
96
|
+
createCheck(
|
|
97
|
+
"security-csp",
|
|
98
|
+
"Has Content Security Policy",
|
|
99
|
+
"security",
|
|
100
|
+
"required",
|
|
101
|
+
"hasContentSecurityPolicy",
|
|
102
|
+
"App has Content Security Policy configured",
|
|
103
|
+
"App must implement Content Security Policy headers"
|
|
104
|
+
),
|
|
105
|
+
// Required - api_usage
|
|
106
|
+
createCheck(
|
|
107
|
+
"api-rate-limiting",
|
|
108
|
+
"Implements rate limiting",
|
|
109
|
+
"api_usage",
|
|
110
|
+
"required",
|
|
111
|
+
"hasRateLimiting",
|
|
112
|
+
"App implements rate limiting and respects Shopify API limits",
|
|
113
|
+
"App must implement rate limiting and respect Shopify API rate limits"
|
|
114
|
+
),
|
|
115
|
+
createCheck(
|
|
116
|
+
"api-retry-logic",
|
|
117
|
+
"Has retry logic for API calls",
|
|
118
|
+
"api_usage",
|
|
119
|
+
"required",
|
|
120
|
+
"hasRetryLogic",
|
|
121
|
+
"App has retry logic with exponential backoff for API calls",
|
|
122
|
+
"App must implement retry logic with backoff for transient API failures"
|
|
123
|
+
),
|
|
124
|
+
createCheck(
|
|
125
|
+
"api-error-handling",
|
|
126
|
+
"Has proper error handling",
|
|
127
|
+
"api_usage",
|
|
128
|
+
"required",
|
|
129
|
+
"hasErrorHandling",
|
|
130
|
+
"App has proper error handling for API responses",
|
|
131
|
+
"App must handle API errors gracefully and provide user feedback"
|
|
132
|
+
),
|
|
133
|
+
// Recommended - billing
|
|
134
|
+
createCheck(
|
|
135
|
+
"billing-integration",
|
|
136
|
+
"Has billing/subscription integration",
|
|
137
|
+
"billing",
|
|
138
|
+
"recommended",
|
|
139
|
+
"hasBillingIntegration",
|
|
140
|
+
"App has billing/subscription integration with Shopify",
|
|
141
|
+
"App should integrate with Shopify billing API for subscriptions"
|
|
142
|
+
),
|
|
143
|
+
// Recommended - performance
|
|
144
|
+
{
|
|
145
|
+
id: "perf-response-time",
|
|
146
|
+
name: "Average response time under 500ms",
|
|
147
|
+
category: "performance",
|
|
148
|
+
severity: "recommended",
|
|
149
|
+
check(config) {
|
|
150
|
+
const avgTime = config.avgResponseTimeMs;
|
|
151
|
+
const passed = avgTime !== void 0 && avgTime < 500;
|
|
152
|
+
const result = {
|
|
153
|
+
id: this.id,
|
|
154
|
+
name: this.name,
|
|
155
|
+
category: this.category,
|
|
156
|
+
severity: this.severity,
|
|
157
|
+
passed,
|
|
158
|
+
message: passed ? `Average response time (${avgTime}ms) is under 500ms` : "Average response time should be under 500ms"
|
|
159
|
+
};
|
|
160
|
+
if (avgTime !== void 0) {
|
|
161
|
+
result.details = `Current average: ${avgTime}ms`;
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
createCheck(
|
|
167
|
+
"perf-optimization",
|
|
168
|
+
"Has performance optimization",
|
|
169
|
+
"performance",
|
|
170
|
+
"recommended",
|
|
171
|
+
"hasPerformanceOptimization",
|
|
172
|
+
"App has performance optimizations (caching, lazy loading, etc.)",
|
|
173
|
+
"App should implement performance optimizations such as caching"
|
|
174
|
+
),
|
|
175
|
+
// Optional - ux
|
|
176
|
+
createCheck(
|
|
177
|
+
"ux-i18n",
|
|
178
|
+
"Supports multiple languages",
|
|
179
|
+
"ux",
|
|
180
|
+
"optional",
|
|
181
|
+
"supportsMultipleLanguages",
|
|
182
|
+
"App supports multiple languages for international merchants",
|
|
183
|
+
"Consider adding multi-language support for broader reach"
|
|
184
|
+
),
|
|
185
|
+
createCheck(
|
|
186
|
+
"ux-accessibility",
|
|
187
|
+
"Has accessibility compliance",
|
|
188
|
+
"ux",
|
|
189
|
+
"optional",
|
|
190
|
+
"hasAccessibilityCompliance",
|
|
191
|
+
"App meets accessibility standards (WCAG)",
|
|
192
|
+
"Consider implementing accessibility features (WCAG compliance)"
|
|
193
|
+
)
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
// src/shopify-compliance/compliance-checker.ts
|
|
197
|
+
var ComplianceChecker = class {
|
|
198
|
+
checks;
|
|
199
|
+
constructor(checks) {
|
|
200
|
+
this.checks = checks ?? BUILT_FOR_SHOPIFY_CHECKS;
|
|
201
|
+
}
|
|
202
|
+
runAll(config) {
|
|
203
|
+
const results = this.checks.map((c) => c.check(config));
|
|
204
|
+
const score = this.generateScore(results);
|
|
205
|
+
const summary = this.buildSummary(results);
|
|
206
|
+
return {
|
|
207
|
+
appName: config.appName,
|
|
208
|
+
checkedAt: /* @__PURE__ */ new Date(),
|
|
209
|
+
totalChecks: results.length,
|
|
210
|
+
passed: results.filter((r) => r.passed).length,
|
|
211
|
+
failed: results.filter((r) => !r.passed && r.severity === "required").length,
|
|
212
|
+
warnings: results.filter((r) => !r.passed && r.severity !== "required").length,
|
|
213
|
+
score,
|
|
214
|
+
checks: results,
|
|
215
|
+
summary
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
runCategory(config, category) {
|
|
219
|
+
return this.checks.filter((c) => c.category === category).map((c) => c.check(config));
|
|
220
|
+
}
|
|
221
|
+
getEligibility(config) {
|
|
222
|
+
const results = this.checks.map((c) => c.check(config));
|
|
223
|
+
const blockers = results.filter((r) => r.severity === "required" && !r.passed);
|
|
224
|
+
return {
|
|
225
|
+
eligible: blockers.length === 0,
|
|
226
|
+
blockers
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
generateScore(results) {
|
|
230
|
+
const required = results.filter((r) => r.severity === "required");
|
|
231
|
+
const recommended = results.filter((r) => r.severity === "recommended");
|
|
232
|
+
const optional = results.filter((r) => r.severity === "optional");
|
|
233
|
+
const requiredScore = required.length > 0 ? required.filter((r) => r.passed).length / required.length * 60 : 60;
|
|
234
|
+
const recommendedScore = recommended.length > 0 ? recommended.filter((r) => r.passed).length / recommended.length * 30 : 30;
|
|
235
|
+
const optionalScore = optional.length > 0 ? optional.filter((r) => r.passed).length / optional.length * 10 : 10;
|
|
236
|
+
return Math.round(requiredScore + recommendedScore + optionalScore);
|
|
237
|
+
}
|
|
238
|
+
buildSummary(results) {
|
|
239
|
+
const required = results.filter((r) => r.severity === "required");
|
|
240
|
+
const recommended = results.filter((r) => r.severity === "recommended");
|
|
241
|
+
const optional = results.filter((r) => r.severity === "optional");
|
|
242
|
+
const requiredPassed = required.filter((r) => r.passed).length;
|
|
243
|
+
const requiredTotal = required.length;
|
|
244
|
+
return {
|
|
245
|
+
requiredPassed,
|
|
246
|
+
requiredTotal,
|
|
247
|
+
recommendedPassed: recommended.filter((r) => r.passed).length,
|
|
248
|
+
recommendedTotal: recommended.length,
|
|
249
|
+
optionalPassed: optional.filter((r) => r.passed).length,
|
|
250
|
+
optionalTotal: optional.length,
|
|
251
|
+
isEligible: requiredPassed === requiredTotal
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
export {
|
|
256
|
+
BUILT_FOR_SHOPIFY_CHECKS,
|
|
257
|
+
ComplianceChecker
|
|
258
|
+
};
|
|
259
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/shopify-compliance/compliance-checks.ts","../../src/shopify-compliance/compliance-checker.ts"],"sourcesContent":["/**\n * Built for Shopify compliance checks.\n */\n\nimport type {\n ComplianceCategory,\n ComplianceCheckConfig,\n ComplianceCheckResult,\n ComplianceSeverity,\n} from './types';\n\nexport interface ComplianceCheck {\n id: string;\n name: string;\n category: ComplianceCategory;\n severity: ComplianceSeverity;\n check: (config: ComplianceCheckConfig) => ComplianceCheckResult;\n}\n\nfunction createCheck(\n id: string,\n name: string,\n category: ComplianceCategory,\n severity: ComplianceSeverity,\n configKey: keyof ComplianceCheckConfig,\n passMessage: string,\n failMessage: string,\n): ComplianceCheck {\n return {\n id,\n name,\n category,\n severity,\n check(config: ComplianceCheckConfig): ComplianceCheckResult {\n const passed = config[configKey] === true;\n return {\n id,\n name,\n category,\n severity,\n passed,\n message: passed ? passMessage : failMessage,\n };\n },\n };\n}\n\nexport const BUILT_FOR_SHOPIFY_CHECKS: ComplianceCheck[] = [\n // Required - authentication\n createCheck(\n 'auth-offline-tokens',\n 'App uses offline access tokens',\n 'authentication',\n 'required',\n 'hasOfflineTokens',\n 'App correctly uses offline access tokens',\n 'App must use offline access tokens for background operations',\n ),\n createCheck(\n 'auth-session-management',\n 'App has session management',\n 'authentication',\n 'required',\n 'hasSessionManagement',\n 'App has proper session management',\n 'App must implement session management for user authentication',\n ),\n\n // Required - webhooks\n createCheck(\n 'webhook-app-uninstall',\n 'Handles APP_UNINSTALLED webhook',\n 'webhooks',\n 'required',\n 'hasAppUninstallHandler',\n 'App handles APP_UNINSTALLED webhook correctly',\n 'App must handle APP_UNINSTALLED webhook to clean up data',\n ),\n {\n id: 'webhook-gdpr',\n name: 'Has GDPR compliance endpoints',\n category: 'gdpr',\n severity: 'required',\n check(config: ComplianceCheckConfig): ComplianceCheckResult {\n const passed = config.hasGDPREndpoints === true;\n return {\n id: this.id,\n name: this.name,\n category: this.category,\n severity: this.severity,\n passed,\n message: passed\n ? 'App has GDPR compliance endpoints (customers/data_request, customers/redact, shop/redact)'\n : 'App must implement GDPR endpoints: customers/data_request, customers/redact, shop/redact',\n };\n },\n },\n createCheck(\n 'webhook-handlers',\n 'Has webhook handlers registered',\n 'webhooks',\n 'required',\n 'hasWebhookHandlers',\n 'App has webhook handlers registered',\n 'App must register webhook handlers for required events',\n ),\n\n // Required - security\n createCheck(\n 'security-headers',\n 'Uses security headers',\n 'security',\n 'required',\n 'hasSecurityHeaders',\n 'App uses security headers (HSTS, X-Content-Type-Options, etc.)',\n 'App must use security headers (HSTS, X-Content-Type-Options, X-Frame-Options)',\n ),\n createCheck(\n 'security-input-validation',\n 'Validates user input',\n 'security',\n 'required',\n 'hasInputValidation',\n 'App validates user input properly',\n 'App must validate and sanitize all user input',\n ),\n createCheck(\n 'security-csp',\n 'Has Content Security Policy',\n 'security',\n 'required',\n 'hasContentSecurityPolicy',\n 'App has Content Security Policy configured',\n 'App must implement Content Security Policy headers',\n ),\n\n // Required - api_usage\n createCheck(\n 'api-rate-limiting',\n 'Implements rate limiting',\n 'api_usage',\n 'required',\n 'hasRateLimiting',\n 'App implements rate limiting and respects Shopify API limits',\n 'App must implement rate limiting and respect Shopify API rate limits',\n ),\n createCheck(\n 'api-retry-logic',\n 'Has retry logic for API calls',\n 'api_usage',\n 'required',\n 'hasRetryLogic',\n 'App has retry logic with exponential backoff for API calls',\n 'App must implement retry logic with backoff for transient API failures',\n ),\n createCheck(\n 'api-error-handling',\n 'Has proper error handling',\n 'api_usage',\n 'required',\n 'hasErrorHandling',\n 'App has proper error handling for API responses',\n 'App must handle API errors gracefully and provide user feedback',\n ),\n\n // Recommended - billing\n createCheck(\n 'billing-integration',\n 'Has billing/subscription integration',\n 'billing',\n 'recommended',\n 'hasBillingIntegration',\n 'App has billing/subscription integration with Shopify',\n 'App should integrate with Shopify billing API for subscriptions',\n ),\n\n // Recommended - performance\n {\n id: 'perf-response-time',\n name: 'Average response time under 500ms',\n category: 'performance',\n severity: 'recommended',\n check(config: ComplianceCheckConfig): ComplianceCheckResult {\n const avgTime = config.avgResponseTimeMs;\n const passed = avgTime !== undefined && avgTime < 500;\n const result: ComplianceCheckResult = {\n id: this.id,\n name: this.name,\n category: this.category,\n severity: this.severity,\n passed,\n message: passed\n ? `Average response time (${avgTime}ms) is under 500ms`\n : 'Average response time should be under 500ms',\n };\n if (avgTime !== undefined) {\n result.details = `Current average: ${avgTime}ms`;\n }\n return result;\n },\n },\n createCheck(\n 'perf-optimization',\n 'Has performance optimization',\n 'performance',\n 'recommended',\n 'hasPerformanceOptimization',\n 'App has performance optimizations (caching, lazy loading, etc.)',\n 'App should implement performance optimizations such as caching',\n ),\n\n // Optional - ux\n createCheck(\n 'ux-i18n',\n 'Supports multiple languages',\n 'ux',\n 'optional',\n 'supportsMultipleLanguages',\n 'App supports multiple languages for international merchants',\n 'Consider adding multi-language support for broader reach',\n ),\n createCheck(\n 'ux-accessibility',\n 'Has accessibility compliance',\n 'ux',\n 'optional',\n 'hasAccessibilityCompliance',\n 'App meets accessibility standards (WCAG)',\n 'Consider implementing accessibility features (WCAG compliance)',\n ),\n];\n","/**\n * Compliance checker for Built for Shopify requirements.\n */\n\nimport type { ComplianceCheck } from './compliance-checks';\nimport { BUILT_FOR_SHOPIFY_CHECKS } from './compliance-checks';\nimport type {\n ComplianceCategory,\n ComplianceCheckConfig,\n ComplianceCheckResult,\n ComplianceReport,\n ComplianceSummary,\n} from './types';\n\nexport class ComplianceChecker {\n private readonly checks: ComplianceCheck[];\n\n constructor(checks?: ComplianceCheck[]) {\n this.checks = checks ?? BUILT_FOR_SHOPIFY_CHECKS;\n }\n\n runAll(config: ComplianceCheckConfig): ComplianceReport {\n const results = this.checks.map((c) => c.check(config));\n const score = this.generateScore(results);\n const summary = this.buildSummary(results);\n\n return {\n appName: config.appName,\n checkedAt: new Date(),\n totalChecks: results.length,\n passed: results.filter((r) => r.passed).length,\n failed: results.filter((r) => !r.passed && r.severity === 'required').length,\n warnings: results.filter((r) => !r.passed && r.severity !== 'required').length,\n score,\n checks: results,\n summary,\n };\n }\n\n runCategory(\n config: ComplianceCheckConfig,\n category: ComplianceCategory,\n ): ComplianceCheckResult[] {\n return this.checks\n .filter((c) => c.category === category)\n .map((c) => c.check(config));\n }\n\n getEligibility(config: ComplianceCheckConfig): {\n eligible: boolean;\n blockers: ComplianceCheckResult[];\n } {\n const results = this.checks.map((c) => c.check(config));\n const blockers = results.filter((r) => r.severity === 'required' && !r.passed);\n return {\n eligible: blockers.length === 0,\n blockers,\n };\n }\n\n generateScore(results: ComplianceCheckResult[]): number {\n const required = results.filter((r) => r.severity === 'required');\n const recommended = results.filter((r) => r.severity === 'recommended');\n const optional = results.filter((r) => r.severity === 'optional');\n\n const requiredScore =\n required.length > 0\n ? (required.filter((r) => r.passed).length / required.length) * 60\n : 60;\n const recommendedScore =\n recommended.length > 0\n ? (recommended.filter((r) => r.passed).length / recommended.length) * 30\n : 30;\n const optionalScore =\n optional.length > 0\n ? (optional.filter((r) => r.passed).length / optional.length) * 10\n : 10;\n\n return Math.round(requiredScore + recommendedScore + optionalScore);\n }\n\n private buildSummary(results: ComplianceCheckResult[]): ComplianceSummary {\n const required = results.filter((r) => r.severity === 'required');\n const recommended = results.filter((r) => r.severity === 'recommended');\n const optional = results.filter((r) => r.severity === 'optional');\n\n const requiredPassed = required.filter((r) => r.passed).length;\n const requiredTotal = required.length;\n\n return {\n requiredPassed,\n requiredTotal,\n recommendedPassed: recommended.filter((r) => r.passed).length,\n recommendedTotal: recommended.length,\n optionalPassed: optional.filter((r) => r.passed).length,\n optionalTotal: optional.length,\n isEligible: requiredPassed === requiredTotal,\n };\n }\n}\n"],"mappings":"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","names":[]}
|