create-effect-project 0.1.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 (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +72 -0
  3. package/dist/bin.js +58245 -0
  4. package/package.json +34 -0
  5. package/templates/_shared/_gitignore +4 -0
  6. package/templates/_shared/config.ts +13 -0
  7. package/templates/_shared/features/_oxlintrc.json +5 -0
  8. package/templates/_shared/features/_vscode.settings.json +6 -0
  9. package/templates/_shared/features/lint.package.json +11 -0
  10. package/templates/_shared/features/lint.tsconfig.json +16 -0
  11. package/templates/_shared/features/observability.noop.ts +9 -0
  12. package/templates/_shared/features/observability.otel.ts +23 -0
  13. package/templates/_shared/features/slop.oxlintrc.json +25 -0
  14. package/templates/_shared/httpapi/api/Api.ts +9 -0
  15. package/templates/_shared/httpapi/api/Notes.ts +25 -0
  16. package/templates/_shared/httpapi/api/System.ts +7 -0
  17. package/templates/_shared/httpapi/client/ApiClient.ts +24 -0
  18. package/templates/_shared/httpapi/server/Notes/http.ts +22 -0
  19. package/templates/_shared/httpapi/server/http.ts +18 -0
  20. package/templates/_shared/notes/Notes.ts +42 -0
  21. package/templates/_shared/notes/domain/Note.ts +19 -0
  22. package/templates/alchemy-http/README.md +79 -0
  23. package/templates/alchemy-http/alchemy.run.ts +22 -0
  24. package/templates/alchemy-http/runtime/bun/_package.json +27 -0
  25. package/templates/alchemy-http/runtime/bun/_tsconfig.json +27 -0
  26. package/templates/alchemy-http/runtime/bun/client.ts +37 -0
  27. package/templates/alchemy-http/runtime/bun/http.test.ts +45 -0
  28. package/templates/alchemy-http/runtime/node/_package.json +28 -0
  29. package/templates/alchemy-http/runtime/node/_tsconfig.json +27 -0
  30. package/templates/alchemy-http/runtime/node/client.ts +37 -0
  31. package/templates/alchemy-http/runtime/node/http.test.ts +48 -0
  32. package/templates/alchemy-http/src/worker.ts +22 -0
  33. package/templates/alchemy-rpc/README.md +83 -0
  34. package/templates/alchemy-rpc/alchemy.run.ts +22 -0
  35. package/templates/alchemy-rpc/runtime/bun/_package.json +27 -0
  36. package/templates/alchemy-rpc/runtime/bun/_tsconfig.json +27 -0
  37. package/templates/alchemy-rpc/runtime/bun/client.ts +43 -0
  38. package/templates/alchemy-rpc/runtime/bun/rpc.test.ts +40 -0
  39. package/templates/alchemy-rpc/runtime/node/_package.json +28 -0
  40. package/templates/alchemy-rpc/runtime/node/_tsconfig.json +27 -0
  41. package/templates/alchemy-rpc/runtime/node/client.ts +43 -0
  42. package/templates/alchemy-rpc/runtime/node/rpc.test.ts +41 -0
  43. package/templates/alchemy-rpc/src/rpc.ts +29 -0
  44. package/templates/alchemy-rpc/src/server/Notes/rpc.ts +17 -0
  45. package/templates/alchemy-rpc/src/worker.ts +24 -0
  46. package/templates/basic/README.md +53 -0
  47. package/templates/basic/runtime/bun/Users.test.ts +31 -0
  48. package/templates/basic/runtime/bun/_package.json +21 -0
  49. package/templates/basic/runtime/bun/_tsconfig.json +10 -0
  50. package/templates/basic/runtime/bun/main.ts +43 -0
  51. package/templates/basic/runtime/node/Users.test.ts +32 -0
  52. package/templates/basic/runtime/node/_package.json +22 -0
  53. package/templates/basic/runtime/node/_tsconfig.json +24 -0
  54. package/templates/basic/runtime/node/main.ts +43 -0
  55. package/templates/basic/src/Users.ts +49 -0
  56. package/templates/basic/src/domain/User.ts +21 -0
  57. package/templates/fullstack/README.md +114 -0
  58. package/templates/fullstack/_gitignore +7 -0
  59. package/templates/fullstack/_package.json +6 -0
  60. package/templates/fullstack/_pnpm-workspace.yaml +2 -0
  61. package/templates/fullstack/_tsconfig.json +9 -0
  62. package/templates/fullstack/apps/api/config.ts +10 -0
  63. package/templates/fullstack/apps/api/http.ts +35 -0
  64. package/templates/fullstack/apps/api/runtime/bun/_package.json +26 -0
  65. package/templates/fullstack/apps/api/runtime/bun/_tsconfig.json +5 -0
  66. package/templates/fullstack/apps/api/runtime/node/_package.json +27 -0
  67. package/templates/fullstack/apps/api/runtime/node/_tsconfig.json +5 -0
  68. package/templates/fullstack/apps/web/runtime/bun/NotesApi.test.ts +65 -0
  69. package/templates/fullstack/apps/web/runtime/bun/_package.json +30 -0
  70. package/templates/fullstack/apps/web/runtime/bun/_tsconfig.json +9 -0
  71. package/templates/fullstack/apps/web/runtime/node/NotesApi.test.ts +65 -0
  72. package/templates/fullstack/apps/web/runtime/node/_package.json +31 -0
  73. package/templates/fullstack/apps/web/runtime/node/_tsconfig.json +9 -0
  74. package/templates/fullstack/apps/web/src/atoms/NotesApi.ts +40 -0
  75. package/templates/fullstack/apps/web/src/router.tsx +64 -0
  76. package/templates/fullstack/apps/web/src/routes/__root.tsx +38 -0
  77. package/templates/fullstack/apps/web/src/routes/index.tsx +97 -0
  78. package/templates/fullstack/apps/web/src/styles.css +40 -0
  79. package/templates/fullstack/apps/web/vite.config.ts +11 -0
  80. package/templates/fullstack/lint.package.json +12 -0
  81. package/templates/http-server/README.md +56 -0
  82. package/templates/http-server/runtime/bun/_package.json +22 -0
  83. package/templates/http-server/runtime/bun/_tsconfig.json +10 -0
  84. package/templates/http-server/runtime/bun/client.ts +40 -0
  85. package/templates/http-server/runtime/bun/http.test.ts +45 -0
  86. package/templates/http-server/runtime/bun/index.ts +25 -0
  87. package/templates/http-server/runtime/node/_package.json +23 -0
  88. package/templates/http-server/runtime/node/_tsconfig.json +24 -0
  89. package/templates/http-server/runtime/node/client.ts +40 -0
  90. package/templates/http-server/runtime/node/http.test.ts +48 -0
  91. package/templates/http-server/runtime/node/index.ts +26 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Client entrypoint — exercises the running server with the generated client.
3
+ *
4
+ * {{runCmd}} dev # in one terminal
5
+ * {{runCmd}} client # in another
6
+ */
7
+ import { NodeRuntime } from "@effect/platform-node"
8
+ import { Effect, Layer } from "effect"
9
+ import { ApiClient } from "./client/ApiClient.ts"
10
+ import { NoteId } from "./domain/Note.ts"
11
+ import * as Observability from "./observability.ts"
12
+
13
+ const program = Effect.gen(function*() {
14
+ const client = yield* ApiClient
15
+
16
+ // `system` is a top level group, so its endpoints sit at the root.
17
+ yield* client.health()
18
+ yield* Effect.logInfo("health: ok")
19
+
20
+ const created = yield* client.notes.create({ payload: { title: "first", body: "hello" } })
21
+ yield* Effect.logInfo("created", created)
22
+
23
+ const fetched = yield* client.notes.getById({ params: { id: created.id } })
24
+ yield* Effect.logInfo("getById", fetched)
25
+
26
+ const all = yield* client.notes.list()
27
+ yield* Effect.logInfo("list", all)
28
+
29
+ // Errors are typed too: an unknown id fails with `NoteNotFound`, which the
30
+ // server returns as a 404.
31
+ const missing = yield* client.notes.getById({ params: { id: NoteId.make("nope") } }).pipe(
32
+ Effect.catchTag("NoteNotFound", () => Effect.succeed("NoteNotFound (404) as expected"))
33
+ )
34
+ yield* Effect.logInfo("getById?id=nope", missing)
35
+ }).pipe(
36
+ Effect.withSpan("client-demo"),
37
+ Effect.provide(Layer.mergeAll(ApiClient.layer, Observability.layer("notes-client")))
38
+ )
39
+
40
+ program.pipe(NodeRuntime.runMain)
@@ -0,0 +1,48 @@
1
+ import { assert, describe, it } from "@effect/vitest"
2
+ import type { Scope } from "effect"
3
+ import { Effect, Layer, References } from "effect"
4
+ import { HttpServer } from "effect/unstable/http"
5
+ import { HttpApiTest } from "effect/unstable/httpapi"
6
+ import { Api } from "../../api/Api.ts"
7
+ import { NoteId } from "../../domain/Note.ts"
8
+ import { Notes } from "../Notes.ts"
9
+ import { NotesApiHandlersNoDeps } from "./http.ts"
10
+
11
+ // Handlers wired to the in-memory Notes, so the whole HTTP pipeline runs
12
+ // without a server: same encoding, routing and decoding as production.
13
+ const TestLayer = Layer.mergeAll(
14
+ NotesApiHandlersNoDeps.pipe(Layer.provide(Notes.layerMemory)),
15
+ HttpServer.layerServices
16
+ )
17
+
18
+ const makeClient = HttpApiTest.groups(Api, ["notes"])
19
+
20
+ const run = <A, E, R>(layer: Layer.Layer<R>, effect: Effect.Effect<A, E, R | Scope.Scope>) =>
21
+ effect.pipe(
22
+ Effect.scoped,
23
+ Effect.provide(layer),
24
+ // Keep the test output to the assertions; raise this to see app logs.
25
+ Effect.provideService(References.MinimumLogLevel, "None")
26
+ )
27
+
28
+ describe("notes api", () => {
29
+ it.effect("creates then lists a note", () =>
30
+ run(TestLayer, Effect.gen(function*() {
31
+ const client = yield* makeClient
32
+ const created = yield* client.notes.create({ payload: { title: "first", body: "hello" } })
33
+ assert.strictEqual(created.title, "first")
34
+
35
+ const listed = yield* client.notes.list()
36
+ assert.strictEqual(listed.length, 1)
37
+ assert.strictEqual(listed[0]?.id, created.id)
38
+ })))
39
+
40
+ it.effect("fails with NoteNotFound for an unknown id", () =>
41
+ run(TestLayer, Effect.gen(function*() {
42
+ const client = yield* makeClient
43
+ const error = yield* Effect.flip(
44
+ client.notes.getById({ params: { id: NoteId.make("nope") } })
45
+ )
46
+ assert.strictEqual(error._tag, "NoteNotFound")
47
+ })))
48
+ })
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Server entrypoint.
3
+ *
4
+ * A schema-first API (`src/api`) is implemented by handlers (`src/server`),
5
+ * served over Node, and consumed by a generated typed client (`src/client`).
6
+ *
7
+ * {{runCmd}} dev # http://localhost:3000/docs
8
+ */
9
+ import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"
10
+ import { Layer } from "effect"
11
+ import { HttpRouter } from "effect/unstable/http"
12
+ import { createServer } from "node:http"
13
+ import { port } from "./config.ts"
14
+ import * as Observability from "./observability.ts"
15
+ import { AllRoutes } from "./server/http.ts"
16
+
17
+ const HttpServerLayer = HttpRouter.serve(AllRoutes).pipe(
18
+ Layer.provide(NodeHttpServer.layerConfig(createServer, { port }))
19
+ )
20
+
21
+ const Main = HttpServerLayer.pipe(
22
+ // Provided at the very end, so every span the app creates is exported.
23
+ Layer.provide(Observability.layer("{{name}}"))
24
+ )
25
+
26
+ Layer.launch(Main).pipe(NodeRuntime.runMain)