@pixelated-tech/components 3.12.0 → 3.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/components/admin/componentusage/componentAnalysis.js +2 -0
  2. package/dist/components/admin/deploy/deployment.integration.js +8 -0
  3. package/dist/components/config/config.js +41 -2
  4. package/dist/components/general/metadata.functions.js +1 -1
  5. package/dist/components/general/well-known.js +137 -0
  6. package/dist/components/integrations/contentful.management.js +25 -25
  7. package/dist/components/sitebuilder/page/lib/pageStorageContentful.js +4 -4
  8. package/dist/config/pixelated.config.json.enc +1 -1
  9. package/dist/index.js +0 -1
  10. package/dist/index.server.js +3 -4
  11. package/dist/scripts/pixelated-eslint-plugin.js +49 -1
  12. package/dist/types/components/admin/componentusage/componentAnalysis.d.ts +2 -5
  13. package/dist/types/components/admin/componentusage/componentAnalysis.d.ts.map +1 -1
  14. package/dist/types/components/admin/deploy/deployment.integration.d.ts +1 -5
  15. package/dist/types/components/admin/deploy/deployment.integration.d.ts.map +1 -1
  16. package/dist/types/components/admin/site-health/site-health-github.integration.d.ts +1 -11
  17. package/dist/types/components/admin/site-health/site-health-github.integration.d.ts.map +1 -1
  18. package/dist/types/components/admin/sites/sites.integration.d.ts +7 -1
  19. package/dist/types/components/admin/sites/sites.integration.d.ts.map +1 -1
  20. package/dist/types/components/config/config.d.ts.map +1 -1
  21. package/dist/types/components/config/config.types.d.ts +2 -2
  22. package/dist/types/components/config/config.types.d.ts.map +1 -1
  23. package/dist/types/components/general/well-known.d.ts +56 -0
  24. package/dist/types/components/general/well-known.d.ts.map +1 -0
  25. package/dist/types/components/integrations/contentful.management.d.ts +1 -5
  26. package/dist/types/components/integrations/contentful.management.d.ts.map +1 -1
  27. package/dist/types/components/shoppingcart/ebay.functions.d.ts +2 -2
  28. package/dist/types/components/shoppingcart/ebay.functions.d.ts.map +1 -1
  29. package/dist/types/components/shoppingcart/paypal.d.ts.map +1 -1
  30. package/dist/types/components/shoppingcart/shoppingcart.functions.d.ts +20 -12
  31. package/dist/types/components/shoppingcart/shoppingcart.functions.d.ts.map +1 -1
  32. package/dist/types/components/sitebuilder/page/lib/pageStorageContentful.d.ts +1 -1
  33. package/dist/types/components/sitebuilder/page/lib/pageStorageContentful.d.ts.map +1 -1
  34. package/dist/types/index.d.ts +0 -1
  35. package/dist/types/index.server.d.ts +3 -4
  36. package/dist/types/scripts/pixelated-eslint-plugin.d.ts +18 -0
  37. package/dist/types/tests/securitytxt.test.d.ts +2 -0
  38. package/dist/types/tests/securitytxt.test.d.ts.map +1 -0
  39. package/package.json +6 -6
  40. package/dist/components/config/config.utils.js +0 -52
  41. package/dist/components/general/humanstxt.js +0 -81
  42. package/dist/types/components/config/config.utils.d.ts +0 -6
  43. package/dist/types/components/config/config.utils.d.ts.map +0 -1
  44. package/dist/types/components/general/humanstxt.d.ts +0 -37
  45. package/dist/types/components/general/humanstxt.d.ts.map +0 -1
@@ -62,6 +62,8 @@ export async function getAllFiles(dirPath, extensions = []) {
62
62
  */
63
63
  export async function checkComponentUsage(sitePath, componentName) {
64
64
  try {
65
+ if (!sitePath)
66
+ return false; // nothing to scan
65
67
  const files = await getAllFiles(sitePath, ['.tsx', '.ts', '.jsx', '.js']);
66
68
  // Special case for semantic components that export multiple functions
67
69
  if (componentName === 'general/semantic') {
@@ -22,6 +22,14 @@ export async function executeDeployment(request, siteConfig, isLocalExecution =
22
22
  */
23
23
  async function executeScript(siteName, versionType, commitMessage, environments, localPath, remote) {
24
24
  const sourceBranch = 'dev'; // Always deploy from dev branch
25
+ // Guard against missing localPath — callers may omit for non-local checks
26
+ if (!localPath) {
27
+ throw new Error('localPath is required for deployment execution');
28
+ }
29
+ // Guard against missing remote — required for git operations
30
+ if (!remote) {
31
+ throw new Error('remote is required for deployment execution');
32
+ }
25
33
  try {
26
34
  // Get current branch and ensure we're on dev
27
35
  const { stdout: currentBranch } = await execAsync('git branch --show-current', { cwd: localPath });
@@ -1,7 +1,10 @@
1
+ import { SECRET_CONFIG_KEYS } from './config.types';
1
2
  import { decrypt, isEncrypted } from './crypto';
2
- import { getClientOnlyPixelatedConfig as stripSecrets } from './config.utils';
3
3
  import fs from 'fs';
4
4
  import path from 'path';
5
+ // NOTE: getClientOnlyPixelatedConfig implementation moved here from
6
+ // src/components/config/config.utils.ts — this consolidates the public
7
+ // config API into one module.
5
8
  const debug = false;
6
9
  /**
7
10
  * Read the full master config blob from local file.
@@ -108,5 +111,41 @@ export function getClientOnlyPixelatedConfig(full) {
108
111
  const src = (full === undefined) ? getFullPixelatedConfig() : full;
109
112
  if (src === null || typeof src !== 'object')
110
113
  return (src || {});
111
- return stripSecrets(src);
114
+ // Inlined secret stripping logic (previously in config.utils)
115
+ const visited = new WeakSet();
116
+ function isSecretKey(key, serviceName) {
117
+ if (SECRET_CONFIG_KEYS.global.includes(key))
118
+ return true;
119
+ if (serviceName && SECRET_CONFIG_KEYS.services[serviceName]) {
120
+ const serviceSecrets = SECRET_CONFIG_KEYS.services[serviceName];
121
+ if (serviceSecrets.includes(key))
122
+ return true;
123
+ }
124
+ return false;
125
+ }
126
+ function strip(obj, serviceName) {
127
+ if (obj === null || typeof obj !== 'object')
128
+ return obj;
129
+ if (visited.has(obj))
130
+ return '[Circular]';
131
+ visited.add(obj);
132
+ if (Array.isArray(obj)) {
133
+ return obj.map((item) => strip(item, serviceName));
134
+ }
135
+ const out = {};
136
+ for (const k of Object.keys(obj)) {
137
+ const currentService = serviceName || k;
138
+ if (isSecretKey(k, serviceName))
139
+ continue;
140
+ out[k] = strip(obj[k], currentService);
141
+ }
142
+ return out;
143
+ }
144
+ try {
145
+ return strip(src);
146
+ }
147
+ catch (err) {
148
+ console.error('Failed to strip secrets from config', err);
149
+ return {};
150
+ }
112
151
  }
@@ -128,5 +128,5 @@ export function generateMetaTags(props) {
128
128
  const image_height = prop_image_height || siteInfo?.image_height;
129
129
  const image_width = prop_image_width || siteInfo?.image_width;
130
130
  const favicon = prop_favicon || siteInfo?.favicon;
131
- return (_jsxs(_Fragment, { children: [_jsx("title", { children: title }), _jsx("meta", { charSet: "UTF-8" }), _jsx("meta", { httpEquiv: "content-type", content: "text/html; charset=UTF-8" }), _jsx("meta", { httpEquiv: 'Expires', content: '0' }), _jsx("meta", { httpEquiv: 'Pragma', content: 'no-cache' }), _jsx("meta", { httpEquiv: 'Cache-Control', content: 'no-cache' }), _jsx("meta", { name: "application-name", content: site_name }), _jsx("meta", { name: "author", content: site_name + ", " + email }), _jsx("meta", { name: 'copyright', content: site_name }), _jsx("meta", { name: "creator", content: site_name }), _jsx("meta", { name: "description", content: description }), _jsx("meta", { name: "keywords", content: keywords }), _jsx("meta", { name: 'language', content: 'EN' }), _jsx("meta", { name: 'owner', content: site_name }), _jsx("meta", { name: "publisher", content: site_name }), _jsx("meta", { name: 'rating', content: 'General' }), _jsx("meta", { name: 'reply-to', content: email }), _jsx("meta", { name: "robots", content: "index, follow" }), _jsx("meta", { name: 'url', content: url }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0, shrink-to-fit=no" }), _jsx("meta", { property: "og:description", content: description }), _jsx("meta", { property: 'og:email', content: email }), _jsx("meta", { property: "og:image", content: image }), _jsx("meta", { property: "og:image:height", content: image_height != null ? String(image_height) : undefined }), _jsx("meta", { property: "og:image:width", content: image_width != null ? String(image_width) : undefined }), _jsx("meta", { property: "og:locale", content: "en_US" }), _jsx("meta", { property: "og:site_name", content: site_name }), _jsx("meta", { property: "og:title", content: title }), _jsx("meta", { property: "og:type", content: "website" }), _jsx("meta", { property: "og:url", content: url }), _jsx("meta", { itemProp: "name", content: site_name }), _jsx("meta", { itemProp: "url", content: url }), _jsx("meta", { itemProp: "description", content: description }), _jsx("meta", { itemProp: "thumbnailUrl", content: image }), _jsx("meta", { property: "twitter:domain", content: newOrigin }), _jsx("meta", { property: "twitter:url", content: url }), _jsx("meta", { name: "twitter:card", content: "summary_large_image" }), _jsx("meta", { name: "twitter:creator", content: site_name }), _jsx("meta", { name: "twitter:description", content: description }), _jsx("meta", { name: "twitter:image", content: image }), _jsx("meta", { name: "twitter:image:height", content: image_height != null ? String(image_height) : undefined }), _jsx("meta", { name: "twitter:image:width", content: image_width != null ? String(image_width) : undefined }), _jsx("meta", { name: "twitter:title", content: title }), _jsx("link", { rel: "author", href: newOrigin }), _jsx("link", { rel: "canonical", href: url }), _jsx("link", { rel: "icon", type: "image/x-icon", href: favicon }), _jsx("link", { rel: "shortcut icon", type: "image/x-icon", href: favicon }), _jsx("link", { rel: "manifest", href: "/manifest.webmanifest" }), _jsx("link", { rel: "preconnect", href: "https://images.ctfassets.net/" }), _jsx("link", { rel: "preconnect", href: "https://res.cloudinary.com/" }), _jsx("link", { rel: "preconnect", href: "https://farm2.static.flickr.com" }), _jsx("link", { rel: "preconnect", href: "https://farm6.static.flickr.com" }), _jsx("link", { rel: "preconnect", href: "https://farm8.static.flickr.com" }), _jsx("link", { rel: "preconnect", href: "https://farm66.static.flickr.com" })] }));
131
+ return (_jsxs(_Fragment, { children: [_jsx("title", { children: title }), _jsx("meta", { charSet: "UTF-8" }), _jsx("meta", { httpEquiv: "content-type", content: "text/html; charset=UTF-8" }), _jsx("meta", { httpEquiv: 'Expires', content: '0' }), _jsx("meta", { httpEquiv: 'Pragma', content: 'no-cache' }), _jsx("meta", { httpEquiv: 'Cache-Control', content: 'no-cache' }), _jsx("meta", { name: "application-name", content: site_name }), _jsx("meta", { name: "author", content: site_name + ", " + email }), _jsx("meta", { name: 'copyright', content: site_name }), _jsx("meta", { name: "creator", content: site_name }), _jsx("meta", { name: "description", content: description }), _jsx("meta", { name: "keywords", content: keywords }), _jsx("meta", { name: 'language', content: 'EN' }), _jsx("meta", { name: 'owner', content: site_name }), _jsx("meta", { name: "publisher", content: site_name }), _jsx("meta", { name: 'rating', content: 'General' }), _jsx("meta", { name: 'reply-to', content: email }), _jsx("meta", { name: "robots", content: "index, follow" }), _jsx("meta", { name: 'url', content: url }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0, shrink-to-fit=no" }), _jsx("meta", { property: "og:description", content: description }), _jsx("meta", { property: 'og:email', content: email }), _jsx("meta", { property: "og:image", content: image }), _jsx("meta", { property: "og:image:height", content: image_height != null ? String(image_height) : undefined }), _jsx("meta", { property: "og:image:width", content: image_width != null ? String(image_width) : undefined }), _jsx("meta", { property: "og:locale", content: "en_US" }), _jsx("meta", { property: "og:site_name", content: site_name }), _jsx("meta", { property: "og:title", content: title }), _jsx("meta", { property: "og:type", content: "website" }), _jsx("meta", { property: "og:url", content: url }), _jsx("meta", { itemProp: "name", content: site_name }), _jsx("meta", { itemProp: "url", content: url }), _jsx("meta", { itemProp: "description", content: description }), _jsx("meta", { itemProp: "thumbnailUrl", content: image }), _jsx("meta", { property: "twitter:domain", content: newOrigin }), _jsx("meta", { property: "twitter:url", content: url }), _jsx("meta", { name: "twitter:card", content: "summary_large_image" }), _jsx("meta", { name: "twitter:creator", content: site_name }), _jsx("meta", { name: "twitter:description", content: description }), _jsx("meta", { name: "twitter:image", content: image }), _jsx("meta", { name: "twitter:image:height", content: image_height != null ? String(image_height) : undefined }), _jsx("meta", { name: "twitter:image:width", content: image_width != null ? String(image_width) : undefined }), _jsx("meta", { name: "twitter:title", content: title }), _jsx("link", { rel: "author", href: "humans.txt" }), _jsx("link", { rel: "canonical", href: url }), _jsx("link", { rel: "icon", type: "image/x-icon", href: favicon }), _jsx("link", { rel: "shortcut icon", type: "image/x-icon", href: favicon }), _jsx("link", { rel: "manifest", href: "/manifest.webmanifest" }), _jsx("link", { rel: "preconnect", href: "https://images.ctfassets.net/" }), _jsx("link", { rel: "preconnect", href: "https://res.cloudinary.com/" }), _jsx("link", { rel: "preconnect", href: "https://farm2.static.flickr.com" }), _jsx("link", { rel: "preconnect", href: "https://farm6.static.flickr.com" }), _jsx("link", { rel: "preconnect", href: "https://farm8.static.flickr.com" }), _jsx("link", { rel: "preconnect", href: "https://farm66.static.flickr.com" })] }));
132
132
  }
@@ -0,0 +1,137 @@
1
+ import PropTypes from 'prop-types';
2
+ import { readFile } from 'fs/promises';
3
+ import crypto from 'crypto';
4
+ import { NextResponse } from 'next/server';
5
+ import { flattenRoutes } from './sitemap';
6
+ /* ===== Shared helpers for .well-known files ===== */
7
+ /**
8
+ * Read JSON from disk safely — returns null on error. Exported for testing.
9
+ */
10
+ export async function safeJSON(path) {
11
+ try {
12
+ const raw = await readFile(path, 'utf8');
13
+ return JSON.parse(raw);
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ /**
20
+ * Normalize a value into a single-line trimmed string (safe for humans.txt / security.txt).
21
+ * Exported for testing.
22
+ */
23
+ export function sanitizeString(v) {
24
+ return v == null ? '' : String(v).replace(/\s+/g, ' ').trim();
25
+ }
26
+ /**
27
+ * Build a plain-text response payload including ETag and standard headers.
28
+ * Exported for testing and reuse across .well-known generators.
29
+ */
30
+ export function createTextResponsePayload(body) {
31
+ const etag = crypto.createHash('sha1').update(body).digest('hex');
32
+ const headers = {
33
+ 'Content-Type': 'text/plain; charset=utf-8',
34
+ 'Cache-Control': 'public, max-age=60, stale-while-revalidate=3600',
35
+ ETag: etag,
36
+ };
37
+ return { body, etag, headers };
38
+ }
39
+ /* ========== HUMANS.TXT ========== */
40
+ generateHumansTxt.propTypes = {
41
+ /** base directory to read package.json / routes.json from (defaults to process.cwd()) */
42
+ cwd: PropTypes.string,
43
+ /** optional package.json object (if provided, fs is not used) */
44
+ pkg: PropTypes.object,
45
+ /** optional routes.json object (if provided, fs is not used) */
46
+ routesJson: PropTypes.object,
47
+ /** limit how many routes to include (default 50) */
48
+ maxRoutes: PropTypes.number,
49
+ };
50
+ export async function generateHumansTxt(opts = {}) {
51
+ const cwd = opts.cwd ?? process.cwd();
52
+ const pkg = opts.pkg ?? (await safeJSON(cwd + '/package.json')) ?? {};
53
+ const data = opts.routesJson ?? (await safeJSON(cwd + '/src/app/data/routes.json')) ?? {};
54
+ const site = data.siteInfo ?? {};
55
+ const routes = Array.isArray(data.routes) ? data.routes : [];
56
+ const lines = [
57
+ '/* HUMAN-READABLE SITE INFORMATION - generated at runtime */',
58
+ '',
59
+ '/* AUTHOR */',
60
+ ` Author Name: ${sanitizeString(site.author ?? '')}`,
61
+ ` Author Address: ${sanitizeString(site.address
62
+ ? [
63
+ site.address.streetAddress,
64
+ site.address.addressLocality,
65
+ site.address.addressRegion,
66
+ site.address.postalCode,
67
+ site.address.addressCountry,
68
+ ]
69
+ .filter(Boolean)
70
+ .join(' ')
71
+ : '')}`,
72
+ ` Author Email: ${sanitizeString(site.email ?? '')}`,
73
+ ` Author Telephone: ${sanitizeString(site.telephone ?? '')}`,
74
+ '',
75
+ '/* DEVELOPER */',
76
+ ` Developer Name: Brian Whaley`,
77
+ ` Developer Company: Pixelated Technologies LLC`,
78
+ ` Developer Address: 10 Jade Circle, Denville NJ 07834 USA`,
79
+ ` Developer Email: brian@pixelated.tech`,
80
+ ` Developer Website: https://www.pixelated.tech`,
81
+ ` Developer Telephone: +1 (973) 722-2601`,
82
+ '',
83
+ '/* SITE */',
84
+ ` Site Name: ${sanitizeString(site.name ?? '')}`,
85
+ ` Site Package Name: ${sanitizeString(pkg.name ?? '')}`,
86
+ ` Site Package Version: ${sanitizeString(pkg.version ?? '')}`,
87
+ ` Site URL: ${sanitizeString(site.url ?? '')}`,
88
+ ` Site Languages: React, Node, NextJS, JavaScript, HTML5, CSS3, SASS `,
89
+ ` Site Tools: VSCode, GitHub, AWS, Contently, Cloudinary, Wordpress, Google Analytics, Google Search Console`,
90
+ ` Site Pages: (${routes.length})`,
91
+ ];
92
+ const limit = typeof opts.maxRoutes === 'number' ? opts.maxRoutes : 50;
93
+ for (const r of flattenRoutes(routes).slice(0, limit)) {
94
+ lines.push(` - ${sanitizeString(r.path ?? r.pathname ?? r.url ?? '')} - ${sanitizeString(r.title ?? '')}`);
95
+ }
96
+ const body = lines.join('\n');
97
+ return createTextResponsePayload(body);
98
+ }
99
+ /* ========== SECURITY.TXT ========== */
100
+ generateSecurityTxt.propTypes = {
101
+ routesJson: PropTypes.object,
102
+ };
103
+ export async function generateSecurityTxt(props = {}) {
104
+ const data = props.routesJson ?? (await safeJSON(process.cwd() + '/src/app/data/routes.json')) ?? {};
105
+ const siteInfo = data.siteInfo ?? {};
106
+ const lines = [
107
+ '# Contact methods for security researchers',
108
+ `Contact: mailto:${sanitizeString(siteInfo.email ?? '')}`,
109
+ '',
110
+ "# Link to your vulnerability disclosure policy",
111
+ 'Policy: ',
112
+ '',
113
+ "# Link to your PGP public key for encrypted communication",
114
+ 'Encryption: ',
115
+ '',
116
+ "# Languages supported",
117
+ 'Preferred-Languages: en',
118
+ '',
119
+ "# Date and time the file should be considered stale",
120
+ `Expires: ${new Date(new Date().getFullYear(), 11, 31).toISOString()}`,
121
+ ];
122
+ const body = lines.join('\n');
123
+ return createTextResponsePayload(body);
124
+ }
125
+ /* ========== Convenience helper ========== */
126
+ /**
127
+ * Create a response for a well-known resource.
128
+ * @param {'humans'|'security'} type - Which resource to generate ('humans' | 'security').
129
+ */
130
+ export async function createWellKnownResponse(type, req, opts = {}) {
131
+ const payload = (type === 'humans') ? await generateHumansTxt(opts) : await generateSecurityTxt(opts);
132
+ const { body, etag, headers } = payload;
133
+ if (req?.headers?.get && req.headers.get('if-none-match') === etag) {
134
+ return new NextResponse(null, { status: 304, headers });
135
+ }
136
+ return new NextResponse(body, { status: 200, headers });
137
+ }
@@ -7,11 +7,11 @@
7
7
  * List all entries of a specific content type
8
8
  */
9
9
  export async function listEntries(contentType, config) {
10
- const { spaceId, accessToken, environment = 'master' } = config;
10
+ const { space_id, delivery_access_token, environment = 'master' } = config;
11
11
  try {
12
- const response = await fetch(`https://api.contentful.com/spaces/${spaceId}/environments/${environment}/entries?content_type=${contentType}`, {
12
+ const response = await fetch(`https://api.contentful.com/spaces/${space_id}/environments/${environment}/entries?content_type=${contentType}`, {
13
13
  headers: {
14
- 'Authorization': `Bearer ${accessToken}`,
14
+ 'Authorization': `Bearer ${delivery_access_token}`,
15
15
  'Content-Type': 'application/json',
16
16
  },
17
17
  });
@@ -37,11 +37,11 @@ export async function listEntries(contentType, config) {
37
37
  * Get a single entry by ID
38
38
  */
39
39
  export async function getEntryById(entryId, config) {
40
- const { spaceId, accessToken, environment = 'master' } = config;
40
+ const { space_id, delivery_access_token, environment = 'master' } = config;
41
41
  try {
42
- const response = await fetch(`https://api.contentful.com/spaces/${spaceId}/environments/${environment}/entries/${entryId}`, {
42
+ const response = await fetch(`https://api.contentful.com/spaces/${space_id}/environments/${environment}/entries/${entryId}`, {
43
43
  headers: {
44
- 'Authorization': `Bearer ${accessToken}`,
44
+ 'Authorization': `Bearer ${delivery_access_token}`,
45
45
  'Content-Type': 'application/json',
46
46
  },
47
47
  });
@@ -71,11 +71,11 @@ export async function getEntryById(entryId, config) {
71
71
  * Search for entries by field value
72
72
  */
73
73
  export async function searchEntriesByField(contentType, fieldName, fieldValue, config) {
74
- const { spaceId, accessToken, environment = 'master' } = config;
74
+ const { space_id, delivery_access_token, environment = 'master' } = config;
75
75
  try {
76
- const response = await fetch(`https://api.contentful.com/spaces/${spaceId}/environments/${environment}/entries?content_type=${contentType}&fields.${fieldName}=${encodeURIComponent(fieldValue)}`, {
76
+ const response = await fetch(`https://api.contentful.com/spaces/${space_id}/environments/${environment}/entries?content_type=${contentType}&fields.${fieldName}=${encodeURIComponent(fieldValue)}`, {
77
77
  headers: {
78
- 'Authorization': `Bearer ${accessToken}`,
78
+ 'Authorization': `Bearer ${delivery_access_token}`,
79
79
  'Content-Type': 'application/json',
80
80
  },
81
81
  });
@@ -101,17 +101,17 @@ export async function searchEntriesByField(contentType, fieldName, fieldValue, c
101
101
  * Create a new entry
102
102
  */
103
103
  export async function createEntry(contentType, fields, config, autoPublish = true) {
104
- const { spaceId, accessToken, environment = 'master' } = config;
104
+ const { space_id, delivery_access_token, environment = 'master' } = config;
105
105
  try {
106
106
  // Convert fields to Contentful format (with 'en-US' locale)
107
107
  const contentfulFields = {};
108
108
  for (const [key, value] of Object.entries(fields)) {
109
109
  contentfulFields[key] = { 'en-US': value };
110
110
  }
111
- const createResponse = await fetch(`https://api.contentful.com/spaces/${spaceId}/environments/${environment}/entries`, {
111
+ const createResponse = await fetch(`https://api.contentful.com/spaces/${space_id}/environments/${environment}/entries`, {
112
112
  method: 'POST',
113
113
  headers: {
114
- 'Authorization': `Bearer ${accessToken}`,
114
+ 'Authorization': `Bearer ${delivery_access_token}`,
115
115
  'Content-Type': 'application/vnd.contentful.management.v1+json',
116
116
  'X-Contentful-Content-Type': contentType,
117
117
  },
@@ -126,10 +126,10 @@ export async function createEntry(contentType, fields, config, autoPublish = tru
126
126
  const newEntry = await createResponse.json();
127
127
  // Publish if requested
128
128
  if (autoPublish) {
129
- await fetch(`https://api.contentful.com/spaces/${spaceId}/environments/${environment}/entries/${newEntry.sys.id}/published`, {
129
+ await fetch(`https://api.contentful.com/spaces/${space_id}/environments/${environment}/entries/${newEntry.sys.id}/published`, {
130
130
  method: 'PUT',
131
131
  headers: {
132
- 'Authorization': `Bearer ${accessToken}`,
132
+ 'Authorization': `Bearer ${delivery_access_token}`,
133
133
  'X-Contentful-Version': newEntry.sys.version.toString(),
134
134
  },
135
135
  });
@@ -151,7 +151,7 @@ export async function createEntry(contentType, fields, config, autoPublish = tru
151
151
  * Update an existing entry
152
152
  */
153
153
  export async function updateEntry(entryId, fields, config, autoPublish = true) {
154
- const { spaceId, accessToken, environment = 'master' } = config;
154
+ const { space_id, delivery_access_token, environment = 'master' } = config;
155
155
  try {
156
156
  // Get current entry to get version
157
157
  const getResponse = await getEntryById(entryId, config);
@@ -167,10 +167,10 @@ export async function updateEntry(entryId, fields, config, autoPublish = true) {
167
167
  for (const [key, value] of Object.entries(fields)) {
168
168
  contentfulFields[key] = { 'en-US': value };
169
169
  }
170
- const updateResponse = await fetch(`https://api.contentful.com/spaces/${spaceId}/environments/${environment}/entries/${entryId}`, {
170
+ const updateResponse = await fetch(`https://api.contentful.com/spaces/${space_id}/environments/${environment}/entries/${entryId}`, {
171
171
  method: 'PUT',
172
172
  headers: {
173
- 'Authorization': `Bearer ${accessToken}`,
173
+ 'Authorization': `Bearer ${delivery_access_token}`,
174
174
  'Content-Type': 'application/vnd.contentful.management.v1+json',
175
175
  'X-Contentful-Version': currentEntry.sys.version.toString(),
176
176
  },
@@ -185,10 +185,10 @@ export async function updateEntry(entryId, fields, config, autoPublish = true) {
185
185
  const updatedEntry = await updateResponse.json();
186
186
  // Publish if requested
187
187
  if (autoPublish) {
188
- await fetch(`https://api.contentful.com/spaces/${spaceId}/environments/${environment}/entries/${entryId}/published`, {
188
+ await fetch(`https://api.contentful.com/spaces/${space_id}/environments/${environment}/entries/${entryId}/published`, {
189
189
  method: 'PUT',
190
190
  headers: {
191
- 'Authorization': `Bearer ${accessToken}`,
191
+ 'Authorization': `Bearer ${delivery_access_token}`,
192
192
  'X-Contentful-Version': updatedEntry.sys.version.toString(),
193
193
  },
194
194
  });
@@ -210,7 +210,7 @@ export async function updateEntry(entryId, fields, config, autoPublish = true) {
210
210
  * Delete an entry (unpublish first, then delete)
211
211
  */
212
212
  export async function deleteEntry(entryId, config) {
213
- const { spaceId, accessToken, environment = 'master' } = config;
213
+ const { space_id, delivery_access_token, environment = 'master' } = config;
214
214
  try {
215
215
  // Get current entry to get version
216
216
  const getResponse = await getEntryById(entryId, config);
@@ -222,18 +222,18 @@ export async function deleteEntry(entryId, config) {
222
222
  }
223
223
  const entry = getResponse.entry;
224
224
  // Unpublish first
225
- await fetch(`https://api.contentful.com/spaces/${spaceId}/environments/${environment}/entries/${entryId}/published`, {
226
- method: 'DELETE',
225
+ await fetch(`https://api.contentful.com/spaces/${space_id}/environments/${environment}/entries/${entry.sys.id}/published`, {
226
+ method: 'PUT',
227
227
  headers: {
228
- 'Authorization': `Bearer ${accessToken}`,
228
+ 'Authorization': `Bearer ${delivery_access_token}`,
229
229
  'X-Contentful-Version': entry.sys.version.toString(),
230
230
  },
231
231
  });
232
232
  // Delete the entry
233
- const deleteResponse = await fetch(`https://api.contentful.com/spaces/${spaceId}/environments/${environment}/entries/${entryId}`, {
233
+ const deleteResponse = await fetch(`https://api.contentful.com/spaces/${space_id}/environments/${environment}/entries/${entryId}`, {
234
234
  method: 'DELETE',
235
235
  headers: {
236
- 'Authorization': `Bearer ${accessToken}`,
236
+ 'Authorization': `Bearer ${delivery_access_token}`,
237
237
  'X-Contentful-Version': (entry.sys.version + 1).toString(),
238
238
  },
239
239
  });
@@ -19,9 +19,9 @@ export async function listContentfulPages(config) {
19
19
  // Map management-style config to delivery apiProps for the CDN-based read helper
20
20
  const apiProps = {
21
21
  base_url: 'https://cdn.contentful.com',
22
- space_id: config.spaceId,
22
+ space_id: config.space_id,
23
23
  environment: config.environment || 'master',
24
- delivery_access_token: config.accessToken,
24
+ delivery_access_token: config.delivery_access_token,
25
25
  };
26
26
  const result = await getContentfulEntriesByType({ apiProps, contentType: CONTENT_TYPE });
27
27
  if (!result || !Array.isArray(result.items)) {
@@ -46,9 +46,9 @@ export async function loadContentfulPage(name, config) {
46
46
  // Use delivery API for reads
47
47
  const apiProps = {
48
48
  base_url: 'https://cdn.contentful.com',
49
- space_id: config.spaceId,
49
+ space_id: config.space_id,
50
50
  environment: config.environment || 'master',
51
- delivery_access_token: config.accessToken,
51
+ delivery_access_token: config.delivery_access_token,
52
52
  };
53
53
  const result = await getContentfulEntriesByType({ apiProps, contentType: CONTENT_TYPE });
54
54
  if (!result || !Array.isArray(result.items) || result.items.length === 0) {
@@ -1 +1 @@
1
- pxl:v1:4cafcdfc486273b5291aa952:a56d5142b18338e2a3419627c0fc4d80:bd4ba14fbe5adcc35537a30c250c48bd82adf8cb6b602fc645da59e39daf913952c2c091d10624182b38166dc88e28e1be616f7a2a6106e0a72329cf12ca1527a1837ec50a556bff7d9d93fe416978950f5270ac26f8eb36d833f185e0ef9282c735a5d84f65a8eaf7c7b95a38206998952b77204d82417d380c28dad7ac8802c6b8a68025c44b6d8402761df93273dfab5a5b6051d52d267bfef6a353b7c1a3bce8ba3e6e4ca1d6195537fef7b74cf4d631f65fdb1702019e531c16513309885bdb0bf99a9acd2cf8a8ab84c8cbbeb2c20bc0653f2da6a465e4eaed11ed7b59e75a7d7e735fa6bede92115646382c7f238684c08fb8beea0a4c215f005f47cbdc9041f1dee79b625495ba4e5cc693c3988b9122c46bd1ca65363d65b30b0ee99b05af6b3c653597dac3f1d1db5c9fbb3ccd53a3c432913211e10d5cec4338e0b48b8380f2be0f3e34aff764d334d334926b6d97192b12b768642a55d3d8af298672717f89e198952deb50282f1ef55c2a4cd7c34d1eabe92df783e1b0a39bcdf5977b8668f62b0b10aaf4c08efd80172e5f07272464d47bc3ed326974cc45ce295c4eb1c099450b14938493a7220231eebc6a26e8abba4bbe1e3e4b937e3220abeca0d5ba9ab9f9cea409914f812ee780b1aaa919bb6160e9372a422d4174a18fa07da05543e3d407f9cc5c14f2bf12b1c4c1e73d9cd31039eaa42bbc41c07646c5be721d1f07eb29a6005a28eafeef43c2b7ccfaddf3db498a7b8429df75772826935db24ac0f3f9c586087e92080bfca691b98273824c51347415c84a82f681d91a38d6ab1bbc7ab65e1661633c2b5b7f768bf3145bf498fa443008b17ae7025b39a4b45898e3781d0bbfcd57670e07b4e67d2b6e14b970c0d82c48fc07b76254e6c68008131159b69eb6a90d29fb818fe5cd1bbbb38c927abbed03e9101cd8aa4ff62c051497df91c11d6b7770e8788c241e6f8046226596fcdb79022bdb68926f4adf567d3c9ddf462b57c30154f9c9cde588377819db6be4b8436a1b78ba10b08e795aae4f21593c083f7fa3d0dc8992c66ba5ea4933a5e8ec24421a2f33e334957eed81de8dfddce307b464fbc37c9053b83d8a499937fa6b6a32559c62e0cdd35a41e0d9442b857a050fb56922d52ceeda1ba9c2ab96c2cfb54dd014ad6ade298ade7be8e2eda1c809e429a4a4ea296bbb5d9b475f0c51f4ce2982d54ed5ea1c13b193a89a2a9cacab91ccff98c74312a057e5ba911796e7b14f0d0ac8506eb9c958ee7fec575984394b9920cbf1a21f77a02f518f03a5cf950dfcc000adff9757ddfb447377464912eacbcd8f5be50b3944c01062aa01c5c1b36198f7b8c75f53cf754c896e73cd1a0a8102fabb1e10815faafd556705c219c081a6e6e1803639789cadad8e24f6d3ed5b519936eecc806e8d0a793a761b2aeba0b0fe4fe216d7fb18fe716320348c717356306570bd6b6070e59111236171ce49e17c4f3953ac856b172887d0fcbc88d3633bfb69925c0029bfcfd73551667f2017ed274e4c86d7ec6673346965368e47e1b71b4775a8a03aefd99c15d5ae484eeae9dc5ced38e40deae11eff719928347535d661ea2923d0ff7100729d09e1af25522719b3ef4bbbb8765015a733c8e2f1410f99807eec19f8dfd6db414de4b3910ec4475fd7005bdffba9965070beaad022b05980e661e4cf1bcdecb7f0ba654e706ca01bbeb1368b8402f14ae93818d76d35b80b28a32157fc12d12b3e053650d13a74c405ab1dbe5e940ea9e5c7b22143528007db5fef88758b5cd03e219318bb787d61ffef11d09c674c05d594671a3e86ed4fddadd609f02999bb02a57b26b05607895f1ec5e4eb03c98da499b9acfd357ca2497c440800b8e5fb8b2f9b54490f6a77f582ff69602e5dad99f46c556aadfe66d9df2fda8cbd887f31082ba9daa18774eaf7b28048ed0cddaf2f5c73f65b4cdaefa7d1c0e93ecfec983bb07a9aa3bd62ce4f387bd2947ecced921f43523559b0e847c3e0d88ce55cecdc2b1eb17a2d2de24e3817e0ab77f1b463106d51c9aaa05bbb4d52b9d89649fca7bc8545451a241d64ff141697c22aaa67b54de79eee22987ae2673b6e82f92360a1fca99ff122ccdb64597632a742b97592a8c0136f6c3aeaaec49db657d66dbdee72d711e716718122b527b88a242f2481fcb97a3403d54d85c3c42bb4a14b7bb785f4f2db48ec38a1721789e03e2f75bf9d8ad5134f5afeaa802a8c61166b75d7e1b96a928fb3f20d963f4235bae1bb8553453cdbf1c27fbcfb913566565595c8bf7a16d0aba8d5be63e08e79505191151da3eb8328bf6687c351e27e1f61621e3eaaa7784c4fd55fb62e9d33804328c59e749af71bc3248e17762e9095e6192a39e1afda1e07fe2f6b05d457e7ce7b0cecdeaf087efa61e2c4ed4cde9f46692d7719305dba811080ec56d35e6207e3a3796aabe02c89c8b1d7c749cbff0292f555a18e4fbbd5457e1c2aa4f9c63987fea066d37f0c3bd642914f0030bc72bcec5fbaf9d445b9a36b5638cc1af5dffd9dc8cd395f92a2d5b44c4d261bc2fe1bd1fff5d9e60f58c99d20a57be20c081ec1d37ea7f5a7017459cb68e27aad952c3d3b6a4b10b34cac652a12fdc293c3c5d626a87f9c4f65eb89a1c01db3c8f340ff0e5bbbed5fb2453d934821847b7cedd83b8e7761a427fc167f501880cb3402daf3156247e9b237bb0c2a37ced4516102897e6e9ac939d797cf8e35efac71ab6e93100d35450822e0bd17dbd5077cb278958d2202c8a5536b079e95884d2072f72d6ca20a6af87f276863f47e729c886e1340a8274263c099329cf2b663ccb86a41c4c0eb32dc0d0d700832dc51af47adb36b15eb7976caceb128ffb3a46411878dac1f281e878a32df401c1b8f7efb77f77d8124b1a2dc11cbb3e7bf31d1076773d13ad6e5d878fe9d95b36814623200eb229ba307ae8dc2aebedcbc624351e9d23fc7e1e0e90b458ea1993122a2b3d3160a62f860a45d08e54b61129aa6d8b98137163e9096fb4da1f58c9c845ba6e4386672cb868c41686d2a1897b0fb0f6a0c337b23d62184200251298072dabae257a8d67e9123b8fab4eaba775ce65b1526b157c6775f7505315f68219fa099c91161db70b153f303134903a6e1fec9525c01c1be44589e9fa95a57c2c8def7cc0a9d8f971e26526c62bb95927f2f7c96d443c0008229af21f2efcb5a9eb9e0051bbddc6e143430ac02a35ab899f2ef1b81db3aa86143b3dc1b42b426dcfb70065d3b265a5984864ae5515ae2250b0d96a38cd548fed9684a02c6227ec5fe37e1fc710961b87dd50ce01d8858eeefc7734f98bf2669572a5dc0b98210400cdb84b5f5c95d5dfc2e2988f8886c340ddc5f0d22325473af8ab2c7a9ab3ad0e8a74b855381edf66d9f6a516df51af3e7ea07a482c488e0b7b08f4b1be0d9840b35e3043336792cf69c068fa8044e76b5a3b05c4b879de2830ff2f0628f566850ec11c0095bd7fbe9f6de449a3087f809c3fa1a12d9ad307e80904778b645b26ae26a0d168b1153aca36f427fea1c22be0d464ef6a7f924595231fcda031bd791020b4348bbd0a735147f99cf95f6d0013dff3c9a827d62d581fccd998c103e6f99ae4be7cb24d5ebf05cb534a403a112eba587dbac95ef981d5745dd23b4551b3a7a71e1ea896c43c4bcc567ec06917548c87d82f6ba6a3e0722eab1a975033a5bce6abd7fa28b815c84601ca2b2ead6d0fce2562252b0dfb1def66afc9c4e3a8f0569b5a3e7e21f6b4690ab538d4e1a30a47417859f596c1f240625d05aa3314027c4a46e1c7c333ca010e69631a5e6edd45d013edde553dab4333fff66a3c79bd683337e74bea1ba0c851d3c200f9aba5ce0876713639ba2cb17c2cc754e8b3f5140691b3663a00ee94869a16191fbe8e1955a2050364abe90939321ce14cabf2e58611474cd2d59fd6f92705a8f50348297edafd798ccb010713c940dad5fb40bcd15b7ea2950a3def71b0998b9383cb85d04bea7b4b14b6e518931f5e33191e1cfb028049d4353eca1b2abae38fe6f7df3da6a0b3503b092a4b8adb2775f453a3f7b4349dea4220077db5f5370aa7479eb7298d831794518ca721fc62babd8e6b4dfb577e9f72b1a3da9542bc8ba6f27fb5140f495d7678ed14883d7fc6c68ff1b4de9f059f5ee049cf50568e679c6f1a34f1edb77fa6f7e6c2cd63482a3d1e3a3b16d00f1d0edb8c72afbcbcebeb80c13c7a6fd5d9cfc1ec521df9529a1e0c4a3b6e044bdf56741b397cf49fd40dc741f24cbb60c7fad2cfdf4e9094aa18b254f54aff303ce8a0dee2e092e55517bc9776339fd8a3f6fdb82002fb9433eb9972df58a9c44666c7eae475a27a6717b0e40ab3e79f5721b3a75d5008a1c483096a6fac0087d3e00628a2e6b0a6ab4325b831547adcd2a7cf7827ae0abc647fd4e2404b3f65a714bf370b0ae9f6f7fc2a6de136258763a3760cba08f7522bf09306cb5ed8028c71a7feb00b2133bd6b3ab21a5118ccd349d503ca0097856ce40543b14e0af2d5f07c0998cb68bbcc21177cdb86d137ff33e67cfabea51c9dfe6e396d7b3368e3ed2392e72160a88c9cf3a8effdb20c51672c74639abad7a441e3714f0ae3bcb52390981c71c9e4d68b6b025b7d9212993e294762589f6707deb30a43e12b2457789024545c0a8ccecdd0764bedb73d6b50aaef3d661d7cc186c9975907da701c4ad5641f59c404cd5553d87200c2ddcbab819f8ad99802eba8f0861df135d4d017a17c5be09be0c062a0d25f008d0f0f336f7afca592135903e72c1a9c3237affae7fa718b9b9efd114cacb8650fa669569dcf03274bae6f0621deb3c26441fb665723543054b587aa6a5a802642a46681ad40ba7823e6292e193ff4d7cddf18028fa883fba7d050797d5c5250666869bbd528bcce34aaf8e479c3e72e12c54a92ec6c4f82902de30b0560b4877a8ffdc62b90bea531cbfbb0e496e9b392113bd667b615674a645dbf27f3d4dd590549745886d9f9a63c5e93daea20fc6742aab62bceff1401e36b8953860271b17d494f661a5ca7b8463d05a84870b85bdbd9b9a04e8d09cf18da76902cd8e51e4c14012e7807d68a53a60c053a23cee570e93ec50cfc00619dcdc132f842cfdf17b0f9bb79979f7e5f7
1
+ pxl:v1:cb4a6489e69c34388bf9b3e4:6f420b0316cbc2f7b8ff526175372cca:ab175029a0c5d9aeb1c7c69ac67db745304191463e5338ea9f711f8c93aad8c1549369a06eb6c1bd972e3a3bfb3ccfb851f2cce837b48542a51dd2e1a23303dabf2db0734caa0fc3006d6f1e51b1c5bbd203e74c1861ef350c4dd8a266ea38f723cac9d964ae8abf34d296af9327284a8dd3eab3f8821920d5995e465067f4c5052c14165a9d9fb316f5a9b8f4a55abc618974df1ad98569bcd5e4d64430a9513287d56a1c0fbc455d7d2ce13bccc99554814268edb61a29923d305690707d7f91c6de1caa1ad72409b3aba95fa82744293c3e5c28c2baffa2a0441c0bf0ffae8cc6dd0678cedb9cd67dc11c582c37714d6cdfaa4a0e80c0e61d5040d678cd4d9332b65fc20074406f44f99ca82c23ecdd64b07f681d0fcd1fc0fd1992250e2245bd0c8df616053e6c0d9f076576a1d30c1ac426f7c00848aa657239e54e87ba4f34a8579b005d9f3ab3d326aaaed98d3f510b32514a2b0ce155ba611286521bb726a45377fadd2d27fdbff5a905f0b213805a5a04a1d09dd1ec46dd5a633e5e522add289a6ce1a68bed32226d74a43adaa5972b93021e2f90a92231c2438012277081bed2df304070ae44776807b09a5ff650a285429504c52fa60f2b02ebd0c421cc06f27694d49aad5423fe496cb2b4d3d69330ca095931e1bdc45d27d4e1f97ec887eb4303f782ab051490c3f820ff94c65464b9718747aaa2bdc5d71c21ada02f3934e5843b63f6f47ad39b61a8a260d2b2278f14ac9b891b6fb0cc9b24d0d635358459d40674e74f1c3f1fdd68566930606eb59e870691d67b334a96e3e5672a111d6f393475f14c805cc154f6c744322c417dee4e3457cea509ab2871257bd5f115ddf1cdf266795374267ef415e1da35708bf897a56d87d89596bee19d244900ba0a1a3c216f3ebb1055efe1319f0fd806afe6332f9700bdbf4585dd1ff1729d46804cc7942e98ea2cbc3cedbe9c3f79130099ca87b78caa248a1a4a71f9f8d198b54073e5a34d028be15ee4a0dd2d10d4ac2e82cf3a2cbe4019aeaa382a167b738ca4293c4d61d479b11f05216e378a064afe8a2bd9292bba48d684fa83a5d910a24b9e8f5ff2fb2fcc1a6024dba4d16565ec403cba779f6cbba00ac582b197af0c2493d3eeb02303e86321832eaaf9ed109f093a7c685c0e12030b3b06ae90c7387106d41ca45b606c1f6c20982b892f24dd541d5beca2e492d18c7cb3643e0629040db750e6c85997ba11ce677fdce6cee16d065d2390cdb22cce45f5e1f9c187604febe71057af13f3cd820da97c1712e0c58cb02c5a111cfc5f5ff0adbc44f9bef8ac9cab94b76239c2e4c15dbf02ee336e3970fb39b7e9389e184a518e3f0e18cfc1dbc571935f6aa1cf3d7e7440902aa6a8a3a7be8faa589ff2c905146b240937385c0a9f69ad7d11419a28c170ccc31b2fda851e82af5f782c1f51b37a1262d67fd8a68d3afdc3331b6b2581e779ad1a6072332d44d7350693fedec4f4a072e55c79175eff6a7c3fee115736a36f26b0999aa38570c91f71c7290fb82d4b5f19eba1e0a6bcf37b62d831629603cb47e618abd01ac6fb3bbecfffc263d946fc388b295300944b064d67b27f50d0544dca746973b6427b85527ed72ba846f3149788deaa04ff66812adfbb594f994af0b227081b6e8d9f303370a88d3907d50acb0c38980095d76c27df3259adf0055d7cb0d774cc78d4a4f684338f2135ed2098be70f386dccda9bc1b5d6be28e586763f9540f199db3509d18ce04a53ba7e3da77f82d197f8e2a11430d65c3a4498b01abe86e6aac3c159a3c6cdf3ede1be2875372e9cc6bb8f1ee787de1193fc28e5c68a3ae9133cab2d8b34bbffb2dc3cb3e02ecc68153c6039b3e64601fb70180a1ee4358c46ff379c832bcb47a8e17a5751c6fa7e39f21d23d74eb55d46004d978594dc63f01ebd4033df4fb2a525c80acedaff402cce7691e396d508d265068cd1e55f057432e7710f0b220c1cd249a670fc84962adac60c94f3670f021f060fb5518afca7ed52717cb90029dbe4c491f2f1c05a8d7d07c3589ba8faf3f2e9f170be1572db5e3001d90441ac4b486cdf9cee8d5f827b563d5f3bdf428fe6fa45d71985af27957065e91f440fa03fd0bef44bf87d825ca995e492391c60a8bb7168cb591330aefd1abb4ce2ea1007223fd4f5135c11fe1d5d26fba5944ff9daf852358cd8ef2a2360d2069b4e80f421c75122d9165cf22ddabfe5b2e31c1aa7bea8b8c9ee94e3f7cca3c9cc915700a789f88abf3c6929c0773f51d38e39224652bf8bdca0cc2744c1c8bd052ca619a80a11ecb93faf3cc8cf1ce83c0c88d0d4566fa689329c22c8551fea8961bdd86363aee3f85170ddc6db28b22c41cb00cf87b03441474e2b988118582c0b9d4578fbcb66b44a905e7c25e66ae3de085e6ff5a1149d4808f077ca5481c3d3d257442cd7ff0b7664d820a416c8728d01819f5cdbd3f26ca7d9456920b9000951e16b2e211929cd9e98e41e7c48161deac3cce9cacdf43a04351b25678724ab1029e66c64218131266c7791431939083ee5b125f5ef6d4fef0cfcfdfd812441ea15beff8c93abe1521beec86378c91818a29225045a4af5dc5167b9cdf0e20df4f2dd600355b906716177a8320edf4789d495b60ecc69ca73866973c68200c26d37d47752a0fba3c06e178bfda2cf2824c93d25aa19aa72ec75d7d8787963378ba4212549e5710a366407be73e9671383e37bba45deea3959a38ab6d7c1e0b45ac4be484b873916b19eff8b50b909ce74e1e5eececfe9041b4d33bbdbfd221f5ae301165dbf890084d3ca36f2f91da7dbea85b05134a0db5c2f782b12d7bd3817cd392817d6e893d34c03b4551dd282c949cea5e7ac00b9b70a5a80925bba1bc3f514b1c43d2f82e97a523d6eaddb0d12168472866b11f2a9025d2facb0eabeba21018c8dd8794f4ecf3a1126dbc75a060bb029ea1b4f398d9690de76c723faa7d8e71050cdafb17a94c769dbcb8bd21a880bba7c863df765ce59089d357d356ac8ecc942dadece79d71b8dba7a7f18f93e96eee1ca05e6b3ea04af947cd0ad21d28438d1ed50bdc0573b20b73f2429d7bfac52edbc9d8b898ec28fdd80d7f23b67391d3b715a445bbeb86336abb27054169c4fed409262fcf737a113df680e6fb1d60d1cfd679584b90f1b302dca6d84c2f62074a70542467263cfbac4379e2795f7c3b08fa44adf998d90a6be1859153b364f5d5743b7febd8cff1918f44877cad7f76954b4145e7cb03ad3b16c338ab9535435a4a86bbbe36a11bfda3190fa4b561ba50b2691a0b6a60b42553bcefa48914238f8fa08f32184e279c5e94bf37e82f5ac0066f89bc17bab8d4221a0d9dac7a640ce3958d65c3d5e5f896de66e70800017b61308b62c9ebd4ddd3cffdc3a74e208c0b5bfb05ad93309704a2f5c143b9d9ba957e920c15954d4347f6cc0caccd45e911aaebb5c2e64711d18b58854faf5d2ffe3612f77ea82d5c672c67007968e37d52f24c7efe36d800614cfb6fa128fd211683884d5487c624370201a0619e29689cf944fb7af6b03cf242aefc34eb92ead068689e1877040ae89b2cd90ceb1da5319c610024376545058e493fe8e38a79ba6b6a2a3d594b07fda088ae436a601f89a379fac5af5bf77e892a4e937867a6bfd424cd01d0a75918e21ddb0bacc6db0d1863ec7c57375ee2bca147327a616294fed2fff515be532adbb78ede2e5147115028278baf9d9ff64cacfad454ddc581fc5c85c8738c3b1243db430e5a8a492f95fc8fec33d8f050784edc8848e9001a33680a3e5ea88bb457f9caca3ac8693084c13c593480a8a93810c7cb34c6c9558ec3c186a8f754058d33d627574718cd50eab6040b575ad214ddbf2f332de8326ea46f2b6a081c3c61882f97ba9d726aab97101506253b3c8b4c922cba9fca9a8c4bcf744f327bdd615d4d38cfc7d0d2c8178bb735459bdc40c879ab05b3301eb9c27aaae044e7b65c68fa21b304beeb71731de29a271437948e04d70b86c4a8f518b0c3ea29d209ca5e90fa0a562b5b22318e1712ee0082200969ff5a2d91389206537295aef15d0a6830e8e5e34d7101a85b50b5101cabc3db885369cabdb993349145bfaf841dd9ddbfc82baa620eb1d9b905db1cc2b1bde20f636212fb40a473315879d7cc2088389de7bfad757f717b677e9c17c8772f784d7f0c93893629641d7e6942fdade396adead18fe3e3b8e0c81e4ac178fa7cfc01c85390b597ad31eb375805e0e4175a7d7703bd6c10b6f7a2c013546e88cfa9b8f4935c109c7a4e8bf31d4a586202801aed8be490e0ba9a0d93d8d709f1760f20d07802c0d2ff36bddd4d470791614984d0838e91bad3d3f6a3b7a5d91ab0b6ad6ed1c1a66cd3f98eff7188cda2e7912270b547d577d0a31f1e94b6c4fa8bced14543e98d91fc10d4d18dabed1d47749eb4601f5bd7466bb5cfb07f810c1119839126d8e249fba398b52a2c92d08e9f0e26c0ca4402d5ad019598e2e737d76435fe22b53cab4b5f8c75df8f747393b9b53114203a3cd8fe0aa5486652b1c43cefc16c5cd04171ad43cae61d4c3ef2da3d2f3ebdf95408b504efcb34900d6f806d4802deecba1fbfd56083caf24d14759be2d1cb7c9ac7d1747ac989a3c46f0c2a61350cc260186895c5aa88798b9aae78674c0c74332d8b4c5d6b62f478775e3b4caec1b846d8e74c384ffbb1db92bc14d1bcc0277786d3d66132241edc0fbf561e14047b1e61c27ca871feab562f1f47c689d595724240971ce2bdda571317cda4ffaca9a78d16f6f36b4efa49b129afcbbf054b9e61704fb344ecd7b089f31d10d774cdf526d00c30ff008225f276c26c4a4ada20752614d5463167fe319b20acc9fd625121d83314d212f0b0e3ea5ab1a61e4d0237df5f185aebe49d88a09bd3294944acb20377e475d2e04a5b68c34e192910df2a83aba2cd6e0eccbda77d47dde51a11a1cb166012953be980e97a6ee9c16e81f7b9d721126320f5c3478779fe7134e94fd91a76f00166a2d6248263c950e93e071f094c912a15427019f1eaf9aeff3ee9a8020af4d78c9088e5e5c2e4c29f7b263347217a669e1868161965d0813854a7a3e0415826964248539c8fd47c6bb43a58d73635a8b3a81486d6bc26f1a8179d85
package/dist/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  // sorted alphabetically and grouped by folder for easier reading
2
2
  export * from './components/config/config.client';
3
3
  export * from './components/config/config.types';
4
- export * from './components/config/config.utils';
5
4
  export * from './components/config/config.validators';
6
5
  export * from './components/general/404';
7
6
  export * from './components/general/accordion';
@@ -6,11 +6,10 @@ export * from './components/admin/sites/sites.integration';
6
6
  export * from './components/config/config';
7
7
  export * from './components/config/config.server';
8
8
  export * from './components/config/config.types';
9
- export * from './components/config/config.utils';
10
9
  export * from './components/config/config.validators';
11
10
  export * from './components/config/crypto';
12
- export * from './components/general/manifest';
13
11
  export * from './components/general/hero';
12
+ export * from './components/general/manifest';
14
13
  export * from './components/general/metadata.functions';
15
14
  export * from './components/general/proxy-handler';
16
15
  export * from './components/general/resume';
@@ -22,9 +21,9 @@ export * from './components/general/schema-recipe';
22
21
  export * from './components/general/schema-services';
23
22
  export * from './components/general/schema-website';
24
23
  export * from './components/general/sitemap';
25
- export * from './components/general/humanstxt';
26
- export * from './components/general/utilities';
27
24
  export * from './components/general/skeleton';
25
+ export * from './components/general/well-known';
26
+ export * from './components/general/utilities';
28
27
  export * from './components/integrations/contentful.delivery';
29
28
  export * from './components/integrations/contentful.management';
30
29
  export * from './components/integrations/flickr';
@@ -348,6 +348,52 @@ const propTypesJsdocRule = {
348
348
  },
349
349
  };
350
350
 
351
+ // ===== RULE: class-name-kebab-case =====
352
+ const classNameKebabCaseRule = {
353
+ meta: {
354
+ type: 'suggestion',
355
+ docs: {
356
+ description: 'Enforce kebab-case for JSX className values',
357
+ category: 'Stylistic',
358
+ recommended: true,
359
+ },
360
+ messages: {
361
+ invalidClass: 'Class name "{{className}}" should be kebab-case (e.g. "callout-title-text").',
362
+ },
363
+ schema: [],
364
+ },
365
+ create(context) {
366
+ const kebabRe = /^[a-z0-9]+(-[a-z0-9]+)*$/;
367
+ return {
368
+ JSXAttribute(node) {
369
+ if (!node.name) return;
370
+ const name = node.name.name;
371
+ if (name !== 'className' && name !== 'class') return;
372
+
373
+ const value = node.value;
374
+ if (!value) return;
375
+
376
+ let text = null;
377
+ if (value.type === 'Literal') text = value.value;
378
+ else if (value.type === 'JSXExpressionContainer') {
379
+ if (value.expression && value.expression.type === 'Literal') text = value.expression.value;
380
+ else if (value.expression && value.expression.type === 'TemplateLiteral') {
381
+ text = value.expression.quasis.map(q => q.value.cooked).join(' ');
382
+ }
383
+ }
384
+ if (typeof text !== 'string') return; // skip dynamic expressions
385
+
386
+ const parts = text.split(/\s+/).filter(Boolean);
387
+ for (const part of parts) {
388
+ if (!kebabRe.test(part)) {
389
+ context.report({ node, messageId: 'invalidClass', data: { className: part } });
390
+ }
391
+ }
392
+ },
393
+ };
394
+ },
395
+ };
396
+
351
397
  const requiredFilesRule = {
352
398
  meta: {
353
399
  type: 'suggestion',
@@ -871,6 +917,7 @@ export default {
871
917
  'required-proptypes-jsdoc': propTypesJsdocRule,
872
918
  'file-name-kebab-case': fileNameKebabCaseRule,
873
919
  'no-duplicate-export-names': noDuplicateExportNamesRule,
920
+ 'class-name-kebab-case': classNameKebabCaseRule,
874
921
  },
875
922
  configs: {
876
923
  recommended: {
@@ -886,7 +933,8 @@ export default {
886
933
  'pixelated/no-debug-true': 'warn',
887
934
  'pixelated/file-name-kebab-case': 'off',
888
935
  'pixelated/required-proptypes-jsdoc': 'warn',
889
- 'pixelated/no-duplicate-export-names': 'warn',
936
+ 'pixelated/no-duplicate-export-names': 'error',
937
+ 'pixelated/class-name-kebab-case': 'warn',
890
938
  },
891
939
  },
892
940
  },
@@ -2,10 +2,7 @@
2
2
  * Component Usage Analysis Services
3
3
  * Server-side utilities for analyzing component usage across sites
4
4
  */
5
- export interface SiteConfig {
6
- name: string;
7
- localPath: string;
8
- }
5
+ import type { SiteConfig } from '../sites/sites.integration';
9
6
  export interface ComponentUsageResult {
10
7
  components: string[];
11
8
  siteList: SiteConfig[];
@@ -27,7 +24,7 @@ export declare function getAllFiles(dirPath: string, extensions?: string[]): Pro
27
24
  /**
28
25
  * Check if a component is used in a site
29
26
  */
30
- export declare function checkComponentUsage(sitePath: string, componentName: string): Promise<boolean>;
27
+ export declare function checkComponentUsage(sitePath: string | undefined, componentName: string): Promise<boolean>;
31
28
  /**
32
29
  * Analyze component usage across all sites
33
30
  */
@@ -1 +1 @@
1
- {"version":3,"file":"componentAnalysis.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/componentusage/componentAnalysis.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,WAAW,EAAE;QAAE,CAAC,SAAS,EAAE,MAAM,GAAG;YAAE,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAA;KAAE,CAAC;CACnE;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAezE;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAoC/F;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAuDnG;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAC1C,UAAU,EAAE,MAAM,EAAE,EACpB,KAAK,EAAE,UAAU,EAAE,GACjB,OAAO,CAAC,oBAAoB,CAAC,CAyC/B"}
1
+ {"version":3,"file":"componentAnalysis.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/componentusage/componentAnalysis.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE7D,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,WAAW,EAAE;QAAE,CAAC,SAAS,EAAE,MAAM,GAAG;YAAE,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAA;KAAE,CAAC;CACnE;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAezE;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAoC/F;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAwD/G;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAC1C,UAAU,EAAE,MAAM,EAAE,EACpB,KAAK,EAAE,UAAU,EAAE,GACjB,OAAO,CAAC,oBAAoB,CAAC,CAyC/B"}
@@ -2,11 +2,7 @@
2
2
  * Deployment Integration Services
3
3
  * Server-side utilities for site deployment operations
4
4
  */
5
- export interface SiteConfig {
6
- name: string;
7
- localPath: string;
8
- remote: string;
9
- }
5
+ import type { SiteConfig } from '../sites/sites.integration';
10
6
  export interface DeploymentRequest {
11
7
  site: string;
12
8
  environments: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"deployment.integration.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/deploy/deployment.integration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACzC;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACtC,OAAO,EAAE,iBAAiB,EAC1B,UAAU,EAAE,UAAU,EACtB,gBAAgB,GAAE,OAAe,GAC/B,OAAO,CAAC,gBAAgB,CAAC,CAU3B"}
1
+ {"version":3,"file":"deployment.integration.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/deploy/deployment.integration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAI7D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACzC;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACtC,OAAO,EAAE,iBAAiB,EAC1B,UAAU,EAAE,UAAU,EACtB,gBAAgB,GAAE,OAAe,GAC/B,OAAO,CAAC,gBAAgB,CAAC,CAU3B"}
@@ -1,21 +1,11 @@
1
1
  import type { GitCommit } from './site-health-types';
2
+ import type { SiteConfig } from '../sites/sites.integration';
2
3
  export interface GitHealthResult {
3
4
  commits: GitCommit[];
4
5
  timestamp: string;
5
6
  status: 'success' | 'error';
6
7
  error?: string;
7
8
  }
8
- export interface SiteConfig {
9
- name: string;
10
- /** Optional: explicit repository identifier (e.g., "owner/repo" or just "repo") */
11
- repo?: string;
12
- /** Optional: remote name (legacy) */
13
- remote?: string;
14
- /** Optional explicit repo owner */
15
- owner?: string;
16
- /** Optional local path used to derive repo name if needed */
17
- localPath?: string;
18
- }
19
9
  /**
20
10
  * Analyze git repository health for a site using the GitHub REST API.
21
11
  * Expects a GitHub token to be present in the master config under `github.token`.
@@ -1 +1 @@
1
- {"version":3,"file":"site-health-github.integration.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/site-health-github.integration.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAMrD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CAgHnM"}
1
+ {"version":3,"file":"site-health-github.integration.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/site-health-github.integration.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAM7D,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CAgHnM"}