@fougere/cli 0.5.0-alpha.0 → 0.6.0-alpha.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/package.json +7 -7
- package/templates/admin/fronds/admin/handlers/UserHandler.ts +3 -3
- package/templates/api/fronds/api/handlers/TaskHandler.ts +3 -3
- package/templates/blog/fronds/blog/handlers/PostHandler.ts +3 -3
- package/templates/flat/CLAUDE.md +3 -3
- package/templates/frond/CLAUDE.md +3 -3
- package/templates/frond/fronds/__name__/handlers/PostHandler.ts +3 -3
- package/templates/fronds/blog/handlers/PostHandler.ts +4 -4
- package/templates/workspace/CLAUDE.md +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fougere/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0-alpha.0",
|
|
4
4
|
"description": "The Fougere CLI — compose a workspace, serve a frond, call an operation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fougere",
|
|
@@ -41,12 +41,12 @@
|
|
|
41
41
|
"consola": "^3.4.2",
|
|
42
42
|
"jiti": "^2.4.2",
|
|
43
43
|
"picocolors": "^1.1.1",
|
|
44
|
-
"@fougere/adapter-sql": "0.
|
|
45
|
-
"@fougere/
|
|
46
|
-
"@fougere/
|
|
47
|
-
"@fougere/
|
|
48
|
-
"@fougere/
|
|
49
|
-
"@fougere/
|
|
44
|
+
"@fougere/adapter-sql": "0.6.0-alpha.0",
|
|
45
|
+
"@fougere/container": "0.6.0-alpha.0",
|
|
46
|
+
"@fougere/defaults": "0.6.0-alpha.0",
|
|
47
|
+
"@fougere/core": "0.6.0-alpha.0",
|
|
48
|
+
"@fougere/transport-http": "0.6.0-alpha.0",
|
|
49
|
+
"@fougere/schema": "0.6.0-alpha.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"vitest": "^4.1.0"
|
|
@@ -9,19 +9,19 @@ export class UserCard extends User.pick('id', 'name', 'status') {}
|
|
|
9
9
|
export default class UserHandler extends Crud(User) {
|
|
10
10
|
/** active→inactive — an operation, not a field write. Judge: active only. */
|
|
11
11
|
async deactivate(id: string): Promise<User> {
|
|
12
|
-
const user = await this.
|
|
12
|
+
const user = await this.storage.findById(id);
|
|
13
13
|
if (!user) {
|
|
14
14
|
throw new FougereError({ code: ErrorCode.NOT_FOUND, message: `User '${id}' not found`, entity: 'user', operation: 'deactivate' });
|
|
15
15
|
}
|
|
16
16
|
if (user.status === 'inactive') {
|
|
17
17
|
throw new FougereError({ code: ErrorCode.CONFLICT, message: 'Already inactive', entity: 'user', operation: 'deactivate' });
|
|
18
18
|
}
|
|
19
|
-
return this.
|
|
19
|
+
return this.storage.update(id, { status: 'inactive' });
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/** Active users, projected to the card contract. */
|
|
23
23
|
async active(): Promise<UserCard[]> {
|
|
24
|
-
const users = await this.
|
|
24
|
+
const users = await this.storage.list({ where: { status: 'active' } });
|
|
25
25
|
return users.map(({ id, name, status }) => ({ id, name, status }));
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -9,19 +9,19 @@ export class TaskCard extends Task.pick('id', 'title', 'status') {}
|
|
|
9
9
|
export default class TaskHandler extends Crud(Task) {
|
|
10
10
|
/** open→done — an operation, not a field write. Judge: open only. */
|
|
11
11
|
async complete(id: string): Promise<Task> {
|
|
12
|
-
const task = await this.
|
|
12
|
+
const task = await this.storage.findById(id);
|
|
13
13
|
if (!task) {
|
|
14
14
|
throw new FougereError({ code: ErrorCode.NOT_FOUND, message: `Task '${id}' not found`, entity: 'task', operation: 'complete' });
|
|
15
15
|
}
|
|
16
16
|
if (task.status === 'done') {
|
|
17
17
|
throw new FougereError({ code: ErrorCode.CONFLICT, message: 'Already done', entity: 'task', operation: 'complete' });
|
|
18
18
|
}
|
|
19
|
-
return this.
|
|
19
|
+
return this.storage.update(id, { status: 'done' });
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/** Still-open tasks, projected to the card contract. */
|
|
23
23
|
async open(): Promise<TaskCard[]> {
|
|
24
|
-
const tasks = await this.
|
|
24
|
+
const tasks = await this.storage.list({ where: { status: 'open' } });
|
|
25
25
|
return tasks.map(({ id, title, status }) => ({ id, title, status }));
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -15,19 +15,19 @@ export default class PostHandler extends Crud(Post) {
|
|
|
15
15
|
* Realise: the server flips the owned field.
|
|
16
16
|
*/
|
|
17
17
|
async publish(id: string): Promise<Post> {
|
|
18
|
-
const post = await this.
|
|
18
|
+
const post = await this.storage.findById(id);
|
|
19
19
|
if (!post) {
|
|
20
20
|
throw new FougereError({ code: ErrorCode.NOT_FOUND, message: `Post '${id}' not found`, entity: 'post', operation: 'publish' });
|
|
21
21
|
}
|
|
22
22
|
if (post.status === 'published') {
|
|
23
23
|
throw new FougereError({ code: ErrorCode.CONFLICT, message: 'Already published', entity: 'post', operation: 'publish' });
|
|
24
24
|
}
|
|
25
|
-
return this.
|
|
25
|
+
return this.storage.update(id, { status: 'published' });
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/** Only published posts exist for the outside world, projected to the card. */
|
|
29
29
|
async listPublished(): Promise<PostCard[]> {
|
|
30
|
-
const posts = await this.
|
|
30
|
+
const posts = await this.storage.list({ where: { status: 'published' } });
|
|
31
31
|
return posts.map(({ id, title, status }) => ({ id, title, status }));
|
|
32
32
|
}
|
|
33
33
|
}
|
package/templates/flat/CLAUDE.md
CHANGED
|
@@ -28,7 +28,7 @@ If you are about to write the same constraint in two places, you have missed the
|
|
|
28
28
|
## A surface is a door, never a logic
|
|
29
29
|
|
|
30
30
|
Every door goes through the handler **façade**, which is the judge: validation, unknown-key refusal,
|
|
31
|
-
collectors. A resolver or route you wire yourself against the
|
|
31
|
+
collectors. A resolver or route you wire yourself against the storage — or worse, against the database —
|
|
32
32
|
is a second door with no judge behind it, and the rules declared in the entities stop applying there.
|
|
33
33
|
|
|
34
34
|
Before adding a surface, reach for its **projection**:
|
|
@@ -44,7 +44,7 @@ exist to add what a projection cannot derive — never to replace it.
|
|
|
44
44
|
|
|
45
45
|
## Reading data
|
|
46
46
|
|
|
47
|
-
Storage is reached through a repository. Never inject `
|
|
47
|
+
Storage is reached through a repository. Never inject `Storage` directly into a handler,
|
|
48
48
|
presenter or collector — the boot refuses it. With no repository file, ask for the default shape:
|
|
49
49
|
|
|
50
50
|
```ts
|
|
@@ -54,7 +54,7 @@ import Product from '../entities/Product.js';
|
|
|
54
54
|
constructor(private products: RepositoryOf<Product>) {}
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
If the handler extends `Crud(Product)`, its inherited `this.
|
|
57
|
+
If the handler extends `Crud(Product)`, its inherited `this.storage` is already backed by that
|
|
58
58
|
repository; do not add a constructor. The repository forwards the guarded storage gestures:
|
|
59
59
|
|
|
60
60
|
```
|
|
@@ -28,7 +28,7 @@ If you are about to write the same constraint in two places, you have missed the
|
|
|
28
28
|
## A surface is a door, never a logic
|
|
29
29
|
|
|
30
30
|
Every door goes through the handler **façade**, which is the judge: validation, unknown-key refusal,
|
|
31
|
-
collectors. A resolver or route you wire yourself against the
|
|
31
|
+
collectors. A resolver or route you wire yourself against the storage — or worse, against the database —
|
|
32
32
|
is a second door with no judge behind it, and the rules declared in the entities stop applying there.
|
|
33
33
|
|
|
34
34
|
Before adding a surface, reach for its **projection**:
|
|
@@ -44,7 +44,7 @@ exist to add what a projection cannot derive — never to replace it.
|
|
|
44
44
|
|
|
45
45
|
## Reading data
|
|
46
46
|
|
|
47
|
-
Storage is reached through a repository. Never inject `
|
|
47
|
+
Storage is reached through a repository. Never inject `Storage` directly into a handler,
|
|
48
48
|
presenter or collector — the boot refuses it. With no repository file, ask for the default shape:
|
|
49
49
|
|
|
50
50
|
```ts
|
|
@@ -54,7 +54,7 @@ import Product from '../entities/Product.js';
|
|
|
54
54
|
constructor(private products: RepositoryOf<Product>) {}
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
If the handler extends `Crud(Product)`, its inherited `this.
|
|
57
|
+
If the handler extends `Crud(Product)`, its inherited `this.storage` is already backed by that
|
|
58
58
|
repository; do not add a constructor. The repository forwards the guarded storage gestures:
|
|
59
59
|
|
|
60
60
|
```
|
|
@@ -6,19 +6,19 @@ export class PostCard extends Post.pick('id', 'title', 'status') {}
|
|
|
6
6
|
export default class PostHandler extends Crud(Post) {
|
|
7
7
|
/** The draft→published transition — an operation, not a field write. */
|
|
8
8
|
async publish(id: string): Promise<Post> {
|
|
9
|
-
const post = await this.
|
|
9
|
+
const post = await this.storage.findById(id);
|
|
10
10
|
if (!post) {
|
|
11
11
|
throw new FougereError({ code: ErrorCode.NOT_FOUND, message: `Post '${id}' not found`, entity: 'post', operation: 'publish' });
|
|
12
12
|
}
|
|
13
13
|
if (post.status === 'published') {
|
|
14
14
|
throw new FougereError({ code: ErrorCode.CONFLICT, message: 'Already published', entity: 'post', operation: 'publish' });
|
|
15
15
|
}
|
|
16
|
-
return this.
|
|
16
|
+
return this.storage.update(id, { status: 'published' });
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/** Only published posts, projected to the card. */
|
|
20
20
|
async listPublished(): Promise<PostCard[]> {
|
|
21
|
-
const posts = await this.
|
|
21
|
+
const posts = await this.storage.list({ where: { status: 'published' } });
|
|
22
22
|
return posts.map(({ id, title, status }) => ({ id, title, status }));
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -13,24 +13,24 @@ export default class PostHandler extends Crud(Post) {
|
|
|
13
13
|
* bars `status` for the whole entity — `NewPost` says what *this* op accepts.
|
|
14
14
|
*/
|
|
15
15
|
async create(input: NewPost): Promise<Post> {
|
|
16
|
-
return this.
|
|
16
|
+
return this.storage.create(input);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/** The draft→published transition — an operation, not a field write. */
|
|
20
20
|
async publish(id: string): Promise<Post> {
|
|
21
|
-
const post = await this.
|
|
21
|
+
const post = await this.storage.findById(id);
|
|
22
22
|
if (!post) {
|
|
23
23
|
throw new FougereError({ code: ErrorCode.NOT_FOUND, message: `Post '${id}' not found`, entity: 'post', operation: 'publish' });
|
|
24
24
|
}
|
|
25
25
|
if (post.status === 'published') {
|
|
26
26
|
throw new FougereError({ code: ErrorCode.CONFLICT, message: 'Already published', entity: 'post', operation: 'publish' });
|
|
27
27
|
}
|
|
28
|
-
return this.
|
|
28
|
+
return this.storage.update(id, { status: 'published' });
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/** Only published posts, projected to the card. */
|
|
32
32
|
async listPublished(): Promise<PostCard[]> {
|
|
33
|
-
const posts = await this.
|
|
33
|
+
const posts = await this.storage.list({ where: { status: 'published' } });
|
|
34
34
|
return posts.map(({ id, title, status }) => ({ id, title, status }));
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -28,7 +28,7 @@ If you are about to write the same constraint in two places, you have missed the
|
|
|
28
28
|
## A surface is a door, never a logic
|
|
29
29
|
|
|
30
30
|
Every door goes through the handler **façade**, which is the judge: validation, unknown-key refusal,
|
|
31
|
-
collectors. A resolver or route you wire yourself against the
|
|
31
|
+
collectors. A resolver or route you wire yourself against the storage — or worse, against the database —
|
|
32
32
|
is a second door with no judge behind it, and the rules declared in the entities stop applying there.
|
|
33
33
|
|
|
34
34
|
Before adding a surface, reach for its **projection**:
|
|
@@ -44,7 +44,7 @@ exist to add what a projection cannot derive — never to replace it.
|
|
|
44
44
|
|
|
45
45
|
## Reading data
|
|
46
46
|
|
|
47
|
-
Storage is reached through a repository. Never inject `
|
|
47
|
+
Storage is reached through a repository. Never inject `Storage` directly into a handler,
|
|
48
48
|
presenter or collector — the boot refuses it. With no repository file, ask for the default shape:
|
|
49
49
|
|
|
50
50
|
```ts
|
|
@@ -54,7 +54,7 @@ import Product from '../entities/Product.js';
|
|
|
54
54
|
constructor(private products: RepositoryOf<Product>) {}
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
If the handler extends `Crud(Product)`, its inherited `this.
|
|
57
|
+
If the handler extends `Crud(Product)`, its inherited `this.storage` is already backed by that
|
|
58
58
|
repository; do not add a constructor. The repository forwards the guarded storage gestures:
|
|
59
59
|
|
|
60
60
|
```
|