Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.

Commit 97474ee

Browse files
authored
Merge pull request #26 from pyreon/bump-pyreon-0.7.0
Bump @pyreon/* deps to 0.7.0
2 parents df9d641 + 39fc1fd commit 97474ee

15 files changed

Lines changed: 500 additions & 188 deletions

File tree

.claude/rules/anti-patterns.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
## Effect cleanup
2121
- `effect()` returns an `Effect` object with `.dispose()` — not a raw function
2222
- Use `onUnmount(() => dispose.dispose())` for cleanup, not `onUnmount(dispose)`
23+
- Prefer `onCleanup()` inside effects for inline cleanup (available since @pyreon/reactivity 0.7.0)
24+
25+
## Context mistakes
26+
- **Never** use `<Context.Provider value={...}>` JSX — Pyreon contexts don't have a `.Provider` component
27+
- Use `provide(context, value)` from `@pyreon/core` (available since 0.6.0) — it calls `pushContext` + `onUnmount(popContext)` automatically
28+
- For manual control: `pushContext(new Map([[ctx.id, value]]))` + `onUnmount(() => popContext())`
2329

2430
## Store mistakes
2531
- **Never** import `signal`/`computed`/`effect` from `@pyreon/reactivity` in app code — use `@pyreon/store` re-exports

.mcp.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"mcpServers": {
3+
"pyreon": {
4+
"command": "bunx",
5+
"args": ["@pyreon/mcp"]
6+
}
7+
}
8+
}

CLAUDE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@ Zero re-exports and integrates the full Pyreon stack:
1616

1717
**Fundamentals (re-exported from `@pyreon/meta`):**
1818

19-
- `@pyreon/store``defineStore`, `signal`, `computed`, `effect`, `batch`
19+
- `@pyreon/store``defineStore`, `signal`, `computed`, `effect`, `batch`, `onCleanup`
2020
- `@pyreon/form``useForm`, `useField`, `useFieldArray`, `FormProvider`
2121
- `@pyreon/validation``zodSchema`, `zodField` (+ valibot, arktype adapters)
2222
- `@pyreon/query``useQuery`, `useMutation`, `QueryClient`, `QueryClientProvider`
2323
- `@pyreon/table``useTable`, `flexRender`
2424
- `@pyreon/virtual``useVirtualizer`, `useWindowVirtualizer`
2525
- `@pyreon/i18n``createI18n`, `I18nProvider`, `useI18n`, `Trans`
2626
- `@pyreon/feature``defineFeature`, `reference` (schema-driven CRUD features)
27+
- `@pyreon/flow``createFlow`, `Flow`, `Background`, `MiniMap`, `Controls`, `Handle`, `computeLayout`
28+
- `@pyreon/code``createEditor`, `CodeEditor`, `DiffEditor`, `TabbedEditor`
2729

2830
**UI System (re-exported from `@pyreon/meta`):**
2931

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,19 @@ The interactive CLI lets you pick your rendering mode, features, and AI toolchai
4141
- Node.js, Bun, and static deploy adapters
4242

4343
**Ecosystem (via @pyreon/meta)**
44-
- State: `@pyreon/store` — signal-based stores
44+
- State: `@pyreon/store` — signal-based stores with `onCleanup` for effect cleanup
4545
- Data: `@pyreon/query` — TanStack Query adapter
4646
- Forms: `@pyreon/form` + `@pyreon/validation` — Zod/Valibot/ArkType
4747
- CRUD: `@pyreon/feature` — schema-driven features
4848
- Tables: `@pyreon/table`, Virtual lists: `@pyreon/virtual`
4949
- i18n: `@pyreon/i18n` — translations, plurals, rich text
50+
- State machines: `@pyreon/machine` — reactive FSM
51+
- Permissions: `@pyreon/permissions` — reactive RBAC/ABAC
52+
- Flow diagrams: `@pyreon/flow` — reactive node graphs with auto-layout
53+
- Code editor: `@pyreon/code` — CodeMirror 6 with signals, diff, tabs, minimap (optional)
54+
- Charts: `@pyreon/charts` — reactive ECharts with auto lazy loading (optional)
55+
- Hotkeys: `@pyreon/hotkeys` — keyboard shortcuts with scoping
56+
- Storage: `@pyreon/storage` — reactive localStorage, cookies, IndexedDB
5057
- Styling: `@pyreon/styler` — CSS-in-JS
5158
- UI: `@pyreon/elements`, `@pyreon/coolgrid`, `@pyreon/hooks`
5259
- Animations: `@pyreon/kinetic` + 120 presets
@@ -64,7 +71,7 @@ The interactive CLI lets you pick your rendering mode, features, and AI toolchai
6471
bun install
6572
bun run dev # Dev mode
6673
bun run build # Build all packages
67-
bun run test # Run tests (309 tests)
74+
bun run test # Run tests (388 tests)
6875
bun run typecheck # Type check all packages
6976
bun run test:template # Validate starter template
7077
```

bun.lock

Lines changed: 242 additions & 93 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cli/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
"typecheck": "tsc --noEmit"
2828
},
2929
"dependencies": {
30-
"@pyreon/cli": "^0.5.4",
30+
"@pyreon/cli": "^0.7.0",
3131
"@pyreon/create-zero": "workspace:^",
32-
"@pyreon/server": "^0.5.4",
32+
"@pyreon/server": "^0.7.0",
3333
"@pyreon/zero": "workspace:^",
3434
"cac": "^7.0.0",
3535
"vite": "^8.0.0"

packages/create-zero/src/index.ts

Lines changed: 95 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ interface ProjectConfig {
1010
targetDir: string
1111
renderMode: 'ssr-stream' | 'ssr-string' | 'ssg' | 'spa'
1212
features: string[]
13+
packageStrategy: 'meta' | 'individual'
1314
aiToolchain: boolean
1415
}
1516

@@ -68,6 +69,26 @@ const FEATURES = {
6869
label: 'Hooks (@pyreon/hooks — 25+ signal-based utilities)',
6970
deps: ['@pyreon/hooks'],
7071
},
72+
charts: {
73+
label: 'Charts (@pyreon/charts — reactive ECharts)',
74+
deps: ['@pyreon/charts'],
75+
},
76+
hotkeys: {
77+
label: 'Hotkeys (@pyreon/hotkeys — keyboard shortcuts)',
78+
deps: ['@pyreon/hotkeys'],
79+
},
80+
storage: {
81+
label: 'Storage (@pyreon/storage — localStorage, cookies, IndexedDB)',
82+
deps: ['@pyreon/storage'],
83+
},
84+
flow: {
85+
label: 'Flow Diagrams (@pyreon/flow — reactive node graphs)',
86+
deps: ['@pyreon/flow'],
87+
},
88+
code: {
89+
label: 'Code Editor (@pyreon/code — CodeMirror 6)',
90+
deps: ['@pyreon/code'],
91+
},
7192
} as const
7293

7394
type FeatureKey = keyof typeof FEATURES
@@ -142,6 +163,20 @@ async function main() {
142163
process.exit(0)
143164
}
144165

166+
// Package strategy
167+
const packageStrategy = await p.select({
168+
message: 'Package imports',
169+
options: [
170+
{ value: 'meta', label: '@pyreon/meta (single barrel)', hint: 'one import for everything — simpler, tree-shaken at build' },
171+
{ value: 'individual', label: 'Individual packages', hint: 'only install what you selected — smaller node_modules' },
172+
],
173+
})
174+
175+
if (p.isCancel(packageStrategy)) {
176+
p.cancel('Cancelled.')
177+
process.exit(0)
178+
}
179+
145180
// AI toolchain
146181
const aiToolchain = await p.confirm({
147182
message: 'Include AI toolchain? (MCP server, CLAUDE.md, doctor)',
@@ -158,6 +193,7 @@ async function main() {
158193
targetDir,
159194
renderMode: renderMode as ProjectConfig['renderMode'],
160195
features: features as string[],
196+
packageStrategy: packageStrategy as ProjectConfig['packageStrategy'],
161197
aiToolchain: aiToolchain as boolean,
162198
}
163199

@@ -266,45 +302,79 @@ async function scaffold(config: ProjectConfig) {
266302

267303
// ─── File generators ────────────────────────────────────────────────────────
268304

305+
// Resolve the correct version range for a @pyreon/* package
306+
function pyreonVersion(pkg: string): string {
307+
// Core packages
308+
const core = ['core', 'reactivity', 'runtime-dom', 'runtime-server', 'server', 'head', 'router', 'vite-plugin', 'compiler', 'cli', 'mcp']
309+
if (core.some((c) => pkg === `@pyreon/${c}`)) return '^0.7.0'
310+
// Zero framework packages
311+
if (pkg === '@pyreon/zero' || pkg === '@pyreon/meta' || pkg === '@pyreon/zero-cli' || pkg === '@pyreon/create-zero') return '^0.2.0'
312+
// Fundamentals
313+
const fundamentals = ['store', 'form', 'validation', 'query', 'table', 'virtual', 'i18n', 'feature', 'machine', 'permissions', 'flow', 'code']
314+
if (fundamentals.some((f) => pkg === `@pyreon/${f}`)) return '^0.6.0'
315+
// UI system
316+
return '^0.2.0'
317+
}
318+
269319
function generatePackageJson(config: ProjectConfig): string {
270320
const deps: Record<string, string> = {
271-
'@pyreon/core': 'latest',
272-
'@pyreon/head': 'latest',
273-
'@pyreon/reactivity': 'latest',
274-
'@pyreon/router': 'latest',
275-
'@pyreon/runtime-dom': 'latest',
276-
'@pyreon/runtime-server': 'latest',
277-
'@pyreon/server': 'latest',
278-
'@pyreon/zero': 'latest',
321+
'@pyreon/core': pyreonVersion('@pyreon/core'),
322+
'@pyreon/head': pyreonVersion('@pyreon/head'),
323+
'@pyreon/reactivity': pyreonVersion('@pyreon/reactivity'),
324+
'@pyreon/router': pyreonVersion('@pyreon/router'),
325+
'@pyreon/runtime-dom': pyreonVersion('@pyreon/runtime-dom'),
326+
'@pyreon/runtime-server': pyreonVersion('@pyreon/runtime-server'),
327+
'@pyreon/server': pyreonVersion('@pyreon/server'),
328+
'@pyreon/zero': pyreonVersion('@pyreon/zero'),
279329
}
280330

281-
// Add feature-specific deps
282-
const allDeps = new Set<string>()
283-
for (const key of config.features) {
284-
const feature = FEATURES[key as FeatureKey]
285-
if (feature) {
286-
for (const dep of feature.deps) allDeps.add(dep)
331+
if (config.packageStrategy === 'meta') {
332+
// Single barrel — includes all fundamentals + UI system
333+
deps['@pyreon/meta'] = pyreonVersion('@pyreon/meta')
334+
// Still need non-pyreon deps for selected features
335+
for (const key of config.features) {
336+
const feature = FEATURES[key as FeatureKey]
337+
if (feature) {
338+
for (const dep of feature.deps) {
339+
if (!dep.startsWith('@pyreon/')) {
340+
if (dep.startsWith('@tanstack/')) {
341+
deps[dep] = dep.includes('query') ? '^5.90.0' : dep.includes('table') ? '^8.21.0' : '^3.13.0'
342+
} else if (dep === 'zod') {
343+
deps[dep] = '^4.0.0'
344+
}
345+
}
346+
}
347+
}
287348
}
288-
}
289-
for (const dep of allDeps) {
290-
if (dep.startsWith('@pyreon/')) {
291-
deps[dep] = 'latest'
292-
} else if (dep.startsWith('@tanstack/')) {
293-
deps[dep] = dep.includes('query') ? '^5.90.0' : dep.includes('table') ? '^8.21.0' : '^3.13.0'
294-
} else if (dep === 'zod') {
295-
deps[dep] = '^4.0.0'
349+
} else {
350+
// Individual packages — only install what's selected
351+
const allDeps = new Set<string>()
352+
for (const key of config.features) {
353+
const feature = FEATURES[key as FeatureKey]
354+
if (feature) {
355+
for (const dep of feature.deps) allDeps.add(dep)
356+
}
357+
}
358+
for (const dep of allDeps) {
359+
if (dep.startsWith('@pyreon/')) {
360+
deps[dep] = pyreonVersion(dep)
361+
} else if (dep.startsWith('@tanstack/')) {
362+
deps[dep] = dep.includes('query') ? '^5.90.0' : dep.includes('table') ? '^8.21.0' : '^3.13.0'
363+
} else if (dep === 'zod') {
364+
deps[dep] = '^4.0.0'
365+
}
296366
}
297367
}
298368

299369
const devDeps: Record<string, string> = {
300-
'@pyreon/vite-plugin': 'latest',
301-
'@pyreon/zero-cli': 'latest',
370+
'@pyreon/vite-plugin': pyreonVersion('@pyreon/vite-plugin'),
371+
'@pyreon/zero-cli': pyreonVersion('@pyreon/zero-cli'),
302372
'typescript': '^5.9.3',
303373
'vite': '^7.0.0',
304374
}
305375

306376
if (config.aiToolchain) {
307-
devDeps['@pyreon/mcp'] = 'latest'
377+
devDeps['@pyreon/mcp'] = pyreonVersion('@pyreon/mcp')
308378
}
309379

310380
const scripts: Record<string, string> = {

packages/create-zero/templates/default/package.json

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,42 +12,18 @@
1212
"doctor:ci": "zero doctor --ci"
1313
},
1414
"dependencies": {
15-
"@pyreon/attrs": "latest",
16-
"@pyreon/coolgrid": "latest",
17-
"@pyreon/core": "latest",
18-
"@pyreon/elements": "latest",
19-
"@pyreon/feature": "latest",
20-
"@pyreon/form": "latest",
21-
"@pyreon/head": "latest",
22-
"@pyreon/hooks": "latest",
23-
"@pyreon/i18n": "latest",
24-
"@pyreon/kinetic": "latest",
25-
"@pyreon/kinetic-presets": "latest",
26-
"@pyreon/query": "latest",
27-
"@pyreon/reactivity": "latest",
28-
"@pyreon/rocketstyle": "latest",
29-
"@pyreon/router": "latest",
30-
"@pyreon/runtime-dom": "latest",
31-
"@pyreon/runtime-server": "latest",
32-
"@pyreon/server": "latest",
33-
"@pyreon/store": "latest",
34-
"@pyreon/styler": "latest",
35-
"@pyreon/table": "latest",
36-
"@pyreon/ui-core": "latest",
37-
"@pyreon/unistyle": "latest",
38-
"@pyreon/validation": "latest",
39-
"@pyreon/virtual": "latest",
40-
"@pyreon/zero": "latest",
15+
"@pyreon/meta": "^0.2.0",
16+
"@pyreon/zero": "^0.2.0",
4117
"@tanstack/query-core": "^5.90.0",
4218
"@tanstack/table-core": "^8.21.0",
4319
"@tanstack/virtual-core": "^3.13.0",
4420
"zod": "^4.0.0"
4521
},
4622
"devDependencies": {
47-
"@pyreon/mcp": "latest",
48-
"@pyreon/vite-plugin": "latest",
49-
"@pyreon/zero-cli": "latest",
23+
"@pyreon/mcp": "^0.7.0",
24+
"@pyreon/vite-plugin": "^0.7.0",
25+
"@pyreon/zero-cli": "^0.2.0",
5026
"typescript": "^5.9.3",
51-
"vite": "^7.0.0"
27+
"vite": "^8.0.0"
5228
}
5329
}

packages/create-zero/templates/default/src/routes/counter.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,21 @@ export default function Counter() {
3131
<button
3232
type="button"
3333
class="btn btn-secondary"
34-
onclick={() => count(count() - 1)}
34+
onclick={() => count.update((n) => n - 1)}
3535
>
3636
-
3737
</button>
3838
<button
3939
type="button"
4040
class="btn btn-primary"
41-
onclick={() => count(0)}
41+
onclick={() => count.set(0)}
4242
>
4343
Reset
4444
</button>
4545
<button
4646
type="button"
4747
class="btn btn-secondary"
48-
onclick={() => count(count() + 1)}
48+
onclick={() => count.update((n) => n + 1)}
4949
>
5050
+
5151
</button>

packages/create-zero/templates/default/src/routes/posts/new.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ export default function NewPostPage() {
3333
<form onSubmit={(e: Event) => form.handleSubmit(e)}>
3434
<div class="form-field">
3535
<label for="title">Title</label>
36-
<input id="title" {...form.register('title')} />
36+
<input id="title" {...form.register('title', {})} />
3737
</div>
3838
<div class="form-field">
3939
<label for="body">Body</label>
40-
<textarea id="body" rows={6} {...form.register('body')} />
40+
<textarea id="body" rows={6} {...form.register('body', {})} />
4141
</div>
4242
<div class="form-field">
4343
<label class="checkbox-label">

0 commit comments

Comments
 (0)