create-relkit 0.0.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.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/dist/generate-files.d.ts +16 -0
  4. package/dist/generate-files.d.ts.map +1 -0
  5. package/dist/generate-files.js +108 -0
  6. package/dist/generate-files.js.map +1 -0
  7. package/dist/generate-output.d.ts +20 -0
  8. package/dist/generate-output.d.ts.map +1 -0
  9. package/dist/generate-output.js +67 -0
  10. package/dist/generate-output.js.map +1 -0
  11. package/dist/generate-process.d.ts +9 -0
  12. package/dist/generate-process.d.ts.map +1 -0
  13. package/dist/generate-process.js +76 -0
  14. package/dist/generate-process.js.map +1 -0
  15. package/dist/generate.d.ts +42 -0
  16. package/dist/generate.d.ts.map +1 -0
  17. package/dist/generate.js +103 -0
  18. package/dist/generate.js.map +1 -0
  19. package/dist/index.d.ts +6 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +32 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/options.d.ts +38 -0
  24. package/dist/options.d.ts.map +1 -0
  25. package/dist/options.js +113 -0
  26. package/dist/options.js.map +1 -0
  27. package/dist/templates/default/README.md +13 -0
  28. package/dist/templates/default/v1/agent/.env.example +3 -0
  29. package/dist/templates/default/v1/agent/README.md +23 -0
  30. package/dist/templates/default/v1/agent/gitignore +10 -0
  31. package/dist/templates/default/v1/agent/package.json +29 -0
  32. package/dist/templates/default/v1/agent/relkit.config.ts +23 -0
  33. package/dist/templates/default/v1/agent/src/agents/assistant.agent.ts +13 -0
  34. package/dist/templates/default/v1/agent/src/env.ts +7 -0
  35. package/dist/templates/default/v1/agent/src/functions/hello.function.ts +21 -0
  36. package/dist/templates/default/v1/agent/src/routes/hello/route.ts +7 -0
  37. package/dist/templates/default/v1/agent/tests/integration/hello.route.test.ts +13 -0
  38. package/dist/templates/default/v1/agent/tests/unit/assistant.agent.test.ts +29 -0
  39. package/dist/templates/default/v1/agent/tests/unit/hello.function.test.ts +9 -0
  40. package/dist/templates/default/v1/agent/tsconfig.json +23 -0
  41. package/dist/templates/default/v1/api/.env.example +4 -0
  42. package/dist/templates/default/v1/api/README.md +25 -0
  43. package/dist/templates/default/v1/api/gitignore +10 -0
  44. package/dist/templates/default/v1/api/package.json +30 -0
  45. package/dist/templates/default/v1/api/relkit.config.ts +19 -0
  46. package/dist/templates/default/v1/api/src/env.ts +8 -0
  47. package/dist/templates/default/v1/api/src/events/order-audit.event.ts +15 -0
  48. package/dist/templates/default/v1/api/src/events/order-confirmation.event.ts +11 -0
  49. package/dist/templates/default/v1/api/src/events/order-created.event.ts +15 -0
  50. package/dist/templates/default/v1/api/src/functions/echo.function.ts +10 -0
  51. package/dist/templates/default/v1/api/src/functions/hello.function.ts +13 -0
  52. package/dist/templates/default/v1/api/src/functions/orders/create-order.function.ts +29 -0
  53. package/dist/templates/default/v1/api/src/functions/orders/price-order.function.ts +10 -0
  54. package/dist/templates/default/v1/api/src/routes/echo/route.ts +6 -0
  55. package/dist/templates/default/v1/api/src/routes/hello/route.ts +6 -0
  56. package/dist/templates/default/v1/api/src/routes/orders/route.ts +9 -0
  57. package/dist/templates/default/v1/api/src/services/orders.service.ts +7 -0
  58. package/dist/templates/default/v1/api/tests/integration/echo.route.test.ts +17 -0
  59. package/dist/templates/default/v1/api/tests/integration/hello.route.test.ts +13 -0
  60. package/dist/templates/default/v1/api/tests/integration/order-events.test.ts +71 -0
  61. package/dist/templates/default/v1/api/tests/integration/orders.route.test.ts +24 -0
  62. package/dist/templates/default/v1/api/tests/unit/echo.function.test.ts +9 -0
  63. package/dist/templates/default/v1/api/tests/unit/hello.function.test.ts +9 -0
  64. package/dist/templates/default/v1/api/tests/unit/orders.function.test.ts +26 -0
  65. package/dist/templates/default/v1/api/tsconfig.json +23 -0
  66. package/dist/templates/default/v1/minimal/.env.example +1 -0
  67. package/dist/templates/default/v1/minimal/README.md +19 -0
  68. package/dist/templates/default/v1/minimal/gitignore +10 -0
  69. package/dist/templates/default/v1/minimal/package.json +29 -0
  70. package/dist/templates/default/v1/minimal/relkit.config.ts +10 -0
  71. package/dist/templates/default/v1/minimal/src/env.ts +5 -0
  72. package/dist/templates/default/v1/minimal/src/functions/hello.function.ts +14 -0
  73. package/dist/templates/default/v1/minimal/src/routes/hello/route.ts +7 -0
  74. package/dist/templates/default/v1/minimal/tests/integration/hello.route.test.ts +13 -0
  75. package/dist/templates/default/v1/minimal/tests/unit/hello.function.test.ts +9 -0
  76. package/dist/templates/default/v1/minimal/tsconfig.json +23 -0
  77. package/dist/validate.d.ts +23 -0
  78. package/dist/validate.d.ts.map +1 -0
  79. package/dist/validate.js +156 -0
  80. package/dist/validate.js.map +1 -0
  81. package/package.json +39 -0
@@ -0,0 +1,113 @@
1
+ export const CREATE_TEMPLATES = ["minimal", "api", "agent"];
2
+ export const CREATE_CLOUDS = ["aws", "none"];
3
+ export const CREATE_DEPLOYMENTS = ["pulumi", "none"];
4
+ export const CREATE_OPTION_DEFAULTS = Object.freeze({
5
+ template: "minimal",
6
+ cloud: "aws",
7
+ deploy: "pulumi",
8
+ install: true,
9
+ git: true,
10
+ examples: true,
11
+ forceEmptyDirectory: false,
12
+ json: false,
13
+ });
14
+ export class CreateOptionsError extends Error {
15
+ code = "RELKIT_CREATE_USAGE";
16
+ constructor(message) {
17
+ super(message);
18
+ this.name = "CreateOptionsError";
19
+ }
20
+ }
21
+ /** Parses the non-interactive create flags and applies the v3 defaults. */
22
+ export function normalizeCreateOptions(args, context = {}) {
23
+ let name;
24
+ let directory;
25
+ let template = CREATE_OPTION_DEFAULTS.template;
26
+ let cloud = CREATE_OPTION_DEFAULTS.cloud;
27
+ let deploy = CREATE_OPTION_DEFAULTS.deploy;
28
+ let install = CREATE_OPTION_DEFAULTS.install;
29
+ let git = CREATE_OPTION_DEFAULTS.git;
30
+ let examples = CREATE_OPTION_DEFAULTS.examples;
31
+ let forceEmptyDirectory = CREATE_OPTION_DEFAULTS.forceEmptyDirectory;
32
+ let json = CREATE_OPTION_DEFAULTS.json || context.json === true;
33
+ for (let index = 0; index < args.length; index += 1) {
34
+ const argument = args[index];
35
+ if (argument === "--json")
36
+ json = true;
37
+ else if (argument === "--force-empty-directory")
38
+ forceEmptyDirectory = true;
39
+ else if (argument === "--install")
40
+ install = true;
41
+ else if (argument === "--no-install")
42
+ install = false;
43
+ else if (argument === "--git")
44
+ git = true;
45
+ else if (argument === "--no-git")
46
+ git = false;
47
+ else if (argument === "--examples")
48
+ examples = true;
49
+ else if (argument === "--no-examples")
50
+ examples = false;
51
+ else if (argument === "--template" || argument.startsWith("--template=")) {
52
+ const option = readValue(args, index, argument, "--template");
53
+ index = option.index;
54
+ template = choice(option.value, "--template", CREATE_TEMPLATES);
55
+ }
56
+ else if (argument === "--cloud" || argument.startsWith("--cloud=")) {
57
+ const option = readValue(args, index, argument, "--cloud");
58
+ index = option.index;
59
+ cloud = choice(option.value, "--cloud", CREATE_CLOUDS);
60
+ }
61
+ else if (argument === "--deploy" || argument.startsWith("--deploy=")) {
62
+ const option = readValue(args, index, argument, "--deploy");
63
+ index = option.index;
64
+ deploy = choice(option.value, "--deploy", CREATE_DEPLOYMENTS);
65
+ }
66
+ else if (argument === "--directory" || argument.startsWith("--directory=")) {
67
+ const option = readValue(args, index, argument, "--directory");
68
+ index = option.index;
69
+ directory = option.value;
70
+ }
71
+ else if (argument.startsWith("-")) {
72
+ throw new CreateOptionsError(`Unknown create option: ${argument}`);
73
+ }
74
+ else if (name === undefined)
75
+ name = argument;
76
+ else
77
+ throw new CreateOptionsError("Only one project name is allowed.");
78
+ }
79
+ if (name === undefined)
80
+ throw new CreateOptionsError("Usage: create-relkit <name> [options]");
81
+ return Object.freeze({
82
+ name,
83
+ template,
84
+ cloud,
85
+ deploy,
86
+ install,
87
+ git,
88
+ examples,
89
+ ...(directory === undefined ? {} : { directory }),
90
+ forceEmptyDirectory,
91
+ json,
92
+ });
93
+ }
94
+ function readValue(args, index, argument, option) {
95
+ const prefix = `${option}=`;
96
+ if (argument.startsWith(prefix)) {
97
+ const value = argument.slice(prefix.length);
98
+ if (value.length > 0)
99
+ return { value, index };
100
+ }
101
+ else {
102
+ const value = args[index + 1];
103
+ if (value !== undefined && !value.startsWith("-"))
104
+ return { value, index: index + 1 };
105
+ }
106
+ throw new CreateOptionsError(`${option} requires a value.`);
107
+ }
108
+ function choice(value, option, allowed) {
109
+ if (allowed.includes(value))
110
+ return value;
111
+ throw new CreateOptionsError(`${option} must be one of: ${allowed.join("|")}.`);
112
+ }
113
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ # Default RelKit templates
2
+
3
+ The `v1` directory contains the bundled project templates used by the
4
+ scaffolder:
5
+
6
+ - `minimal` — one function and a `GET /hello` route;
7
+ - `api` — the minimal example plus a JSON `POST /echo` route;
8
+ - `agent` — the minimal example plus a read-only tool and agent descriptor.
9
+
10
+ Each variant is self-contained and can be copied as a project root. It uses
11
+ the current checked-in RelKit package version (`0.0.0`), Bun `1.3.10`, and
12
+ TypeScript `5.9.2`. The generator owns project-name substitution and later
13
+ installation/Git behavior.
@@ -0,0 +1,3 @@
1
+ LOG_LEVEL=info
2
+ OPENAI_API_KEY=
3
+ ANTHROPIC_API_KEY=
@@ -0,0 +1,23 @@
1
+ # my-app
2
+
3
+ A RelKit TypeScript/Bun project with the minimal HTTP example and a bounded
4
+ agent backed by a function-derived read-only tool. Tests use deterministic AI
5
+ SDK test models, while production uses the configured AWS provider set.
6
+
7
+ The agent may omit `model` to use the configured `defaultProvider` and
8
+ `defaultModel`; production keeps OpenAI and Anthropic credentials as
9
+ environment references in `modelProviders`.
10
+
11
+ ## Commands
12
+
13
+ ```sh
14
+ bun install
15
+ bun run dev
16
+ bun run test
17
+ bun run check
18
+ bun run typecheck
19
+ bun run build
20
+ ```
21
+
22
+ The example route is `GET /hello?name=RelKit`. Agent prompt and result content
23
+ are not captured by default.
@@ -0,0 +1,10 @@
1
+ node_modules/
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+ .relkit/generated/
6
+ .relkit/build/
7
+ .relkit/state/
8
+ .relkit/observability/
9
+ coverage/
10
+ dist/
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "my-app",
3
+ "private": true,
4
+ "type": "module",
5
+ "packageManager": "bun@1.3.10",
6
+ "scripts": {
7
+ "dev": "relkit dev",
8
+ "check": "relkit check",
9
+ "typecheck": "tsc --noEmit",
10
+ "test": "bun test",
11
+ "test:unit": "bun test tests/unit",
12
+ "test:integration": "bun test tests/integration",
13
+ "build": "relkit build",
14
+ "start": "relkit start",
15
+ "graph": "relkit graph print",
16
+ "deploy:preview": "relkit deploy preview",
17
+ "deploy": "relkit deploy up"
18
+ },
19
+ "dependencies": {
20
+ "@relkit/app": "0.0.0",
21
+ "@relkit/providers-standard": "0.0.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/bun": "1.3.10",
25
+ "@relkit/cli": "0.0.0",
26
+ "@relkit/testing": "0.0.0",
27
+ "typescript": "5.9.2"
28
+ }
29
+ }
@@ -0,0 +1,23 @@
1
+ import { aiSdk, defineConfig, external } from "@relkit/app/config";
2
+ import env from "@app/env.js";
3
+
4
+ export default defineConfig({
5
+ env,
6
+ models: {
7
+ default: external(
8
+ aiSdk({
9
+ defaultProvider: "openai",
10
+ defaultModel: "gpt-5-mini",
11
+ openai: { apiKey: env.OPENAI_API_KEY },
12
+ anthropic: {
13
+ defaultModel: "claude-sonnet-4-5",
14
+ apiKey: env.ANTHROPIC_API_KEY,
15
+ },
16
+ }),
17
+ ),
18
+ },
19
+ telemetry: { bodyCapture: { mode: "off" } },
20
+ server: { port: 3000, maxBodyBytes: 1_048_576 },
21
+ inspector: { port: 3210 },
22
+ deployment: { target: "aws", adapter: "pulumi" },
23
+ });
@@ -0,0 +1,13 @@
1
+ import { defineAgent } from "@relkit/app/agents";
2
+ import { z } from "@relkit/app/schema";
3
+ import { lookup } from "@app/functions/hello.function.js";
4
+
5
+ export default defineAgent({
6
+ id: "assistant",
7
+ input: z.object({ question: z.string().min(1) }),
8
+ output: z.object({ answer: z.string() }),
9
+ model: "openai:gpt-5-mini",
10
+ instructions: "Answer greeting questions with the read-only lookup tool.",
11
+ tools: [lookup],
12
+ limits: { maxSteps: 4, maxToolCalls: 4, timeoutMs: 10_000 },
13
+ });
@@ -0,0 +1,7 @@
1
+ import { defineEnv, env } from "@relkit/app/config";
2
+
3
+ export default defineEnv({
4
+ LOG_LEVEL: env.literal("trace", "debug", "info", "warn", "error").default("info"),
5
+ OPENAI_API_KEY: env.secret().requiredIn("production"),
6
+ ANTHROPIC_API_KEY: env.secret().requiredIn("production"),
7
+ });
@@ -0,0 +1,21 @@
1
+ import { defineFunction } from "@relkit/app/functions";
2
+ import { z } from "@relkit/app/schema";
3
+
4
+ const hello = defineFunction({
5
+ id: "hello",
6
+ input: z.object({ name: z.string().min(1).default("world") }),
7
+ output: z.object({ message: z.string() }),
8
+ handler: async ({ name }, context) => {
9
+ context.log.info("hello invoked", { name });
10
+ return { message: `Hello, ${name}!` };
11
+ },
12
+ });
13
+
14
+ export const lookup = hello.asTool({
15
+ id: "hello.lookup",
16
+ description: "Read a greeting for a supplied name",
17
+ sideEffect: "read",
18
+ approval: "never",
19
+ });
20
+
21
+ export default hello;
@@ -0,0 +1,7 @@
1
+ import { defineRoute } from "@relkit/app/routes";
2
+ import hello from "@app/functions/hello.function.js";
3
+
4
+ export const GET = defineRoute({
5
+ id: "hello.http",
6
+ target: hello,
7
+ });
@@ -0,0 +1,13 @@
1
+ import { afterAll, expect, test } from "bun:test";
2
+ import { createTestApplication } from "@relkit/testing";
3
+ import config from "../../relkit.config.js";
4
+
5
+ const testApp = await createTestApplication(config);
6
+
7
+ test("GET /hello", async () => {
8
+ const response = await testApp.http.request("/hello?name=Mustafa");
9
+ expect(response.status).toBe(200);
10
+ expect(await response.json()).toEqual({ message: "Hello, Mustafa!" });
11
+ });
12
+
13
+ afterAll(() => testApp.close());
@@ -0,0 +1,29 @@
1
+ import { expect, test } from "bun:test";
2
+ import { createTestAgent, invokeFunction } from "@relkit/testing";
3
+ import assistant from "@app/agents/assistant.agent.js";
4
+ import hello, { lookup } from "@app/functions/hello.function.js";
5
+
6
+ test("assistant uses a function-derived tool with an offline AI SDK model", async () => {
7
+ expect(assistant.model).toBe("openai:gpt-5-mini");
8
+ expect(assistant.tools.map((tool) => tool.ref.id)).toEqual(["hello.lookup"]);
9
+ expect(assistant.limits).toEqual({ maxSteps: 4, maxToolCalls: 4, timeoutMs: 10_000 });
10
+
11
+ const agent = createTestAgent({
12
+ agent: assistant,
13
+ tools: [lookup],
14
+ engine: {
15
+ invoke: (request) =>
16
+ invokeFunction(hello, request.input as Parameters<typeof hello.invoke>[0]),
17
+ },
18
+ model: { provider: "openai", modelId: "gpt-5-mini" },
19
+ script: [
20
+ { type: "tool-call", callId: "call-1", toolId: "hello.lookup", input: { name: "Ada" } },
21
+ { type: "final", output: { answer: "Hello, Ada!" } },
22
+ ],
23
+ });
24
+
25
+ await expect(agent.invoke({ question: "greet Ada" })).resolves.toEqual({
26
+ answer: "Hello, Ada!",
27
+ });
28
+ expect(agent.model.calls).toHaveLength(2);
29
+ });
@@ -0,0 +1,9 @@
1
+ import { expect, test } from "bun:test";
2
+ import { invokeFunction } from "@relkit/testing";
3
+ import hello from "@app/functions/hello.function.js";
4
+
5
+ test("hello returns a greeting", async () => {
6
+ await expect(invokeFunction(hello, { name: "Mustafa" })).resolves.toEqual({
7
+ message: "Hello, Mustafa!",
8
+ });
9
+ });
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "compilerOptions": {
4
+ "target": "ES2022",
5
+ "module": "ESNext",
6
+ "moduleResolution": "Bundler",
7
+ "baseUrl": ".",
8
+ "paths": { "@app/*": ["src/*"] },
9
+ "lib": ["ES2022"],
10
+ "types": ["bun"],
11
+ "strict": true,
12
+ "noUncheckedIndexedAccess": true,
13
+ "exactOptionalPropertyTypes": true,
14
+ "verbatimModuleSyntax": true,
15
+ "isolatedModules": true,
16
+ "noEmit": true,
17
+ "skipLibCheck": true,
18
+ "forceConsistentCasingInFileNames": true
19
+ },
20
+ "files": [".relkit/generated/event-registry.d.ts"],
21
+ "include": ["src/**/*.ts", "tests/**/*.ts", "relkit.config.ts"],
22
+ "exclude": ["node_modules", ".relkit"]
23
+ }
@@ -0,0 +1,4 @@
1
+ LOG_LEVEL=info
2
+ EVENT_REGION=us-east-1
3
+ EVENT_ENDPOINT=http://localhost:4566
4
+ EVENT_BUS_NAME=default
@@ -0,0 +1,25 @@
1
+ # my-app
2
+
3
+ A RelKit TypeScript/Bun API project with greeting, echo, and order routes.
4
+ Functions own the application handlers; routes describe transport mapping and
5
+ responses. Source-scoped IDs are inferred where safe, the order route targets a
6
+ small service, and nested calls use descriptor `invoke`.
7
+
8
+ ## Commands
9
+
10
+ ```sh
11
+ bun install
12
+ bun run dev
13
+ bun run test
14
+ bun run check
15
+ bun run typecheck
16
+ bun run build
17
+ ```
18
+
19
+ The example routes are `GET /hello?name=RelKit`, `POST /echo` with a JSON
20
+ `message` field, and `POST /orders` with `{ orderId, sku, quantity }`.
21
+
22
+ Creating an order publishes `orders.created`. Its confirmation and audit
23
+ listeners are independent durable, at-least-once deliveries: one listener can
24
+ fail without rolling back the other, and delivery is not a transaction or a
25
+ promise of simultaneous execution.
@@ -0,0 +1,10 @@
1
+ node_modules/
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+ .relkit/generated/
6
+ .relkit/build/
7
+ .relkit/state/
8
+ .relkit/observability/
9
+ coverage/
10
+ dist/
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "my-app",
3
+ "private": true,
4
+ "type": "module",
5
+ "packageManager": "bun@1.3.10",
6
+ "scripts": {
7
+ "dev": "relkit dev",
8
+ "check": "relkit check",
9
+ "typecheck": "tsc --noEmit",
10
+ "test": "bun test",
11
+ "test:unit": "bun test tests/unit",
12
+ "test:integration": "bun test tests/integration",
13
+ "build": "relkit build",
14
+ "start": "relkit start",
15
+ "graph": "relkit graph print",
16
+ "deploy:preview": "relkit deploy preview",
17
+ "deploy": "relkit deploy up"
18
+ },
19
+ "dependencies": {
20
+ "@relkit/app": "0.0.0",
21
+ "@relkit/providers-standard": "0.0.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/bun": "1.3.10",
25
+ "@relkit/cli": "0.0.0",
26
+ "@relkit/events": "0.0.0",
27
+ "@relkit/testing": "0.0.0",
28
+ "typescript": "5.9.2"
29
+ }
30
+ }
@@ -0,0 +1,19 @@
1
+ import { defineConfig, eventBridge, managed } from "@relkit/app/config";
2
+ import env from "@app/env.js";
3
+
4
+ export default defineConfig({
5
+ env,
6
+ events: {
7
+ default: managed(
8
+ eventBridge({
9
+ region: env.EVENT_REGION,
10
+ endpoint: env.EVENT_ENDPOINT,
11
+ busName: env.EVENT_BUS_NAME,
12
+ }),
13
+ ),
14
+ },
15
+ telemetry: { bodyCapture: { mode: "off" } },
16
+ server: { port: 3000, maxBodyBytes: 1_048_576 },
17
+ inspector: { port: 3210 },
18
+ deployment: { target: "aws", adapter: "pulumi" },
19
+ });
@@ -0,0 +1,8 @@
1
+ import { defineEnv, env } from "@relkit/app/config";
2
+
3
+ export default defineEnv({
4
+ LOG_LEVEL: env.literal("trace", "debug", "info", "warn", "error").default("info"),
5
+ EVENT_REGION: env.string().default("us-east-1"),
6
+ EVENT_ENDPOINT: env.url().requiredIn("development", "production"),
7
+ EVENT_BUS_NAME: env.string().requiredIn("development", "production"),
8
+ });
@@ -0,0 +1,15 @@
1
+ import { onEvent } from "@relkit/app/events";
2
+
3
+ const orderAudit = onEvent(
4
+ "orders.created",
5
+ async (payload, context) => {
6
+ context.log.info("order.audit.recorded", {
7
+ orderId: payload.orderId,
8
+ sku: payload.sku,
9
+ totalCents: payload.totalCents,
10
+ });
11
+ },
12
+ { id: "orders.audit" },
13
+ );
14
+
15
+ export default orderAudit;
@@ -0,0 +1,11 @@
1
+ import { onEvent } from "@relkit/app/events";
2
+
3
+ const orderConfirmation = onEvent(
4
+ "orders.created",
5
+ async (payload, context) => {
6
+ context.log.info("order.confirmation.requested", { orderId: payload.orderId });
7
+ },
8
+ { id: "orders.confirmation" },
9
+ );
10
+
11
+ export default orderConfirmation;
@@ -0,0 +1,15 @@
1
+ import { defineEvent } from "@relkit/app/events";
2
+ import { z } from "@relkit/app/schema";
3
+
4
+ const orderCreated = defineEvent({
5
+ id: "orders.created",
6
+ version: 1,
7
+ payload: z.object({
8
+ orderId: z.string().min(1),
9
+ sku: z.string().min(1),
10
+ totalCents: z.number().int().nonnegative(),
11
+ }),
12
+ title: "Order created",
13
+ });
14
+
15
+ export default orderCreated;
@@ -0,0 +1,10 @@
1
+ import { defineFunction } from "@relkit/app/functions";
2
+ import { z } from "@relkit/app/schema";
3
+
4
+ const echo = defineFunction({
5
+ input: z.object({ message: z.string().min(1) }),
6
+ output: z.object({ message: z.string() }),
7
+ handler: async ({ message }) => ({ message }),
8
+ });
9
+
10
+ export default echo;
@@ -0,0 +1,13 @@
1
+ import { defineFunction } from "@relkit/app/functions";
2
+ import { z } from "@relkit/app/schema";
3
+
4
+ const hello = defineFunction({
5
+ input: z.object({ name: z.string().min(1).default("world") }),
6
+ output: z.object({ message: z.string() }),
7
+ handler: async ({ name }, context) => {
8
+ context.log.info("hello invoked", { name });
9
+ return { message: `Hello, ${name}!` };
10
+ },
11
+ });
12
+
13
+ export default hello;
@@ -0,0 +1,29 @@
1
+ import { defineFunction } from "@relkit/app/functions";
2
+ import { z } from "@relkit/app/schema";
3
+ import orderCreated from "@app/events/order-created.event.js";
4
+ import priceOrder from "@app/functions/orders/price-order.function.js";
5
+
6
+ const orderInput = z.object({
7
+ orderId: z.string().min(1),
8
+ sku: z.string().min(1),
9
+ quantity: z.number().int().positive(),
10
+ });
11
+ const orderOutput = z.object({
12
+ orderId: z.string(),
13
+ sku: z.string(),
14
+ totalCents: z.number().int().nonnegative(),
15
+ });
16
+
17
+ const createOrder = defineFunction({
18
+ input: orderInput,
19
+ output: orderOutput,
20
+ dependencies: { events: { orderCreated } },
21
+ handler: async (input, context) => {
22
+ const { totalCents } = await priceOrder.invoke({ quantity: input.quantity });
23
+ const order = { orderId: input.orderId, sku: input.sku, totalCents };
24
+ await context.events.orderCreated.publish(order);
25
+ return order;
26
+ },
27
+ });
28
+
29
+ export default createOrder;
@@ -0,0 +1,10 @@
1
+ import { defineFunction } from "@relkit/app/functions";
2
+ import { z } from "@relkit/app/schema";
3
+
4
+ const priceOrder = defineFunction({
5
+ input: z.object({ quantity: z.number().int().positive() }),
6
+ output: z.object({ totalCents: z.number().int().nonnegative() }),
7
+ handler: ({ quantity }) => ({ totalCents: quantity * 100 }),
8
+ });
9
+
10
+ export default priceOrder;
@@ -0,0 +1,6 @@
1
+ import { defineRoute } from "@relkit/app/routes";
2
+ import echo from "@app/functions/echo.function.js";
3
+
4
+ export const POST = defineRoute({
5
+ target: echo,
6
+ });
@@ -0,0 +1,6 @@
1
+ import { defineRoute } from "@relkit/app/routes";
2
+ import hello from "@app/functions/hello.function.js";
3
+
4
+ export const GET = defineRoute({
5
+ target: hello,
6
+ });
@@ -0,0 +1,9 @@
1
+ import { defineRoute } from "@relkit/app/routes";
2
+ import orders from "@app/services/orders.service.js";
3
+
4
+ export const POST = defineRoute({
5
+ // Reuse the checked application function instead of duplicating HTTP logic.
6
+ target: orders.createOrder,
7
+ // Successful creates return the conventional HTTP 201 status.
8
+ successStatus: 201,
9
+ });
@@ -0,0 +1,7 @@
1
+ import { defineService } from "@relkit/app/services";
2
+ import createOrder from "@app/functions/orders/create-order.function.js";
3
+ import priceOrder from "@app/functions/orders/price-order.function.js";
4
+
5
+ const orders = defineService({ functions: { createOrder, priceOrder } });
6
+
7
+ export default orders;
@@ -0,0 +1,17 @@
1
+ import { afterAll, expect, test } from "bun:test";
2
+ import { createTestApplication } from "@relkit/testing";
3
+ import config from "../../relkit.config.js";
4
+
5
+ const testApp = await createTestApplication(config);
6
+
7
+ test("POST /echo", async () => {
8
+ const response = await testApp.http.request("/echo", {
9
+ method: "POST",
10
+ headers: { "content-type": "application/json" },
11
+ body: JSON.stringify({ message: "hello" }),
12
+ });
13
+ expect(response.status).toBe(200);
14
+ expect(await response.json()).toEqual({ message: "hello" });
15
+ });
16
+
17
+ afterAll(() => testApp.close());
@@ -0,0 +1,13 @@
1
+ import { afterAll, expect, test } from "bun:test";
2
+ import { createTestApplication } from "@relkit/testing";
3
+ import config from "../../relkit.config.js";
4
+
5
+ const testApp = await createTestApplication(config);
6
+
7
+ test("GET /hello", async () => {
8
+ const response = await testApp.http.request("/hello?name=Mustafa");
9
+ expect(response.status).toBe(200);
10
+ expect(await response.json()).toEqual({ message: "Hello, Mustafa!" });
11
+ });
12
+
13
+ afterAll(() => testApp.close());