Skip to content

Commit 39cf629

Browse files
authored
feat: implementing mixin (#106)
* feat: implementing mixin * format * update
1 parent d94fdf3 commit 39cf629

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1458
-1052
lines changed

.prettierignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
packages/language/src/generated/**
2-
**/schema.ts
2+
**/test/**/schema.ts
3+
**/test/**/models.ts
4+
**/test/**/input.ts
5+
samples/**/schema.ts
6+
samples/**/models.ts
7+
samples/**/input.ts

CLAUDE.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,26 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
55
## Development Commands
66

77
### Build System
8+
89
- `pnpm build` - Build all packages using Turbo
910
- `pnpm watch` - Watch mode for all packages
1011
- `pnpm lint` - Run ESLint across all packages
1112
- `pnpm test` - Run tests for all packages
1213

1314
### Package Management
15+
1416
- Uses `pnpm` with workspaces
1517
- Package manager is pinned to `[email protected]`
1618
- Packages are located in `packages/`, `samples/`, and `tests/`
1719

1820
### Testing
21+
1922
- Runtime package tests: `pnpm test` (includes vitest, typing generation, and typecheck)
20-
- CLI tests: `pnpm test`
23+
- CLI tests: `pnpm test`
2124
- E2E tests are in `tests/e2e/` directory
2225

2326
### ZenStack CLI Commands
27+
2428
- `npx zenstack init` - Initialize ZenStack in a project
2529
- `npx zenstack generate` - Compile ZModel schema to TypeScript
2630
- `npx zenstack db push` - Sync schema to database (uses Prisma)
@@ -30,44 +34,51 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
3034
## Architecture Overview
3135

3236
### Core Components
37+
3338
- **@zenstackhq/runtime** - Main database client and ORM engine built on Kysely
3439
- **@zenstackhq/cli** - Command line interface and project management
3540
- **@zenstackhq/language** - ZModel language specification and parser (uses Langium)
3641
- **@zenstackhq/sdk** - Code generation utilities and schema processing
3742

3843
### Key Architecture Patterns
44+
3945
- **Monorepo Structure**: Uses pnpm workspaces with Turbo for build orchestration
4046
- **Language-First Design**: ZModel DSL compiles to TypeScript, not runtime code generation
4147
- **Kysely-Based ORM**: V3 uses Kysely as query builder instead of Prisma runtime dependency
4248
- **Plugin Architecture**: Runtime plugins for query interception and entity mutation hooks
4349

4450
### ZModel to TypeScript Flow
51+
4552
1. ZModel schema (`schema.zmodel`) defines database structure and policies
4653
2. `zenstack generate` compiles ZModel to TypeScript schema (`schema.ts`)
4754
3. Schema is used to instantiate `ZenStackClient` with type-safe CRUD operations
4855
4. Client provides both high-level ORM API and low-level Kysely query builder
4956

5057
### Package Dependencies
58+
5159
- **Runtime**: Depends on Kysely, Zod, and various utility libraries
5260
- **CLI**: Depends on language package, Commander.js, and Prisma (for migrations)
5361
- **Language**: Uses Langium for grammar parsing and AST generation
5462
- **Database Support**: SQLite (better-sqlite3) and PostgreSQL (pg) only
5563

5664
### Testing Strategy
65+
5766
- Runtime package has comprehensive client API tests and policy tests
5867
- CLI has action-specific tests for commands
5968
- E2E tests validate real-world schema compatibility (cal.com, formbricks, trigger.dev)
6069
- Type coverage tests ensure TypeScript inference works correctly
6170

6271
## Key Differences from Prisma
72+
6373
- No runtime dependency on @prisma/client
6474
- Pure TypeScript implementation without Rust/WASM
6575
- Built-in access control and validation (coming soon)
6676
- Kysely query builder as escape hatch instead of raw SQL
6777
- Schema-first approach with ZModel DSL extension of Prisma schema language
6878

6979
## Development Notes
80+
7081
- Always run `zenstack generate` after modifying ZModel schemas
7182
- Database migrations still use Prisma CLI under the hood
7283
- Plugin system allows interception at ORM, Kysely, and entity mutation levels
73-
- Computed fields are evaluated at database level for performance
84+
- Computed fields are evaluated at database level for performance

TODO.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
- [x] validate
1111
- [ ] format
1212
- [ ] db seed
13+
- [ ] ZModel
14+
- [ ] View support
1315
- [ ] ORM
1416
- [x] Create
1517
- [x] Input validation
@@ -56,13 +58,14 @@
5658
- [x] Aggregate
5759
- [x] Group by
5860
- [x] Raw queries
59-
- [ ] Transactions
61+
- [x] Transactions
6062
- [x] Interactive transaction
6163
- [x] Sequential transaction
6264
- [ ] Extensions
6365
- [x] Query builder API
6466
- [x] Computed fields
6567
- [x] Prisma client extension
68+
- [ ] Custom procedures
6669
- [ ] Misc
6770
- [x] JSDoc for CRUD methods
6871
- [x] Cache validation schemas
@@ -71,14 +74,16 @@
7174
- [x] Many-to-many relation
7275
- [ ] Empty AND/OR/NOT behavior
7376
- [?] Logging
74-
- [ ] Error system
77+
- [x] Error system
7578
- [x] Custom table name
7679
- [x] Custom field name
7780
- [ ] Strict undefined checks
7881
- [ ] DbNull vs JsonNull
7982
- [ ] Benchmark
8083
- [ ] Plugin
8184
- [ ] Post-mutation hooks should be called after transaction is committed
85+
- [x] TypeDef and mixin
86+
- [ ] Strongly typed JSON
8287
- [ ] Polymorphism
8388
- [ ] Validation
8489
- [ ] Access Policy

packages/cli/src/actions/generate.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ type Options = {
1717
* CLI action for generating code from schema
1818
*/
1919
export async function run(options: Options) {
20+
const start = Date.now();
21+
2022
const schemaFile = getSchemaFile(options.schema);
2123

2224
const model = await loadSchemaDocument(schemaFile);
@@ -40,7 +42,7 @@ export async function run(options: Options) {
4042
}
4143

4244
if (!options.silent) {
43-
console.log(colors.green('Generation completed successfully.'));
45+
console.log(colors.green(`Generation completed successfully in ${Date.now() - start}ms.`));
4446
console.log(`You can now create a ZenStack client with it.
4547
4648
\`\`\`ts

packages/cli/src/actions/validate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ export async function run(options: Options) {
1919
// Re-throw to maintain CLI exit code behavior
2020
throw error;
2121
}
22-
}
22+
}

packages/cli/test/ts-schema-gen.test.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,89 @@ model Post {
181181
},
182182
});
183183
});
184+
185+
it('merges fields and attributes from mixins', async () => {
186+
const { schema } = await generateTsSchema(`
187+
type Timestamped {
188+
createdAt DateTime @default(now())
189+
updatedAt DateTime @updatedAt
190+
}
191+
192+
type Named {
193+
name String
194+
@@unique([name])
195+
}
196+
197+
model User with Timestamped Named {
198+
id String @id @default(uuid())
199+
email String @unique
200+
}
201+
`);
202+
expect(schema).toMatchObject({
203+
models: {
204+
User: {
205+
fields: {
206+
id: { type: 'String' },
207+
email: { type: 'String' },
208+
createdAt: {
209+
type: 'DateTime',
210+
default: expect.objectContaining({ function: 'now', kind: 'call' }),
211+
},
212+
updatedAt: { type: 'DateTime', updatedAt: true },
213+
name: { type: 'String' },
214+
},
215+
uniqueFields: expect.objectContaining({
216+
name: { type: 'String' },
217+
}),
218+
},
219+
},
220+
});
221+
});
222+
223+
it('generates type definitions', async () => {
224+
const { schema } = await generateTsSchema(`
225+
type Base {
226+
name String
227+
@@meta('foo', 'bar')
228+
}
229+
230+
type Address with Base {
231+
street String
232+
city String
233+
}
234+
`);
235+
expect(schema).toMatchObject({
236+
typeDefs: {
237+
Base: {
238+
fields: {
239+
name: { type: 'String' },
240+
},
241+
attributes: [
242+
{
243+
name: '@@meta',
244+
args: [
245+
{ name: 'name', value: { kind: 'literal', value: 'foo' } },
246+
{ name: 'value', value: { kind: 'literal', value: 'bar' } },
247+
],
248+
},
249+
],
250+
},
251+
Address: {
252+
fields: {
253+
street: { type: 'String' },
254+
city: { type: 'String' },
255+
},
256+
attributes: [
257+
{
258+
name: '@@meta',
259+
args: [
260+
{ name: 'name', value: { kind: 'literal', value: 'foo' } },
261+
{ name: 'value', value: { kind: 'literal', value: 'bar' } },
262+
],
263+
},
264+
],
265+
},
266+
},
267+
});
268+
});
184269
});

packages/language/package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@
3737
"default": "./dist/ast.cjs"
3838
}
3939
},
40+
"./utils": {
41+
"import": {
42+
"types": "./dist/utils.d.ts",
43+
"default": "./dist/utils.js"
44+
},
45+
"require": {
46+
"types": "./dist/utils.d.cts",
47+
"default": "./dist/utils.cjs"
48+
}
49+
},
4050
"./package.json": {
4151
"import": "./package.json",
4252
"require": "./package.json"
@@ -51,6 +61,7 @@
5161
"@types/pluralize": "^0.0.33",
5262
"@zenstackhq/eslint-config": "workspace:*",
5363
"@zenstackhq/typescript-config": "workspace:*",
64+
"@zenstackhq/common-helpers": "workspace:*",
5465
"langium-cli": "catalog:"
5566
},
5667
"volta": {

0 commit comments

Comments
 (0)