@nexus_js/cli 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nexus Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # Nexus CLI (`nexus`, `create-nexus`)
2
+
3
+ Command-line tools for [Nexus](https://nexusjs.dev) — the full-stack web framework with islands architecture, Svelte 5 runes, and server actions.
4
+
5
+ - **Documentation:** [https://nexusjs.dev](https://nexusjs.dev)
6
+ - **Repository:** [github.com/bierfor/nexus](https://github.com/bierfor/nexus)
7
+ - **Issues:** [github.com/bierfor/nexus/issues](https://github.com/bierfor/nexus/issues)
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pnpm add -D @nexus_js/cli
13
+ # or
14
+ npm install -D @nexus_js/cli
15
+ ```
16
+
17
+ Global install (optional):
18
+
19
+ ```bash
20
+ npm install -g @nexus_js/cli
21
+ ```
22
+
23
+ ## Commands
24
+
25
+ | Command | Description |
26
+ |--------|-------------|
27
+ | `nexus dev` | Start the development server with HMR |
28
+ | `nexus build` | Production build |
29
+ | `nexus start` | Run the production server |
30
+ | `nexus studio` | Open Nexus Studio (dev dashboard) |
31
+ | `nexus routes` | Print the route manifest |
32
+ | `nexus check` | TypeScript check |
33
+ | `create-nexus <name>` | Scaffold a new project |
34
+
35
+ ## New project
36
+
37
+ ```bash
38
+ npm exec --package=@nexus_js/cli@latest -- create-nexus my-app
39
+ ```
40
+
41
+ Or install globally and run the `create-nexus` binary:
42
+
43
+ ```bash
44
+ npm install -g @nexus_js/cli
45
+ create-nexus my-app
46
+ ```
47
+
48
+ See the [full documentation](https://nexusjs.dev) for configuration, routing, `.nx` components, and deployment.
49
+
50
+ ## License
51
+
52
+ MIT © Nexus contributors
package/dist/add.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Nexus Marketplace — `nexus add <block>`
3
+ *
4
+ * Installs pre-built "Nexus Blocks": components, auth systems, DB schemas,
5
+ * and more — directly scaffolded into your project.
6
+ *
7
+ * Registry format (fetched from GitHub or local for offline):
8
+ * blocks.json → { id, name, description, files: [{ path, content }] }
9
+ *
10
+ * Usage:
11
+ * nexus add auth → Full auth system (middleware + routes + actions)
12
+ * nexus add pokecard → Pre-styled island with Runes configured
13
+ * nexus add analytics → Privacy-first page-view tracker
14
+ * nexus add rate-limit → Edge rate limiter middleware
15
+ * nexus add → Interactive picker (lists available blocks)
16
+ */
17
+ export interface NexusBlock {
18
+ id: string;
19
+ name: string;
20
+ category: 'auth' | 'ui' | 'data' | 'middleware' | 'analytics';
21
+ description: string;
22
+ tags: string[];
23
+ files: BlockFile[];
24
+ postInstall?: string;
25
+ }
26
+ interface BlockFile {
27
+ path: string;
28
+ content: string;
29
+ }
30
+ /** Entry point called by packages/cli/src/bin.ts */
31
+ export declare function runAdd(opts: {
32
+ blockId?: string;
33
+ root: string;
34
+ }): Promise<void>;
35
+ export {};
36
+ //# sourceMappingURL=add.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../src/add.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAcH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAW,MAAM,CAAC;IACpB,IAAI,EAAS,MAAM,CAAC;IACpB,QAAQ,EAAK,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,YAAY,GAAG,WAAW,CAAC;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAS,MAAM,EAAE,CAAC;IACtB,KAAK,EAAQ,SAAS,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,SAAS;IACjB,IAAI,EAAK,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAkUD,oDAAoD;AACpD,wBAAsB,MAAM,CAAC,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAMpF"}
package/dist/add.js ADDED
@@ -0,0 +1,342 @@
1
+ /**
2
+ * Nexus Marketplace — `nexus add <block>`
3
+ *
4
+ * Installs pre-built "Nexus Blocks": components, auth systems, DB schemas,
5
+ * and more — directly scaffolded into your project.
6
+ *
7
+ * Registry format (fetched from GitHub or local for offline):
8
+ * blocks.json → { id, name, description, files: [{ path, content }] }
9
+ *
10
+ * Usage:
11
+ * nexus add auth → Full auth system (middleware + routes + actions)
12
+ * nexus add pokecard → Pre-styled island with Runes configured
13
+ * nexus add analytics → Privacy-first page-view tracker
14
+ * nexus add rate-limit → Edge rate limiter middleware
15
+ * nexus add → Interactive picker (lists available blocks)
16
+ */
17
+ import { writeFile, mkdir } from 'node:fs/promises';
18
+ import { join, dirname } from 'node:path';
19
+ import { createInterface } from 'node:readline';
20
+ const c = {
21
+ reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
22
+ red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m',
23
+ mag: '\x1b[35m', cyan: '\x1b[36m', gray: '\x1b[90m',
24
+ };
25
+ // ── Built-in blocks ───────────────────────────────────────────────────────────
26
+ const BLOCKS = [
27
+ {
28
+ id: 'auth',
29
+ name: 'Nexus Auth',
30
+ category: 'auth',
31
+ description: 'Full auth system: login/register routes + session middleware + Server Actions',
32
+ tags: ['authentication', 'sessions', 'middleware'],
33
+ postInstall: 'Set AUTH_SECRET in your .env file and run nexus build.',
34
+ files: [
35
+ {
36
+ path: 'src/lib/auth.ts',
37
+ content: `/**
38
+ * Nexus Auth — Server-side session helpers.
39
+ * Generated by: nexus add auth
40
+ */
41
+ import type { NexusContext } from '@nexus_js/server';
42
+
43
+ export interface Session {
44
+ userId: string;
45
+ email: string;
46
+ role: 'user' | 'admin';
47
+ expiresAt: number;
48
+ }
49
+
50
+ export function getSession(ctx: NexusContext): Session | null {
51
+ const cookie = ctx.request.headers.get('cookie');
52
+ if (!cookie) return null;
53
+ const match = /nx-session=([^;]+)/.exec(cookie);
54
+ if (!match) return null;
55
+ try {
56
+ return JSON.parse(atob(match[1])) as Session;
57
+ } catch { return null; }
58
+ }
59
+
60
+ export function requireSession(ctx: NexusContext): Session {
61
+ const session = getSession(ctx);
62
+ if (!session || session.expiresAt < Date.now()) {
63
+ ctx.redirect('/login');
64
+ throw new Error('Unreachable'); // redirect throws
65
+ }
66
+ return session;
67
+ }
68
+ `,
69
+ },
70
+ {
71
+ path: 'src/routes/login/+page.nx',
72
+ content: `---
73
+ import { createAction } from '@nexus_js/server';
74
+ import { defineHead } from '@nexus_js/head';
75
+
76
+ defineHead({ title: 'Login — Nexus Auth' });
77
+
78
+ const loginAction = createAction(async (formData: FormData, ctx) => {
79
+ const email = formData.get('email')?.toString() ?? '';
80
+ const password = formData.get('password')?.toString() ?? '';
81
+
82
+ // TODO: validate against your DB
83
+ if (email && password) {
84
+ ctx.setCookie('nx-session', btoa(JSON.stringify({
85
+ userId: '1', email, role: 'user', expiresAt: Date.now() + 86400_000
86
+ })), { httpOnly: true, sameSite: 'lax' });
87
+ ctx.redirect('/');
88
+ }
89
+ }, { idempotent: false, race: 'reject' });
90
+ ---
91
+
92
+ <main class="auth-page">
93
+ <form method="POST" class="auth-form">
94
+ <h1>Sign in</h1>
95
+ <input type="email" name="email" placeholder="Email" required />
96
+ <input type="password" name="password" placeholder="Password" required />
97
+ <button type="submit">Sign in</button>
98
+ </form>
99
+ </main>
100
+
101
+ <style>
102
+ .auth-page { display:flex; align-items:center; justify-content:center; min-height:80vh }
103
+ .auth-form { display:flex; flex-direction:column; gap:12px; width:320px }
104
+ .auth-form h1 { font-size:24px; font-weight:700; margin-bottom:8px }
105
+ .auth-form input { padding:10px 14px; border:1px solid var(--border,#e5e7eb); border-radius:8px }
106
+ .auth-form button { padding:10px; background:#7c3aed; color:#fff; border:none; border-radius:8px; font-weight:600; cursor:pointer }
107
+ </style>
108
+ `,
109
+ },
110
+ {
111
+ path: 'src/middleware/auth.ts',
112
+ content: `/**
113
+ * Nexus Auth Middleware — protects routes requiring a session.
114
+ * Generated by: nexus add auth
115
+ */
116
+ import { defineMiddleware } from '@nexus_js/middleware';
117
+ import { getSession } from '../lib/auth.js';
118
+
119
+ export const authMiddleware = defineMiddleware(async (ctx, next) => {
120
+ const session = getSession(ctx);
121
+ const isProtected = ctx.request.url.includes('/dashboard') ||
122
+ ctx.request.url.includes('/profile');
123
+
124
+ if (isProtected && !session) {
125
+ return ctx.redirect('/login');
126
+ }
127
+ return next();
128
+ });
129
+ `,
130
+ },
131
+ ],
132
+ },
133
+ {
134
+ id: 'analytics',
135
+ name: 'Nexus Analytics',
136
+ category: 'analytics',
137
+ description: 'Privacy-first page-view counter using Server Actions + Shield Cache.',
138
+ tags: ['analytics', 'privacy', 'pageviews'],
139
+ postInstall: 'Add <Analytics /> to your root layout.',
140
+ files: [
141
+ {
142
+ path: 'src/components/Analytics.nx',
143
+ content: `---
144
+ // Privacy-first analytics — no cookies, no fingerprinting.
145
+ // Generated by: nexus add analytics
146
+ import { cache } from '@nexus_js/runtime';
147
+ const key = 'nx-' + crypto.randomUUID().slice(0, 8); // ephemeral session id
148
+ ---
149
+
150
+ <script client:idle>
151
+ // Fires a page-view beacon on mount (client:idle = after page paint)
152
+ const path = window.location.pathname;
153
+ navigator.sendBeacon?.('/_nexus/action/track-view', JSON.stringify({ path }));
154
+ </script>
155
+ `,
156
+ },
157
+ {
158
+ path: 'src/routes/api/analytics/+page.nx',
159
+ content: `---
160
+ import { createAction, registerAction } from '@nexus_js/server';
161
+
162
+ // In-memory counter (replace with DB in production)
163
+ const views = new Map<string, number>();
164
+
165
+ registerAction('track-view', async (input: { path: string }) => {
166
+ views.set(input.path, (views.get(input.path) ?? 0) + 1);
167
+ }, { idempotent: false });
168
+
169
+ const data = Object.fromEntries(views);
170
+ ---
171
+ <pre>{JSON.stringify(data, null, 2)}</pre>
172
+ `,
173
+ },
174
+ ],
175
+ },
176
+ {
177
+ id: 'pokecard',
178
+ name: 'PokéCard Island',
179
+ category: 'ui',
180
+ description: 'Pre-styled Pokémon card island with shiny toggle Rune. Demo component.',
181
+ tags: ['ui', 'island', 'demo', 'runes'],
182
+ files: [
183
+ {
184
+ path: 'src/components/PokéCard.nx',
185
+ content: `---
186
+ export interface Props {
187
+ id: number;
188
+ name: string;
189
+ sprite: string;
190
+ types: string[];
191
+ color: string;
192
+ }
193
+ const { id, name, sprite, types, color } = $props<Props>();
194
+ ---
195
+
196
+ <script>
197
+ let shiny = $state(false);
198
+ </script>
199
+
200
+ <a href="/pokemon/{id}" class="pokecard" style="--color: {color}">
201
+ <div class="card-bg">
202
+ <img src="{sprite}" alt="{name}" width="120" height="120" />
203
+ <button onclick="{() => shiny = !shiny}" class="shiny-btn">
204
+ {shiny ? '✨ Shiny' : '✨ Normal'}
205
+ </button>
206
+ </div>
207
+ <div class="card-body">
208
+ <span class="dex-num">#{String(id).padStart(3, '0')}</span>
209
+ <h3>{name}</h3>
210
+ <div class="types">
211
+ {#each types as type}
212
+ <span class="type-badge type-{type}">{type}</span>
213
+ {/each}
214
+ </div>
215
+ </div>
216
+ </a>
217
+
218
+ <style>
219
+ .pokecard { display:block; border-radius:16px; overflow:hidden; text-decoration:none; color:inherit; background:var(--surface, #13131f); border:1px solid var(--border, #1e1e30); transition:transform .2s }
220
+ .pokecard:hover { transform:translateY(-4px) }
221
+ .card-bg { background:linear-gradient(135deg, color-mix(in srgb, var(--color) 20%, transparent), transparent); padding:20px; display:flex; flex-direction:column; align-items:center; gap:8px }
222
+ .card-body { padding:12px 16px 16px }
223
+ .dex-num { font-size:11px; color:#64748b; font-family:monospace }
224
+ h3 { font-size:16px; font-weight:700; text-transform:capitalize; margin:4px 0 8px }
225
+ .types { display:flex; gap:4px; flex-wrap:wrap }
226
+ .type-badge { padding:2px 10px; border-radius:999px; font-size:11px; font-weight:700; background:#374151; color:#fff }
227
+ .shiny-btn { padding:4px 12px; border-radius:999px; border:1px solid #374151; background:transparent; color:#94a3b8; font-size:11px; cursor:pointer }
228
+ </style>
229
+ `,
230
+ },
231
+ ],
232
+ },
233
+ {
234
+ id: 'rate-limit',
235
+ name: 'Edge Rate Limiter',
236
+ category: 'middleware',
237
+ description: 'IP-based rate limiting middleware for API routes and Server Actions.',
238
+ tags: ['security', 'middleware', 'edge'],
239
+ files: [
240
+ {
241
+ path: 'src/middleware/rate-limit.ts',
242
+ content: `/**
243
+ * Nexus Edge Rate Limiter — in-memory sliding window.
244
+ * Generated by: nexus add rate-limit
245
+ *
246
+ * For production, replace the Map with an Upstash/Redis adapter.
247
+ */
248
+ import { defineMiddleware } from '@nexus_js/middleware';
249
+
250
+ const windows = new Map<string, { count: number; reset: number }>();
251
+
252
+ export function createRateLimit(opts: {
253
+ requests: number;
254
+ windowMs: number;
255
+ message?: string;
256
+ }) {
257
+ return defineMiddleware(async (ctx, next) => {
258
+ const ip = ctx.request.headers.get('x-forwarded-for')?.split(',')[0].trim() ?? 'unknown';
259
+ const now = Date.now();
260
+ const win = windows.get(ip);
261
+
262
+ if (!win || now > win.reset) {
263
+ windows.set(ip, { count: 1, reset: now + opts.windowMs });
264
+ return next();
265
+ }
266
+
267
+ if (win.count >= opts.requests) {
268
+ return new Response(
269
+ JSON.stringify({ error: opts.message ?? 'Too Many Requests' }),
270
+ { status: 429, headers: { 'content-type': 'application/json', 'retry-after': String(Math.ceil((win.reset - now) / 1000)) } }
271
+ );
272
+ }
273
+
274
+ win.count++;
275
+ return next();
276
+ });
277
+ }
278
+
279
+ // Default: 100 req / 60s per IP
280
+ export const rateLimitMiddleware = createRateLimit({ requests: 100, windowMs: 60_000 });
281
+ `,
282
+ },
283
+ ],
284
+ },
285
+ ];
286
+ // ── CLI logic ─────────────────────────────────────────────────────────────────
287
+ function listBlocks() {
288
+ console.log(`\n ${c.mag}${c.bold}◆ Nexus Marketplace${c.reset}\n`);
289
+ const byCategory = new Map();
290
+ for (const block of BLOCKS) {
291
+ if (!byCategory.has(block.category))
292
+ byCategory.set(block.category, []);
293
+ byCategory.get(block.category).push(block);
294
+ }
295
+ for (const [cat, blocks] of byCategory) {
296
+ console.log(` ${c.cyan}${cat}${c.reset}`);
297
+ for (const b of blocks) {
298
+ console.log(` ${c.bold}nexus add ${b.id}${c.reset} ${c.dim}${b.description}${c.reset}`);
299
+ }
300
+ console.log('');
301
+ }
302
+ console.log(` ${c.dim}Run ${c.reset}${c.bold}nexus add <id>${c.reset}${c.dim} to install a block.${c.reset}\n`);
303
+ }
304
+ async function installBlock(blockId, root) {
305
+ const block = BLOCKS.find((b) => b.id === blockId);
306
+ if (!block) {
307
+ console.error(`\n ${c.red}✖${c.reset} Block "${blockId}" not found.\n`);
308
+ listBlocks();
309
+ process.exit(1);
310
+ }
311
+ console.log(`\n ${c.mag}${c.bold}◆ Nexus Marketplace${c.reset} Installing ${c.bold}${block.name}${c.reset}...\n`);
312
+ for (const file of block.files) {
313
+ const fullPath = join(root, file.path);
314
+ await mkdir(dirname(fullPath), { recursive: true });
315
+ await writeFile(fullPath, file.content, 'utf-8');
316
+ console.log(` ${c.green}✔${c.reset} Created ${c.cyan}${file.path}${c.reset}`);
317
+ }
318
+ if (block.postInstall) {
319
+ console.log(`\n ${c.yellow}→${c.reset} ${block.postInstall}`);
320
+ }
321
+ console.log(`\n ${c.green}◆${c.reset} ${block.name} installed successfully.\n`);
322
+ }
323
+ /** Interactive block picker when no ID is given */
324
+ async function pickBlock(root) {
325
+ listBlocks();
326
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
327
+ const answer = await new Promise((resolve) => {
328
+ rl.question(` Which block? ${c.dim}(id)${c.reset} `, resolve);
329
+ });
330
+ rl.close();
331
+ await installBlock(answer.trim(), root);
332
+ }
333
+ /** Entry point called by packages/cli/src/bin.ts */
334
+ export async function runAdd(opts) {
335
+ if (!opts.blockId) {
336
+ await pickBlock(opts.root);
337
+ }
338
+ else {
339
+ await installBlock(opts.blockId, opts.root);
340
+ }
341
+ }
342
+ //# sourceMappingURL=add.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.js","sourceRoot":"","sources":["../src/add.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Nexus Bundle Budget Analyzer
3
+ *
4
+ * Generates a detailed visual report of JS payload per route.
5
+ * Runs automatically after `nexus build` or standalone with `nexus analyze`.
6
+ *
7
+ * Terminal output:
8
+ *
9
+ * ◆ Nexus Bundle Analysis
10
+ * ════════════════════════════════════════════════════════
11
+ *
12
+ * Route: /
13
+ * ┌─────────────────────────────┬────────┬────────┬───────┐
14
+ * │ Module │ Raw │ Gzip │ Share │
15
+ * ├─────────────────────────────┼────────┼────────┼───────┤
16
+ * │ @nexus_js/runtime (shared) │ 4.2KB │ 1.8KB │ ███░ │
17
+ * │ islands/Counter.client.js │ 1.1KB │ 0.5KB │ █░░░ │
18
+ * │ islands/SearchBar.client.js │ 2.8KB │ 1.1KB │ ██░░ │
19
+ * ├─────────────────────────────┼────────┼────────┼───────┤
20
+ * │ TOTAL │ 8.1KB │ 3.4KB │ │
21
+ * └─────────────────────────────┴────────┴────────┴───────┘
22
+ * Budget: ✓ Under 10KB limit
23
+ *
24
+ * Route: /blog/[slug] ← OVER BUDGET ⚠
25
+ * ...
26
+ *
27
+ * ─── Summary ─────────────────────────────────────────────
28
+ * Routes analyzed: 12
29
+ * Under budget: 10
30
+ * Over budget: 2 ⚠ /dashboard, /checkout
31
+ * Largest route: /checkout (48KB gzip)
32
+ * Zero-JS routes: 4 (/, /about, /blog, /contact)
33
+ */
34
+ export interface BundleModule {
35
+ name: string;
36
+ path: string;
37
+ rawBytes: number;
38
+ gzipBytes: number;
39
+ kind: 'runtime' | 'island' | 'shared' | 'action';
40
+ }
41
+ export interface RouteBudget {
42
+ route: string;
43
+ modules: BundleModule[];
44
+ totalRaw: number;
45
+ totalGzip: number;
46
+ isZeroJS: boolean;
47
+ overBudget: boolean;
48
+ budgetBytes: number;
49
+ }
50
+ export interface AnalysisReport {
51
+ routes: RouteBudget[];
52
+ runtimeSize: number;
53
+ generatedAt: string;
54
+ totalIslands: number;
55
+ zeroJSRoutes: number;
56
+ overBudgetRoutes: string[];
57
+ }
58
+ export interface AnalyzerOptions {
59
+ root: string;
60
+ /** Gzip budget per route in bytes (default: 50KB) */
61
+ budgetBytes?: number;
62
+ /** Show per-module breakdown (default: true) */
63
+ verbose?: boolean;
64
+ /** Output format */
65
+ format?: 'terminal' | 'json' | 'html';
66
+ /** Write report to file */
67
+ outFile?: string;
68
+ }
69
+ export declare function analyzeBundles(opts: AnalyzerOptions): Promise<AnalysisReport>;
70
+ //# sourceMappingURL=analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAOH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;CAClD;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oBAAoB;IACpB,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;IACtC,2BAA2B;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAmBD,wBAAsB,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CA+BnF"}