create-relkit 0.7.4 → 0.7.6
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.d.ts +24 -33
- package/dist/create-capabilities.d.ts.map +1 -1
- package/dist/create-capabilities.js +52 -9
- package/dist/create-capabilities.js.map +1 -1
- package/dist/create-capabilities.types.d.ts +55 -0
- package/dist/create-capabilities.types.d.ts.map +1 -0
- package/dist/create-capabilities.types.js +2 -0
- package/dist/create-capabilities.types.js.map +1 -0
- package/dist/domain-artifact.d.ts.map +1 -1
- package/dist/domain-artifact.js +11 -3
- package/dist/domain-artifact.js.map +1 -1
- package/dist/domain-planning.d.ts +5 -0
- package/dist/domain-planning.d.ts.map +1 -1
- package/dist/domain-planning.js +25 -16
- package/dist/domain-planning.js.map +1 -1
- package/dist/generate-deployment-manifest.d.ts +10 -0
- package/dist/generate-deployment-manifest.d.ts.map +1 -0
- package/dist/generate-deployment-manifest.js +56 -0
- package/dist/generate-deployment-manifest.js.map +1 -0
- package/dist/generate-deployment.d.ts +2 -3
- package/dist/generate-deployment.d.ts.map +1 -1
- package/dist/generate-deployment.js +6 -47
- package/dist/generate-deployment.js.map +1 -1
- package/dist/generate-examples.d.ts +11 -0
- package/dist/generate-examples.d.ts.map +1 -0
- package/dist/generate-examples.js +28 -0
- package/dist/generate-examples.js.map +1 -0
- package/dist/generate-files-utilities.d.ts +1 -8
- package/dist/generate-files-utilities.d.ts.map +1 -1
- package/dist/generate-files-utilities.js +5 -19
- package/dist/generate-files-utilities.js.map +1 -1
- package/dist/project-discovery-artifacts.d.ts +15 -0
- package/dist/project-discovery-artifacts.d.ts.map +1 -0
- package/dist/project-discovery-artifacts.js +107 -0
- package/dist/project-discovery-artifacts.js.map +1 -0
- package/dist/project-discovery-artifacts.types.d.ts +22 -0
- package/dist/project-discovery-artifacts.types.d.ts.map +1 -0
- package/dist/project-discovery-artifacts.types.js +2 -0
- package/dist/project-discovery-artifacts.types.js.map +1 -0
- package/dist/project-discovery-facts.d.ts +8 -9
- package/dist/project-discovery-facts.d.ts.map +1 -1
- package/dist/project-discovery-facts.js +29 -135
- package/dist/project-discovery-facts.js.map +1 -1
- package/dist/project-discovery-members.d.ts +16 -0
- package/dist/project-discovery-members.d.ts.map +1 -0
- package/dist/project-discovery-members.js +57 -0
- package/dist/project-discovery-members.js.map +1 -0
- package/dist/project-discovery-types.d.ts +8 -0
- package/dist/project-discovery-types.d.ts.map +1 -1
- package/dist/render-auth-database.d.ts +33 -0
- package/dist/render-auth-database.d.ts.map +1 -0
- package/dist/render-auth-database.js +69 -0
- package/dist/render-auth-database.js.map +1 -0
- package/dist/render-auth-sources.d.ts +8 -2
- package/dist/render-auth-sources.d.ts.map +1 -1
- package/dist/render-auth-sources.js +9 -3
- package/dist/render-auth-sources.js.map +1 -1
- package/dist/render-auth.d.ts +14 -11
- package/dist/render-auth.d.ts.map +1 -1
- package/dist/render-auth.js +41 -108
- package/dist/render-auth.js.map +1 -1
- package/dist/render-database-sources.d.ts +7 -1
- package/dist/render-database-sources.d.ts.map +1 -1
- package/dist/render-database-sources.js +3 -2
- package/dist/render-database-sources.js.map +1 -1
- package/dist/render-database.d.ts +7 -10
- package/dist/render-database.d.ts.map +1 -1
- package/dist/render-database.js +12 -4
- package/dist/render-database.js.map +1 -1
- package/dist/render-database.types.d.ts +15 -0
- package/dist/render-database.types.d.ts.map +1 -0
- package/dist/render-database.types.js +2 -0
- package/dist/render-database.types.js.map +1 -0
- package/dist/render-route-sources.d.ts +3 -1
- package/dist/render-route-sources.d.ts.map +1 -1
- package/dist/render-route-sources.js +4 -2
- package/dist/render-route-sources.js.map +1 -1
- package/dist/render-routes.d.ts +1 -0
- package/dist/render-routes.d.ts.map +1 -1
- package/dist/render-routes.js +2 -1
- package/dist/render-routes.js.map +1 -1
- package/dist/render-service-members.d.ts +32 -0
- package/dist/render-service-members.d.ts.map +1 -0
- package/dist/render-service-members.js +61 -0
- package/dist/render-service-members.js.map +1 -0
- package/dist/render-service-requests.d.ts +35 -0
- package/dist/render-service-requests.d.ts.map +1 -0
- package/dist/render-service-requests.js +62 -0
- package/dist/render-service-requests.js.map +1 -0
- package/dist/render-service-support.d.ts +37 -0
- package/dist/render-service-support.d.ts.map +1 -0
- package/dist/render-service-support.js +74 -0
- package/dist/render-service-support.js.map +1 -0
- package/dist/render-service.d.ts +16 -13
- package/dist/render-service.d.ts.map +1 -1
- package/dist/render-service.js +33 -149
- package/dist/render-service.js.map +1 -1
- package/dist/render-service.types.d.ts +13 -0
- package/dist/render-service.types.d.ts.map +1 -0
- package/dist/render-service.types.js +2 -0
- package/dist/render-service.types.js.map +1 -0
- package/dist/scaffold-layout.d.ts +10 -0
- package/dist/scaffold-layout.d.ts.map +1 -0
- package/dist/scaffold-layout.js +29 -0
- package/dist/scaffold-layout.js.map +1 -0
- package/dist/templates/default/README.md +1 -1
- package/dist/templates/default/v1/agent/.agents/skills/relkit/SKILL.md +48 -0
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/agent-apis.md +59 -0
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/cli.md +63 -0
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/config-environments.md +96 -0
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/data-apis.md +74 -0
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/domain-apis.md +92 -0
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/http-apis.md +70 -0
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/jobs-apis.md +49 -0
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/layout-migration.md +35 -0
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/observability-apis.md +53 -0
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/realtime-apis.md +98 -0
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/streaming-apis.md +75 -0
- package/dist/templates/default/v1/agent/.agents/skills/relkit/references/testing-apis.md +71 -0
- package/dist/templates/default/v1/agent/README.md +9 -0
- package/dist/templates/default/v1/agent/package.json +4 -4
- package/dist/templates/default/v1/agent/src/routes/hello/route.ts +2 -1
- package/dist/templates/default/v1/agent/src/{hello → services/hello}/agents/assistant.agent.ts +2 -1
- package/dist/templates/default/v1/agent/src/{hello → services/hello}/agents/review.agent.ts +1 -0
- package/dist/templates/default/v1/agent/src/{hello → services/hello}/functions/hello.function.ts +7 -0
- package/dist/templates/default/v1/agent/src/services/hello/service.ts +5 -0
- package/dist/templates/default/v1/agent/src/{hello → services/hello}/tools/lookup.tool.ts +2 -1
- package/dist/templates/default/v1/agent/tests/fixtures/lookup.tool.ts +2 -1
- package/dist/templates/default/v1/agent/tests/fixtures/mcp-options.ts +2 -1
- package/dist/templates/default/v1/agent/tests/unit/assistant.agent.test.ts +3 -2
- package/dist/templates/default/v1/agent/tests/unit/hello.function.test.ts +2 -1
- package/dist/templates/default/v1/agent/tests/unit/lookup.tool.test.ts +2 -1
- package/dist/templates/default/v1/agent/tests/unit/review.agent.test.ts +2 -1
- package/dist/templates/default/v1/agent/tsconfig.json +1 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/SKILL.md +48 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/agent-apis.md +59 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/cli.md +63 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/config-environments.md +96 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/data-apis.md +74 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/domain-apis.md +92 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/http-apis.md +70 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/jobs-apis.md +49 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/layout-migration.md +35 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/observability-apis.md +53 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/realtime-apis.md +98 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/streaming-apis.md +75 -0
- package/dist/templates/default/v1/api/.agents/skills/relkit/references/testing-apis.md +71 -0
- package/dist/templates/default/v1/api/README.md +10 -0
- package/dist/templates/default/v1/api/package.json +3 -3
- package/dist/templates/default/v1/api/src/routes/echo/route.ts +2 -1
- package/dist/templates/default/v1/api/src/routes/hello/route.ts +2 -1
- package/dist/templates/default/v1/api/src/routes/orders/route.ts +2 -1
- package/dist/templates/default/v1/api/src/{echo → services/echo}/functions/echo.function.ts +1 -0
- package/dist/templates/default/v1/api/src/services/echo/service.ts +5 -0
- package/dist/templates/default/v1/api/src/{hello → services/hello}/functions/hello.function.ts +7 -0
- package/dist/templates/default/v1/api/src/services/hello/service.ts +5 -0
- package/dist/templates/default/v1/api/src/{orders → services/orders}/functions/create-order.function.ts +2 -1
- package/dist/templates/default/v1/api/src/{orders → services/orders}/functions/price-order.function.ts +1 -0
- package/dist/templates/default/v1/api/src/services/orders/service.ts +8 -0
- package/dist/templates/default/v1/api/tests/unit/echo.function.test.ts +2 -1
- package/dist/templates/default/v1/api/tests/unit/hello.function.test.ts +2 -1
- package/dist/templates/default/v1/api/tests/unit/orders.function.test.ts +2 -1
- package/dist/templates/default/v1/api/tsconfig.json +1 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/SKILL.md +48 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/agent-apis.md +59 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/cli.md +63 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/config-environments.md +96 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/data-apis.md +74 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/domain-apis.md +92 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/http-apis.md +70 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/jobs-apis.md +49 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/layout-migration.md +35 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/observability-apis.md +53 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/realtime-apis.md +98 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/streaming-apis.md +75 -0
- package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/testing-apis.md +71 -0
- package/dist/templates/default/v1/fullstack/README.md +9 -0
- package/dist/templates/default/v1/fullstack/package.json +5 -5
- package/dist/templates/default/v1/fullstack/src/routes/hello/route.ts +2 -1
- package/dist/templates/default/v1/fullstack/src/{hello → services/hello}/agents/assistant.agent.ts +2 -1
- package/dist/templates/default/v1/fullstack/src/{hello → services/hello}/agents/review.agent.ts +1 -0
- package/dist/templates/default/v1/fullstack/src/{hello → services/hello}/functions/hello.function.ts +1 -0
- package/dist/templates/default/v1/fullstack/src/services/hello/service.ts +5 -0
- package/dist/templates/default/v1/fullstack/src/{hello → services/hello}/tools/lookup.tool.ts +2 -1
- package/dist/templates/default/v1/fullstack/tests/unit/assistant.agent.test.ts +3 -2
- package/dist/templates/default/v1/fullstack/tests/unit/hello.function.test.ts +2 -1
- package/dist/templates/default/v1/fullstack/tests/unit/review.agent.test.ts +2 -1
- package/dist/templates/default/v1/fullstack/tsconfig.json +1 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/SKILL.md +48 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/agent-apis.md +59 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/cli.md +63 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/config-environments.md +96 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/data-apis.md +74 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/domain-apis.md +92 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/http-apis.md +70 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/jobs-apis.md +49 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/layout-migration.md +35 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/observability-apis.md +53 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/realtime-apis.md +98 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/streaming-apis.md +75 -0
- package/dist/templates/default/v1/minimal/.agents/skills/relkit/references/testing-apis.md +71 -0
- package/dist/templates/default/v1/minimal/README.md +10 -0
- package/dist/templates/default/v1/minimal/package.json +3 -3
- package/dist/templates/default/v1/minimal/src/routes/hello/route.ts +2 -1
- package/dist/templates/default/v1/minimal/src/{hello → services/hello}/functions/hello.function.ts +7 -0
- package/dist/templates/default/v1/minimal/src/services/hello/service.ts +5 -0
- package/dist/templates/default/v1/minimal/tests/unit/hello.function.test.ts +2 -1
- package/dist/templates/default/v1/minimal/tsconfig.json +1 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/SKILL.md +48 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/agent-apis.md +59 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/cli.md +63 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/config-environments.md +96 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/data-apis.md +74 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/domain-apis.md +92 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/http-apis.md +70 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/jobs-apis.md +49 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/layout-migration.md +35 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/observability-apis.md +53 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/realtime-apis.md +98 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/streaming-apis.md +75 -0
- package/dist/templates/default/v1/tasks/.agents/skills/relkit/references/testing-apis.md +71 -0
- package/dist/templates/default/v1/tasks/README.md +8 -1
- package/dist/templates/default/v1/tasks/package.json +3 -3
- package/dist/templates/default/v1/tasks/src/routes/health/route.ts +2 -1
- package/dist/templates/default/v1/tasks/src/{orders → services/orders}/functions/health.function.ts +1 -0
- package/dist/templates/default/v1/tasks/src/{orders → services/orders}/jobs/export-orders.job.ts +2 -1
- package/dist/templates/default/v1/tasks/src/services/orders/service.ts +11 -0
- package/dist/templates/default/v1/tasks/src/{orders → services/orders}/tasks/export-orders.task.ts +1 -0
- package/dist/templates/default/v1/tasks/tests/unit/export-orders.task.test.ts +2 -1
- package/dist/templates/default/v1/tasks/tsconfig.json +2 -0
- package/package.json +3 -3
- package/dist/templates/default/v1/agent/src/hello/service.ts +0 -4
- package/dist/templates/default/v1/api/src/echo/service.ts +0 -4
- package/dist/templates/default/v1/api/src/hello/service.ts +0 -4
- package/dist/templates/default/v1/api/src/orders/service.ts +0 -7
- package/dist/templates/default/v1/fullstack/src/hello/service.ts +0 -4
- package/dist/templates/default/v1/minimal/src/hello/service.ts +0 -4
- package/dist/templates/default/v1/tasks/src/orders/service.ts +0 -10
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Functions, services, events, and errors
|
|
2
|
+
|
|
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
|
+
|
|
5
|
+
## `defineFunction` and `defineService`
|
|
6
|
+
|
|
7
|
+
Put domain behavior in `src/services/<domain>/functions/*.function.ts`. A function validates input before its handler and validates the returned output:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { defineFunction } from "@relkit/app/functions";
|
|
11
|
+
import { z } from "@relkit/app/schema";
|
|
12
|
+
|
|
13
|
+
const greet = defineFunction({
|
|
14
|
+
id: "hello.greet",
|
|
15
|
+
input: z.object({ name: z.string().min(1) }),
|
|
16
|
+
output: z.object({ message: z.string() }),
|
|
17
|
+
handler: async ({ name }) => ({ message: `Hello, ${name}!` }),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export default greet;
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Expose selected functions from `src/services/<domain>/service.ts`:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { defineService } from "@relkit/app/services";
|
|
27
|
+
import greet from "@app/services/hello/functions/greet.function.js";
|
|
28
|
+
|
|
29
|
+
export default defineService({ functions: { greet } });
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
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
|
+
|
|
34
|
+
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
|
+
|
|
36
|
+
## `defineEvent` and `defineEventFunction`
|
|
37
|
+
|
|
38
|
+
Declare an event payload in `src/services/<domain>/events/*.event.ts`:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { defineEvent } from "@relkit/app/events";
|
|
42
|
+
import { z } from "@relkit/app/schema";
|
|
43
|
+
|
|
44
|
+
const orderCreated = defineEvent({
|
|
45
|
+
id: "orders.created",
|
|
46
|
+
version: 1,
|
|
47
|
+
input: z.object({ orderId: z.string() }),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
declare global {
|
|
51
|
+
namespace Relkit {
|
|
52
|
+
interface EventRegistry {
|
|
53
|
+
readonly "orders.created": typeof orderCreated;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default orderCreated;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
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
|
+
|
|
63
|
+
An event-only consumer reacts without returning a result to its publisher:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { defineEventFunction } from "@relkit/app/events";
|
|
67
|
+
|
|
68
|
+
export default defineEventFunction({
|
|
69
|
+
id: "orders.confirmation",
|
|
70
|
+
event: "orders.created",
|
|
71
|
+
handler: async ({ orderId }, context) => {
|
|
72
|
+
context.log.info("Order created", { orderId });
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## `defineError`
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { defineError } from "@relkit/app/functions";
|
|
81
|
+
import { z } from "@relkit/app/schema";
|
|
82
|
+
|
|
83
|
+
export default defineError({
|
|
84
|
+
id: "orders.not-found",
|
|
85
|
+
data: z.object({ orderId: z.string() }),
|
|
86
|
+
message: ({ orderId }) => `Order ${orderId} was not found`,
|
|
87
|
+
http: { status: 404 },
|
|
88
|
+
retry: { kind: "later", afterMs: 1_000 },
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Declare only expected failures callers should handle on a function. Use the route layer for HTTP-specific request mapping and middleware.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# HTTP routes and request policy
|
|
2
|
+
|
|
3
|
+
Routes live under `src/routes/**/route.ts`. The file path supplies the URL, and named exports supply methods. Import route APIs from `@relkit/app/routes`. Use a public service function as the target for ordinary application endpoints.
|
|
4
|
+
|
|
5
|
+
## `defineRoute`: one function-backed method
|
|
6
|
+
|
|
7
|
+
In `src/routes/hello/route.ts`, this exposes the service's public `hello` function as `GET /hello`:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { defineRoute } from "@relkit/app/routes";
|
|
11
|
+
import hello from "@app/services/hello/service.js";
|
|
12
|
+
|
|
13
|
+
export const GET = defineRoute({ target: hello.hello });
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
A function-backed route can also set `successStatus`, `request`, `responses`, `rateLimit`, and `timeoutMs` when needed. The target must be a public callable function; jobs, events, agents, and tools are not route targets. Ordinary JSON request and response schemas are inferred from the target unless you supply explicit mapping.
|
|
17
|
+
|
|
18
|
+
## `defineRoute`: raw Web handler
|
|
19
|
+
|
|
20
|
+
Use `handler` instead of `target` for a standalone Web `Request`/`Response` handler:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { defineRoute } from "@relkit/app/routes";
|
|
24
|
+
|
|
25
|
+
export const GET = defineRoute({
|
|
26
|
+
handler: async () => Response.json({ ok: true }),
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
A raw handler has a generic OpenAPI response. The recognized Better Auth mount also uses a raw route:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { defineRoute } from "@relkit/app/routes";
|
|
34
|
+
import auth from "@app/services/auth/service.js";
|
|
35
|
+
|
|
36
|
+
export const ALL = defineRoute({ handler: auth.handler });
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## `defineServiceRoutes`: methods on one service
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { defineServiceRoutes } from "@relkit/app/routes";
|
|
43
|
+
import orders from "@app/services/orders/service.js";
|
|
44
|
+
|
|
45
|
+
export const { GET, POST } = defineServiceRoutes(orders, {
|
|
46
|
+
GET: "listOrders",
|
|
47
|
+
POST: { member: "createOrder", successStatus: 201 },
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, and `OPTIONS` are supported service-route exports. `ALL` is for raw handlers. Do not add a `path` or `method` option to `defineRoute`; the filesystem and export determine both. The CLI can scaffold either form through `add route --mode route` or `--mode service-route`. It will not overwrite an existing route file; edit it directly to add a method.
|
|
52
|
+
|
|
53
|
+
Use `request` mapping only when inference cannot express headers, cookies, names, or nesting; it replaces the inferred mapping. Use `responses` only when you need an explicit response contract. `defineMiddleware(path, handler)` is for HTTP policy, and `defineTransform({ schema })` is for request transforms:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { defineMiddleware } from "@relkit/app/routes";
|
|
57
|
+
|
|
58
|
+
export default defineMiddleware("/orders/*", async (_context, next) => {
|
|
59
|
+
await next();
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { defineTransform } from "@relkit/app/routes";
|
|
65
|
+
import { z } from "@relkit/app/schema";
|
|
66
|
+
|
|
67
|
+
export default defineTransform({ schema: z.string() });
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Application authorization must be enforced in the relevant route or function logic. After changing a route, run `bun run check` and `bun run typecheck`, exercise the HTTP endpoint, and inspect `/_relkit/v1/api-reference` while `bun run dev` is running.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Tasks and jobs
|
|
2
|
+
|
|
3
|
+
Use a task for work that must outlive its initiating request; a function remains a request- or event-scoped operation.
|
|
4
|
+
|
|
5
|
+
## `defineTask`
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { defineTask } from "@relkit/app/tasks";
|
|
9
|
+
import { z } from "@relkit/app/schema";
|
|
10
|
+
|
|
11
|
+
export default defineTask({
|
|
12
|
+
id: "orders.export-orders",
|
|
13
|
+
version: "1",
|
|
14
|
+
execution: "durable",
|
|
15
|
+
input: z.object({ orderIds: z.array(z.string()) }),
|
|
16
|
+
output: z.object({ exported: z.number().int().nonnegative() }),
|
|
17
|
+
progress: z.object({ completed: z.number().int().nonnegative() }),
|
|
18
|
+
handler: async ({ orderIds }, context) => {
|
|
19
|
+
await context.progress.emit({ completed: orderIds.length });
|
|
20
|
+
return { exported: orderIds.length };
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## `defineJob`
|
|
26
|
+
|
|
27
|
+
Bind the task to a public job name and an explicit client policy in `src/services/orders/jobs/export-orders.job.ts`:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { defineJob } from "@relkit/app/jobs";
|
|
31
|
+
import exportOrders from "@app/services/orders/tasks/export-orders.task.js";
|
|
32
|
+
|
|
33
|
+
export default defineJob({
|
|
34
|
+
id: "orders.export-orders-job",
|
|
35
|
+
name: "exportOrders",
|
|
36
|
+
task: exportOrders,
|
|
37
|
+
client: {
|
|
38
|
+
public: true,
|
|
39
|
+
operations: ["trigger", "get", "list", "watch"],
|
|
40
|
+
fields: ["status", "progress", "output"],
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Expose the task and job through the owning service's `tasks` and `jobs` maps. Keep the task ID and version stable; change the version for an intentional incompatible task contract. Make side effects idempotent because work may retry or resume.
|
|
46
|
+
|
|
47
|
+
The app's `relkit.config.ts` selects a compatible jobs provider. Run `bun run relkit jobs capabilities` to inspect its actual guarantees; a durable task needs a binding that supports durable execution. `relkit add task` and `relkit add job --target <task-id>` scaffold the pair without running a worker or provisioning a provider.
|
|
48
|
+
|
|
49
|
+
A trigger admits work and returns a run handle, not the task's output. Keep its `runId` and `operationId`, then read or watch the run; a typed client alone does not authorize a caller. A raw HTTP trigger can return the accepted handle with status `202`. Use `bun run relkit local status` to inspect local provider state before exercising a Docker-backed job.
|
package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/layout-migration.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Optional flat layout migration
|
|
2
|
+
|
|
3
|
+
Flat `src/<name>/service.ts` applications remain supported. Migrate only when grouped placement helps the app. Preview from the application root:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
bun run relkit migrate layout
|
|
7
|
+
bun run relkit --json migrate layout
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
The deterministic plan moves complete service roots to `src/services/<name>`, including helpers and assets. It reports import, test, tooling, documentation, and local guidance rewrites, conflicts, and references needing manual action. Preview leaves authored files, lockfiles, generated output, persistent state, migration history, and credentials unchanged.
|
|
11
|
+
|
|
12
|
+
Stop the app's active development session and resolve touched tracked changes, occupied destinations, mixed boundaries, symlinks, and ambiguous/computed references before applying:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
bun run relkit migrate layout --write
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Write stages the candidate with installed dependencies and requires baseline/candidate check, typecheck, logical graph, and offline deployment compatibility before publication. It does not install packages, run database migrations or seeds, provision providers, or edit global skills. Publication is coordinated across files; the restricted local receipt retains exact before/after bytes and permissions for failure or crash recovery. Keep that receipt private.
|
|
19
|
+
|
|
20
|
+
Rollback with the exact path returned by the write result:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
bun run relkit migrate layout --rollback .relkit/migrations/RECEIPT_ID/receipt.json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Rollback refuses later edits or occupied original locations instead of overwriting them. Recovery conflicts preserve user bytes and require inspection of the retained receipt. Successful migration or restoration needs fresh preparation:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
bun run check
|
|
30
|
+
bun run typecheck
|
|
31
|
+
bun run relkit dev --prepare
|
|
32
|
+
bun run dev
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Compare actual application responses and durable runs after the move. Logical service/function/task/job/channel/agent IDs and versions remain unchanged; diagnostic and editor paths point at their real grouped sources.
|
package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/observability-apis.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Observability and tracing
|
|
2
|
+
|
|
3
|
+
RELKIT records HTTP requests and invocation spans automatically. Use `context.log` for structured application records and `context.trace` for application work inside an existing function that needs more detail.
|
|
4
|
+
|
|
5
|
+
## Log and trace inside a function
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { defineFunction } from "@relkit/app/functions";
|
|
9
|
+
import { z } from "@relkit/app/schema";
|
|
10
|
+
|
|
11
|
+
export default defineFunction({
|
|
12
|
+
id: "orders.total",
|
|
13
|
+
input: z.object({ quantity: z.number().int().positive() }),
|
|
14
|
+
output: z.object({ totalCents: z.number().int().nonnegative() }),
|
|
15
|
+
handler: async ({ quantity }, context) => {
|
|
16
|
+
const totalCents = await context.trace.span(
|
|
17
|
+
"pricing.calculate",
|
|
18
|
+
{ attributes: { "pricing.strategy": "catalog" } },
|
|
19
|
+
async () => quantity * 1_000,
|
|
20
|
+
);
|
|
21
|
+
context.trace.event("pricing.calculated", { "pricing.total_cents": totalCents });
|
|
22
|
+
context.trace.setAttributes({ "pricing.quantity": quantity });
|
|
23
|
+
context.log.info("orders.total.calculated", { quantity, totalCents });
|
|
24
|
+
return { totalCents };
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`span(name, callback)` creates an internal child span; `span(name, { attributes, kind }, callback)` adds attributes and an `internal` or `client` kind. `event(name, attributes?)` adds an event to the active span, and `setAttributes` annotates it. RELKIT ends child spans on success or failure. Attribute values must be strings, finite numbers, or booleans. Do not log secrets or put payloads or random IDs in span attributes.
|
|
30
|
+
|
|
31
|
+
## Configure capture and export
|
|
32
|
+
|
|
33
|
+
Edit the existing `relkit.config.ts` and preserve its environment and provider settings:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { defineApp } from "@relkit/app/config";
|
|
37
|
+
import env from "@app/platform/env.js";
|
|
38
|
+
|
|
39
|
+
export default defineApp({
|
|
40
|
+
env,
|
|
41
|
+
telemetry: {
|
|
42
|
+
redaction: { mode: "development-redacted", maxBytes: 65_536 },
|
|
43
|
+
localRetention: { maxRecords: 2_000, maxAgeMs: 3_600_000, maxBytes: 16_777_216 },
|
|
44
|
+
exportSampling: { traceRate: 0.25, minimumLogLevel: "info" },
|
|
45
|
+
},
|
|
46
|
+
server: { port: 3000 },
|
|
47
|
+
inspector: { port: 3210 },
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The template already supplies redaction. `localRetention` bounds development history; `exportSampling` controls remote export independently of local capture. Add named Sentry or OTLP exporters only when those integrations and their secret environment bindings are configured. Redact before storage or export. No `relkit add observability` kind exists; configure `telemetry` in `defineApp`.
|
|
52
|
+
|
|
53
|
+
Run `bun run dev`, make one identifiable HTTP request, then inspect Requests, Traces, and Logs in the Inspector. Use `bun run dev -- --verbose` for DEBUG terminal output or `--json` for structured terminal records. Development history is stored in `.relkit/observability/observability.duckdb`; this is diagnostic history, not business storage.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Realtime channels
|
|
2
|
+
|
|
3
|
+
Use a channel to notify connected clients about a change. Keep the authoritative state in a route or database query; use channel events to refresh or update that state. Application events deliver backend work, while channels deliver live client updates.
|
|
4
|
+
|
|
5
|
+
## `defineChannel`
|
|
6
|
+
|
|
7
|
+
Create `src/services/announcements/channels/announcements.channel.ts`:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { defineChannel } from "@relkit/app/realtime";
|
|
11
|
+
import { z } from "@relkit/app/schema";
|
|
12
|
+
|
|
13
|
+
const announcements = defineChannel({
|
|
14
|
+
id: "announcements.feed",
|
|
15
|
+
params: z.object({}),
|
|
16
|
+
events: { posted: z.object({ message: z.string(), postedAt: z.string() }) },
|
|
17
|
+
client: { public: true },
|
|
18
|
+
replay: { retentionMs: 300_000, maxEvents: 1_000 },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export default announcements;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`params` selects a partition, `events` validates named payloads, and `client` controls browser exposure. Omit `client` for backend-only channels. Use `client: { authorize: async (params, context) => ... }` for protected partitions and verify access to the specific resource, not merely that a session exists. `presence: "count"` adds connection counts. A channel with no events must declare presence.
|
|
25
|
+
|
|
26
|
+
## Publish and subscribe
|
|
27
|
+
|
|
28
|
+
After the application change succeeds, publish one validated event from a backend function:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { defineFunction } from "@relkit/app/functions";
|
|
32
|
+
import { z } from "@relkit/app/schema";
|
|
33
|
+
import announcements from "@app/services/announcements/channels/announcements.channel.js";
|
|
34
|
+
|
|
35
|
+
export default defineFunction({
|
|
36
|
+
id: "announcements.post",
|
|
37
|
+
input: z.object({ message: z.string().min(1) }),
|
|
38
|
+
output: z.object({ accepted: z.literal(true) }),
|
|
39
|
+
handler: async ({ message }) => {
|
|
40
|
+
await announcements.trigger({}, "posted", {
|
|
41
|
+
message,
|
|
42
|
+
postedAt: new Date().toISOString(),
|
|
43
|
+
});
|
|
44
|
+
return { accepted: true as const };
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The `trigger()` receipt confirms the provider accepted the append; it does not prove delivery to every browser. If a database write and publication must be atomic, use an outbox in the database transaction. A retryable publish can pass a RELKIT-issued `idempotencyKey` as the fourth argument.
|
|
50
|
+
|
|
51
|
+
In a React client with `@relkit/client`, subscribe using the generated channel registry:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
"use client";
|
|
55
|
+
|
|
56
|
+
import { useChannel, useRoute } from "@relkit/client/react";
|
|
57
|
+
|
|
58
|
+
export default function Announcements() {
|
|
59
|
+
const announcements = useRoute("GET /announcements", { input: {} });
|
|
60
|
+
const channel = useChannel("announcements.feed", {
|
|
61
|
+
params: {},
|
|
62
|
+
on: { posted: () => void announcements.refetch() },
|
|
63
|
+
onCaughtUp: async () => {
|
|
64
|
+
await announcements.refetch();
|
|
65
|
+
},
|
|
66
|
+
onGap: async () => {
|
|
67
|
+
await announcements.refetch();
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return <p>Channel: {channel.status}</p>;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Load authoritative state through a route before relying on updates. `onGap` must restore it when replay cannot bridge a disconnect. `channel.status` and `channel.caughtUp` report connection and recovery state. `useRealtime()` is the lower-level subscription manager; a channel descriptor's `getPresence(params)` reads presence when declared.
|
|
76
|
+
|
|
77
|
+
## Provider and verification
|
|
78
|
+
|
|
79
|
+
Configure a compatible `realtime` provider and `defaults.realtime` in `relkit.config.ts`. For a one-machine app with `@relkit/local` installed, the relevant configuration is:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { defineApp } from "@relkit/app/config";
|
|
83
|
+
import { localRealtime } from "@relkit/local";
|
|
84
|
+
import env from "@app/platform/env.js";
|
|
85
|
+
|
|
86
|
+
export default defineApp({
|
|
87
|
+
env,
|
|
88
|
+
realtime: localRealtime(),
|
|
89
|
+
defaults: { realtime: "default" },
|
|
90
|
+
telemetry: { redaction: { mode: "development-redacted", maxBytes: 65_536 } },
|
|
91
|
+
server: { port: 3000 },
|
|
92
|
+
inspector: { port: 3210 },
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Merge these fields into an existing `defineApp` while preserving its other providers and settings. Redis supports fanout across application instances. Provider storage, retention, and Redis persistence determine recovery guarantees. Run `bun run check`, `bun run typecheck`, then test publication, browser subscription, reconnect, and gap recovery. Inspect channels in the Inspector while `bun run dev` is running.
|
|
97
|
+
|
|
98
|
+
The current `relkit add` kinds do not include `channel`. Create the descriptor and provider configuration in the app source.
|
package/dist/templates/default/v1/fullstack/.agents/skills/relkit/references/streaming-apis.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Streaming responses
|
|
2
|
+
|
|
3
|
+
Use a typed stream when one request returns a sequence to its caller. It ends with that request and can be cancelled. A realtime channel notifies connected clients across requests; a job watch follows one accepted background run. Agent and job content streams have their own client contracts.
|
|
4
|
+
|
|
5
|
+
## `streamOf` with `defineFunction`
|
|
6
|
+
|
|
7
|
+
Create a function in `src/services/reports/functions/stream-report.function.ts`:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { defineFunction, streamOf } from "@relkit/app/functions";
|
|
11
|
+
import { z } from "@relkit/app/schema";
|
|
12
|
+
|
|
13
|
+
export default defineFunction({
|
|
14
|
+
id: "reports.stream",
|
|
15
|
+
input: z.object({ reportId: z.string().min(1) }),
|
|
16
|
+
output: streamOf(z.object({ completed: z.number().int(), total: z.number().int() })),
|
|
17
|
+
handler: async function* (_input, context) {
|
|
18
|
+
for (let completed = 1; completed <= 10; completed += 1) {
|
|
19
|
+
if (context.signal.aborted) return;
|
|
20
|
+
yield { completed, total: 10 };
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`streamOf(itemSchema)` validates each yielded item. The iterator is lazy and single-consumer; its invocation scope stays open until completion or cancellation. Expose this function from its domain service before mapping it to HTTP.
|
|
27
|
+
|
|
28
|
+
For protected data, read the authenticated session and enforce ownership inside the
|
|
29
|
+
function. Verify an authorized stream through the actual generated HTTP server to
|
|
30
|
+
its completion event, then check other users/tenants and cancellation cleanup.
|
|
31
|
+
Direct test-runtime invocation alone does not verify request context through lazy
|
|
32
|
+
iterator pulls. Keep public descriptors as plain TypeScript.
|
|
33
|
+
|
|
34
|
+
## `defineRoute` for a stream
|
|
35
|
+
|
|
36
|
+
In `src/routes/reports/[reportId]/route.ts`, import the public service member:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { defineRoute, http } from "@relkit/app/routes";
|
|
40
|
+
import reports from "@app/services/reports/service.js";
|
|
41
|
+
|
|
42
|
+
export const GET = defineRoute({
|
|
43
|
+
target: reports.streamReport,
|
|
44
|
+
request: http.input({ reportId: http.path("reportId") }),
|
|
45
|
+
client: { operation: "query" },
|
|
46
|
+
stream: { format: "sse" },
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
A function-backed stream preserves typed items and failures with the managed client. Native SSE sends `relkit-item` events and an explicit completion or sanitized error event. For custom text, byte, or Web stream responses, use a raw `defineRoute({ handler })` and a Web `Response`. A raw response does not provide the managed item contract.
|
|
51
|
+
|
|
52
|
+
## `useStream` in React
|
|
53
|
+
|
|
54
|
+
With `@relkit/client` and a generated route registry:
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
"use client";
|
|
58
|
+
|
|
59
|
+
import { useStream } from "@relkit/client/react";
|
|
60
|
+
|
|
61
|
+
export default function Report() {
|
|
62
|
+
const report = useStream("GET /reports/:reportId");
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<section>
|
|
66
|
+
<button onClick={() => void report.start({ reportId: "daily" })}>Start</button>
|
|
67
|
+
<button onClick={() => report.cancel()}>Cancel</button>
|
|
68
|
+
<p>Status: {report.status}</p>
|
|
69
|
+
<p>Items: {report.items.length}</p>
|
|
70
|
+
</section>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`useStream` also exposes `reset`. Query-classified streams can opt into `autoStart`; mutation streams require an explicit `start`. Cancelling or ending iteration releases upstream work. Test item validation, normal completion, cancellation, and a failure after the first item by exercising the HTTP/client path.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Testing a RELKIT application
|
|
2
|
+
|
|
3
|
+
Generated projects use Bun's test runner and `@relkit/testing`. Start with the existing `tests/unit` and `tests/integration` examples, then test the boundary that owns the behavior. Keep external providers and test state explicit; `RELKIT_ENV=test` alone does not select fakes.
|
|
4
|
+
|
|
5
|
+
## Direct function tests
|
|
6
|
+
|
|
7
|
+
`invokeFunction` runs a function descriptor through RELKIT's input/output validation and handler path without an HTTP server. Import a service's public function through its `service.ts` when testing the public member:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { expect, test } from "bun:test";
|
|
11
|
+
import { invokeFunction } from "@relkit/testing";
|
|
12
|
+
import orders from "@app/services/orders/service.js";
|
|
13
|
+
|
|
14
|
+
test("createOrder calculates its total", async () => {
|
|
15
|
+
await expect(
|
|
16
|
+
invokeFunction(orders.createOrder, { orderId: "order-1", sku: "book", quantity: 3 }),
|
|
17
|
+
).resolves.toEqual({ orderId: "order-1", sku: "book", totalCents: 300 });
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Pass explicit `env`, `context`, `clients`, or other `InvokeFunctionOptions` when the function needs them. Test invalid input and declared errors as well as success. Direct invocation does not test route mapping or middleware.
|
|
22
|
+
|
|
23
|
+
For several invocations that share a deterministic clock or provider state, use `createTestRuntime({ app, env, providers, startTimeMs })` and `runtime.invoke(target, input)`. `runtime.clock.advance(ms)` controls its test clock. Always `await runtime.close()` in `finally` or `afterAll`.
|
|
24
|
+
|
|
25
|
+
## Application and HTTP tests
|
|
26
|
+
|
|
27
|
+
`createTestApplication` discovers the generated project's routes and services. Pass its existing `relkit.config.ts`; `resourceProviders: "fake"` explicitly replaces graph-required resource providers for local tests:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { afterAll, expect, test } from "bun:test";
|
|
31
|
+
import { createTestApplication } from "@relkit/testing";
|
|
32
|
+
import config from "../../relkit.config.js";
|
|
33
|
+
|
|
34
|
+
const app = await createTestApplication(config, { resourceProviders: "fake" });
|
|
35
|
+
afterAll(() => app.close());
|
|
36
|
+
|
|
37
|
+
test("POST /orders maps and validates input", async () => {
|
|
38
|
+
const response = await app.http.request("/orders", {
|
|
39
|
+
method: "POST",
|
|
40
|
+
headers: { "content-type": "application/json" },
|
|
41
|
+
body: JSON.stringify({ orderId: "order-1", sku: "book", quantity: 3 }),
|
|
42
|
+
});
|
|
43
|
+
expect(response.status).toBe(201);
|
|
44
|
+
expect(await response.json()).toEqual({ orderId: "order-1", sku: "book", totalCents: 300 });
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Use `app.http.request`, `get`, or other client methods to assert status, body, and headers. Add requests for invalid input, anonymous callers, and unauthorized resource access where relevant. The harness has `runtime`, `clock`, `fakes`, and `observability` for controlled state and inspection. Options include `env`, `environment`, `bindingValues`, `providers`, and `projectRoot`; use explicit values for test-only configuration. This in-process HTTP boundary does not prove the full production middleware, cookie/origin policy, OpenAPI, or network transport. Verify those against a running backend when relevant.
|
|
49
|
+
|
|
50
|
+
## Provider and feature tests
|
|
51
|
+
|
|
52
|
+
- `createTestCacheFake`, `createTestBucketFake`, `createTestJobFake`, and `createTestEventFake` provide owned, inspectable test dependencies. `createTestFakes` groups native clients. Close any separately created fake after use. Replace every configured provider profile the graph needs, not only the one under direct assertion.
|
|
53
|
+
- `createTestHttpClient` exercises a standalone in-memory HTTP application. Use `createTestApplication` when route discovery and service wiring matter. `createTestRealtimeProvider` and `createTestAgentStateProvider` supply local stateful provider replacements.
|
|
54
|
+
- `createTestModel`, `createTestAgent`, and `assertAgentTrace` support deterministic agent execution. Use scripted responses and inspect tool calls or traces instead of live model calls or sleeps.
|
|
55
|
+
- For jobs, assert behavior beyond the descriptor shape: input/output, accepted run handle, progress, retry or cancellation policy, and idempotency where applicable. Use the test job adapter or fake appropriate to the contract.
|
|
56
|
+
- For database and auth integration, migrate an isolated temporary database before starting the application. Supply both parsed RELKIT environment and any native auth process environment required by the provider; close the app before removing test-owned files. Test public and protected requests with real cookies and separate users. A direct function mock does not verify migrations, sessions, or route protection.
|
|
57
|
+
- For realtime, streaming, and observability, test the exposed route or channel contract and recorded events/spans as applicable. Use a running backend for actual transport, connection, and browser behavior.
|
|
58
|
+
|
|
59
|
+
## Run the generated project checks
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
bun test tests/unit/hello.function.test.ts
|
|
63
|
+
bun run test:unit
|
|
64
|
+
bun run test:integration
|
|
65
|
+
bun run test
|
|
66
|
+
bun run check
|
|
67
|
+
bun run typecheck
|
|
68
|
+
bun run build
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Run only scripts present in the generated `package.json`; the task template may have a different test layout. Keep Docker, external providers, and cloud acceptance as explicit integration gates. The installed `@relkit/testing` declarations and the project's existing tests are the source for exact helper options.
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# my-app
|
|
2
2
|
|
|
3
|
+
This maintained fullstack candidate is exercised internally. Public fullstack creation
|
|
4
|
+
remains disabled until its exact native and readiness certification passes.
|
|
5
|
+
|
|
3
6
|
A Relkit backend and Next.js typed client. The example includes an offline
|
|
4
7
|
native LangChain agent with typed todo/tool state, a LangGraph interrupt and
|
|
5
8
|
resume flow, explicit application-owned thread IDs, and selectable SSE or
|
|
@@ -20,3 +23,9 @@ was skipped, the finite preparation command above validates and prepares it afte
|
|
|
20
23
|
`bun install`. The API and Next.js commands start independently; `bun run dev:api`
|
|
21
24
|
and `bun run dev:web` can also be used separately. Backend readiness does not
|
|
22
25
|
mean the Next.js page or Inspector has finished starting.
|
|
26
|
+
|
|
27
|
+
Backend services live in `src/services/hello/`; backend routes and shared
|
|
28
|
+
configuration stay in `src/routes/` and `src/platform/`. The frontend remains
|
|
29
|
+
under `web/`. Keep backend descriptors and native agent handlers in ordinary
|
|
30
|
+
TypeScript. Existing flat backends remain compatible, with optional migration
|
|
31
|
+
preview through `relkit migrate layout`.
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@langchain/langgraph": "1.4.14",
|
|
18
|
-
"@relkit/app": "0.7.
|
|
19
|
-
"@relkit/client": "0.7.
|
|
20
|
-
"@relkit/local": "0.7.
|
|
18
|
+
"@relkit/app": "0.7.6",
|
|
19
|
+
"@relkit/client": "0.7.6",
|
|
20
|
+
"@relkit/local": "0.7.6",
|
|
21
21
|
"@tanstack/react-query": "5.102.3",
|
|
22
22
|
"effect": "4.0.1",
|
|
23
23
|
"langchain": "1.5.10",
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"react-dom": "19.2.8"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@relkit/cli": "0.7.
|
|
30
|
-
"@relkit/testing": "0.7.
|
|
29
|
+
"@relkit/cli": "0.7.6",
|
|
30
|
+
"@relkit/testing": "0.7.6",
|
|
31
31
|
"@types/bun": "1.3.10",
|
|
32
32
|
"@types/react": "19.2.18",
|
|
33
33
|
"@types/react-dom": "19.2.7",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
/** Maps the HTTP endpoint to its owning service's public member. */
|
|
1
2
|
import { defineRoute } from "@relkit/app/routes";
|
|
2
|
-
import hello from "@app/hello/service.js";
|
|
3
|
+
import hello from "@app/services/hello/service.js";
|
|
3
4
|
|
|
4
5
|
export const GET = defineRoute({ target: hello.hello, client: { operation: "query" } });
|
package/dist/templates/default/v1/fullstack/src/{hello → services/hello}/agents/assistant.agent.ts
RENAMED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
/** Runs an offline greeting agent with RELKIT/native tools, todo middleware and public state observation. */
|
|
1
2
|
import { defineAgent } from "@relkit/app/agents";
|
|
2
3
|
import { z } from "@relkit/app/schema";
|
|
3
4
|
import { FakeToolCallingModel, todoListMiddleware, tool } from "langchain";
|
|
4
|
-
import lookup from "@app/hello/tools/lookup.tool.js";
|
|
5
|
+
import lookup from "@app/services/hello/tools/lookup.tool.js";
|
|
5
6
|
|
|
6
7
|
const uppercase = tool(async ({ text }: { readonly text: string }) => text.toUpperCase(), {
|
|
7
8
|
name: "uppercase",
|
package/dist/templates/default/v1/fullstack/src/{hello → services/hello}/agents/review.agent.ts
RENAMED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** Checkpoints a review graph in memory, interrupts for a boolean decision, and resumes to its final result. */
|
|
1
2
|
import { defineGraph, defineGraphNode } from "@relkit/app/agents";
|
|
2
3
|
import { z } from "@relkit/app/schema";
|
|
3
4
|
import { END, MemorySaver, START, StateSchema, interrupt } from "@langchain/langgraph";
|
package/dist/templates/default/v1/fullstack/src/{hello → services/hello}/tools/lookup.tool.ts
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/** Exposes the hello function as a read-only greeting tool with its existing two-second timeout. */
|
|
2
|
+
import hello from "@app/services/hello/functions/hello.function.js";
|
|
2
3
|
|
|
3
4
|
export default hello.asTool({
|
|
4
5
|
id: "hello.lookup",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
/** Checks assistant.agent through the supported public RELKIT APIs. */
|
|
1
2
|
import { expect, test } from "bun:test";
|
|
2
3
|
import { invokeAgent } from "@relkit/app/agents";
|
|
3
|
-
import assistant from "@app/hello/agents/assistant.agent.js";
|
|
4
|
-
import lookup from "@app/hello/tools/lookup.tool.js";
|
|
4
|
+
import assistant from "@app/services/hello/agents/assistant.agent.js";
|
|
5
|
+
import lookup from "@app/services/hello/tools/lookup.tool.js";
|
|
5
6
|
|
|
6
7
|
test("assistant runs with native todo middleware and the offline model", async () => {
|
|
7
8
|
expect(assistant.client?.state).toEqual(["todos"]);
|