create-pyric 0.1.0-alpha.9
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 +216 -0
- package/README.md +20 -0
- package/dist/bin.d.ts +9 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +42 -0
- package/dist/bin.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/parse-args.d.ts +11 -0
- package/dist/parse-args.d.ts.map +1 -0
- package/dist/parse-args.js +58 -0
- package/dist/parse-args.js.map +1 -0
- package/dist/scaffold.d.ts +84 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +244 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/templates.d.ts +37 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +611 -0
- package/dist/templates.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scaffold templates for `pyric init` (engine in `./init.js`).
|
|
3
|
+
*
|
|
4
|
+
* `web` (the default) scaffolds a **Vite app** wired to the `@pyric/cli/vite`
|
|
5
|
+
* plugin: `vite dev` runs the app's CANONICAL `firebase/*` imports against the
|
|
6
|
+
* in-process sandbox; `vite build` ships the real `firebase` package. One
|
|
7
|
+
* toolchain, no graduation cliff — the sandbox↔Firebase swap is environmental
|
|
8
|
+
* (dev vs build), never a code edit (the design rationale section 9).
|
|
9
|
+
*
|
|
10
|
+
* `static` is the serve-era scaffold (no bundler): a static app `pyric dev`
|
|
11
|
+
* runs against the in-page sandbox via a runtime import map. For pre-built /
|
|
12
|
+
* retrofit apps, or anyone who wants zero build step.
|
|
13
|
+
*
|
|
14
|
+
* `node` is the script-style scaffold (backend fixtures, agent loops). Its
|
|
15
|
+
* canonical imports are swapped by the dev command and remain Firebase under
|
|
16
|
+
* the production command.
|
|
17
|
+
*/
|
|
18
|
+
// ─── web template ─────────────────────────────────────────────────────
|
|
19
|
+
const WEB_INDEX_HTML = (name) => `<!doctype html>
|
|
20
|
+
<html>
|
|
21
|
+
<head>
|
|
22
|
+
<meta charset="utf-8" />
|
|
23
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
24
|
+
<title>${name}</title>
|
|
25
|
+
<style>
|
|
26
|
+
body { font: 16px/1.5 system-ui, sans-serif; max-width: 640px; margin: 3rem auto; padding: 0 1rem; }
|
|
27
|
+
button { padding: 0.4rem 0.9rem; cursor: pointer; }
|
|
28
|
+
form { display: flex; gap: 0.5rem; margin: 1rem 0; }
|
|
29
|
+
input { flex: 1; padding: 0.4rem 0.6rem; }
|
|
30
|
+
ul { padding-left: 1.2rem; }
|
|
31
|
+
.status { color: #666; }
|
|
32
|
+
</style>
|
|
33
|
+
</head>
|
|
34
|
+
<body>
|
|
35
|
+
<main>
|
|
36
|
+
<h1>${name}</h1>
|
|
37
|
+
<p class="status" id="auth-status">Signed out</p>
|
|
38
|
+
<button id="sign-in">Sign in with Google</button>
|
|
39
|
+
<button id="sign-out" hidden>Sign out</button>
|
|
40
|
+
<form id="add-post" hidden>
|
|
41
|
+
<input id="post-title" placeholder="Post title" required />
|
|
42
|
+
<button type="submit">Add post</button>
|
|
43
|
+
</form>
|
|
44
|
+
<ul id="posts"></ul>
|
|
45
|
+
</main>
|
|
46
|
+
<script type="module" src="/app.js"></script>
|
|
47
|
+
</body>
|
|
48
|
+
</html>
|
|
49
|
+
`;
|
|
50
|
+
const WEB_APP_JS = `// Canonical firebase/* imports. Under \`pyric dev\` they are served by the
|
|
51
|
+
// in-page pyric sandbox (the config below is ignored); under any standard
|
|
52
|
+
// bundler/pipeline the same imports resolve to the real \`firebase\` package.
|
|
53
|
+
// Graduation changes where you run this code, never the code itself.
|
|
54
|
+
import { initializeApp } from 'firebase/app';
|
|
55
|
+
import {
|
|
56
|
+
getAuth,
|
|
57
|
+
onAuthStateChanged,
|
|
58
|
+
signInWithPopup,
|
|
59
|
+
signOut,
|
|
60
|
+
GoogleAuthProvider,
|
|
61
|
+
} from 'firebase/auth';
|
|
62
|
+
import {
|
|
63
|
+
getFirestore,
|
|
64
|
+
collection,
|
|
65
|
+
onSnapshot,
|
|
66
|
+
addDoc,
|
|
67
|
+
serverTimestamp,
|
|
68
|
+
} from 'firebase/firestore';
|
|
69
|
+
|
|
70
|
+
const app = initializeApp({
|
|
71
|
+
// Graduation: your real web-app config from the Firebase console
|
|
72
|
+
// (see .env.example). Unused while developing under \`pyric dev\`.
|
|
73
|
+
apiKey: 'demo',
|
|
74
|
+
authDomain: 'demo.firebaseapp.com',
|
|
75
|
+
projectId: 'demo',
|
|
76
|
+
});
|
|
77
|
+
const auth = getAuth(app);
|
|
78
|
+
const db = getFirestore(app);
|
|
79
|
+
|
|
80
|
+
const els = {
|
|
81
|
+
status: document.getElementById('auth-status'),
|
|
82
|
+
signIn: document.getElementById('sign-in'),
|
|
83
|
+
signOut: document.getElementById('sign-out'),
|
|
84
|
+
form: document.getElementById('add-post'),
|
|
85
|
+
title: document.getElementById('post-title'),
|
|
86
|
+
posts: document.getElementById('posts'),
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
els.signIn.addEventListener('click', () => signInWithPopup(auth, new GoogleAuthProvider()));
|
|
90
|
+
els.signOut.addEventListener('click', () => signOut(auth));
|
|
91
|
+
|
|
92
|
+
onAuthStateChanged(auth, (user) => {
|
|
93
|
+
els.status.textContent = user
|
|
94
|
+
? 'Signed in as ' + (user.displayName ?? user.email)
|
|
95
|
+
: 'Signed out';
|
|
96
|
+
els.signIn.hidden = !!user;
|
|
97
|
+
els.signOut.hidden = !user;
|
|
98
|
+
els.form.hidden = !user;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
els.form.addEventListener('submit', async (e) => {
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
// The owner-based rules require uid == request.auth.uid on create.
|
|
104
|
+
await addDoc(collection(db, 'posts'), {
|
|
105
|
+
title: els.title.value.trim(),
|
|
106
|
+
uid: auth.currentUser.uid,
|
|
107
|
+
createdAt: serverTimestamp(),
|
|
108
|
+
});
|
|
109
|
+
els.title.value = '';
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
onSnapshot(collection(db, 'posts'), (snap) => {
|
|
113
|
+
els.posts.replaceChildren(
|
|
114
|
+
...snap.docs.map((d) => {
|
|
115
|
+
const li = document.createElement('li');
|
|
116
|
+
li.textContent = d.data().title;
|
|
117
|
+
return li;
|
|
118
|
+
}),
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
`;
|
|
122
|
+
const WEB_RULES = `rules_version = '2';
|
|
123
|
+
service cloud.firestore {
|
|
124
|
+
match /databases/{database}/documents {
|
|
125
|
+
// Owner-based from line 1 — \`pyric dev\` hot-reloads this file and
|
|
126
|
+
// ships a sign-in helper, so safe rules are cheap to iterate. These
|
|
127
|
+
// deploy as-is.
|
|
128
|
+
match /posts/{postId} {
|
|
129
|
+
allow read: if true;
|
|
130
|
+
allow create: if request.auth != null
|
|
131
|
+
&& request.resource.data.uid == request.auth.uid;
|
|
132
|
+
allow update, delete: if request.auth != null
|
|
133
|
+
&& resource.data.uid == request.auth.uid;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Default deny — opt in per collection.
|
|
137
|
+
match /{document=**} {
|
|
138
|
+
allow read, write: if false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
`;
|
|
143
|
+
const WEB_FIREBASE_JSON = `{
|
|
144
|
+
"firestore": {
|
|
145
|
+
"rules": "firestore.rules",
|
|
146
|
+
"indexes": "firestore.indexes.json"
|
|
147
|
+
},
|
|
148
|
+
"hosting": {
|
|
149
|
+
"public": "public",
|
|
150
|
+
"rewrites": [{ "source": "**", "destination": "/index.html" }]
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
`;
|
|
154
|
+
const WEB_SEED_JSON = `{
|
|
155
|
+
"posts/welcome": { "title": "Welcome to pyric", "uid": "seed" },
|
|
156
|
+
"posts/sandboxed": { "title": "This page runs on the in-page sandbox", "uid": "seed" }
|
|
157
|
+
}
|
|
158
|
+
`;
|
|
159
|
+
const WEB_ENV_EXAMPLE = `# Graduation config — your real Firebase web app (console → project settings).
|
|
160
|
+
# Unused under \`pyric dev\`; wire it into public/app.js (or a bundler env)
|
|
161
|
+
# when you deploy against the real backend.
|
|
162
|
+
FIREBASE_API_KEY=
|
|
163
|
+
FIREBASE_AUTH_DOMAIN=
|
|
164
|
+
FIREBASE_PROJECT_ID=
|
|
165
|
+
FIREBASE_STORAGE_BUCKET=
|
|
166
|
+
FIREBASE_MESSAGING_SENDER_ID=
|
|
167
|
+
FIREBASE_APP_ID=
|
|
168
|
+
`;
|
|
169
|
+
const webReadme = (name) => `# ${name}
|
|
170
|
+
|
|
171
|
+
A Firebase web app. In development it runs entirely on pyric's in-page
|
|
172
|
+
sandbox — no Firebase project, credentials, or emulators.
|
|
173
|
+
|
|
174
|
+
- **Develop:** \`bun install && bun run dev\` — serves \`public/\` with the
|
|
175
|
+
sandbox standing in for Firebase: seeded data, rules enforced + hot-reloaded,
|
|
176
|
+
popup sign-in via the helper dialog.
|
|
177
|
+
- **Agent:** \`bun run dev:agent\` — same, plus the MCP bridge on the dev-server
|
|
178
|
+
origin (\`/__pyric/mcp\`).
|
|
179
|
+
- **Persist (optional):** \`pyric dev --persist --seed seed.json\` — data and
|
|
180
|
+
test users survive reloads and restarts in \`.pyric/state/state.json\`
|
|
181
|
+
(plain JSON; gitignored). Promote lived state to a committable fixture
|
|
182
|
+
with \`pyric snapshot\`, then re-serve it: \`pyric dev --seed pyric-state.json\`.
|
|
183
|
+
- **Graduate:** fill \`.env\` from the Firebase console and point the config in
|
|
184
|
+
\`public/app.js\` at it, then run \`npx firebase-tools deploy\`. Bare
|
|
185
|
+
\`firebase/*\` imports need a bundler
|
|
186
|
+
(e.g. \`vite build\`) or an import map in production — \`pyric dev\`
|
|
187
|
+
provides the map in dev.
|
|
188
|
+
|
|
189
|
+
The app code uses canonical \`firebase/*\` imports everywhere. Switching
|
|
190
|
+
between sandbox and real Firebase is about **where you run it**, never what
|
|
191
|
+
you wrote.
|
|
192
|
+
`;
|
|
193
|
+
const GITIGNORE = `node_modules/
|
|
194
|
+
dist/
|
|
195
|
+
.env
|
|
196
|
+
.firebaserc
|
|
197
|
+
.pyric/
|
|
198
|
+
*.log
|
|
199
|
+
`;
|
|
200
|
+
const FIRESTORE_INDEXES = `{
|
|
201
|
+
"indexes": [],
|
|
202
|
+
"fieldOverrides": []
|
|
203
|
+
}
|
|
204
|
+
`;
|
|
205
|
+
// ─── node template (init v1 scaffold, carried over) ───────────────────
|
|
206
|
+
const NODE_APP_TS = `// Canonical Firebase imports stay unchanged between sandbox and production.
|
|
207
|
+
// \`bun run dev\` activates @pyric/cli/register; \`bun start\` loads Firebase.
|
|
208
|
+
import { initializeApp } from 'firebase/app';
|
|
209
|
+
import { getFirestore, collection, getDocs } from 'firebase/firestore';
|
|
210
|
+
import { seed } from './seed.ts';
|
|
211
|
+
|
|
212
|
+
const app = initializeApp({
|
|
213
|
+
apiKey: process.env.FIREBASE_API_KEY ?? 'pyric-local',
|
|
214
|
+
authDomain: process.env.FIREBASE_AUTH_DOMAIN,
|
|
215
|
+
projectId: process.env.FIREBASE_PROJECT_ID ?? 'pyric-local',
|
|
216
|
+
appId: process.env.FIREBASE_APP_ID ?? 'pyric-local',
|
|
217
|
+
});
|
|
218
|
+
const db = getFirestore(app);
|
|
219
|
+
|
|
220
|
+
if (process.env.PYRIC_SANDBOX) {
|
|
221
|
+
await seed(db);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const snap = await getDocs(collection(db, 'posts'));
|
|
225
|
+
console.log(\`\${snap.size} posts:\`);
|
|
226
|
+
snap.forEach((doc) => console.log(\` \${doc.id}:\`, doc.data()));
|
|
227
|
+
|
|
228
|
+
// Production: fill .env, deploy firestore.rules, then \`bun start\`.
|
|
229
|
+
`;
|
|
230
|
+
const NODE_SEED_TS = `import { collection, addDoc, type Firestore } from 'firebase/firestore';
|
|
231
|
+
|
|
232
|
+
export async function seed(db: Firestore): Promise<void> {
|
|
233
|
+
await addDoc(collection(db, 'posts'), {
|
|
234
|
+
title: 'Hello, Pyric',
|
|
235
|
+
author: 'sandbox',
|
|
236
|
+
createdAt: new Date(),
|
|
237
|
+
});
|
|
238
|
+
await addDoc(collection(db, 'posts'), {
|
|
239
|
+
title: 'Local-first by default',
|
|
240
|
+
author: 'sandbox',
|
|
241
|
+
createdAt: new Date(),
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
`;
|
|
245
|
+
const NODE_ENV_EXAMPLE = `# Production Firebase config (Firebase console -> project settings).
|
|
246
|
+
# Sandbox development uses the fallback values in src/app.ts.
|
|
247
|
+
FIREBASE_API_KEY=
|
|
248
|
+
FIREBASE_AUTH_DOMAIN=
|
|
249
|
+
FIREBASE_PROJECT_ID=
|
|
250
|
+
FIREBASE_APP_ID=
|
|
251
|
+
`;
|
|
252
|
+
const NODE_RULES = `rules_version = '2';
|
|
253
|
+
service cloud.firestore {
|
|
254
|
+
match /databases/{database}/documents {
|
|
255
|
+
// Local-first defaults: open in the sandbox so the quickstart
|
|
256
|
+
// works out of the box. **Tighten these before deploying with firebase-tools**
|
|
257
|
+
// — anonymous read+write is not what you want
|
|
258
|
+
// in the wild.
|
|
259
|
+
match /posts/{postId} {
|
|
260
|
+
allow read: if true;
|
|
261
|
+
allow write: if true;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Default deny for everything else — opt in per collection.
|
|
265
|
+
match /{document=**} {
|
|
266
|
+
allow read, write: if false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
`;
|
|
271
|
+
const NODE_FIREBASE_JSON = `{
|
|
272
|
+
"firestore": {
|
|
273
|
+
"rules": "firestore.rules",
|
|
274
|
+
"indexes": "firestore.indexes.json"
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
`;
|
|
278
|
+
const nodeReadme = (name) => `# ${name}
|
|
279
|
+
|
|
280
|
+
A Firebase app whose canonical imports run against Pyric in development and
|
|
281
|
+
real Firebase in production. No application-code switch is required.
|
|
282
|
+
|
|
283
|
+
## Quick start
|
|
284
|
+
|
|
285
|
+
\`\`\`bash
|
|
286
|
+
bun install
|
|
287
|
+
bun run dev # Pyric sandbox through the Node package swap
|
|
288
|
+
bun start # production: real Firebase
|
|
289
|
+
\`\`\`
|
|
290
|
+
|
|
291
|
+
## Use with an MCP-connected agent (Claude Code)
|
|
292
|
+
|
|
293
|
+
Install the pyric Claude Code plugin once. It auto-connects through a bundled
|
|
294
|
+
stdio proxy that discovers the running bridge from \`.pyric/serve.json\` and probes
|
|
295
|
+
both IPv4 + IPv6, so there is NO \`claude mcp add\` step and no hand-written URL (a
|
|
296
|
+
static \`127.0.0.1\` URL hits the loopback-family trap). Just start the bridge:
|
|
297
|
+
|
|
298
|
+
\`\`\`bash
|
|
299
|
+
pyric bridge # default port 5174
|
|
300
|
+
\`\`\`
|
|
301
|
+
|
|
302
|
+
and the agent's pyric tools attach automatically.
|
|
303
|
+
|
|
304
|
+
## Graduating to a real Firebase project
|
|
305
|
+
|
|
306
|
+
Graduation is a command change, not a code edit:
|
|
307
|
+
|
|
308
|
+
1. Create a project at https://console.firebase.google.com and fill \`.env\`
|
|
309
|
+
(see \`.env.example\`).
|
|
310
|
+
2. **Tighten \`firestore.rules\`** — the scaffolded rules are open for
|
|
311
|
+
sandbox convenience.
|
|
312
|
+
3. Deploy them with the Firebase CLI: add a \`.firebaserc\`
|
|
313
|
+
(\`{ "projects": { "default": "your-project-id" } }\`), then run
|
|
314
|
+
\`npx firebase-tools deploy --only firestore:rules\`.
|
|
315
|
+
4. Run the same canonical-import code against the real backend: \`bun start\`.
|
|
316
|
+
`;
|
|
317
|
+
// ─── web template (Vite + @pyric/cli/vite) ───────────────────────────
|
|
318
|
+
const VITE_INDEX_HTML = (name) => `<!doctype html>
|
|
319
|
+
<html>
|
|
320
|
+
<head>
|
|
321
|
+
<meta charset="utf-8" />
|
|
322
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
323
|
+
<title>${name}</title>
|
|
324
|
+
<style>
|
|
325
|
+
body { font: 16px/1.5 system-ui, sans-serif; max-width: 640px; margin: 3rem auto; padding: 0 1rem; }
|
|
326
|
+
button { padding: 0.4rem 0.9rem; cursor: pointer; }
|
|
327
|
+
form { display: flex; gap: 0.5rem; margin: 1rem 0; }
|
|
328
|
+
input { flex: 1; padding: 0.4rem 0.6rem; }
|
|
329
|
+
ul { padding-left: 1.2rem; }
|
|
330
|
+
.status { color: #666; }
|
|
331
|
+
</style>
|
|
332
|
+
</head>
|
|
333
|
+
<body>
|
|
334
|
+
<main>
|
|
335
|
+
<h1>${name}</h1>
|
|
336
|
+
<p class="status" id="auth-status">Signed out</p>
|
|
337
|
+
<button id="sign-in">Sign in with Google</button>
|
|
338
|
+
<button id="sign-out" hidden>Sign out</button>
|
|
339
|
+
<form id="add-post" hidden>
|
|
340
|
+
<input id="post-title" placeholder="Post title" required />
|
|
341
|
+
<button type="submit">Add post</button>
|
|
342
|
+
</form>
|
|
343
|
+
<ul id="posts"></ul>
|
|
344
|
+
</main>
|
|
345
|
+
<script type="module" src="/src/main.ts"></script>
|
|
346
|
+
</body>
|
|
347
|
+
</html>
|
|
348
|
+
`;
|
|
349
|
+
const VITE_MAIN_TS = `// Canonical firebase/* imports — UNCHANGED between dev and prod.
|
|
350
|
+
// In \`vite dev\` the \`@pyric/cli/vite\` plugin swaps these to an in-process
|
|
351
|
+
// sandbox (the config below is accepted but ignored). \`vite build\` ships the
|
|
352
|
+
// real \`firebase\` package and uses the SAME config. Graduation is a build, not
|
|
353
|
+
// a code edit.
|
|
354
|
+
import { initializeApp } from 'firebase/app';
|
|
355
|
+
import {
|
|
356
|
+
getAuth,
|
|
357
|
+
onAuthStateChanged,
|
|
358
|
+
signInWithPopup,
|
|
359
|
+
signOut,
|
|
360
|
+
GoogleAuthProvider,
|
|
361
|
+
} from 'firebase/auth';
|
|
362
|
+
import {
|
|
363
|
+
getFirestore,
|
|
364
|
+
collection,
|
|
365
|
+
onSnapshot,
|
|
366
|
+
addDoc,
|
|
367
|
+
serverTimestamp,
|
|
368
|
+
} from 'firebase/firestore';
|
|
369
|
+
|
|
370
|
+
const app = initializeApp({
|
|
371
|
+
// Filled from .env (see .env.example) at \`vite build\` time for production.
|
|
372
|
+
// Ignored in \`vite dev\` — the pyric sandbox stands in for Firebase.
|
|
373
|
+
apiKey: import.meta.env.VITE_FIREBASE_API_KEY ?? 'demo',
|
|
374
|
+
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN ?? 'demo.firebaseapp.com',
|
|
375
|
+
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID ?? 'demo',
|
|
376
|
+
});
|
|
377
|
+
const auth = getAuth(app);
|
|
378
|
+
const db = getFirestore(app);
|
|
379
|
+
|
|
380
|
+
const $ = <T extends HTMLElement>(id: string): T => document.getElementById(id) as T;
|
|
381
|
+
const els = {
|
|
382
|
+
status: $('auth-status'),
|
|
383
|
+
signIn: $<HTMLButtonElement>('sign-in'),
|
|
384
|
+
signOut: $<HTMLButtonElement>('sign-out'),
|
|
385
|
+
form: $<HTMLFormElement>('add-post'),
|
|
386
|
+
title: $<HTMLInputElement>('post-title'),
|
|
387
|
+
posts: $('posts'),
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
els.signIn.addEventListener('click', () => signInWithPopup(auth, new GoogleAuthProvider()));
|
|
391
|
+
els.signOut.addEventListener('click', () => signOut(auth));
|
|
392
|
+
|
|
393
|
+
onAuthStateChanged(auth, (user) => {
|
|
394
|
+
els.status.textContent = user
|
|
395
|
+
? 'Signed in as ' + (user.displayName ?? user.email)
|
|
396
|
+
: 'Signed out';
|
|
397
|
+
els.signIn.hidden = !!user;
|
|
398
|
+
els.signOut.hidden = !user;
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
els.form.addEventListener('submit', async (e) => {
|
|
402
|
+
e.preventDefault();
|
|
403
|
+
// The form stays visible while signed out ON PURPOSE: submitting then
|
|
404
|
+
// ATTEMPTS the write, the owner-based rules deny it (create requires
|
|
405
|
+
// uid == request.auth.uid), and the denial shows up in Pyric Studio's
|
|
406
|
+
// Traffic tab — the rules-teaching loop this demo exists for.
|
|
407
|
+
const user = auth.currentUser;
|
|
408
|
+
try {
|
|
409
|
+
await addDoc(collection(db, 'posts'), {
|
|
410
|
+
title: els.title.value.trim(),
|
|
411
|
+
uid: user?.uid ?? 'anonymous',
|
|
412
|
+
createdAt: serverTimestamp(),
|
|
413
|
+
});
|
|
414
|
+
els.title.value = '';
|
|
415
|
+
} catch (err) {
|
|
416
|
+
els.status.textContent = user
|
|
417
|
+
? \`Write failed: \${(err as { code?: string }).code ?? String(err)}\`
|
|
418
|
+
: 'Denied by rules (signed out) — see the Traffic tab in Pyric Studio.';
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
onSnapshot(collection(db, 'posts'), (snap) => {
|
|
423
|
+
els.posts.replaceChildren(
|
|
424
|
+
...snap.docs.map((d) => {
|
|
425
|
+
const li = document.createElement('li');
|
|
426
|
+
li.textContent = (d.data() as { title?: string }).title ?? '';
|
|
427
|
+
return li;
|
|
428
|
+
}),
|
|
429
|
+
);
|
|
430
|
+
});
|
|
431
|
+
`;
|
|
432
|
+
const VITE_CONFIG = `import { defineConfig } from 'vite';
|
|
433
|
+
import { pyricSandbox } from '@pyric/cli/vite';
|
|
434
|
+
|
|
435
|
+
// Under \`vite dev\` pyricSandbox() swaps firebase/* to the in-process pyric
|
|
436
|
+
// sandbox and deploys + hot-reloads firestore.rules — no Firebase project,
|
|
437
|
+
// credentials, or emulators. \`vite build\` (mode production) ships the real
|
|
438
|
+
// firebase package; the swap never reaches the deployed artifact. For a
|
|
439
|
+
// self-contained sandbox preview you can serve under \`pyric dev\`, build with a
|
|
440
|
+
// non-production mode: \`vite build --mode development\` (see the \`build:sandbox\`
|
|
441
|
+
// script). That output is marked and can never be deployed.
|
|
442
|
+
export default defineConfig({
|
|
443
|
+
plugins: [pyricSandbox()],
|
|
444
|
+
});
|
|
445
|
+
`;
|
|
446
|
+
const VITE_TSCONFIG = `{
|
|
447
|
+
"compilerOptions": {
|
|
448
|
+
"target": "ES2022",
|
|
449
|
+
"module": "ESNext",
|
|
450
|
+
"moduleResolution": "bundler",
|
|
451
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
452
|
+
"strict": true,
|
|
453
|
+
"skipLibCheck": true,
|
|
454
|
+
"noEmit": true,
|
|
455
|
+
"types": ["vite/client"]
|
|
456
|
+
},
|
|
457
|
+
"include": ["src"]
|
|
458
|
+
}
|
|
459
|
+
`;
|
|
460
|
+
// Owner-based rules for the Vite web template. Same shape as the static
|
|
461
|
+
// template's WEB_RULES, but the comment reflects the plugin (not pyric dev) —
|
|
462
|
+
// keep this in lockstep with examples/vite-sandbox-app/firestore.rules.
|
|
463
|
+
const VITE_RULES = `rules_version = '2';
|
|
464
|
+
service cloud.firestore {
|
|
465
|
+
match /databases/{database}/documents {
|
|
466
|
+
// Owner-based from line 1 — the Vite plugin deploys + hot-reloads this file
|
|
467
|
+
// into the sandbox, so safe rules are cheap to iterate. These deploy as-is.
|
|
468
|
+
match /posts/{postId} {
|
|
469
|
+
allow read: if true;
|
|
470
|
+
allow create: if request.auth != null
|
|
471
|
+
&& request.resource.data.uid == request.auth.uid;
|
|
472
|
+
allow update, delete: if request.auth != null
|
|
473
|
+
&& resource.data.uid == request.auth.uid;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Default deny — opt in per collection.
|
|
477
|
+
match /{document=**} {
|
|
478
|
+
allow read, write: if false;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
`;
|
|
483
|
+
const VITE_ENV_DTS = `/// <reference types="vite/client" />
|
|
484
|
+
`;
|
|
485
|
+
const VITE_FIREBASE_JSON = `{
|
|
486
|
+
"firestore": {
|
|
487
|
+
"rules": "firestore.rules",
|
|
488
|
+
"indexes": "firestore.indexes.json"
|
|
489
|
+
},
|
|
490
|
+
"hosting": {
|
|
491
|
+
"public": "dist",
|
|
492
|
+
"rewrites": [{ "source": "**", "destination": "/index.html" }]
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
`;
|
|
496
|
+
const VITE_ENV_EXAMPLE = `# Your real Firebase web-app config (Firebase console -> project settings).
|
|
497
|
+
# UNUSED in \`vite dev\` (the pyric sandbox stands in); USED by \`vite build\` for
|
|
498
|
+
# production. Vite only exposes \`VITE_\`-prefixed vars to client code.
|
|
499
|
+
VITE_FIREBASE_API_KEY=
|
|
500
|
+
VITE_FIREBASE_AUTH_DOMAIN=
|
|
501
|
+
VITE_FIREBASE_PROJECT_ID=
|
|
502
|
+
VITE_FIREBASE_STORAGE_BUCKET=
|
|
503
|
+
VITE_FIREBASE_MESSAGING_SENDER_ID=
|
|
504
|
+
VITE_FIREBASE_APP_ID=
|
|
505
|
+
`;
|
|
506
|
+
const viteReadme = (name) => `# ${name}
|
|
507
|
+
|
|
508
|
+
A Firebase web app built with Vite. In development it runs entirely on pyric's
|
|
509
|
+
in-process sandbox — no Firebase project, credentials, or emulators.
|
|
510
|
+
|
|
511
|
+
- **Develop:** \`bun install && bun run dev\` — \`vite dev\` with the
|
|
512
|
+
\`@pyric/cli/vite\` plugin swapping \`firebase/*\` to the sandbox: seeded data,
|
|
513
|
+
your \`firestore.rules\` deployed + hot-reloaded, popup sign-in.
|
|
514
|
+
- **Build for production:** \`bun run build\` — \`vite build\` ships the real
|
|
515
|
+
\`firebase\` package. Fill \`.env\` from the Firebase console (see
|
|
516
|
+
\`.env.example\`); the SAME config you wrote runs against real Firebase. There
|
|
517
|
+
is no separate "graduation" step — dev and prod are one toolchain.
|
|
518
|
+
- **Deploy:** \`npx firebase-tools deploy\` after the production build
|
|
519
|
+
(\`hosting.public\` is \`dist/\`, Vite's build output).
|
|
520
|
+
|
|
521
|
+
> Your app code uses canonical \`firebase/*\` imports everywhere. Switching
|
|
522
|
+
> between the sandbox and real Firebase is \`vite dev\` vs \`vite build\`, never
|
|
523
|
+
> what you wrote.
|
|
524
|
+
|
|
525
|
+
The plugin is dev-only: SharedWorker multi-tab sync, \`--persist\`, capture, and
|
|
526
|
+
the MCP bridge (all available today under \`pyric dev\`) arrive in the plugin in
|
|
527
|
+
later releases. For a pre-built / no-build app, use \`pyric init --template static\`
|
|
528
|
+
+ \`pyric dev\`.
|
|
529
|
+
`;
|
|
530
|
+
// ─── the registry ─────────────────────────────────────────────────────
|
|
531
|
+
export const TEMPLATES = {
|
|
532
|
+
// web (default) — a Vite app on the @pyric/cli/vite plugin. `vite dev` runs
|
|
533
|
+
// on the sandbox; `vite build` ships real firebase. One toolchain.
|
|
534
|
+
web: {
|
|
535
|
+
scripts: {
|
|
536
|
+
dev: 'vite',
|
|
537
|
+
build: 'vite build',
|
|
538
|
+
'build:sandbox': 'vite build --mode development',
|
|
539
|
+
preview: 'vite preview',
|
|
540
|
+
},
|
|
541
|
+
// The real firebase package ships day one so the production `vite build`
|
|
542
|
+
// resolves the same canonical imports against it — no code edit at graduation.
|
|
543
|
+
dependencies: { firebase: '^12.12.0' },
|
|
544
|
+
devDependencies: { '@pyric/cli': '*', vite: '^6.0.0', typescript: '^5.7.0' },
|
|
545
|
+
dirs: ['src'],
|
|
546
|
+
files: (name) => [
|
|
547
|
+
{ name: 'index.html', content: VITE_INDEX_HTML(name) },
|
|
548
|
+
{ name: 'vite.config.ts', content: VITE_CONFIG },
|
|
549
|
+
{ name: 'tsconfig.json', content: VITE_TSCONFIG },
|
|
550
|
+
{ name: 'src/main.ts', content: VITE_MAIN_TS },
|
|
551
|
+
{ name: 'src/vite-env.d.ts', content: VITE_ENV_DTS },
|
|
552
|
+
{ name: 'firestore.rules', content: VITE_RULES },
|
|
553
|
+
{ name: 'firebase.json', content: VITE_FIREBASE_JSON },
|
|
554
|
+
{ name: 'firestore.indexes.json', content: FIRESTORE_INDEXES },
|
|
555
|
+
{ name: '.env.example', content: VITE_ENV_EXAMPLE },
|
|
556
|
+
{ name: 'README.md', content: viteReadme(name) },
|
|
557
|
+
{ name: '.gitignore', content: GITIGNORE },
|
|
558
|
+
],
|
|
559
|
+
nextSteps: [
|
|
560
|
+
'bun install',
|
|
561
|
+
'bun run dev # vite dev on the pyric sandbox',
|
|
562
|
+
'bun run build # production build against real Firebase',
|
|
563
|
+
],
|
|
564
|
+
},
|
|
565
|
+
node: {
|
|
566
|
+
scripts: {
|
|
567
|
+
start: 'node --env-file-if-exists=.env --experimental-strip-types src/app.ts',
|
|
568
|
+
dev: 'pyric dev --no-open -- node --env-file-if-exists=.env --experimental-strip-types src/app.ts',
|
|
569
|
+
bridge: 'pyric bridge',
|
|
570
|
+
},
|
|
571
|
+
dependencies: { firebase: '^12.12.0' },
|
|
572
|
+
devDependencies: { '@pyric/cli': '*', '@types/node': '^22.0.0', typescript: '^5.7.0' },
|
|
573
|
+
dirs: ['src'],
|
|
574
|
+
files: (name) => [
|
|
575
|
+
{ name: 'src/app.ts', content: NODE_APP_TS },
|
|
576
|
+
{ name: '.env.example', content: NODE_ENV_EXAMPLE },
|
|
577
|
+
{ name: 'src/seed.ts', content: NODE_SEED_TS },
|
|
578
|
+
{ name: 'firestore.rules', content: NODE_RULES },
|
|
579
|
+
{ name: 'firebase.json', content: NODE_FIREBASE_JSON },
|
|
580
|
+
{ name: 'firestore.indexes.json', content: FIRESTORE_INDEXES },
|
|
581
|
+
{ name: 'README.md', content: nodeReadme(name) },
|
|
582
|
+
{ name: '.gitignore', content: GITIGNORE },
|
|
583
|
+
],
|
|
584
|
+
nextSteps: ['bun install', 'bun run dev', 'bun start # production: real Firebase'],
|
|
585
|
+
},
|
|
586
|
+
// static — the serve-era, no-bundler scaffold: a static app `pyric dev`
|
|
587
|
+
// runs against the in-page sandbox via a runtime import map. For pre-built /
|
|
588
|
+
// retrofit apps, or anyone who wants zero build step.
|
|
589
|
+
static: {
|
|
590
|
+
scripts: {
|
|
591
|
+
dev: 'pyric dev --seed seed.json',
|
|
592
|
+
'dev:agent': 'pyric dev --bridge --seed seed.json',
|
|
593
|
+
},
|
|
594
|
+
dependencies: { firebase: '^12.12.0' },
|
|
595
|
+
devDependencies: { '@pyric/cli': '*' },
|
|
596
|
+
dirs: ['public'],
|
|
597
|
+
files: (name) => [
|
|
598
|
+
{ name: 'public/index.html', content: WEB_INDEX_HTML(name) },
|
|
599
|
+
{ name: 'public/app.js', content: WEB_APP_JS },
|
|
600
|
+
{ name: 'firestore.rules', content: WEB_RULES },
|
|
601
|
+
{ name: 'firebase.json', content: WEB_FIREBASE_JSON },
|
|
602
|
+
{ name: 'firestore.indexes.json', content: FIRESTORE_INDEXES },
|
|
603
|
+
{ name: 'seed.json', content: WEB_SEED_JSON },
|
|
604
|
+
{ name: '.env.example', content: WEB_ENV_EXAMPLE },
|
|
605
|
+
{ name: 'README.md', content: webReadme(name) },
|
|
606
|
+
{ name: '.gitignore', content: GITIGNORE },
|
|
607
|
+
],
|
|
608
|
+
nextSteps: ['bun install', 'bun run dev', 'bun run dev:agent # agents: MCP at /__pyric/mcp'],
|
|
609
|
+
},
|
|
610
|
+
};
|
|
611
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-pyric",
|
|
3
|
+
"version": "0.1.0-alpha.9",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"homepage": "https://pyric.dev",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/davideast/pyric.git",
|
|
9
|
+
"directory": "packages/create-pyric"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/davideast/pyric/issues"
|
|
13
|
+
},
|
|
14
|
+
"description": "Scaffold a Pyric + Vite app — `npm create pyric`.",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"bin": {
|
|
17
|
+
"create-pyric": "./dist/bin.js"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc && chmod +x dist/bin.js",
|
|
32
|
+
"test": "bun test",
|
|
33
|
+
"typecheck": "bun x tsc -p tsconfig.json --noEmit"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/bun": "latest",
|
|
37
|
+
"typescript": "^5.7.0"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=22.15"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
}
|
|
45
|
+
}
|