@stackwright-pro/launch-stackwright-pro 0.0.0-beta-20260417191149

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # launch-stackwright-pro
2
+
3
+ 🚢 Scaffold a new **Stackwright Pro** project in one command — complete with
4
+ OpenAPI integration, RBAC auth, mock users, and the otter raft ready to build
5
+ your site for you.
6
+
7
+ ## How It Works
8
+
9
+ This package uses the **scaffold hooks system** (`@stackwright-pro/scaffold-hooks`)
10
+ to automatically wire up Pro features:
11
+
12
+ 1. **Import hooks** → Auto-registers Pro packages during scaffolding
13
+ 2. **Call `scaffold()`** → Hooks automatically add:
14
+ - Pro dependencies (`@stackwright-pro/mcp`, `@stackwright-pro/otters`, etc.)
15
+ - MCP server configuration in `.code-puppy.json`
16
+ - Role-based dev scripts (`dev:admin`, `dev:analyst`, `dev:viewer`)
17
+ 3. **Post-processing** → Copies Pro templates, adds auth config, generates README
18
+ 4. **Auto-install** → `pnpm install` runs automatically after scaffolding —
19
+ citizen developers get a project ready to use without knowing what a package manager is
20
+
21
+ ## Quick Start
22
+
23
+ ```bash
24
+ pnpx @stackwright-pro/launch-stackwright-pro my-app -y
25
+ # Dependencies install automatically (~7s)
26
+ cd my-app
27
+ uvx stackwright-pro-raft # Start the otter raft
28
+ ```
29
+
30
+ ### With an OpenAPI Spec
31
+
32
+ ```bash
33
+ npx launch-stackwright-pro my-app --spec ./my-api.yaml --yes
34
+ ```
35
+
36
+ The spec gets copied into `specs/` and wired into `stackwright.yml`. The
37
+ prebuild script generates types & a client on the first `pnpm dev`.
38
+
39
+ ## What Gets Created
40
+
41
+ ```
42
+ my-app/
43
+ ├── pages/
44
+ │ ├── _app.tsx # Pro _app with AuthProvider
45
+ │ ├── _document.tsx
46
+ │ ├── index.ts
47
+ │ └── [...slug].tsx
48
+ ├── lib/
49
+ │ └── mock-auth.ts # Dev-mode mock users (admin/analyst/viewer)
50
+ ├── scripts/
51
+ │ └── prebuild.js # Reads stackwright.yml → runs OpenAPI plugin
52
+ ├── specs/ # Only if --spec was provided
53
+ │ └── <your-spec>.yaml
54
+ ├── node_modules/
55
+ │ ├── @stackwright/otters/ # 🦦 OSS otters (brand, theme, page, foreman)
56
+ │ └── @stackwright-pro/otters/ # 🦦 Pro otters (api, data, dashboard, foreman)
57
+ ├── stackwright.yml # Theme + auth + integrations config
58
+ ├── next.config.js # Pro config (transpile pro pkgs + yaml-loader)
59
+ ├── yaml.d.ts # TS declarations for YAML imports
60
+ └── package.json # OSS + Pro dependencies (via scaffold hooks)
61
+ ```
62
+
63
+ ## CLI Options
64
+
65
+ ```
66
+ launch-stackwright-pro [directory]
67
+
68
+ Options:
69
+ --name <name> Project name (used in package.json)
70
+ --title <title> Site title shown in the app bar and browser tab
71
+ --theme <themeId> Theme ID (e.g., corporate, creative, minimal)
72
+ --force Overwrite existing directory
73
+ --skip-otters Skip otter raft setup
74
+ -y, --yes Skip prompts, use defaults
75
+ --spec <path> Path to an OpenAPI spec (YAML or JSON)
76
+ --spec-name <name> Name for the API integration (default: derived from filename)
77
+ -V, --version Output the version number
78
+ -h, --help Display help
79
+ ```
80
+
81
+ ## Role-Based Dev Scripts
82
+
83
+ The scaffolded project includes convenience scripts for developing against
84
+ different mock roles:
85
+
86
+ ```bash
87
+ pnpm dev # No mock auth — unauthenticated
88
+ pnpm dev:admin # MOCK_USER=admin
89
+ pnpm dev:analyst # MOCK_USER=analyst
90
+ pnpm dev:viewer # MOCK_USER=viewer
91
+ ```
92
+
93
+ ## What's Different from OSS `launch-stackwright`?
94
+
95
+ | Feature | OSS | Pro |
96
+ | ------------------- | --- | --- |
97
+ | Base scaffold | ✅ | ✅ |
98
+ | Otter raft | ✅ | ✅ |
99
+ | RBAC auth | ❌ | ✅ |
100
+ | Mock users | ❌ | ✅ |
101
+ | OpenAPI integration | ❌ | ✅ |
102
+ | Prebuild code-gen | ❌ | ✅ |
103
+ | YAML config loading | ❌ | ✅ |
104
+
105
+ ## Scaffold Hooks
106
+
107
+ The `@stackwright-pro/scaffold-hooks` package handles Pro package injection
108
+ automatically via the hooks system:
109
+
110
+ | Hook | What It Does |
111
+ | ------------------------- | -------------------------------------------------------- |
112
+ | `pro-dependencies` | Adds `@stackwright-pro/*` packages, fixes workspace refs |
113
+ | `pro-mcp-config` | Configures Pro MCP server in `.code-puppy.json` |
114
+ | `verify-pro-installation` | Prints success message after install |
115
+
116
+ ## Docs
117
+
118
+ See the main [Stackwright Pro documentation](https://github.com/Per-Aspera-LLC/stackwright-pro)
119
+ for architecture details, auth deep-dives, and deployment guides.
120
+
121
+ ## Starting the Otter Raft
122
+
123
+ The otter raft runs in two modes:
124
+
125
+ ### Interactive Mode (Default)
126
+
127
+ ```bash
128
+ python -m stackwright_pro.raft.cli_adapter foreman --spawn
129
+ ```
130
+
131
+ This spawns code-puppy with the foreman system prompt. The foreman:
132
+
133
+ - Asks questions about your project via TUI
134
+ - Coordinates specialist otters (brand, theme, api, auth, page)
135
+ - Uses certificate pinning to ensure only approved otters are invoked
136
+
137
+ ### Phases Mode (Debugging)
138
+
139
+ ```bash
140
+ python -m stackwright_pro.raft.cli_adapter foreman --phases
141
+ ```
142
+
143
+ Executes phases sequentially via the Python state machine. Useful for debugging.
package/dist/index.js ADDED
@@ -0,0 +1,566 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __commonJS = (cb, mod) => function __require() {
10
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+
29
+ // package.json
30
+ var require_package = __commonJS({
31
+ "package.json"(exports2, module2) {
32
+ module2.exports = {
33
+ name: "@stackwright-pro/launch-stackwright-pro",
34
+ version: "0.0.0-beta-20260417191149",
35
+ description: "Launch a new Stackwright Pro project with OpenAPI integration, auth, and the otter raft",
36
+ license: "MIT",
37
+ publishConfig: {
38
+ access: "public"
39
+ },
40
+ repository: {
41
+ type: "git",
42
+ url: "https://github.com/Per-Aspera-LLC/stackwright-pro"
43
+ },
44
+ keywords: [
45
+ "stackwright",
46
+ "stackwright-pro",
47
+ "scaffolding",
48
+ "openapi",
49
+ "government",
50
+ "hackathon"
51
+ ],
52
+ files: [
53
+ "dist",
54
+ "templates"
55
+ ],
56
+ bin: {
57
+ "launch-stackwright-pro": "dist/index.js"
58
+ },
59
+ scripts: {
60
+ build: "tsup",
61
+ dev: "tsup --watch",
62
+ test: "vitest run",
63
+ "test:coverage": "vitest run --coverage"
64
+ },
65
+ dependencies: {
66
+ "@stackwright-pro/scaffold-hooks": "latest",
67
+ "@stackwright/cli": "^0.7.1-alpha.0",
68
+ "@stackwright/scaffold-core": "^0.1.0",
69
+ chalk: "^5.6.2",
70
+ commander: "^14.0.3",
71
+ "fs-extra": "^11.3",
72
+ "js-yaml": "^4.1.0"
73
+ },
74
+ devDependencies: {
75
+ "@types/fs-extra": "^11.0",
76
+ "@types/js-yaml": "^4.0.9",
77
+ "@types/node": "^24.0.0",
78
+ typescript: "^5.0",
79
+ tsup: "^8.5",
80
+ vitest: "^4.0.18"
81
+ }
82
+ };
83
+ }
84
+ });
85
+
86
+ // src/index.ts
87
+ var import_commander = require("commander");
88
+ var import_path = __toESM(require("path"));
89
+ var import_os = __toESM(require("os"));
90
+ var import_fs_extra = __toESM(require("fs-extra"));
91
+ var import_chalk = __toESM(require("chalk"));
92
+ var import_js_yaml = __toESM(require("js-yaml"));
93
+ var import_child_process = require("child_process");
94
+ var import_cli = require("@stackwright/cli");
95
+ var import_scaffold_core = require("@stackwright/scaffold-core");
96
+ var import_scaffold_hooks = require("@stackwright-pro/scaffold-hooks");
97
+ (0, import_scaffold_hooks.registerProScaffoldHooks)();
98
+ var { version } = require_package();
99
+ async function copyProTemplate(templateName, destPath) {
100
+ const src = import_path.default.resolve(__dirname, "..", "templates", "pro", templateName);
101
+ await import_fs_extra.default.copy(src, destPath);
102
+ }
103
+ async function addAuthToStackwrightYml(targetDir) {
104
+ const ymlPath = import_path.default.join(targetDir, "stackwright.yml");
105
+ const content = await import_fs_extra.default.readFile(ymlPath, "utf-8");
106
+ const config = import_js_yaml.default.load(content);
107
+ config.auth = {
108
+ type: "pki",
109
+ profile: "dod_cac",
110
+ source: "gateway_headers",
111
+ roles: [
112
+ { name: "ADMIN", permissions: ["*"] },
113
+ { name: "ANALYST", permissions: ["data:read", "data:write"] },
114
+ { name: "VIEWER", permissions: ["data:read"] }
115
+ ],
116
+ protected_routes: [{ path: "/*", roles: ["VIEWER", "ANALYST", "ADMIN"] }],
117
+ public_routes: ["/", "/getting-started"]
118
+ };
119
+ await import_fs_extra.default.writeFile(ymlPath, import_js_yaml.default.dump(config, { lineWidth: 120 }));
120
+ }
121
+ async function addProDepsToPackageJson(targetDir) {
122
+ const pkgPath = import_path.default.join(targetDir, "package.json");
123
+ const content = await import_fs_extra.default.readFile(pkgPath, "utf-8");
124
+ const pkg = JSON.parse(content);
125
+ const dependencies = pkg.dependencies || {};
126
+ const devDependencies = pkg.devDependencies || {};
127
+ const scripts = pkg.scripts || {};
128
+ Object.assign(dependencies, {
129
+ "@stackwright-pro/mcp": "latest",
130
+ "@stackwright-pro/otters": "latest",
131
+ "@stackwright-pro/openapi": "latest",
132
+ "@stackwright-pro/auth": "latest",
133
+ "@stackwright-pro/auth-nextjs": "latest",
134
+ zod: "^3.23.0"
135
+ });
136
+ Object.assign(devDependencies, {
137
+ "@stoplight/prism-cli": "^5.14.2"
138
+ });
139
+ Object.assign(scripts, {
140
+ "dev:admin": "MOCK_USER=admin next dev",
141
+ "dev:analyst": "MOCK_USER=analyst next dev",
142
+ "dev:viewer": "MOCK_USER=viewer next dev",
143
+ prebuild: "node scripts/prebuild.js",
144
+ predev: "node scripts/prebuild.js"
145
+ });
146
+ pkg.dependencies = dependencies;
147
+ pkg.devDependencies = devDependencies;
148
+ pkg.scripts = scripts;
149
+ await import_fs_extra.default.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
150
+ }
151
+ async function addIntegrationToStackwrightYml(targetDir, specName, specFilename, mockUrl) {
152
+ const ymlPath = import_path.default.join(targetDir, "stackwright.yml");
153
+ const content = await import_fs_extra.default.readFile(ymlPath, "utf-8");
154
+ const config = import_js_yaml.default.load(content);
155
+ const integration = {
156
+ type: "openapi",
157
+ name: specName,
158
+ spec: `./specs/${specFilename}`,
159
+ collections: []
160
+ };
161
+ if (mockUrl) {
162
+ integration.mockUrl = mockUrl;
163
+ }
164
+ config.integrations = [integration];
165
+ await import_fs_extra.default.writeFile(ymlPath, import_js_yaml.default.dump(config, { lineWidth: 120 }));
166
+ }
167
+ async function handleSpec(targetDir, specPath, specName) {
168
+ const resolvedSpec = import_path.default.resolve(specPath);
169
+ if (!import_fs_extra.default.existsSync(resolvedSpec)) {
170
+ throw new Error(`Spec file not found: ${resolvedSpec}`);
171
+ }
172
+ const specFilename = import_path.default.basename(resolvedSpec);
173
+ const derivedName = specName || import_path.default.basename(specFilename, import_path.default.extname(specFilename));
174
+ const specsDir = import_path.default.join(targetDir, "specs");
175
+ await import_fs_extra.default.ensureDir(specsDir);
176
+ await import_fs_extra.default.copy(resolvedSpec, import_path.default.join(specsDir, specFilename));
177
+ return { specFilename, derivedName };
178
+ }
179
+ function generateReadme(options) {
180
+ const { projectName, hasSpec, specName, specFilename, hasOtters, hasMock } = options;
181
+ const apiIntegrationSection = hasSpec ? `## \u{1F4E1} API Integration
182
+
183
+ Your OpenAPI spec was copied to \`specs/${specFilename}\` and wired up in \`stackwright.yml\`.
184
+
185
+ **What gets generated:**
186
+
187
+ \`\`\`typescript
188
+ // src/generated/${specName}/schemas.ts
189
+ export const EquipmentSchema = z.object({
190
+ id: z.string(),
191
+ name: z.string(),
192
+ status: z.enum(['operational', 'maintenance', 'retired']),
193
+ // ... Zod validation for runtime safety
194
+ });
195
+
196
+ // src/generated/${specName}/types.ts
197
+ export type Equipment = z.infer<typeof EquipmentSchema>;
198
+
199
+ // src/generated/${specName}/client.ts
200
+ export class ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client {
201
+ async getEquipment(id: string): Promise<Equipment> {
202
+ // Auto-wired with auth, validation, error handling
203
+ }
204
+ }
205
+ \`\`\`
206
+
207
+ **How it works:**
208
+ 1. The \`predev\` script in \`package.json\` runs \`prebuild.js\` before every \`pnpm dev\`
209
+ 2. Prebuild reads your spec and generates type-safe code
210
+ 3. You import and use it:
211
+
212
+ \`\`\`typescript
213
+ import { ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client } from '@/generated/${specName}/client';
214
+ import type { Equipment } from '@/generated/${specName}/types';
215
+
216
+ const client = new ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client();
217
+ const gear: Equipment = await client.getEquipment('123');
218
+ \`\`\`
219
+
220
+ No manual typing. No drift. No runtime surprises.
221
+
222
+ ` : "";
223
+ const mockSection = hasMock ? `## \u{1F3AD} Mock Server
224
+
225
+ Prism mock server is configured to serve your API at **http://localhost:4010**.
226
+
227
+ \`\`\`bash
228
+ pnpm dev:mock # Starts Prism + Next.js together
229
+ \`\`\`
230
+
231
+ This starts:
232
+ 1. Prism mock server on port 4010 (serves your OpenAPI spec)
233
+ 2. Next.js dev server on port 3000
234
+
235
+ The generated API client is automatically configured to use the mock server.
236
+
237
+ ` : "";
238
+ const aiAgentsSection = hasOtters ? `## \u{1F9A6} AI Agents (The Otter Raft)
239
+
240
+ Your project includes **SIX specialized AI agents** in \`node_modules/@stackwright-pro/otters/\`:
241
+
242
+ - \`stackwright-pro-foreman-otter\` - Master coordinator that orchestrates all specialist otters
243
+ - \`stackwright-pro-api-otter\` - Discovers entities from OpenAPI specs
244
+ - \`stackwright-pro-auth-otter\` - Configures CAC/OIDC auth, RBAC rules, and audit middleware
245
+ - \`stackwright-pro-data-otter\` - Configures endpoint filters & ISR revalidation
246
+ - \`stackwright-pro-page-otter\` - Generates pages auto-wired with data, theme, and auth
247
+ - \`stackwright-pro-dashboard-otter\` - Builds live data dashboards
248
+
249
+ **Start the otter raft:**
250
+
251
+ \`\`\`bash
252
+ uvx stackwright-pro-raft
253
+ \`\`\`
254
+
255
+ > **Local dev (before PyPI publish):** \`python -m stackwright_pro.raft.cli_adapter foreman\`
256
+
257
+ The raft is a Python state machine with a Rich terminal UI \u2014 it asks questions about your brand, auth, and data preferences, then coordinates specialist otters to build your app. All user interaction happens in the terminal; code-puppy is called as a one-shot subprocess for creative tasks only.
258
+
259
+ **Example session prompt:**
260
+
261
+ "Build me a logistics dashboard from our OpenAPI spec. Connect to /equipment and /supplies endpoints."
262
+
263
+ ` : "";
264
+ return `# ${projectName}
265
+
266
+ A Stackwright Pro application with role-based auth, OpenAPI integration, and intelligent AI agents.
267
+
268
+ ## \u{1F680} Quick Start
269
+
270
+ \`\`\`bash
271
+ # Start development server
272
+ pnpm dev
273
+
274
+ # Open your browser
275
+ open http://localhost:3000
276
+ \`\`\`
277
+
278
+ ## \u{1F465} Role-Based Development
279
+
280
+ Stackwright Pro includes **mock authentication** for local development with three pre-configured roles:
281
+
282
+ \`\`\`bash
283
+ pnpm dev:admin # Full permissions (*)
284
+ pnpm dev:analyst # Read & write data
285
+ pnpm dev:viewer # Read-only access
286
+ \`\`\`
287
+
288
+ **How it works:**
289
+ - Each script sets the \`MOCK_USER\` environment variable
290
+ - \`lib/mock-auth.ts\` intercepts requests and injects the appropriate user context
291
+ - Your pages & API routes respect the RBAC rules in \`stackwright.yml\`
292
+ - No backend required for testing auth flows! \u{1F389}
293
+
294
+ **Pro tip:** Test your UI for all three roles to ensure proper permissions enforcement.
295
+
296
+ ${mockSection}## \u{1F4C1} Project Structure
297
+
298
+ \`\`\`
299
+ .
300
+ \u251C\u2500\u2500 pages/
301
+ \u2502 \u251C\u2500\u2500 _app.tsx # Pro version with AuthProvider + shadcn
302
+ \u2502 \u251C\u2500\u2500 index.tsx # Home page (auto-generated by Stackwright)
303
+ \u2502 \u251C\u2500\u2500 about/
304
+ \u2502 \u2502 \u2514\u2500\u2500 content.yml # Page content in YAML
305
+ \u2502 \u2514\u2500\u2500 ...
306
+ \u251C\u2500\u2500 lib/
307
+ \u2502 \u2514\u2500\u2500 mock-auth.ts # Dev-only auth mocking (no backend needed)
308
+ \u251C\u2500\u2500 scripts/
309
+ \u2502 \u2514\u2500\u2500 prebuild.js # Auto-generates code from OpenAPI specs
310
+ \u251C\u2500\u2500 specs/ # OpenAPI spec files (if --spec was used)
311
+ \u251C\u2500\u2500 src/
312
+ \u2502 \u2514\u2500\u2500 generated/ # Auto-generated types & clients
313
+ \u2502 \u2514\u2500\u2500 {specName}/
314
+ \u2502 \u251C\u2500\u2500 schemas.ts # Zod schemas for runtime validation
315
+ \u2502 \u251C\u2500\u2500 types.ts # TypeScript types
316
+ \u2502 \u251C\u2500\u2500 client.ts # API client with auth
317
+ \u2502 \u2514\u2500\u2500 provider.ts # CollectionProvider for Stackwright
318
+ \u251C\u2500\u2500 .stackwright/
319
+ \u2502 \u2514\u2500\u2500 otters/ # AI agent configs (4 specialized otters)
320
+ \u251C\u2500\u2500 stackwright.yml # Theme, auth, API integrations
321
+ \u2514\u2500\u2500 package.json
322
+ \`\`\`
323
+
324
+ **Key Files:**
325
+ - \`pages/_app.tsx\` - Wraps your app with \`AuthProvider\` for RBAC
326
+ - \`stackwright.yml\` - Single source of truth for theme, auth roles, and API wiring
327
+ - \`lib/mock-auth.ts\` - Mocks CAC/PIV headers locally so you can test auth flows
328
+ - \`scripts/prebuild.js\` - Runs before dev/build to generate types from your OpenAPI spec
329
+
330
+ ## \u{1F4C4} Adding Pages
331
+
332
+ Stackwright uses **YAML for content**, so you never touch JSX unless you want to.
333
+
334
+ **Example:** Create \`pages/about/content.yml\`:
335
+
336
+ \`\`\`yaml
337
+ title: About Us
338
+ description: Learn more about our mission
339
+
340
+ sections:
341
+ - type: main
342
+ heading: Who We Are
343
+ body: |
344
+ We build mission-critical logistics systems for the Marine Corps.
345
+
346
+ - type: feature_list
347
+ title: Our Capabilities
348
+ features:
349
+ - icon: Security
350
+ title: Zero Trust
351
+ description: PKI-based auth with CAC/PIV
352
+ - icon: Api
353
+ title: Real-time Data
354
+ description: OpenAPI-backed, Zod-validated
355
+ \`\`\`
356
+
357
+ That's it. Navigate to \`/about\` and it just works. No routing config, no React components.
358
+
359
+ ${apiIntegrationSection}## \u{1F3A8} Customizing Theme
360
+
361
+ Edit \`stackwright.yml\` to change colors, fonts, spacing \u2014 applies everywhere instantly.
362
+
363
+ \`\`\`yaml
364
+ theme:
365
+ id: my-theme
366
+ name: My Custom Theme
367
+ colors:
368
+ primary: "#C41E3A" # Marine Corps red
369
+ background: "#FFFFFF"
370
+ text: "#1A1A1A"
371
+ accent: "#FFD700" # Gold
372
+ typography:
373
+ fontFamily: "'Inter', sans-serif"
374
+ h1Size: "2.5rem"
375
+ spacing:
376
+ unit: 8
377
+ \`\`\`
378
+
379
+ No CSS files. No theme provider boilerplate. Just YAML. Stackwright handles the rest.
380
+
381
+ ## \u{1F510} Auth Configuration
382
+
383
+ Stackwright Pro comes with **3 pre-configured roles** in \`stackwright.yml\`:
384
+
385
+ \`\`\`yaml
386
+ auth:
387
+ roles:
388
+ - name: ADMIN
389
+ permissions: ['*'] # Full access
390
+ - name: ANALYST
391
+ permissions: ['data:read', 'data:write'] # Read/write data
392
+ - name: VIEWER
393
+ permissions: ['data:read'] # Read-only
394
+ \`\`\`
395
+
396
+ **Gate content in your YAML:**
397
+
398
+ \`\`\`yaml
399
+ sections:
400
+ - type: button
401
+ label: Delete Equipment
402
+ action: /api/equipment/delete
403
+ auth:
404
+ required_roles: [ADMIN]
405
+ fallback: hide # Options: hide, disable, show_message
406
+ \`\`\`
407
+
408
+ Only admins see the button. Analysts and viewers? Button doesn't exist in the DOM.
409
+
410
+ **Route protection:**
411
+
412
+ \`\`\`yaml
413
+ auth:
414
+ protected_routes:
415
+ - path: /admin/*
416
+ roles: [ADMIN]
417
+ - path: /equipment/*
418
+ roles: [ANALYST, ADMIN]
419
+ public_routes:
420
+ - /
421
+ - /about
422
+ \`\`\`
423
+
424
+ No middleware to write. No useAuth hooks to remember. RBAC is declarative.
425
+
426
+ ${aiAgentsSection}## \u{1F4DA} Learn More
427
+
428
+ - **Stackwright Docs:** [https://stackwright.dev](https://stackwright.dev)
429
+ - **OSS Repo:** [https://github.com/Per-Aspera-LLC/stackwright](https://github.com/Per-Aspera-LLC/stackwright)
430
+ - **Pro Repo:** [https://github.com/Per-Aspera-LLC/stackwright-pro](https://github.com/Per-Aspera-LLC/stackwright-pro)
431
+
432
+ **Questions?** File an issue or ping us in the discussions. We're here to help you ship faster.
433
+ `;
434
+ }
435
+ async function launch(targetDir, options) {
436
+ const dirName = import_path.default.basename(targetDir);
437
+ console.log(import_chalk.default.cyan.bold("\n\u{1F6A2} Launching Stackwright Pro...\n"));
438
+ const scaffoldOpts = {
439
+ name: options.name || dirName,
440
+ ...options.title !== void 0 && { title: options.title },
441
+ ...options.theme !== void 0 && { theme: options.theme },
442
+ ...options.force !== void 0 && { force: options.force },
443
+ ...options.yes !== void 0 && { noInteractive: options.yes }
444
+ };
445
+ await (0, import_cli.scaffold)(targetDir, scaffoldOpts);
446
+ console.log(import_chalk.default.green("\u2705 Base project scaffolded (hooks added Pro deps + MCP config)"));
447
+ await addProDepsToPackageJson(targetDir);
448
+ console.log(import_chalk.default.green("\u{1F9A6} Pro packages added to package.json"));
449
+ console.log(import_chalk.default.cyan("\n\u{1F4E6} Installing dependencies (this takes a moment)..."));
450
+ if (!options.skipInstall) {
451
+ try {
452
+ (0, import_child_process.execSync)("pnpm install", { cwd: targetDir, stdio: "inherit", timeout: 12e4 });
453
+ console.log(import_chalk.default.green("\u2705 Dependencies installed"));
454
+ } catch {
455
+ console.warn(import_chalk.default.yellow("\u26A0\uFE0F pnpm install failed \u2014 run it manually: pnpm install"));
456
+ }
457
+ } else {
458
+ console.log(import_chalk.default.dim("\u2139\uFE0F Skipping install (--skip-install). Run: pnpm install"));
459
+ }
460
+ const dependencyMode = "standalone";
461
+ await (0, import_scaffold_core.runScaffoldHooks)("postInstall", {
462
+ targetDir,
463
+ projectName: options.name || import_path.default.basename(targetDir),
464
+ siteTitle: options.title || options.name || import_path.default.basename(targetDir),
465
+ themeId: options.theme || "corporate",
466
+ packageJson: {},
467
+ dependencyMode
468
+ });
469
+ await copyProTemplate("_app.tsx", import_path.default.join(targetDir, "pages", "_app.tsx"));
470
+ await copyProTemplate("content.yml", import_path.default.join(targetDir, "pages", "content.yml"));
471
+ await copyProTemplate("next.config.js", import_path.default.join(targetDir, "next.config.js"));
472
+ await import_fs_extra.default.ensureDir(import_path.default.join(targetDir, "lib"));
473
+ await copyProTemplate("mock-auth.ts", import_path.default.join(targetDir, "lib", "mock-auth.ts"));
474
+ await copyProTemplate("yaml.d.ts", import_path.default.join(targetDir, "yaml.d.ts"));
475
+ await import_fs_extra.default.ensureDir(import_path.default.join(targetDir, "scripts"));
476
+ await copyProTemplate("prebuild.js", import_path.default.join(targetDir, "scripts", "prebuild.js"));
477
+ console.log(import_chalk.default.green("\u{1F510} Auth integration added (RBAC with 3 roles)"));
478
+ await addAuthToStackwrightYml(targetDir);
479
+ let specInfo = null;
480
+ const MOCK_URL = "http://localhost:4010";
481
+ if (options.spec) {
482
+ specInfo = await handleSpec(targetDir, options.spec, options.specName);
483
+ await addIntegrationToStackwrightYml(
484
+ targetDir,
485
+ specInfo.derivedName,
486
+ specInfo.specFilename,
487
+ options.mock ? MOCK_URL : void 0
488
+ );
489
+ console.log(import_chalk.default.green("\u{1F4E1} OpenAPI integration wired up"));
490
+ if (options.mock) {
491
+ console.log(import_chalk.default.green("\u{1F3AD} Prism mock server configured"));
492
+ }
493
+ } else if (options.mock) {
494
+ console.log(
495
+ import_chalk.default.yellow(
496
+ "\u26A0\uFE0F No spec provided with --mock. Using sample Petstore spec for demo. Replace with your API spec."
497
+ )
498
+ );
499
+ const specsDir = import_path.default.join(targetDir, "specs");
500
+ await import_fs_extra.default.ensureDir(specsDir);
501
+ await copyProTemplate("petstore.yaml", import_path.default.join(specsDir, "petstore.yaml"));
502
+ specInfo = { specFilename: "petstore.yaml", derivedName: "petstore" };
503
+ await addIntegrationToStackwrightYml(
504
+ targetDir,
505
+ specInfo.derivedName,
506
+ specInfo.specFilename,
507
+ MOCK_URL
508
+ );
509
+ console.log(import_chalk.default.green("\u{1F4E1} Sample Petstore spec wired up"));
510
+ console.log(import_chalk.default.green("\u{1F3AD} Prism mock server configured"));
511
+ }
512
+ const readmeContent = generateReadme({
513
+ projectName: options.name || dirName,
514
+ hasSpec: !!specInfo,
515
+ ...specInfo?.derivedName !== void 0 && { specName: specInfo.derivedName },
516
+ ...specInfo?.specFilename !== void 0 && { specFilename: specInfo.specFilename },
517
+ hasOtters: !options.skipOtters,
518
+ hasMock: !!options.mock
519
+ });
520
+ await import_fs_extra.default.writeFile(import_path.default.join(targetDir, "README.md"), readmeContent);
521
+ console.log(import_chalk.default.green("\u{1F4C4} README.md created"));
522
+ const relDir = import_path.default.relative(process.cwd(), targetDir) || ".";
523
+ console.log(import_chalk.default.cyan.bold("\n\u{1F389} All set! Here's what to do next:\n"));
524
+ console.log(import_chalk.default.white(` 1. cd ${relDir}`));
525
+ console.log(import_chalk.default.white(" 2. uvx stackwright-pro-raft"));
526
+ console.log(import_chalk.default.dim(" (local dev: python -m stackwright_pro.raft.cli_adapter foreman)"));
527
+ if (specInfo) {
528
+ console.log(import_chalk.default.cyan(`
529
+ \u{1F4E1} Your API spec was copied to specs/${specInfo.specFilename}`));
530
+ console.log(import_chalk.default.dim(" The prebuild will generate types & client on first `pnpm dev`."));
531
+ }
532
+ if (options.mock) {
533
+ console.log(import_chalk.default.cyan("\n\u{1F3AD} Run with mock server:"));
534
+ console.log(import_chalk.default.white(" pnpm dev:mock # Starts Prism + Next.js"));
535
+ }
536
+ if (!options.skipOtters) {
537
+ console.log(import_chalk.default.cyan.bold("\n\u{1F9A6} The otter raft is ready to build your site!"));
538
+ console.log(import_chalk.default.dim(" Run the command above to get started.\n"));
539
+ }
540
+ }
541
+ async function main() {
542
+ const program = new import_commander.Command();
543
+ program.name("launch-stackwright-pro").description("\u{1F6A2} Launch a new Stackwright Pro project with auth, OpenAPI, and the otter raft").version(version).argument("[directory]", "Project directory", ".").option("--name <name>", "Project name (used in package.json)").option("--title <title>", "Site title shown in the app bar and browser tab").option("--theme <themeId>", "Theme ID (e.g., corporate, creative, minimal)").option("--force", "Launch even if the target directory is not empty").option("--skip-otters", "Skip copying otter raft configs").option("-y, --yes", "Skip all prompts, use defaults").option("--mock", "Configure Prism mock server for API development (runs on port 4010)").option(
544
+ "--spec <path>",
545
+ "Path to an OpenAPI spec (YAML or JSON) \u2014 copies into project and wires up integration"
546
+ ).option(
547
+ "--spec-name <name>",
548
+ "Name for the API integration (default: derived from spec filename)"
549
+ ).option(
550
+ "--skip-install",
551
+ "Skip automatic pnpm install (useful in CI or when using a different package manager)"
552
+ ).action(async (directory, options) => {
553
+ const homeDir = import_os.default.homedir();
554
+ const targetDir = import_path.default.resolve(directory);
555
+ if (!targetDir.startsWith(homeDir + import_path.default.sep) && targetDir !== homeDir) {
556
+ console.warn(import_chalk.default.yellow(`\u26A0\uFE0F Scaffolding outside your home directory: ${targetDir}`));
557
+ }
558
+ await launch(targetDir, options);
559
+ });
560
+ await program.parseAsync(process.argv);
561
+ }
562
+ main().catch((err) => {
563
+ console.error(import_chalk.default.red("\n\u274C Launch failed:"), err);
564
+ process.exit(1);
565
+ });
566
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../package.json","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@stackwright-pro/launch-stackwright-pro\",\n \"version\": \"0.0.0-beta-20260417191149\",\n \"description\": \"Launch a new Stackwright Pro project with OpenAPI integration, auth, and the otter raft\",\n \"license\": \"MIT\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/Per-Aspera-LLC/stackwright-pro\"\n },\n \"keywords\": [\n \"stackwright\",\n \"stackwright-pro\",\n \"scaffolding\",\n \"openapi\",\n \"government\",\n \"hackathon\"\n ],\n \"files\": [\n \"dist\",\n \"templates\"\n ],\n \"bin\": {\n \"launch-stackwright-pro\": \"dist/index.js\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"test:coverage\": \"vitest run --coverage\"\n },\n \"dependencies\": {\n \"@stackwright-pro/scaffold-hooks\": \"latest\",\n \"@stackwright/cli\": \"^0.7.1-alpha.0\",\n \"@stackwright/scaffold-core\": \"^0.1.0\",\n \"chalk\": \"^5.6.2\",\n \"commander\": \"^14.0.3\",\n \"fs-extra\": \"^11.3\",\n \"js-yaml\": \"^4.1.0\"\n },\n \"devDependencies\": {\n \"@types/fs-extra\": \"^11.0\",\n \"@types/js-yaml\": \"^4.0.9\",\n \"@types/node\": \"^24.0.0\",\n \"typescript\": \"^5.0\",\n \"tsup\": \"^8.5\",\n \"vitest\": \"^4.0.18\"\n }\n}\n","import { Command } from 'commander';\nimport path from 'path';\nimport os from 'os';\nimport fs from 'fs-extra';\nimport chalk from 'chalk';\nimport yaml from 'js-yaml';\nimport { execSync } from 'child_process';\n\nimport { scaffold, ScaffoldOptions } from '@stackwright/cli';\nimport { runScaffoldHooks } from '@stackwright/scaffold-core';\nimport { registerProScaffoldHooks } from '@stackwright-pro/scaffold-hooks';\n// Register Pro hooks (adds deps, MCP config, etc.)\n// Must be called before scaffold() to ensure hooks run in the CLI's scaffold-core context\nregisterProScaffoldHooks();\n\nconst { version } = require('../package.json') as { version: string };\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface LaunchProOptions {\n name?: string;\n title?: string;\n theme?: string;\n force?: boolean;\n skipOtters?: boolean;\n skipInstall?: boolean;\n yes?: boolean;\n spec?: string;\n specName?: string;\n mock?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Template copying (Pro-specific - hooks can't handle these)\n// ---------------------------------------------------------------------------\n\nasync function copyProTemplate(templateName: string, destPath: string): Promise<void> {\n const src = path.resolve(__dirname, '..', 'templates', 'pro', templateName);\n await fs.copy(src, destPath);\n}\n\n// ---------------------------------------------------------------------------\n// Auth config (Pro-specific - hooks can't handle these)\n// ---------------------------------------------------------------------------\n\nasync function addAuthToStackwrightYml(targetDir: string): Promise<void> {\n const ymlPath = path.join(targetDir, 'stackwright.yml');\n const content = await fs.readFile(ymlPath, 'utf-8');\n const config = yaml.load(content) as Record<string, unknown>;\n\n config.auth = {\n type: 'pki',\n profile: 'dod_cac',\n source: 'gateway_headers',\n roles: [\n { name: 'ADMIN', permissions: ['*'] },\n { name: 'ANALYST', permissions: ['data:read', 'data:write'] },\n { name: 'VIEWER', permissions: ['data:read'] },\n ],\n protected_routes: [{ path: '/*', roles: ['VIEWER', 'ANALYST', 'ADMIN'] }],\n public_routes: ['/', '/getting-started'],\n };\n\n await fs.writeFile(ymlPath, yaml.dump(config, { lineWidth: 120 }));\n}\n\nasync function addProDepsToPackageJson(targetDir: string): Promise<void> {\n const pkgPath = path.join(targetDir, 'package.json');\n const content = await fs.readFile(pkgPath, 'utf-8');\n const pkg = JSON.parse(content) as Record<string, any>;\n\n const dependencies = (pkg.dependencies as Record<string, string>) || {};\n const devDependencies = (pkg.devDependencies as Record<string, string>) || {};\n const scripts = (pkg.scripts as Record<string, string>) || {};\n\n // Pro runtime dependencies\n Object.assign(dependencies, {\n '@stackwright-pro/mcp': 'latest',\n '@stackwright-pro/otters': 'latest',\n '@stackwright-pro/openapi': 'latest',\n '@stackwright-pro/auth': 'latest',\n '@stackwright-pro/auth-nextjs': 'latest',\n zod: '^3.23.0',\n });\n\n // Pro dev dependencies\n Object.assign(devDependencies, {\n '@stoplight/prism-cli': '^5.14.2',\n });\n\n // Role-based dev scripts\n Object.assign(scripts, {\n 'dev:admin': 'MOCK_USER=admin next dev',\n 'dev:analyst': 'MOCK_USER=analyst next dev',\n 'dev:viewer': 'MOCK_USER=viewer next dev',\n prebuild: 'node scripts/prebuild.js',\n predev: 'node scripts/prebuild.js',\n });\n\n pkg.dependencies = dependencies;\n pkg.devDependencies = devDependencies;\n pkg.scripts = scripts;\n\n await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\\n', 'utf-8');\n}\n\nasync function addIntegrationToStackwrightYml(\n targetDir: string,\n specName: string,\n specFilename: string,\n mockUrl?: string\n): Promise<void> {\n const ymlPath = path.join(targetDir, 'stackwright.yml');\n const content = await fs.readFile(ymlPath, 'utf-8');\n const config = yaml.load(content) as Record<string, unknown>;\n\n const integration: Record<string, unknown> = {\n type: 'openapi',\n name: specName,\n spec: `./specs/${specFilename}`,\n collections: [],\n };\n\n if (mockUrl) {\n integration.mockUrl = mockUrl;\n }\n\n config.integrations = [integration];\n\n await fs.writeFile(ymlPath, yaml.dump(config, { lineWidth: 120 }));\n}\n\n// ---------------------------------------------------------------------------\n// Spec handling (Pro-specific - hooks can't handle these)\n// ---------------------------------------------------------------------------\n\nasync function handleSpec(\n targetDir: string,\n specPath: string,\n specName?: string\n): Promise<{ specFilename: string; derivedName: string }> {\n const resolvedSpec = path.resolve(specPath);\n\n if (!fs.existsSync(resolvedSpec)) {\n throw new Error(`Spec file not found: ${resolvedSpec}`);\n }\n\n const specFilename = path.basename(resolvedSpec);\n const derivedName = specName || path.basename(specFilename, path.extname(specFilename));\n\n const specsDir = path.join(targetDir, 'specs');\n await fs.ensureDir(specsDir);\n await fs.copy(resolvedSpec, path.join(specsDir, specFilename));\n\n return { specFilename, derivedName };\n}\n\n// ---------------------------------------------------------------------------\n// README generator (Pro-specific - hooks can't handle these)\n// ---------------------------------------------------------------------------\n\nfunction generateReadme(options: {\n projectName: string;\n hasSpec: boolean;\n specName?: string;\n specFilename?: string;\n hasOtters: boolean;\n hasMock: boolean;\n}): string {\n const { projectName, hasSpec, specName, specFilename, hasOtters, hasMock } = options;\n\n // Build API Integration section if spec is present\n const apiIntegrationSection = hasSpec\n ? `## 📡 API Integration\n\nYour OpenAPI spec was copied to \\`specs/${specFilename}\\` and wired up in \\`stackwright.yml\\`.\n\n**What gets generated:**\n\n\\`\\`\\`typescript\n// src/generated/${specName}/schemas.ts\nexport const EquipmentSchema = z.object({\n id: z.string(),\n name: z.string(),\n status: z.enum(['operational', 'maintenance', 'retired']),\n // ... Zod validation for runtime safety\n});\n\n// src/generated/${specName}/types.ts\nexport type Equipment = z.infer<typeof EquipmentSchema>;\n\n// src/generated/${specName}/client.ts\nexport class ${specName!.charAt(0).toUpperCase() + specName!.slice(1)}Client {\n async getEquipment(id: string): Promise<Equipment> {\n // Auto-wired with auth, validation, error handling\n }\n}\n\\`\\`\\`\n\n**How it works:**\n1. The \\`predev\\` script in \\`package.json\\` runs \\`prebuild.js\\` before every \\`pnpm dev\\`\n2. Prebuild reads your spec and generates type-safe code\n3. You import and use it:\n\n\\`\\`\\`typescript\nimport { ${specName!.charAt(0).toUpperCase() + specName!.slice(1)}Client } from '@/generated/${specName}/client';\nimport type { Equipment } from '@/generated/${specName}/types';\n\nconst client = new ${specName!.charAt(0).toUpperCase() + specName!.slice(1)}Client();\nconst gear: Equipment = await client.getEquipment('123');\n\\`\\`\\`\n\nNo manual typing. No drift. No runtime surprises.\n\n`\n : '';\n\n // Build mock server section if enabled\n const mockSection = hasMock\n ? `## 🎭 Mock Server\n\nPrism mock server is configured to serve your API at **http://localhost:4010**.\n\n\\`\\`\\`bash\npnpm dev:mock # Starts Prism + Next.js together\n\\`\\`\\`\n\nThis starts:\n1. Prism mock server on port 4010 (serves your OpenAPI spec)\n2. Next.js dev server on port 3000\n\nThe generated API client is automatically configured to use the mock server.\n\n`\n : '';\n\n // Build AI Agents section if otters are present\n const aiAgentsSection = hasOtters\n ? `## 🦦 AI Agents (The Otter Raft)\n\nYour project includes **SIX specialized AI agents** in \\`node_modules/@stackwright-pro/otters/\\`:\n\n- \\`stackwright-pro-foreman-otter\\` - Master coordinator that orchestrates all specialist otters\n- \\`stackwright-pro-api-otter\\` - Discovers entities from OpenAPI specs\n- \\`stackwright-pro-auth-otter\\` - Configures CAC/OIDC auth, RBAC rules, and audit middleware\n- \\`stackwright-pro-data-otter\\` - Configures endpoint filters & ISR revalidation\n- \\`stackwright-pro-page-otter\\` - Generates pages auto-wired with data, theme, and auth\n- \\`stackwright-pro-dashboard-otter\\` - Builds live data dashboards\n\n**Start the otter raft:**\n\n\\`\\`\\`bash\nuvx stackwright-pro-raft\n\\`\\`\\`\n\n> **Local dev (before PyPI publish):** \\`python -m stackwright_pro.raft.cli_adapter foreman\\`\n\nThe raft is a Python state machine with a Rich terminal UI — it asks questions about your brand, auth, and data preferences, then coordinates specialist otters to build your app. All user interaction happens in the terminal; code-puppy is called as a one-shot subprocess for creative tasks only.\n\n**Example session prompt:**\n\n\"Build me a logistics dashboard from our OpenAPI spec. Connect to /equipment and /supplies endpoints.\"\n\n`\n : '';\n\n return `# ${projectName}\n\nA Stackwright Pro application with role-based auth, OpenAPI integration, and intelligent AI agents.\n\n## 🚀 Quick Start\n\n\\`\\`\\`bash\n# Start development server\npnpm dev\n\n# Open your browser\nopen http://localhost:3000\n\\`\\`\\`\n\n## 👥 Role-Based Development\n\nStackwright Pro includes **mock authentication** for local development with three pre-configured roles:\n\n\\`\\`\\`bash\npnpm dev:admin # Full permissions (*)\npnpm dev:analyst # Read & write data\npnpm dev:viewer # Read-only access\n\\`\\`\\`\n\n**How it works:**\n- Each script sets the \\`MOCK_USER\\` environment variable\n- \\`lib/mock-auth.ts\\` intercepts requests and injects the appropriate user context\n- Your pages & API routes respect the RBAC rules in \\`stackwright.yml\\`\n- No backend required for testing auth flows! 🎉\n\n**Pro tip:** Test your UI for all three roles to ensure proper permissions enforcement.\n\n${mockSection}## 📁 Project Structure\n\n\\`\\`\\`\n.\n├── pages/\n│ ├── _app.tsx # Pro version with AuthProvider + shadcn\n│ ├── index.tsx # Home page (auto-generated by Stackwright)\n│ ├── about/\n│ │ └── content.yml # Page content in YAML\n│ └── ...\n├── lib/\n│ └── mock-auth.ts # Dev-only auth mocking (no backend needed)\n├── scripts/\n│ └── prebuild.js # Auto-generates code from OpenAPI specs\n├── specs/ # OpenAPI spec files (if --spec was used)\n├── src/\n│ └── generated/ # Auto-generated types & clients\n│ └── {specName}/\n│ ├── schemas.ts # Zod schemas for runtime validation\n│ ├── types.ts # TypeScript types\n│ ├── client.ts # API client with auth\n│ └── provider.ts # CollectionProvider for Stackwright\n├── .stackwright/\n│ └── otters/ # AI agent configs (4 specialized otters)\n├── stackwright.yml # Theme, auth, API integrations\n└── package.json\n\\`\\`\\`\n\n**Key Files:**\n- \\`pages/_app.tsx\\` - Wraps your app with \\`AuthProvider\\` for RBAC\n- \\`stackwright.yml\\` - Single source of truth for theme, auth roles, and API wiring\n- \\`lib/mock-auth.ts\\` - Mocks CAC/PIV headers locally so you can test auth flows\n- \\`scripts/prebuild.js\\` - Runs before dev/build to generate types from your OpenAPI spec\n\n## 📄 Adding Pages\n\nStackwright uses **YAML for content**, so you never touch JSX unless you want to.\n\n**Example:** Create \\`pages/about/content.yml\\`:\n\n\\`\\`\\`yaml\ntitle: About Us\ndescription: Learn more about our mission\n\nsections:\n - type: main\n heading: Who We Are\n body: |\n We build mission-critical logistics systems for the Marine Corps.\n\n - type: feature_list\n title: Our Capabilities\n features:\n - icon: Security\n title: Zero Trust\n description: PKI-based auth with CAC/PIV\n - icon: Api\n title: Real-time Data\n description: OpenAPI-backed, Zod-validated\n\\`\\`\\`\n\nThat's it. Navigate to \\`/about\\` and it just works. No routing config, no React components.\n\n${apiIntegrationSection}## 🎨 Customizing Theme\n\nEdit \\`stackwright.yml\\` to change colors, fonts, spacing — applies everywhere instantly.\n\n\\`\\`\\`yaml\ntheme:\n id: my-theme\n name: My Custom Theme\n colors:\n primary: \"#C41E3A\" # Marine Corps red\n background: \"#FFFFFF\"\n text: \"#1A1A1A\"\n accent: \"#FFD700\" # Gold\n typography:\n fontFamily: \"'Inter', sans-serif\"\n h1Size: \"2.5rem\"\n spacing:\n unit: 8\n\\`\\`\\`\n\nNo CSS files. No theme provider boilerplate. Just YAML. Stackwright handles the rest.\n\n## 🔐 Auth Configuration\n\nStackwright Pro comes with **3 pre-configured roles** in \\`stackwright.yml\\`:\n\n\\`\\`\\`yaml\nauth:\n roles:\n - name: ADMIN\n permissions: ['*'] # Full access\n - name: ANALYST \n permissions: ['data:read', 'data:write'] # Read/write data\n - name: VIEWER\n permissions: ['data:read'] # Read-only\n\\`\\`\\`\n\n**Gate content in your YAML:**\n\n\\`\\`\\`yaml\nsections:\n - type: button\n label: Delete Equipment\n action: /api/equipment/delete\n auth:\n required_roles: [ADMIN]\n fallback: hide # Options: hide, disable, show_message\n\\`\\`\\`\n\nOnly admins see the button. Analysts and viewers? Button doesn't exist in the DOM.\n\n**Route protection:**\n\n\\`\\`\\`yaml\nauth:\n protected_routes:\n - path: /admin/*\n roles: [ADMIN]\n - path: /equipment/* \n roles: [ANALYST, ADMIN]\n public_routes:\n - /\n - /about\n\\`\\`\\`\n\nNo middleware to write. No useAuth hooks to remember. RBAC is declarative.\n\n${aiAgentsSection}## 📚 Learn More\n\n- **Stackwright Docs:** [https://stackwright.dev](https://stackwright.dev)\n- **OSS Repo:** [https://github.com/Per-Aspera-LLC/stackwright](https://github.com/Per-Aspera-LLC/stackwright)\n- **Pro Repo:** [https://github.com/Per-Aspera-LLC/stackwright-pro](https://github.com/Per-Aspera-LLC/stackwright-pro)\n\n**Questions?** File an issue or ping us in the discussions. We're here to help you ship faster.\n`;\n}\n\n// ---------------------------------------------------------------------------\n// Main launch flow\n// ---------------------------------------------------------------------------\n\nasync function launch(targetDir: string, options: LaunchProOptions): Promise<void> {\n const dirName = path.basename(targetDir);\n\n console.log(chalk.cyan.bold('\\n🚢 Launching Stackwright Pro...\\n'));\n\n // ------------------------------------------------------------------\n // 1. Scaffold base OSS project via @stackwright/cli\n // Hooks auto-handle: Pro deps, MCP config, role-based scripts\n // ------------------------------------------------------------------\n\n const scaffoldOpts: ScaffoldOptions = {\n name: options.name || dirName,\n ...(options.title !== undefined && { title: options.title }),\n ...(options.theme !== undefined && { theme: options.theme }),\n ...(options.force !== undefined && { force: options.force }),\n ...(options.yes !== undefined && { noInteractive: options.yes }),\n };\n\n await scaffold(targetDir, scaffoldOpts);\n console.log(chalk.green('✅ Base project scaffolded (hooks added Pro deps + MCP config)'));\n\n // ------------------------------------------------------------------\n // 1b. Run pnpm install — citizen devs shouldn't need to know this step exists\n // ------------------------------------------------------------------\n\n // Patch Pro packages directly into package.json before install.\n // This bypasses the scaffold hooks system (which has a known version-resolution\n // issue) and guarantees pro deps land regardless of hook execution order.\n await addProDepsToPackageJson(targetDir);\n console.log(chalk.green('🦦 Pro packages added to package.json'));\n\n console.log(chalk.cyan('\\n📦 Installing dependencies (this takes a moment)...'));\n if (!options.skipInstall) {\n try {\n execSync('pnpm install', { cwd: targetDir, stdio: 'inherit', timeout: 120_000 });\n console.log(chalk.green('✅ Dependencies installed'));\n } catch {\n console.warn(chalk.yellow('⚠️ pnpm install failed — run it manually: pnpm install'));\n }\n } else {\n console.log(chalk.dim('ℹ️ Skipping install (--skip-install). Run: pnpm install'));\n }\n\n // Fire postInstall hooks now that install has run\n const dependencyMode: 'workspace' | 'standalone' = 'standalone';\n await runScaffoldHooks('postInstall', {\n targetDir,\n projectName: options.name || path.basename(targetDir),\n siteTitle: options.title || options.name || path.basename(targetDir),\n themeId: options.theme || 'corporate',\n packageJson: {},\n dependencyMode,\n });\n\n // ------------------------------------------------------------------\n // 2. Copy Pro templates (hooks can't handle these)\n // ------------------------------------------------------------------\n\n // Replace _app.tsx with Pro version (AuthProvider, shadcn, etc.)\n await copyProTemplate('_app.tsx', path.join(targetDir, 'pages', '_app.tsx'));\n\n // Add root content.yml for the home page\n await copyProTemplate('content.yml', path.join(targetDir, 'pages', 'content.yml'));\n\n // Replace next.config.js with Pro version (transpile pro pkgs)\n await copyProTemplate('next.config.js', path.join(targetDir, 'next.config.js'));\n\n // Add lib/mock-auth.ts\n await fs.ensureDir(path.join(targetDir, 'lib'));\n await copyProTemplate('mock-auth.ts', path.join(targetDir, 'lib', 'mock-auth.ts'));\n\n // Add yaml.d.ts for TypeScript support\n await copyProTemplate('yaml.d.ts', path.join(targetDir, 'yaml.d.ts'));\n\n // Add scripts/prebuild.js\n await fs.ensureDir(path.join(targetDir, 'scripts'));\n await copyProTemplate('prebuild.js', path.join(targetDir, 'scripts', 'prebuild.js'));\n\n console.log(chalk.green('🔐 Auth integration added (RBAC with 3 roles)'));\n\n // ------------------------------------------------------------------\n // 3. Add auth section to stackwright.yml\n // ------------------------------------------------------------------\n\n await addAuthToStackwrightYml(targetDir);\n\n // ------------------------------------------------------------------\n // 4. Handle --spec if provided\n // ------------------------------------------------------------------\n\n let specInfo: { specFilename: string; derivedName: string } | null = null;\n const MOCK_URL = 'http://localhost:4010';\n\n if (options.spec) {\n specInfo = await handleSpec(targetDir, options.spec, options.specName);\n\n // Pass mockUrl if --mock flag is set\n await addIntegrationToStackwrightYml(\n targetDir,\n specInfo.derivedName,\n specInfo.specFilename,\n options.mock ? MOCK_URL : undefined\n );\n console.log(chalk.green('📡 OpenAPI integration wired up'));\n\n if (options.mock) {\n console.log(chalk.green('🎭 Prism mock server configured'));\n }\n } else if (options.mock) {\n // --mock without --spec: use sample Petstore spec for demo\n console.log(\n chalk.yellow(\n '⚠️ No spec provided with --mock. Using sample Petstore spec for demo. Replace with your API spec.'\n )\n );\n\n const specsDir = path.join(targetDir, 'specs');\n await fs.ensureDir(specsDir);\n await copyProTemplate('petstore.yaml', path.join(specsDir, 'petstore.yaml'));\n\n specInfo = { specFilename: 'petstore.yaml', derivedName: 'petstore' };\n\n await addIntegrationToStackwrightYml(\n targetDir,\n specInfo.derivedName,\n specInfo.specFilename,\n MOCK_URL\n );\n console.log(chalk.green('📡 Sample Petstore spec wired up'));\n console.log(chalk.green('🎭 Prism mock server configured'));\n }\n\n // ------------------------------------------------------------------\n // 5. Generate README\n // ------------------------------------------------------------------\n\n const readmeContent = generateReadme({\n projectName: options.name || dirName,\n hasSpec: !!specInfo,\n ...(specInfo?.derivedName !== undefined && { specName: specInfo.derivedName }),\n ...(specInfo?.specFilename !== undefined && { specFilename: specInfo.specFilename }),\n hasOtters: !options.skipOtters,\n hasMock: !!options.mock,\n });\n\n await fs.writeFile(path.join(targetDir, 'README.md'), readmeContent);\n console.log(chalk.green('📄 README.md created'));\n\n // ------------------------------------------------------------------\n // 6. Print next steps\n // ------------------------------------------------------------------\n\n const relDir = path.relative(process.cwd(), targetDir) || '.';\n\n console.log(chalk.cyan.bold(\"\\n🎉 All set! Here's what to do next:\\n\"));\n console.log(chalk.white(` 1. cd ${relDir}`));\n console.log(chalk.white(' 2. uvx stackwright-pro-raft'));\n console.log(chalk.dim(' (local dev: python -m stackwright_pro.raft.cli_adapter foreman)'));\n\n if (specInfo) {\n console.log(chalk.cyan(`\\n📡 Your API spec was copied to specs/${specInfo.specFilename}`));\n console.log(chalk.dim(' The prebuild will generate types & client on first `pnpm dev`.'));\n }\n\n if (options.mock) {\n console.log(chalk.cyan('\\n🎭 Run with mock server:'));\n console.log(chalk.white(' pnpm dev:mock # Starts Prism + Next.js'));\n }\n\n if (!options.skipOtters) {\n console.log(chalk.cyan.bold('\\n🦦 The otter raft is ready to build your site!'));\n console.log(chalk.dim(' Run the command above to get started.\\n'));\n }\n}\n\n// ---------------------------------------------------------------------------\n// CLI definition\n// ---------------------------------------------------------------------------\n\nasync function main(): Promise<void> {\n const program = new Command();\n\n program\n .name('launch-stackwright-pro')\n .description('🚢 Launch a new Stackwright Pro project with auth, OpenAPI, and the otter raft')\n .version(version)\n .argument('[directory]', 'Project directory', '.')\n .option('--name <name>', 'Project name (used in package.json)')\n .option('--title <title>', 'Site title shown in the app bar and browser tab')\n .option('--theme <themeId>', 'Theme ID (e.g., corporate, creative, minimal)')\n .option('--force', 'Launch even if the target directory is not empty')\n .option('--skip-otters', 'Skip copying otter raft configs')\n .option('-y, --yes', 'Skip all prompts, use defaults')\n .option('--mock', 'Configure Prism mock server for API development (runs on port 4010)')\n .option(\n '--spec <path>',\n 'Path to an OpenAPI spec (YAML or JSON) — copies into project and wires up integration'\n )\n .option(\n '--spec-name <name>',\n 'Name for the API integration (default: derived from spec filename)'\n )\n .option(\n '--skip-install',\n 'Skip automatic pnpm install (useful in CI or when using a different package manager)'\n )\n .action(async (directory: string, options: LaunchProOptions) => {\n const homeDir = os.homedir();\n const targetDir = path.resolve(directory);\n\n // Warn (don't block) when scaffolding outside home dir — unusual for a user-facing CLI\n if (!targetDir.startsWith(homeDir + path.sep) && targetDir !== homeDir) {\n console.warn(chalk.yellow(`⚠️ Scaffolding outside your home directory: ${targetDir}`));\n }\n\n await launch(targetDir, options);\n });\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((err: unknown) => {\n console.error(chalk.red('\\n❌ Launch failed:'), err);\n process.exit(1);\n});\n"],"mappings":"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","names":["exports","module","path","fs","yaml","chalk","os"]}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@stackwright-pro/launch-stackwright-pro",
3
+ "version": "0.0.0-beta-20260417191149",
4
+ "description": "Launch a new Stackwright Pro project with OpenAPI integration, auth, and the otter raft",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/Per-Aspera-LLC/stackwright-pro"
12
+ },
13
+ "keywords": [
14
+ "stackwright",
15
+ "stackwright-pro",
16
+ "scaffolding",
17
+ "openapi",
18
+ "government",
19
+ "hackathon"
20
+ ],
21
+ "files": [
22
+ "dist",
23
+ "templates"
24
+ ],
25
+ "bin": {
26
+ "launch-stackwright-pro": "dist/index.js"
27
+ },
28
+ "dependencies": {
29
+ "@stackwright-pro/scaffold-hooks": "latest",
30
+ "@stackwright/cli": "^0.7.1-alpha.0",
31
+ "@stackwright/scaffold-core": "^0.1.0",
32
+ "chalk": "^5.6.2",
33
+ "commander": "^14.0.3",
34
+ "fs-extra": "^11.3",
35
+ "js-yaml": "^4.1.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/fs-extra": "^11.0",
39
+ "@types/js-yaml": "^4.0.9",
40
+ "@types/node": "^24.0.0",
41
+ "typescript": "^5.0",
42
+ "tsup": "^8.5",
43
+ "vitest": "^4.0.18"
44
+ },
45
+ "scripts": {
46
+ "build": "tsup",
47
+ "dev": "tsup --watch",
48
+ "test": "vitest run",
49
+ "test:coverage": "vitest run --coverage"
50
+ }
51
+ }
@@ -0,0 +1,35 @@
1
+ import type { AppProps } from 'next/app';
2
+ import { registerDefaultIcons } from '@stackwright/icons';
3
+ import { registerNextJSComponents } from '@stackwright/nextjs';
4
+ import { registerShadcnComponents } from '@stackwright/ui-shadcn';
5
+ import { registerDisplayComponents } from '@stackwright-pro/display-components';
6
+ import { registerPulseComponents } from '@stackwright-pro/pulse';
7
+ import { AuthProvider } from '@stackwright-pro/auth';
8
+ import '@stackwright/ui-shadcn/styles.css';
9
+ import authConfig from '../src/auth-config.json'; // Generated at build time by prebuild.js
10
+
11
+ // Register Stackwright components
12
+ registerNextJSComponents();
13
+ registerDefaultIcons();
14
+ registerShadcnComponents();
15
+ registerDisplayComponents();
16
+ registerPulseComponents();
17
+
18
+ export default function App({ Component, pageProps }: AppProps) {
19
+ const user = pageProps.user || null;
20
+ const session = pageProps.session || null;
21
+
22
+ return (
23
+ <AuthProvider
24
+ user={user}
25
+ session={session}
26
+ rbacConfig={{
27
+ roles: authConfig.roles || [],
28
+ protected_routes: authConfig.protected_routes || [],
29
+ public_routes: authConfig.public_routes || [],
30
+ }}
31
+ >
32
+ <Component {...pageProps} />
33
+ </AuthProvider>
34
+ );
35
+ }
@@ -0,0 +1,11 @@
1
+ content:
2
+ content_items:
3
+ - type: main
4
+ label: hero-section
5
+ heading:
6
+ text: Petstore API
7
+ textSize: h1
8
+ textColor: secondary
9
+ textBlocks:
10
+ - text: Welcome to your new Stackwright Pro project. Edit pages/content.yml to update this page.
11
+ textSize: h6
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Mock authentication for development mode.
3
+ *
4
+ * Use the MOCK_USER env var to select a role:
5
+ * MOCK_USER=admin pnpm dev
6
+ * MOCK_USER=analyst pnpm dev
7
+ * MOCK_USER=viewer pnpm dev
8
+ *
9
+ * Or use the convenience scripts: pnpm dev:admin, pnpm dev:analyst, pnpm dev:viewer
10
+ */
11
+
12
+ export const MOCK_USERS = {
13
+ admin: {
14
+ id: 'mock-admin-001',
15
+ name: 'Admin User',
16
+ email: 'admin@usmc.mil',
17
+ roles: ['ADMIN'],
18
+ edipi: '1234567890',
19
+ },
20
+ analyst: {
21
+ id: 'mock-analyst-001',
22
+ name: 'Analyst User',
23
+ email: 'analyst@usmc.mil',
24
+ roles: ['ANALYST'],
25
+ edipi: '0987654321',
26
+ },
27
+ viewer: {
28
+ id: 'mock-viewer-001',
29
+ name: 'Viewer User',
30
+ email: 'viewer@usmc.mil',
31
+ roles: ['VIEWER'],
32
+ edipi: '1122334455',
33
+ },
34
+ } as const;
35
+
36
+ export type MockRole = keyof typeof MOCK_USERS;
37
+
38
+ export function getMockUser(role?: string) {
39
+ const key = role || process.env.MOCK_USER;
40
+ if (!key) return null;
41
+ return MOCK_USERS[key as MockRole] ?? null;
42
+ }
@@ -0,0 +1,18 @@
1
+ const { createStackwrightNextConfig } = require('@stackwright/nextjs');
2
+
3
+ module.exports = createStackwrightNextConfig({
4
+ transpilePackages: [
5
+ '@stackwright/core',
6
+ '@stackwright/nextjs',
7
+ '@stackwright/themes',
8
+ '@stackwright/types',
9
+ '@stackwright/icons',
10
+ '@stackwright/collections',
11
+ '@stackwright/ui-shadcn',
12
+ '@stackwright-pro/openapi',
13
+ '@stackwright-pro/auth',
14
+ '@stackwright-pro/auth-nextjs',
15
+ '@stackwright-pro/display-components',
16
+ ],
17
+ // YAML is parsed server-side at build time - no loader needed in browser
18
+ });
@@ -0,0 +1,60 @@
1
+ openapi: 3.0.0
2
+ info:
3
+ title: Petstore API (Sample)
4
+ description: A sample API for testing mock server functionality
5
+ version: 1.0.0
6
+ servers:
7
+ - url: http://localhost:4010
8
+ description: Mock server
9
+ paths:
10
+ /pets:
11
+ get:
12
+ summary: List all pets
13
+ operationId: listPets
14
+ tags:
15
+ - pets
16
+ responses:
17
+ '200':
18
+ description: A list of pets
19
+ content:
20
+ application/json:
21
+ schema:
22
+ type: array
23
+ items:
24
+ $ref: '#/components/schemas/Pet'
25
+ /pets/{id}:
26
+ get:
27
+ summary: Get a pet by ID
28
+ operationId: getPetById
29
+ tags:
30
+ - pets
31
+ parameters:
32
+ - name: id
33
+ in: path
34
+ required: true
35
+ schema:
36
+ type: integer
37
+ responses:
38
+ '200':
39
+ description: A single pet
40
+ content:
41
+ application/json:
42
+ schema:
43
+ $ref: '#/components/schemas/Pet'
44
+ '404':
45
+ description: Pet not found
46
+ components:
47
+ schemas:
48
+ Pet:
49
+ type: object
50
+ required:
51
+ - id
52
+ - name
53
+ properties:
54
+ id:
55
+ type: integer
56
+ format: int64
57
+ name:
58
+ type: string
59
+ tag:
60
+ type: string
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Stackwright Pro prebuild script
3
+ *
4
+ * Runs stackwright-prebuild to generate content JSON files from YAML,
5
+ * then runs the OpenAPI plugin to generate API client code.
6
+ * Also extracts auth config and generates auth-config.json for client-side use.
7
+ */
8
+
9
+ const { spawn } = require('child_process');
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const yaml = require('js-yaml');
13
+ const { createOpenAPIPlugin } = require('@stackwright-pro/openapi');
14
+
15
+ /**
16
+ * Run stackwright-prebuild to generate _root.json, _site.json, etc.
17
+ */
18
+ async function runStackwrightPrebuild(projectRoot) {
19
+ return new Promise((resolve, reject) => {
20
+ // Use pnpm exec to ensure we use the correct package manager
21
+ const proc = spawn(process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm', ['exec', 'stackwright-prebuild'], {
22
+ cwd: projectRoot,
23
+ stdio: 'inherit',
24
+ shell: true,
25
+ });
26
+ proc.on('close', (code) => {
27
+ if (code === 0) {
28
+ resolve();
29
+ } else {
30
+ reject(new Error(`stackwright-prebuild exited with code ${code}`));
31
+ }
32
+ });
33
+ proc.on('error', reject);
34
+ });
35
+ }
36
+
37
+ /**
38
+ * Parse full stackwright.yml using js-yaml for complete config parsing
39
+ */
40
+ function parseStackwrightYml(content) {
41
+ return yaml.load(content);
42
+ }
43
+
44
+ async function main() {
45
+ try {
46
+ const projectRoot = path.resolve(__dirname, '..');
47
+
48
+ const configPath = path.join(projectRoot, 'stackwright.yml');
49
+ if (!fs.existsSync(configPath)) {
50
+ console.log('No stackwright.yml found — skipping prebuild.');
51
+ return;
52
+ }
53
+
54
+ // Run stackwright-prebuild first to generate content JSON files
55
+ console.log('Generating content files...');
56
+ await runStackwrightPrebuild(projectRoot);
57
+
58
+ const configContent = fs.readFileSync(configPath, 'utf-8');
59
+ const fullConfig = parseStackwrightYml(configContent);
60
+
61
+ // Extract auth config and write as JSON for client-side use
62
+ if (fullConfig.auth) {
63
+ const authConfig = fullConfig.auth;
64
+ const srcDir = path.join(projectRoot, 'src');
65
+
66
+ if (!fs.existsSync(srcDir)) {
67
+ fs.mkdirSync(srcDir, { recursive: true });
68
+ }
69
+
70
+ fs.writeFileSync(
71
+ path.join(srcDir, 'auth-config.json'),
72
+ JSON.stringify(authConfig, null, 2)
73
+ );
74
+ console.log('✓ Generated src/auth-config.json');
75
+ }
76
+
77
+ // Extract integrations for OpenAPI plugin
78
+ const siteConfig = { integrations: fullConfig.integrations || [] };
79
+
80
+ if (!siteConfig.integrations.length) {
81
+ console.log('No integrations in stackwright.yml — skipping OpenAPI plugin.');
82
+ console.log('✓ Prebuild complete!');
83
+ return;
84
+ }
85
+
86
+ console.log(`Found ${siteConfig.integrations.length} integration(s). Running OpenAPI plugin...`);
87
+
88
+ const plugin = createOpenAPIPlugin();
89
+
90
+ if (plugin.beforeBuild) {
91
+ await plugin.beforeBuild({
92
+ siteConfig,
93
+ projectRoot,
94
+ });
95
+ }
96
+
97
+ console.log('✓ Prebuild complete!');
98
+ } catch (error) {
99
+ console.error('Prebuild failed:', error.message);
100
+ console.error(error.stack);
101
+ process.exit(1);
102
+ }
103
+ }
104
+
105
+ main();
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "display": "Next.js",
4
+ "compilerOptions": {
5
+ "target": "ES2022",
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "jsx": "preserve",
8
+ "module": "ESNext",
9
+ "moduleResolution": "bundler",
10
+ "resolveJsonModule": true,
11
+ "allowJs": true,
12
+ "checkJs": false,
13
+ "strict": true,
14
+ "noEmit": true,
15
+ "esModuleInterop": true,
16
+ "skipLibCheck": true,
17
+ "isolatedModules": true,
18
+ "jsxImportSource": "react",
19
+ "paths": {
20
+ "@/*": ["./*"]
21
+ }
22
+ },
23
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"],
24
+ "exclude": ["node_modules", "dist", "build", ".next"]
25
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * TypeScript declarations for YAML modules
3
+ * Used when importing YAML files server-side (e.g., prebuild.js)
4
+ *
5
+ * Note: We do NOT import YAML in browser code - auth-config.json is generated at build time
6
+ */
7
+
8
+ declare module '*.yaml' {
9
+ const value: any;
10
+ export default value;
11
+ export const load: (content: string) => any;
12
+ }
13
+
14
+ declare module '*.yml' {
15
+ const value: any;
16
+ export default value;
17
+ export const load: (content: string) => any;
18
+ }