create-relkit 0.7.7 → 0.7.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/dist/create-capabilities.js +4 -4
- package/dist/render-database-sources.d.ts +0 -4
- package/dist/render-database-sources.d.ts.map +1 -1
- package/dist/render-database-sources.js +2 -26
- package/dist/render-database-sources.js.map +1 -1
- package/dist/render-database.d.ts.map +1 -1
- package/dist/render-database.js +2 -5
- package/dist/render-database.js.map +1 -1
- package/dist/templates/default/README.md +1 -1
- package/dist/templates/default/v1/agent/.agents/skills/relkit/SKILL.md +29 -1
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/cli.md +7 -0
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/data-apis.md +16 -0
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/domain-apis.md +76 -3
- package/dist/templates/default/v1/agent/gitignore +1 -4
- package/dist/templates/default/v1/agent/package.json +4 -4
- package/dist/templates/default/v1/api/.agents/skills/relkit/SKILL.md +29 -1
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/cli.md +7 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/data-apis.md +16 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/domain-apis.md +76 -3
- package/dist/templates/default/v1/api/gitignore +1 -4
- package/dist/templates/default/v1/api/package.json +3 -3
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/SKILL.md +29 -1
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/cli.md +7 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/data-apis.md +16 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/domain-apis.md +76 -3
- package/dist/templates/default/v1/fullstack/gitignore +3 -1
- package/dist/templates/default/v1/fullstack/package.json +5 -5
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/SKILL.md +29 -1
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/cli.md +7 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/data-apis.md +16 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/domain-apis.md +76 -3
- package/dist/templates/default/v1/minimal/gitignore +1 -4
- package/dist/templates/default/v1/minimal/package.json +3 -3
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/SKILL.md +29 -1
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/cli.md +7 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/data-apis.md +16 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/domain-apis.md +76 -3
- package/dist/templates/default/v1/tasks/gitignore +1 -4
- package/dist/templates/default/v1/tasks/package.json +3 -3
- package/package.json +3 -3
|
@@ -2,6 +2,60 @@
|
|
|
2
2
|
|
|
3
3
|
Generated projects map `@app/*` to `src/*`; keep the `.js` suffix on source imports. Use the installed package types when an exact signature matters, and run `bun run check` to validate the discovered graph.
|
|
4
4
|
|
|
5
|
+
## Choose a domain before choosing a folder
|
|
6
|
+
|
|
7
|
+
For work spanning business capabilities, map responsibility to services before adding artifacts. Record the rules/state each service owns, the operations/events it exposes, its dependencies, and who coordinates invariants that cross boundaries. Service names should describe those responsibilities, not merely repeat the application name.
|
|
8
|
+
|
|
9
|
+
For example, a commerce backend could use:
|
|
10
|
+
|
|
11
|
+
| Service | Owns | Public contract |
|
|
12
|
+
| --------------- | ----------------------------------------- | ------------------------------------ |
|
|
13
|
+
| `orders` | Order lifecycle and accepted order totals | Place/cancel order, `orders.created` |
|
|
14
|
+
| `pricing` | Price calculation and discount rules | Calculate a quote |
|
|
15
|
+
| `notifications` | Notification delivery and deduplication | React to an order event |
|
|
16
|
+
|
|
17
|
+
This is an example, not a required taxonomy. Related entities such as cars and car models may belong to one fleet service. Split by distinct rules and lifecycles, not by table count. One coherent capability can remain one service. A shared database, auth service, or tenant identifier is infrastructure reuse, not a reason to collect all business operations under one service.
|
|
18
|
+
|
|
19
|
+
### Public collaboration
|
|
20
|
+
|
|
21
|
+
Each service selects its own operations; another service must not act as a barrel for its neighbor's internals:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// src/services/pricing/service.ts
|
|
25
|
+
import { defineService } from "@relkit/app/services";
|
|
26
|
+
import calculateQuote from "./functions/calculate-quote.function.js";
|
|
27
|
+
|
|
28
|
+
export default defineService({ functions: { calculateQuote } });
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
An orders function can call the public pricing contract. For a pure calculation that needs no provider state:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import pricing from "@app/services/pricing/service.js";
|
|
35
|
+
|
|
36
|
+
// Inside an orders function handler:
|
|
37
|
+
const quote = await pricing.calculateQuote.invoke({ sku, quantity });
|
|
38
|
+
// Apply the order's acceptance rules to the validated quote.
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Use the installed declarations for the calculation's actual input/output. Do not import `pricing/functions/*`, call a foreign handler, or expose the same function as a member of two services. A route for orders targets the orders service; separate URLs alone do not establish domain boundaries.
|
|
42
|
+
|
|
43
|
+
For an asynchronous reaction, orders owns and exposes `orders.created`; a consumer under `src/services/notifications/functions/` uses `defineEventFunction({ id: "notifications.order-created", event: "orders.created", ... })`. Event-only consumers are discovered in their owning domain and are not added to its callable functions map. The event examples below show publication and subscription.
|
|
44
|
+
|
|
45
|
+
### Invariants and transactions
|
|
46
|
+
|
|
47
|
+
Name one owner for a command's consistency rules. For example, accepting an order and reserving stock may require one atomic decision. A service call followed by a second write is not proof of an atomic transaction, and `.invoke()` must not be assumed to inherit the caller's transaction or resources.
|
|
48
|
+
|
|
49
|
+
Inspect installed transaction/invocation behavior before composing provider-backed commands. Choose a supported transaction coordinator or keep tightly coupled invariants within one cohesive owner. Define narrow public operations for other domains; do not duplicate another domain's rules or silently write its owned state from arbitrary helpers. Use events for reactions that can occur after commit, with an outbox when publication must survive a crash. Events alone do not preserve synchronous atomicity.
|
|
50
|
+
|
|
51
|
+
Keep one shared database service and its ordered migration history when the app uses one database. Do not create a database, auth service, or migration history per business domain.
|
|
52
|
+
|
|
53
|
+
Changing an existing operation's domain ownership is a redesign, not just a grouped/flat layout move. Review IDs, durable versions, policies, callers, and stored history explicitly; do not promise that a folder move between domains preserves identity.
|
|
54
|
+
|
|
55
|
+
### Review the resulting boundaries
|
|
56
|
+
|
|
57
|
+
Compare the actual service graph and routes with the ownership map. Check that each planned capability has real operations under its owner, imports use public service boundaries, business rules live with their owner, and no catch-all service still contains the implementation behind empty facades. Review any merged or split responsibility and verify cross-domain failure/rollback behavior. A passing graph check proves structural validity, not semantic cohesion.
|
|
58
|
+
|
|
5
59
|
## `defineFunction` and `defineService`
|
|
6
60
|
|
|
7
61
|
Put domain behavior in `src/services/<domain>/functions/*.function.ts`. A function validates input before its handler and validates the returned output:
|
|
@@ -31,6 +85,8 @@ export default defineService({ functions: { greet } });
|
|
|
31
85
|
|
|
32
86
|
The service also accepts `events`, `tasks`, and `jobs` maps. Its members appear directly as `service.greet`; there is no `service.functions.greet` namespace. Membership makes a function available across domains, but does not create an HTTP route or grant authorization.
|
|
33
87
|
|
|
88
|
+
Each `service.ts` constructs and exports exactly one runtime service value. Keep ordinary helpers and error descriptors inside their owning domain; expose callable operations through the service's `functions` map. Do not re-export helpers or descriptors from a specialized database or auth `service.ts`.
|
|
89
|
+
|
|
34
90
|
Within a domain, import a sibling function directly. Across domains and from routes, import the other domain's `service.ts`. Invoke a function descriptor with `await service.greet.invoke(input)`; do not call its handler directly. Leave an internal function out of the service map. Tools, agents, caches, buckets, and event-only functions are not service members.
|
|
35
91
|
|
|
36
92
|
## `defineEvent` and `defineEventFunction`
|
|
@@ -60,13 +116,13 @@ export default orderCreated;
|
|
|
60
116
|
|
|
61
117
|
Expose it through `defineService({ events: { orderCreated } })` when other domains need it. A publisher declares the exact ID in `publishes: ["orders.created"]` and calls `await context.events["orders.created"].publish({ orderId })`.
|
|
62
118
|
|
|
63
|
-
An event-only consumer reacts without returning a result to its publisher:
|
|
119
|
+
An event-only consumer in `src/services/notifications/functions/` reacts without returning a result to its publisher:
|
|
64
120
|
|
|
65
121
|
```ts
|
|
66
122
|
import { defineEventFunction } from "@relkit/app/events";
|
|
67
123
|
|
|
68
124
|
export default defineEventFunction({
|
|
69
|
-
id: "
|
|
125
|
+
id: "notifications.order-created",
|
|
70
126
|
event: "orders.created",
|
|
71
127
|
handler: async ({ orderId }, context) => {
|
|
72
128
|
context.log.info("Order created", { orderId });
|
|
@@ -89,4 +145,21 @@ export default defineError({
|
|
|
89
145
|
});
|
|
90
146
|
```
|
|
91
147
|
|
|
92
|
-
Declare only expected failures callers should handle on a function. Use the route layer for HTTP-specific request mapping and middleware.
|
|
148
|
+
Declare only expected failures callers should handle on a function. Return each declared error through the public `fail` helper so it appears in the inferred handler result; throwing a declared error from an ordinary helper does not satisfy that contract. Use the route layer for HTTP-specific request mapping and middleware.
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { defineFunction, fail } from "@relkit/app/functions";
|
|
152
|
+
import { z } from "@relkit/app/schema";
|
|
153
|
+
import AuthenticationRequired from "../errors/authentication-required.error.js";
|
|
154
|
+
|
|
155
|
+
export default defineFunction({
|
|
156
|
+
input: z.object({}),
|
|
157
|
+
output: z.object({ id: z.string() }),
|
|
158
|
+
errors: [AuthenticationRequired],
|
|
159
|
+
handler: async (_input, context) => {
|
|
160
|
+
const session = await context.auth.getSession();
|
|
161
|
+
if (session === null) return fail(AuthenticationRequired, {});
|
|
162
|
+
return { id: session.user.id };
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
```
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
"jobs:trigger": "relkit jobs trigger --job exportOrders --input-file examples/export-orders.json --operation-id example-trigger"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@relkit/app": "0.7.
|
|
16
|
+
"@relkit/app": "0.7.9",
|
|
17
17
|
"effect": "4.0.1"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@types/bun": "1.3.10",
|
|
21
|
-
"@relkit/cli": "0.7.
|
|
22
|
-
"@relkit/testing": "0.7.
|
|
21
|
+
"@relkit/cli": "0.7.9",
|
|
22
|
+
"@relkit/testing": "0.7.9",
|
|
23
23
|
"typescript": "5.9.3"
|
|
24
24
|
}
|
|
25
25
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-relkit",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.9",
|
|
4
4
|
"description": "Create a RELKIT application from a supported project template.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@clack/prompts": "1.7.0",
|
|
47
|
-
"@relkit/compiler": "0.7.
|
|
47
|
+
"@relkit/compiler": "0.7.9",
|
|
48
48
|
"typescript": "5.9.3",
|
|
49
49
|
"effect": "4.0.1",
|
|
50
|
-
"@relkit/contracts": "0.7.
|
|
50
|
+
"@relkit/contracts": "0.7.9"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@effect/vitest": "4.0.1",
|