@forgezero/providers 0.1.9 → 0.1.10
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 +160 -52
- package/dist/binance.js +95 -38
- package/dist/chain.d.ts +1 -1
- package/dist/chain.js +95 -38
- package/dist/database.d.ts +2 -2
- package/dist/database.js +95 -38
- package/dist/email.d.ts +26 -44
- package/dist/email.js +160 -127
- package/dist/http.d.ts +2 -2
- package/dist/http.js +95 -38
- package/dist/index.d.ts +80 -17
- package/dist/index.js +97 -36
- package/dist/pool.js +95 -38
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +95 -38
- package/dist/translation.d.ts +6 -4
- package/dist/translation.js +95 -38
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,77 +1,185 @@
|
|
|
1
1
|
# @forgezero/providers
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
which failures are worth retrying.**
|
|
3
|
+
Provider identities, named capabilities, and service-owned priority/fallback.
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Zero runtime dependencies. Bun, Node 18+, Deno, Cloudflare Workers — anywhere
|
|
12
|
-
`fetch` and Web Crypto exist.
|
|
5
|
+
A provider declares what a vendor can do. A service declares what an
|
|
6
|
+
application needs. Configuration attaches one provider method to one service
|
|
7
|
+
method, so adding a provider never changes the service contract and adding a
|
|
8
|
+
service never forks a provider.
|
|
13
9
|
|
|
14
10
|
```bash
|
|
15
11
|
bun add @forgezero/providers
|
|
16
12
|
```
|
|
17
13
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
## Built-in providers
|
|
15
|
+
|
|
16
|
+
| provider | methods | import |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| JetEmail | `send`, `sendBatch` | `@forgezero/providers/email` |
|
|
19
|
+
| SMTP | `send` | `@forgezero/providers/email` |
|
|
20
|
+
| ArangoDB | `query` | `@forgezero/providers/database` |
|
|
21
|
+
| EVM JSON-RPC | `request` | `@forgezero/providers/chain` |
|
|
22
|
+
| HTTP | `request` | `@forgezero/providers/http` |
|
|
23
|
+
| S3-compatible storage | `request` | `@forgezero/providers/storage` |
|
|
24
|
+
| Google AI Studio | `translate` | `@forgezero/providers/translation` |
|
|
25
|
+
|
|
26
|
+
`pool`, `binance`, and `realtime` are specialised provider utilities/clients.
|
|
27
|
+
Every public subpath is documented at
|
|
28
|
+
[forgezero.net/docs/providers](https://www.forgezero.net/docs/providers).
|
|
29
|
+
|
|
30
|
+
## Email: exact capability routing
|
|
31
|
+
|
|
32
|
+
JetEmail provides native single and batch delivery. SMTP provides single
|
|
33
|
+
delivery only. ForgeZero does not pretend SMTP has a batch capability by
|
|
34
|
+
silently looping.
|
|
21
35
|
|
|
22
|
-
|
|
23
|
-
|
|
36
|
+
```ts
|
|
37
|
+
import {
|
|
38
|
+
createRegistry,
|
|
39
|
+
envCredentials,
|
|
40
|
+
staticConfig
|
|
41
|
+
} from '@forgezero/providers';
|
|
42
|
+
import {
|
|
43
|
+
emailProviders,
|
|
44
|
+
emailService
|
|
45
|
+
} from '@forgezero/providers/email';
|
|
46
|
+
|
|
47
|
+
const registry = createRegistry({
|
|
48
|
+
providers: emailProviders,
|
|
49
|
+
credentials: envCredentials(process.env),
|
|
24
50
|
config: staticConfig({
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
51
|
+
providers: {
|
|
52
|
+
jetPrimary: {
|
|
53
|
+
providerId: 'jetemail',
|
|
54
|
+
enabled: true,
|
|
55
|
+
config: { from: 'ops@example.com' },
|
|
56
|
+
secretRef: 'jetemail'
|
|
57
|
+
},
|
|
58
|
+
smtpBackup: {
|
|
59
|
+
providerId: 'smtp',
|
|
60
|
+
enabled: true,
|
|
61
|
+
config: {
|
|
62
|
+
host: 'smtp.example.com',
|
|
63
|
+
port: 587,
|
|
64
|
+
from: 'ops@example.com',
|
|
65
|
+
transport
|
|
66
|
+
},
|
|
67
|
+
secretRef: 'smtp'
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
services: {
|
|
71
|
+
email: {
|
|
72
|
+
send: [
|
|
73
|
+
{ instanceKey: 'jetPrimary', providerMethod: 'send', priority: 1, enabled: true },
|
|
74
|
+
{ instanceKey: 'smtpBackup', providerMethod: 'send', priority: 2, enabled: true }
|
|
75
|
+
],
|
|
76
|
+
sendBatch: [
|
|
77
|
+
{ instanceKey: 'jetPrimary', providerMethod: 'sendBatch', priority: 1, enabled: true }
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const email = registry.service(emailService);
|
|
85
|
+
const outcome = await email.call('send', {
|
|
86
|
+
to: 'ada@example.com',
|
|
87
|
+
subject: 'Your invite',
|
|
88
|
+
html: '<p>Welcome</p>'
|
|
31
89
|
});
|
|
32
90
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
//
|
|
91
|
+
if (!outcome.ok) throw outcome.error;
|
|
92
|
+
outcome.result; // provider-neutral result
|
|
93
|
+
outcome.selected; // exact provider instance and method
|
|
94
|
+
outcome.fallbackUsed; // true when a lower priority succeeded
|
|
95
|
+
outcome.attempts; // every failed, skipped and successful attempt
|
|
36
96
|
```
|
|
37
97
|
|
|
38
|
-
|
|
98
|
+
Hooks and health writes are awaited. A successful fallback therefore still
|
|
99
|
+
returns the complete failure trail for auditing or operator notification.
|
|
39
100
|
|
|
40
|
-
|
|
41
|
-
the loop decides what to do:
|
|
101
|
+
## Define a custom provider
|
|
42
102
|
|
|
43
|
-
|
|
44
|
-
|---|---|---|
|
|
45
|
-
| `terminal` | the request itself is wrong — a 400, a malformed address | **stop.** Every other provider will reject it too |
|
|
46
|
-
| `retryable` | this vendor failed — a 401, a 500, a dropped socket | try the next one. Their key being bad says nothing about the next key |
|
|
47
|
-
| `backoff` | rate limited — a 429 with `Retry-After` | wait, then continue |
|
|
103
|
+
A provider does not name a service:
|
|
48
104
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
105
|
+
```ts
|
|
106
|
+
import {
|
|
107
|
+
defineProvider,
|
|
108
|
+
defineProviderMethod
|
|
109
|
+
} from '@forgezero/providers';
|
|
110
|
+
|
|
111
|
+
export const postmark = defineProvider({
|
|
112
|
+
id: 'postmark',
|
|
113
|
+
label: 'Postmark',
|
|
114
|
+
credentials: {
|
|
115
|
+
type: 'object',
|
|
116
|
+
additionalProperties: false,
|
|
117
|
+
properties: { token: { type: 'string', writeOnly: true } }
|
|
118
|
+
},
|
|
119
|
+
methods: {
|
|
120
|
+
deliver: defineProviderMethod<Notice, DeliveryReceipt>({
|
|
121
|
+
invoke: async (context, notice) =>
|
|
122
|
+
post(context.config, await context.secret('token'), notice),
|
|
123
|
+
classify: (error) => status(error) === 400 ? 'terminal' : 'retryable'
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
```
|
|
52
128
|
|
|
53
|
-
##
|
|
129
|
+
## Define a custom service
|
|
54
130
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
it. A provider disabled by configuration never counts against health, because a
|
|
58
|
-
vendor you turned off is not a vendor that is failing.
|
|
131
|
+
The application owns the service vocabulary and attaches provider methods in
|
|
132
|
+
configuration:
|
|
59
133
|
|
|
60
|
-
|
|
134
|
+
```ts
|
|
135
|
+
import { defineService, serviceMethod } from '@forgezero/providers';
|
|
136
|
+
|
|
137
|
+
export const notifications = defineService({
|
|
138
|
+
key: 'notifications',
|
|
139
|
+
methods: {
|
|
140
|
+
deliver: serviceMethod<Notice, DeliveryReceipt>()
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// services.notifications.deliver attaches postmark.deliver by priority.
|
|
145
|
+
const result = await registry.service(notifications).call('deliver', notice);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
An SSOT route handler calls `notifications.deliver`; it never imports Postmark,
|
|
149
|
+
JetEmail, or SMTP. A tenant may add both its own provider and its own service
|
|
150
|
+
without changing ForgeZero source.
|
|
151
|
+
|
|
152
|
+
## Vault and external credential sources
|
|
61
153
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
154
|
+
`@forgezero/providers` never imports `@forgezero/vault`. It consumes a tiny
|
|
155
|
+
structural interface. ForgeZero provides adapters in `@forgezero/vault/providers`;
|
|
156
|
+
other consumers can inject any secret manager.
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
import { chainCredentials, envCredentials } from '@forgezero/providers';
|
|
160
|
+
import { vaultCredentials } from '@forgezero/vault/providers';
|
|
161
|
+
|
|
162
|
+
const credentials = chainCredentials(
|
|
163
|
+
vaultCredentials(vault),
|
|
164
|
+
{
|
|
165
|
+
name: 'customer-secret-service',
|
|
166
|
+
get: (reference, field) => external.read(reference, field)
|
|
167
|
+
},
|
|
168
|
+
envCredentials(process.env) // bootstrap/systemd-projected fallback
|
|
169
|
+
);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Failure classification
|
|
173
|
+
|
|
174
|
+
| kind | meaning | registry action |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| `terminal` | the request is invalid for every provider | stop |
|
|
177
|
+
| `retryable` | this provider/key failed | try the next attachment |
|
|
178
|
+
| `backoff` | the provider is healthy but rate-limiting | preserve health and continue according to policy |
|
|
71
179
|
|
|
72
|
-
|
|
180
|
+
Three fault strikes mark an attachment offline; a success clears them. Backoff
|
|
181
|
+
does not count as a fault.
|
|
73
182
|
|
|
74
183
|
## Licence
|
|
75
184
|
|
|
76
|
-
MIT.
|
|
77
|
-
deploys — and usable entirely on its own, with no ForgeZero account.
|
|
185
|
+
MIT. Usable independently of a ForgeZero account.
|
package/dist/binance.js
CHANGED
|
@@ -38,25 +38,45 @@ function chainCredentials(...sources) {
|
|
|
38
38
|
}
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
-
function staticConfig(
|
|
41
|
+
function staticConfig(config) {
|
|
42
42
|
const health = new Map;
|
|
43
43
|
return {
|
|
44
44
|
name: "static",
|
|
45
|
-
async
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
async provider(instanceKey) {
|
|
46
|
+
const provider = config.providers[instanceKey];
|
|
47
|
+
return provider ? { instanceKey, ...provider, config: { ...provider.config } } : undefined;
|
|
48
|
+
},
|
|
49
|
+
async list(serviceKey, methodKey) {
|
|
50
|
+
return (config.services[serviceKey]?.[methodKey] ?? []).map((attachment) => ({
|
|
51
|
+
...attachment,
|
|
52
|
+
health: health.get(`${serviceKey}:${methodKey}:${attachment.instanceKey}:${attachment.providerMethod}`) ?? attachment.health
|
|
49
53
|
}));
|
|
50
54
|
},
|
|
51
|
-
async recordHealth(serviceKey,
|
|
52
|
-
health.set(`${serviceKey}:${
|
|
55
|
+
async recordHealth(serviceKey, methodKey, instanceKey, providerMethod, next) {
|
|
56
|
+
health.set(`${serviceKey}:${methodKey}:${instanceKey}:${providerMethod}`, next);
|
|
53
57
|
}
|
|
54
58
|
};
|
|
55
59
|
}
|
|
60
|
+
function defineProviderMethod(method) {
|
|
61
|
+
return method;
|
|
62
|
+
}
|
|
56
63
|
function defineProvider(spec) {
|
|
57
64
|
return spec;
|
|
58
65
|
}
|
|
66
|
+
function defineSingleMethodProvider(spec) {
|
|
67
|
+
const { method, invoke, classify, ...identity } = spec;
|
|
68
|
+
return defineProvider({
|
|
69
|
+
...identity,
|
|
70
|
+
methods: { [method]: defineProviderMethod({ invoke, classify }) }
|
|
71
|
+
});
|
|
72
|
+
}
|
|
59
73
|
var STRIKES_TO_OFFLINE = 3;
|
|
74
|
+
function serviceMethod() {
|
|
75
|
+
return Object.freeze({});
|
|
76
|
+
}
|
|
77
|
+
function defineService(definition) {
|
|
78
|
+
return definition;
|
|
79
|
+
}
|
|
60
80
|
function nextHealth(current, kind) {
|
|
61
81
|
if (kind === "success")
|
|
62
82
|
return { strikes: 0, status: "ok" };
|
|
@@ -71,80 +91,117 @@ function nextHealth(current, kind) {
|
|
|
71
91
|
}
|
|
72
92
|
function createRegistry(options) {
|
|
73
93
|
const byId = new Map(options.providers.map((provider) => [provider.id, provider]));
|
|
74
|
-
async function call(serviceKey, args, callOptions = {}) {
|
|
75
|
-
const configured = [...await options.config.list(serviceKey)].filter((
|
|
94
|
+
async function call(serviceKey, methodKey, args, callOptions = {}) {
|
|
95
|
+
const configured = [...await options.config.list(serviceKey, methodKey)].filter((attachment) => attachment.enabled).sort((a, b) => a.priority - b.priority);
|
|
76
96
|
const attempts = [];
|
|
77
|
-
for (const
|
|
97
|
+
for (const attachment of configured) {
|
|
78
98
|
if (callOptions.signal?.aborted) {
|
|
79
99
|
const cancelled = {
|
|
80
100
|
ok: false,
|
|
101
|
+
fallbackUsed: attempts.length > 0,
|
|
81
102
|
attempts,
|
|
82
|
-
error: new ProviderError("CALL_ABORTED", `The "${serviceKey}" call was cancelled.`)
|
|
103
|
+
error: new ProviderError("CALL_ABORTED", `The "${serviceKey}.${methodKey}" call was cancelled.`)
|
|
83
104
|
};
|
|
84
|
-
options.after?.(cancelled);
|
|
105
|
+
await options.after?.(cancelled);
|
|
85
106
|
return cancelled;
|
|
86
107
|
}
|
|
87
|
-
const
|
|
88
|
-
if (!
|
|
89
|
-
attempts.push({ providerId:
|
|
108
|
+
const instance = await options.config.provider(attachment.instanceKey);
|
|
109
|
+
if (!instance) {
|
|
110
|
+
attempts.push({ providerId: "unknown", instanceKey: attachment.instanceKey, providerMethod: attachment.providerMethod, outcome: "skipped", error: "instance not registered" });
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const spec = byId.get(instance.providerId);
|
|
114
|
+
const method = spec?.methods[attachment.providerMethod];
|
|
115
|
+
if (!spec || !method) {
|
|
116
|
+
attempts.push({ providerId: instance.providerId, instanceKey: instance.instanceKey, providerMethod: attachment.providerMethod, outcome: "skipped", error: !spec ? "provider not registered" : "method not supported" });
|
|
90
117
|
continue;
|
|
91
118
|
}
|
|
92
|
-
if (
|
|
93
|
-
attempts.push({ providerId:
|
|
119
|
+
if (!instance.enabled) {
|
|
120
|
+
attempts.push({ providerId: instance.providerId, instanceKey: instance.instanceKey, providerMethod: attachment.providerMethod, outcome: "skipped", error: "instance disabled" });
|
|
94
121
|
continue;
|
|
95
122
|
}
|
|
96
|
-
|
|
123
|
+
if (attachment.health?.status === "offline") {
|
|
124
|
+
attempts.push({ providerId: instance.providerId, instanceKey: instance.instanceKey, providerMethod: attachment.providerMethod, outcome: "skipped", error: "offline" });
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
await options.before?.({ service: serviceKey, method: methodKey, provider: instance.providerId, instance: instance.instanceKey });
|
|
128
|
+
const startedAt = performance.now();
|
|
97
129
|
try {
|
|
98
|
-
const result = await
|
|
99
|
-
config:
|
|
100
|
-
secret: (field) => options.credentials.get(
|
|
130
|
+
const result = await method.invoke({
|
|
131
|
+
config: instance.config,
|
|
132
|
+
secret: (field) => options.credentials.get(instance.secretRef, field),
|
|
101
133
|
signal: callOptions.signal
|
|
102
134
|
}, args);
|
|
103
|
-
attempts.push({ providerId:
|
|
104
|
-
await options.config.recordHealth(serviceKey,
|
|
105
|
-
const sent = {
|
|
106
|
-
|
|
135
|
+
attempts.push({ providerId: instance.providerId, instanceKey: instance.instanceKey, providerMethod: attachment.providerMethod, outcome: "sent", durationMs: performance.now() - startedAt });
|
|
136
|
+
await options.config.recordHealth(serviceKey, methodKey, instance.instanceKey, attachment.providerMethod, nextHealth(attachment.health, "success"));
|
|
137
|
+
const sent = {
|
|
138
|
+
ok: true,
|
|
139
|
+
result,
|
|
140
|
+
provider: instance.providerId,
|
|
141
|
+
instance: instance.instanceKey,
|
|
142
|
+
method: attachment.providerMethod,
|
|
143
|
+
selected: { providerId: instance.providerId, instanceKey: instance.instanceKey, providerMethod: attachment.providerMethod },
|
|
144
|
+
fallbackUsed: attempts.length > 1,
|
|
145
|
+
attempts
|
|
146
|
+
};
|
|
147
|
+
await options.after?.(sent);
|
|
107
148
|
return sent;
|
|
108
149
|
} catch (error) {
|
|
109
150
|
if (callOptions.signal?.aborted) {
|
|
110
151
|
const cancelled = {
|
|
111
152
|
ok: false,
|
|
153
|
+
fallbackUsed: attempts.length > 0,
|
|
112
154
|
attempts,
|
|
113
|
-
error: new ProviderError("CALL_ABORTED", `The "${serviceKey}" call was cancelled.`)
|
|
155
|
+
error: new ProviderError("CALL_ABORTED", `The "${serviceKey}.${methodKey}" call was cancelled.`)
|
|
114
156
|
};
|
|
115
|
-
options.after?.(cancelled);
|
|
157
|
+
await options.after?.(cancelled);
|
|
116
158
|
return cancelled;
|
|
117
159
|
}
|
|
118
|
-
const kind =
|
|
160
|
+
const kind = method.classify(error);
|
|
161
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
162
|
+
const code = error instanceof ProviderError ? error.code : typeof error?.code === "string" ? error.code : undefined;
|
|
119
163
|
attempts.push({
|
|
120
|
-
providerId:
|
|
164
|
+
providerId: instance.providerId,
|
|
165
|
+
instanceKey: instance.instanceKey,
|
|
166
|
+
providerMethod: attachment.providerMethod,
|
|
121
167
|
outcome: "failed",
|
|
122
168
|
kind,
|
|
123
|
-
|
|
169
|
+
durationMs: performance.now() - startedAt,
|
|
170
|
+
failure: { kind, ...code ? { code } : {}, message },
|
|
171
|
+
error: message
|
|
124
172
|
});
|
|
125
|
-
await options.config.recordHealth(serviceKey,
|
|
173
|
+
await options.config.recordHealth(serviceKey, methodKey, instance.instanceKey, attachment.providerMethod, nextHealth(attachment.health, kind));
|
|
126
174
|
if (kind === "terminal") {
|
|
127
175
|
const refused = {
|
|
128
176
|
ok: false,
|
|
177
|
+
fallbackUsed: attempts.length > 1,
|
|
129
178
|
attempts,
|
|
130
179
|
error: new ProviderError("PAYLOAD_REJECTED", "The request was refused as malformed; no provider will accept it.")
|
|
131
180
|
};
|
|
132
|
-
options.after?.(refused);
|
|
181
|
+
await options.after?.(refused);
|
|
133
182
|
return refused;
|
|
134
183
|
}
|
|
135
184
|
}
|
|
136
185
|
}
|
|
137
186
|
const failed = {
|
|
138
187
|
ok: false,
|
|
188
|
+
fallbackUsed: attempts.length > 1,
|
|
139
189
|
attempts,
|
|
140
|
-
error: new ProviderError(attempts.length === 0 ? "NO_PROVIDER" : "ALL_PROVIDERS_FAILED", attempts.length === 0 ? `No provider is configured for "${serviceKey}".` : `Every provider for "${serviceKey}" failed or was skipped.`)
|
|
190
|
+
error: new ProviderError(attempts.length === 0 ? "NO_PROVIDER" : "ALL_PROVIDERS_FAILED", attempts.length === 0 ? `No provider method is configured for "${serviceKey}.${methodKey}".` : `Every provider method for "${serviceKey}.${methodKey}" failed or was skipped.`)
|
|
141
191
|
};
|
|
142
|
-
options.after?.(failed);
|
|
192
|
+
await options.after?.(failed);
|
|
143
193
|
return failed;
|
|
144
194
|
}
|
|
145
|
-
|
|
195
|
+
function service(definition) {
|
|
196
|
+
return {
|
|
197
|
+
call(method, args, callOptions) {
|
|
198
|
+
return call(definition.key, method, args, callOptions);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return { call, service };
|
|
146
203
|
}
|
|
147
|
-
var VERSION = "0.1.
|
|
204
|
+
var VERSION = "0.1.10";
|
|
148
205
|
|
|
149
206
|
// src/http.ts
|
|
150
207
|
class BudgetExhausted extends ProviderError {
|
|
@@ -227,9 +284,9 @@ function createHttpClient(config) {
|
|
|
227
284
|
budget: () => budgetState(config.budget.host)
|
|
228
285
|
};
|
|
229
286
|
}
|
|
230
|
-
var http =
|
|
287
|
+
var http = defineSingleMethodProvider({
|
|
231
288
|
id: "http",
|
|
232
|
-
|
|
289
|
+
method: "request",
|
|
233
290
|
label: "HTTP",
|
|
234
291
|
multiInstance: true,
|
|
235
292
|
credentials: {
|
package/dist/chain.d.ts
CHANGED
|
@@ -96,4 +96,4 @@ export type ChainResult = {
|
|
|
96
96
|
op: 'receipt';
|
|
97
97
|
receipt: ChainReceipt | null;
|
|
98
98
|
};
|
|
99
|
-
export declare const evmRpc: import("./index").
|
|
99
|
+
export declare const evmRpc: import("./index").ProviderDefinition<Record<"request", import("./index").ProviderMethodSpec<ChainCall, ChainResult>>>;
|