@pixelated-tech/components 3.11.2 → 3.11.4

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 (38) hide show
  1. package/README.md +17 -106
  2. package/dist/components/admin/site-health/site-health-axe-core.integration.js +1 -1
  3. package/dist/components/admin/site-health/site-health-github.integration.js +0 -1
  4. package/dist/components/general/tiles.css +132 -32
  5. package/dist/components/general/tiles.js +12 -2
  6. package/dist/components/sitebuilder/form/formbuilder.js +2 -1
  7. package/dist/components/sitebuilder/form/formextractor.js +2 -1
  8. package/dist/components/sitebuilder/form/formutils.js +13 -2
  9. package/dist/config/pixelated.config.json.enc +1 -1
  10. package/dist/scripts/config-vault.js +7 -5
  11. package/dist/scripts/config-vault.ts +7 -5
  12. package/dist/scripts/pixelated-eslint-plugin.js +300 -0
  13. package/dist/types/components/admin/site-health/site-health-axe-core.integration.d.ts +1 -63
  14. package/dist/types/components/admin/site-health/site-health-axe-core.integration.d.ts.map +1 -1
  15. package/dist/types/components/admin/site-health/site-health-github.integration.d.ts +1 -6
  16. package/dist/types/components/admin/site-health/site-health-github.integration.d.ts.map +1 -1
  17. package/dist/types/components/admin/site-health/site-health-security.integration.d.ts +1 -8
  18. package/dist/types/components/admin/site-health/site-health-security.integration.d.ts.map +1 -1
  19. package/dist/types/components/admin/site-health/site-health-types.d.ts +2 -0
  20. package/dist/types/components/admin/site-health/site-health-types.d.ts.map +1 -1
  21. package/dist/types/components/general/tiles.d.ts +9 -0
  22. package/dist/types/components/general/tiles.d.ts.map +1 -1
  23. package/dist/types/components/sitebuilder/form/formbuilder.d.ts.map +1 -1
  24. package/dist/types/components/sitebuilder/form/formextractor.d.ts.map +1 -1
  25. package/dist/types/components/sitebuilder/form/formutils.d.ts +0 -5
  26. package/dist/types/components/sitebuilder/form/formutils.d.ts.map +1 -1
  27. package/dist/types/scripts/pixelated-eslint-plugin.d.ts +124 -0
  28. package/dist/types/scripts/pixelated-eslint-plugin.d.ts.map +1 -1
  29. package/dist/types/stories/general/tiles.stories.d.ts +12 -0
  30. package/dist/types/stories/general/tiles.stories.d.ts.map +1 -1
  31. package/dist/types/stories/sitebuilder/form.honeypot.stories.d.ts.map +1 -1
  32. package/dist/types/tests/components/general/sitemap.test.d.ts.map +1 -0
  33. package/dist/types/tests/pixelated-eslint-plugin.exports.test.d.ts +2 -0
  34. package/dist/types/tests/pixelated-eslint-plugin.exports.test.d.ts.map +1 -0
  35. package/package.json +5 -5
  36. package/dist/components/general/sitemap.test.js +0 -19
  37. package/dist/types/components/general/sitemap.test.d.ts.map +0 -1
  38. /package/dist/types/{components → tests/components}/general/sitemap.test.d.ts +0 -0
package/README.md CHANGED
@@ -85,8 +85,8 @@ Pixelated Components is committed to creating inclusive web experiences. All com
85
85
  ## 📦 Installation & Setup
86
86
 
87
87
  ### Requirements
88
- - **React**: 18.0 or higher
89
- - **Next.js**: 13.0 or higher (recommended)
88
+ - **React**: 19.0 or higher
89
+ - **Next.js**: 16.0 or higher
90
90
  - **Node.js**: 18.0 or higher
91
91
  - **TypeScript**: 4.9 or higher (optional, but recommended)
92
92
 
@@ -105,10 +105,10 @@ pnpm add @pixelated-tech/components
105
105
 
106
106
  ### Peer Dependencies
107
107
 
108
- This library requires the following peer dependencies (install if not already present):
108
+ This library requires the following peer dependencies (install if not already present). Versions in examples match the package's peerDependency policy:
109
109
 
110
110
  ```bash
111
- npm install react react-dom prop-types
111
+ npm install react@^19 react-dom@^19 next@^16 prop-types
112
112
  ```
113
113
 
114
114
  ### TypeScript Support
@@ -162,86 +162,12 @@ npm run dev
162
162
 
163
163
  Visit `http://localhost:3006` to explore the admin interface and see components in action.
164
164
 
165
- ### General Components
166
- Reusable UI components for common patterns:
167
- - **Accordion** - Expandable content sections using native `<details>` elements
168
- - **Callout** - Flexible content highlight blocks with image support
169
- - **CSS** - Dynamic CSS utilities and styling helpers
170
- - **Image** - Advanced image component with lazy loading and optimization
171
- - **Loading** - Progress indicators and loading states
172
- - **MicroInteractions** - Subtle animations and interaction effects
173
- - **Modal** - Dialog overlays and popups
174
- - **Semantic** - Semantic HTML structure components
175
- - **SidePanel** - Slide-out panel component for additional content
176
- - **Table** - Data display and table components
177
-
178
- ### CMS Integration
179
- Headless CMS and content management components:
180
- - **WordPress** - Blog post integration and display with automatic Photon CDN URL processing
181
- - **Contentful** - Headless CMS components and utilities with delivery and management APIs
182
- - **PageBuilder** - Dynamic page construction from JSON
183
- - **PageEngine** - Advanced page rendering with Contentful integration
184
-
185
- ### UI Components
186
- User interface and interaction components:
187
- - **Carousel** - Image and content sliders (Hero, Reviews, Portfolio)
188
- - **Forms** - Form builder, validation, and emailer components
189
- - **FormBuilder** - Advanced form construction and configuration
190
- - **FormComponents** - Individual form field components and utilities
191
- - **FormEngine** - Form rendering and processing engine
192
- - **Menu** - Navigation components (Simple, Accordion, Expando)
193
- - **Tab** - Tabbed interface component for organizing content
194
- - **Tiles** - Image grid and tile layouts
195
- - **FontSelector** - Font selection and Google Fonts integration
196
- - **CompoundFontSelector** - Advanced font selection with multiple font families
197
-
198
- ### Development Tools
199
- Components for development, configuration, and site building:
200
- - **ComponentPropertiesForm** - Form for editing component properties
201
- - **ComponentSelector** - Component selection interface
202
- - **ComponentTree** - Visual component hierarchy display
203
- - **ConfigBuilder** - Interactive configuration builder for site settings, metadata, routes, and visual design tokens
204
- - **ConfigEngine** - Configuration processing and validation engine
205
- - **PageBuilderUI** - User interface for page building
206
- - **SaveLoadSection** - Save and load functionality for configurations
207
-
208
- ### SEO & Schema
209
- Search engine optimization and structured data:
210
- - **404** - Custom 404 error page component
211
- - **FAQAccordion** - Interactive FAQ component with schema.org markup and search
212
- - **GoogleAnalytics** - Google Analytics integration
213
- - **GoogleMap** - Interactive Google Maps component
214
- - **GoogleSearch** - Google Custom Search integration
215
- - **JSON-LD** - Structured data schemas (LocalBusiness, Recipe, BlogPosting, etc.)
216
- - **Manifest** - Web app manifest generation
217
- - **MetaTags** - Dynamic meta tag injection
218
- - **SchemaBlogPosting** - Blog post structured data
219
- - **SchemaLocalBusiness** - Local business structured data
220
- - **SchemaRecipe** - Recipe structured data
221
- - **SchemaServices** - Services structured data
222
- - **SchemaWebsite** - Website structured data
223
- - **Sitemap** - XML sitemap generation
224
- - **Social Cards** - Open Graph and Twitter card generation
225
- - **BuzzwordBingo** - Interactive buzzword bingo game
226
- - **Markdown** - Markdown rendering component
227
- - **Recipe** - Recipe display component
228
- - **Resume** - Resume/CV display component
229
- - **Timeline** - Timeline visualization component
230
-
231
- ### Third-Party Integrations
232
- External service integrations:
233
- - **Calendly** - Scheduling and appointment booking
234
- - **Cloudinary** - Image optimization and delivery
235
- - **GoogleReviews** - Google business reviews integration
236
- - **Gravatar** - User avatar integration
237
- - **HubSpot** - CRM and marketing automation
238
- - **Instagram** - Social media image integration
239
- - **Flickr** - Photo sharing integration
240
- - **PayPal** - Payment processing
241
- - **ShoppingCart** - E-commerce shopping cart functionality
242
- - **eBay** - Store listings and shopping cart
243
- - **NerdJokes** - Entertainment content integration
244
- - **Yelp** - Business reviews and ratings
165
+ ### Component index
166
+ The full component index and API reference live in `docs/components.md` (single source-of-truth). For interactive exploration, run Storybook: `npm run storybook`.
167
+
168
+ - Quick link: [Component API reference](docs/components.md)
169
+
170
+ If you want to browse individual components interactively, run Storybook: `npm run storybook`.
245
171
 
246
172
 
247
173
  ### Utilities
@@ -268,28 +194,6 @@ Comprehensive site health monitoring and analytics:
268
194
  - **SiteHealthDependencyVulnerabilities** - Dependency security scanning
269
195
 
270
196
 
271
- ## 🎨 Visual Design Configuration
272
-
273
- The ConfigBuilder now includes a **Visual Design** tab that allows users to configure visual design tokens such as colors, fonts, spacing, and other design system variables. These tokens are stored in the `routes.json` file under the `visualdesign` object and can be used throughout your application for consistent theming.
274
-
275
- ### Features:
276
- - **Color Management**: Primary, secondary, accent colors with color picker inputs
277
- - **Typography**: Font family and base font size settings
278
- - **Spacing & Layout**: Border radius, box shadows, transitions
279
- - **Form-Based Editing**: User-friendly form interface powered by FormEngine
280
- - **JSON Storage**: Design tokens stored as flattened key-value pairs in routes.json
281
-
282
- ### Usage:
283
- ```tsx
284
- import { ConfigBuilder } from '@pixelated-tech/components';
285
-
286
- function MyConfigPage() {
287
- return <ConfigBuilder />;
288
- }
289
- ```
290
-
291
- The visual design tokens can be accessed in your components via the config context or directly from the routes.json file.
292
-
293
197
 
294
198
  ## � Quick Start
295
199
 
@@ -336,6 +240,13 @@ See our detailed [Roadmap](docs/roadmap.md) for planned improvements and refacto
336
240
 
337
241
  See our comprehensive [Testing Documentation](docs/testing.md) for test coverage, setup, and strategies.
338
242
 
243
+ Quick conventions:
244
+ - Unit & integration tests → `src/tests` (filename: `*.test.{ts,tsx}`)
245
+ - Shared harnesses / fixtures → `src/test`
246
+ - Storybook stories & `play()` tests → `src/stories` (run with `npm run storybook`)
247
+
248
+ Following these locations keeps CI discovery and story-driven QA consistent across the monorepo.
249
+
339
250
 
340
251
  <!-- CONTRIBUTING -->
341
252
  ## Contributing
@@ -2,6 +2,7 @@
2
2
  import puppeteer from 'puppeteer';
3
3
  import fs from 'fs';
4
4
  import path from 'path';
5
+ import { getFullPixelatedConfig } from '../../config/config';
5
6
  const debug = false;
6
7
  export async function performAxeCoreAnalysis(url, runtime_env = 'auto') {
7
8
  try {
@@ -66,7 +67,6 @@ export async function performAxeCoreAnalysis(url, runtime_env = 'auto') {
66
67
  };
67
68
  }
68
69
  }
69
- import { getFullPixelatedConfig } from '../../config/config';
70
70
  /**
71
71
  * runAxeCoreAnalysis(url, runtime_env)
72
72
  *
@@ -1,7 +1,6 @@
1
1
  "use server";
2
2
  import { getFullPixelatedConfig } from '../../config/config';
3
3
  import path from 'path';
4
- // Version extraction: we derive a version from commit messages (e.g., v1.2.3) instead of fetching tags and fuzzy-matching.
5
4
  // Debug logging is off by default. Set to true/false here (do not use env vars).
6
5
  const debug = false;
7
6
  /**
@@ -11,12 +11,9 @@
11
11
  }
12
12
 
13
13
  .tile-image {
14
- position: relative;
15
- aspect-ratio: 1;
16
14
  border: 2px solid #CCC;
17
15
  /* border-radius: 20px; */
18
16
  overflow: hidden;
19
- margin: 10px;
20
17
  }
21
18
 
22
19
  .tile-image.clickable {
@@ -26,23 +23,11 @@
26
23
  .tile-image img {
27
24
  width: 100%;
28
25
  height: 100% !important;
29
- position: relative;
30
- display: inline-block;
31
26
  align-self: start;
32
27
  object-fit: cover;
33
28
  }
34
29
 
35
30
  .tile-image-overlay {
36
- visibility: hidden;
37
- position: absolute;
38
- width: 0%;
39
- height: 100%;
40
- bottom: 0;
41
- left: 0;
42
- display: inline-block;
43
- background: rgba(51, 102, 153, .7);
44
- transition: all 0.25s ease-in-out;
45
- /* All other styling - see example */
46
31
  img {
47
32
  vertical-align: top; /* Default is baseline, this fixes a common alignment issue */
48
33
  }
@@ -54,27 +39,10 @@
54
39
  cursor: pointer;
55
40
  }
56
41
 
57
- .tile-image:hover .tile-image-overlay {
58
- visibility: visible;
59
- width: 100% !important;
60
- }
61
-
62
42
  .tile-image-overlay-text {
63
- position: absolute;
64
43
  padding: 20px;
65
- left: 0;
66
- bottom: 0;
67
- color: white;
68
44
  font-size: 20px;
69
45
  font-weight: bold;
70
- visibility: hidden;
71
- transition: visibility 0s ease-in-out;
72
- transition-delay: 0s;
73
- }
74
-
75
- .tile-image:hover .tile-image-overlay-text {
76
- visibility: visible;
77
- transition-delay: 0.25s;
78
46
  }
79
47
 
80
48
  .tile-image-overlay-title {
@@ -85,3 +53,135 @@
85
53
  font-size: 16px;
86
54
  font-weight: initial;
87
55
  }
56
+
57
+
58
+
59
+
60
+ /* ====== OVERLAY VARIANT (CSS-only) ======
61
+ - Applied by adding `variant="overlay"` to <Tiles/> (adds `.overlay` to each `.tile`)
62
+ */
63
+
64
+ .tile.overlay {
65
+
66
+ .tile-image {
67
+ position: relative;
68
+ aspect-ratio: 1;
69
+ margin: 10px;
70
+ }
71
+
72
+ .tile-image img {
73
+ width: 100%;
74
+ height: 100% !important;
75
+ position: relative;
76
+ display: inline-block;
77
+ align-self: start;
78
+ object-fit: cover;
79
+ }
80
+
81
+ .tile-image-overlay {
82
+ visibility: hidden;
83
+ position: absolute;
84
+ width: 0%;
85
+ height: 100%;
86
+ bottom: 0;
87
+ left: 0;
88
+ display: inline-block;
89
+ background: rgba(51, 102, 153, .7);
90
+ transition: all 0.25s ease-in-out;
91
+ /* All other styling - see example */
92
+ img {
93
+ vertical-align: top; /* Default is baseline, this fixes a common alignment issue */
94
+ }
95
+ }
96
+
97
+ .tile-image:hover .tile-image-overlay {
98
+ visibility: visible;
99
+ width: 100% !important;
100
+ }
101
+
102
+ .tile-image-overlay-text {
103
+ position: absolute;
104
+ padding: 20px;
105
+ left: 0;
106
+ bottom: 0;
107
+ color: white;
108
+ font-size: 20px;
109
+ font-weight: bold;
110
+ visibility: hidden;
111
+ transition: visibility 0s ease-in-out;
112
+ transition-delay: 0s;
113
+ }
114
+
115
+ .tile-image:hover .tile-image-overlay-text {
116
+ visibility: visible;
117
+ transition-delay: 0.25s;
118
+ }
119
+
120
+ .tile-image-overlay-title {
121
+ margin-bottom: 10px;
122
+ }
123
+
124
+ .tile-image-overlay-body{
125
+ font-size: 16px;
126
+ font-weight: initial;
127
+ }
128
+ }
129
+
130
+
131
+
132
+
133
+
134
+
135
+ /* ====== CAPTION VARIANT (CSS-only) ======
136
+ - Applied by adding `variant="caption"` to <Tiles/> (adds `.caption` to each `.tile`)
137
+ */
138
+ .tile.caption {
139
+ * {
140
+ visibility: visible;
141
+ display: block;
142
+ position: relative;
143
+ }
144
+
145
+ .tile-image {
146
+ aspect-ratio: auto;
147
+ border-radius: 10px;
148
+ }
149
+
150
+ .tile-image img {
151
+ width: 100%;
152
+ height: 100% !important;
153
+ align-self: start;
154
+ object-fit: cover;
155
+ aspect-ratio: 1;
156
+ }
157
+
158
+ .tile-image-overlay {
159
+ background-color: #EEEEEE;
160
+ height: 100px !important;
161
+ }
162
+
163
+ .tile-image-overlay,
164
+ .tile-image-overlay * {
165
+ width: 100% !important;
166
+ height: auto;
167
+ transition: none;
168
+ }
169
+
170
+ .tile-image-overlay-title {
171
+ margin-bottom: 0px;
172
+ color: black;
173
+ }
174
+
175
+ .tile-image-overlay-text {
176
+ transition: none;
177
+ color: black;
178
+ padding: 10px;
179
+ }
180
+
181
+ .tile-image-overlay-body{
182
+ font-size: var(--font-size-base);
183
+ font-weight: bold;
184
+ color: black;
185
+ }
186
+
187
+ }
@@ -6,15 +6,21 @@ import { SmartImage } from "./smartimage";
6
6
  import { usePixelatedConfig } from '../config/config.client';
7
7
  import "../../css/pixelated.grid.scss";
8
8
  import "./tiles.css";
9
+ export const TilesVariants = ['caption', 'overlay'];
9
10
  Tiles.propTypes = {
10
11
  cards: PropTypes.array.isRequired,
11
12
  rowCount: PropTypes.number,
12
13
  imgClick: PropTypes.func,
14
+ /**
15
+ * Optional visual variant. Allowed values are enumerated so consumers get
16
+ * a discoverable, typed API.
17
+ */
18
+ variant: PropTypes.oneOf(TilesVariants),
13
19
  };
14
20
  export function Tiles(props) {
15
21
  const rowCount = props.rowCount ?? 2;
16
22
  if (props.cards && props.cards.length > 0) {
17
- return (_jsx("div", { className: "tiles-container", children: _jsx("div", { className: `tile-container row-${rowCount}col`, children: props.cards.map((card, i) => (_jsx("div", { className: "gridItem", children: _jsx(Tile, { index: i, cardLength: props.cards.length, link: card.link, image: card.image, imageAlt: card.imageAlt, bodyText: card.bodyText, imgClick: props.imgClick }) }, i))) }) }));
23
+ return (_jsx("div", { className: "tiles-container", children: _jsx("div", { className: `tile-container row-${rowCount}col`, children: props.cards.map((card, i) => (_jsx("div", { className: "gridItem", children: _jsx(Tile, { index: i, cardLength: props.cards.length, link: card.link, image: card.image, imageAlt: card.imageAlt, bodyText: card.bodyText, imgClick: props.imgClick, variant: (props.variant ?? "overlay") }) }, i))) }) }));
18
24
  }
19
25
  else {
20
26
  return (_jsx(Loading, {}));
@@ -29,12 +35,16 @@ Tile.propTypes = {
29
35
  imageAlt: PropTypes.string,
30
36
  bodyText: PropTypes.string,
31
37
  imgClick: PropTypes.func,
38
+ /** 'caption' - visual caption beneath image (prefers bodyText, falls back to imageAlt) */
39
+ variant: PropTypes.oneOf(TilesVariants),
32
40
  };
33
41
  function Tile(props) {
34
42
  const config = usePixelatedConfig();
35
43
  const imgClick = props.imgClick;
44
+ const captionText = (props.bodyText && props.bodyText.length > 0) ? props.bodyText : (props.imageAlt ?? "");
36
45
  const tileBody = _jsxs("div", { className: "tile-image" + (imgClick ? " clickable" : ""), children: [_jsx(SmartImage, { src: props.image, title: props?.imageAlt ?? undefined, alt: props?.imageAlt ?? "", onClick: imgClick ? (event) => imgClick(event, props.image) : undefined, cloudinaryEnv: config?.cloudinary?.product_env ?? undefined }), _jsx("div", { className: "tile-image-overlay", children: _jsxs("div", { className: "tile-image-overlay-text", children: [_jsx("div", { className: "tile-image-overlay-title", children: props.imageAlt }), _jsx("div", { className: "tile-image-overlay-body", children: props.bodyText })] }) })] });
37
- return (_jsx("div", { className: "tile", id: 'tile-' + props.index, children: props.link ?
46
+ const rootClass = `tile${(props.variant) ? ' ' + props.variant : ''}`;
47
+ return (_jsx("div", { className: rootClass, id: 'tile-' + props.index, children: props.link ?
38
48
  _jsx("a", { href: props.link, className: "tileLink", children: tileBody })
39
49
  :
40
50
  tileBody }));
@@ -3,8 +3,9 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useState } from 'react';
4
4
  import PropTypes from 'prop-types';
5
5
  import * as FC from './formcomponents';
6
- import { mapTypeToComponent, generateTypeField, debug } from './formutils';
6
+ import { mapTypeToComponent, generateTypeField } from './formutils';
7
7
  import { FormEngine } from './formengine';
8
+ const debug = false;
8
9
  /* ===== FORM BUILDER =====
9
10
  Display all the components for a Form Builder -
10
11
  Element Buttons, Element Details, and the Form */
@@ -3,8 +3,9 @@ import { useState, useEffect } from 'react';
3
3
  import PropTypes from 'prop-types';
4
4
  import * as FC from './formcomponents';
5
5
  import * as FVF from './formfieldvalidations';
6
- import { capitalize, attributeMap, debug } from './formutils';
6
+ import { capitalize, attributeMap } from './formutils';
7
7
  import { FormEngine } from './formengine';
8
+ const debug = false;
8
9
  /*
9
10
  ===== FORM EXTRACT =====
10
11
  Submit a page URL of a form to extract all form elements and
@@ -1,6 +1,6 @@
1
1
  // Shared utility functions for form components
2
2
  import { generateKey, capitalize, attributeMap } from '../../general/utilities';
3
- export const debug = false;
3
+ const debug = false;
4
4
  /**
5
5
  * Maps input type to form component name
6
6
  */
@@ -18,8 +18,19 @@ export function mapTypeToComponent(myType) {
18
18
  }
19
19
  /**
20
20
  * Generates field JSON for form building
21
+ *
22
+ * NOTE: This implementation is currently unused by other modules in-repo and
23
+ * is exported by the package barrel which previously caused a duplicate
24
+ * export collision with `componentGeneration.generateFieldJSON` during
25
+ * Storybook's webpack build. To avoid accidental breakage for external
26
+ * consumers we are making this implementation internal (non-exported)
27
+ * and marking it for removal in a future maintenance sweep.
28
+ *
29
+ * TODO (cleanup): remove this function and any remaining aliases once
30
+ * consumers have migrated — see `docs/roadmap.md` (Refactoring & Cleanup).
31
+ * @deprecated internal-only — slated for removal
21
32
  */
22
- export function generateFieldJSON(component, type) {
33
+ function generateFieldJSON(component, type) {
23
34
  const form = {};
24
35
  form.fields = [];
25
36
  // Common fields for all components
@@ -1 +1 @@
1
- pxl:v1:9b93a0054684e42a36e1bf4c:4349f4740c87bf559c74977e3384cc2a:76c5d9a21ff4b084598fe6b4b3a16e882e1791a7684ce0d84c779c1618f3e58b4e313e92ebf9a65343d9d3d6742fdbe39235b350ad2dec57fc4d40e61f763b383e156f194f2e9d4c0722c0888dcc1d72243993f6967bef84e373d6872ba68def35a13bf92be5dea221982ef82d6bb629dd9620bd5fa7021e1947f73e4c781918107eec08cf57c8245ec08f9dcfedf41d7caca6f17e58c51d529b86a2b443e7c75a606c90bff2ae26bc5311741f531b6c02335d65c41b94458c0379470ead213b99569fe8e37d8e390c392792901e1e092e1ccbad533b636f0e0ec22c78856c404f487a85205f1506d7fafc651dd0cf5cb5916a8bd9f245dbf24d4b830beb6788c8d3ac50e547138bed3f814cc7200d6a625af83c7b1cd809d0f586376dd8c324ca702bc7f9c63f6552d7e122d3d0d3fb3b3fa09c222b65079882a3b6ec4e3678c152d98ffbe0d2808c5124c63ed86d64ecfc01a443c7401d28585a0a4344909758ae34f32ca9a773e19706517b3116dd66a70da629cc577cc7d82b268846c6e1afdb4a6e93ed6157345a15508dd15cfab86adc6effb40136b0d427410d25d2e44e477d35b19f599ac247f6e774ed6c5686092a7529b8901e1b925d9302219bed92a395a22090887e333e785ed66be1b5472cd8f673b7aa383be6385d1c8112c49ea85f0829969a11a59d6e52403a8b83e189f64409ca408ee045a9e6f9a6a9b83c69d108b8e6f93fbd417ac80ee8fab2b08183c93582871f155623c0adcd0711df3e8d52b195deeb7581ac4e12be304a1accee35ef8b1ffb32f460b2afba7ff5193b009ed1172c08d111e9ca6d6b7532a5c40ac8dd2316ec7dd4a82ed90837afbc96e6289cf442d9b14ac2183c4aded31bb88adfdc7935289493806e21566abf26f96e82ab633e70ae88b0fbf263d16e2bb97dcf85b7d1ea4254c3037bbbe006060ff415f5021bbd18c94379b7de8346aa91b962637fe2c783a2b314d6b729149be633b1f6ce3b3bc652b86fba4064bda7ec00d32c686fe85cc8552004b409dbfce9bcd266ca7e05d00b93f8c6b6bd9d7fb8c2ed68ecf986d1493528d5f92830c20c813a597342ac630c15830c0e7d786c92351492d11fd86340840c58ecd8086306a46ef4404e5b3bdb1d873395b8d12fe043dac7808b023b9b1109ba3c5a5d78193558f1cff977d2b8ea422818114c52722b1e838dbaa43052ddb7104a5d653142ccec4be43db7b2eb450e25c70a37ae0f7ccccc085f85e698ba60787204ec9f9e26cd6d8bd6e40d2cff06c7b42e3b9449d283e9d55e4954e2a8957fddd59b26338882fcd1126300d073c0cf93a99861fd23dff6ad5c5ed536ca8a9682a2e63bd89c8daf4b8dd9936e3fe2493b05db57e6cd92bcafc9c11343ef1475871f5f57cfedc9bde6a21d06f5113b25c68f5ae85da0cb1010cfd85fad9cea3ee9334c7351b2b8fd71c0a60021284a1b8acbe8c1212755c85dd872ad614618f97e28b6513c9cf0ce670a83ce4107ce20d74152c303b2fb27bddf22baf522832da674dfdfb154269a694cac7cbdc1ade1d924915575ab0613d6aeecd638993f8a9fadfa77e62db0f0caace4c5342849ccff72df1f19700892bb86da51f9ecb02fcce2105802bdf1a22c0c5ef7aace31f19c8ac9472be539f4465827984624a945738879d1edb862cd6be7619128ccf3275c6bcbde10aa85c59376cf1712b2f8ad9942223cf436849918b4e591cb77a18fa0b9c93264d8a621163d15b703d10cd80cd397091fc7b63f3ec12b2c2c88f7dff0a06f8c62c90748c85ac8055e2f412021c3fd991d2a7a4f0f39f90ccb850a55929845983c196b9eb7dea2552beca53eaf87e9a3cf194d70c320143baca93190ef55bdd43894a743f55cd8ee425709e69debff9cb3f5cf48a3431b6aa181e86b081b6c2c7764526f469fc324ddd820db8f874e56dd07bb5c3a06a5285f5983515ea367f405a7f1af09296a43444966d2c94fe554edc2e9afb0d7af1fd08958a3c8ce048afe83e158ec84ce8af8dad0649e1822d1531e75d605bcfad8da2f43b2ebde683d3283ade22e8c7dcc0d4653ff0f8ffd9c5f51887e147270471a15ef036a5bd1725e79b26f2f3905d42b45f422df7f37a99b86df1c6b9c680820d3d9211074b8b1f6ec71c44e272ec082a16a14ef47562b83849998587e5e1623770ff534897a56433d760b96d5ad936028a05f2199981e5a28ee38fce5f24e6a75fc956f485249fff260effaf6866b7ab5a9aef284352da444e87e68aa7f1e4964ecd4ec9572c7b7bdde3afc692e003f519dc1c6d7cee76ec2a113ea87e45b1025a102b65b1e4a874411f4f33c1facb1452fef8da2cce7b4b3074076b3c6ad33f07289e932a5652daa3514b983bf3dc57cbf4da25e53f66db56d8dc70e0660563fb8d427b978b7c3031cea35b6a4c57c51b81a36ab58d2a4b4743c016503eeefbc5df595484e17a2132729a0479c03bdb2d78531de988f1cd3caf532f73ed1a19050ea2c986b1ae8c1c3b9ebbd42d0a20c8ee107f891bcd4a51fdc912336f161dfb7c4528da8a0cc8c9dcd54f6ad856c5107bfe9e343df72a17bdc3bf62869eaf64c9699f16afe8a2b70b8f20a750a434c26f4db07c519cfc6ee003635d83790bfaef377d2b5a5e3337f5a302ff2aac19b04ff089fcfd0da6ee9123295ea7d97a4a89ded9d9d4b2a6a32633c64f93d46226b1c11a4ef4d4826f9b31596120d43174ca7bcd83cec94607b7e3c06acff1c1b5a08de0f7bf7de4c250ed5dbad94f1d6ac6bb456282d14e194b5467837c011c8bbd23d8db2b9e286309d831eadf829642355040fad9a81b02cd6b30fe7852a7095359b69d6cf91331577f7f673a73104a09c8bef95b72421556a324d7631d1b73997ba88d33d5c01669d7cf042b91ebb69be0283604dd4b9056cd9360c3a6dac5d9b49a7650c2aced38f8bf397b1bcf401a4dc6b16c5eddfe05c162136006e451b910aacc1f3298f8cf829bb9265289a16045ea1d8553cfa90ce3b02c0fa6f6cbc43a46a071c74e3c5d19caf4ee5f4377e09212536a79584046893b7df5aa678481cc1369516e71604c272337cb89b3f4e42ab321abcb9337da176f90346f06ff34a19377688ac337ea84e4b736b3c69e26270dc52df45d31e102d60a47c25180fdf4759bd5fbf341db9dfb3799bc4cdf5a64c9249dbd46aad212b64baf9a86ae83774afd995be9ca67c281f1fa9be8faee6594b4be68917069b39170f6b9fe500ccf362f12aa5623503bb8703969bff093450fc56ee9fd81b39fa0809f15fcdc1de3b3f1d498c88979b46f797a9c5190bb43a48a9a55c799d053a770ee6d284ca5dbc30bfc8ed43e6a7db9cecce3c8e2262a910d94a5fc3d9ab175dc30d9416a7e067e71371dd060484ff1271d77553091cebc41770959de3e8007aa1311705e9b6efb202d26b500363bb5968a283c1c9e8db5d622fbe46c0e59c2a12f1e0fce1f8f9bc042c92cfa357204bb7e6b40a5ceacdf8e67fc7dcaf44c315343b61e943bdc2d64f2fb4ff2908b45ba47a538dc0a58b35db969c718c2056c59d920c8a177ec75f9004ce34bc066085774c8ebb591621fd8bc77e008c652843ada84675320dfcefffe9c2b4ba65ca710fd73aa54c825615ced42421778303f50775a478c8c247e963d41129dca9b300a7ee205f20724635a58b7912bb94c8200383cd1beeefd843705d696cc514c104371e6c6df9c10f008d37b849626e37e1f633b10a183722d56142c6c00f1c19a14583250bcc6b1f1b16b5b71ef0da418618ccd9d2a2491936ce8ef72cec3d9307cd7331ad458f5d776a4d3159afdc130674d3a290e7a30f459ded0d55bcd73a96d7d13ddb02fa670e13be0b3aa66177f569f9aed094dfa43b91b3e295127b2eff71aeaffd970d7c234bd2d345620ace82714f90eeb075ae1c5f97805591d42754d0b3ad8ff5c328e1350042e047f473abba89b5418df34641e4e4c1004f54c1c9496eb5dfd058f3b1f2f093b1c5af854b9c6a1af652b72a806dd0babe25c1008b3507a4c894b4cc73d900b18a4ba6ee480d72bbf6bf0a9f9e9605ef810659fa05d03f2730715512b23e026300575bdb3f866484914526d382aed34e69b7b8bf8d1c5b347078183ca58d332e2953977232caf4f819b1fcb4fa1d26f95c97d656541516581bcd52ac32f950c5721a12b5d7e56f64f2280712935bf42656671ed7422cae379783ec8f1101370f33848eecac624b11a761a3a9e224d89b0c59c64742e9bb29af7712692f5d8c9209187c5ea450f232852128c4ccfcb1769df4030b5355245f936605bff1a79b4155d7dd1cd9903d4740edd8a29f070f1d58a8fe8706e71a863dfeb0e268112fdb6df0f105adbb037a5c09a4e1b9ec6df09e2bd06323e99a78dcc4e3e5e16a81e9a989fd752fb649004c89c51175233210518e9eefb8e66427cd42a91b995b35fd70c40f44516154bbe104ea5cc75b2a394f09e0a38155e270828079da80edfbd78cc69cee894063524eff9f920100f5dbe68cde22aa07378824367b3e96af78549c11a992c6f6b902d65b96904fe502d7eadfbb14041215596f214b8c9c5278491401741a1e08691438e121a4dc654444358803f7cc24f71cc108c8b35d44002be7e31d5e6c971dac20035db116dcbb30a525d8006cc8128b8acd8288eb0f4b83b4b8f6863aa8f9e6046e5ad4b7d2d06f7b2f72df2b8931484bad852a4421399c25df3ad77e29be34094de4ecf8e1ad44fb87cb11a04344a96289a4031a6608fa38e735f2d847066be89ee18603f415b450ff64b9308ae4bd1cec7b1736fd2cf8cd2114a2794e89358a6720599c7661f35285b54f35e69f5e09b3994543febb588b5ea700f4f24026c719a71408e4202950aedab90bc3c73d767658e8fa710f682e73eecb3e976a7960acfcf207e4d0901ba7f8df5a1a5814d52a775daac952ebe04930ce7d00e8c4f3e84f9d723b434a6a94a4a7c0a7d63902ec9d2b866c4e1e8914bd30ddfc0bb54c7e0d752679f33764674001a777a97a7bd23c6d16944d0be4f30bd1f3139ad5b90c1e8d85f4350df83471ee8d706473b3ff82d44b283334eae7166265f3b210e2d6e4cc171669b056097ff5d378a238ad1ed9d453a98b981ba20ddcb3dc02e51f139fd3eeee2514ee23f37a70f9aa9abd15
1
+ pxl:v1:b15a24756358d6e6b02272a2:9bfd2086414fa1eaa92153908ed0e15d:ab8b7769a766de9be76c08c748e59ffeb147312bb908995fd88456c5cc76366350bf44d28f9b3ed9ebabb70ca106e4fa9196e559e81ce591b6764abce5415bb5013dc07538d6d0d92a377d46c1a6f2f96327c06d269053672fb532a274bc5b8483bf06b8a9f6e16d9733be3c5033a967d91527ffb53f1e5c65dfd66b983e4e3f2ec086e01ddd68a0a4d85a2b3dc3bef21024e50c11b2c8256c8dd1b2f2c8c71aaba175063b61f5253d05c0c2847ecf4ddb6b40f8aa9af5de027598b1f747b72cdd6ba85025fdf30f35d47f9c10de625209bd28ea11bd73decc15f1de580be1b155032b2857ee9ac8212aeb943c42a773fa75a0867413c455cce15bc77406bf2b5ba0f5da75cd14735f53b3d22962b27c04c418bcb7e241850f64a873649c3a52c67a84db2f9a3c4a7605ec5114ad1f74a0eef57d57b1dd98d87b144336d516b2d48324df0143e5687abdbd5ffbb9a0751e5f91577742fe460c390c519d73b3da3da2c1a4d32a738875106a58c91b6b04016eba50f57a026c9d67312bdfb53992ac025e503ee818aed29959337947e1d0d81e6cccd0d1a921cfe31d225ce744cc82340ce4af6aa308677cee985a7e7ae1d1b7d21274ca8264ee6481e5f001d1d4dea91dc0eb787d2cf1b990d9690e9bb83489d0e354d1071bff8a1668550303f04256f7e60736146a5da09e15b65d467af7194ece67feefc45736d009f8043d76c30b9263e04224977d8b9ce64b5e6317e6f3a604c76b1290ffa410e51dd069258321448675382907f32c6b51dfede27fa6db5244952f883c57b8d6cf9572e6cfa7a4d973f86c1ab3b54a7bf765deee12ea7d381eef24e1424f09dcdddd219930826dcc69d924ccd1937538b4edb8e347da359b638aeb475b38b08307d5ec568b959ac9c4dfd1889bf2c963b7bc0b65bdbef0d89a75b026ca4af6e87641ff604ae2da4b3c19a1c6a3a3f32ad870343f342d609946392d3e540044d7df067b00eb7ec16284a9b42439f853bdd6492163382d1e90d6e24e46b5ff1061cb51e8089ddfa3985775d03551f082cef25b0feeda0a9cc416a25f01d71f4797332b654895ee0766735dc02329e48f56d4583eb5fb9ca9e0f9c71b7ac21b3abecb55edca90731d7d69c16dbd31c0f01f3842fc1b4322383932c70e0131e6a723d95f26adbe8777f9d0bfaa75f310b411d9203689a8e13238328f04ae7ebb34e9212b77edd89b54f6924654a31d17c646a24bff9a096305be689c10a1dad1830199679568f5054616fe18572f44aac4846c3649e6b58509b6096efdbbf5a0bbcec5e97a48fb7497ab2d74376e76f269366b1d5dfd426fbcace4ee5bf775e9c9b804eb87363c8f9394a96c703c84cef79d1a9ae846c537ffcbb5daaaf4efdab9c6cf2ba12f6df46b8ea62acefa01f1201432808758b38643e6154bb74d0b7af6f01461357b58e51c726df49dd59585ed034473469de5f9454aff9794c0f08af4e7a976de871a3e67a7769a2b7d69aea8438767a06dd12815471bfe359c274e8b3e9e9b0419712c85d59b1844034bdf2d486cd9cb94b875672acbcd0dcbb3156e5b52c69a4f0f2339c682183b31a7a0f0757b505fa4c33e31e4554f535d001b441daed3122f7a6d6fd9563ad55d71ef0c361bd908370f52d63c96488f3dd7aa1f480e562fb8275119e5b3c866e4536c5a1dbb6f3e89ccca9ccf44cd7f60ba5e2de6f68b8f22c6fec7ff146bb4eb84230712ea1638b515bfb5e00518fe4ab4c7465c4a0b7b422fa9ecfb3dc7f5ff44915dd3aa982e6f9304e3068446e45dfa4e61eb6c7649cd8b4918ac3c369f42a7d71a9ce69afb63b9a4bd421c6e65e1e781b11aee3d22d85a776f8306a80d00956ca44163c7c7517d8fdb916808f42714bde340d39dc85aa606507f4f8942b28aa596ee67b478eab4439f3f8462d07a37555a8405eface5b4e45fe62c779e818c0e0f2ffb1c8885a2873e08a39e6731a421a4edd82fd0f18b16a3ed249e4a8fe3e41ea1c20e2d886a57ca64115b85c494879f9d1f0538c67c87c32d38225c2516b5d57138a0ac1741b88a076952042c75ea75a344e3154ce7729bfe524afc79ba82b5e80f0cac64d15d9af1e0fc4065415613f72942013db82178e9458d598c1e4c933ff7be954ad45e903fc1c0fa97bd1af49aa9cb6bf5ab4a14e801cfb2f9ebc3a1116ee7ec210cc07e9dd5bbd40b286028f2a4f1859473cdcbfdefd782c4eea8484e238d90081b48b5c6010dd16069a95485095fbe0fbdf5b50ba89dea877fa254d6583958bba61bd01f5edd53b6182678cd5df5c26adecb0427ba8b4763f83d281c93a3fa220528b25b6f14cdd8978d86f2bb402b9d576b390edf2738b243dd7a334951199dc63effb2c826e70257d310e56848a85f2e47042f934a0937d4f0d5ffd8dc06822cd5a3cecd12b628842c3f4e994989a655af25e7170dc9d24bbe3dc244606e3f196d062240defafe6d8276e02cf2da35514fbf93e8a3457631af251b1d4d707986c85b88d34ee81e77f3f9ca8507c00319bacdf1458656d9dc4198d81db75333e232ecb1366c5aa51d0fe2053c33cac284f9be16c9a6994de0227f55fc944ddf6277be6f0fad647ab3f35fbf3a75c0432025d41773dea229c021d508f30f2771e04b37bfb6471ac26ff9ad282cb5454464f6b1d26d02fa86ea2c6727ed637a2f0622d84891324368cefdb4ac6b9726372cfb9be4f05f47ea0005f3d892f8ab4aeabb666001f120b87207801cf279625f94cdc3ae93ce019c631e18d0ea88d70c88464d94274a8ab767c4b959c598c5472b6263358c29599351f74d9c73375e921d2d239f1675dd55b02a0450d99f8d0b5d7a83a3559f133f1af11c21c4f639929cecb7dbf3d97d81b5b19260800977ecf63c2cda31c82960e2c8f20eee1cc803d3613d7b324dfd6c54176f39fcdda99cf4f90ad4366dccf12b35a095de628fb63fa40fe39cd7f87dc85e287c9a9b6636e4444bd47650d85d01b58c06174fe725bc7737b9d3fdf7c61da147fcb2ce39cb74514650a1a7266bd91a8eae5f76149bf6ce4332d235ca5509ff7d53e51480f0e89294ea83edb84d91bbd8deaba4dbc850bab27fdd2d8e01a9e8dc51a1cacb27fb9e6c551ea5c9d0994245384f21ca9b0de3594c7a597ad52bdbddb81b706ffd05dc21ba766bb8cd13a1de888af0217ff9905069c19a7ca0cf1ce3b9a5111caffd50f65a0a0d24982ffda39c7343d73b1992ec3a3c5048c6d96ebd1064be6c3cc65ee4bc843fc552f6b25cc960919c35c41910e354e7aa2fa6c00bbd8b605e666338378794e9ed210ac265e92c1958b4e6eaca420c0e546b135b2cf4cf25a206424a3619f6cd3fef71f6f34960ee36a0236711e8b1b2e4ac03f78d94d54975f798bfa8826e409de8790c0feda76c6f85c0275de2dd08302766147a06e105f48e9e5db0f820110d24ab91561bc3cf4eb27c60d21eaf81d38964fc0ece16418ab2b561c9c0acada88dfd577ff0a3762139efa8edc09a23b6c33fd47b0bb5b52fc7afd09e66cce984452133a15d2c4de8d4779511175c946fca6933b016191f96f7c3ba9f8cbac7fc1267c1f01777817424c38bde51842213e3e1e5d84333fb2d805da2860d57009908a80e0e012b9683808a2e1759b48c0b64b043a04a35bf962e082b041853f5c5d7466ea2f714b8f2422fd6162daeb4a236546e493b06f15edfe0d7869aeffbaafc8bf9a138301fc121783832aef1a5e17dd7e548421115135b26b56bdfc0f0322d99653b7a3bad31e3e34e032e1803b31e46a20c2c1232b10cd2020c06106c8f795b7241c6253d9080498cc60e7c7b93549522c0d0c5c33e3c87e6096096cde13c0233dde2c59d32e40b5f21d8b114f84fbe98df27cacb130353ab81aa531b1884d99fef4e115c60d7b110ef05300a90270d8d7e9d5a761465973b37d1f9c4ae569e80b08e900423aeb5bb839f066de9e687d97a8405482ffb14bc942cc909b1507f0a98b98b2e907cb522078da150d977814e3985359a4eaf4325c2f8f271da8bb7927b35862db461a7c10b88f95be8a1ffbb11c15b6615eecf9a48687c3106aba3245934f5900d3049bd704cc6439919fb085a51d19ec883801fa6156031cc17b1b608f2a9c64a505373b62d90fed060ba14390e9d7a1fcc48ed4c9f15879dc8f827095f73fe6a21558311c4dd4cac91d1f1c128e8e378ecf91b04ac87d92cd3d9026e8b4a9439254c98930a7987f3dc8e8dc84d330ed25f65207822b03896ce43c18ad1cf0d9abe6ca5d092f14a19a202600679353b041a6f9bbdc17791e527a9dcd223b034f983add3c57052ec62ae4b61f6c3b8e5099c3ec0aa8ce5d4f0e1f308ff46803c5c72a5f902d02b65d713101dc83a252dfdb0fd4c36cf14030221a34b993fcb4f3261eb9aa884eb7ebc41fcef9b72765e09b63e2fc818a80f7128b153e763883ef632c6f97b77ea84ab070d85d129eb7cf2bd3d44419fa271ca0531339d9a2470a7ff37dc2c6da56c08b7bbff47d47c9d5cec9f106b3a3f23c98b65128e995dd47251d21acff2566614ca911318ad0416f1658eb8d31956d8f5ff0d22ab4390fee5e3cdf251bbc113b68b847203c3d65edeb7feb7a929ea25f2153837acd99486cf3b368b15cbd2ca3fe377bd95ef17b4fb77d8e633b747adc89f2738741a794a6058b5cf94bbb4a8849d6a3de88de7649155ba279f41f36895ed5b2161710c541c1bcbfd64bde555fd35aa0ed950725d9c595447fb73b52d4ab4b1affc5756babe36a42649d24b121244d9205344ffe6668a097c5c4fc83dff002c3af4ca6f357dd184c864b6cda545301ae0e333cc0ebccdbf00cad40fa4b133f8961751746feb4eb45d1b426e2a02effc5d4eb0f39df08c2aff63db26575df75f827ef33e20db0e1775f3c08d3833812b15348fa8cf8aba7643926ba11a87d11c34981690811019e7d1c109e5ec9863b4c49b65a6df5126e26a2c5867432632bf15389c243af3eba40e75ddbba75b315ab387a4d27c2000c56ec136808505087389b4a81466d08a2e32ee7de86efedd17d5390bb14aa1e79dcbe4103eb1bca793ef5f4a0ccfa3fe60d1210ef26dd955fc0b59180eaf04fd258272b173815596bda4575788156e3408c2aaaecb99293e83b1d85b3857e5432c8e158738d92a11cd783cf4af
@@ -77,8 +77,8 @@ if (command === 'encrypt' || command === 'decrypt') {
77
77
  * - Decrypt in-place and copy plaintext to .next/server/pixelated.config.json
78
78
  * - Validate JSON and emit a concise success message
79
79
  */
80
- function decryptPostBuild() {
81
- const DEBUG = process.env.PIXELATED_CONFIG_DEBUG === '1';
80
+ function decryptPostBuild(opts = {}) {
81
+ const debug = opts.debug ?? false;
82
82
  const candidates = [
83
83
  path.join(process.cwd(), 'src/app/config/pixelated.config.json.enc'),
84
84
  path.join(process.cwd(), 'src/config/pixelated.config.json.enc'),
@@ -92,7 +92,7 @@ function decryptPostBuild() {
92
92
  }
93
93
  }
94
94
  if (!foundEnc) {
95
- if (DEBUG)
95
+ if (debug)
96
96
  console.log('No encrypted config found; nothing to do.');
97
97
  process.exit(0);
98
98
  }
@@ -136,7 +136,7 @@ function decryptPostBuild() {
136
136
  // Validate JSON
137
137
  JSON.parse(decrypted);
138
138
  console.log('Config injected into .next/server/pixelated.config.json');
139
- if (DEBUG)
139
+ if (debug)
140
140
  console.log(`Decrypted ${path.basename(foundEnc)} -> ${injectPath}`);
141
141
  process.exit(0);
142
142
  }
@@ -177,7 +177,9 @@ try {
177
177
  console.log(`Successfully decrypted ${path.basename(targetPath)} -> ${path.basename(destPath)}`);
178
178
  }
179
179
  else if (command === 'postbuild' || command === 'post-build' || command === 'inject') {
180
- decryptPostBuild();
180
+ // CLI-only debug opt-in: explicit flag (do NOT use env vars for debug)
181
+ const cliDebug = process.argv.includes('--debug') || process.argv.some(a => a.startsWith('--debug='));
182
+ decryptPostBuild({ debug: cliDebug });
181
183
  }
182
184
  else {
183
185
  console.error(`Unknown command: ${command}`);
@@ -83,8 +83,8 @@ if (command === 'encrypt' || command === 'decrypt') {
83
83
  * - Decrypt in-place and copy plaintext to .next/server/pixelated.config.json
84
84
  * - Validate JSON and emit a concise success message
85
85
  */
86
- function decryptPostBuild(): void {
87
- const DEBUG = process.env.PIXELATED_CONFIG_DEBUG === '1';
86
+ function decryptPostBuild(opts: { debug?: boolean } = {}): void {
87
+ const debug = opts.debug ?? false;
88
88
  const candidates = [
89
89
  path.join(process.cwd(), 'src/app/config/pixelated.config.json.enc'),
90
90
  path.join(process.cwd(), 'src/config/pixelated.config.json.enc'),
@@ -100,7 +100,7 @@ function decryptPostBuild(): void {
100
100
  }
101
101
 
102
102
  if (!foundEnc) {
103
- if (DEBUG) console.log('No encrypted config found; nothing to do.');
103
+ if (debug) console.log('No encrypted config found; nothing to do.');
104
104
  process.exit(0);
105
105
  }
106
106
 
@@ -144,7 +144,7 @@ function decryptPostBuild(): void {
144
144
  // Validate JSON
145
145
  JSON.parse(decrypted);
146
146
  console.log('Config injected into .next/server/pixelated.config.json');
147
- if (DEBUG) console.log(`Decrypted ${path.basename(foundEnc)} -> ${injectPath}`);
147
+ if (debug) console.log(`Decrypted ${path.basename(foundEnc)} -> ${injectPath}`);
148
148
  process.exit(0);
149
149
  } catch (err: any) {
150
150
  console.error(`Post-build decrypt failed: ${err.message}`);
@@ -182,7 +182,9 @@ try {
182
182
  atomicWrite(destPath, decrypted);
183
183
  console.log(`Successfully decrypted ${path.basename(targetPath)} -> ${path.basename(destPath)}`);
184
184
  } else if (command === 'postbuild' || command === 'post-build' || command === 'inject') {
185
- decryptPostBuild();
185
+ // CLI-only debug opt-in: explicit flag (do NOT use env vars for debug)
186
+ const cliDebug = process.argv.includes('--debug') || process.argv.some(a => a.startsWith('--debug='));
187
+ decryptPostBuild({ debug: cliDebug });
186
188
  } else {
187
189
  console.error(`Unknown command: ${command}`);
188
190
  process.exit(1);