@gjermundgaraba/effect-actions 0.19.0 → 0.21.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 +19 -41
- package/dist/Action.d.mts +140 -48
- package/dist/Action.mjs +88 -44
- package/dist/ActionCli.d.mts +70 -35
- package/dist/ActionCli.mjs +53 -34
- package/dist/ActionHttp.d.mts +70 -38
- package/dist/ActionHttp.mjs +76 -25
- package/dist/ActionMcp.d.mts +56 -24
- package/dist/ActionMcp.mjs +104 -61
- package/dist/ActionRpc.d.mts +222 -57
- package/dist/ActionRpc.mjs +301 -49
- package/dist/ActionToolkit.d.mts +22 -7
- package/dist/ActionToolkit.mjs +37 -14
- package/dist/Authentication.d.mts +42 -12
- package/dist/Authentication.mjs +38 -32
- package/dist/Event.d.mts +370 -0
- package/dist/Event.mjs +103 -0
- package/dist/Testing.d.mts +1 -3
- package/dist/Testing.mjs +2 -3
- package/dist/Webhook.d.mts +146 -0
- package/dist/Webhook.mjs +72 -0
- package/dist/authentication/provider.d.mts +35 -12
- package/dist/authentication/provider.mjs +2 -3
- package/dist/authentication/refusal.mjs +3 -4
- package/dist/cli/commands.d.mts +9 -7
- package/dist/cli/commands.mjs +85 -35
- package/dist/contract/ast.mjs +5 -3
- package/dist/contract/authorizer.d.mts +8 -0
- package/dist/contract/authorizer.mjs +20 -0
- package/dist/contract/call.d.mts +9 -8
- package/dist/contract/call.mjs +49 -8
- package/dist/contract/callback.d.mts +7 -0
- package/dist/contract/callback.mjs +27 -0
- package/dist/contract/caller.d.mts +12 -3
- package/dist/contract/caller.mjs +23 -5
- package/dist/contract/canonical.mjs +10 -0
- package/dist/contract/codec.d.mts +9 -0
- package/dist/contract/codec.mjs +9 -0
- package/dist/contract/codes.mjs +81 -0
- package/dist/contract/declared.d.mts +0 -2
- package/dist/contract/declared.mjs +26 -11
- package/dist/contract/digest.mjs +6 -0
- package/dist/contract/errors.d.mts +9 -5
- package/dist/contract/errors.mjs +2 -3
- package/dist/contract/feed.d.mts +127 -0
- package/dist/contract/feed.mjs +249 -0
- package/dist/contract/hints.mjs +1 -2
- package/dist/contract/hub.d.mts +94 -0
- package/dist/contract/hub.mjs +133 -0
- package/dist/contract/idempotency.d.mts +47 -0
- package/dist/contract/idempotency.mjs +99 -0
- package/dist/contract/implementation.d.mts +73 -51
- package/dist/contract/implementation.mjs +94 -60
- package/dist/contract/input-check.d.mts +15 -0
- package/dist/contract/input-check.mjs +16 -0
- package/dist/contract/lease.mjs +28 -0
- package/dist/contract/listed.d.mts +4 -0
- package/dist/contract/listed.mjs +22 -0
- package/dist/contract/log.d.mts +41 -0
- package/dist/contract/log.mjs +144 -0
- package/dist/contract/media.d.mts +1 -3
- package/dist/contract/media.mjs +1 -2
- package/dist/contract/names.mjs +15 -6
- package/dist/contract/occurrence.d.mts +29 -0
- package/dist/contract/occurrence.mjs +45 -0
- package/dist/contract/pages.d.mts +13 -0
- package/dist/contract/pages.mjs +39 -0
- package/dist/contract/progress.d.mts +26 -0
- package/dist/contract/progress.mjs +72 -0
- package/dist/contract/push.d.mts +20 -0
- package/dist/contract/push.mjs +96 -0
- package/dist/contract/receiving.d.mts +29 -0
- package/dist/contract/receiving.mjs +42 -0
- package/dist/contract/recent.mjs +12 -0
- package/dist/contract/reported.mjs +16 -0
- package/dist/contract/serving.mjs +63 -0
- package/dist/contract/status.mjs +15 -7
- package/dist/contract/stream.d.mts +34 -0
- package/dist/contract/stream.mjs +31 -0
- package/dist/contract/subscribe.d.mts +45 -0
- package/dist/contract/subscribe.mjs +50 -0
- package/dist/contract/transient.mjs +28 -0
- package/dist/contract/watch.d.mts +18 -0
- package/dist/contract/watch.mjs +105 -0
- package/dist/contract/webhooks.d.mts +42 -0
- package/dist/contract/webhooks.mjs +53 -0
- package/dist/http/client.d.mts +37 -14
- package/dist/http/client.mjs +52 -14
- package/dist/http/events.d.mts +56 -0
- package/dist/http/events.mjs +102 -0
- package/dist/http/placed.d.mts +36 -0
- package/dist/http/placed.mjs +71 -0
- package/dist/http/schema-errors.mjs +2 -3
- package/dist/http/streamed.d.mts +21 -0
- package/dist/http/streamed.mjs +94 -0
- package/dist/http/watch.mjs +50 -0
- package/dist/mcp/bridge.mjs +283 -0
- package/dist/mcp/client.d.mts +30 -99
- package/dist/mcp/client.mjs +60 -134
- package/dist/mcp/events.mjs +142 -0
- package/dist/mcp/progress.mjs +34 -0
- package/dist/mcp/protocol.mjs +40 -15
- package/dist/mcp/reply.d.mts +79 -0
- package/dist/mcp/reply.mjs +99 -0
- package/dist/mcp/request.d.mts +40 -0
- package/dist/mcp/request.mjs +51 -0
- package/dist/mcp/watch.mjs +115 -0
- package/dist/rpc/watch.mjs +39 -0
- package/dist/stdio/console.mjs +14 -7
- package/dist/testing/memory.d.mts +1 -3
- package/dist/testing/memory.mjs +4 -5
- package/dist/webhook/address.mjs +47 -0
- package/dist/webhook/attempt.mjs +65 -0
- package/dist/webhook/receiver.mjs +67 -0
- package/dist/webhook/signing.mjs +28 -0
- package/dist/webhook/stored.mjs +57 -0
- package/dist/webhook/subscriptions.mjs +342 -0
- package/docs/Action-failures.md +2 -2
- package/docs/Action-streams.md +115 -0
- package/docs/Action.md +58 -124
- package/docs/ActionCli-failures.md +1 -2
- package/docs/ActionCli.md +83 -88
- package/docs/ActionHttp-failures.md +3 -3
- package/docs/ActionHttp.md +90 -70
- package/docs/ActionMcp-failures.md +2 -1
- package/docs/ActionMcp.md +120 -92
- package/docs/ActionRpc-events.md +126 -0
- package/docs/ActionRpc-failures.md +1 -2
- package/docs/ActionRpc.md +108 -79
- package/docs/ActionToolkit-failures.md +1 -2
- package/docs/ActionToolkit.md +33 -90
- package/docs/Authentication-advanced.md +57 -61
- package/docs/Authentication-failures.md +5 -4
- package/docs/Authentication.md +23 -58
- package/docs/Event-failures.md +15 -0
- package/docs/Event.md +281 -0
- package/docs/README.md +51 -82
- package/docs/Testing-failures.md +1 -5
- package/docs/Testing.md +126 -86
- package/docs/Webhook-failures.md +8 -0
- package/docs/Webhook.md +319 -0
- package/docs/setup-failures.md +2 -3
- package/docs/setup.md +32 -53
- package/examples/README.md +12 -37
- package/examples/assistant/README.md +21 -26
- package/examples/assistant/src/agent.ts +66 -24
- package/examples/assistant/src/cli.ts +7 -4
- package/examples/assistant/src/commands.ts +9 -4
- package/examples/assistant/src/identity/Identity.ts +5 -4
- package/examples/assistant/src/identity/authorize.ts +13 -5
- package/examples/assistant/src/identity/handlers.ts +3 -2
- package/examples/assistant/src/identity/permissions.ts +9 -3
- package/examples/assistant/src/platform/tasks-file.ts +15 -11
- package/examples/assistant/src/scripted.ts +7 -4
- package/examples/assistant/src/tasks/Tasks.ts +20 -6
- package/examples/assistant/src/tasks/handlers.ts +9 -5
- package/examples/assistant/src/tasks/store.ts +38 -11
- package/examples/assistant/src/toolkit.ts +11 -7
- package/examples/assistant/test/commands.test.ts +53 -27
- package/examples/assistant/test/identity/identity.test.ts +27 -11
- package/examples/assistant/test/platform/tasks-file.test.ts +39 -23
- package/examples/assistant/test/tasks/tasks.test.ts +62 -34
- package/examples/assistant/test/toolkit.test.ts +104 -75
- package/examples/build-events/README.md +83 -0
- package/examples/build-events/src/Api.ts +27 -0
- package/examples/build-events/src/Rpc.ts +26 -0
- package/examples/build-events/src/builds/Builds.ts +108 -0
- package/examples/build-events/src/builds/events.ts +54 -0
- package/examples/build-events/src/builds/handlers.ts +58 -0
- package/examples/build-events/src/builds/log.ts +30 -0
- package/examples/build-events/src/builds/status.ts +28 -0
- package/examples/build-events/src/builds/store.ts +130 -0
- package/examples/build-events/src/cli.ts +18 -0
- package/examples/build-events/src/commands.ts +32 -0
- package/examples/build-events/src/deploys/Deploys.ts +44 -0
- package/examples/build-events/src/deploys/approvers.ts +55 -0
- package/examples/build-events/src/deploys/events.ts +32 -0
- package/examples/build-events/src/deploys/handlers.ts +38 -0
- package/examples/build-events/src/identity/Identity.ts +29 -0
- package/examples/build-events/src/identity/members.ts +7 -0
- package/examples/build-events/src/identity/verifier.ts +22 -0
- package/examples/build-events/src/notifier/Notices.ts +26 -0
- package/examples/build-events/src/notifier/routes.ts +71 -0
- package/examples/build-events/src/notifier.ts +48 -0
- package/examples/build-events/src/routes.ts +71 -0
- package/examples/build-events/src/server.ts +62 -0
- package/examples/build-events/test/builds/events.test.ts +68 -0
- package/examples/build-events/test/builds/log.test.ts +71 -0
- package/examples/build-events/test/builds/progress.test.ts +57 -0
- package/examples/build-events/test/builds/status.test.ts +42 -0
- package/examples/build-events/test/commands.test.ts +103 -0
- package/examples/build-events/test/deploys/events.test.ts +52 -0
- package/examples/build-events/test/http.test.ts +95 -0
- package/examples/build-events/test/notifier.test.ts +80 -0
- package/examples/build-events/test/routes.test.ts +96 -0
- package/examples/build-events/test/rpc.test.ts +84 -0
- package/examples/build-events/test/support/as.ts +8 -0
- package/examples/build-events/test/support/memory.ts +43 -0
- package/examples/build-events/test/webhooks.test.ts +143 -0
- package/examples/kitchen-sink/README.md +178 -166
- package/examples/kitchen-sink/src/Actions.ts +11 -3
- package/examples/kitchen-sink/src/Api.ts +12 -6
- package/examples/kitchen-sink/src/Rpc.ts +7 -5
- package/examples/kitchen-sink/src/admin/Admin.ts +21 -10
- package/examples/kitchen-sink/src/admin/authenticate.ts +8 -4
- package/examples/kitchen-sink/src/admin/authorize.ts +13 -5
- package/examples/kitchen-sink/src/admin/grants.ts +5 -3
- package/examples/kitchen-sink/src/admin/handlers.ts +9 -5
- package/examples/kitchen-sink/src/agent.ts +14 -6
- package/examples/kitchen-sink/src/assistant/Assistant.ts +21 -15
- package/examples/kitchen-sink/src/assistant/conversations.ts +14 -9
- package/examples/kitchen-sink/src/assistant/demo-model.ts +2 -2
- package/examples/kitchen-sink/src/assistant/handlers.ts +42 -22
- package/examples/kitchen-sink/src/attachments/Attachments.ts +6 -6
- package/examples/kitchen-sink/src/attachments/download.ts +7 -6
- package/examples/kitchen-sink/src/attachments/handlers.ts +9 -4
- package/examples/kitchen-sink/src/attachments/store.ts +25 -18
- package/examples/kitchen-sink/src/browser.ts +10 -4
- package/examples/kitchen-sink/src/cli.ts +28 -12
- package/examples/kitchen-sink/src/config.ts +1 -1
- package/examples/kitchen-sink/src/identity/Identity.ts +24 -10
- package/examples/kitchen-sink/src/identity/authenticate.ts +19 -11
- package/examples/kitchen-sink/src/identity/authorize.ts +14 -4
- package/examples/kitchen-sink/src/identity/directory.ts +16 -7
- package/examples/kitchen-sink/src/identity/handlers.ts +1 -0
- package/examples/kitchen-sink/src/identity/members.ts +7 -2
- package/examples/kitchen-sink/src/identity/quota.ts +23 -20
- package/examples/kitchen-sink/src/issues/Issues.ts +91 -15
- package/examples/kitchen-sink/src/issues/assistant.ts +4 -2
- package/examples/kitchen-sink/src/issues/events.ts +46 -0
- package/examples/kitchen-sink/src/issues/export.ts +35 -0
- package/examples/kitchen-sink/src/issues/guide.ts +4 -3
- package/examples/kitchen-sink/src/issues/handlers.ts +20 -7
- package/examples/kitchen-sink/src/issues/store.ts +156 -67
- package/examples/kitchen-sink/src/issues/write-limit.ts +44 -18
- package/examples/kitchen-sink/src/memory.ts +11 -2
- package/examples/kitchen-sink/src/ops.ts +10 -5
- package/examples/kitchen-sink/src/request-policy.ts +25 -9
- package/examples/kitchen-sink/src/routes.ts +67 -27
- package/examples/kitchen-sink/src/sdk.ts +22 -12
- package/examples/kitchen-sink/src/seed.ts +15 -9
- package/examples/kitchen-sink/src/server.ts +24 -10
- package/examples/kitchen-sink/src/stdio.ts +9 -3
- package/examples/kitchen-sink/src/workspaces/Workspaces.ts +10 -7
- package/examples/kitchen-sink/src/workspaces/handlers.ts +3 -2
- package/examples/kitchen-sink/src/workspaces/registry.ts +16 -10
- package/examples/kitchen-sink/src/workspaces/tenant.ts +12 -5
- package/examples/kitchen-sink/test/admin/admin.test.ts +38 -23
- package/examples/kitchen-sink/test/assistant/assistant.test.ts +471 -299
- package/examples/kitchen-sink/test/attachments/attachments.test.ts +135 -94
- package/examples/kitchen-sink/test/identity/identity.test.ts +110 -82
- package/examples/kitchen-sink/test/identity/session.test.ts +65 -43
- package/examples/kitchen-sink/test/issues/assistant.test.ts +50 -31
- package/examples/kitchen-sink/test/issues/events.test.ts +273 -0
- package/examples/kitchen-sink/test/issues/export.test.ts +120 -0
- package/examples/kitchen-sink/test/issues/issues.test.ts +105 -75
- package/examples/kitchen-sink/test/request-policy.test.ts +130 -33
- package/examples/kitchen-sink/test/routes.test.ts +102 -57
- package/examples/kitchen-sink/test/support/as.ts +9 -0
- package/examples/kitchen-sink/test/support/scripted.ts +6 -5
- package/examples/kitchen-sink/test/support/services.ts +17 -0
- package/examples/live-rpc/README.md +33 -35
- package/examples/live-rpc/src/Rpc.ts +9 -5
- package/examples/live-rpc/src/browser.ts +39 -11
- package/examples/live-rpc/src/chat/Chat.ts +30 -16
- package/examples/live-rpc/src/chat/authorize.ts +2 -1
- package/examples/live-rpc/src/chat/events.ts +74 -0
- package/examples/live-rpc/src/chat/handlers.ts +13 -5
- package/examples/live-rpc/src/chat/room.ts +9 -5
- package/examples/live-rpc/src/chat/store.ts +51 -14
- package/examples/live-rpc/src/client.ts +22 -5
- package/examples/live-rpc/src/identity/Identity.ts +3 -3
- package/examples/live-rpc/src/identity/verifier.ts +8 -4
- package/examples/live-rpc/src/page.ts +58 -12
- package/examples/live-rpc/src/routes.ts +14 -10
- package/examples/live-rpc/src/server.ts +15 -5
- package/examples/live-rpc/test/browser.test.ts +127 -50
- package/examples/live-rpc/test/chat/chat.test.ts +215 -23
- package/examples/live-rpc/test/routes.test.ts +130 -46
- package/examples/live-rpc/test/server.test.ts +94 -33
- package/examples/mcp-tools/README.md +24 -38
- package/examples/mcp-tools/src/platform/fake-farm.ts +63 -24
- package/examples/mcp-tools/src/platform/print-farm.ts +59 -29
- package/examples/mcp-tools/src/printers/Printers.ts +18 -7
- package/examples/mcp-tools/src/printers/farm.ts +15 -5
- package/examples/mcp-tools/src/printers/handlers.ts +12 -6
- package/examples/mcp-tools/src/routes.ts +8 -6
- package/examples/mcp-tools/src/server.ts +12 -5
- package/examples/mcp-tools/src/stdio.ts +3 -2
- package/examples/mcp-tools/test/printers/printers.test.ts +132 -89
- package/examples/mcp-tools/test/routes.test.ts +140 -81
- package/examples/notes-service/README.md +30 -45
- package/examples/notes-service/src/Api.ts +1 -0
- package/examples/notes-service/src/browser.ts +15 -14
- package/examples/notes-service/src/cli.ts +8 -3
- package/examples/notes-service/src/commands.ts +46 -20
- package/examples/notes-service/src/identity/Identity.ts +2 -2
- package/examples/notes-service/src/identity/authorize.ts +6 -2
- package/examples/notes-service/src/identity/handlers.ts +3 -2
- package/examples/notes-service/src/identity/users.ts +2 -1
- package/examples/notes-service/src/identity/verifier.ts +12 -6
- package/examples/notes-service/src/notes/Notes.ts +46 -22
- package/examples/notes-service/src/notes/handlers.ts +18 -9
- package/examples/notes-service/src/notes/store.ts +70 -43
- package/examples/notes-service/src/routes.ts +7 -5
- package/examples/notes-service/src/server.ts +12 -5
- package/examples/notes-service/src/status/Status.ts +1 -1
- package/examples/notes-service/src/status/handlers.ts +5 -3
- package/examples/notes-service/src/stdio.ts +7 -3
- package/examples/notes-service/src/worker.ts +6 -3
- package/examples/notes-service/test/commands.test.ts +164 -91
- package/examples/notes-service/test/notes/as.ts +3 -1
- package/examples/notes-service/test/notes/idempotency.test.ts +97 -67
- package/examples/notes-service/test/notes/in-process.test.ts +26 -15
- package/examples/notes-service/test/notes/notes.test.ts +49 -31
- package/examples/notes-service/test/routes.test.ts +106 -71
- package/examples/notes-service/test/worker.test.ts +21 -13
- package/examples/quickstart/README.md +2 -6
- package/examples/quickstart/src/Api.ts +1 -0
- package/examples/quickstart/src/client.ts +6 -3
- package/examples/quickstart/src/greeting/Greeting.ts +1 -1
- package/examples/quickstart/src/greeting/handlers.ts +5 -2
- package/examples/quickstart/src/routes.ts +3 -2
- package/examples/quickstart/src/server.ts +6 -2
- package/examples/quickstart/test/routes.test.ts +3 -2
- package/package.json +19 -17
package/dist/ActionRpc.mjs
CHANGED
|
@@ -1,37 +1,90 @@
|
|
|
1
1
|
import { Anyone } from "./contract/caller.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { InvalidInput, Unauthenticated } from "./contract/errors.mjs";
|
|
3
|
+
import { bindingErrors, bindingOf, eventErrors } from "./contract/declared.mjs";
|
|
4
|
+
import { reservedList, reservedNames } from "./contract/names.mjs";
|
|
5
|
+
import { Progressed, answerFrom, elementsFrom } from "./contract/progress.mjs";
|
|
6
|
+
import { elementOf } from "./contract/stream.mjs";
|
|
7
|
+
import { acquire, elementsOf } from "./contract/implementation.mjs";
|
|
8
|
+
import { checked, checkedStream, decodeInput, inputParseOptions, sendingJson, watchInput } from "./contract/call.mjs";
|
|
9
|
+
import { concealed } from "./contract/reported.mjs";
|
|
10
|
+
import { acquireFeeds } from "./contract/feed.mjs";
|
|
11
|
+
import { PushedOf, Subscription, SubscriptionOf, queuedPush } from "./contract/push.mjs";
|
|
4
12
|
import { providerOf } from "./authentication/provider.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
13
|
+
import { listed } from "./contract/listed.mjs";
|
|
14
|
+
import { provideServed, servingOf } from "./contract/serving.mjs";
|
|
15
|
+
import { watching } from "./contract/watch.mjs";
|
|
16
|
+
import { transportOf } from "./rpc/watch.mjs";
|
|
17
|
+
import { Cause, Effect, ErrorReporter, Layer, Match, Option, Predicate, Queue, Result, Schedule, Schema, SchemaTransformation, Stream } from "effect";
|
|
7
18
|
import { HttpClientRequest, HttpServerRequest } from "effect/http";
|
|
8
19
|
import { HttpApiBuilder } from "effect/http-api";
|
|
9
|
-
import { Rpc, RpcClient, RpcGroup,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
import { Rpc, RpcClient, RpcGroup, RpcMiddleware, RpcSchema, RpcServer } from "effect/rpc";
|
|
21
|
+
// src/rpc/ActionRpc.ts
|
|
22
|
+
/**
|
|
23
|
+
* What the stream rpc of an action declaring `progress` sends: a `Progress` for each value its
|
|
24
|
+
* handler reports, then a `Final` with its answer.
|
|
25
|
+
*/
|
|
26
|
+
const ProgressedOf = (progress, success) => Schema.Union([Schema.TaggedStruct("Progress", { value: progress }), Schema.TaggedStruct("Final", { value: success })]);
|
|
27
|
+
const elementedOf = (element, progress) => Schema.Union([Schema.TaggedStruct("Element", { value: element }), ...progress === void 0 ? [] : [Schema.TaggedStruct("Progress", { value: progress })]]);
|
|
28
|
+
/** Whether `action`'s rpc is a stream rpc: it answers a stream, or declares `progress`. */
|
|
29
|
+
const streams = (action) => action.progress !== void 0 || elementOf(action.success) !== void 0;
|
|
30
|
+
/** What `action`'s rpc answers, or each element it streams where it is a stream rpc. */
|
|
31
|
+
const sentOf = (action) => {
|
|
32
|
+
const element = elementOf(action.success);
|
|
33
|
+
if (element !== void 0) return elementedOf(element, action.progress);
|
|
34
|
+
return action.progress === void 0 ? action.success : ProgressedOf(action.progress, action.success);
|
|
35
|
+
};
|
|
36
|
+
const authenticatedKey = "effect-actions/ActionRpc/Authenticated";
|
|
37
|
+
const nativeOf = (group) => group;
|
|
38
|
+
const authenticatedOf = (authentication) => RpcMiddleware.Service()(authenticatedKey, { error: Schema.Union([Unauthenticated, ...authentication.error]) });
|
|
39
|
+
/** The middleware authenticating `rpc`, wherever among its middleware it runs. */
|
|
40
|
+
const authenticatedIn = (rpc) => [...rpc.middlewares].find(({ key }) => key === authenticatedKey);
|
|
41
|
+
function make(contracts, options = {}) {
|
|
42
|
+
const { actions, events } = listed(contracts);
|
|
43
|
+
const bound = bindingOf("ActionRpc binding", actions, events, options);
|
|
44
|
+
const rpcOf = (action) => Rpc.make(action.name, {
|
|
14
45
|
payload: action.input,
|
|
15
|
-
success: action
|
|
16
|
-
error: Schema.Union(bindingErrors(action, bound))
|
|
17
|
-
|
|
46
|
+
success: sentOf(action),
|
|
47
|
+
error: Schema.Union(bindingErrors(action, bound)),
|
|
48
|
+
stream: streams(action)
|
|
49
|
+
});
|
|
50
|
+
const given = options.rpcs === void 0 ? RpcGroup.make() : nativeOf(options.rpcs);
|
|
51
|
+
for (const tag of given.requests.keys()) if (reservedNames.includes(tag) || contracts.some((contract) => contract.name === tag)) throw new Error(`ActionRpc binding: the rpc "${tag}" of rpcs is named after an action, an event type or a name the client keeps, ${reservedList} (rename it)`);
|
|
52
|
+
const rpcs = bound.authentication === void 0 ? given : given.middleware(authenticatedOf(bound.authentication));
|
|
53
|
+
const group = RpcGroup.make(...actions.map(rpcOf), ...events.map((event) => Rpc.make(event.name, {
|
|
54
|
+
payload: SubscriptionOf(event),
|
|
55
|
+
success: PushedOf(event),
|
|
56
|
+
error: Schema.Union(eventErrors(event, bound)),
|
|
57
|
+
stream: true
|
|
58
|
+
}))).merge(rpcs);
|
|
18
59
|
return {
|
|
19
60
|
actions,
|
|
61
|
+
events,
|
|
20
62
|
...bound,
|
|
63
|
+
rpcs,
|
|
21
64
|
group
|
|
22
65
|
};
|
|
23
66
|
}
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
67
|
+
const withRequestOf = (headers) => (effect) => effect.pipe(Effect.provideService(HttpServerRequest.HttpServerRequest, HttpServerRequest.fromClientRequest(HttpClientRequest.get("/", { headers }))), Effect.provideService(HttpServerRequest.ParsedSearchParams, {}));
|
|
68
|
+
/** `call` inside `steps`, the first innermost, each given the call's options. */
|
|
69
|
+
const throughSteps = (steps, call, options) => {
|
|
70
|
+
let wrapped = call;
|
|
71
|
+
for (const step of steps) {
|
|
72
|
+
const inner = wrapped;
|
|
73
|
+
wrapped = Effect.suspend(() => step(inner, options));
|
|
30
74
|
}
|
|
75
|
+
return wrapped;
|
|
31
76
|
};
|
|
77
|
+
/**
|
|
78
|
+
* The step authenticating a call with `authentication`'s provider: the identity its verification
|
|
79
|
+
* answers is provided to the call, the verification run as `verifying` runs it.
|
|
80
|
+
*/
|
|
81
|
+
const authenticatingOf = (authentication) => Effect.map(providerOf(authentication), (provider) => {
|
|
82
|
+
const verify = Effect.flatMap(HttpApiBuilder.securityDecode(authentication.security), provider.authenticate);
|
|
83
|
+
return (verifying) => (effect, options) => Effect.flatMap(verifying(verify, options), (actor) => Effect.provideService(effect, authentication.identity, actor));
|
|
84
|
+
});
|
|
32
85
|
var DeclaredFailure = class {
|
|
33
|
-
json;
|
|
34
86
|
[ErrorReporter.ignore] = true;
|
|
87
|
+
json;
|
|
35
88
|
constructor(json) {
|
|
36
89
|
this.json = json;
|
|
37
90
|
}
|
|
@@ -39,18 +92,32 @@ var DeclaredFailure = class {
|
|
|
39
92
|
return Predicate.isString(this.json) ? this.json : JSON.stringify(this.json);
|
|
40
93
|
}
|
|
41
94
|
};
|
|
42
|
-
const
|
|
95
|
+
const asJson = () => Schema.link()(Schema.Json, SchemaTransformation.transform({
|
|
43
96
|
decode: (json) => new DeclaredFailure(json),
|
|
44
97
|
encode: (failure) => failure.json
|
|
45
|
-
}))
|
|
98
|
+
}));
|
|
99
|
+
const Declared = Schema.instanceOf(DeclaredFailure, {
|
|
100
|
+
toCodecJson: asJson,
|
|
101
|
+
toCodecIso: asJson
|
|
102
|
+
});
|
|
103
|
+
const streamOf = (name) => Rpc.make(name, {
|
|
104
|
+
payload: Schema.Json,
|
|
105
|
+
success: Schema.Json,
|
|
106
|
+
error: Declared,
|
|
107
|
+
stream: true
|
|
108
|
+
});
|
|
109
|
+
const erasedOf = (actions, events) => RpcGroup.make(...actions.map((action) => streams(action) ? streamOf(action.name) : Rpc.make(action.name, {
|
|
110
|
+
payload: Schema.Json,
|
|
111
|
+
success: Schema.Json,
|
|
112
|
+
error: Declared
|
|
113
|
+
})), ...events.map((event) => streamOf(event.name)));
|
|
46
114
|
const callBoundaryOf = (error) => {
|
|
47
115
|
const encode = Schema.encodeUnknownEffect(Schema.toCodecJson(error));
|
|
48
|
-
const internal = (cause) => Effect.logError(cause).pipe(Effect.andThen(ErrorReporter.report(cause)), Effect.andThen(Effect.die(new InternalError())));
|
|
49
116
|
return (effect) => Effect.catchCause(effect, (cause) => {
|
|
50
|
-
if (Cause.hasDies(cause)) return
|
|
117
|
+
if (Cause.hasDies(cause)) return concealed(cause);
|
|
51
118
|
const failure = Cause.findFail(cause);
|
|
52
119
|
return Result.isSuccess(failure) ? encode(failure.success.error).pipe(Effect.matchCauseEffect({
|
|
53
|
-
onFailure: (unencoded) =>
|
|
120
|
+
onFailure: (unencoded) => concealed(Cause.combine(cause, unencoded)),
|
|
54
121
|
onSuccess: (encoded) => Effect.fail(new DeclaredFailure(encoded))
|
|
55
122
|
})) : Effect.failCause(cause);
|
|
56
123
|
});
|
|
@@ -61,42 +128,227 @@ const handlerOf = (action, run, steps, error) => {
|
|
|
61
128
|
const boundary = callBoundaryOf(error);
|
|
62
129
|
return (payload, options) => {
|
|
63
130
|
const call = Effect.suspend(() => decode(payload)).pipe(Effect.flatMap(run), Effect.flatMap(encode));
|
|
64
|
-
const wrapped = steps
|
|
131
|
+
const wrapped = throughSteps(steps, call, {
|
|
132
|
+
...options,
|
|
133
|
+
payload
|
|
134
|
+
});
|
|
135
|
+
return boundary(wrapped);
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* The handler of the stream rpc of an action declaring `progress` or answering a stream: its call
|
|
140
|
+
* authenticates before its stream starts, then runs in the call's scope, what `sending` sends sent
|
|
141
|
+
* one at a time, so a caller reading slowly holds up the call, whose progress then drops the
|
|
142
|
+
* oldest values. A failure ends the stream as a call's ends it.
|
|
143
|
+
*/
|
|
144
|
+
const streamingHandlerOf = (action, sending, steps, error) => {
|
|
145
|
+
const decode = decodeInput(action);
|
|
146
|
+
const boundary = callBoundaryOf(error);
|
|
147
|
+
const encodeProgress = Schema.encodeUnknownEffect(Schema.toCodecJson(action.progress ?? Schema.Never));
|
|
148
|
+
return (payload, options) => {
|
|
149
|
+
const call = Effect.gen(function* () {
|
|
150
|
+
const input = yield* decode(payload);
|
|
151
|
+
const sent = yield* Queue.bounded(1);
|
|
152
|
+
const send = (each) => Effect.asVoid(Queue.offer(sent, each));
|
|
153
|
+
yield* sending(input, { progress: (value) => Effect.flatMap(Effect.orDie(encodeProgress(value)), (json) => send(Progressed.Progress({ value: json }))) }, send).pipe(boundary, Queue.into(sent), Effect.forkScoped({ startImmediately: true }));
|
|
154
|
+
return sent;
|
|
155
|
+
});
|
|
156
|
+
const wrapped = throughSteps(steps, call, {
|
|
157
|
+
...options,
|
|
158
|
+
payload
|
|
159
|
+
});
|
|
160
|
+
return boundary(wrapped);
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
/** Each value `run` reports sent as a `Progress`, then its answer as a `Final`. */
|
|
164
|
+
const sendingAnswer = (success, run) => {
|
|
165
|
+
const encode = Schema.encodeUnknownEffect(Schema.toCodecJson(success));
|
|
166
|
+
return (input, reporting, send) => run(input, reporting).pipe(Effect.flatMap(encode), Effect.flatMap((value) => send(Progressed.Final({ value }))));
|
|
167
|
+
};
|
|
168
|
+
/** Each element of the stream `run` answers sent as an `Element`, beside its progress. */
|
|
169
|
+
const sendingElements = (element, run) => {
|
|
170
|
+
const encode = Schema.encodeUnknownEffect(Schema.toCodecJson(element));
|
|
171
|
+
return (input, reporting, send) => Stream.runForEach(elementsOf(run(input, reporting)), (each) => Effect.flatMap(encode(each), (value) => send(Progressed.Element({ value }))));
|
|
172
|
+
};
|
|
173
|
+
/** The key a protocol sets to its `SubscriptionKeeper` on its `RpcServer.Protocol` service. */
|
|
174
|
+
const subscriptionKeeper = Symbol.for("effect-actions/ActionRpc/subscriptionKeeper");
|
|
175
|
+
const keeperOf = (protocol) => {
|
|
176
|
+
return Predicate.hasProperty(protocol, subscriptionKeeper) ? protocol[subscriptionKeeper] : void 0;
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* The handler of one event type's stream rpc: the subscription's request authenticates and is
|
|
180
|
+
* admitted before its stream starts, so a refusal fails the call; the feed is then read from the
|
|
181
|
+
* call, in its scope, into a bounded queue, each element encoded, and read on through a source's
|
|
182
|
+
* failure, before its first `Checked` too. A later refusal ends the stream as a declared failure.
|
|
183
|
+
* Over a protocol with a `keeper`, the admitted subscription is kept, and every position sent.
|
|
184
|
+
*/
|
|
185
|
+
const subscriptionHandlerOf = (feed, steps, error, keeper) => {
|
|
186
|
+
const decode = Schema.decodeUnknownEffect(Subscription, inputParseOptions);
|
|
187
|
+
const decodeEventInput = decodeInput(feed.event);
|
|
188
|
+
const sent = Schema.toCodecJson(PushedOf(feed.event));
|
|
189
|
+
const encode = Schema.encodeEffect(sent);
|
|
190
|
+
const decodeSent = Schema.decodeUnknownOption(sent);
|
|
191
|
+
const boundary = callBoundaryOf(error);
|
|
192
|
+
return (payload, options) => {
|
|
193
|
+
const call = Effect.gen(function* () {
|
|
194
|
+
const { input, cursor } = yield* Effect.mapError(decode(payload), InvalidInput.fromSchemaError);
|
|
195
|
+
const open = yield* feed.stream(yield* decodeEventInput(input));
|
|
196
|
+
if (keeper === void 0) return yield* queuedPush(open, cursor, encode, { ending: boundary });
|
|
197
|
+
yield* keeper.keep({
|
|
198
|
+
clientId: options.client.id,
|
|
199
|
+
requestId: options.requestId,
|
|
200
|
+
tag: feed.event.name,
|
|
201
|
+
payload,
|
|
202
|
+
headers: Object.entries(options.headers),
|
|
203
|
+
resumeFrom: (values) => {
|
|
204
|
+
const reached = values.flatMap((value) => Option.toArray(decodeSent(value))).map((pushed) => Match.value(pushed).pipe(Match.tag("Failed", () => null), Match.orElse((positioned) => positioned.cursor))).findLast(Predicate.isNotNull);
|
|
205
|
+
return reached === void 0 ? void 0 : {
|
|
206
|
+
input,
|
|
207
|
+
cursor: reached
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
return yield* queuedPush(open, cursor, encode, {
|
|
212
|
+
ending: boundary,
|
|
213
|
+
inOrder: true
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
const wrapped = throughSteps(steps, call, {
|
|
65
217
|
...options,
|
|
66
218
|
payload
|
|
67
|
-
})
|
|
219
|
+
});
|
|
68
220
|
return boundary(wrapped);
|
|
69
221
|
};
|
|
70
222
|
};
|
|
223
|
+
/**
|
|
224
|
+
* The key a protocol answering each call once sets to `true` on its `RpcServer.Protocol`
|
|
225
|
+
* service, as the Cloudflare package's stub protocol does, since Effect's protocol has no flag of
|
|
226
|
+
* its own for it: `layer` refuses to serve a streaming action, an event type or a native stream
|
|
227
|
+
* rpc on it, where a stream is answered with a defect at its first element and one with none
|
|
228
|
+
* waits for good.
|
|
229
|
+
*/
|
|
230
|
+
const unaryProtocol = Symbol.for("effect-actions/ActionRpc/unary");
|
|
231
|
+
const verifyingNative = (verify, { headers }) => withRequestOf(headers)(verify).pipe(Effect.catchCauseIf(Cause.hasDies, concealed));
|
|
232
|
+
const requested = (effect, { headers }) => withRequestOf(headers)(effect);
|
|
233
|
+
const isUnary = (protocol) => Predicate.hasProperty(protocol, unaryProtocol) && protocol[unaryProtocol] === true;
|
|
71
234
|
function layer(binding, served, options = {}) {
|
|
72
|
-
const
|
|
235
|
+
const selected = servingOf("RPC binding", binding, served, options);
|
|
236
|
+
const { apps, actions, events, eventTypes } = selected;
|
|
73
237
|
const middleware = options.middleware ?? [];
|
|
74
|
-
const jsonGroup =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
error: DeclaredFromJson
|
|
78
|
-
})));
|
|
238
|
+
const jsonGroup = erasedOf(actions, eventTypes);
|
|
239
|
+
const natives = [...nativeOf(binding.rpcs).requests.values()];
|
|
240
|
+
const authentication = natives.length === 0 ? selected.authentication : binding.authentication;
|
|
79
241
|
const handlers = Layer.unwrap(Effect.gen(function* () {
|
|
80
|
-
const
|
|
81
|
-
if (
|
|
242
|
+
const unauthenticated = authentication === void 0 ? void 0 : natives.find((rpc) => authenticatedIn(rpc) === void 0);
|
|
243
|
+
if (unauthenticated !== void 0) return yield* Effect.die(/* @__PURE__ */ new Error(`ActionRpc.layer: the native rpc ${unauthenticated._tag} of a protected binding runs without its authentication: add it to the group ActionRpc.make is given as its rpcs, not to the binding's rpcs after`));
|
|
244
|
+
const protocol = yield* RpcServer.Protocol;
|
|
245
|
+
const keeper = keeperOf(protocol);
|
|
246
|
+
if (isUnary(protocol)) {
|
|
247
|
+
const streamed = [...actions.filter(streams), ...eventTypes].map(({ name }) => name);
|
|
248
|
+
const nativeStreams = natives.flatMap((rpc) => RpcSchema.isStreamSchema(rpc.successSchema) ? [rpc._tag] : []);
|
|
249
|
+
if (streamed.length + nativeStreams.length > 0) return yield* Effect.die(/* @__PURE__ */ new Error(`ActionRpc.layer serves a protocol answering each call once, and ${[...streamed, ...nativeStreams].join(", ")} stream: serve them over a socket or HTTP, and on this protocol the others, ${nativeStreams.length > 0 ? "from a binding whose rpcs do not stream, " : ""}listed in actions`));
|
|
250
|
+
}
|
|
82
251
|
const listedMiddleware = yield* Effect.forEach(middleware, (key) => Effect.service(key));
|
|
83
|
-
const
|
|
84
|
-
const bound = yield* acquire(apps);
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
252
|
+
const authenticating = authentication === void 0 ? void 0 : yield* authenticatingOf(authentication);
|
|
253
|
+
const bound = yield* acquire(apps, authentication);
|
|
254
|
+
const feeds = yield* acquireFeeds(events);
|
|
255
|
+
const stepsOf = (caller) => caller === Anyone || authenticating === void 0 ? [...listedMiddleware, requested] : [
|
|
256
|
+
...listedMiddleware,
|
|
257
|
+
authenticating((verify) => verify),
|
|
258
|
+
requested
|
|
259
|
+
];
|
|
260
|
+
const handlerFor = (action, run, steps, error) => {
|
|
261
|
+
const element = elementOf(action.success);
|
|
262
|
+
if (element !== void 0) return streamingHandlerOf(action, sendingElements(element, run), steps, error);
|
|
263
|
+
return action.progress === void 0 ? handlerOf(action, run, steps, error) : streamingHandlerOf(action, sendingAnswer(action.success, run), steps, error);
|
|
264
|
+
};
|
|
265
|
+
const byName = {
|
|
266
|
+
...Object.fromEntries(bound.map(([action, run]) => [action.name, handlerFor(action, run, stepsOf(action.caller), Schema.Union(bindingErrors(action, binding)))])),
|
|
267
|
+
...Object.fromEntries(feeds.map((feed) => [feed.event.name, subscriptionHandlerOf(feed, stepsOf(feed.event.caller), Schema.Union(eventErrors(feed.event, binding)), keeper)]))
|
|
268
|
+
};
|
|
269
|
+
const servedActions = jsonGroup.toLayer(byName);
|
|
270
|
+
const authenticated = natives.map(authenticatedIn).find(Predicate.isNotUndefined);
|
|
271
|
+
if (authenticated === void 0 || authenticating === void 0) return servedActions;
|
|
272
|
+
const authenticate = authenticating(verifyingNative);
|
|
273
|
+
return Layer.merge(servedActions, Layer.succeed(authenticated)(authenticate));
|
|
274
|
+
})).pipe(provideServed(selected));
|
|
275
|
+
return RpcServer.layer(jsonGroup.merge(binding.rpcs), { disableFatalDefects: true }).pipe(Layer.provide(handlers));
|
|
89
276
|
}
|
|
277
|
+
const isDeclared = (failure) => failure instanceof DeclaredFailure;
|
|
278
|
+
/** Fail with the declared failure a wrapper holds, decoded by `error`, or die where it does not. */
|
|
279
|
+
const failedOf = (error) => {
|
|
280
|
+
const decode = Schema.decodeUnknownEffect(Schema.toCodecJson(error));
|
|
281
|
+
return (failure) => Effect.flatMap(Effect.orDie(decode(failure.json)), Effect.fail);
|
|
282
|
+
};
|
|
283
|
+
/**
|
|
284
|
+
* A client's method of `action`, calling `method`, its rpc of `erasedOf`, with its input's JSON:
|
|
285
|
+
* what the server sends, its answer or each element it streams, and its declared failure, decoded
|
|
286
|
+
* by the action's schemas; one that does not decode is a defect, as the native client makes it.
|
|
287
|
+
*/
|
|
288
|
+
const actionMethodOf = (action, binding, method) => {
|
|
289
|
+
const decode = Schema.decodeUnknownEffect(Schema.toCodecJson(sentOf(action)));
|
|
290
|
+
const failed = failedOf(Schema.Union(bindingErrors(action, binding)));
|
|
291
|
+
const sent = (json) => Effect.orDie(decode(json));
|
|
292
|
+
if (!streams(action)) {
|
|
293
|
+
const call = method;
|
|
294
|
+
return checked(action, sendingJson, (json) => call(json).pipe(Effect.catchIf(isDeclared, failed), Effect.flatMap(sent)));
|
|
295
|
+
}
|
|
296
|
+
const call = method;
|
|
297
|
+
const progressed = (json) => call(json).pipe(Stream.catchIf(isDeclared, (failure) => Stream.fromEffect(failed(failure))), Stream.mapEffect(sent));
|
|
298
|
+
return elementOf(action.success) === void 0 ? checked(action, sendingJson, (json, onProgress) => answerFrom(progressed(json), onProgress)) : checkedStream(action, sendingJson, (json, onProgress) => elementsFrom(progressed(json), onProgress));
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* A client's subscription to `event`, calling `method`, its stream rpc of `erasedOf`, with its
|
|
302
|
+
* input's JSON and the cursor it resumes from: what the server sends, and its declared failure,
|
|
303
|
+
* decoded by the event's schemas; one that does not decode is a defect, as for an action.
|
|
304
|
+
*/
|
|
305
|
+
const eventMethodOf = (event, binding, method) => {
|
|
306
|
+
const decode = Schema.decodeUnknownEffect(Schema.toCodecJson(PushedOf(event)));
|
|
307
|
+
const failed = failedOf(Schema.Union(eventErrors(event, binding)));
|
|
308
|
+
const call = method;
|
|
309
|
+
return (input) => (cursor) => call({
|
|
310
|
+
input,
|
|
311
|
+
cursor
|
|
312
|
+
}).pipe(Stream.catchIf(isDeclared, (failure) => Stream.fromEffect(failed(failure))), Stream.mapEffect((json) => Effect.orDie(decode(json))));
|
|
313
|
+
};
|
|
90
314
|
function client(binding, options) {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
315
|
+
const actions = new Map(binding.actions.map((action) => [action.name, action]));
|
|
316
|
+
const events = new Map(binding.events.map((event) => [event.name, event]));
|
|
317
|
+
const clients = Effect.all([RpcClient.make(erasedOf(binding.actions, binding.events).merge(nativeOf(binding.rpcs)), options), Effect.map(Effect.service(RpcClient.Protocol), ({ supportsAck }) => !supportsAck)]);
|
|
318
|
+
return Effect.map(clients, ([native, silenceEnds]) => {
|
|
319
|
+
const rpcs = Object.entries(native);
|
|
320
|
+
const methods = Object.fromEntries(rpcs.filter(([tag]) => !events.has(tag)).map(([tag, method]) => {
|
|
321
|
+
const action = actions.get(tag);
|
|
322
|
+
return [tag, action === void 0 ? method : actionMethodOf(action, binding, method)];
|
|
323
|
+
}));
|
|
324
|
+
if (events.size === 0) return methods;
|
|
325
|
+
const feeds = new Map(rpcs.flatMap(([tag, method]) => {
|
|
326
|
+
const event = events.get(tag);
|
|
327
|
+
return event === void 0 ? [] : [[tag, eventMethodOf(event, binding, method)]];
|
|
98
328
|
}));
|
|
329
|
+
const watch = (event, input, watchOptions) => {
|
|
330
|
+
const feed = feeds.get(event.name);
|
|
331
|
+
if (feed === void 0) return Stream.die(/* @__PURE__ */ new Error(`ActionRpc.client: the binding holds no event type ${event.name} (watch one the binding ActionRpc.make returned lists)`));
|
|
332
|
+
return Stream.unwrap(Effect.map(watchInput(event, input), ({ json }) => watching(transportOf(event, feed(json), silenceEnds), watchOptions)));
|
|
333
|
+
};
|
|
334
|
+
return {
|
|
335
|
+
...methods,
|
|
336
|
+
watch
|
|
337
|
+
};
|
|
99
338
|
});
|
|
100
339
|
}
|
|
101
|
-
|
|
102
|
-
|
|
340
|
+
/**
|
|
341
|
+
* A socket client's `retryPolicy`, for `RpcClient.layerProtocolSocket`, that stops connecting
|
|
342
|
+
* again once the server closes the socket with one of `codes`, such as a refusal of its upgrade:
|
|
343
|
+
* until then it is Effect's own default, `Schedule.min([Schedule.exponential(500, 1.5),
|
|
344
|
+
* Schedule.spaced(5000)])`, which Effect does not export. A page refused with its code stops, its
|
|
345
|
+
* calls and streams failing with an `RpcClientError` whose `reason` is that `SocketCloseError`,
|
|
346
|
+
* its `code` the one it closed with.
|
|
347
|
+
*
|
|
348
|
+
* **Gotchas**
|
|
349
|
+
*
|
|
350
|
+
* - A client without it connects again after a refusal as after any close, every five seconds for good.
|
|
351
|
+
* - Any other socket error, a drop included, is retried as Effect's default retries it.
|
|
352
|
+
*/
|
|
353
|
+
const reconnectUntil = (...codes) => Schedule.min([Schedule.exponential(500, 1.5), Schedule.spaced(5e3)]).pipe(Schedule.setInputType(), Schedule.while(({ input: { reason } }) => !(Predicate.isTagged(reason, "SocketCloseError") && Predicate.hasProperty(reason, "code") && codes.some((code) => code === reason.code))));
|
|
354
|
+
export { client, layer, make, reconnectUntil, subscriptionKeeper, unaryProtocol };
|
package/dist/ActionToolkit.d.mts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { Codec } from "./contract/codec.mjs";
|
|
1
2
|
import { BuiltIns } from "./contract/errors.mjs";
|
|
2
|
-
import { ActionOf, BuildError, BuildServices, Holding, Known, Member, Offered, OptionalUnless, RequestOf, SelectedOf,
|
|
3
|
+
import { ActionOf, BuildError, BuildServices, Holding, Known, Member, Offered, OptionalUnless, RequestOf, SelectedOf, ServedActions, Serving } from "./contract/implementation.mjs";
|
|
4
|
+
import { ToolSuccess } from "./contract/stream.mjs";
|
|
3
5
|
import { Any } from "./Action.mjs";
|
|
4
6
|
import { Effect, Layer, Schema } from "effect";
|
|
5
7
|
import { Prompt, Tool, Toolkit } from "effect/ai";
|
|
6
|
-
//#region src/toolkit/ActionToolkit.d.ts
|
|
7
8
|
type NativeTool<A extends Any, R> = Tool.Tool<A["name"], {
|
|
8
9
|
readonly parameters: Schema.toCodecJson<A["input"]>;
|
|
9
|
-
readonly success: Schema.toCodecJson<A["success"]
|
|
10
|
+
readonly success: A["progress"] extends Codec ? Schema.toCodecJson<Schema.Union<readonly [ProgressOf<A["progress"]>, ToolSuccess<A["success"]>]>> : Schema.toCodecJson<ToolSuccess<A["success"]>>;
|
|
10
11
|
readonly failure: Schema.toCodecJson<Schema.Union<ReadonlyArray<A["error"][number] | BuiltIns>>>;
|
|
11
12
|
readonly failureMode: "return";
|
|
12
13
|
}, R>;
|
|
@@ -48,7 +49,7 @@ export interface Options<A extends Any> {
|
|
|
48
49
|
* Each keeps its implementation's authorization and builder. Defaults to every action of them,
|
|
49
50
|
* one added to an implementation later included.
|
|
50
51
|
*/
|
|
51
|
-
readonly actions?:
|
|
52
|
+
readonly actions?: readonly A[] | undefined;
|
|
52
53
|
/**
|
|
53
54
|
* Whether a model's call needs approval before it runs: a boolean, or an Effect of one, for
|
|
54
55
|
* each call, with Effect's native approval context. `LanguageModel` asks for approval
|
|
@@ -64,6 +65,19 @@ export interface Options<A extends Any> {
|
|
|
64
65
|
*/
|
|
65
66
|
readonly needsApproval?: (call: ToolCall<A>, context: Tool.NeedsApprovalContext) => boolean | Effect.Effect<boolean, unknown>;
|
|
66
67
|
}
|
|
68
|
+
/** A value an action reported, as a toolkit's preliminary result holds it. */
|
|
69
|
+
declare class Reported<A> {
|
|
70
|
+
readonly _tag = "Progress";
|
|
71
|
+
readonly value: A;
|
|
72
|
+
constructor(value: A);
|
|
73
|
+
}
|
|
74
|
+
/** A preliminary result of a tool whose action declares `progress`: one value it reported. */
|
|
75
|
+
declare const ProgressOf: (progress: Codec) => Schema.decodeTo<Schema.declare<Reported<unknown>, Reported<unknown>>, Schema.TaggedStruct<"Progress", {
|
|
76
|
+
readonly value: Codec;
|
|
77
|
+
}>, never, never>;
|
|
78
|
+
type ProgressOf<P> = P extends Codec ? Schema.decodeTo<Schema.declare<Reported<P["Type"]>>, Schema.TaggedStruct<"Progress", {
|
|
79
|
+
readonly value: P;
|
|
80
|
+
}>> : never;
|
|
67
81
|
/**
|
|
68
82
|
* Project implementations into Effect's native AI toolkit: one tool per action, keyed by its
|
|
69
83
|
* name, of the listed `actions` or, without them, of every action of the implementations.
|
|
@@ -74,11 +88,13 @@ export interface Options<A extends Any> {
|
|
|
74
88
|
*
|
|
75
89
|
* **Gotchas**
|
|
76
90
|
*
|
|
91
|
+
* - An action answering a stream, `Action.Stream`, answers its elements collected into one array once the stream ends, its tool's success that array.
|
|
92
|
+
* - An action declaring `progress` streams each value as a result with `preliminary: true` before its success, its `result` `{ _tag: "Progress", value }`, so its tool's success schema is that or `success`; a model reads the success alone.
|
|
77
93
|
* - A success is a result with `isFailure: false`, `result` the value and `encodedResult` its JSON; a declared error a result with `isFailure: true` and `result` the error, which the model reads, never a raised failure; a refusal as `Action.Unauthenticated` and `Action.Forbidden` say.
|
|
78
94
|
* - Arguments that do not decode are a result with `isFailure: true` whose `result` is an `AiError` with `reason` a `ToolParameterValidationError`, never `InvalidInput`, and neither `authorize` nor the handler runs; undeclared arguments are dropped and the call runs.
|
|
79
95
|
* - A defect, or a failure the action's errors reject, is the call's defect; a success or an error that does not encode fails the call with an `AiError`, `Failed to encode result for tool …`.
|
|
80
96
|
*/
|
|
81
|
-
export declare function make<const Apps extends
|
|
97
|
+
export declare function make<const Apps extends ServedActions, const O extends Options<ActionOf<Member<Apps>>> = {}>(implementations: Apps, ...options: OptionalUnless<O, Options<ActionOf<Member<Apps>>> & O & NoInfer<Known<O, Options<Any>>>>): Tools<{ readonly [T in ToolFor<Member<Apps>, Offered<O, ActionOf<Member<Apps>>>> as T["name"]]: T; }, BuildError<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>>, BuildServices<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>>>;
|
|
82
98
|
/** A tool call a model asked to have approved, which no answer has resolved yet. */
|
|
83
99
|
export interface Approval {
|
|
84
100
|
/** What a `tool-approval-response` names to answer it. */
|
|
@@ -103,6 +119,5 @@ export interface Approval {
|
|
|
103
119
|
* Guide: `docs/ActionToolkit.md` in this package; in an application:
|
|
104
120
|
* `examples/kitchen-sink/src/assistant/handlers.ts`.
|
|
105
121
|
*/
|
|
106
|
-
export declare const openApprovals: ({ content: messages }: Prompt.Prompt) =>
|
|
107
|
-
//#endregion
|
|
122
|
+
export declare const openApprovals: ({ content: messages }: Prompt.Prompt) => readonly Approval[];
|
|
108
123
|
export {};
|
package/dist/ActionToolkit.mjs
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { assertOnce, heldOnce } from "./contract/names.mjs";
|
|
2
1
|
import { projectedErrors } from "./contract/declared.mjs";
|
|
2
|
+
import { assertOnce, heldOnce } from "./contract/names.mjs";
|
|
3
|
+
import { progressSink } from "./contract/progress.mjs";
|
|
4
|
+
import { toolSuccessOf } from "./contract/stream.mjs";
|
|
5
|
+
import { Implementation, acquire, answeringWhole, provideHandlers, select, toList } from "./contract/implementation.mjs";
|
|
3
6
|
import { hintsOf } from "./contract/hints.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import { Effect, Schema } from "effect";
|
|
7
|
+
import { Effect, Schema, SchemaGetter } from "effect";
|
|
6
8
|
import { Tool, Toolkit } from "effect/ai";
|
|
7
|
-
|
|
9
|
+
// src/toolkit/ActionToolkit.ts
|
|
8
10
|
const annotateIfGiven = (tool, key, value) => value === void 0 ? tool : tool.annotate(key, value);
|
|
9
11
|
const annotateFromContract = (tool, action) => {
|
|
10
12
|
const { readOnlyHint, destructiveHint, idempotentHint, openWorldHint } = hintsOf(action);
|
|
@@ -12,13 +14,32 @@ const annotateFromContract = (tool, action) => {
|
|
|
12
14
|
const hinted = tool.annotate(Tool.Readonly, readOnlyHint).annotate(Tool.Destructive, destructiveHint).annotate(Tool.Idempotent, idempotentHint).annotate(Tool.OpenWorld, openWorldHint);
|
|
13
15
|
return annotateIfGiven(annotateIfGiven(hinted, Tool.Title, title), Tool.Meta, _meta === void 0 ? void 0 : { ..._meta });
|
|
14
16
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
17
|
+
/** A value an action reported, as a toolkit's preliminary result holds it. */
|
|
18
|
+
var Reported = class {
|
|
19
|
+
_tag = "Progress";
|
|
20
|
+
value;
|
|
21
|
+
constructor(value) {
|
|
22
|
+
this.value = value;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
/** A preliminary result of a tool whose action declares `progress`: one value it reported. */
|
|
26
|
+
const ProgressOf = (progress) => Schema.TaggedStruct("Progress", { value: progress }).pipe(Schema.decodeTo(Schema.declare((input) => input instanceof Reported), {
|
|
27
|
+
decode: SchemaGetter.transform(({ value }) => new Reported(value)),
|
|
28
|
+
encode: SchemaGetter.transform((reported) => ({
|
|
29
|
+
_tag: "Progress",
|
|
30
|
+
value: reported.value
|
|
31
|
+
}))
|
|
32
|
+
}));
|
|
33
|
+
const jsonToolOf = (action) => {
|
|
34
|
+
const success = toolSuccessOf(action.success);
|
|
35
|
+
return Tool.make(action.name, {
|
|
36
|
+
description: action.description,
|
|
37
|
+
parameters: Schema.toCodecJson(action.input),
|
|
38
|
+
success: Schema.toCodecJson(action.progress === void 0 ? success : Schema.Union([ProgressOf(action.progress), success])),
|
|
39
|
+
failure: Schema.toCodecJson(Schema.Union(projectedErrors(action))),
|
|
40
|
+
failureMode: "return"
|
|
41
|
+
});
|
|
42
|
+
};
|
|
22
43
|
function make(apps, options) {
|
|
23
44
|
const served = select(toList(apps), options?.actions);
|
|
24
45
|
const needsApproval = options?.needsApproval;
|
|
@@ -29,12 +50,15 @@ function make(apps, options) {
|
|
|
29
50
|
action,
|
|
30
51
|
input
|
|
31
52
|
}, context);
|
|
32
|
-
return Effect.isEffect(decided) ? Effect.
|
|
53
|
+
return Effect.isEffect(decided) ? Effect.orElseSucceed(decided, () => true) : decided;
|
|
33
54
|
}), { id: `${Implementation.runKey(app)}/${action.name}` });
|
|
34
55
|
const toolkit = Toolkit.make(...served.flatMap((app) => app.actions.map((action) => annotateFromContract(toolOf(action, app), action))));
|
|
35
56
|
return {
|
|
36
57
|
toolkit,
|
|
37
|
-
layer: toolkit.toLayer(Effect.map(acquire(served), (bound) => Object.fromEntries(bound.map(([action,
|
|
58
|
+
layer: toolkit.toLayer(Effect.map(acquire(served, void 0), (bound) => Object.fromEntries(bound.map(([action, handler]) => {
|
|
59
|
+
const run = answeringWhole(action, handler);
|
|
60
|
+
return [action.name, (input, { preliminary }) => run(input, progressSink((value) => preliminary(new Reported(value))))];
|
|
61
|
+
})))).pipe(provideHandlers(served))
|
|
38
62
|
};
|
|
39
63
|
}
|
|
40
64
|
/**
|
|
@@ -64,5 +88,4 @@ const openApprovals = ({ content: messages }) => {
|
|
|
64
88
|
}];
|
|
65
89
|
});
|
|
66
90
|
};
|
|
67
|
-
//#endregion
|
|
68
91
|
export { make, openApprovals };
|