@snappy-stack/sdk 0.1.6 → 0.1.7

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/bin/check.js CHANGED
File without changes
package/dist/plugin.cjs CHANGED
@@ -26,8 +26,10 @@ function scanSource(srcDir) {
26
26
  for (const file of files) {
27
27
  const content = fs__default.default.readFileSync(file, "utf8");
28
28
  const ext = path2__default.default.extname(file);
29
- const isStudio = file.replace(/\\/g, "/").includes("components/studio");
30
- if (!isStudio && [".tsx", ".jsx", ".css", ".astro"].includes(ext)) {
29
+ const normalizedPath = file.replace(/\\/g, "/");
30
+ const isStudio = normalizedPath.includes("components/studio");
31
+ const isStatusPage = normalizedPath.includes("MaintenancePage.astro") || normalizedPath.includes("pages/maintenance.astro") || normalizedPath.includes("pages/suspended.astro") || normalizedPath.includes("pages/development.astro");
32
+ if (!isStudio && !isStatusPage && [".tsx", ".jsx", ".css", ".astro"].includes(ext)) {
31
33
  let scanContent = content.replace(/:root\s*{[^}]*}/g, "").replace(/@theme\s*{[^}]*}/g, "");
32
34
  const lines = scanContent.split("\n");
33
35
  const filteredLines = lines.filter((line) => !line.trim().startsWith("--"));
@@ -37,7 +39,7 @@ function scanSource(srcDir) {
37
39
  const hslMatches = scanContent.match(hslRegex) || [];
38
40
  result.hardcodedValues += hexMatches.length + rgbMatches.length + hslMatches.length;
39
41
  }
40
- if ([".tsx", ".jsx", ".astro"].includes(ext)) {
42
+ if (!isStatusPage && [".tsx", ".jsx", ".astro"].includes(ext)) {
41
43
  const imgTags = content.match(imgTagRegex) || [];
42
44
  for (const tag of imgTags) {
43
45
  if (!altAttrRegex.test(tag)) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/audit/scanner.ts","../src/audit/scorer.ts","../src/plugin.ts"],"names":["fs","path","getAllFiles"],"mappings":"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","file":"plugin.cjs","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nexport interface AuditResult {\n hardcodedValues: number;\n missingAltTags: number;\n missingMetaTags: {\n title: boolean;\n description: boolean;\n ogImage: boolean;\n };\n unoptimizedImages: number;\n totalBuildSize: number; // in bytes\n}\n\nexport function scanSource(srcDir: string): AuditResult {\n const files = getAllFiles(srcDir);\n const result: AuditResult = {\n hardcodedValues: 0,\n missingAltTags: 0,\n missingMetaTags: { title: false, description: false, ogImage: false },\n unoptimizedImages: 0,\n totalBuildSize: 0\n };\n\n // Regex patterns\n const hexRegex = /#(?:[0-9a-fA-F]{3}){1,2}\\b/g;\n const rgbRegex = /rgb\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*\\)/g;\n const hslRegex = /hsl\\(\\s*\\d+\\s*,\\s*\\d+%\\s*,\\s*\\d+%\\s*\\)/g;\n const imgTagRegex = /<img\\s+[^>]*>/gi;\n const altAttrRegex = /alt=(?:[\"']|\\{)/i;\n\n for (const file of files) {\n const content = fs.readFileSync(file, 'utf8');\n const ext = path.extname(file);\n\n // 1. Style Scan (Hardcoded values)\n const isStudio = file.replace(/\\\\/g, '/').includes('components/studio');\n\n if (!isStudio && ['.tsx', '.jsx', '.css', '.astro'].includes(ext)) {\n // Smarter scan: ignore definitions in :root or @theme or lines defining --vars\n let scanContent = content\n .replace(/:root\\s*{[^}]*}/g, '')\n .replace(/@theme\\s*{[^}]*}/g, '');\n \n // Also ignore lines that are clearly CSS variable definitions\n const lines = scanContent.split('\\n');\n const filteredLines = lines.filter(line => !line.trim().startsWith('--'));\n scanContent = filteredLines.join('\\n');\n\n const hexMatches = scanContent.match(hexRegex) || [];\n const rgbMatches = scanContent.match(rgbRegex) || [];\n const hslMatches = scanContent.match(hslRegex) || [];\n result.hardcodedValues += hexMatches.length + rgbMatches.length + hslMatches.length;\n }\n\n // 2. Accessibility Scan (Alt tags)\n if (['.tsx', '.jsx', '.astro'].includes(ext)) {\n const imgTags = content.match(imgTagRegex) || [];\n for (const tag of imgTags) {\n if (!altAttrRegex.test(tag)) {\n result.missingAltTags++;\n }\n }\n }\n\n // 3. SEO Scan (Only in primary layouts or index)\n if (file.toLowerCase().includes('layout') || file.toLowerCase().includes('index.astro')) {\n if (content.includes('<title>') || content.includes('<SEO')) result.missingMetaTags.title = true;\n if (content.includes('name=\"description\"') || content.includes('property=\"description\"') || content.includes('<SEO')) {\n result.missingMetaTags.description = true;\n }\n if (content.includes('property=\"og:image\"') || content.includes('<SEO')) result.missingMetaTags.ogImage = true;\n }\n }\n\n return result;\n}\n\nfunction getAllFiles(dirPath: string, arrayOfFiles: string[] = []) {\n if (!fs.existsSync(dirPath)) return [];\n const files = fs.readdirSync(dirPath);\n\n files.forEach((file) => {\n if (fs.statSync(path.join(dirPath, file)).isDirectory()) {\n if (file !== 'node_modules' && file !== '.astro') {\n arrayOfFiles = getAllFiles(path.join(dirPath, file), arrayOfFiles);\n }\n } else {\n arrayOfFiles.push(path.join(dirPath, file));\n }\n });\n\n return arrayOfFiles;\n}\n","import { AuditResult } from './scanner.js';\n\nexport interface ScoreBreakdown {\n performance: number;\n accessibility: number;\n seo: number;\n compliance: number;\n optimization: number;\n bundle: number;\n total: number;\n}\n\nexport function calculateScore(result: AuditResult): ScoreBreakdown {\n // Weights based on Blueprint v1.3\n const weights = {\n performance: 0.30,\n accessibility: 0.20,\n seo: 0.20,\n compliance: 0.15,\n optimization: 0.10,\n bundle: 0.05\n };\n\n // 1. Performance (Initial 100, -10 per missing alt - proxy for poor hygiene)\n let perf = 100 - (result.missingAltTags * 10);\n perf = Math.max(0, perf);\n\n // 2. Accessibility (-20 per missing alt)\n let a11y = 100 - (result.missingAltTags * 20);\n a11y = Math.max(0, a11y);\n\n // 3. SEO (Weighted based on meta presence)\n let seo = 0;\n if (result.missingMetaTags.title) seo += 40;\n if (result.missingMetaTags.description) seo += 40;\n if (result.missingMetaTags.ogImage) seo += 20;\n\n // 4. Compliance (Hardcoded values penalty)\n let compliance = 100 - (result.hardcodedValues * 5);\n compliance = Math.max(0, compliance);\n\n // 5. Optimization (Placeholder for media optimization audit)\n let optimization = 100; // Default until deep media audit implemented\n\n // 6. Bundle Size (Heuristic)\n let bundle = 100;\n if (result.totalBuildSize > 10 * 1024 * 1024) bundle = 40; // Penalty > 10MB\n\n const total = Math.round(\n (perf * weights.performance) +\n (a11y * weights.accessibility) +\n (seo * weights.seo) +\n (compliance * weights.compliance) +\n (optimization * weights.optimization) +\n (bundle * weights.bundle)\n );\n\n return {\n performance: perf,\n accessibility: a11y,\n seo,\n compliance,\n optimization,\n bundle,\n total\n };\n}\n","import fs from 'fs';\r\nimport path from 'path';\r\nimport { scanSource } from './audit/scanner.js';\r\nimport { calculateScore, ScoreBreakdown } from './audit/scorer.js';\r\nimport { createPublicClient } from './client.js';\r\n\r\nexport { createPublicClient };\r\n\r\nexport function snappyCreditEnforcer() {\r\n return {\r\n name: 'snappy-credit-enforcer',\r\n async buildStart() {\r\n const srcDir = path.resolve(process.cwd(), 'src');\r\n const files = getAllFiles(srcDir);\r\n let hasCredit = false;\r\n\r\n for (const file of files) {\r\n if (file.endsWith('.astro') || file.endsWith('.tsx') || file.endsWith('.jsx')) {\r\n const content = fs.readFileSync(file, 'utf8');\r\n if (content.includes('SnappyCredit') || content.includes('<SnappyCredit')) {\r\n hasCredit = true;\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (!hasCredit) {\r\n throw new Error(\r\n '\\n\\n\\x1b[31m[SNAPPY_SDK_E401]\\x1b[0m\\nBuild validation failed: Mandatory <SnappyCredit /> component missing.\\n\\n'\r\n );\r\n }\r\n },\r\n\r\n async buildEnd() {\r\n const srcDir = path.resolve(process.cwd(), 'src');\r\n const auditResult = scanSource(srcDir);\r\n const score = calculateScore(auditResult);\r\n\r\n printReport(score);\r\n\r\n if (score.total < 50) {\r\n console.error('\\n\\x1b[31mšŸ‘¹ BLOCKED:\\x1b[0m SNAPPY Score is below the quality threshold (50).');\r\n console.error('Ship clean or don\\'t ship at all. šŸ”«šŸ›”ļøšŸ’“\\n');\r\n process.exit(1);\r\n }\r\n\r\n // Sync score with Core API if configured\r\n const token = process.env.SNAPPY_TOKEN;\r\n const baseUrl = process.env.SNAPPY_URL || 'https://core.wicky.id';\r\n if (token) {\r\n try {\r\n // Simple fetch to update score\r\n await fetch(`${baseUrl}/v1/projects/self/score`, {\r\n method: 'PATCH',\r\n headers: {\r\n 'Authorization': `Bearer ${token}`,\r\n 'Content-Type': 'application/json'\r\n },\r\n body: JSON.stringify({ score: score.total })\r\n });\r\n } catch (e) {\r\n // Silent fail on score sync - don't block build if API is down\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nfunction printReport(s: ScoreBreakdown) {\r\n const reset = '\\x1b[0m';\r\n const green = '\\x1b[32m';\r\n const yellow = '\\x1b[33m';\r\n const red = '\\x1b[31m';\r\n const cyan = '\\x1b[36m';\r\n\r\n const status = s.total >= 70 ? `${green}āœ… PASS${reset}` : s.total >= 50 ? `${yellow}āš ļø WARNING${reset}` : `${red}šŸ‘¹ BLOCKED${reset}`;\r\n\r\n console.log(`\r\n${cyan}╔══════════════════════════════════╗\r\nā•‘ SNAPPY SCORE REPORT ā•‘\r\n╠══════════════════════════════════╣${reset}\r\nā•‘ Performance ${fmt(s.performance)} 30% ā•‘\r\nā•‘ Accessibility ${fmt(s.accessibility)} 20% ā•‘\r\nā•‘ SEO Completeness ${fmt(s.seo)} 20% ā•‘\r\nā•‘ Zero Hardcoded ${fmt(s.compliance)} 15% ā•‘\r\nā•‘ Image Optimization ${fmt(s.optimization)} 10% ā•‘\r\nā•‘ Bundle Size ${fmt(s.bundle)} 5% ā•‘\r\n${cyan}╠══════════════════════════════════╣${reset}\r\nā•‘ TOTAL SCORE: ${s.total}/100 ā•‘\r\nā•‘ STATUS: ${status} ā•‘\r\n${cyan}ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•${reset}\r\n`);\r\n}\r\n\r\nfunction fmt(n: number) {\r\n const str = `${n}/100`;\r\n return str.padEnd(8, ' ');\r\n}\r\n\r\nfunction getAllFiles(dirPath: string, arrayOfFiles: string[] = []) {\r\n if (!fs.existsSync(dirPath)) return [];\r\n const files = fs.readdirSync(dirPath);\r\n files.forEach((file: string) => {\r\n if (fs.statSync(path.join(dirPath, file)).isDirectory()) {\r\n if (file !== 'node_modules' && file !== '.astro') {\r\n arrayOfFiles = getAllFiles(path.join(dirPath, file), arrayOfFiles);\r\n }\r\n } else {\r\n arrayOfFiles.push(path.join(dirPath, file));\r\n }\r\n });\r\n return arrayOfFiles;\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/audit/scanner.ts","../src/audit/scorer.ts","../src/plugin.ts"],"names":["fs","path","getAllFiles"],"mappings":"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","file":"plugin.cjs","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nexport interface AuditResult {\n hardcodedValues: number;\n missingAltTags: number;\n missingMetaTags: {\n title: boolean;\n description: boolean;\n ogImage: boolean;\n };\n unoptimizedImages: number;\n totalBuildSize: number; // in bytes\n}\n\nexport function scanSource(srcDir: string): AuditResult {\n const files = getAllFiles(srcDir);\n const result: AuditResult = {\n hardcodedValues: 0,\n missingAltTags: 0,\n missingMetaTags: { title: false, description: false, ogImage: false },\n unoptimizedImages: 0,\n totalBuildSize: 0\n };\n\n // Regex patterns\n const hexRegex = /#(?:[0-9a-fA-F]{3}){1,2}\\b/g;\n const rgbRegex = /rgb\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*\\)/g;\n const hslRegex = /hsl\\(\\s*\\d+\\s*,\\s*\\d+%\\s*,\\s*\\d+%\\s*\\)/g;\n const imgTagRegex = /<img\\s+[^>]*>/gi;\n const altAttrRegex = /alt=(?:[\"']|\\{)/i;\n\n for (const file of files) {\n const content = fs.readFileSync(file, 'utf8');\n const ext = path.extname(file);\n\n // 1. Style Scan (Hardcoded values)\n const normalizedPath = file.replace(/\\\\/g, '/');\n const isStudio = normalizedPath.includes('components/studio');\n const isStatusPage = normalizedPath.includes('MaintenancePage.astro') || \n normalizedPath.includes('pages/maintenance.astro') ||\n normalizedPath.includes('pages/suspended.astro') ||\n normalizedPath.includes('pages/development.astro');\n\n if (!isStudio && !isStatusPage && ['.tsx', '.jsx', '.css', '.astro'].includes(ext)) {\n // Smarter scan: ignore definitions in :root or @theme or lines defining --vars\n let scanContent = content\n .replace(/:root\\s*{[^}]*}/g, '')\n .replace(/@theme\\s*{[^}]*}/g, '');\n \n // Also ignore lines that are clearly CSS variable definitions\n const lines = scanContent.split('\\n');\n const filteredLines = lines.filter(line => !line.trim().startsWith('--'));\n scanContent = filteredLines.join('\\n');\n\n const hexMatches = scanContent.match(hexRegex) || [];\n const rgbMatches = scanContent.match(rgbRegex) || [];\n const hslMatches = scanContent.match(hslRegex) || [];\n result.hardcodedValues += hexMatches.length + rgbMatches.length + hslMatches.length;\n }\n\n // 2. Accessibility Scan (Alt tags)\n if (!isStatusPage && ['.tsx', '.jsx', '.astro'].includes(ext)) {\n const imgTags = content.match(imgTagRegex) || [];\n for (const tag of imgTags) {\n if (!altAttrRegex.test(tag)) {\n result.missingAltTags++;\n }\n }\n }\n\n // 3. SEO Scan (Only in primary layouts or index)\n if (file.toLowerCase().includes('layout') || file.toLowerCase().includes('index.astro')) {\n if (content.includes('<title>') || content.includes('<SEO')) result.missingMetaTags.title = true;\n if (content.includes('name=\"description\"') || content.includes('property=\"description\"') || content.includes('<SEO')) {\n result.missingMetaTags.description = true;\n }\n if (content.includes('property=\"og:image\"') || content.includes('<SEO')) result.missingMetaTags.ogImage = true;\n }\n }\n\n return result;\n}\n\nfunction getAllFiles(dirPath: string, arrayOfFiles: string[] = []) {\n if (!fs.existsSync(dirPath)) return [];\n const files = fs.readdirSync(dirPath);\n\n files.forEach((file) => {\n if (fs.statSync(path.join(dirPath, file)).isDirectory()) {\n if (file !== 'node_modules' && file !== '.astro') {\n arrayOfFiles = getAllFiles(path.join(dirPath, file), arrayOfFiles);\n }\n } else {\n arrayOfFiles.push(path.join(dirPath, file));\n }\n });\n\n return arrayOfFiles;\n}\n","import { AuditResult } from './scanner.js';\n\nexport interface ScoreBreakdown {\n performance: number;\n accessibility: number;\n seo: number;\n compliance: number;\n optimization: number;\n bundle: number;\n total: number;\n}\n\nexport function calculateScore(result: AuditResult): ScoreBreakdown {\n // Weights based on Blueprint v1.3\n const weights = {\n performance: 0.30,\n accessibility: 0.20,\n seo: 0.20,\n compliance: 0.15,\n optimization: 0.10,\n bundle: 0.05\n };\n\n // 1. Performance (Initial 100, -10 per missing alt - proxy for poor hygiene)\n let perf = 100 - (result.missingAltTags * 10);\n perf = Math.max(0, perf);\n\n // 2. Accessibility (-20 per missing alt)\n let a11y = 100 - (result.missingAltTags * 20);\n a11y = Math.max(0, a11y);\n\n // 3. SEO (Weighted based on meta presence)\n let seo = 0;\n if (result.missingMetaTags.title) seo += 40;\n if (result.missingMetaTags.description) seo += 40;\n if (result.missingMetaTags.ogImage) seo += 20;\n\n // 4. Compliance (Hardcoded values penalty)\n let compliance = 100 - (result.hardcodedValues * 5);\n compliance = Math.max(0, compliance);\n\n // 5. Optimization (Placeholder for media optimization audit)\n let optimization = 100; // Default until deep media audit implemented\n\n // 6. Bundle Size (Heuristic)\n let bundle = 100;\n if (result.totalBuildSize > 10 * 1024 * 1024) bundle = 40; // Penalty > 10MB\n\n const total = Math.round(\n (perf * weights.performance) +\n (a11y * weights.accessibility) +\n (seo * weights.seo) +\n (compliance * weights.compliance) +\n (optimization * weights.optimization) +\n (bundle * weights.bundle)\n );\n\n return {\n performance: perf,\n accessibility: a11y,\n seo,\n compliance,\n optimization,\n bundle,\n total\n };\n}\n","import fs from 'fs';\r\nimport path from 'path';\r\nimport { scanSource } from './audit/scanner.js';\r\nimport { calculateScore, ScoreBreakdown } from './audit/scorer.js';\r\nimport { createPublicClient } from './client.js';\r\n\r\nexport { createPublicClient };\r\n\r\nexport function snappyCreditEnforcer() {\r\n return {\r\n name: 'snappy-credit-enforcer',\r\n async buildStart() {\r\n const srcDir = path.resolve(process.cwd(), 'src');\r\n const files = getAllFiles(srcDir);\r\n let hasCredit = false;\r\n\r\n for (const file of files) {\r\n if (file.endsWith('.astro') || file.endsWith('.tsx') || file.endsWith('.jsx')) {\r\n const content = fs.readFileSync(file, 'utf8');\r\n if (content.includes('SnappyCredit') || content.includes('<SnappyCredit')) {\r\n hasCredit = true;\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (!hasCredit) {\r\n throw new Error(\r\n '\\n\\n\\x1b[31m[SNAPPY_SDK_E401]\\x1b[0m\\nBuild validation failed: Mandatory <SnappyCredit /> component missing.\\n\\n'\r\n );\r\n }\r\n },\r\n\r\n async buildEnd() {\r\n const srcDir = path.resolve(process.cwd(), 'src');\r\n const auditResult = scanSource(srcDir);\r\n const score = calculateScore(auditResult);\r\n\r\n printReport(score);\r\n\r\n if (score.total < 50) {\r\n console.error('\\n\\x1b[31mšŸ‘¹ BLOCKED:\\x1b[0m SNAPPY Score is below the quality threshold (50).');\r\n console.error('Ship clean or don\\'t ship at all. šŸ”«šŸ›”ļøšŸ’“\\n');\r\n process.exit(1);\r\n }\r\n\r\n // Sync score with Core API if configured\r\n const token = process.env.SNAPPY_TOKEN;\r\n const baseUrl = process.env.SNAPPY_URL || 'https://core.wicky.id';\r\n if (token) {\r\n try {\r\n // Simple fetch to update score\r\n await fetch(`${baseUrl}/v1/projects/self/score`, {\r\n method: 'PATCH',\r\n headers: {\r\n 'Authorization': `Bearer ${token}`,\r\n 'Content-Type': 'application/json'\r\n },\r\n body: JSON.stringify({ score: score.total })\r\n });\r\n } catch (e) {\r\n // Silent fail on score sync - don't block build if API is down\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nfunction printReport(s: ScoreBreakdown) {\r\n const reset = '\\x1b[0m';\r\n const green = '\\x1b[32m';\r\n const yellow = '\\x1b[33m';\r\n const red = '\\x1b[31m';\r\n const cyan = '\\x1b[36m';\r\n\r\n const status = s.total >= 70 ? `${green}āœ… PASS${reset}` : s.total >= 50 ? `${yellow}āš ļø WARNING${reset}` : `${red}šŸ‘¹ BLOCKED${reset}`;\r\n\r\n console.log(`\r\n${cyan}╔══════════════════════════════════╗\r\nā•‘ SNAPPY SCORE REPORT ā•‘\r\n╠══════════════════════════════════╣${reset}\r\nā•‘ Performance ${fmt(s.performance)} 30% ā•‘\r\nā•‘ Accessibility ${fmt(s.accessibility)} 20% ā•‘\r\nā•‘ SEO Completeness ${fmt(s.seo)} 20% ā•‘\r\nā•‘ Zero Hardcoded ${fmt(s.compliance)} 15% ā•‘\r\nā•‘ Image Optimization ${fmt(s.optimization)} 10% ā•‘\r\nā•‘ Bundle Size ${fmt(s.bundle)} 5% ā•‘\r\n${cyan}╠══════════════════════════════════╣${reset}\r\nā•‘ TOTAL SCORE: ${s.total}/100 ā•‘\r\nā•‘ STATUS: ${status} ā•‘\r\n${cyan}ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•${reset}\r\n`);\r\n}\r\n\r\nfunction fmt(n: number) {\r\n const str = `${n}/100`;\r\n return str.padEnd(8, ' ');\r\n}\r\n\r\nfunction getAllFiles(dirPath: string, arrayOfFiles: string[] = []) {\r\n if (!fs.existsSync(dirPath)) return [];\r\n const files = fs.readdirSync(dirPath);\r\n files.forEach((file: string) => {\r\n if (fs.statSync(path.join(dirPath, file)).isDirectory()) {\r\n if (file !== 'node_modules' && file !== '.astro') {\r\n arrayOfFiles = getAllFiles(path.join(dirPath, file), arrayOfFiles);\r\n }\r\n } else {\r\n arrayOfFiles.push(path.join(dirPath, file));\r\n }\r\n });\r\n return arrayOfFiles;\r\n}\r\n"]}
package/dist/plugin.js CHANGED
@@ -19,8 +19,10 @@ function scanSource(srcDir) {
19
19
  for (const file of files) {
20
20
  const content = fs.readFileSync(file, "utf8");
21
21
  const ext = path2.extname(file);
22
- const isStudio = file.replace(/\\/g, "/").includes("components/studio");
23
- if (!isStudio && [".tsx", ".jsx", ".css", ".astro"].includes(ext)) {
22
+ const normalizedPath = file.replace(/\\/g, "/");
23
+ const isStudio = normalizedPath.includes("components/studio");
24
+ const isStatusPage = normalizedPath.includes("MaintenancePage.astro") || normalizedPath.includes("pages/maintenance.astro") || normalizedPath.includes("pages/suspended.astro") || normalizedPath.includes("pages/development.astro");
25
+ if (!isStudio && !isStatusPage && [".tsx", ".jsx", ".css", ".astro"].includes(ext)) {
24
26
  let scanContent = content.replace(/:root\s*{[^}]*}/g, "").replace(/@theme\s*{[^}]*}/g, "");
25
27
  const lines = scanContent.split("\n");
26
28
  const filteredLines = lines.filter((line) => !line.trim().startsWith("--"));
@@ -30,7 +32,7 @@ function scanSource(srcDir) {
30
32
  const hslMatches = scanContent.match(hslRegex) || [];
31
33
  result.hardcodedValues += hexMatches.length + rgbMatches.length + hslMatches.length;
32
34
  }
33
- if ([".tsx", ".jsx", ".astro"].includes(ext)) {
35
+ if (!isStatusPage && [".tsx", ".jsx", ".astro"].includes(ext)) {
34
36
  const imgTags = content.match(imgTagRegex) || [];
35
37
  for (const tag of imgTags) {
36
38
  if (!altAttrRegex.test(tag)) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/audit/scanner.ts","../src/audit/scorer.ts","../src/plugin.ts"],"names":["path","getAllFiles","fs"],"mappings":"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","file":"plugin.js","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nexport interface AuditResult {\n hardcodedValues: number;\n missingAltTags: number;\n missingMetaTags: {\n title: boolean;\n description: boolean;\n ogImage: boolean;\n };\n unoptimizedImages: number;\n totalBuildSize: number; // in bytes\n}\n\nexport function scanSource(srcDir: string): AuditResult {\n const files = getAllFiles(srcDir);\n const result: AuditResult = {\n hardcodedValues: 0,\n missingAltTags: 0,\n missingMetaTags: { title: false, description: false, ogImage: false },\n unoptimizedImages: 0,\n totalBuildSize: 0\n };\n\n // Regex patterns\n const hexRegex = /#(?:[0-9a-fA-F]{3}){1,2}\\b/g;\n const rgbRegex = /rgb\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*\\)/g;\n const hslRegex = /hsl\\(\\s*\\d+\\s*,\\s*\\d+%\\s*,\\s*\\d+%\\s*\\)/g;\n const imgTagRegex = /<img\\s+[^>]*>/gi;\n const altAttrRegex = /alt=(?:[\"']|\\{)/i;\n\n for (const file of files) {\n const content = fs.readFileSync(file, 'utf8');\n const ext = path.extname(file);\n\n // 1. Style Scan (Hardcoded values)\n const isStudio = file.replace(/\\\\/g, '/').includes('components/studio');\n\n if (!isStudio && ['.tsx', '.jsx', '.css', '.astro'].includes(ext)) {\n // Smarter scan: ignore definitions in :root or @theme or lines defining --vars\n let scanContent = content\n .replace(/:root\\s*{[^}]*}/g, '')\n .replace(/@theme\\s*{[^}]*}/g, '');\n \n // Also ignore lines that are clearly CSS variable definitions\n const lines = scanContent.split('\\n');\n const filteredLines = lines.filter(line => !line.trim().startsWith('--'));\n scanContent = filteredLines.join('\\n');\n\n const hexMatches = scanContent.match(hexRegex) || [];\n const rgbMatches = scanContent.match(rgbRegex) || [];\n const hslMatches = scanContent.match(hslRegex) || [];\n result.hardcodedValues += hexMatches.length + rgbMatches.length + hslMatches.length;\n }\n\n // 2. Accessibility Scan (Alt tags)\n if (['.tsx', '.jsx', '.astro'].includes(ext)) {\n const imgTags = content.match(imgTagRegex) || [];\n for (const tag of imgTags) {\n if (!altAttrRegex.test(tag)) {\n result.missingAltTags++;\n }\n }\n }\n\n // 3. SEO Scan (Only in primary layouts or index)\n if (file.toLowerCase().includes('layout') || file.toLowerCase().includes('index.astro')) {\n if (content.includes('<title>') || content.includes('<SEO')) result.missingMetaTags.title = true;\n if (content.includes('name=\"description\"') || content.includes('property=\"description\"') || content.includes('<SEO')) {\n result.missingMetaTags.description = true;\n }\n if (content.includes('property=\"og:image\"') || content.includes('<SEO')) result.missingMetaTags.ogImage = true;\n }\n }\n\n return result;\n}\n\nfunction getAllFiles(dirPath: string, arrayOfFiles: string[] = []) {\n if (!fs.existsSync(dirPath)) return [];\n const files = fs.readdirSync(dirPath);\n\n files.forEach((file) => {\n if (fs.statSync(path.join(dirPath, file)).isDirectory()) {\n if (file !== 'node_modules' && file !== '.astro') {\n arrayOfFiles = getAllFiles(path.join(dirPath, file), arrayOfFiles);\n }\n } else {\n arrayOfFiles.push(path.join(dirPath, file));\n }\n });\n\n return arrayOfFiles;\n}\n","import { AuditResult } from './scanner.js';\n\nexport interface ScoreBreakdown {\n performance: number;\n accessibility: number;\n seo: number;\n compliance: number;\n optimization: number;\n bundle: number;\n total: number;\n}\n\nexport function calculateScore(result: AuditResult): ScoreBreakdown {\n // Weights based on Blueprint v1.3\n const weights = {\n performance: 0.30,\n accessibility: 0.20,\n seo: 0.20,\n compliance: 0.15,\n optimization: 0.10,\n bundle: 0.05\n };\n\n // 1. Performance (Initial 100, -10 per missing alt - proxy for poor hygiene)\n let perf = 100 - (result.missingAltTags * 10);\n perf = Math.max(0, perf);\n\n // 2. Accessibility (-20 per missing alt)\n let a11y = 100 - (result.missingAltTags * 20);\n a11y = Math.max(0, a11y);\n\n // 3. SEO (Weighted based on meta presence)\n let seo = 0;\n if (result.missingMetaTags.title) seo += 40;\n if (result.missingMetaTags.description) seo += 40;\n if (result.missingMetaTags.ogImage) seo += 20;\n\n // 4. Compliance (Hardcoded values penalty)\n let compliance = 100 - (result.hardcodedValues * 5);\n compliance = Math.max(0, compliance);\n\n // 5. Optimization (Placeholder for media optimization audit)\n let optimization = 100; // Default until deep media audit implemented\n\n // 6. Bundle Size (Heuristic)\n let bundle = 100;\n if (result.totalBuildSize > 10 * 1024 * 1024) bundle = 40; // Penalty > 10MB\n\n const total = Math.round(\n (perf * weights.performance) +\n (a11y * weights.accessibility) +\n (seo * weights.seo) +\n (compliance * weights.compliance) +\n (optimization * weights.optimization) +\n (bundle * weights.bundle)\n );\n\n return {\n performance: perf,\n accessibility: a11y,\n seo,\n compliance,\n optimization,\n bundle,\n total\n };\n}\n","import fs from 'fs';\r\nimport path from 'path';\r\nimport { scanSource } from './audit/scanner.js';\r\nimport { calculateScore, ScoreBreakdown } from './audit/scorer.js';\r\nimport { createPublicClient } from './client.js';\r\n\r\nexport { createPublicClient };\r\n\r\nexport function snappyCreditEnforcer() {\r\n return {\r\n name: 'snappy-credit-enforcer',\r\n async buildStart() {\r\n const srcDir = path.resolve(process.cwd(), 'src');\r\n const files = getAllFiles(srcDir);\r\n let hasCredit = false;\r\n\r\n for (const file of files) {\r\n if (file.endsWith('.astro') || file.endsWith('.tsx') || file.endsWith('.jsx')) {\r\n const content = fs.readFileSync(file, 'utf8');\r\n if (content.includes('SnappyCredit') || content.includes('<SnappyCredit')) {\r\n hasCredit = true;\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (!hasCredit) {\r\n throw new Error(\r\n '\\n\\n\\x1b[31m[SNAPPY_SDK_E401]\\x1b[0m\\nBuild validation failed: Mandatory <SnappyCredit /> component missing.\\n\\n'\r\n );\r\n }\r\n },\r\n\r\n async buildEnd() {\r\n const srcDir = path.resolve(process.cwd(), 'src');\r\n const auditResult = scanSource(srcDir);\r\n const score = calculateScore(auditResult);\r\n\r\n printReport(score);\r\n\r\n if (score.total < 50) {\r\n console.error('\\n\\x1b[31mšŸ‘¹ BLOCKED:\\x1b[0m SNAPPY Score is below the quality threshold (50).');\r\n console.error('Ship clean or don\\'t ship at all. šŸ”«šŸ›”ļøšŸ’“\\n');\r\n process.exit(1);\r\n }\r\n\r\n // Sync score with Core API if configured\r\n const token = process.env.SNAPPY_TOKEN;\r\n const baseUrl = process.env.SNAPPY_URL || 'https://core.wicky.id';\r\n if (token) {\r\n try {\r\n // Simple fetch to update score\r\n await fetch(`${baseUrl}/v1/projects/self/score`, {\r\n method: 'PATCH',\r\n headers: {\r\n 'Authorization': `Bearer ${token}`,\r\n 'Content-Type': 'application/json'\r\n },\r\n body: JSON.stringify({ score: score.total })\r\n });\r\n } catch (e) {\r\n // Silent fail on score sync - don't block build if API is down\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nfunction printReport(s: ScoreBreakdown) {\r\n const reset = '\\x1b[0m';\r\n const green = '\\x1b[32m';\r\n const yellow = '\\x1b[33m';\r\n const red = '\\x1b[31m';\r\n const cyan = '\\x1b[36m';\r\n\r\n const status = s.total >= 70 ? `${green}āœ… PASS${reset}` : s.total >= 50 ? `${yellow}āš ļø WARNING${reset}` : `${red}šŸ‘¹ BLOCKED${reset}`;\r\n\r\n console.log(`\r\n${cyan}╔══════════════════════════════════╗\r\nā•‘ SNAPPY SCORE REPORT ā•‘\r\n╠══════════════════════════════════╣${reset}\r\nā•‘ Performance ${fmt(s.performance)} 30% ā•‘\r\nā•‘ Accessibility ${fmt(s.accessibility)} 20% ā•‘\r\nā•‘ SEO Completeness ${fmt(s.seo)} 20% ā•‘\r\nā•‘ Zero Hardcoded ${fmt(s.compliance)} 15% ā•‘\r\nā•‘ Image Optimization ${fmt(s.optimization)} 10% ā•‘\r\nā•‘ Bundle Size ${fmt(s.bundle)} 5% ā•‘\r\n${cyan}╠══════════════════════════════════╣${reset}\r\nā•‘ TOTAL SCORE: ${s.total}/100 ā•‘\r\nā•‘ STATUS: ${status} ā•‘\r\n${cyan}ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•${reset}\r\n`);\r\n}\r\n\r\nfunction fmt(n: number) {\r\n const str = `${n}/100`;\r\n return str.padEnd(8, ' ');\r\n}\r\n\r\nfunction getAllFiles(dirPath: string, arrayOfFiles: string[] = []) {\r\n if (!fs.existsSync(dirPath)) return [];\r\n const files = fs.readdirSync(dirPath);\r\n files.forEach((file: string) => {\r\n if (fs.statSync(path.join(dirPath, file)).isDirectory()) {\r\n if (file !== 'node_modules' && file !== '.astro') {\r\n arrayOfFiles = getAllFiles(path.join(dirPath, file), arrayOfFiles);\r\n }\r\n } else {\r\n arrayOfFiles.push(path.join(dirPath, file));\r\n }\r\n });\r\n return arrayOfFiles;\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/audit/scanner.ts","../src/audit/scorer.ts","../src/plugin.ts"],"names":["path","getAllFiles","fs"],"mappings":"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","file":"plugin.js","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nexport interface AuditResult {\n hardcodedValues: number;\n missingAltTags: number;\n missingMetaTags: {\n title: boolean;\n description: boolean;\n ogImage: boolean;\n };\n unoptimizedImages: number;\n totalBuildSize: number; // in bytes\n}\n\nexport function scanSource(srcDir: string): AuditResult {\n const files = getAllFiles(srcDir);\n const result: AuditResult = {\n hardcodedValues: 0,\n missingAltTags: 0,\n missingMetaTags: { title: false, description: false, ogImage: false },\n unoptimizedImages: 0,\n totalBuildSize: 0\n };\n\n // Regex patterns\n const hexRegex = /#(?:[0-9a-fA-F]{3}){1,2}\\b/g;\n const rgbRegex = /rgb\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*\\)/g;\n const hslRegex = /hsl\\(\\s*\\d+\\s*,\\s*\\d+%\\s*,\\s*\\d+%\\s*\\)/g;\n const imgTagRegex = /<img\\s+[^>]*>/gi;\n const altAttrRegex = /alt=(?:[\"']|\\{)/i;\n\n for (const file of files) {\n const content = fs.readFileSync(file, 'utf8');\n const ext = path.extname(file);\n\n // 1. Style Scan (Hardcoded values)\n const normalizedPath = file.replace(/\\\\/g, '/');\n const isStudio = normalizedPath.includes('components/studio');\n const isStatusPage = normalizedPath.includes('MaintenancePage.astro') || \n normalizedPath.includes('pages/maintenance.astro') ||\n normalizedPath.includes('pages/suspended.astro') ||\n normalizedPath.includes('pages/development.astro');\n\n if (!isStudio && !isStatusPage && ['.tsx', '.jsx', '.css', '.astro'].includes(ext)) {\n // Smarter scan: ignore definitions in :root or @theme or lines defining --vars\n let scanContent = content\n .replace(/:root\\s*{[^}]*}/g, '')\n .replace(/@theme\\s*{[^}]*}/g, '');\n \n // Also ignore lines that are clearly CSS variable definitions\n const lines = scanContent.split('\\n');\n const filteredLines = lines.filter(line => !line.trim().startsWith('--'));\n scanContent = filteredLines.join('\\n');\n\n const hexMatches = scanContent.match(hexRegex) || [];\n const rgbMatches = scanContent.match(rgbRegex) || [];\n const hslMatches = scanContent.match(hslRegex) || [];\n result.hardcodedValues += hexMatches.length + rgbMatches.length + hslMatches.length;\n }\n\n // 2. Accessibility Scan (Alt tags)\n if (!isStatusPage && ['.tsx', '.jsx', '.astro'].includes(ext)) {\n const imgTags = content.match(imgTagRegex) || [];\n for (const tag of imgTags) {\n if (!altAttrRegex.test(tag)) {\n result.missingAltTags++;\n }\n }\n }\n\n // 3. SEO Scan (Only in primary layouts or index)\n if (file.toLowerCase().includes('layout') || file.toLowerCase().includes('index.astro')) {\n if (content.includes('<title>') || content.includes('<SEO')) result.missingMetaTags.title = true;\n if (content.includes('name=\"description\"') || content.includes('property=\"description\"') || content.includes('<SEO')) {\n result.missingMetaTags.description = true;\n }\n if (content.includes('property=\"og:image\"') || content.includes('<SEO')) result.missingMetaTags.ogImage = true;\n }\n }\n\n return result;\n}\n\nfunction getAllFiles(dirPath: string, arrayOfFiles: string[] = []) {\n if (!fs.existsSync(dirPath)) return [];\n const files = fs.readdirSync(dirPath);\n\n files.forEach((file) => {\n if (fs.statSync(path.join(dirPath, file)).isDirectory()) {\n if (file !== 'node_modules' && file !== '.astro') {\n arrayOfFiles = getAllFiles(path.join(dirPath, file), arrayOfFiles);\n }\n } else {\n arrayOfFiles.push(path.join(dirPath, file));\n }\n });\n\n return arrayOfFiles;\n}\n","import { AuditResult } from './scanner.js';\n\nexport interface ScoreBreakdown {\n performance: number;\n accessibility: number;\n seo: number;\n compliance: number;\n optimization: number;\n bundle: number;\n total: number;\n}\n\nexport function calculateScore(result: AuditResult): ScoreBreakdown {\n // Weights based on Blueprint v1.3\n const weights = {\n performance: 0.30,\n accessibility: 0.20,\n seo: 0.20,\n compliance: 0.15,\n optimization: 0.10,\n bundle: 0.05\n };\n\n // 1. Performance (Initial 100, -10 per missing alt - proxy for poor hygiene)\n let perf = 100 - (result.missingAltTags * 10);\n perf = Math.max(0, perf);\n\n // 2. Accessibility (-20 per missing alt)\n let a11y = 100 - (result.missingAltTags * 20);\n a11y = Math.max(0, a11y);\n\n // 3. SEO (Weighted based on meta presence)\n let seo = 0;\n if (result.missingMetaTags.title) seo += 40;\n if (result.missingMetaTags.description) seo += 40;\n if (result.missingMetaTags.ogImage) seo += 20;\n\n // 4. Compliance (Hardcoded values penalty)\n let compliance = 100 - (result.hardcodedValues * 5);\n compliance = Math.max(0, compliance);\n\n // 5. Optimization (Placeholder for media optimization audit)\n let optimization = 100; // Default until deep media audit implemented\n\n // 6. Bundle Size (Heuristic)\n let bundle = 100;\n if (result.totalBuildSize > 10 * 1024 * 1024) bundle = 40; // Penalty > 10MB\n\n const total = Math.round(\n (perf * weights.performance) +\n (a11y * weights.accessibility) +\n (seo * weights.seo) +\n (compliance * weights.compliance) +\n (optimization * weights.optimization) +\n (bundle * weights.bundle)\n );\n\n return {\n performance: perf,\n accessibility: a11y,\n seo,\n compliance,\n optimization,\n bundle,\n total\n };\n}\n","import fs from 'fs';\r\nimport path from 'path';\r\nimport { scanSource } from './audit/scanner.js';\r\nimport { calculateScore, ScoreBreakdown } from './audit/scorer.js';\r\nimport { createPublicClient } from './client.js';\r\n\r\nexport { createPublicClient };\r\n\r\nexport function snappyCreditEnforcer() {\r\n return {\r\n name: 'snappy-credit-enforcer',\r\n async buildStart() {\r\n const srcDir = path.resolve(process.cwd(), 'src');\r\n const files = getAllFiles(srcDir);\r\n let hasCredit = false;\r\n\r\n for (const file of files) {\r\n if (file.endsWith('.astro') || file.endsWith('.tsx') || file.endsWith('.jsx')) {\r\n const content = fs.readFileSync(file, 'utf8');\r\n if (content.includes('SnappyCredit') || content.includes('<SnappyCredit')) {\r\n hasCredit = true;\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (!hasCredit) {\r\n throw new Error(\r\n '\\n\\n\\x1b[31m[SNAPPY_SDK_E401]\\x1b[0m\\nBuild validation failed: Mandatory <SnappyCredit /> component missing.\\n\\n'\r\n );\r\n }\r\n },\r\n\r\n async buildEnd() {\r\n const srcDir = path.resolve(process.cwd(), 'src');\r\n const auditResult = scanSource(srcDir);\r\n const score = calculateScore(auditResult);\r\n\r\n printReport(score);\r\n\r\n if (score.total < 50) {\r\n console.error('\\n\\x1b[31mšŸ‘¹ BLOCKED:\\x1b[0m SNAPPY Score is below the quality threshold (50).');\r\n console.error('Ship clean or don\\'t ship at all. šŸ”«šŸ›”ļøšŸ’“\\n');\r\n process.exit(1);\r\n }\r\n\r\n // Sync score with Core API if configured\r\n const token = process.env.SNAPPY_TOKEN;\r\n const baseUrl = process.env.SNAPPY_URL || 'https://core.wicky.id';\r\n if (token) {\r\n try {\r\n // Simple fetch to update score\r\n await fetch(`${baseUrl}/v1/projects/self/score`, {\r\n method: 'PATCH',\r\n headers: {\r\n 'Authorization': `Bearer ${token}`,\r\n 'Content-Type': 'application/json'\r\n },\r\n body: JSON.stringify({ score: score.total })\r\n });\r\n } catch (e) {\r\n // Silent fail on score sync - don't block build if API is down\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nfunction printReport(s: ScoreBreakdown) {\r\n const reset = '\\x1b[0m';\r\n const green = '\\x1b[32m';\r\n const yellow = '\\x1b[33m';\r\n const red = '\\x1b[31m';\r\n const cyan = '\\x1b[36m';\r\n\r\n const status = s.total >= 70 ? `${green}āœ… PASS${reset}` : s.total >= 50 ? `${yellow}āš ļø WARNING${reset}` : `${red}šŸ‘¹ BLOCKED${reset}`;\r\n\r\n console.log(`\r\n${cyan}╔══════════════════════════════════╗\r\nā•‘ SNAPPY SCORE REPORT ā•‘\r\n╠══════════════════════════════════╣${reset}\r\nā•‘ Performance ${fmt(s.performance)} 30% ā•‘\r\nā•‘ Accessibility ${fmt(s.accessibility)} 20% ā•‘\r\nā•‘ SEO Completeness ${fmt(s.seo)} 20% ā•‘\r\nā•‘ Zero Hardcoded ${fmt(s.compliance)} 15% ā•‘\r\nā•‘ Image Optimization ${fmt(s.optimization)} 10% ā•‘\r\nā•‘ Bundle Size ${fmt(s.bundle)} 5% ā•‘\r\n${cyan}╠══════════════════════════════════╣${reset}\r\nā•‘ TOTAL SCORE: ${s.total}/100 ā•‘\r\nā•‘ STATUS: ${status} ā•‘\r\n${cyan}ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•${reset}\r\n`);\r\n}\r\n\r\nfunction fmt(n: number) {\r\n const str = `${n}/100`;\r\n return str.padEnd(8, ' ');\r\n}\r\n\r\nfunction getAllFiles(dirPath: string, arrayOfFiles: string[] = []) {\r\n if (!fs.existsSync(dirPath)) return [];\r\n const files = fs.readdirSync(dirPath);\r\n files.forEach((file: string) => {\r\n if (fs.statSync(path.join(dirPath, file)).isDirectory()) {\r\n if (file !== 'node_modules' && file !== '.astro') {\r\n arrayOfFiles = getAllFiles(path.join(dirPath, file), arrayOfFiles);\r\n }\r\n } else {\r\n arrayOfFiles.push(path.join(dirPath, file));\r\n }\r\n });\r\n return arrayOfFiles;\r\n}\r\n"]}
package/package.json CHANGED
@@ -1,57 +1,58 @@
1
- {
2
- "name": "@snappy-stack/sdk",
3
- "version": "0.1.6",
4
- "engines": {
5
- "node": ">=22"
6
- },
7
- "license": "SEE LICENSE IN LICENSE",
8
- "publishConfig": {
9
- "registry": "https://registry.npmjs.org/",
10
- "access": "public"
11
- },
12
- "bin": {
13
- "snappy-check": "./dist/bin/check.js"
14
- },
15
- "files": [
16
- "dist",
17
- "README.md",
18
- "LICENSE"
19
- ],
20
- "repository": {
21
- "type": "git",
22
- "url": "https://github.com/Snappy-Stack/Snappy_Stack"
23
- },
24
- "type": "module",
25
- "exports": {
26
- ".": {
27
- "types": "./dist/index.d.ts",
28
- "import": "./dist/index.js",
29
- "require": "./dist/index.cjs"
30
- },
31
- "./plugin": {
32
- "types": "./dist/plugin.d.ts",
33
- "import": "./dist/plugin.js",
34
- "require": "./dist/plugin.cjs"
35
- },
36
- "./check": {
37
- "types": "./dist/bin/check.d.ts",
38
- "import": "./dist/bin/check.js",
39
- "require": "./dist/bin/check.cjs"
40
- }
41
- },
42
- "main": "./dist/index.js",
43
- "types": "./dist/index.d.ts",
44
- "devDependencies": {
45
- "@types/node": "^22.0.0",
46
- "@types/react": "^19.2.14",
47
- "tsup": "^8.0.0",
48
- "typescript": "^5.0.0"
49
- },
50
- "dependencies": {
51
- "react": "^19.2.4"
52
- },
53
- "scripts": {
54
- "build": "tsup",
55
- "dev": "tsup --watch"
56
- }
57
- }
1
+ {
2
+ "name": "@snappy-stack/sdk",
3
+ "version": "0.1.7",
4
+ "engines": {
5
+ "node": ">=22"
6
+ },
7
+ "packageManager": "pnpm@9.15.0",
8
+ "license": "SEE LICENSE IN LICENSE",
9
+ "publishConfig": {
10
+ "registry": "https://registry.npmjs.org/",
11
+ "access": "public"
12
+ },
13
+ "bin": {
14
+ "snappy-check": "./dist/bin/check.js"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/Snappy-Stack/Snappy_Stack"
24
+ },
25
+ "type": "module",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js",
30
+ "require": "./dist/index.cjs"
31
+ },
32
+ "./plugin": {
33
+ "types": "./dist/plugin.d.ts",
34
+ "import": "./dist/plugin.js",
35
+ "require": "./dist/plugin.cjs"
36
+ },
37
+ "./check": {
38
+ "types": "./dist/bin/check.d.ts",
39
+ "import": "./dist/bin/check.js",
40
+ "require": "./dist/bin/check.cjs"
41
+ }
42
+ },
43
+ "main": "./dist/index.js",
44
+ "types": "./dist/index.d.ts",
45
+ "scripts": {
46
+ "build": "tsup",
47
+ "dev": "tsup --watch"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^22.0.0",
51
+ "@types/react": "^19.2.14",
52
+ "tsup": "^8.0.0",
53
+ "typescript": "^5.0.0"
54
+ },
55
+ "dependencies": {
56
+ "react": "^19.2.4"
57
+ }
58
+ }