@fora-protocol/sdk 1.0.3
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/LICENSE +201 -0
- package/README.md +37 -0
- package/gen/ts/vocab/functiontokens.js +78 -0
- package/gen/ts/vocab/functiontokens.ts +84 -0
- package/gen/ts/vocab/geographytokens.js +30 -0
- package/gen/ts/vocab/geographytokens.ts +37 -0
- package/gen/ts/vocab/pricingunits.js +56 -0
- package/gen/ts/vocab/pricingunits.ts +63 -0
- package/gen/ts/vocab/quotametrics.js +40 -0
- package/gen/ts/vocab/quotametrics.ts +47 -0
- package/gen/ts/vocab/usertypes.js +40 -0
- package/gen/ts/vocab/usertypes.ts +46 -0
- package/gen/ts/wire/base.js +245 -0
- package/gen/ts/wire/base.ts +255 -0
- package/gen/ts/wire/names.js +41 -0
- package/gen/ts/wire/names.ts +42 -0
- package/gen/ts/wire/schemas.js +111 -0
- package/gen/ts/wire/schemas.ts +216 -0
- package/package.json +164 -0
- package/sdk/ts/client/content.d.ts +74 -0
- package/sdk/ts/client/content.js +466 -0
- package/sdk/ts/client/errors.d.ts +79 -0
- package/sdk/ts/client/errors.js +148 -0
- package/sdk/ts/client/index.d.ts +241 -0
- package/sdk/ts/client/index.js +740 -0
- package/sdk/ts/client/route.d.ts +41 -0
- package/sdk/ts/client/route.js +107 -0
- package/sdk/ts/client/send.d.ts +64 -0
- package/sdk/ts/client/send.js +142 -0
- package/sdk/ts/client/transport.d.ts +187 -0
- package/sdk/ts/client/transport.js +381 -0
- package/sdk/ts/core/multisig-parse.d.ts +47 -0
- package/sdk/ts/core/multisig-parse.js +234 -0
- package/sdk/ts/core/sign-request.d.ts +83 -0
- package/sdk/ts/core/sign-request.js +146 -0
- package/sdk/ts/core/sign.d.ts +31 -0
- package/sdk/ts/core/sign.js +82 -0
- package/sdk/ts/core/signing-transport.d.ts +75 -0
- package/sdk/ts/core/signing-transport.js +172 -0
- package/sdk/ts/core/verifier.d.ts +177 -0
- package/sdk/ts/core/verifier.js +269 -0
- package/sdk/ts/core/verify-multisig-request.d.ts +72 -0
- package/sdk/ts/core/verify-multisig-request.js +112 -0
- package/sdk/ts/core/verify-request.d.ts +181 -0
- package/sdk/ts/core/verify-request.js +284 -0
- package/sdk/ts/core/window.d.ts +22 -0
- package/sdk/ts/core/window.js +43 -0
- package/sdk/ts/core/wire-canon.d.ts +20 -0
- package/sdk/ts/core/wire-canon.js +174 -0
- package/sdk/ts/hono/middleware.d.ts +38 -0
- package/sdk/ts/hono/middleware.js +64 -0
- package/sdk/ts/resolvers/errors.d.ts +130 -0
- package/sdk/ts/resolvers/errors.js +150 -0
- package/sdk/ts/resolvers/http.d.ts +98 -0
- package/sdk/ts/resolvers/http.js +274 -0
- package/sdk/ts/resolvers/index.d.ts +9 -0
- package/sdk/ts/resolvers/index.js +15 -0
- package/sdk/ts/resolvers/jwks.d.ts +2 -0
- package/sdk/ts/resolvers/jwks.js +43 -0
- package/sdk/ts/resolvers/offer-key-cache.d.ts +68 -0
- package/sdk/ts/resolvers/offer-key-cache.js +152 -0
- package/sdk/ts/resolvers/registration-requirements.d.ts +47 -0
- package/sdk/ts/resolvers/registration-requirements.js +220 -0
- package/sdk/ts/resolvers/ssrf.d.ts +28 -0
- package/sdk/ts/resolvers/ssrf.js +235 -0
- package/sdk/ts/resolvers/static.d.ts +7 -0
- package/sdk/ts/resolvers/static.js +16 -0
- package/sdk/ts/resolvers/wba.d.ts +134 -0
- package/sdk/ts/resolvers/wba.js +506 -0
- package/sdk/ts/resolvers/wellknown.d.ts +28 -0
- package/sdk/ts/resolvers/wellknown.js +169 -0
- package/sdk/ts/src/acceptance.d.ts +46 -0
- package/sdk/ts/src/acceptance.js +162 -0
- package/sdk/ts/src/base64url.d.ts +19 -0
- package/sdk/ts/src/base64url.js +55 -0
- package/sdk/ts/src/crossfield.d.ts +28 -0
- package/sdk/ts/src/crossfield.js +215 -0
- package/sdk/ts/src/endpoint-rule.d.ts +30 -0
- package/sdk/ts/src/endpoint-rule.js +63 -0
- package/sdk/ts/src/errordetail.d.ts +115 -0
- package/sdk/ts/src/errordetail.js +256 -0
- package/sdk/ts/src/hashurl.d.ts +5 -0
- package/sdk/ts/src/hashurl.js +15 -0
- package/sdk/ts/src/host-ref.d.ts +70 -0
- package/sdk/ts/src/host-ref.js +320 -0
- package/sdk/ts/src/hosts.d.ts +138 -0
- package/sdk/ts/src/hosts.js +214 -0
- package/sdk/ts/src/idempotency.d.ts +11 -0
- package/sdk/ts/src/idempotency.js +27 -0
- package/sdk/ts/src/jsondepth.d.ts +27 -0
- package/sdk/ts/src/jsondepth.js +77 -0
- package/sdk/ts/src/licenseterm.d.ts +113 -0
- package/sdk/ts/src/licenseterm.js +433 -0
- package/sdk/ts/src/money.d.ts +21 -0
- package/sdk/ts/src/money.js +60 -0
- package/sdk/ts/src/offer-sign.d.ts +11 -0
- package/sdk/ts/src/offer-sign.js +31 -0
- package/sdk/ts/src/opaque-url.d.ts +3 -0
- package/sdk/ts/src/opaque-url.js +20 -0
- package/sdk/ts/src/pop.d.ts +51 -0
- package/sdk/ts/src/pop.js +166 -0
- package/sdk/ts/src/regschema.d.ts +229 -0
- package/sdk/ts/src/regschema.js +1315 -0
- package/sdk/ts/src/scopes.d.ts +20 -0
- package/sdk/ts/src/scopes.js +45 -0
- package/sdk/ts/src/signurl.d.ts +23 -0
- package/sdk/ts/src/signurl.js +107 -0
- package/sdk/ts/src/thumbprint.d.ts +14 -0
- package/sdk/ts/src/thumbprint.js +24 -0
- package/sdk/ts/src/verify.d.ts +49 -0
- package/sdk/ts/src/verify.js +109 -0
- package/sdk/ts/src/wire-names.d.ts +1 -0
- package/sdk/ts/src/wire-names.js +16 -0
- package/sdk/ts/src/wire.d.ts +68 -0
- package/sdk/ts/src/wire.js +114 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// The client's typed failure — TS port of sdk/go/connect/callerror.go.
|
|
2
|
+
//
|
|
3
|
+
// A refusal and a failure are different things, and a caller handles the situations
|
|
4
|
+
// differently. It cannot tell them apart from a message, so the class is carried as a
|
|
5
|
+
// value.
|
|
6
|
+
//
|
|
7
|
+
// The one worth naming is: WE refused to send. The routing checks that precede a call to
|
|
8
|
+
// an offer-derived address can decline before anything leaves the process, and folding
|
|
9
|
+
// that into "unreachable" — as a network timeout — hides the difference between "the
|
|
10
|
+
// network is bad" and "the address failed a security check". They call for opposite
|
|
11
|
+
// responses.
|
|
12
|
+
/**
|
|
13
|
+
* ForaCallError is the client's typed failure. Every verb throws this and nothing else,
|
|
14
|
+
* so `instanceof ForaCallError` is not a coin flip on the very type callers are told to
|
|
15
|
+
* branch on — which is what happens when a method yields a typed error where it declines
|
|
16
|
+
* to send and a bare transport error where the peer refuses.
|
|
17
|
+
*/
|
|
18
|
+
export class ForaCallError extends Error {
|
|
19
|
+
kind;
|
|
20
|
+
op;
|
|
21
|
+
status;
|
|
22
|
+
reason;
|
|
23
|
+
detail;
|
|
24
|
+
/** The peer's own developer message, taken from its typed reason. Empty when the
|
|
25
|
+
* answer carried none.
|
|
26
|
+
*
|
|
27
|
+
* A field rather than something to recover from `message`'s rendering, because a
|
|
28
|
+
* reason rendered into prose cannot be read back out without parsing it. It sits
|
|
29
|
+
* BESIDE `reason` rather than in it: `reason` is the peer's machine token.
|
|
30
|
+
*
|
|
31
|
+
* NON-AUTHORITATIVE and UNBOUNDED — the contract says both of the field it comes
|
|
32
|
+
* from. Branch on `kind` or on the typed reason, never on this text, and bound it
|
|
33
|
+
* before rendering it to a log line or an agent. */
|
|
34
|
+
peerMessage;
|
|
35
|
+
constructor(init) {
|
|
36
|
+
super(renderCallError(init), init.cause !== undefined ? { cause: init.cause } : undefined);
|
|
37
|
+
this.name = "ForaCallError";
|
|
38
|
+
this.kind = init.kind;
|
|
39
|
+
this.op = init.op;
|
|
40
|
+
this.status = init.status;
|
|
41
|
+
this.reason = init.reason;
|
|
42
|
+
this.detail = init.detail;
|
|
43
|
+
// Taken ONLY from what the decode site passed, never derived from `detail`
|
|
44
|
+
// here. A detail is not always the peer's: the content leg SYNTHESIZES one
|
|
45
|
+
// from the edge's refusal token, and its message is this SDK's own sentence
|
|
46
|
+
// with the token quoted into it. Deriving from the field would put the SDK's
|
|
47
|
+
// words in the field that claims to hold a remote party's, which is the one
|
|
48
|
+
// thing this field exists not to do.
|
|
49
|
+
this.peerMessage = init.peerMessage ?? "";
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The most specific machine-readable reason available: the peer's own token when it
|
|
53
|
+
* sent one, otherwise the failure class.
|
|
54
|
+
*/
|
|
55
|
+
reasonOf() {
|
|
56
|
+
return this.reason !== undefined && this.reason !== ""
|
|
57
|
+
? this.reason
|
|
58
|
+
: this.kind;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// renderCallError mirrors the Go failure renderer's shape (package, op, class, status,
|
|
62
|
+
// reason, cause) so a log line reads the same whichever SDK produced it.
|
|
63
|
+
function renderCallError(init) {
|
|
64
|
+
const parts = [`fora/client: ${init.op}: ${init.kind}`];
|
|
65
|
+
if (init.status !== undefined && init.status !== 0) {
|
|
66
|
+
parts.push(`status ${init.status}`);
|
|
67
|
+
}
|
|
68
|
+
if (init.reason !== undefined && init.reason !== "") {
|
|
69
|
+
parts.push(`reason ${init.reason}`);
|
|
70
|
+
}
|
|
71
|
+
if (init.cause !== undefined) {
|
|
72
|
+
parts.push(String(init.cause instanceof Error ? init.cause.message : init.cause));
|
|
73
|
+
}
|
|
74
|
+
return parts.join(": ");
|
|
75
|
+
}
|
|
76
|
+
/** The refusal for an address that failed a routing check. Its own constructor because
|
|
77
|
+
* every such refusal must state which check declined and must never carry a status:
|
|
78
|
+
* nothing was sent, so there is nothing to report a status for. */
|
|
79
|
+
export function notSent(op, cause) {
|
|
80
|
+
return new ForaCallError({ kind: "not_sent", op, cause });
|
|
81
|
+
}
|
|
82
|
+
/** The refusal for a request that could not be assembled, or an answer that could not be
|
|
83
|
+
* read as the protocol defines it. */
|
|
84
|
+
export function malformed(op, cause) {
|
|
85
|
+
return new ForaCallError({ kind: "malformed", op, cause });
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The Connect code a peer answered with, mapped onto the failure class.
|
|
89
|
+
*
|
|
90
|
+
* A peer that answered is a refusal, whatever it said: the distinction a caller needs is
|
|
91
|
+
* "it said no" versus "it never answered", and only the first is worth surfacing a reason
|
|
92
|
+
* for. Three codes land on the second side. `unavailable` is the transport failure
|
|
93
|
+
* proper; `deadline_exceeded` and `canceled` are LOCAL outcomes wearing a Connect code,
|
|
94
|
+
* so neither means the peer reached a verdict — classifying a caller's own cancellation
|
|
95
|
+
* as a refusal would tell it the Exchange declined a call the Exchange may never have
|
|
96
|
+
* seen. `resource_exhausted` is the read cap seen from this side: the peer's answer was
|
|
97
|
+
* larger than the client agreed to read.
|
|
98
|
+
*/
|
|
99
|
+
/**
|
|
100
|
+
* The Connect code a status implies when the body is not an envelope that names one.
|
|
101
|
+
*
|
|
102
|
+
* connect-go derives the code from the HTTP STATUS in exactly two cases: a body it cannot
|
|
103
|
+
* read as an envelope, and an envelope carrying no code. Both are what a deployment's own
|
|
104
|
+
* infrastructure produces — a gateway draining, a proxy answering with its own HTML page —
|
|
105
|
+
* and the difference between "the Exchange declined this" and "nothing answered" is the
|
|
106
|
+
* whole reason the failure classes exist. Anything not listed is `unknown`, which reads as
|
|
107
|
+
* a refusal.
|
|
108
|
+
*
|
|
109
|
+
* Not a rule invented here: it is connect-go's own table (protocol.go, httpToCode), and
|
|
110
|
+
* the transport-failure corpus is captured from a real client so a future change to it is
|
|
111
|
+
* reported rather than mirrored by hand.
|
|
112
|
+
*/
|
|
113
|
+
const STATUS_CODES = {
|
|
114
|
+
// A 3xx reached this client only because the send refused to follow it, and every leg
|
|
115
|
+
// refuses. That is a server that did not answer the call, not one that declined it —
|
|
116
|
+
// which is what all three failure taxonomies say a redirect is. connect-go maps these
|
|
117
|
+
// to CodeUnknown, but it never sees one: its transport follows redirects, so the row is
|
|
118
|
+
// unreachable there rather than decided.
|
|
119
|
+
301: "unavailable",
|
|
120
|
+
302: "unavailable",
|
|
121
|
+
303: "unavailable",
|
|
122
|
+
307: "unavailable",
|
|
123
|
+
308: "unavailable",
|
|
124
|
+
400: "internal",
|
|
125
|
+
401: "unauthenticated",
|
|
126
|
+
403: "permission_denied",
|
|
127
|
+
404: "unimplemented",
|
|
128
|
+
429: "unavailable",
|
|
129
|
+
502: "unavailable",
|
|
130
|
+
503: "unavailable",
|
|
131
|
+
504: "unavailable",
|
|
132
|
+
};
|
|
133
|
+
/** The Connect code a non-envelope answer carries, derived from its status. */
|
|
134
|
+
export function connectCodeFromStatus(status) {
|
|
135
|
+
return STATUS_CODES[status] ?? "unknown";
|
|
136
|
+
}
|
|
137
|
+
export function kindOfConnectCode(code) {
|
|
138
|
+
switch (code) {
|
|
139
|
+
case "unavailable":
|
|
140
|
+
case "deadline_exceeded":
|
|
141
|
+
case "canceled":
|
|
142
|
+
return "unreachable";
|
|
143
|
+
case "resource_exhausted":
|
|
144
|
+
return "too_large";
|
|
145
|
+
default:
|
|
146
|
+
return "refused";
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { type DiscoveryResult, type Mode, type VerifiedOffer } from "../core/verifier.ts";
|
|
2
|
+
import type { z } from "zod";
|
|
3
|
+
import { type Window } from "../core/window.ts";
|
|
4
|
+
import { DisputeResponseSchema, GetAccountStatusResponseSchema, PushResourcesResponseSchema, RefreshCatalogResponseSchema, RegisterResponseSchema, RemoveResourcesResponseSchema, TransactionResponseSchema, UsageReportResponseSchema } from "../../../gen/ts/wire/schemas.ts";
|
|
5
|
+
import { type Content } from "./content.ts";
|
|
6
|
+
import { type RegistrationRequirements } from "../resolvers/index.ts";
|
|
7
|
+
import type { EndpointResolver } from "./route.ts";
|
|
8
|
+
import { type UnarySend, type Validation } from "./transport.ts";
|
|
9
|
+
/** How long a delivery-fetch proof stays valid, in seconds.
|
|
10
|
+
*
|
|
11
|
+
* Short on purpose, and deliberately NOT the signed URL's own expiry, which can be hours:
|
|
12
|
+
* the proof covers only the method and the URL, so for as long as the window is open
|
|
13
|
+
* anyone who observes the request can repeat it. */
|
|
14
|
+
export declare const DEFAULT_PROOF_WINDOW_SEC = 30;
|
|
15
|
+
/** Reports what one Exchange asks of a registration.
|
|
16
|
+
*
|
|
17
|
+
* An interface for the same two reasons the endpoint seam is one: a test can drive a
|
|
18
|
+
* registration without standing up a manifest server, and this module has no way to
|
|
19
|
+
* accept a terms digest or a schema from configuration — the only way to skip the read
|
|
20
|
+
* is to set `terms_digest` on the request, where the signature covers it.
|
|
21
|
+
*
|
|
22
|
+
* An implementation MUST NOT serve the answer from a cache. The contract requires a
|
|
23
|
+
* registering client to read the digest from a freshly fetched manifest, so a cached one
|
|
24
|
+
* breaks the rule the field exists to record.
|
|
25
|
+
*
|
|
26
|
+
* An implementation's FAILURE decides how a caller is told to react, so it is part of the
|
|
27
|
+
* contract rather than an implementation detail. A failure that is a VERDICT — the domain
|
|
28
|
+
* is unusable, the deployment excludes it, the document served is not an Exchange's, or it
|
|
29
|
+
* is one this reader cannot use — MUST throw the resolver tier's ExchangeNotPermitted,
|
|
30
|
+
* ManifestNotExchange or ManifestUnusable, or the invalid-host error raised for a
|
|
31
|
+
* value that is not a bare domain;
|
|
32
|
+
* those surface as `not_sent`, which tells the caller not to retry. Anything else is read
|
|
33
|
+
* as a transport failure and reported as `unreachable`, i.e. worth retrying. An
|
|
34
|
+
* implementation that throws a bare error for a refusal therefore has its final answer
|
|
35
|
+
* retried indefinitely.
|
|
36
|
+
*
|
|
37
|
+
* ManifestUnusable is the seam's word for "the document arrived and cannot be read for
|
|
38
|
+
* what a registration owes". The SDK's own reader reaches it for a document version it
|
|
39
|
+
* cannot classify, and treats its other two disappointments as absence or as a transport
|
|
40
|
+
* failure. An implementation STRICTER than that one — validating the whole document, or
|
|
41
|
+
* applying a narrower version rule — reaches for the same word, and would otherwise hold
|
|
42
|
+
* a final answer this seam reported as transient.
|
|
43
|
+
*
|
|
44
|
+
* Note which class that is NOT. ManifestVersionRefused belongs to the endpoint seam and
|
|
45
|
+
* is absent from the list above on purpose: the two vocabularies are disjoint, one
|
|
46
|
+
* answering whether an endpoint may be dialled and this one whether a document can be
|
|
47
|
+
* read. An implementation that throws the endpoint class for a version refusal here has
|
|
48
|
+
* its verdict read as a transport failure. */
|
|
49
|
+
export interface RegistrationRequirementsReader {
|
|
50
|
+
resolveRegistrationRequirements(exchange: string): Promise<RegistrationRequirements>;
|
|
51
|
+
}
|
|
52
|
+
/** Everything a client is built from. Every field is injected; the client owns none of it. */
|
|
53
|
+
export interface ClientOptions {
|
|
54
|
+
/** The RFC 9421 request signer. Custody stays with the application — the SDK receives
|
|
55
|
+
* a non-extractable CryptoKey and the keyid it signs under, never key bytes. */
|
|
56
|
+
signer?: {
|
|
57
|
+
privKey: CryptoKey;
|
|
58
|
+
keyid: string;
|
|
59
|
+
};
|
|
60
|
+
/** The PUBLIC half of the key `signer` signs with. A bound delivery fetch presents it
|
|
61
|
+
* in a header and derives the agent identity from it, and a non-extractable CryptoKey
|
|
62
|
+
* cannot yield it — custody keeps the private half, so the public half is supplied
|
|
63
|
+
* alongside. Without it the client can buy but cannot fetch what it bought.
|
|
64
|
+
*
|
|
65
|
+
* There is deliberately no option for a separate agent PRIVATE key. The protocol
|
|
66
|
+
* carries one agent identity: agent_identity_hash is the thumbprint of the agent's
|
|
67
|
+
* request-signing key, an Exchange verifies the detached acceptance against the key
|
|
68
|
+
* registered for the caller its request signature identified, and the delivery URL is
|
|
69
|
+
* bound to that same thumbprint. A second key would be refused at execute, and any URL
|
|
70
|
+
* it did produce could never be fetched. */
|
|
71
|
+
agentPublicKey?: CryptoKey;
|
|
72
|
+
/** The agent's own identity, forwarded on discovery and required on a purchase: both
|
|
73
|
+
* reference services resolve the calling agent from it and refuse a request naming
|
|
74
|
+
* none. */
|
|
75
|
+
requester?: Record<string, unknown>;
|
|
76
|
+
/** Offer-verification strictness. Defaults to "strict" — fail-closed. */
|
|
77
|
+
verification?: Mode;
|
|
78
|
+
/** Whether an outbound request is checked against its generated schema first.
|
|
79
|
+
* Defaults to "strict", which is deliberately stricter than Go — see the Validation
|
|
80
|
+
* type for why. Orthogonal to `verification`: this one is about the message going
|
|
81
|
+
* out, that one about the offers coming back. */
|
|
82
|
+
validation?: Validation;
|
|
83
|
+
/** Resolves an exchange identity to its raw 32-byte Ed25519 offer-signing key.
|
|
84
|
+
* Injected: the client owns no key state. */
|
|
85
|
+
resolveOfferKey?: (exchange: string) => Promise<Uint8Array<ArrayBuffer> | undefined>;
|
|
86
|
+
/** Turns an offer's exchange domain into that Exchange's own advertised origin. Never
|
|
87
|
+
* configuration — a usage report and a dispute go where the signed offer says. */
|
|
88
|
+
endpointResolver?: EndpointResolver;
|
|
89
|
+
/** Reports what one Exchange asks of a registration — the terms revision submitting
|
|
90
|
+
* one accepts, and the schema its registration_data must match. Defaults to the
|
|
91
|
+
* well-known reader over the SSRF-guarded transport, built once with this client:
|
|
92
|
+
* the domain comes off the request rather than from configuration, so it is the
|
|
93
|
+
* request-derived provenance that takes the guarded default.
|
|
94
|
+
*
|
|
95
|
+
* The reader it takes holds no document cache, and that is the point rather than an
|
|
96
|
+
* implementation detail: the contract requires a registering client to read the terms
|
|
97
|
+
* digest from a FRESHLY fetched manifest, so an implementation serving it from a
|
|
98
|
+
* cache breaks the rule the field exists to record. There is deliberately no option
|
|
99
|
+
* to supply a digest or a schema directly — a caller managing its own requirements
|
|
100
|
+
* sets `terms_digest` on the request, which suppresses the read and says so on the
|
|
101
|
+
* message the signature covers. */
|
|
102
|
+
registrationRequirements?: RegistrationRequirementsReader;
|
|
103
|
+
/** The WBA directory origin this client signs as. */
|
|
104
|
+
signatureAgent?: string;
|
|
105
|
+
/** The RFC 9421 freshness window stamped on every outbound call. */
|
|
106
|
+
signWindow?: Window;
|
|
107
|
+
/** The freshness window stamped on a delivery-fetch proof. */
|
|
108
|
+
proofWindow?: Window;
|
|
109
|
+
/** Mints the X-Request-ID correlation value. Absent sends no header. */
|
|
110
|
+
requestId?: () => string;
|
|
111
|
+
/** The dialing seam for the configured (home Exchange / Broker) leg. */
|
|
112
|
+
send?: UnarySend;
|
|
113
|
+
/** The dialing seam for the OFFER-DERIVED leg. Defaults to the SSRF-guarded send,
|
|
114
|
+
* because the caller names a domain, the manifest it serves names an endpoint, and a
|
|
115
|
+
* signed call then goes there. */
|
|
116
|
+
guardedSend?: UnarySend;
|
|
117
|
+
maxRPCReadBytes?: number;
|
|
118
|
+
callTimeoutMs?: number;
|
|
119
|
+
contentTimeoutMs?: number;
|
|
120
|
+
maxContentBytes?: number;
|
|
121
|
+
/** The clock the offer Verifier reads, in epoch milliseconds. */
|
|
122
|
+
now?: () => number;
|
|
123
|
+
}
|
|
124
|
+
/** Tunes a single state-mutating call. */
|
|
125
|
+
export interface CallOptions {
|
|
126
|
+
/**
|
|
127
|
+
* Pins the idempotency key for this call. Reusing a key makes the call a deliberate
|
|
128
|
+
* replay: the server dedupes on it (a fresh key is minted per call by default). The
|
|
129
|
+
* SDK never tracks keys — the server owns dedup.
|
|
130
|
+
*
|
|
131
|
+
* Hold the key and pass the same one back when retrying, on every verb that takes
|
|
132
|
+
* this option. The key identifies the ACTION, not the attempt: a fresh key on a retry
|
|
133
|
+
* reads to the server as a second purchase, a second report, a second dispute.
|
|
134
|
+
*/
|
|
135
|
+
idempotencyKey?: string;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The response types, inferred from the generated schemas rather than restated.
|
|
139
|
+
*
|
|
140
|
+
* A verb returning `Record<string, unknown>` hands a caller no help exactly where it is
|
|
141
|
+
* needed: `transaction_id`, `report_id` and the retrieval endpoint are the links of the
|
|
142
|
+
* dispute chain, and every read of one was an unchecked index. Python's verbs return the
|
|
143
|
+
* generated models, so the two faces were the same verb names over materially different
|
|
144
|
+
* ergonomics.
|
|
145
|
+
*/
|
|
146
|
+
export type TransactionResponse = z.infer<typeof TransactionResponseSchema>;
|
|
147
|
+
/** The answer to a usage report; carries the `report_id` a dispute is filed against. */
|
|
148
|
+
export type UsageReportResponse = z.infer<typeof UsageReportResponseSchema>;
|
|
149
|
+
/** The answer to a dispute. */
|
|
150
|
+
export type DisputeResponse = z.infer<typeof DisputeResponseSchema>;
|
|
151
|
+
/** The agent-facing Exchange client. */
|
|
152
|
+
export interface Client {
|
|
153
|
+
discover(query: Record<string, unknown>): Promise<DiscoveryResult>;
|
|
154
|
+
execute(offer: VerifiedOffer, opts?: CallOptions): Promise<TransactionResponse>;
|
|
155
|
+
reportUsage(report: Record<string, unknown>, opts?: CallOptions): Promise<UsageReportResponse>;
|
|
156
|
+
dispute(request: Record<string, unknown>, opts?: CallOptions): Promise<DisputeResponse>;
|
|
157
|
+
/** Create this agent's account at the Exchange the request names. Takes no
|
|
158
|
+
* CallOptions: the message carries no idempotency key, because registering again
|
|
159
|
+
* returns the same account handle. */
|
|
160
|
+
register(request: Record<string, unknown>): Promise<RegisterResponse>;
|
|
161
|
+
/** Read whether this agent's account at the named Exchange is active. An empty
|
|
162
|
+
* `billing_ref` is a NORMAL answer — no account there yet. */
|
|
163
|
+
getAccountStatus(request: Record<string, unknown>): Promise<GetAccountStatusResponse>;
|
|
164
|
+
fetch(signedURL: string): Promise<Content>;
|
|
165
|
+
}
|
|
166
|
+
/** The Broker client. */
|
|
167
|
+
export interface BrokerClient {
|
|
168
|
+
resolve(request: Record<string, unknown>): Promise<DiscoveryResult>;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* createClient builds a client against baseURL — the agent's HOME Exchange, the one its
|
|
172
|
+
* account lives on.
|
|
173
|
+
*
|
|
174
|
+
* Discovery and purchase go to baseURL. A usage report or a dispute does NOT: those reach
|
|
175
|
+
* the Exchange that ISSUED the offer, resolved per call from that Exchange's own
|
|
176
|
+
* manifest, over a separately guarded transport.
|
|
177
|
+
*/
|
|
178
|
+
export declare function createClient(baseURL: string, options?: ClientOptions): Client;
|
|
179
|
+
/**
|
|
180
|
+
* createBrokerClient builds a client against a Broker's base URL.
|
|
181
|
+
*
|
|
182
|
+
* A SEPARATE constructor rather than a second surface on the exchange client because the
|
|
183
|
+
* two speak to different parties. A Broker is not an Exchange: it fans a query out across
|
|
184
|
+
* Exchanges it knows and relays back what they offered, so its address is the Broker's,
|
|
185
|
+
* not any Exchange's. Hanging both off one base URL would mean one of the two was always
|
|
186
|
+
* pointed at the wrong party.
|
|
187
|
+
*
|
|
188
|
+
* It takes the same options, but only the ones a discovery call has any use for do
|
|
189
|
+
* anything, and two need care. A single pinned offer key is the wrong shape here: Broker
|
|
190
|
+
* fan-out returns offers minted by different Exchanges, so inject a resolver that
|
|
191
|
+
* resolves each issuing Exchange's own key. And `requester` is REQUIRED, not optional: a
|
|
192
|
+
* Broker resolves the calling agent from it and declines a request naming none, so
|
|
193
|
+
* resolve refuses locally rather than spending a round trip to be told.
|
|
194
|
+
*/
|
|
195
|
+
export declare function createBrokerClient(baseURL: string, options?: ClientOptions): BrokerClient;
|
|
196
|
+
/** The answer to a registration: the account handle this Exchange minted, and the terms
|
|
197
|
+
* revision it recorded against it. */
|
|
198
|
+
export type RegisterResponse = z.infer<typeof RegisterResponseSchema>;
|
|
199
|
+
/** The answer to an account-status read. An empty account handle is a NORMAL answer: it
|
|
200
|
+
* means this agent holds no account at that Exchange yet. */
|
|
201
|
+
export type GetAccountStatusResponse = z.infer<typeof GetAccountStatusResponseSchema>;
|
|
202
|
+
/** The answer to a catalog push: accepted/rejected counts and the warnings the accepted terms carry. */
|
|
203
|
+
export type PushResourcesResponse = z.infer<typeof PushResourcesResponseSchema>;
|
|
204
|
+
/** The answer to a catalog removal. */
|
|
205
|
+
export type RemoveResourcesResponse = z.infer<typeof RemoveResourcesResponseSchema>;
|
|
206
|
+
/** The answer to a catalog refresh request. */
|
|
207
|
+
export type RefreshCatalogResponse = z.infer<typeof RefreshCatalogResponseSchema>;
|
|
208
|
+
/** The publisher-facing Catalog client. */
|
|
209
|
+
export interface CatalogClient {
|
|
210
|
+
pushResources(request: Record<string, unknown>): Promise<PushResourcesResponse>;
|
|
211
|
+
removeResources(request: Record<string, unknown>): Promise<RemoveResourcesResponse>;
|
|
212
|
+
refreshCatalog(request: Record<string, unknown>): Promise<RefreshCatalogResponse>;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* createCatalogClient builds a client against an Exchange's CATALOG endpoint — the
|
|
216
|
+
* publisher role's face: push, remove and refresh the catalog entries a publisher, or a
|
|
217
|
+
* contributor it authorised, supplies.
|
|
218
|
+
*
|
|
219
|
+
* A SEPARATE constructor, as the Broker's is, and for a related reason: the address is a
|
|
220
|
+
* different one. An Exchange advertises CatalogService at its manifest's
|
|
221
|
+
* `catalog_endpoint`, distinct from the ExchangeService endpoint the agent client dials,
|
|
222
|
+
* and the caller is a different party holding a different key — a contributor's, named
|
|
223
|
+
* by `caller_id`, never an agent's. Hanging the catalog verbs on the agent client would
|
|
224
|
+
* carry every agent-only holder into a client that uses none of them, and point one of
|
|
225
|
+
* the two roles at the wrong address.
|
|
226
|
+
*
|
|
227
|
+
* The publisher chose the Exchange, so the origin is configuration and the leg runs on
|
|
228
|
+
* the plain send — the posture of the agent client's home Exchange, not of its
|
|
229
|
+
* offer-derived leg. It takes the same options; `signer` is what a real push needs (an
|
|
230
|
+
* Exchange refuses an unsigned catalog call), and the agent-only ones — the requester,
|
|
231
|
+
* the agent key, the offer-key resolver, the endpoint resolver, the guarded send — are
|
|
232
|
+
* inert here rather than errors, so one option set can build every face.
|
|
233
|
+
*/
|
|
234
|
+
export declare function createCatalogClient(baseURL: string, options?: ClientOptions): CatalogClient;
|
|
235
|
+
export { ForaCallError } from "./errors.ts";
|
|
236
|
+
export type { CallErrorKind } from "./errors.ts";
|
|
237
|
+
export type { Content } from "./content.ts";
|
|
238
|
+
export type { EndpointResolver } from "./route.ts";
|
|
239
|
+
export type { UnaryRequest, UnaryResponse, UnarySend, Validation } from "./transport.ts";
|
|
240
|
+
export { DEFAULT_CALL_TIMEOUT_MS, DEFAULT_MAX_RPC_READ_BYTES, NOT_CANONICAL_WIRE_NAMING, } from "./transport.ts";
|
|
241
|
+
export { DEFAULT_CONTENT_TIMEOUT_MS, DEFAULT_MAX_CONTENT_BYTES, } from "./content.ts";
|