@gjermundgaraba/effect-actions 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -40
- package/dist/Action.d.mts +29 -117
- package/dist/Action.mjs +14 -53
- package/dist/ActionCli.d.mts +60 -28
- package/dist/ActionCli.mjs +54 -48
- package/dist/ActionHttp.d.mts +18 -67
- package/dist/ActionHttp.mjs +20 -70
- package/dist/ActionMcp.d.mts +23 -29
- package/dist/ActionMcp.mjs +56 -114
- package/dist/ActionRpc.d.mts +147 -0
- package/dist/ActionRpc.mjs +113 -0
- package/dist/ActionToolkit.d.mts +5 -8
- package/dist/ActionToolkit.mjs +3 -3
- package/dist/Authentication.d.mts +15 -15
- package/dist/Authentication.mjs +42 -96
- package/dist/Testing.d.mts +28 -23
- package/dist/Testing.mjs +27 -44
- package/dist/{internal/authentication.d.mts → authentication/provider.d.mts} +41 -9
- package/dist/authentication/provider.mjs +12 -0
- package/dist/authentication/refusal.mjs +36 -0
- package/dist/{internal/cli.d.mts → cli/commands.d.mts} +16 -12
- package/dist/cli/commands.mjs +302 -0
- package/dist/{internal → contract}/call.d.mts +2 -2
- package/dist/contract/call.mjs +10 -0
- package/dist/{internal → contract}/errors.d.mts +13 -10
- package/dist/{internal → contract}/errors.mjs +9 -14
- package/dist/{internal → contract}/implementation.d.mts +37 -21
- package/dist/{internal → contract}/implementation.mjs +18 -49
- package/dist/contract/media.d.mts +13 -0
- package/dist/contract/media.mjs +18 -0
- package/dist/contract/rules.d.mts +25 -0
- package/dist/{internal/actions.mjs → contract/rules.mjs} +6 -42
- package/dist/{internal → http}/client.d.mts +7 -9
- package/dist/http/client.mjs +17 -0
- package/dist/http/declared.mjs +20 -0
- package/dist/http/schema-errors.mjs +9 -0
- package/dist/mcp/protocol.d.mts +17 -0
- package/dist/{internal/mcp.mjs → mcp/protocol.mjs} +29 -62
- package/dist/stdio/console.mjs +64 -0
- package/dist/{internal → testing}/memory.d.mts +1 -1
- package/dist/{internal → testing}/memory.mjs +5 -17
- package/dist/toolkit/tools.mjs +29 -0
- package/docs/Action-failures.md +47 -0
- package/docs/Action-in-process.md +93 -0
- package/docs/Action.md +206 -426
- package/docs/ActionCli-failures.md +102 -0
- package/docs/ActionCli.md +206 -293
- package/docs/ActionHttp-failures.md +83 -0
- package/docs/ActionHttp.md +202 -282
- package/docs/ActionMcp-failures.md +31 -0
- package/docs/ActionMcp.md +222 -271
- package/docs/ActionRpc-failures.md +21 -0
- package/docs/ActionRpc.md +178 -0
- package/docs/ActionToolkit-failures.md +15 -0
- package/docs/ActionToolkit.md +100 -119
- package/docs/Authentication-advanced.md +139 -0
- package/docs/Authentication-failures.md +99 -0
- package/docs/Authentication.md +142 -392
- package/docs/CONTEXT.md +39 -24
- package/docs/README.md +71 -35
- package/docs/Testing-failures.md +20 -0
- package/docs/Testing.md +186 -151
- package/docs/guarantees.md +35 -96
- package/docs/setup-failures.md +34 -0
- package/docs/setup.md +87 -91
- package/examples/README.md +68 -0
- package/examples/assistant/README.md +92 -0
- package/examples/assistant/src/agent.ts +92 -0
- package/examples/assistant/src/cli.ts +34 -0
- package/examples/assistant/src/commands.ts +25 -0
- package/examples/assistant/src/identity/Identity.ts +33 -0
- package/examples/assistant/src/identity/authorize.ts +24 -0
- package/examples/assistant/src/identity/handlers.ts +10 -0
- package/examples/assistant/src/identity/members.ts +9 -0
- package/examples/assistant/src/identity/permissions.ts +23 -0
- package/examples/assistant/src/scripted.ts +23 -0
- package/examples/assistant/src/tasks/Tasks.ts +63 -0
- package/examples/assistant/src/tasks/handlers.ts +25 -0
- package/examples/assistant/src/tasks/store.ts +96 -0
- package/examples/assistant/src/toolkit.ts +30 -0
- package/examples/assistant/test/commands.test.ts +57 -0
- package/examples/assistant/test/identity/identity.test.ts +43 -0
- package/examples/assistant/test/tasks/store.test.ts +48 -0
- package/examples/assistant/test/tasks/tasks.test.ts +75 -0
- package/examples/assistant/test/toolkit.test.ts +121 -0
- package/examples/kitchen-sink/README.md +317 -0
- package/examples/kitchen-sink/src/Actions.ts +19 -0
- package/examples/kitchen-sink/src/Api.ts +24 -0
- package/examples/kitchen-sink/src/Rpc.ts +12 -0
- package/examples/kitchen-sink/src/admin/Admin.ts +59 -0
- package/examples/kitchen-sink/src/admin/authenticate.ts +18 -0
- package/examples/kitchen-sink/src/admin/authorize.ts +27 -0
- package/examples/kitchen-sink/src/admin/grants.ts +22 -0
- package/examples/kitchen-sink/src/admin/handlers.ts +21 -0
- package/examples/kitchen-sink/src/admin/keys.ts +8 -0
- package/examples/kitchen-sink/src/agent.ts +47 -0
- package/examples/kitchen-sink/src/assistant/Assistant.ts +82 -0
- package/examples/kitchen-sink/src/assistant/conversations.ts +71 -0
- package/examples/kitchen-sink/src/assistant/demo-model.ts +33 -0
- package/examples/kitchen-sink/src/assistant/handlers.ts +146 -0
- package/examples/kitchen-sink/src/attachments/Attachments.ts +48 -0
- package/examples/kitchen-sink/src/attachments/download.ts +49 -0
- package/examples/kitchen-sink/src/attachments/handlers.ts +38 -0
- package/examples/kitchen-sink/src/attachments/store.ts +58 -0
- package/examples/kitchen-sink/src/browser.ts +33 -0
- package/examples/kitchen-sink/src/cli.ts +85 -0
- package/examples/kitchen-sink/src/config.ts +18 -0
- package/examples/kitchen-sink/src/identity/Identity.ts +61 -0
- package/examples/kitchen-sink/src/identity/authenticate.ts +87 -0
- package/examples/kitchen-sink/src/identity/authorize.ts +16 -0
- package/examples/kitchen-sink/src/identity/directory.ts +52 -0
- package/examples/kitchen-sink/src/identity/handlers.ts +9 -0
- package/examples/kitchen-sink/src/identity/members.ts +13 -0
- package/examples/kitchen-sink/src/identity/quota.ts +62 -0
- package/examples/kitchen-sink/src/issues/Issues.ts +109 -0
- package/examples/kitchen-sink/src/issues/assistant.ts +23 -0
- package/examples/kitchen-sink/src/issues/guide.ts +37 -0
- package/examples/kitchen-sink/src/issues/handlers.ts +37 -0
- package/examples/kitchen-sink/src/issues/store.ts +96 -0
- package/examples/kitchen-sink/src/issues/write-limit.ts +41 -0
- package/examples/kitchen-sink/src/memory.ts +30 -0
- package/examples/kitchen-sink/src/ops.ts +42 -0
- package/examples/kitchen-sink/src/request-policy.ts +63 -0
- package/examples/kitchen-sink/src/routes.ts +120 -0
- package/examples/kitchen-sink/src/sdk.ts +46 -0
- package/examples/kitchen-sink/src/seed.ts +65 -0
- package/examples/kitchen-sink/src/server.ts +62 -0
- package/examples/kitchen-sink/src/stdio.ts +34 -0
- package/examples/kitchen-sink/src/workspaces/Workspaces.ts +28 -0
- package/examples/kitchen-sink/src/workspaces/handlers.ts +23 -0
- package/examples/kitchen-sink/src/workspaces/registry.ts +46 -0
- package/examples/kitchen-sink/src/workspaces/tenant.ts +17 -0
- package/examples/kitchen-sink/test/admin/admin.test.ts +67 -0
- package/examples/kitchen-sink/test/assistant/assistant.test.ts +435 -0
- package/examples/kitchen-sink/test/attachments/attachments.test.ts +161 -0
- package/examples/kitchen-sink/test/identity/identity.test.ts +137 -0
- package/examples/kitchen-sink/test/identity/session.test.ts +110 -0
- package/examples/kitchen-sink/test/issues/assistant.test.ts +75 -0
- package/examples/kitchen-sink/test/issues/issues.test.ts +135 -0
- package/examples/kitchen-sink/test/request-policy.test.ts +125 -0
- package/examples/kitchen-sink/test/routes.test.ts +123 -0
- package/examples/kitchen-sink/test/support/scripted.ts +40 -0
- package/examples/live-rpc/README.md +107 -0
- package/examples/live-rpc/src/Rpc.ts +8 -0
- package/examples/live-rpc/src/browser.ts +98 -0
- package/examples/live-rpc/src/chat/Chat.ts +75 -0
- package/examples/live-rpc/src/chat/authorize.ts +16 -0
- package/examples/live-rpc/src/chat/handlers.ts +50 -0
- package/examples/live-rpc/src/chat/room.ts +23 -0
- package/examples/live-rpc/src/chat/store.ts +54 -0
- package/examples/live-rpc/src/client.ts +37 -0
- package/examples/live-rpc/src/identity/Identity.ts +19 -0
- package/examples/live-rpc/src/identity/members.ts +8 -0
- package/examples/live-rpc/src/identity/verifier.ts +23 -0
- package/examples/live-rpc/src/routes.ts +22 -0
- package/examples/live-rpc/src/server.ts +43 -0
- package/examples/live-rpc/test/browser.test.ts +105 -0
- package/examples/live-rpc/test/chat/chat.test.ts +69 -0
- package/examples/live-rpc/test/routes.test.ts +99 -0
- package/examples/live-rpc/test/server.test.ts +75 -0
- package/examples/mcp-tools/README.md +136 -0
- package/examples/mcp-tools/src/instructions.ts +12 -0
- package/examples/mcp-tools/src/printers/Printers.ts +119 -0
- package/examples/mcp-tools/src/printers/fake.ts +115 -0
- package/examples/mcp-tools/src/printers/farm.ts +136 -0
- package/examples/mcp-tools/src/printers/handlers.ts +29 -0
- package/examples/mcp-tools/src/routes.ts +52 -0
- package/examples/mcp-tools/src/server.ts +30 -0
- package/examples/mcp-tools/src/stdio.ts +26 -0
- package/examples/mcp-tools/test/printers/printers.test.ts +189 -0
- package/examples/mcp-tools/test/routes.test.ts +202 -0
- package/examples/notes-service/README.md +108 -0
- package/examples/notes-service/src/Api.ts +13 -0
- package/examples/notes-service/src/browser.ts +41 -0
- package/examples/notes-service/src/cli.ts +22 -0
- package/examples/notes-service/src/commands.ts +89 -0
- package/examples/notes-service/src/identity/Identity.ts +31 -0
- package/examples/notes-service/src/identity/authorize.ts +18 -0
- package/examples/notes-service/src/identity/handlers.ts +10 -0
- package/examples/notes-service/src/identity/users.ts +9 -0
- package/examples/notes-service/src/identity/verifier.ts +36 -0
- package/examples/notes-service/src/notes/Notes.ts +91 -0
- package/examples/notes-service/src/notes/handlers.ts +33 -0
- package/examples/notes-service/src/notes/store.ts +73 -0
- package/examples/notes-service/src/routes.ts +45 -0
- package/examples/notes-service/src/server.ts +26 -0
- package/examples/notes-service/src/status/Status.ts +10 -0
- package/examples/notes-service/src/status/handlers.ts +14 -0
- package/examples/notes-service/src/stdio.ts +25 -0
- package/examples/notes-service/test/commands.test.ts +134 -0
- package/examples/notes-service/test/notes/notes.test.ts +103 -0
- package/examples/notes-service/test/routes.test.ts +84 -0
- package/examples/quickstart/README.md +31 -0
- package/examples/quickstart/src/Api.ts +5 -0
- package/examples/quickstart/src/client.ts +13 -0
- package/examples/quickstart/src/greeting/Greeting.ts +12 -0
- package/examples/quickstart/src/greeting/handlers.ts +6 -0
- package/examples/quickstart/src/routes.ts +11 -0
- package/examples/quickstart/src/server.ts +16 -0
- package/examples/quickstart/test/greeting/greeting.test.ts +18 -0
- package/package.json +23 -5
- package/dist/internal/actions.d.mts +0 -9
- package/dist/internal/authentication.mjs +0 -11
- package/dist/internal/call.mjs +0 -12
- package/dist/internal/cli.mjs +0 -328
- package/dist/internal/client.mjs +0 -25
- package/dist/internal/console.mjs +0 -77
- package/dist/internal/declared.d.mts +0 -13
- package/dist/internal/declared.mjs +0 -34
- package/dist/internal/mcp.d.mts +0 -14
- package/dist/internal/media.d.mts +0 -8
- package/dist/internal/media.mjs +0 -26
- package/dist/internal/refusal.mjs +0 -67
- package/dist/internal/schema-errors.mjs +0 -20
- package/dist/internal/tools.mjs +0 -44
package/docs/CONTEXT.md
CHANGED
|
@@ -1,29 +1,44 @@
|
|
|
1
1
|
# Context
|
|
2
2
|
|
|
3
|
-
Vocabulary for `@gjermundgaraba/effect-actions`. The docs, the
|
|
4
|
-
words with these meanings. Module names and Effect's own terms (`HttpApi`, `Tool`,
|
|
5
|
-
`McpServer`, `Command`) mean what their modules say.
|
|
3
|
+
Vocabulary for `@gjermundgaraba/effect-actions`. The docs, the examples, the code and the tests
|
|
4
|
+
use these words with these meanings. Module names and Effect's own terms (`HttpApi`, `Tool`,
|
|
5
|
+
`Toolkit`, `McpServer`, `Rpc`, `RpcGroup`, `RpcServer`, `Command`) mean what their modules say.
|
|
6
|
+
|
|
7
|
+
Read this page alone, in one tool call: pages read together are cut. Tasks are routed from
|
|
8
|
+
[README.md](README.md), and each module's failures from its `-failures.md` page.
|
|
6
9
|
|
|
7
10
|
## Terms
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
12
|
+
### Contracts and calls
|
|
13
|
+
|
|
14
|
+
- **Action**: one contract: a name, a description, input, success and declared error schemas, `readOnly`, `caller`, and its `mcp` options. Holds no behavior. Made by `Action.make`.
|
|
15
|
+
- **Read-only**: `readOnly`, required on every action: whether it leaves its resource unchanged. It enforces nothing itself.
|
|
16
|
+
- **Caller**: who may call an action, `caller`. `Action.Anyone` makes a **public action**; an identity service, such as `caller: CurrentUser`, makes a **protected action**.
|
|
17
|
+
- **Identity**: the service a protected action names as its caller. A **trusted identity** is one only a host supplies, on a local surface, which no token names.
|
|
18
|
+
- **Handler**: a function from an action's decoded input to an Effect of its decoded success. The services it yields per call, and an authorizer's, are **request-time requirements**.
|
|
19
|
+
- **Implementation**: what `Action.implement` returns: actions bound to their handlers and, where any is protected, their authorizer. Every surface that runs handlers takes one, or a list.
|
|
20
|
+
- **Builder**: an Effect given to `Action.implement` in place of handlers, which returns them, once per layer graph. The services it yields are **build-time requirements**, or **startup services** ([Action.md](Action.md#implement-actions)).
|
|
21
|
+
- **Layer graph**: the layers Effect builds together, each built once ([Action-in-process.md](Action-in-process.md#share-one-build-across-servers)).
|
|
22
|
+
- **Host**: the application that serves the surfaces: it builds their layers, provides their services and runs the server, subprocess or CLI.
|
|
23
|
+
- **Surface**: where callers reach actions. A **remote surface** authenticates its callers (`ActionHttp.layer`, `ActionMcp.layerHttp`, `ActionRpc.layer`). A **local surface** has no one to authenticate (`ActionToolkit`, a local `ActionCli` command, `ActionMcp.runStdio`, `Action.client`): the host provides the identity.
|
|
24
|
+
- **Binding**: what `ActionHttp.make` or `ActionRpc.make` returns: the actions its surface serves, as browser-safe data the server and every client share.
|
|
25
|
+
- **Client**: one method per action, `client.<action>(input)`: `ActionHttp.client`, `ActionRpc.client`, `Testing.mcpClient` and, in process, `Action.client`.
|
|
26
|
+
- **Authorizer**: `authorize`, a function of the called action that an implementation of protected actions states. It succeeds or fails with a refusal ([Action.md](Action.md#authorize-calls)).
|
|
27
|
+
- **Authentication descriptor**: what `Authentication.make` returns: a named, browser-safe declaration of how a remote caller proves an identity. A binding or an MCP endpoint serving protected actions names it.
|
|
28
|
+
- **Authentication provider**: what `Authentication.layer` returns for a descriptor, holding its **verifier**: the server-only function from a credential to the identity.
|
|
29
|
+
- **Layer middleware**: native `HttpApiMiddleware` or `RpcMiddleware` given to `ActionHttp.layer` or `ActionRpc.layer` as `middleware`, run around every route or rpc the layer serves.
|
|
30
|
+
- **Mixed endpoint**: an MCP endpoint serving public and protected tools on one URL ([ActionMcp.md](ActionMcp.md#protect-tools)).
|
|
31
|
+
- **Declared error**: an error schema listed in an action's `error`, which its handler may fail with ([guarantees.md](guarantees.md#wire-behavior)).
|
|
32
|
+
- **Built-in errors**: `Action.InvalidInput`, `Action.Unauthenticated` and `Action.Forbidden`, together `Action.BuiltIn`: errors the library produces and every surface declares ([Action.md](Action.md#fail-with-declared-and-built-in-errors)).
|
|
33
|
+
- **Refusal**: `Action.Refusal`, `Unauthenticated | Forbidden`: what authentication or an authorizer refuses a caller with. A **step-up refusal** is one an OAuth client acts on: an `Unauthenticated`, or a `Forbidden` naming `scopes` ([Authentication.md](Authentication.md#publish-oauth-discovery)).
|
|
34
|
+
- **Media field**: a field of an action's success whose schema is a media kind, `Action.Image`, which an MCP tool **lifts** into a content block of its own ([ActionMcp.md](ActionMcp.md#return-images-and-other-media)).
|
|
35
|
+
|
|
36
|
+
### Layout of an application
|
|
37
|
+
|
|
38
|
+
The parts of the layout every example uses: [examples/README.md](../examples/README.md#how-they-are-laid-out).
|
|
39
|
+
|
|
40
|
+
- **Domain**: one subject of the application, such as notes or identity, and the folder holding everything about it. A folder is never a mechanism, such as `contracts/` or `handlers/`.
|
|
41
|
+
- **Contract file**: the PascalCase file of a domain, such as `notes/Notes.ts`: its schemas, errors and actions, and the identity and descriptor it declares. It imports no server code.
|
|
42
|
+
- **Routes**: `routes.ts`, the host's surfaces as one Layer. A route, singular, is one HTTP route.
|
|
43
|
+
- **Entrypoint**: a file that runs the application one way, such as `server.ts`, `stdio.ts` or `cli.ts`: the **composition root**, the one place that chooses the services, the platform and, on a local surface, the caller.
|
|
44
|
+
- **Demo shortcut**: what an example does in place of what a deployment needs, marked `DEMO ONLY` in a comment.
|
package/docs/README.md
CHANGED
|
@@ -1,47 +1,83 @@
|
|
|
1
|
-
# effect-actions
|
|
1
|
+
# effect-actions
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Read the card for a module before writing code that uses it. Exported TypeScript declarations are the exact signature reference. Vocabulary is defined in
|
|
8
|
-
[CONTEXT.md](CONTEXT.md); the pages use those terms exactly.
|
|
3
|
+
`@gjermundgaraba/effect-actions` defines an Effect action once, as a contract with one
|
|
4
|
+
implementation, and serves it over HTTP, MCP and Effect RPC, gives it to a model as a native
|
|
5
|
+
Toolkit, and runs it from a CLI. Every surface is Effect's own server or client, running the
|
|
6
|
+
same schemas, handler and authorization.
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
## How to read these pages
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
**Arriving with an error?** Search the `*-failures.md` pages for a fragment of its message
|
|
11
|
+
first (step 5), before the examples and the pages.
|
|
12
|
+
|
|
13
|
+
1. **Open one page per tool call, with your file-reading tool.** A page fits one read. Several
|
|
14
|
+
in one call, however many commands it runs, are cut by its output limit, and what is cut goes
|
|
15
|
+
unnoticed.
|
|
16
|
+
2. **Start from the example closest to what you are building** ([Examples](#examples)). Its
|
|
17
|
+
README has a layout tree and a "Where to look" table naming the file for each thing. Copy its
|
|
18
|
+
layout and idioms: the examples are the reference for how code is written.
|
|
19
|
+
3. **Read [Action.md](Action.md) completely.** Every surface builds on it.
|
|
20
|
+
4. **Read the page of each surface you serve** ([Pages](#pages)), and the example files its task
|
|
21
|
+
sections link.
|
|
22
|
+
5. **When something fails, search the `*-failures.md` pages** for a fragment of the message. Each
|
|
23
|
+
module page has one beside it, such as [Action-failures.md](Action-failures.md).
|
|
24
|
+
|
|
25
|
+
A service others call is [notes-service](../examples/notes-service/) plus
|
|
26
|
+
[Action.md](Action.md), [ActionHttp.md](ActionHttp.md), [ActionMcp.md](ActionMcp.md),
|
|
27
|
+
[Authentication.md](Authentication.md) and [Testing.md](Testing.md).
|
|
28
|
+
|
|
29
|
+
Where things are, in a project that installs the package: these pages in
|
|
30
|
+
`node_modules/@gjermundgaraba/effect-actions/docs/`, the examples in `examples/` beside them, and
|
|
31
|
+
the declarations with their JSDoc, the reference for signatures, in `dist/<Module>.d.mts`, all at
|
|
32
|
+
the installed version. Install first ([setup.md](setup.md#install)): it needs TypeScript 7 and
|
|
33
|
+
one Effect release. Every module is a subpath import, `@gjermundgaraba/effect-actions/Action`;
|
|
34
|
+
there is no package root.
|
|
15
35
|
|
|
16
36
|
## Pages
|
|
17
37
|
|
|
18
|
-
| Read
|
|
19
|
-
|
|
|
20
|
-
| [setup.md](setup.md)
|
|
21
|
-
| [Action.md](Action.md)
|
|
22
|
-
| [
|
|
23
|
-
| [
|
|
24
|
-
| [
|
|
25
|
-
| [
|
|
26
|
-
| [
|
|
27
|
-
| [
|
|
28
|
-
| [
|
|
29
|
-
| [
|
|
38
|
+
| Read | When you need to |
|
|
39
|
+
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
40
|
+
| [setup.md](setup.md) | install, import the modules, write the minimal program, lay out an application, bundle a browser client |
|
|
41
|
+
| [Action.md](Action.md) | define a contract and who may call it, implement it, authorize calls, fail with declared and built-in errors |
|
|
42
|
+
| [Action-in-process.md](Action-in-process.md) | call actions from your own code, such as a job; share one build of the implementations across servers and jobs |
|
|
43
|
+
| [ActionHttp.md](ActionHttp.md) | bind actions to JSON routes, serve them, limit request bodies, publish OpenAPI, call them from Effect or a browser |
|
|
44
|
+
| [ActionMcp.md](ActionMcp.md) | serve MCP tools over HTTP or stdio, describe them to a model, return images, protect them, allow browser clients, add prompts and resources |
|
|
45
|
+
| [ActionRpc.md](ActionRpc.md) | bind actions to Effect rpcs, serve them on a protocol, call them, send request context with a call |
|
|
46
|
+
| [ActionToolkit.md](ActionToolkit.md) | give a model actions as a native Effect AI `Toolkit`, name the caller, ask for approval |
|
|
47
|
+
| [ActionCli.md](ActionCli.md) | run actions as local commands, or call a server from commands; shape flags, input and output; add a command of your own |
|
|
48
|
+
| [Authentication.md](Authentication.md) | declare how callers sign in, verify credentials, publish OAuth discovery, step up for a scope |
|
|
49
|
+
| [Authentication-advanced.md](Authentication-advanced.md) | use an API key or a cookie, read the request in a verifier, protect a route of your own, run a request policy before authentication |
|
|
50
|
+
| [Testing.md](Testing.md) | test an implementation in process, and routes, tools and rpcs in memory, as a signed-in caller; assert on errors |
|
|
51
|
+
| [guarantees.md](guarantees.md) | see the status and body of one case, such as bad input, a declared error or a refusal, on each surface; what the library leaves to you |
|
|
52
|
+
| [CONTEXT.md](CONTEXT.md) | look up a term the pages use |
|
|
30
53
|
|
|
31
|
-
##
|
|
54
|
+
## Examples
|
|
32
55
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
56
|
+
| Example | What it is | Start from it when |
|
|
57
|
+
| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
58
|
+
| [quickstart](../examples/quickstart/) | One action, served as a JSON route and an MCP tool, called with its typed client. | You want the smallest thing that runs. |
|
|
59
|
+
| [notes-service](../examples/notes-service/) | A service with bearer authentication: a JSON API and MCP tools from one implementation, a CLI, a browser client. | You are building a service others call. Most applications are this. |
|
|
60
|
+
| [mcp-tools](../examples/mcp-tools/) | An MCP server whose tools wrap an upstream HTTP API, over stdio and over HTTP, with an image result. | You are putting an existing API in front of a model. |
|
|
61
|
+
| [assistant](../examples/assistant/) | No server: the same implementations as a model's tools, with approval of writes, and as local commands. | A model calls your code in process, or you are building a local CLI. |
|
|
62
|
+
| [live-rpc](../examples/live-rpc/) | Effect RPC over a WebSocket with a browser-shaped client, each message carrying its own token. | A client keeps a connection open, or the application speaks Effect RPC. |
|
|
63
|
+
| [kitchen-sink](../examples/kitchen-sink/) | The reference: a multi-tenant issue tracker using every module and nearly every option, with an index. | You need a working use of one specific feature. |
|
|
38
64
|
|
|
39
|
-
|
|
65
|
+
Each example's README says where to look for what. The index in
|
|
66
|
+
[kitchen-sink](../examples/kitchen-sink/README.md#index) maps every export and option it uses to
|
|
67
|
+
a file. Every token, user and store in the examples is a demo, marked `DEMO ONLY` where it must
|
|
68
|
+
change before a deployment.
|
|
40
69
|
|
|
41
|
-
|
|
70
|
+
## Choose a surface
|
|
42
71
|
|
|
43
|
-
|
|
72
|
+
| Callers | Use |
|
|
73
|
+
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
74
|
+
| Speak JSON over HTTP | `ActionHttp`; clients use `ActionHttp.client`, or `ActionHttp.fetchClient` outside an Effect |
|
|
75
|
+
| Are Effect programs, such as a page over one WebSocket | `ActionRpc`; clients use `ActionRpc.client` |
|
|
76
|
+
| Are MCP clients | `ActionMcp.layerHttp` for a hosted endpoint, `ActionMcp.runStdio` for a subprocess, which runs the handlers itself |
|
|
77
|
+
| Are a model in the same process | `ActionToolkit` |
|
|
78
|
+
| Are people or scripts in a terminal | `ActionCli`: `make` runs handlers locally, `remote` calls a server through its HTTP binding |
|
|
79
|
+
| Are your own code in the same process, such as a job or a test | `Action.client` |
|
|
44
80
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
[
|
|
81
|
+
Surfaces combine: every one that runs handlers takes the same implementations, one or a list,
|
|
82
|
+
and enforces each contract's `caller` and its implementation's `authorize`
|
|
83
|
+
([Action.md](Action.md#authorize-calls)).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Testing failure modes
|
|
2
|
+
|
|
3
|
+
What you see when something is wrong, and the fix. Search for a fragment of the message.
|
|
4
|
+
The tasks are in [Testing.md](Testing.md).
|
|
5
|
+
|
|
6
|
+
- A route answers an empty 500: a defect, or a result that does not encode. `Testing.layer` logs the cause as an error, and nothing for a request that succeeds or is refused: read the test's output, or collect it with a `Logger` or an `ErrorReporter` provided around the layer.
|
|
7
|
+
- A client method fails with `Action.Unauthenticated`: routes under `Testing.layer` keep their real authentication. Give the client a credential in `transformClient` ([Test as a signed-in caller](Testing.md#test-as-a-signed-in-caller)).
|
|
8
|
+
- A protected route answers 401 where the test expects 415 or 400: authentication runs before the body is read. Send a valid credential.
|
|
9
|
+
- `Type 'Provider<CurrentUser, "notes-service.Login">' is missing the following properties from type 'Scope'` at `it.effect`, or `… is not assignable to type 'never'` at `Effect.runPromise`: the routes serve a protected action without a provider of its descriptor. Provide the real one or a test verifier to the routes; a caller provided around `Testing.layer` does not satisfy them.
|
|
10
|
+
- `Type 'CurrentUser' is missing the following properties from type 'Scope'` at `it.effect`, or `… is not assignable to type 'never'` at `Effect.runPromise`: the test still owes the identity. An `Action.client` call of a protected action takes its caller per call: provide it around the call. Or a public action's handler reads the identity, which no route provides to it: make the action protected. Or, where the caller is provided already, a builder yields it: move that `yield*` into the handler ([Action-failures.md](Action-failures.md#where-an-implementation-is-served)).
|
|
11
|
+
- `Type '<Service>' is missing the following properties from type 'Scope'` at every `it.effect`, for a service that is no identity, such as a store, a limiter or a `LanguageModel`: a builder now yields it, and the routes require it. Provide its layer to `Testing.layer(routes)`, as the entrypoint provides it to the server.
|
|
12
|
+
- `Type 'unknown' is not assignable to type 'Scope'` at `it.effect`, or `Argument of type 'Effect<…>' is not assignable` at an `Effect.flip` or `Effect.exit`, around a loop over an array of calls to different actions: their successes and errors differ, so the array has no one element type. Annotate it, `ReadonlyArray<Effect.Effect<unknown, unknown>>`, with the caller as a third parameter for `Action.client` calls of protected actions, or assert each call on its own.
|
|
13
|
+
- An rpc call never answers under `Testing.layer`, and nothing is logged: the layer holds the rpc layer twice, under two protocols, and built it for the other one, a WebSocket. Give the rpc layer its WebSocket in the entrypoint, so the routes a test serves hold it once, under the test's protocol ([Serve the rpcs](ActionRpc.md#serve-the-rpcs)).
|
|
14
|
+
- A test passes whatever message an error carries: `toEqual` never compares a tagged error's `message`. Assert it separately ([Assert on errors](Testing.md#assert-on-errors)).
|
|
15
|
+
- The test reads a service the handlers never changed: it was provided inside the routes and again to the test, two instances. Provide it once, around `Testing.layer`, with `Layer.provideMerge`.
|
|
16
|
+
- `MCP tools/call "<name>" answered 404`: the endpoint is not at `/mcp`, the default. Pass `mcpClient` its `url`.
|
|
17
|
+
- `MCP tools/call "<name>" returned an error: Invalid parameters for tool ...`: the endpoint serves another contract under that name. Pass `mcpClient` the served contract value.
|
|
18
|
+
- A builder or handler fails with `NotFound: FileSystem.<method> (<path>)` for a file that exists, or a file route answers 500: with no `FileSystem` provided around `Testing.layer`, the routes read a no-op one. Provide `NodeFileSystem.layer` or `NodeServices.layer`. In a command test, the nearer of two provided is read: provide a fake `FileSystem` on the command, inside `NodeServices.layer`.
|
|
19
|
+
- `Argument of type 'Layer<…, FileSystem | Generator | HttpPlatform | HttpRouter | Path>' is not assignable` at `HttpRouter.toWebHandler(routes)`: a web handler's routes need the platform services `Testing.layer(routes)` supplies itself. Provide `HttpServer.layerServices` to them.
|
|
20
|
+
- `UrlError` from `HttpClientRequest.toWebResult` or `toWeb` of an `mcpRequest`, or an `HttpClientError` whose reason is `InvalidUrlError` from a client method or a sent `mcpRequest`: the `url` is relative, such as the default `/mcp`, which only the `HttpClient` of `Testing.layer` resolves. Pass an absolute one.
|