@pixelated-tech/components 3.7.5 → 3.7.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/components/config/config.js +37 -4
- package/dist/components/config/config.server.js +1 -0
- package/dist/components/config/crypto.js +62 -0
- package/dist/components/general/google.reviews.components.js +2 -1
- package/dist/components/general/menu-expando.js +2 -1
- package/dist/components/general/proxy-handler.js +57 -0
- package/dist/components/general/smartimage.js +3 -1
- package/dist/components/shoppingcart/shoppingcart.components.js +1 -1
- package/dist/components/utilities/functions.js +2 -0
- package/dist/index.js +0 -2
- package/dist/index.server.js +2 -0
- package/dist/scripts/config-vault.js +51 -0
- package/dist/scripts/config-vault.ts +55 -0
- package/dist/scripts/{proptypes-inferprops.js → pixelated-eslint-plugin.js} +238 -3
- package/dist/scripts/release.sh +17 -1
- package/dist/types/components/config/config.d.ts +1 -1
- package/dist/types/components/config/config.d.ts.map +1 -1
- package/dist/types/components/config/config.server.d.ts.map +1 -1
- package/dist/types/components/config/crypto.d.ts +15 -0
- package/dist/types/components/config/crypto.d.ts.map +1 -0
- package/dist/types/components/general/google.reviews.components.d.ts.map +1 -1
- package/dist/types/components/general/menu-expando.d.ts.map +1 -1
- package/dist/types/components/general/proxy-handler.d.ts +20 -0
- package/dist/types/components/general/proxy-handler.d.ts.map +1 -0
- package/dist/types/components/general/smartimage.d.ts.map +1 -1
- package/dist/types/components/shoppingcart/shoppingcart.components.d.ts.map +1 -1
- package/dist/types/components/utilities/functions.d.ts.map +1 -1
- package/dist/types/index.d.ts +0 -2
- package/dist/types/index.server.d.ts +2 -0
- package/dist/types/scripts/config-vault.d.ts +3 -0
- package/dist/types/scripts/config-vault.d.ts.map +1 -0
- package/dist/types/scripts/pixelated-eslint-plugin.d.ts +117 -0
- package/dist/types/scripts/pixelated-eslint-plugin.d.ts.map +1 -0
- package/dist/types/tests/proxy-handler.test.d.ts +2 -0
- package/dist/types/tests/proxy-handler.test.d.ts.map +1 -0
- package/package.json +3 -2
- package/dist/types/scripts/proptypes-inferprops.d.ts +0 -28
- package/dist/types/scripts/proptypes-inferprops.d.ts.map +0 -1
|
@@ -1,15 +1,48 @@
|
|
|
1
|
+
import { decrypt, isEncrypted } from './crypto';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
1
4
|
const debug = false;
|
|
2
5
|
/**
|
|
3
|
-
* Read the full master config blob from environment.
|
|
6
|
+
* Read the full master config blob from environment or local file.
|
|
4
7
|
* This function is intended for server-side use only.
|
|
5
8
|
*/
|
|
6
9
|
export function getFullPixelatedConfig() {
|
|
7
|
-
|
|
10
|
+
let raw = process.env.PIXELATED_CONFIG_JSON || (process.env.PIXELATED_CONFIG_B64 && Buffer.from(process.env.PIXELATED_CONFIG_B64, 'base64').toString('utf8'));
|
|
11
|
+
let source = process.env.PIXELATED_CONFIG_JSON ? 'PIXELATED_CONFIG_JSON' : (process.env.PIXELATED_CONFIG_B64 ? 'PIXELATED_CONFIG_B64' : 'none');
|
|
12
|
+
// If not in environment, try reading from the conventional file location
|
|
8
13
|
if (!raw) {
|
|
9
|
-
|
|
14
|
+
const configPath = path.join(process.cwd(), 'src/app/config/pixelated.config.json');
|
|
15
|
+
if (fs.existsSync(configPath)) {
|
|
16
|
+
try {
|
|
17
|
+
raw = fs.readFileSync(configPath, 'utf8');
|
|
18
|
+
source = 'src/app/config/pixelated.config.json';
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
console.error(`Failed to read config file at ${configPath}`, err);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (!raw) {
|
|
26
|
+
console.error('PIXELATED_CONFIG not found: neither environment variables nor src/app/config/pixelated.config.json are available.');
|
|
10
27
|
return {};
|
|
11
28
|
}
|
|
12
|
-
|
|
29
|
+
// Handle decryption if the content is encrypted
|
|
30
|
+
if (isEncrypted(raw)) {
|
|
31
|
+
const key = process.env.PIXELATED_CONFIG_KEY;
|
|
32
|
+
if (!key) {
|
|
33
|
+
console.error('PIXELATED_CONFIG is encrypted but PIXELATED_CONFIG_KEY is not set in the environment.');
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
raw = decrypt(raw, key);
|
|
38
|
+
if (debug)
|
|
39
|
+
console.log(`PIXELATED_CONFIG decrypted using key from environment.`);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
console.error('Failed to decrypt PIXELATED_CONFIG', err);
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
13
46
|
try {
|
|
14
47
|
const parsed = JSON.parse(raw);
|
|
15
48
|
if (debug)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
/**
|
|
3
|
+
* AES-256-GCM encryption/decryption utility.
|
|
4
|
+
* Requires a 32-byte key (64 hex characters).
|
|
5
|
+
*/
|
|
6
|
+
const ALGORITHM = 'aes-256-gcm';
|
|
7
|
+
const IV_LENGTH = 12; // GCM recommended IV length
|
|
8
|
+
const AUTH_TAG_LENGTH = 16;
|
|
9
|
+
const ENCRYPTED_PREFIX = 'pxl:v1:';
|
|
10
|
+
/**
|
|
11
|
+
* Encrypts a string using a hex-encoded 32-byte key.
|
|
12
|
+
* Returns a prefixed string: pxl:v1:iv:authTag:encryptedContent (all hex).
|
|
13
|
+
*/
|
|
14
|
+
export function encrypt(text, keyHex) {
|
|
15
|
+
if (!keyHex)
|
|
16
|
+
throw new Error('Encryption key is required.');
|
|
17
|
+
const key = Buffer.from(keyHex, 'hex');
|
|
18
|
+
if (key.length !== 32) {
|
|
19
|
+
throw new Error('Encryption key must be 32 bytes (64 hex characters).');
|
|
20
|
+
}
|
|
21
|
+
const iv = crypto.randomBytes(IV_LENGTH);
|
|
22
|
+
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
|
|
23
|
+
let encrypted = cipher.update(text, 'utf8', 'hex');
|
|
24
|
+
encrypted += cipher.final('hex');
|
|
25
|
+
const authTag = cipher.getAuthTag().toString('hex');
|
|
26
|
+
return `${ENCRYPTED_PREFIX}${iv.toString('hex')}:${authTag}:${encrypted}`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Decrypts a string using a hex-encoded 32-byte key.
|
|
30
|
+
* Expects format: pxl:v1:iv:authTag:encryptedContent
|
|
31
|
+
*/
|
|
32
|
+
export function decrypt(payload, keyHex) {
|
|
33
|
+
if (!keyHex)
|
|
34
|
+
throw new Error('Decryption key is required.');
|
|
35
|
+
if (!payload.startsWith(ENCRYPTED_PREFIX)) {
|
|
36
|
+
throw new Error('Payload is not in a recognized encrypted format.');
|
|
37
|
+
}
|
|
38
|
+
const data = payload.slice(ENCRYPTED_PREFIX.length);
|
|
39
|
+
const key = Buffer.from(keyHex, 'hex');
|
|
40
|
+
if (key.length !== 32) {
|
|
41
|
+
throw new Error('Decryption key must be 32 bytes (64 hex characters).');
|
|
42
|
+
}
|
|
43
|
+
const parts = data.split(':');
|
|
44
|
+
if (parts.length !== 3) {
|
|
45
|
+
throw new Error('Invalid encrypted data format. Expected iv:authTag:encryptedContent');
|
|
46
|
+
}
|
|
47
|
+
const [ivHex, authTagHex, encryptedHex] = parts;
|
|
48
|
+
const iv = Buffer.from(ivHex, 'hex');
|
|
49
|
+
const authTag = Buffer.from(authTagHex, 'hex');
|
|
50
|
+
const encryptedText = Buffer.from(encryptedHex, 'hex');
|
|
51
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
|
|
52
|
+
decipher.setAuthTag(authTag);
|
|
53
|
+
let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
|
|
54
|
+
decrypted += decipher.final('utf8');
|
|
55
|
+
return decrypted;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Checks if a string is encrypted using our format.
|
|
59
|
+
*/
|
|
60
|
+
export function isEncrypted(text) {
|
|
61
|
+
return typeof text === 'string' && text.startsWith(ENCRYPTED_PREFIX);
|
|
62
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect } from 'react';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
|
+
import { SmartImage } from './smartimage';
|
|
5
6
|
import { getGoogleReviewsByPlaceId } from './google.reviews.functions';
|
|
6
7
|
import './google.reviews.css';
|
|
7
8
|
const GOOGLE_MAPS_API_KEY = 'AIzaSyBtknq7LHzN0xb0lIN3K0CXXf0swVp6ReA';
|
|
@@ -50,5 +51,5 @@ export function GoogleReviewsCard(props) {
|
|
|
50
51
|
if (error) {
|
|
51
52
|
return (_jsx("div", { className: "google-reviews-card", children: _jsxs("p", { className: "error", children: ["Error: ", error] }) }));
|
|
52
53
|
}
|
|
53
|
-
return (_jsxs("div", { className: "google-reviews-card", children: [_jsx("h3", { children: place?.name || 'Reviews' }), place?.formatted_address && (_jsx("p", { className: "address", children: place.formatted_address })), reviews.length === 0 ? (_jsx("p", { className: "no-reviews", children: "No reviews found." })) : (_jsx("ul", { children: reviews.map((r, i) => (_jsxs("li", { children: [_jsxs("div", { className: "review-header", children: [r.profile_photo_url && (_jsx("div", { className: "profile-photo-container", children: _jsx(
|
|
54
|
+
return (_jsxs("div", { className: "google-reviews-card", children: [_jsx("h3", { children: place?.name || 'Reviews' }), place?.formatted_address && (_jsx("p", { className: "address", children: place.formatted_address })), reviews.length === 0 ? (_jsx("p", { className: "no-reviews", children: "No reviews found." })) : (_jsx("ul", { children: reviews.map((r, i) => (_jsxs("li", { children: [_jsxs("div", { className: "review-header", children: [r.profile_photo_url && (_jsx("div", { className: "profile-photo-container", children: _jsx(SmartImage, { src: r.profile_photo_url, alt: r.author_name, className: "profile-photo" }) })), _jsxs("div", { children: [_jsx("div", { className: "author-name", children: r.author_name }), _jsxs("div", { className: "rating", children: ['★'.repeat(r.rating), '☆'.repeat(5 - r.rating), " ", r.rating, "/5", r.relative_time_description && _jsxs("span", { children: [" \u00B7 ", r.relative_time_description] })] })] })] }), r.text && _jsx("div", { className: "review-text", children: r.text })] }, i))) })), place && (_jsx("a", { href: `https://search.google.com/local/writereview?placeid=${place.place_id}`, target: "_blank", rel: "noopener noreferrer", className: "write-review", children: "Write a review on Google \u2192" }))] }));
|
|
54
55
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useRef } from 'react';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
|
+
import { SmartImage } from './smartimage';
|
|
5
6
|
import './menu-expando.css';
|
|
6
7
|
MenuExpando.propTypes = {
|
|
7
8
|
menuItems: PropTypes.oneOfType([
|
|
@@ -145,5 +146,5 @@ export function MenuExpandoButton(props) {
|
|
|
145
146
|
handleMenuExpandoButtonClick(event);
|
|
146
147
|
}
|
|
147
148
|
}
|
|
148
|
-
return (_jsx("div", { className: "menuExpandoButton", id: "menuExpandoButton", onClick: handleMenuExpandoButtonClick, onKeyDown: handleKeyDown, tabIndex: 0, role: "button", "aria-label": "Toggle mobile menu", children: _jsx(
|
|
149
|
+
return (_jsx("div", { className: "menuExpandoButton", id: "menuExpandoButton", onClick: handleMenuExpandoButtonClick, onKeyDown: handleKeyDown, tabIndex: 0, role: "button", "aria-label": "Toggle mobile menu", children: _jsx(SmartImage, { src: "/images/icons/mobile-menu2.png", title: "Mobile Menu", alt: "Mobile Menu" }) }));
|
|
149
150
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
/**
|
|
3
|
+
* handlePixelatedProxy
|
|
4
|
+
*
|
|
5
|
+
* A centralized middleware handler for all Pixelated Technology sites.
|
|
6
|
+
* Manages standard headers (x-path, x-url) and enforces a robust Security Policy.
|
|
7
|
+
*
|
|
8
|
+
* TODO: Future enhancement - Accept an options object to allow per-site CSP overrides,
|
|
9
|
+
* enabling/disabling specific permissions (e.g., camera access), or setting custom rate limits.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* STANDARD_PROXY_MATCHER
|
|
13
|
+
*
|
|
14
|
+
* A statically analyzable matcher array for Next.js middleware.
|
|
15
|
+
* Excludes static assets, images, API routes, and favicons from proxy processing.
|
|
16
|
+
*/
|
|
17
|
+
export const STANDARD_PROXY_MATCHER = ["/((?!_next/image|_next/static|api|favicon.ico).*)"];
|
|
18
|
+
export function handlePixelatedProxy(req) {
|
|
19
|
+
const path = req.nextUrl.pathname + (req.nextUrl.search || "");
|
|
20
|
+
const origin = req.nextUrl?.origin ?? new URL(req.url).origin;
|
|
21
|
+
const url = req.nextUrl?.href ?? req.url ?? `${origin}${path}`;
|
|
22
|
+
const requestHeaders = new Headers(req.headers);
|
|
23
|
+
requestHeaders.set("x-path", path);
|
|
24
|
+
requestHeaders.set("x-origin", String(origin));
|
|
25
|
+
requestHeaders.set("x-url", String(url));
|
|
26
|
+
const response = NextResponse.next({
|
|
27
|
+
request: {
|
|
28
|
+
headers: requestHeaders,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
// --- Security Headers ---
|
|
32
|
+
// HSTS: Force HTTPS for 1 year
|
|
33
|
+
response.headers.set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload");
|
|
34
|
+
// Clickjacking Protection
|
|
35
|
+
response.headers.set("X-Frame-Options", "DENY");
|
|
36
|
+
// MIME-Sniffing Protection
|
|
37
|
+
response.headers.set("X-Content-Type-Options", "nosniff");
|
|
38
|
+
// Referrer Policy
|
|
39
|
+
response.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
40
|
+
// Permissions Policy: Lock down hardware access
|
|
41
|
+
response.headers.set("Permissions-Policy", "camera=(), microphone=(), geolocation=(), interest-cohort=()");
|
|
42
|
+
// Content Security Policy (CSP)
|
|
43
|
+
// Includes all discovered domains in the workspace: HubSpot, Gravatar, Flickr, Contentful, Cloudinary, eBay, and Google Analytics + Search.
|
|
44
|
+
const csp = [
|
|
45
|
+
"default-src 'self'",
|
|
46
|
+
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://va.vercel-scripts.com https://*.googletagmanager.com https://*.hs-scripts.com https://*.hs-analytics.net https://*.hsforms.net https://*.hscollectedforms.net https://*.hs-banner.com https://*.google.com https://*.doubleclick.net https://*.googleadservices.com https://*.adtrafficquality.google https://*.hsappstatic.net https://assets.calendly.com",
|
|
47
|
+
"connect-src 'self' https: https://*.hubspot.com https://proxy.pixelated.tech https://sendmail.pixelated.tech https://*.google-analytics.com https://*.analytics.google.com",
|
|
48
|
+
"img-src 'self' data: https: https://*.gravatar.com https://*.staticflickr.com https://*.ctfassets.net https://res.cloudinary.com https://*.ebayimg.com",
|
|
49
|
+
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://*.google.com",
|
|
50
|
+
"font-src 'self' data: https://fonts.gstatic.com",
|
|
51
|
+
"frame-src 'self' https://*.hubspot.com https://*.googletagmanager.com https://*.adtrafficquality.google https://*.google.com https://calendly.com https://*.calendly.com https://*.hsforms.net",
|
|
52
|
+
"frame-ancestors 'none'",
|
|
53
|
+
"object-src 'none'",
|
|
54
|
+
].join("; ");
|
|
55
|
+
response.headers.set("Content-Security-Policy", csp);
|
|
56
|
+
return response;
|
|
57
|
+
}
|
|
@@ -163,5 +163,7 @@ export function SmartImage(props) {
|
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
165
|
/* ===== IMG VARIANT ===== */
|
|
166
|
-
return (
|
|
166
|
+
return (
|
|
167
|
+
/* eslint-disable-next-line pixelated/no-raw-img */
|
|
168
|
+
_jsx("img", { ...newProps, ref: imgRef, alt: newProps.alt }));
|
|
167
169
|
}
|
|
@@ -173,7 +173,7 @@ export function ShoppingCartItem(props) {
|
|
|
173
173
|
const thisItemTarget = "_self"; // "_blank"
|
|
174
174
|
const config = usePixelatedConfig();
|
|
175
175
|
return (_jsxs("div", { className: "pixCartItem row-12col", children: [_jsx("div", { className: "pixCartItemPhoto grid-s1-e4", children: thisItem.itemURL && thisItem.itemImageURL
|
|
176
|
-
? _jsx("a", { href: thisItem.itemURL, target: thisItemTarget, rel: "noopener noreferrer", children: _jsx(
|
|
176
|
+
? _jsx("a", { href: thisItem.itemURL, target: thisItemTarget, rel: "noopener noreferrer", children: _jsx(SmartImage, { src: thisItem.itemImageURL, alt: thisItem.itemTitle, cloudinaryEnv: config?.cloudinary?.product_env, cloudinaryDomain: config?.cloudinary?.baseUrl, cloudinaryTransforms: config?.cloudinary?.transforms }) })
|
|
177
177
|
: thisItem.itemImageURL
|
|
178
178
|
? (
|
|
179
179
|
/* <img src={thisItem.itemImageURL} title={thisItem.itemTitle} alt={thisItem.itemTitle} /> */
|
|
@@ -200,7 +200,9 @@ export const SERVER_ONLY_PATTERNS = [
|
|
|
200
200
|
/\bfs\b/,
|
|
201
201
|
/\bfs\.readFileSync\b/,
|
|
202
202
|
/\bfs\.existsSync\b/,
|
|
203
|
+
/\bcrypto\b/,
|
|
203
204
|
/\bimport.*googleapis\b|\brequire.*googleapis\b/, // Actual import of googleapis
|
|
205
|
+
/\bimport.*next\/server\b|\brequire.*next\/server\b/, // Actual import of next/server
|
|
204
206
|
/\bimport.*path\b|\brequire.*path\b/, // Actual import of path module
|
|
205
207
|
/\bprocess\.cwd\(\)/,
|
|
206
208
|
/\brequire\.resolve\b/,
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
// sorted alphabetically and grouped by folder for easier reading
|
|
2
|
-
export * from './components/config/config';
|
|
3
2
|
export * from './components/config/config.client';
|
|
4
|
-
export * from './components/config/config.server';
|
|
5
3
|
export * from './components/config/config.types';
|
|
6
4
|
export * from './components/general/404';
|
|
7
5
|
export * from './components/general/accordion';
|
package/dist/index.server.js
CHANGED
|
@@ -8,6 +8,7 @@ export * from './components/admin/sites/sites.integration';
|
|
|
8
8
|
export * from './components/config/config';
|
|
9
9
|
export * from './components/config/config.server';
|
|
10
10
|
export * from './components/config/config.types';
|
|
11
|
+
export * from './components/config/crypto';
|
|
11
12
|
// SEO
|
|
12
13
|
export * from './components/general/contentful.delivery';
|
|
13
14
|
export * from './components/general/contentful.management';
|
|
@@ -18,6 +19,7 @@ export * from './components/general/gravatar.functions';
|
|
|
18
19
|
export * from './components/general/instagram.functions';
|
|
19
20
|
export * from './components/general/manifest';
|
|
20
21
|
export * from './components/general/metadata.functions';
|
|
22
|
+
export * from './components/general/proxy-handler';
|
|
21
23
|
export * from './components/general/resume';
|
|
22
24
|
export * from './components/general/schema-blogposting';
|
|
23
25
|
export * from './components/general/schema-blogposting.functions';
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { encrypt, decrypt, isEncrypted } from '../components/config/crypto';
|
|
5
|
+
/**
|
|
6
|
+
* CLI Tool for encrypting/decrypting pixelated.config.json
|
|
7
|
+
* Usage:
|
|
8
|
+
* npx tsx src/scripts/config-vault.js encrypt <filePath> <key>
|
|
9
|
+
* npx tsx src/scripts/config-vault.js decrypt <filePath> <key>
|
|
10
|
+
*/
|
|
11
|
+
const [, , command, targetPath, key] = process.argv;
|
|
12
|
+
if (!command || !targetPath || !key) {
|
|
13
|
+
console.log('Usage:');
|
|
14
|
+
console.log(' encrypt <filePath> <key> - Encrypts the file in place');
|
|
15
|
+
console.log(' decrypt <filePath> <key> - Decrypts the file in place');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
const fullPath = path.isAbsolute(targetPath) ? targetPath : path.resolve(process.cwd(), targetPath);
|
|
19
|
+
if (!fs.existsSync(fullPath)) {
|
|
20
|
+
console.error(`File not found: ${fullPath}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
24
|
+
try {
|
|
25
|
+
if (command === 'encrypt') {
|
|
26
|
+
if (isEncrypted(content)) {
|
|
27
|
+
console.log('File is already encrypted.');
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
const encrypted = encrypt(content, key);
|
|
31
|
+
fs.writeFileSync(fullPath, encrypted, 'utf8');
|
|
32
|
+
console.log(`Successfully encrypted ${targetPath}`);
|
|
33
|
+
}
|
|
34
|
+
else if (command === 'decrypt') {
|
|
35
|
+
if (!isEncrypted(content)) {
|
|
36
|
+
console.log('File is not encrypted.');
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
const decrypted = decrypt(content, key);
|
|
40
|
+
fs.writeFileSync(fullPath, decrypted, 'utf8');
|
|
41
|
+
console.log(`Successfully decrypted ${targetPath}`);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
console.error(`Unknown command: ${command}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
console.error(`Operation failed: ${err.message}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { encrypt, decrypt, isEncrypted } from '../components/config/crypto';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* CLI Tool for encrypting/decrypting pixelated.config.json
|
|
8
|
+
* Usage:
|
|
9
|
+
* npx tsx src/scripts/config-vault.js encrypt <filePath> <key>
|
|
10
|
+
* npx tsx src/scripts/config-vault.js decrypt <filePath> <key>
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const [,, command, targetPath, key] = process.argv;
|
|
14
|
+
|
|
15
|
+
if (!command || !targetPath || !key) {
|
|
16
|
+
console.log('Usage:');
|
|
17
|
+
console.log(' encrypt <filePath> <key> - Encrypts the file in place');
|
|
18
|
+
console.log(' decrypt <filePath> <key> - Decrypts the file in place');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const fullPath = path.isAbsolute(targetPath) ? targetPath : path.resolve(process.cwd(), targetPath);
|
|
23
|
+
|
|
24
|
+
if (!fs.existsSync(fullPath)) {
|
|
25
|
+
console.error(`File not found: ${fullPath}`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
if (command === 'encrypt') {
|
|
33
|
+
if (isEncrypted(content)) {
|
|
34
|
+
console.log('File is already encrypted.');
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
const encrypted = encrypt(content, key);
|
|
38
|
+
fs.writeFileSync(fullPath, encrypted, 'utf8');
|
|
39
|
+
console.log(`Successfully encrypted ${targetPath}`);
|
|
40
|
+
} else if (command === 'decrypt') {
|
|
41
|
+
if (!isEncrypted(content)) {
|
|
42
|
+
console.log('File is not encrypted.');
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
const decrypted = decrypt(content, key);
|
|
46
|
+
fs.writeFileSync(fullPath, decrypted, 'utf8');
|
|
47
|
+
console.log(`Successfully decrypted ${targetPath}`);
|
|
48
|
+
} else {
|
|
49
|
+
console.error(`Unknown command: ${command}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
} catch (err: any) {
|
|
53
|
+
console.error(`Operation failed: ${err.message}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
@@ -1,6 +1,45 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Pixelated ESLint Plugin
|
|
6
|
+
* Enforces workspace standards for SEO, performance, and project structure.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/* ===== CLIENT COMPONENT DETECTION (Copied from functions.ts for standalone usage) ===== */
|
|
10
|
+
const CLIENT_ONLY_PATTERNS = [
|
|
11
|
+
/\baddEventListener\b/,
|
|
12
|
+
/\bcreateContext\b/,
|
|
13
|
+
/\bdocument\./,
|
|
14
|
+
/\blocalStorage\b/,
|
|
15
|
+
/\bnavigator\./,
|
|
16
|
+
/\bonBlur\b/,
|
|
17
|
+
/\bonChange\b/,
|
|
18
|
+
/\bonClick\b/,
|
|
19
|
+
/\bonFocus\b/,
|
|
20
|
+
/\bonInput\b/,
|
|
21
|
+
/\bonKey\b/,
|
|
22
|
+
/\bonMouse\b/,
|
|
23
|
+
/\bonSubmit\b/,
|
|
24
|
+
/\bremoveEventListener\b/,
|
|
25
|
+
/\bsessionStorage\b/,
|
|
26
|
+
/\buseCallback\b/,
|
|
27
|
+
/\buseContext\b/,
|
|
28
|
+
/\buseEffect\b/,
|
|
29
|
+
/\buseLayoutEffect\b/,
|
|
30
|
+
/\buseMemo\b/,
|
|
31
|
+
/\buseReducer\b/,
|
|
32
|
+
/\buseRef\b/,
|
|
33
|
+
/\buseState\b/,
|
|
34
|
+
/\bwindow\./,
|
|
35
|
+
/["']use client["']/ // Client directive
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
function isClientComponent(fileContent) {
|
|
39
|
+
return CLIENT_ONLY_PATTERNS.some(pattern => pattern.test(fileContent));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const propTypesInferPropsRule = {
|
|
4
43
|
meta: {
|
|
5
44
|
type: 'problem',
|
|
6
45
|
docs: {
|
|
@@ -202,4 +241,200 @@ export default {
|
|
|
202
241
|
},
|
|
203
242
|
};
|
|
204
243
|
},
|
|
205
|
-
};
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const requiredSchemasRule = {
|
|
247
|
+
meta: {
|
|
248
|
+
type: 'suggestion',
|
|
249
|
+
docs: {
|
|
250
|
+
description: 'Ensure required SEO schemas are present in layout.tsx',
|
|
251
|
+
category: 'SEO',
|
|
252
|
+
recommended: true,
|
|
253
|
+
},
|
|
254
|
+
messages: {
|
|
255
|
+
missingSchema: 'Required SEO Schema "{{schemaName}}" is missing from layout.tsx.',
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
create(context) {
|
|
259
|
+
if (!context.getFilename().endsWith('layout.tsx')) return {};
|
|
260
|
+
|
|
261
|
+
const requiredSchemas = ['WebsiteSchema', 'LocalBusinessSchema', 'ServicesSchema'];
|
|
262
|
+
const foundSchemas = new Set();
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
JSXIdentifier(node) {
|
|
266
|
+
if (requiredSchemas.includes(node.name)) {
|
|
267
|
+
foundSchemas.add(node.name);
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
'Program:exit'() {
|
|
271
|
+
requiredSchemas.forEach(schema => {
|
|
272
|
+
if (!foundSchemas.has(schema)) {
|
|
273
|
+
context.report({
|
|
274
|
+
loc: { line: 1, column: 0 },
|
|
275
|
+
messageId: 'missingSchema',
|
|
276
|
+
data: { schemaName: schema },
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const requiredFilesRule = {
|
|
286
|
+
meta: {
|
|
287
|
+
type: 'suggestion',
|
|
288
|
+
docs: {
|
|
289
|
+
description: 'Ensure critical project files are present',
|
|
290
|
+
category: 'Project Structure',
|
|
291
|
+
recommended: true,
|
|
292
|
+
},
|
|
293
|
+
messages: {
|
|
294
|
+
missingFile: 'Missing recommended project file: "{{fileName}}".',
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
create(context) {
|
|
298
|
+
// Only run this check once per project execution, ideally on layout.tsx
|
|
299
|
+
if (!context.getFilename().endsWith('layout.tsx')) return {};
|
|
300
|
+
|
|
301
|
+
const projectRoot = context.cwd;
|
|
302
|
+
const requiredFiles = [
|
|
303
|
+
{ name: 'sitemap', pattern: /sitemap\.(ts|js|xml|tsx)$/ },
|
|
304
|
+
{ name: 'manifest', pattern: /manifest\.(json|ts|tsx)$/ },
|
|
305
|
+
{ name: 'not-found', pattern: /not-found\.tsx$/ },
|
|
306
|
+
{ name: 'robots', pattern: /robots\.(ts|tsx)$/ },
|
|
307
|
+
{ name: 'proxy.ts', pattern: /^proxy\.ts$/ },
|
|
308
|
+
{ name: 'amplify.yml', pattern: /^amplify\.yml$/ },
|
|
309
|
+
];
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
'Program:exit'() {
|
|
313
|
+
try {
|
|
314
|
+
const files = fs.readdirSync(projectRoot);
|
|
315
|
+
|
|
316
|
+
// Check common subdirectories
|
|
317
|
+
let appFiles = [];
|
|
318
|
+
let srcFiles = [];
|
|
319
|
+
const appPath = path.join(projectRoot, 'src/app');
|
|
320
|
+
const srcPath = path.join(projectRoot, 'src');
|
|
321
|
+
|
|
322
|
+
if (fs.existsSync(appPath)) {
|
|
323
|
+
appFiles = fs.readdirSync(appPath);
|
|
324
|
+
}
|
|
325
|
+
if (fs.existsSync(srcPath)) {
|
|
326
|
+
srcFiles = fs.readdirSync(srcPath);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const allFiles = [...files, ...appFiles, ...srcFiles];
|
|
330
|
+
|
|
331
|
+
requiredFiles.forEach(req => {
|
|
332
|
+
const found = allFiles.some(f => req.pattern.test(f));
|
|
333
|
+
if (!found) {
|
|
334
|
+
context.report({
|
|
335
|
+
loc: { line: 1, column: 0 },
|
|
336
|
+
messageId: 'missingFile',
|
|
337
|
+
data: { fileName: req.name },
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
} catch (e) {
|
|
342
|
+
// Ignore errors
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const noRawImgRule = {
|
|
350
|
+
meta: {
|
|
351
|
+
type: 'suggestion',
|
|
352
|
+
docs: {
|
|
353
|
+
description: 'Prevent usage of raw <img> tags in favor of SmartImage',
|
|
354
|
+
category: 'Performance',
|
|
355
|
+
recommended: true,
|
|
356
|
+
},
|
|
357
|
+
messages: {
|
|
358
|
+
useSmartImage: 'Use <SmartImage /> instead of raw <img> for better performance and CDN support.',
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
create(context) {
|
|
362
|
+
return {
|
|
363
|
+
JSXOpeningElement(node) {
|
|
364
|
+
if (node.name.name === 'img') {
|
|
365
|
+
context.report({
|
|
366
|
+
node,
|
|
367
|
+
messageId: 'useSmartImage',
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
};
|
|
372
|
+
},
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
const requiredFaqRule = {
|
|
376
|
+
meta: {
|
|
377
|
+
type: 'suggestion',
|
|
378
|
+
docs: {
|
|
379
|
+
description: 'Ensure FAQ page and FAQSchema are present',
|
|
380
|
+
category: 'SEO',
|
|
381
|
+
recommended: true,
|
|
382
|
+
},
|
|
383
|
+
messages: {
|
|
384
|
+
missingFaqPage: 'FAQ page is missing. FAQ pages are strongly recommended for every site (src/app/faq/page.tsx).',
|
|
385
|
+
missingFaqSchema: 'FAQSchema is missing from the FAQ page.',
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
create(context) {
|
|
389
|
+
// Only check this when linting layout.tsx
|
|
390
|
+
if (!context.getFilename().endsWith('layout.tsx')) return {};
|
|
391
|
+
|
|
392
|
+
const projectRoot = context.cwd;
|
|
393
|
+
const faqPath = path.join(projectRoot, 'src/app/faq/page.tsx');
|
|
394
|
+
|
|
395
|
+
return {
|
|
396
|
+
'Program:exit'() {
|
|
397
|
+
if (!fs.existsSync(faqPath)) {
|
|
398
|
+
context.report({
|
|
399
|
+
loc: { line: 1, column: 0 },
|
|
400
|
+
messageId: 'missingFaqPage',
|
|
401
|
+
});
|
|
402
|
+
} else {
|
|
403
|
+
try {
|
|
404
|
+
const content = fs.readFileSync(faqPath, 'utf8');
|
|
405
|
+
if (!content.includes('FAQSchema')) {
|
|
406
|
+
context.report({
|
|
407
|
+
loc: { line: 1, column: 0 },
|
|
408
|
+
messageId: 'missingFaqSchema',
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
} catch (e) {
|
|
412
|
+
// Ignore read errors
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
},
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
export default {
|
|
421
|
+
rules: {
|
|
422
|
+
'prop-types-inferprops': propTypesInferPropsRule,
|
|
423
|
+
'required-schemas': requiredSchemasRule,
|
|
424
|
+
'required-files': requiredFilesRule,
|
|
425
|
+
'no-raw-img': noRawImgRule,
|
|
426
|
+
'required-faq': requiredFaqRule,
|
|
427
|
+
},
|
|
428
|
+
configs: {
|
|
429
|
+
recommended: {
|
|
430
|
+
rules: {
|
|
431
|
+
'pixelated/prop-types-inferprops': 'error',
|
|
432
|
+
'pixelated/required-schemas': 'warn',
|
|
433
|
+
'pixelated/required-files': 'warn',
|
|
434
|
+
'pixelated/no-raw-img': 'warn',
|
|
435
|
+
'pixelated/required-faq': 'warn',
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
};
|
|
440
|
+
|
package/dist/scripts/release.sh
CHANGED
|
@@ -103,7 +103,14 @@ if [ "$current_branch" != "dev" ]; then
|
|
|
103
103
|
fi
|
|
104
104
|
|
|
105
105
|
echo "📦 Step 1: Updating dependencies..."
|
|
106
|
-
npm outdated | awk 'NR>1 {print $1"@"$4}'
|
|
106
|
+
UPDATES=$(npm outdated | awk 'NR>1 {print $1"@"$4}' || true)
|
|
107
|
+
if [ -n "$UPDATES" ]; then
|
|
108
|
+
echo "Updating the following packages: $UPDATES"
|
|
109
|
+
echo "$UPDATES" | xargs npm install --force --save 2>/dev/null || true
|
|
110
|
+
echo "✅ Successfully updated: $UPDATES"
|
|
111
|
+
else
|
|
112
|
+
echo "✅ No dependency updates needed."
|
|
113
|
+
fi
|
|
107
114
|
npm audit fix --force 2>/dev/null || true
|
|
108
115
|
|
|
109
116
|
echo "🔍 Step 2: Running lint..."
|
|
@@ -124,6 +131,10 @@ if [ "$version_type" != "none" ]; then
|
|
|
124
131
|
fi
|
|
125
132
|
|
|
126
133
|
echo "💾 Step 5: Committing changes..."
|
|
134
|
+
if npm run | grep -q "config:encrypt"; then
|
|
135
|
+
echo "🔒 Encrypting configuration..."
|
|
136
|
+
npm run config:encrypt
|
|
137
|
+
fi
|
|
127
138
|
commit_message=$(prompt_commit_message)
|
|
128
139
|
git add . -v
|
|
129
140
|
if git diff --cached --quiet; then
|
|
@@ -171,6 +182,11 @@ else
|
|
|
171
182
|
echo "ℹ️ Tag v$new_version already exists"
|
|
172
183
|
fi
|
|
173
184
|
|
|
185
|
+
if npm run | grep -q "config:decrypt"; then
|
|
186
|
+
echo "🔓 Decrypting configuration for local development..."
|
|
187
|
+
npm run config:decrypt
|
|
188
|
+
fi
|
|
189
|
+
|
|
174
190
|
echo "🔐 Step 9: Publishing to npm..."
|
|
175
191
|
should_publish=$(prompt_publish)
|
|
176
192
|
if [ "$should_publish" = "yes" ]; then
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PixelatedConfig } from './config.types';
|
|
2
2
|
/**
|
|
3
|
-
* Read the full master config blob from environment.
|
|
3
|
+
* Read the full master config blob from environment or local file.
|
|
4
4
|
* This function is intended for server-side use only.
|
|
5
5
|
*/
|
|
6
6
|
export declare function getFullPixelatedConfig(): PixelatedConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/components/config/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/components/config/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAMtD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,eAAe,CA8CxD;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,CAAC,EAAE,eAAe,GAAG,eAAe,CAwBpF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.server.d.ts","sourceRoot":"","sources":["../../../../src/components/config/config.server.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.server.d.ts","sourceRoot":"","sources":["../../../../src/components/config/config.server.tsx"],"names":[],"mappings":"AAGA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAUnD,MAAM,MAAM,iCAAiC,GAAG,UAAU,CAAC,OAAO,6BAA6B,CAAC,SAAS,CAAC,CAAC;AAC3G,wBAAsB,6BAA6B,CAAC,KAAK,EAAE,iCAAiC,oDAM3F;yBANqB,6BAA6B"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encrypts a string using a hex-encoded 32-byte key.
|
|
3
|
+
* Returns a prefixed string: pxl:v1:iv:authTag:encryptedContent (all hex).
|
|
4
|
+
*/
|
|
5
|
+
export declare function encrypt(text: string, keyHex: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Decrypts a string using a hex-encoded 32-byte key.
|
|
8
|
+
* Expects format: pxl:v1:iv:authTag:encryptedContent
|
|
9
|
+
*/
|
|
10
|
+
export declare function decrypt(payload: string, keyHex: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Checks if a string is encrypted using our format.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isEncrypted(text: string): boolean;
|
|
15
|
+
//# sourceMappingURL=crypto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../../../src/components/config/crypto.ts"],"names":[],"mappings":"AAYA;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAgB5D;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CA6B/D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google.reviews.components.d.ts","sourceRoot":"","sources":["../../../../src/components/general/google.reviews.components.tsx"],"names":[],"mappings":"AAGA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"google.reviews.components.d.ts","sourceRoot":"","sources":["../../../../src/components/general/google.reviews.components.tsx"],"names":[],"mappings":"AAGA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGnD,OAAO,sBAAsB,CAAC;AAW9B,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,SAAS,CAAC,CAAC;AACnF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,qBAAqB,2CAiG7D;yBAjGe,iBAAiB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"menu-expando.d.ts","sourceRoot":"","sources":["../../../../src/components/general/menu-expando.tsx"],"names":[],"mappings":"AAIA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"menu-expando.d.ts","sourceRoot":"","sources":["../../../../src/components/general/menu-expando.tsx"],"names":[],"mappings":"AAIA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,oBAAoB,CAAC;AAY5B,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,SAAS,CAAC,CAAC;AACvE,wBAAgB,WAAW,CAAC,KAAK,EAAE,eAAe,2CAqJjD;yBArJe,WAAW;;;;;;;;;;;AA2J3B,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,SAAS,CAAC,CAAC;AAC/E,wBAAgB,eAAe,CAAC,KAAK,EAAE,mBAAmB,2CAIzD;yBAJe,eAAe;;;;;;AAQ/B,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,SAAS,CAAC,CAAC;AACnF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,qBAAqB,2CA4B7D;yBA5Be,iBAAiB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import type { NextRequest } from "next/server";
|
|
3
|
+
/**
|
|
4
|
+
* handlePixelatedProxy
|
|
5
|
+
*
|
|
6
|
+
* A centralized middleware handler for all Pixelated Technology sites.
|
|
7
|
+
* Manages standard headers (x-path, x-url) and enforces a robust Security Policy.
|
|
8
|
+
*
|
|
9
|
+
* TODO: Future enhancement - Accept an options object to allow per-site CSP overrides,
|
|
10
|
+
* enabling/disabling specific permissions (e.g., camera access), or setting custom rate limits.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* STANDARD_PROXY_MATCHER
|
|
14
|
+
*
|
|
15
|
+
* A statically analyzable matcher array for Next.js middleware.
|
|
16
|
+
* Excludes static assets, images, API routes, and favicons from proxy processing.
|
|
17
|
+
*/
|
|
18
|
+
export declare const STANDARD_PROXY_MATCHER: string[];
|
|
19
|
+
export declare function handlePixelatedProxy(req: NextRequest): NextResponse<unknown>;
|
|
20
|
+
//# sourceMappingURL=proxy-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-handler.d.ts","sourceRoot":"","sources":["../../../../src/components/general/proxy-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,UAAwD,CAAC;AAE5F,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,WAAW,yBAkDpD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smartimage.d.ts","sourceRoot":"","sources":["../../../../src/components/general/smartimage.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmB,MAAM,OAAO,CAAC;AACxC,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AA4EnD,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;AACjH,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"smartimage.d.ts","sourceRoot":"","sources":["../../../../src/components/general/smartimage.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmB,MAAM,OAAO,CAAC;AACxC,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AA4EnD,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;AACjH,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,2CAqI/C;yBArIe,UAAU"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shoppingcart.components.d.ts","sourceRoot":"","sources":["../../../../src/components/shoppingcart/shoppingcart.components.tsx"],"names":[],"mappings":"AAIA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAMnD,OAAO,8BAA8B,CAAC;AAStC,OAAO,oBAAoB,CAAC;AAa5B,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;AACzE,wBAAgB,YAAY,CAAE,KAAK,EAAE,gBAAgB,2CAuLpD;yBAvLe,YAAY;;;;;AAoM5B,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAC;AACjF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"shoppingcart.components.d.ts","sourceRoot":"","sources":["../../../../src/components/shoppingcart/shoppingcart.components.tsx"],"names":[],"mappings":"AAIA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAMnD,OAAO,8BAA8B,CAAC;AAStC,OAAO,oBAAoB,CAAC;AAa5B,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;AACzE,wBAAgB,YAAY,CAAE,KAAK,EAAE,gBAAgB,2CAuLpD;yBAvLe,YAAY;;;;;AAoM5B,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAC;AACjF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,oBAAoB,2CAoD3D;yBApDe,gBAAgB;;;;;;;;;;;;AA+EhC,MAAM,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,SAAS,CAAC,CAAC;AAC3E,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,2CA0CrD;yBA1Ce,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;AAgD7B,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,SAAS,CAAC,CAAC;AACrE,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,2CAkC/C;yBAlCe,UAAU;;;;;AAyC1B,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,SAAS,CAAC,CAAC;AAC/E,wBAAgB,eAAe,CAAC,KAAK,EAAE,mBAAmB,2CAQzD;yBARe,eAAe;;;;;;AAgB/B,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,SAAS,CAAC,CAAC;AAC/E,wBAAgB,eAAe,CAAC,KAAK,EAAE,mBAAmB,2CAkBzD;yBAlBe,eAAe;;;;;;;AAyB/B,MAAM,MAAM,kBAAkB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,SAAS,CAAC,CAAC;AAC7E,wBAAgB,cAAc,CAAC,KAAK,EAAE,kBAAkB,2CAQvD;yBARe,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../../../../src/components/utilities/functions.ts"],"names":[],"mappings":"AAGA,wBAAgB,QAAQ,CAAE,GAAG,EAAE,MAAM,oBAUpC;AAGD,wBAAgB,SAAS,CAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG;;EAmBxC;AAED,wBAAgB,wBAAwB,CAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,QAIhF;AAED,wBAAgB,aAAa,CAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,UAOtD;AAED,wBAAgB,WAAW,WAc1B;AAED,wBAAgB,YAAY,WAK3B;AAED,wBAAgB,UAAU,CAAE,GAAG,EAAE,MAAM,UAEtC;AAQD,wBAAgB,YAAY,CAAE,YAAY,EAAE,MAAM,UAgCjD;AAID;;;;OAII;AACJ,wBAAgB,YAAY,SAoB3B;AAKD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,UA0BhC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE9D;AAGD;;GAEG;AACH,eAAO,MAAM,uBAAuB,UAQnC,CAAC;AAEF,eAAO,MAAM,wBAAwB,UAOpC,CAAC;AAGF;;GAEG;AACH,eAAO,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../../../../src/components/utilities/functions.ts"],"names":[],"mappings":"AAGA,wBAAgB,QAAQ,CAAE,GAAG,EAAE,MAAM,oBAUpC;AAGD,wBAAgB,SAAS,CAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG;;EAmBxC;AAED,wBAAgB,wBAAwB,CAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,QAIhF;AAED,wBAAgB,aAAa,CAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,UAOtD;AAED,wBAAgB,WAAW,WAc1B;AAED,wBAAgB,YAAY,WAK3B;AAED,wBAAgB,UAAU,CAAE,GAAG,EAAE,MAAM,UAEtC;AAQD,wBAAgB,YAAY,CAAE,YAAY,EAAE,MAAM,UAgCjD;AAID;;;;OAII;AACJ,wBAAgB,YAAY,SAoB3B;AAKD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,UA0BhC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE9D;AAGD;;GAEG;AACH,eAAO,MAAM,uBAAuB,UAQnC,CAAC;AAEF,eAAO,MAAM,wBAAwB,UAOpC,CAAC;AAGF;;GAEG;AACH,eAAO,MAAM,oBAAoB,UAkBhC,CAAC"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
export * from "./components/config/config";
|
|
2
1
|
export * from "./components/config/config.client";
|
|
3
|
-
export * from "./components/config/config.server";
|
|
4
2
|
export * from "./components/config/config.types";
|
|
5
3
|
export * from "./components/general/404";
|
|
6
4
|
export * from "./components/general/accordion";
|
|
@@ -2,6 +2,7 @@ export * from "./components/admin/sites/sites.integration";
|
|
|
2
2
|
export * from "./components/config/config";
|
|
3
3
|
export * from "./components/config/config.server";
|
|
4
4
|
export * from "./components/config/config.types";
|
|
5
|
+
export * from "./components/config/crypto";
|
|
5
6
|
export * from "./components/general/contentful.delivery";
|
|
6
7
|
export * from "./components/general/contentful.management";
|
|
7
8
|
export * from "./components/general/flickr";
|
|
@@ -11,6 +12,7 @@ export * from "./components/general/gravatar.functions";
|
|
|
11
12
|
export * from "./components/general/instagram.functions";
|
|
12
13
|
export * from "./components/general/manifest";
|
|
13
14
|
export * from "./components/general/metadata.functions";
|
|
15
|
+
export * from "./components/general/proxy-handler";
|
|
14
16
|
export * from "./components/general/resume";
|
|
15
17
|
export * from "./components/general/schema-blogposting";
|
|
16
18
|
export * from "./components/general/schema-blogposting.functions";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-vault.d.ts","sourceRoot":"","sources":["../../../src/scripts/config-vault.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
declare namespace _default {
|
|
2
|
+
let rules: {
|
|
3
|
+
'prop-types-inferprops': {
|
|
4
|
+
meta: {
|
|
5
|
+
type: string;
|
|
6
|
+
docs: {
|
|
7
|
+
description: string;
|
|
8
|
+
category: string;
|
|
9
|
+
recommended: boolean;
|
|
10
|
+
};
|
|
11
|
+
fixable: boolean;
|
|
12
|
+
schema: never[];
|
|
13
|
+
messages: {
|
|
14
|
+
missingPropTypes: string;
|
|
15
|
+
missingInferProps: string;
|
|
16
|
+
invalidInferProps: string;
|
|
17
|
+
missingInferPropsUsage: string;
|
|
18
|
+
propTypesPlacement: string;
|
|
19
|
+
inferPropsPlacement: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
create(context: any): {
|
|
23
|
+
FunctionDeclaration(node: any): void;
|
|
24
|
+
AssignmentExpression(node: any): void;
|
|
25
|
+
TSTypeAliasDeclaration(node: any): void;
|
|
26
|
+
'FunctionDeclaration:exit'(node: any): void;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
'required-schemas': {
|
|
30
|
+
meta: {
|
|
31
|
+
type: string;
|
|
32
|
+
docs: {
|
|
33
|
+
description: string;
|
|
34
|
+
category: string;
|
|
35
|
+
recommended: boolean;
|
|
36
|
+
};
|
|
37
|
+
messages: {
|
|
38
|
+
missingSchema: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
create(context: any): {
|
|
42
|
+
JSXIdentifier?: undefined;
|
|
43
|
+
'Program:exit'?: undefined;
|
|
44
|
+
} | {
|
|
45
|
+
JSXIdentifier(node: any): void;
|
|
46
|
+
'Program:exit'(): void;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
'required-files': {
|
|
50
|
+
meta: {
|
|
51
|
+
type: string;
|
|
52
|
+
docs: {
|
|
53
|
+
description: string;
|
|
54
|
+
category: string;
|
|
55
|
+
recommended: boolean;
|
|
56
|
+
};
|
|
57
|
+
messages: {
|
|
58
|
+
missingFile: string;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
create(context: any): {
|
|
62
|
+
'Program:exit'?: undefined;
|
|
63
|
+
} | {
|
|
64
|
+
'Program:exit'(): void;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
'no-raw-img': {
|
|
68
|
+
meta: {
|
|
69
|
+
type: string;
|
|
70
|
+
docs: {
|
|
71
|
+
description: string;
|
|
72
|
+
category: string;
|
|
73
|
+
recommended: boolean;
|
|
74
|
+
};
|
|
75
|
+
messages: {
|
|
76
|
+
useSmartImage: string;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
create(context: any): {
|
|
80
|
+
JSXOpeningElement(node: any): void;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
'required-faq': {
|
|
84
|
+
meta: {
|
|
85
|
+
type: string;
|
|
86
|
+
docs: {
|
|
87
|
+
description: string;
|
|
88
|
+
category: string;
|
|
89
|
+
recommended: boolean;
|
|
90
|
+
};
|
|
91
|
+
messages: {
|
|
92
|
+
missingFaqPage: string;
|
|
93
|
+
missingFaqSchema: string;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
create(context: any): {
|
|
97
|
+
'Program:exit'?: undefined;
|
|
98
|
+
} | {
|
|
99
|
+
'Program:exit'(): void;
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
namespace configs {
|
|
104
|
+
namespace recommended {
|
|
105
|
+
let rules_1: {
|
|
106
|
+
'pixelated/prop-types-inferprops': string;
|
|
107
|
+
'pixelated/required-schemas': string;
|
|
108
|
+
'pixelated/required-files': string;
|
|
109
|
+
'pixelated/no-raw-img': string;
|
|
110
|
+
'pixelated/required-faq': string;
|
|
111
|
+
};
|
|
112
|
+
export { rules_1 as rules };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export default _default;
|
|
117
|
+
//# sourceMappingURL=pixelated-eslint-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pixelated-eslint-plugin.d.ts","sourceRoot":"","sources":["../../../src/scripts/pixelated-eslint-plugin.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-handler.test.d.ts","sourceRoot":"","sources":["../../../src/tests/proxy-handler.test.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixelated-tech/components",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Pixelated Technologies",
|
|
@@ -85,7 +85,8 @@
|
|
|
85
85
|
"test:coverage": "vitest run --coverage",
|
|
86
86
|
"test:run": "vitest run",
|
|
87
87
|
"lint": "eslint --fix",
|
|
88
|
-
"release": "./src/scripts/release.sh"
|
|
88
|
+
"release": "./src/scripts/release.sh",
|
|
89
|
+
"vault": "tsx src/scripts/config-vault.ts"
|
|
89
90
|
},
|
|
90
91
|
"scripts-old": {
|
|
91
92
|
"buildBabel": "npm run buildClean && NODE_ENV=production babel src --out-dir dist --copy-files",
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
declare namespace _default {
|
|
2
|
-
namespace meta {
|
|
3
|
-
let type: string;
|
|
4
|
-
namespace docs {
|
|
5
|
-
let description: string;
|
|
6
|
-
let category: string;
|
|
7
|
-
let recommended: boolean;
|
|
8
|
-
}
|
|
9
|
-
let fixable: boolean;
|
|
10
|
-
let schema: never[];
|
|
11
|
-
namespace messages {
|
|
12
|
-
let missingPropTypes: string;
|
|
13
|
-
let missingInferProps: string;
|
|
14
|
-
let invalidInferProps: string;
|
|
15
|
-
let missingInferPropsUsage: string;
|
|
16
|
-
let propTypesPlacement: string;
|
|
17
|
-
let inferPropsPlacement: string;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function create(context: any): {
|
|
21
|
-
FunctionDeclaration(node: any): void;
|
|
22
|
-
AssignmentExpression(node: any): void;
|
|
23
|
-
TSTypeAliasDeclaration(node: any): void;
|
|
24
|
-
'FunctionDeclaration:exit'(node: any): void;
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
export default _default;
|
|
28
|
-
//# sourceMappingURL=proptypes-inferprops.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"proptypes-inferprops.d.ts","sourceRoot":"","sources":["../../../src/scripts/proptypes-inferprops.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;IAqBC;;;;;MAsLC"}
|