@snappy-stack/sdk 0.1.4 → 0.1.6

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/index.cjs CHANGED
@@ -1,9 +1,87 @@
1
- 'use strict';var jsxRuntime=require('react/jsx-runtime'),f=require('fs'),g=require('path');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var f__default=/*#__PURE__*/_interopDefault(f);var g__default=/*#__PURE__*/_interopDefault(g);var u=class{config;constructor(t){this.config={...t,baseUrl:t.baseUrl||"https://core.wicky.id"};}async fetch(t,n={}){let e=`${this.config.baseUrl}${t}`,o={Authorization:`Bearer ${this.config.token}`,"Content-Type":"application/json",...n.headers},i=await fetch(e,{...n,headers:o});if(i.status===429)throw new Error("SNAPPY_SDK_E429: Too many requests. Rate limit exceeded.");if(!i.ok){let r=await i.json().catch(()=>({error:"Unknown error"}));throw new Error(`SNAPPY_SDK_E${i.status}: ${r.error||i.statusText}`)}return i.json()}collection(t){return {getAll:(n={})=>{let e=new URLSearchParams({locale:n.locale||this.config.locale||"en",status:n.status||"PUBLISHED",limit:(n.limit||20).toString(),offset:(n.offset||0).toString()}).toString();return this.fetch(`/v1/content/${t}?${e}`)},getOne:(n,e)=>{let o=e||this.config.locale||"en";return this.fetch(`/v1/content/${t}/${n}?locale=${o}`)},getBySlug:(n,e)=>{let o=e||this.config.locale||"en";return this.fetch(`/v1/content/${t}/slug/${n}?locale=${o}`)}}}global(t){return {get:n=>{let e=n||this.config.locale||"en";return this.fetch(`/v1/globals/${t}?locale=${e}`)}}}media={getAll:()=>this.fetch("/v1/media"),getOne:t=>this.fetch(`/v1/media/${t}`)};async heartbeat(){return this.fetch("/v1/heartbeat")}async search(t,n){let e=n||this.config.locale||"en";return this.fetch(`/v1/search?q=${encodeURIComponent(t)}&locale=${e}`)}};function N(s){return new u(s)}var E=()=>jsxRuntime.jsx("div",{className:"snappy-credit py-8 mt-12 border-t border-white/5 text-center",children:jsxRuntime.jsxs("p",{className:"text-[10px] font-mono uppercase tracking-[0.3em] text-white/30",children:["Built with"," ",jsxRuntime.jsx("a",{href:"https://wicky.id/snappy",target:"_blank",rel:"noopener noreferrer",className:"text-accent hover:text-accent/80 transition-colors",children:"SNAPPY STACK"})," ","by"," ",jsxRuntime.jsx("a",{href:"https://wicky.id",target:"_blank",rel:"noopener noreferrer",className:"hover:text-white/50 transition-colors",children:"wicky.id"})]})});E.snappyMarker="SK_SNAPPY_V2_CREDIT_VERIFIED";function y(s){let t=x(s),n={hardcodedValues:0,missingAltTags:0,missingMetaTags:{title:false,description:false,ogImage:false},unoptimizedImages:0,totalBuildSize:0},e=/#(?:[0-9a-fA-F]{3}){1,2}\b/g,o=/rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)/g,i=/hsl\(\s*\d+\s*,\s*\d+%\s*,\s*\d+%\s*\)/g,r=/<img\s+[^>]*>/gi,m=/alt=(?:["']|\{)/i;for(let a of t){let l=f__default.default.readFileSync(a,"utf8"),b=g__default.default.extname(a);if([".tsx",".jsx",".css",".astro"].includes(b)){let c=l.replace(/:root\s*{[^}]*}/g,"").replace(/@theme\s*{[^}]*}/g,"");c=c.split(`
2
- `).filter(C=>!C.trim().startsWith("--")).join(`
3
- `);let w=c.match(e)||[],T=c.match(o)||[],R=c.match(i)||[];n.hardcodedValues+=w.length+T.length+R.length;}if([".tsx",".jsx",".astro"].includes(b)){let c=l.match(r)||[];for(let S of c)m.test(S)||n.missingAltTags++;}(a.toLowerCase().includes("layout")||a.toLowerCase().includes("index.astro"))&&(l.includes("<title>")&&(n.missingMetaTags.title=true),(l.includes('name="description"')||l.includes('property="description"'))&&(n.missingMetaTags.description=true),l.includes('property="og:image"')&&(n.missingMetaTags.ogImage=true));}return n}function x(s,t=[]){return f__default.default.existsSync(s)?(f__default.default.readdirSync(s).forEach(e=>{f__default.default.statSync(g__default.default.join(s,e)).isDirectory()?e!=="node_modules"&&e!==".astro"&&(t=x(g__default.default.join(s,e),t)):t.push(g__default.default.join(s,e));}),t):[]}function $(s){let t={performance:.3,accessibility:.2,seo:.2,compliance:.15,optimization:.1,bundle:.05},n=100-s.missingAltTags*10;n=Math.max(0,n);let e=100-s.missingAltTags*20;e=Math.max(0,e);let o=0;s.missingMetaTags.title&&(o+=40),s.missingMetaTags.description&&(o+=40),s.missingMetaTags.ogImage&&(o+=20);let i=100-s.hardcodedValues*5;i=Math.max(0,i);let r=100,m=100;s.totalBuildSize>10*1024*1024&&(m=40);let a=Math.round(n*t.performance+e*t.accessibility+o*t.seo+i*t.compliance+r*t.optimization+m*t.bundle);return {performance:n,accessibility:e,seo:o,compliance:i,optimization:r,bundle:m,total:a}}function V(){return {name:"snappy-credit-enforcer",async buildStart(){let s=g__default.default.resolve(process.cwd(),"src"),t=A(s),n=false;for(let e of t)if(e.endsWith(".astro")||e.endsWith(".tsx")||e.endsWith(".jsx")){let o=f__default.default.readFileSync(e,"utf8");if(o.includes("SnappyCredit")||o.includes("<SnappyCredit")){n=true;break}}if(!n)throw new Error(`
1
+ 'use strict';
4
2
 
5
- \x1B[31m[SNAPPY_SDK_E401]\x1B[0m
6
- Build validation failed: Mandatory <SnappyCredit /> component missing.
3
+ var chunkX7RFIMX7_cjs = require('./chunk-X7RFIMX7.cjs');
4
+ var react = require('react');
5
+ var jsxRuntime = require('react/jsx-runtime');
7
6
 
8
- `)},async buildEnd(){let s=g__default.default.resolve(process.cwd(),"src"),t=y(s),n=$(t);n.total<50&&process.exit(1);let e=process.env.SNAPPY_TOKEN,o=process.env.SNAPPY_URL||"https://core.wicky.id";if(e)try{await fetch(`${o}/v1/projects/self/score`,{method:"PATCH",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({score:n.total})});}catch{}}}}function A(s,t=[]){return f__default.default.existsSync(s)?(f__default.default.readdirSync(s).forEach(e=>{f__default.default.statSync(g__default.default.join(s,e)).isDirectory()?e!=="node_modules"&&e!==".astro"&&(t=A(g__default.default.join(s,e),t)):t.push(g__default.default.join(s,e));}),t):[]}
9
- exports.SnappyClient=u;exports.SnappyCredit=E;exports.createClient=N;exports.snappyCreditEnforcer=V;
7
+ var SnappyCredit = ({ client }) => {
8
+ const [studio, setStudio] = react.useState({ name: "Wicky.ID", url: "https://wicky.id" });
9
+ const [siteName, setSiteName] = react.useState("Project");
10
+ const [isLoading, setIsLoading] = react.useState(true);
11
+ react.useEffect(() => {
12
+ const fetchBranding = async () => {
13
+ const snappy = client || globalThis.snappy;
14
+ if (!snappy) return;
15
+ try {
16
+ setIsLoading(true);
17
+ const [heartbeat, profileResponse] = await Promise.all([
18
+ snappy.heartbeat().catch(() => null),
19
+ snappy.profile.get().catch(() => {
20
+ return snappy.global("site-settings").get().catch(() => null);
21
+ })
22
+ ]);
23
+ const hbData = heartbeat?.data || heartbeat;
24
+ const profData = profileResponse?.data || profileResponse;
25
+ if (hbData) {
26
+ setStudio({
27
+ name: hbData.studio_name || "Wicky.ID",
28
+ url: hbData.studio_url || "https://wicky.id"
29
+ });
30
+ }
31
+ const resolvedName = profData?.site_settings?.site_name || profData?.site_name || hbData?.project_name || "Project";
32
+ setSiteName(resolvedName);
33
+ } catch (err) {
34
+ } finally {
35
+ setIsLoading(false);
36
+ }
37
+ };
38
+ fetchBranding();
39
+ }, [client]);
40
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "snappy-credit text-center", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[10px] font-mono tracking-wide text-white/30 flex items-center justify-center gap-1", children: [
41
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Made with \u2665 and" }),
42
+ /* @__PURE__ */ jsxRuntime.jsx(
43
+ "a",
44
+ {
45
+ href: "https://wicky.id/snappy",
46
+ target: "_blank",
47
+ rel: "noopener noreferrer",
48
+ className: "text-accent hover:text-accent/80 transition-colors px-1",
49
+ children: "Snappy"
50
+ }
51
+ ),
52
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "by" }),
53
+ isLoading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-2 w-16 bg-white/5 animate-pulse rounded mx-1" }) : /* @__PURE__ */ jsxRuntime.jsx(
54
+ "a",
55
+ {
56
+ href: studio.url,
57
+ target: "_blank",
58
+ rel: "noopener noreferrer",
59
+ className: "hover:text-white/50 transition-colors px-1 underline-offset-4 hover:underline",
60
+ children: studio.name
61
+ }
62
+ ),
63
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "for" }),
64
+ isLoading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-2 w-24 bg-white/5 animate-pulse rounded ml-1" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-white/60 ml-1", children: siteName })
65
+ ] }) });
66
+ };
67
+ SnappyCredit.snappyMarker = "SK_SNAPPY_V2_CREDIT_VERIFIED";
68
+
69
+ Object.defineProperty(exports, "SnappyClient", {
70
+ enumerable: true,
71
+ get: function () { return chunkX7RFIMX7_cjs.SnappyClient; }
72
+ });
73
+ Object.defineProperty(exports, "SnappyPublicClient", {
74
+ enumerable: true,
75
+ get: function () { return chunkX7RFIMX7_cjs.SnappyPublicClient; }
76
+ });
77
+ Object.defineProperty(exports, "createClient", {
78
+ enumerable: true,
79
+ get: function () { return chunkX7RFIMX7_cjs.createClient; }
80
+ });
81
+ Object.defineProperty(exports, "createPublicClient", {
82
+ enumerable: true,
83
+ get: function () { return chunkX7RFIMX7_cjs.createPublicClient; }
84
+ });
85
+ exports.SnappyCredit = SnappyCredit;
86
+ //# sourceMappingURL=index.cjs.map
87
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Credit.tsx"],"names":["useState","useEffect","jsxs","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import React, { useState, useEffect } from 'react';\n\nexport interface SnappyCreditProps {\n client?: any; // Avoiding circular dependency, usually passed from the app\n}\n\nexport const SnappyCredit: React.FC<SnappyCreditProps> = ({ client }) => {\n const [studio, setStudio] = useState({ name: 'Wicky.ID', url: 'https://wicky.id' });\n const [siteName, setSiteName] = useState('Project');\n const [isLoading, setIsLoading] = useState(true);\n\n useEffect(() => {\n const fetchBranding = async () => {\n // Use provided client or global snappy if available\n const snappy = client || (globalThis as any).snappy;\n if (!snappy) return;\n\n try {\n setIsLoading(true);\n \n // 1. Fetch heartbeat and profile with silent fail/fallback\n const [heartbeat, profileResponse] = await Promise.all([\n snappy.heartbeat().catch(() => null),\n snappy.profile.get().catch(() => {\n // Fallback: try global if profile fails (backwards compatibility)\n return snappy.global('site-settings').get().catch(() => null);\n })\n ]);\n\n const hbData = heartbeat?.data || heartbeat;\n const profData = profileResponse?.data || profileResponse;\n\n if (hbData) {\n setStudio({\n name: hbData.studio_name || 'Wicky.ID',\n url: hbData.studio_url || 'https://wicky.id'\n });\n }\n\n const resolvedName = profData?.site_settings?.site_name || \n profData?.site_name || \n hbData?.project_name || \n 'Project';\n \n setSiteName(resolvedName);\n \n } catch (err: any) {\n // Silently use defaults\n } finally {\n setIsLoading(false);\n }\n };\n\n fetchBranding();\n }, [client]);\n\n return (\n <div className=\"snappy-credit text-center\">\n <p className=\"text-[10px] font-mono tracking-wide text-white/30 flex items-center justify-center gap-1\">\n <span>Made with ♄ and</span>\n <a \n href=\"https://wicky.id/snappy\" \n target=\"_blank\" \n rel=\"noopener noreferrer\"\n className=\"text-accent hover:text-accent/80 transition-colors px-1\"\n >\n Snappy\n </a>\n <span>by</span>\n {isLoading ? (\n <span className=\"h-2 w-16 bg-white/5 animate-pulse rounded mx-1\" />\n ) : (\n <a \n href={studio.url} \n target=\"_blank\" \n rel=\"noopener noreferrer\"\n className=\"hover:text-white/50 transition-colors px-1 underline-offset-4 hover:underline\"\n >\n {studio.name}\n </a>\n )}\n <span>for</span>\n {isLoading ? (\n <span className=\"h-2 w-24 bg-white/5 animate-pulse rounded ml-1\" />\n ) : (\n <span className=\"text-white/60 ml-1\">{siteName}</span>\n )}\n </p>\n </div>\n );\n};\n\n// Hidden marker for build-time enforcement\n(SnappyCredit as any).snappyMarker = 'SK_SNAPPY_V2_CREDIT_VERIFIED';\n"]}
package/dist/index.d.cts CHANGED
@@ -1,42 +1,9 @@
1
+ export { E as Entry, G as Global, H as Heartbeat, M as Media, P as Project, R as Review, a as ReviewStatus, S as SiteSettings, b as SnappyClient, c as SnappyConfig, d as SnappyList, e as SnappyOne, f as SnappyProfile, g as SnappyPublicClient, h as Social, U as User, i as createClient, j as createPublicClient } from './client-FIWQgwNq.cjs';
1
2
  import React from 'react';
2
3
 
3
- interface SnappyConfig {
4
- token: string;
5
- baseUrl?: string;
6
- locale?: string;
4
+ interface SnappyCreditProps {
5
+ client?: any;
7
6
  }
8
- declare class SnappyClient {
9
- private config;
10
- constructor(config: SnappyConfig);
11
- private fetch;
12
- collection(slug: string): {
13
- getAll: (params?: {
14
- locale?: string;
15
- status?: string;
16
- limit?: number;
17
- offset?: number;
18
- }) => Promise<any>;
19
- getOne: (id: string, locale?: string) => Promise<any>;
20
- getBySlug: (entrySlug: string, locale?: string) => Promise<any>;
21
- };
22
- global(slug: string): {
23
- get: (locale?: string) => Promise<any>;
24
- };
25
- media: {
26
- getAll: () => Promise<any>;
27
- getOne: (id: string) => Promise<any>;
28
- };
29
- heartbeat(): Promise<any>;
30
- search(query: string, locale?: string): Promise<any>;
31
- }
32
- declare function createClient(config: SnappyConfig): SnappyClient;
33
-
34
- declare const SnappyCredit: React.FC;
35
-
36
- declare function snappyCreditEnforcer(): {
37
- name: string;
38
- buildStart(): Promise<void>;
39
- buildEnd(): Promise<void>;
40
- };
7
+ declare const SnappyCredit: React.FC<SnappyCreditProps>;
41
8
 
42
- export { SnappyClient, type SnappyConfig, SnappyCredit, createClient, snappyCreditEnforcer };
9
+ export { SnappyCredit, type SnappyCreditProps };
package/dist/index.d.ts CHANGED
@@ -1,42 +1,9 @@
1
+ export { E as Entry, G as Global, H as Heartbeat, M as Media, P as Project, R as Review, a as ReviewStatus, S as SiteSettings, b as SnappyClient, c as SnappyConfig, d as SnappyList, e as SnappyOne, f as SnappyProfile, g as SnappyPublicClient, h as Social, U as User, i as createClient, j as createPublicClient } from './client-FIWQgwNq.js';
1
2
  import React from 'react';
2
3
 
3
- interface SnappyConfig {
4
- token: string;
5
- baseUrl?: string;
6
- locale?: string;
4
+ interface SnappyCreditProps {
5
+ client?: any;
7
6
  }
8
- declare class SnappyClient {
9
- private config;
10
- constructor(config: SnappyConfig);
11
- private fetch;
12
- collection(slug: string): {
13
- getAll: (params?: {
14
- locale?: string;
15
- status?: string;
16
- limit?: number;
17
- offset?: number;
18
- }) => Promise<any>;
19
- getOne: (id: string, locale?: string) => Promise<any>;
20
- getBySlug: (entrySlug: string, locale?: string) => Promise<any>;
21
- };
22
- global(slug: string): {
23
- get: (locale?: string) => Promise<any>;
24
- };
25
- media: {
26
- getAll: () => Promise<any>;
27
- getOne: (id: string) => Promise<any>;
28
- };
29
- heartbeat(): Promise<any>;
30
- search(query: string, locale?: string): Promise<any>;
31
- }
32
- declare function createClient(config: SnappyConfig): SnappyClient;
33
-
34
- declare const SnappyCredit: React.FC;
35
-
36
- declare function snappyCreditEnforcer(): {
37
- name: string;
38
- buildStart(): Promise<void>;
39
- buildEnd(): Promise<void>;
40
- };
7
+ declare const SnappyCredit: React.FC<SnappyCreditProps>;
41
8
 
42
- export { SnappyClient, type SnappyConfig, SnappyCredit, createClient, snappyCreditEnforcer };
9
+ export { SnappyCredit, type SnappyCreditProps };
package/dist/index.js CHANGED
@@ -1,9 +1,69 @@
1
- import {jsx,jsxs}from'react/jsx-runtime';import f from'fs';import g from'path';var u=class{config;constructor(t){this.config={...t,baseUrl:t.baseUrl||"https://core.wicky.id"};}async fetch(t,n={}){let e=`${this.config.baseUrl}${t}`,o={Authorization:`Bearer ${this.config.token}`,"Content-Type":"application/json",...n.headers},i=await fetch(e,{...n,headers:o});if(i.status===429)throw new Error("SNAPPY_SDK_E429: Too many requests. Rate limit exceeded.");if(!i.ok){let r=await i.json().catch(()=>({error:"Unknown error"}));throw new Error(`SNAPPY_SDK_E${i.status}: ${r.error||i.statusText}`)}return i.json()}collection(t){return {getAll:(n={})=>{let e=new URLSearchParams({locale:n.locale||this.config.locale||"en",status:n.status||"PUBLISHED",limit:(n.limit||20).toString(),offset:(n.offset||0).toString()}).toString();return this.fetch(`/v1/content/${t}?${e}`)},getOne:(n,e)=>{let o=e||this.config.locale||"en";return this.fetch(`/v1/content/${t}/${n}?locale=${o}`)},getBySlug:(n,e)=>{let o=e||this.config.locale||"en";return this.fetch(`/v1/content/${t}/slug/${n}?locale=${o}`)}}}global(t){return {get:n=>{let e=n||this.config.locale||"en";return this.fetch(`/v1/globals/${t}?locale=${e}`)}}}media={getAll:()=>this.fetch("/v1/media"),getOne:t=>this.fetch(`/v1/media/${t}`)};async heartbeat(){return this.fetch("/v1/heartbeat")}async search(t,n){let e=n||this.config.locale||"en";return this.fetch(`/v1/search?q=${encodeURIComponent(t)}&locale=${e}`)}};function N(s){return new u(s)}var E=()=>jsx("div",{className:"snappy-credit py-8 mt-12 border-t border-white/5 text-center",children:jsxs("p",{className:"text-[10px] font-mono uppercase tracking-[0.3em] text-white/30",children:["Built with"," ",jsx("a",{href:"https://wicky.id/snappy",target:"_blank",rel:"noopener noreferrer",className:"text-accent hover:text-accent/80 transition-colors",children:"SNAPPY STACK"})," ","by"," ",jsx("a",{href:"https://wicky.id",target:"_blank",rel:"noopener noreferrer",className:"hover:text-white/50 transition-colors",children:"wicky.id"})]})});E.snappyMarker="SK_SNAPPY_V2_CREDIT_VERIFIED";function y(s){let t=x(s),n={hardcodedValues:0,missingAltTags:0,missingMetaTags:{title:false,description:false,ogImage:false},unoptimizedImages:0,totalBuildSize:0},e=/#(?:[0-9a-fA-F]{3}){1,2}\b/g,o=/rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)/g,i=/hsl\(\s*\d+\s*,\s*\d+%\s*,\s*\d+%\s*\)/g,r=/<img\s+[^>]*>/gi,m=/alt=(?:["']|\{)/i;for(let a of t){let l=f.readFileSync(a,"utf8"),b=g.extname(a);if([".tsx",".jsx",".css",".astro"].includes(b)){let c=l.replace(/:root\s*{[^}]*}/g,"").replace(/@theme\s*{[^}]*}/g,"");c=c.split(`
2
- `).filter(C=>!C.trim().startsWith("--")).join(`
3
- `);let w=c.match(e)||[],T=c.match(o)||[],R=c.match(i)||[];n.hardcodedValues+=w.length+T.length+R.length;}if([".tsx",".jsx",".astro"].includes(b)){let c=l.match(r)||[];for(let S of c)m.test(S)||n.missingAltTags++;}(a.toLowerCase().includes("layout")||a.toLowerCase().includes("index.astro"))&&(l.includes("<title>")&&(n.missingMetaTags.title=true),(l.includes('name="description"')||l.includes('property="description"'))&&(n.missingMetaTags.description=true),l.includes('property="og:image"')&&(n.missingMetaTags.ogImage=true));}return n}function x(s,t=[]){return f.existsSync(s)?(f.readdirSync(s).forEach(e=>{f.statSync(g.join(s,e)).isDirectory()?e!=="node_modules"&&e!==".astro"&&(t=x(g.join(s,e),t)):t.push(g.join(s,e));}),t):[]}function $(s){let t={performance:.3,accessibility:.2,seo:.2,compliance:.15,optimization:.1,bundle:.05},n=100-s.missingAltTags*10;n=Math.max(0,n);let e=100-s.missingAltTags*20;e=Math.max(0,e);let o=0;s.missingMetaTags.title&&(o+=40),s.missingMetaTags.description&&(o+=40),s.missingMetaTags.ogImage&&(o+=20);let i=100-s.hardcodedValues*5;i=Math.max(0,i);let r=100,m=100;s.totalBuildSize>10*1024*1024&&(m=40);let a=Math.round(n*t.performance+e*t.accessibility+o*t.seo+i*t.compliance+r*t.optimization+m*t.bundle);return {performance:n,accessibility:e,seo:o,compliance:i,optimization:r,bundle:m,total:a}}function V(){return {name:"snappy-credit-enforcer",async buildStart(){let s=g.resolve(process.cwd(),"src"),t=A(s),n=false;for(let e of t)if(e.endsWith(".astro")||e.endsWith(".tsx")||e.endsWith(".jsx")){let o=f.readFileSync(e,"utf8");if(o.includes("SnappyCredit")||o.includes("<SnappyCredit")){n=true;break}}if(!n)throw new Error(`
1
+ export { SnappyClient, SnappyPublicClient, createClient, createPublicClient } from './chunk-TLDZ32ME.js';
2
+ import { useState, useEffect } from 'react';
3
+ import { jsx, jsxs } from 'react/jsx-runtime';
4
4
 
5
- \x1B[31m[SNAPPY_SDK_E401]\x1B[0m
6
- Build validation failed: Mandatory <SnappyCredit /> component missing.
5
+ var SnappyCredit = ({ client }) => {
6
+ const [studio, setStudio] = useState({ name: "Wicky.ID", url: "https://wicky.id" });
7
+ const [siteName, setSiteName] = useState("Project");
8
+ const [isLoading, setIsLoading] = useState(true);
9
+ useEffect(() => {
10
+ const fetchBranding = async () => {
11
+ const snappy = client || globalThis.snappy;
12
+ if (!snappy) return;
13
+ try {
14
+ setIsLoading(true);
15
+ const [heartbeat, profileResponse] = await Promise.all([
16
+ snappy.heartbeat().catch(() => null),
17
+ snappy.profile.get().catch(() => {
18
+ return snappy.global("site-settings").get().catch(() => null);
19
+ })
20
+ ]);
21
+ const hbData = heartbeat?.data || heartbeat;
22
+ const profData = profileResponse?.data || profileResponse;
23
+ if (hbData) {
24
+ setStudio({
25
+ name: hbData.studio_name || "Wicky.ID",
26
+ url: hbData.studio_url || "https://wicky.id"
27
+ });
28
+ }
29
+ const resolvedName = profData?.site_settings?.site_name || profData?.site_name || hbData?.project_name || "Project";
30
+ setSiteName(resolvedName);
31
+ } catch (err) {
32
+ } finally {
33
+ setIsLoading(false);
34
+ }
35
+ };
36
+ fetchBranding();
37
+ }, [client]);
38
+ return /* @__PURE__ */ jsx("div", { className: "snappy-credit text-center", children: /* @__PURE__ */ jsxs("p", { className: "text-[10px] font-mono tracking-wide text-white/30 flex items-center justify-center gap-1", children: [
39
+ /* @__PURE__ */ jsx("span", { children: "Made with \u2665 and" }),
40
+ /* @__PURE__ */ jsx(
41
+ "a",
42
+ {
43
+ href: "https://wicky.id/snappy",
44
+ target: "_blank",
45
+ rel: "noopener noreferrer",
46
+ className: "text-accent hover:text-accent/80 transition-colors px-1",
47
+ children: "Snappy"
48
+ }
49
+ ),
50
+ /* @__PURE__ */ jsx("span", { children: "by" }),
51
+ isLoading ? /* @__PURE__ */ jsx("span", { className: "h-2 w-16 bg-white/5 animate-pulse rounded mx-1" }) : /* @__PURE__ */ jsx(
52
+ "a",
53
+ {
54
+ href: studio.url,
55
+ target: "_blank",
56
+ rel: "noopener noreferrer",
57
+ className: "hover:text-white/50 transition-colors px-1 underline-offset-4 hover:underline",
58
+ children: studio.name
59
+ }
60
+ ),
61
+ /* @__PURE__ */ jsx("span", { children: "for" }),
62
+ isLoading ? /* @__PURE__ */ jsx("span", { className: "h-2 w-24 bg-white/5 animate-pulse rounded ml-1" }) : /* @__PURE__ */ jsx("span", { className: "text-white/60 ml-1", children: siteName })
63
+ ] }) });
64
+ };
65
+ SnappyCredit.snappyMarker = "SK_SNAPPY_V2_CREDIT_VERIFIED";
7
66
 
8
- `)},async buildEnd(){let s=g.resolve(process.cwd(),"src"),t=y(s),n=$(t);n.total<50&&process.exit(1);let e=process.env.SNAPPY_TOKEN,o=process.env.SNAPPY_URL||"https://core.wicky.id";if(e)try{await fetch(`${o}/v1/projects/self/score`,{method:"PATCH",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({score:n.total})});}catch{}}}}function A(s,t=[]){return f.existsSync(s)?(f.readdirSync(s).forEach(e=>{f.statSync(g.join(s,e)).isDirectory()?e!=="node_modules"&&e!==".astro"&&(t=A(g.join(s,e),t)):t.push(g.join(s,e));}),t):[]}
9
- export{u as SnappyClient,E as SnappyCredit,N as createClient,V as snappyCreditEnforcer};
67
+ export { SnappyCredit };
68
+ //# sourceMappingURL=index.js.map
69
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Credit.tsx"],"names":[],"mappings":";;;;AAMO,IAAM,YAAA,GAA4C,CAAC,EAAE,MAAA,EAAO,KAAM;AACvE,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,QAAA,CAAS,EAAE,IAAA,EAAM,UAAA,EAAY,GAAA,EAAK,kBAAA,EAAoB,CAAA;AAClF,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,SAAS,CAAA;AAClD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,IAAI,CAAA;AAE/C,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,gBAAgB,YAAY;AAEhC,MAAA,MAAM,MAAA,GAAS,UAAW,UAAA,CAAmB,MAAA;AAC7C,MAAA,IAAI,CAAC,MAAA,EAAQ;AAEb,MAAA,IAAI;AACF,QAAA,YAAA,CAAa,IAAI,CAAA;AAGjB,QAAA,MAAM,CAAC,SAAA,EAAW,eAAe,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,UACrD,MAAA,CAAO,SAAA,EAAU,CAAE,KAAA,CAAM,MAAM,IAAI,CAAA;AAAA,UACnC,MAAA,CAAO,OAAA,CAAQ,GAAA,EAAI,CAAE,MAAM,MAAM;AAE/B,YAAA,OAAO,MAAA,CAAO,OAAO,eAAe,CAAA,CAAE,KAAI,CAAE,KAAA,CAAM,MAAM,IAAI,CAAA;AAAA,UAC9D,CAAC;AAAA,SACF,CAAA;AAED,QAAA,MAAM,MAAA,GAAS,WAAW,IAAA,IAAQ,SAAA;AAClC,QAAA,MAAM,QAAA,GAAW,iBAAiB,IAAA,IAAQ,eAAA;AAE1C,QAAA,IAAI,MAAA,EAAQ;AACV,UAAA,SAAA,CAAU;AAAA,YACR,IAAA,EAAM,OAAO,WAAA,IAAe,UAAA;AAAA,YAC5B,GAAA,EAAK,OAAO,UAAA,IAAc;AAAA,WAC3B,CAAA;AAAA,QACH;AAEA,QAAA,MAAM,eAAe,QAAA,EAAU,aAAA,EAAe,aAC1B,QAAA,EAAU,SAAA,IACV,QAAQ,YAAA,IACR,SAAA;AAEpB,QAAA,WAAA,CAAY,YAAY,CAAA;AAAA,MAE1B,SAAS,GAAA,EAAU;AAAA,MAEnB,CAAA,SAAE;AACA,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAAA,IACF,CAAA;AAEA,IAAA,aAAA,EAAc;AAAA,EAChB,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,EAAA,2BACG,KAAA,EAAA,EAAI,SAAA,EAAU,6BACb,QAAA,kBAAA,IAAA,CAAC,GAAA,EAAA,EAAE,WAAU,0FAAA,EACX,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,UAAK,QAAA,EAAA,sBAAA,EAAe,CAAA;AAAA,oBACrB,GAAA;AAAA,MAAC,GAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,yBAAA;AAAA,QACL,MAAA,EAAO,QAAA;AAAA,QACP,GAAA,EAAI,qBAAA;AAAA,QACJ,SAAA,EAAU,yDAAA;AAAA,QACX,QAAA,EAAA;AAAA;AAAA,KAED;AAAA,oBACA,GAAA,CAAC,UAAK,QAAA,EAAA,IAAA,EAAE,CAAA;AAAA,IACP,SAAA,mBACC,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,kDAAiD,CAAA,mBAEjE,GAAA;AAAA,MAAC,GAAA;AAAA,MAAA;AAAA,QACC,MAAM,MAAA,CAAO,GAAA;AAAA,QACb,MAAA,EAAO,QAAA;AAAA,QACP,GAAA,EAAI,qBAAA;AAAA,QACJ,SAAA,EAAU,+EAAA;AAAA,QAET,QAAA,EAAA,MAAA,CAAO;AAAA;AAAA,KACV;AAAA,oBAEF,GAAA,CAAC,UAAK,QAAA,EAAA,KAAA,EAAG,CAAA;AAAA,IACR,SAAA,mBACC,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,gDAAA,EAAiD,oBAEjE,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,oBAAA,EAAsB,QAAA,EAAA,QAAA,EAAS;AAAA,GAAA,EAEnD,CAAA,EACF,CAAA;AAEJ;AAGC,YAAA,CAAqB,YAAA,GAAe,8BAAA","file":"index.js","sourcesContent":["import React, { useState, useEffect } from 'react';\n\nexport interface SnappyCreditProps {\n client?: any; // Avoiding circular dependency, usually passed from the app\n}\n\nexport const SnappyCredit: React.FC<SnappyCreditProps> = ({ client }) => {\n const [studio, setStudio] = useState({ name: 'Wicky.ID', url: 'https://wicky.id' });\n const [siteName, setSiteName] = useState('Project');\n const [isLoading, setIsLoading] = useState(true);\n\n useEffect(() => {\n const fetchBranding = async () => {\n // Use provided client or global snappy if available\n const snappy = client || (globalThis as any).snappy;\n if (!snappy) return;\n\n try {\n setIsLoading(true);\n \n // 1. Fetch heartbeat and profile with silent fail/fallback\n const [heartbeat, profileResponse] = await Promise.all([\n snappy.heartbeat().catch(() => null),\n snappy.profile.get().catch(() => {\n // Fallback: try global if profile fails (backwards compatibility)\n return snappy.global('site-settings').get().catch(() => null);\n })\n ]);\n\n const hbData = heartbeat?.data || heartbeat;\n const profData = profileResponse?.data || profileResponse;\n\n if (hbData) {\n setStudio({\n name: hbData.studio_name || 'Wicky.ID',\n url: hbData.studio_url || 'https://wicky.id'\n });\n }\n\n const resolvedName = profData?.site_settings?.site_name || \n profData?.site_name || \n hbData?.project_name || \n 'Project';\n \n setSiteName(resolvedName);\n \n } catch (err: any) {\n // Silently use defaults\n } finally {\n setIsLoading(false);\n }\n };\n\n fetchBranding();\n }, [client]);\n\n return (\n <div className=\"snappy-credit text-center\">\n <p className=\"text-[10px] font-mono tracking-wide text-white/30 flex items-center justify-center gap-1\">\n <span>Made with ♄ and</span>\n <a \n href=\"https://wicky.id/snappy\" \n target=\"_blank\" \n rel=\"noopener noreferrer\"\n className=\"text-accent hover:text-accent/80 transition-colors px-1\"\n >\n Snappy\n </a>\n <span>by</span>\n {isLoading ? (\n <span className=\"h-2 w-16 bg-white/5 animate-pulse rounded mx-1\" />\n ) : (\n <a \n href={studio.url} \n target=\"_blank\" \n rel=\"noopener noreferrer\"\n className=\"hover:text-white/50 transition-colors px-1 underline-offset-4 hover:underline\"\n >\n {studio.name}\n </a>\n )}\n <span>for</span>\n {isLoading ? (\n <span className=\"h-2 w-24 bg-white/5 animate-pulse rounded ml-1\" />\n ) : (\n <span className=\"text-white/60 ml-1\">{siteName}</span>\n )}\n </p>\n </div>\n );\n};\n\n// Hidden marker for build-time enforcement\n(SnappyCredit as any).snappyMarker = 'SK_SNAPPY_V2_CREDIT_VERIFIED';\n"]}
@@ -0,0 +1,209 @@
1
+ 'use strict';
2
+
3
+ var chunkX7RFIMX7_cjs = require('./chunk-X7RFIMX7.cjs');
4
+ var fs = require('fs');
5
+ var path2 = require('path');
6
+
7
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
+
9
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
10
+ var path2__default = /*#__PURE__*/_interopDefault(path2);
11
+
12
+ function scanSource(srcDir) {
13
+ const files = getAllFiles(srcDir);
14
+ const result = {
15
+ hardcodedValues: 0,
16
+ missingAltTags: 0,
17
+ missingMetaTags: { title: false, description: false, ogImage: false },
18
+ unoptimizedImages: 0,
19
+ totalBuildSize: 0
20
+ };
21
+ const hexRegex = /#(?:[0-9a-fA-F]{3}){1,2}\b/g;
22
+ const rgbRegex = /rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)/g;
23
+ const hslRegex = /hsl\(\s*\d+\s*,\s*\d+%\s*,\s*\d+%\s*\)/g;
24
+ const imgTagRegex = /<img\s+[^>]*>/gi;
25
+ const altAttrRegex = /alt=(?:["']|\{)/i;
26
+ for (const file of files) {
27
+ const content = fs__default.default.readFileSync(file, "utf8");
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)) {
31
+ let scanContent = content.replace(/:root\s*{[^}]*}/g, "").replace(/@theme\s*{[^}]*}/g, "");
32
+ const lines = scanContent.split("\n");
33
+ const filteredLines = lines.filter((line) => !line.trim().startsWith("--"));
34
+ scanContent = filteredLines.join("\n");
35
+ const hexMatches = scanContent.match(hexRegex) || [];
36
+ const rgbMatches = scanContent.match(rgbRegex) || [];
37
+ const hslMatches = scanContent.match(hslRegex) || [];
38
+ result.hardcodedValues += hexMatches.length + rgbMatches.length + hslMatches.length;
39
+ }
40
+ if ([".tsx", ".jsx", ".astro"].includes(ext)) {
41
+ const imgTags = content.match(imgTagRegex) || [];
42
+ for (const tag of imgTags) {
43
+ if (!altAttrRegex.test(tag)) {
44
+ result.missingAltTags++;
45
+ }
46
+ }
47
+ }
48
+ if (file.toLowerCase().includes("layout") || file.toLowerCase().includes("index.astro")) {
49
+ if (content.includes("<title>") || content.includes("<SEO")) result.missingMetaTags.title = true;
50
+ if (content.includes('name="description"') || content.includes('property="description"') || content.includes("<SEO")) {
51
+ result.missingMetaTags.description = true;
52
+ }
53
+ if (content.includes('property="og:image"') || content.includes("<SEO")) result.missingMetaTags.ogImage = true;
54
+ }
55
+ }
56
+ return result;
57
+ }
58
+ function getAllFiles(dirPath, arrayOfFiles = []) {
59
+ if (!fs__default.default.existsSync(dirPath)) return [];
60
+ const files = fs__default.default.readdirSync(dirPath);
61
+ files.forEach((file) => {
62
+ if (fs__default.default.statSync(path2__default.default.join(dirPath, file)).isDirectory()) {
63
+ if (file !== "node_modules" && file !== ".astro") {
64
+ arrayOfFiles = getAllFiles(path2__default.default.join(dirPath, file), arrayOfFiles);
65
+ }
66
+ } else {
67
+ arrayOfFiles.push(path2__default.default.join(dirPath, file));
68
+ }
69
+ });
70
+ return arrayOfFiles;
71
+ }
72
+
73
+ // src/audit/scorer.ts
74
+ function calculateScore(result) {
75
+ const weights = {
76
+ performance: 0.3,
77
+ accessibility: 0.2,
78
+ seo: 0.2,
79
+ compliance: 0.15,
80
+ optimization: 0.1,
81
+ bundle: 0.05
82
+ };
83
+ let perf = 100 - result.missingAltTags * 10;
84
+ perf = Math.max(0, perf);
85
+ let a11y = 100 - result.missingAltTags * 20;
86
+ a11y = Math.max(0, a11y);
87
+ let seo = 0;
88
+ if (result.missingMetaTags.title) seo += 40;
89
+ if (result.missingMetaTags.description) seo += 40;
90
+ if (result.missingMetaTags.ogImage) seo += 20;
91
+ let compliance = 100 - result.hardcodedValues * 5;
92
+ compliance = Math.max(0, compliance);
93
+ let optimization = 100;
94
+ let bundle = 100;
95
+ if (result.totalBuildSize > 10 * 1024 * 1024) bundle = 40;
96
+ const total = Math.round(
97
+ perf * weights.performance + a11y * weights.accessibility + seo * weights.seo + compliance * weights.compliance + optimization * weights.optimization + bundle * weights.bundle
98
+ );
99
+ return {
100
+ performance: perf,
101
+ accessibility: a11y,
102
+ seo,
103
+ compliance,
104
+ optimization,
105
+ bundle,
106
+ total
107
+ };
108
+ }
109
+
110
+ // src/plugin.ts
111
+ function snappyCreditEnforcer() {
112
+ return {
113
+ name: "snappy-credit-enforcer",
114
+ async buildStart() {
115
+ const srcDir = path2__default.default.resolve(process.cwd(), "src");
116
+ const files = getAllFiles2(srcDir);
117
+ let hasCredit = false;
118
+ for (const file of files) {
119
+ if (file.endsWith(".astro") || file.endsWith(".tsx") || file.endsWith(".jsx")) {
120
+ const content = fs__default.default.readFileSync(file, "utf8");
121
+ if (content.includes("SnappyCredit") || content.includes("<SnappyCredit")) {
122
+ hasCredit = true;
123
+ break;
124
+ }
125
+ }
126
+ }
127
+ if (!hasCredit) {
128
+ throw new Error(
129
+ "\n\n\x1B[31m[SNAPPY_SDK_E401]\x1B[0m\nBuild validation failed: Mandatory <SnappyCredit /> component missing.\n\n"
130
+ );
131
+ }
132
+ },
133
+ async buildEnd() {
134
+ const srcDir = path2__default.default.resolve(process.cwd(), "src");
135
+ const auditResult = scanSource(srcDir);
136
+ const score = calculateScore(auditResult);
137
+ printReport(score);
138
+ if (score.total < 50) {
139
+ console.error("\n\x1B[31m\u{1F479} BLOCKED:\x1B[0m SNAPPY Score is below the quality threshold (50).");
140
+ console.error("Ship clean or don't ship at all. \u{1F52B}\u{1F6E1}\uFE0F\u{1F493}\n");
141
+ process.exit(1);
142
+ }
143
+ const token = process.env.SNAPPY_TOKEN;
144
+ const baseUrl = process.env.SNAPPY_URL || "https://core.wicky.id";
145
+ if (token) {
146
+ try {
147
+ await fetch(`${baseUrl}/v1/projects/self/score`, {
148
+ method: "PATCH",
149
+ headers: {
150
+ "Authorization": `Bearer ${token}`,
151
+ "Content-Type": "application/json"
152
+ },
153
+ body: JSON.stringify({ score: score.total })
154
+ });
155
+ } catch (e) {
156
+ }
157
+ }
158
+ }
159
+ };
160
+ }
161
+ function printReport(s) {
162
+ const reset = "\x1B[0m";
163
+ const green = "\x1B[32m";
164
+ const yellow = "\x1B[33m";
165
+ const red = "\x1B[31m";
166
+ const cyan = "\x1B[36m";
167
+ const status = s.total >= 70 ? `${green}\u2705 PASS${reset}` : s.total >= 50 ? `${yellow}\u26A0\uFE0F WARNING${reset}` : `${red}\u{1F479} BLOCKED${reset}`;
168
+ console.log(`
169
+ ${cyan}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
170
+ \u2551 SNAPPY SCORE REPORT \u2551
171
+ \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563${reset}
172
+ \u2551 Performance ${fmt(s.performance)} 30% \u2551
173
+ \u2551 Accessibility ${fmt(s.accessibility)} 20% \u2551
174
+ \u2551 SEO Completeness ${fmt(s.seo)} 20% \u2551
175
+ \u2551 Zero Hardcoded ${fmt(s.compliance)} 15% \u2551
176
+ \u2551 Image Optimization ${fmt(s.optimization)} 10% \u2551
177
+ \u2551 Bundle Size ${fmt(s.bundle)} 5% \u2551
178
+ ${cyan}\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563${reset}
179
+ \u2551 TOTAL SCORE: ${s.total}/100 \u2551
180
+ \u2551 STATUS: ${status} \u2551
181
+ ${cyan}\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D${reset}
182
+ `);
183
+ }
184
+ function fmt(n) {
185
+ const str = `${n}/100`;
186
+ return str.padEnd(8, " ");
187
+ }
188
+ function getAllFiles2(dirPath, arrayOfFiles = []) {
189
+ if (!fs__default.default.existsSync(dirPath)) return [];
190
+ const files = fs__default.default.readdirSync(dirPath);
191
+ files.forEach((file) => {
192
+ if (fs__default.default.statSync(path2__default.default.join(dirPath, file)).isDirectory()) {
193
+ if (file !== "node_modules" && file !== ".astro") {
194
+ arrayOfFiles = getAllFiles2(path2__default.default.join(dirPath, file), arrayOfFiles);
195
+ }
196
+ } else {
197
+ arrayOfFiles.push(path2__default.default.join(dirPath, file));
198
+ }
199
+ });
200
+ return arrayOfFiles;
201
+ }
202
+
203
+ Object.defineProperty(exports, "createPublicClient", {
204
+ enumerable: true,
205
+ get: function () { return chunkX7RFIMX7_cjs.createPublicClient; }
206
+ });
207
+ exports.snappyCreditEnforcer = snappyCreditEnforcer;
208
+ //# sourceMappingURL=plugin.cjs.map
209
+ //# sourceMappingURL=plugin.cjs.map
@@ -0,0 +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"]}
@@ -0,0 +1,9 @@
1
+ export { j as createPublicClient } from './client-FIWQgwNq.cjs';
2
+
3
+ declare function snappyCreditEnforcer(): {
4
+ name: string;
5
+ buildStart(): Promise<void>;
6
+ buildEnd(): Promise<void>;
7
+ };
8
+
9
+ export { snappyCreditEnforcer };
@@ -0,0 +1,9 @@
1
+ export { j as createPublicClient } from './client-FIWQgwNq.js';
2
+
3
+ declare function snappyCreditEnforcer(): {
4
+ name: string;
5
+ buildStart(): Promise<void>;
6
+ buildEnd(): Promise<void>;
7
+ };
8
+
9
+ export { snappyCreditEnforcer };