create-pyric 0.1.0-alpha.16 → 0.1.0-alpha.18

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  Scaffold a Pyric app. Used by:
4
4
 
5
5
  ```bash
6
- npm create pyric [dir]
6
+ npm create pyric@latest [dir]
7
7
  ```
8
8
 
9
9
  Default template is **web**: a Vite app wired to `@pyric/cli/vite`. Then:
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Scaffold template definition for Next.js (`npm create pyric -- --template nextjs`).
3
+ */
4
+ import type { ScaffoldTemplate } from './templates.js';
5
+ export declare const NEXTJS_TEMPLATE: ScaffoldTemplate;
6
+ //# sourceMappingURL=template-nextjs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-nextjs.d.ts","sourceRoot":"","sources":["../src/template-nextjs.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAkevD,eAAO,MAAM,eAAe,EAAE,gBAwC7B,CAAC"}
@@ -0,0 +1,511 @@
1
+ const NEXTJS_CONFIG_MJS = `import { fileURLToPath } from 'node:url';
2
+ import { withPyric } from '@pyric/cli/next';
3
+
4
+ /** @type {import('next').NextConfig} */
5
+ const nextConfig = {
6
+ reactStrictMode: true,
7
+ outputFileTracingRoot: fileURLToPath(new URL('.', import.meta.url)),
8
+ allowedDevOrigins: [
9
+ 'localhost',
10
+ '127.0.0.1',
11
+ 'localhost:3000',
12
+ '127.0.0.1:3000',
13
+ 'localhost:4000',
14
+ '127.0.0.1:4000',
15
+ 'localhost:4288',
16
+ '127.0.0.1:4288',
17
+ 'localhost:4289',
18
+ '127.0.0.1:4289',
19
+ ],
20
+ };
21
+
22
+ // Under development mode (\`pyric dev -- next dev\`), withPyric maps client-side
23
+ // firebase/* SDK imports to Pyric sandbox adapters via Webpack/Turbopack aliases,
24
+ // externalizes server-side firebase and firebase-admin imports for Node loader
25
+ // hooks (@pyric/cli/register), and proxies /__pyric/* bridge traffic.
26
+ // Under \`next build\` (mode production), withPyric acts as an identity passthrough,
27
+ // compiling canonical Firebase SDKs untouched with zero runtime overhead.
28
+ export default withPyric(nextConfig);
29
+ `;
30
+ const NEXTJS_TSCONFIG_JSON = `{
31
+ "compilerOptions": {
32
+ "target": "ES2022",
33
+ "lib": ["dom", "dom.iterable", "esnext"],
34
+ "allowJs": true,
35
+ "skipLibCheck": true,
36
+ "strict": true,
37
+ "noEmit": true,
38
+ "esModuleInterop": true,
39
+ "module": "esnext",
40
+ "moduleResolution": "bundler",
41
+ "resolveJsonModule": true,
42
+ "isolatedModules": true,
43
+ "jsx": "preserve",
44
+ "incremental": true,
45
+ "plugins": [
46
+ {
47
+ "name": "next"
48
+ }
49
+ ],
50
+ "paths": {
51
+ "@/*": ["./src/*"]
52
+ }
53
+ },
54
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
55
+ "exclude": ["node_modules"]
56
+ }
57
+ `;
58
+ const NEXTJS_ENV_EXAMPLE = `# Your real Firebase application configuration from the Firebase Console.
59
+ # UNUSED in local development under \`pyric dev\` (the Pyric sandbox stands in);
60
+ # USED by \`next build\` for production cloud builds. Next.js exposes environment
61
+ # variables prefixed with \`NEXT_PUBLIC_\` to client-side code in the browser.
62
+ NEXT_PUBLIC_FIREBASE_API_KEY=
63
+ NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=
64
+ NEXT_PUBLIC_FIREBASE_PROJECT_ID=
65
+ NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=
66
+ NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
67
+ NEXT_PUBLIC_FIREBASE_APP_ID=
68
+ `;
69
+ const NEXTJS_GITIGNORE = `# next.js
70
+ /.next/
71
+ /out/
72
+ /build/
73
+
74
+ # production
75
+ /build
76
+ /dist
77
+
78
+ # misc
79
+ .DS_Store
80
+ *.pem
81
+
82
+ # debug
83
+ npm-debug.log*
84
+ yarn-debug.log*
85
+ yarn-error.log*
86
+
87
+ # local env files
88
+ .env*.local
89
+ .env
90
+ .env.pyric
91
+
92
+ # pyric session files
93
+ .pyric/
94
+
95
+ # vercel
96
+ .vercel
97
+
98
+ # typescript
99
+ *.tsbuildinfo
100
+ next-env.d.ts
101
+ `;
102
+ const NEXTJS_FIREBASE_JSON = `{
103
+ "firestore": {
104
+ "rules": "firestore.rules",
105
+ "indexes": "firestore.indexes.json"
106
+ }
107
+ }
108
+ `;
109
+ const NEXTJS_FIRESTORE_INDEXES = `{
110
+ "indexes": [],
111
+ "fieldOverrides": []
112
+ }
113
+ `;
114
+ const NEXTJS_FIRESTORE_RULES = `rules_version = '2';
115
+ service cloud.firestore {
116
+ match /databases/{database}/documents {
117
+ // Owner-based security rules from line 1 — Pyric deploys and hot-reloads
118
+ // this configuration into the local sandbox environment. These deploy as-is
119
+ // to production Firebase instances.
120
+ match /posts/{postId} {
121
+ allow read: if true;
122
+ allow create: if request.auth != null
123
+ && request.resource.data.uid == request.auth.uid;
124
+ allow update, delete: if request.auth != null
125
+ && resource.data.uid == request.auth.uid;
126
+ }
127
+
128
+ // Default deny — require explicit authorization rules per collection.
129
+ match /{document=**} {
130
+ allow read, write: if false;
131
+ }
132
+ }
133
+ }
134
+ `;
135
+ function buildRootLayout(projectName) {
136
+ return `import type { Metadata } from 'next';
137
+ import React from 'react';
138
+
139
+ export const metadata: Metadata = {
140
+ title: '${projectName}',
141
+ description: 'Local Firebase development with Pyric and Next.js',
142
+ };
143
+
144
+ interface LayoutProps {
145
+ children: React.ReactNode;
146
+ }
147
+
148
+ export default function RootLayout({ children }: LayoutProps): React.JSX.Element {
149
+ const containerStyle: React.CSSProperties = {
150
+ font: '16px/1.5 system-ui, sans-serif',
151
+ maxWidth: '640px',
152
+ margin: '3rem auto',
153
+ padding: '0 1rem',
154
+ };
155
+
156
+ return (
157
+ <html lang="en">
158
+ <body style={containerStyle}>
159
+ {children}
160
+ </body>
161
+ </html>
162
+ );
163
+ }
164
+ `;
165
+ }
166
+ function buildStatusApiRoute() {
167
+ return `import { NextResponse } from 'next/server';
168
+ import { getApps, initializeApp } from 'firebase-admin/app';
169
+ import { getFirestore } from 'firebase-admin/firestore';
170
+
171
+ const DEFAULT_PROJECT_ID = 'demo-nextjs-app';
172
+
173
+ function getAdminApp() {
174
+ const activeApps = getApps();
175
+ if (activeApps.length > 0) {
176
+ return activeApps[0];
177
+ }
178
+ return initializeApp({ projectId: DEFAULT_PROJECT_ID });
179
+ }
180
+
181
+ function resolveRuntimeEnvironment(): string {
182
+ if (process.env.PYRIC_SANDBOX !== undefined) {
183
+ return 'pyric-sandbox';
184
+ }
185
+ return 'production';
186
+ }
187
+
188
+ async function fetchPostsSnapshot(db: FirebaseFirestore.Firestore, maxRetries = 6, delayMs = 500): Promise<FirebaseFirestore.QuerySnapshot> {
189
+ let attempts = 0;
190
+ for (;;) {
191
+ try {
192
+ attempts += 1;
193
+ const snapshot = await db.collection('posts').get();
194
+ return snapshot;
195
+ } catch (error) {
196
+ if (attempts >= maxRetries) {
197
+ throw error;
198
+ }
199
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
200
+ }
201
+ }
202
+ }
203
+
204
+ export async function GET(): Promise<NextResponse> {
205
+ const app = getAdminApp();
206
+ const db = getFirestore(app);
207
+
208
+ try {
209
+ const postsSnapshot = await fetchPostsSnapshot(db);
210
+ const documentCount = postsSnapshot.size;
211
+ const runtimeTarget = resolveRuntimeEnvironment();
212
+
213
+ return NextResponse.json({
214
+ status: 'ok',
215
+ environment: runtimeTarget,
216
+ count: documentCount,
217
+ });
218
+ } catch (error) {
219
+ const errCode = (error as { code?: string }).code;
220
+ const errMessage = errCode !== undefined ? errCode : String(error);
221
+ return NextResponse.json(
222
+ { status: 'error', details: errMessage },
223
+ { status: 500 },
224
+ );
225
+ }
226
+ }
227
+ `;
228
+ }
229
+ function buildHomePage(projectName) {
230
+ return `'use client';
231
+
232
+ import React, { useEffect, useState, type FormEvent, type CSSProperties } from 'react';
233
+ import { initializeApp, getApps, getApp, type FirebaseApp } from 'firebase/app';
234
+ import {
235
+ getAuth,
236
+ onAuthStateChanged,
237
+ signInWithPopup,
238
+ signOut,
239
+ GoogleAuthProvider,
240
+ type User,
241
+ } from 'firebase/auth';
242
+ import {
243
+ getFirestore,
244
+ collection,
245
+ onSnapshot,
246
+ addDoc,
247
+ serverTimestamp,
248
+ type DocumentData,
249
+ } from 'firebase/firestore';
250
+
251
+ interface PostRecord {
252
+ id: string;
253
+ title: string;
254
+ uid: string;
255
+ }
256
+
257
+ interface ServerStatusResponse {
258
+ status: string;
259
+ environment?: string;
260
+ count?: number;
261
+ details?: string;
262
+ }
263
+
264
+ const DEFAULT_FIREBASE_CONFIG = {
265
+ apiKey: 'demo-api-key',
266
+ authDomain: 'demo-nextjs-app.firebaseapp.com',
267
+ projectId: 'demo-nextjs-app',
268
+ };
269
+
270
+ function resolveClientFirebaseApp(): FirebaseApp {
271
+ const existingApps = getApps();
272
+ if (existingApps.length > 0) {
273
+ return getApp();
274
+ }
275
+ return initializeApp(DEFAULT_FIREBASE_CONFIG);
276
+ }
277
+
278
+ function resolveUserDisplayLabel(currentUser: User): string {
279
+ if (currentUser.displayName !== null && currentUser.displayName !== '') {
280
+ return \`Signed in as \${currentUser.displayName}\`;
281
+ }
282
+ if (currentUser.email !== null && currentUser.email !== '') {
283
+ return \`Signed in as \${currentUser.email}\`;
284
+ }
285
+ return \`Signed in as \${currentUser.uid}\`;
286
+ }
287
+
288
+ export default function HomePage(): React.JSX.Element {
289
+ const [activeUser, setActiveUser] = useState<User | null>(null);
290
+ const [postsList, setPostsList] = useState<PostRecord[]>([]);
291
+ const [newPostTitle, setNewPostTitle] = useState<string>('');
292
+ const [authStatusText, setAuthStatusText] = useState<string>('Checking authentication state...');
293
+ const [backendApiStatusText, setBackendApiStatusText] = useState<string>('Connecting to Server API...');
294
+
295
+ useEffect(() => {
296
+ const clientApp = resolveClientFirebaseApp();
297
+ const authService = getAuth(clientApp);
298
+ const firestoreDb = getFirestore(clientApp);
299
+
300
+ const unsubscribeAuth = onAuthStateChanged(authService, (userState) => {
301
+ setActiveUser(userState);
302
+ if (userState !== null) {
303
+ const userLabel = resolveUserDisplayLabel(userState);
304
+ setAuthStatusText(userLabel);
305
+ } else {
306
+ setAuthStatusText('Signed out');
307
+ }
308
+ });
309
+
310
+ const postsCollectionRef = collection(firestoreDb, 'posts');
311
+ const unsubscribeSnapshot = onSnapshot(postsCollectionRef, (snapshot) => {
312
+ const currentPosts: PostRecord[] = [];
313
+ for (const documentSnapshot of snapshot.docs) {
314
+ const rawData = documentSnapshot.data() as DocumentData;
315
+ const documentTitle = typeof rawData.title === 'string' ? rawData.title : 'Untitled Post';
316
+ const authorId = typeof rawData.uid === 'string' ? rawData.uid : 'anonymous';
317
+ const postEntry: PostRecord = {
318
+ id: documentSnapshot.id,
319
+ title: documentTitle,
320
+ uid: authorId,
321
+ };
322
+ currentPosts.push(postEntry);
323
+ }
324
+ setPostsList(currentPosts);
325
+
326
+ fetch('/api/status')
327
+ .then(async (response) => {
328
+ const payload = (await response.json().catch(() => ({ status: 'error', details: \`HTTP \${response.status}\` }))) as ServerStatusResponse;
329
+ if (!response.ok) {
330
+ const failureDetail = payload.details !== undefined ? payload.details : \`HTTP \${response.status}\`;
331
+ throw new Error(failureDetail);
332
+ }
333
+ return payload;
334
+ })
335
+ .then((payload) => {
336
+ if (payload.status === 'ok' && payload.environment !== undefined && payload.count !== undefined) {
337
+ setBackendApiStatusText(\`Server-Side Admin API Runtime: \${payload.environment} (\${payload.count} database records)\`);
338
+ } else {
339
+ const failureDetail = payload.details !== undefined ? payload.details : 'Unknown error';
340
+ setBackendApiStatusText(\`Server-Side Admin API reported an error: \${failureDetail}\`);
341
+ }
342
+ })
343
+ .catch((err: unknown) => {
344
+ const failureDetail = err instanceof Error ? err.message : String(err);
345
+ setBackendApiStatusText(\`Server-Side Admin API unavailable (\${failureDetail})\`);
346
+ });
347
+ });
348
+
349
+ return () => {
350
+ unsubscribeAuth();
351
+ unsubscribeSnapshot();
352
+ };
353
+ }, []);
354
+
355
+ const onSignInClicked = async () => {
356
+ const clientApp = resolveClientFirebaseApp();
357
+ const authService = getAuth(clientApp);
358
+ const googleProvider = new GoogleAuthProvider();
359
+ await signInWithPopup(authService, googleProvider);
360
+ };
361
+
362
+ const onSignOutClicked = async () => {
363
+ const clientApp = resolveClientFirebaseApp();
364
+ const authService = getAuth(clientApp);
365
+ await signOut(authService);
366
+ };
367
+
368
+ const onPostFormSubmitted = async (event: FormEvent<HTMLFormElement>) => {
369
+ event.preventDefault();
370
+ const clientApp = resolveClientFirebaseApp();
371
+ const authService = getAuth(clientApp);
372
+ const firestoreDb = getFirestore(clientApp);
373
+ const currentUser = authService.currentUser;
374
+
375
+ const trimmedTitle = newPostTitle.trim();
376
+ if (trimmedTitle === '') {
377
+ return;
378
+ }
379
+
380
+ try {
381
+ const postsCollectionRef = collection(firestoreDb, 'posts');
382
+ const authorId = currentUser !== null ? currentUser.uid : 'anonymous';
383
+ await addDoc(postsCollectionRef, {
384
+ title: trimmedTitle,
385
+ uid: authorId,
386
+ createdAt: serverTimestamp(),
387
+ });
388
+ setNewPostTitle('');
389
+ } catch (writeError) {
390
+ const errorCode = (writeError as { code?: string }).code;
391
+ const displayMessage = errorCode !== undefined ? errorCode : String(writeError);
392
+ if (currentUser !== null) {
393
+ setAuthStatusText(\`Write operation failed: \${displayMessage}\`);
394
+ } else {
395
+ setAuthStatusText('Denied by security rules (signed out) — check the Traffic tab in Pyric Studio.');
396
+ }
397
+ }
398
+ };
399
+
400
+ const buttonStyle: CSSProperties = { padding: '0.4rem 0.9rem', cursor: 'pointer' };
401
+ const inputStyle: CSSProperties = { flex: 1, padding: '0.4rem 0.6rem' };
402
+ const formStyle: CSSProperties = { display: 'flex', gap: '0.5rem', margin: '1rem 0' };
403
+
404
+ return (
405
+ <main>
406
+ <h1>${projectName}</h1>
407
+ <p id="auth-status" style={{ color: '#555', fontWeight: 'bold' }}>
408
+ {authStatusText}
409
+ </p>
410
+ <p id="api-status" style={{ color: '#0066cc', fontSize: '0.9rem', marginBottom: '1rem' }}>
411
+ {backendApiStatusText}
412
+ </p>
413
+
414
+ {activeUser === null ? (
415
+ <button id="sign-in-button" type="button" onClick={onSignInClicked} style={buttonStyle}>
416
+ Sign in with Google
417
+ </button>
418
+ ) : (
419
+ <button id="sign-out-button" type="button" onClick={onSignOutClicked} style={buttonStyle}>
420
+ Sign out
421
+ </button>
422
+ )}
423
+
424
+ <form id="add-post-form" onSubmit={onPostFormSubmitted} style={formStyle}>
425
+ <input
426
+ id="post-title-input"
427
+ type="text"
428
+ value={newPostTitle}
429
+ onChange={(event) => setNewPostTitle(event.target.value)}
430
+ placeholder="Post title"
431
+ required
432
+ style={inputStyle}
433
+ />
434
+ <button id="submit-post-button" type="submit" style={buttonStyle}>
435
+ Add post
436
+ </button>
437
+ </form>
438
+
439
+ <h2>Posts</h2>
440
+ {postsList.length === 0 ? (
441
+ <p style={{ color: '#888', fontStyle: 'italic' }}>No posts yet in database.</p>
442
+ ) : (
443
+ <ul id="posts-list" style={{ paddingLeft: '1.2rem' }}>
444
+ {postsList.map((postItem) => (
445
+ <li key={postItem.id}>
446
+ <strong>{postItem.title}</strong>{' '}
447
+ <span style={{ color: '#777', fontSize: '0.8rem' }}>(by {postItem.uid})</span>
448
+ </li>
449
+ ))}
450
+ </ul>
451
+ )}
452
+ </main>
453
+ );
454
+ }
455
+ `;
456
+ }
457
+ function buildReadme(projectName) {
458
+ return `# ${projectName}
459
+
460
+ A Firebase web application built with Next.js and wrapped with \`@pyric/cli/next\` (\`withPyric\`).
461
+ In development, the application runs entirely on Pyric's local sandbox—requiring
462
+ zero Firebase projects, service account keys, or cloud emulators.
463
+
464
+ - **Develop:** \`npm run dev\` or \`bun run dev\` — executes \`pyric dev -- next dev\`. The \`withPyric\` wrapper substitutes client SDK imports with local sandbox mirrors via Webpack and Turbopack aliases, intercepts server-side API requests via \`@pyric/cli/register\`, and proxies WebSocket bridge connection traffic automatically.
465
+ - **Build for production:** \`npm run build\` or \`bun run build\` — executing \`next build\` in production mode activates identity passthrough in \`withPyric\`, compiling standard Firebase and Firebase Admin SDKs untouched with zero runtime overhead.
466
+ - **Start:** \`npm start\` or \`bun run start\` — serves your built Next.js production server.
467
+ - **Deploy:** \`npx firebase-tools deploy\` (via Firebase Web Frameworks) after production build, or deploy standard built artifacts directly to Vercel and cloud compute platforms.
468
+ `;
469
+ }
470
+ export const NEXTJS_TEMPLATE = {
471
+ scripts: {
472
+ dev: 'pyric dev -- next dev',
473
+ 'dev:direct': 'next dev',
474
+ build: 'next build',
475
+ start: 'next start',
476
+ },
477
+ dependencies: {
478
+ firebase: '^12.12.0',
479
+ 'firebase-admin': '^13.0.0',
480
+ next: '^15.0.0',
481
+ react: '^19.0.0',
482
+ 'react-dom': '^19.0.0',
483
+ },
484
+ devDependencies: {
485
+ '@pyric/cli': '*',
486
+ '@types/node': '^22.0.0',
487
+ '@types/react': '^19.0.0',
488
+ '@types/react-dom': '^19.0.0',
489
+ typescript: '^5.7.0',
490
+ },
491
+ dirs: ['src', 'src/app', 'src/app/api', 'src/app/api/status'],
492
+ files: (name) => [
493
+ { name: 'next.config.mjs', content: NEXTJS_CONFIG_MJS },
494
+ { name: 'tsconfig.json', content: NEXTJS_TSCONFIG_JSON },
495
+ { name: '.env.example', content: NEXTJS_ENV_EXAMPLE },
496
+ { name: '.gitignore', content: NEXTJS_GITIGNORE },
497
+ { name: 'firebase.json', content: NEXTJS_FIREBASE_JSON },
498
+ { name: 'firestore.indexes.json', content: NEXTJS_FIRESTORE_INDEXES },
499
+ { name: 'firestore.rules', content: NEXTJS_FIRESTORE_RULES },
500
+ { name: 'README.md', content: buildReadme(name) },
501
+ { name: 'src/app/layout.tsx', content: buildRootLayout(name) },
502
+ { name: 'src/app/page.tsx', content: buildHomePage(name) },
503
+ { name: 'src/app/api/status/route.ts', content: buildStatusApiRoute() },
504
+ ],
505
+ nextSteps: [
506
+ 'npm install # or: bun install',
507
+ 'npm run dev # Next.js dev server on the Pyric sandbox',
508
+ 'npm run build # production build against real Firebase',
509
+ ],
510
+ };
511
+ //# sourceMappingURL=template-nextjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-nextjs.js","sourceRoot":"","sources":["../src/template-nextjs.ts"],"names":[],"mappings":"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"}
@@ -15,7 +15,7 @@
15
15
  * canonical imports are swapped by the dev command and remain Firebase under
16
16
  * the production command.
17
17
  */
18
- export declare const TEMPLATE_NAMES: readonly ["web", "node", "static", "chat"];
18
+ export declare const TEMPLATE_NAMES: readonly ["web", "node", "static", "chat", "nextjs"];
19
19
  export type TemplateName = (typeof TEMPLATE_NAMES)[number];
20
20
  export declare function isTemplateName(value: string): value is TemplateName;
21
21
  export interface ScaffoldTemplate {
@@ -1 +1 @@
1
- {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,eAAO,MAAM,cAAc,4CAA6C,CAAC;AACzE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,YAAY,CAEnE;AAED,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC;6EACyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,8EAA8E;IAC9E,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,qDAAqD;IACrD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9D,4DAA4D;IAC5D,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAMD,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAkBD,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG;IAC9E,WAAW,EAAE;QACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACrC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACxC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACpC,CAAC;IACF,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB,CAkEA;AAqiBD,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,YAAY,EAAE,gBAAgB,CAmG5D,CAAC"}
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,eAAO,MAAM,cAAc,sDAAuD,CAAC;AACnF,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,YAAY,CAEnE;AAED,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC;6EACyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,8EAA8E;IAC9E,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,qDAAqD;IACrD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9D,4DAA4D;IAC5D,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAMD,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAkBD,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG;IAC9E,WAAW,EAAE;QACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACrC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACxC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACpC,CAAC;IACF,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB,CAkEA;AAimBD,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,YAAY,EAAE,gBAAgB,CAoG5D,CAAC"}