@salesforce/agentic-common 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +21 -0
- package/README.md +262 -0
- package/dist/clock.d.ts +12 -0
- package/dist/clock.js +20 -0
- package/dist/clock.js.map +1 -0
- package/dist/connection-factory.d.ts +59 -0
- package/dist/connection-factory.js +82 -0
- package/dist/connection-factory.js.map +1 -0
- package/dist/connection.d.ts +50 -0
- package/dist/connection.js +57 -0
- package/dist/connection.js.map +1 -0
- package/dist/error-utils.d.ts +9 -0
- package/dist/error-utils.js +32 -0
- package/dist/error-utils.js.map +1 -0
- package/dist/event-bus.d.ts +27 -0
- package/dist/event-bus.js +52 -0
- package/dist/event-bus.js.map +1 -0
- package/dist/id-generator.d.ts +6 -0
- package/dist/id-generator.js +11 -0
- package/dist/id-generator.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/log.d.ts +26 -0
- package/dist/log.js +25 -0
- package/dist/log.js.map +1 -0
- package/dist/retryer.d.ts +137 -0
- package/dist/retryer.js +175 -0
- package/dist/retryer.js.map +1 -0
- package/dist/sf-api-env.d.ts +21 -0
- package/dist/sf-api-env.js +56 -0
- package/dist/sf-api-env.js.map +1 -0
- package/dist/test/tsconfig.tsbuildinfo +1 -0
- package/package.json +58 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Terms of Use
|
|
2
|
+
|
|
3
|
+
Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
4
|
+
|
|
5
|
+
These Terms of Use govern the download, installation, and/or use of this software provided by Salesforce, Inc. ("Salesforce") (the "Software"), were last updated on March 24, 2026, and constitute a legally binding agreement between you and Salesforce. If you do not agree to these Terms of Use, do not install or use the Software. The Software may link to third-party software components licensed under various open source licenses ("Open Source Components"). These Terms of Use pertain solely to Salesforce's proprietary code in the Software. It does not alter or extend any rights or obligations regarding the Open Source Components. For clarity, your use of the Open Source Components is governed by the terms of the applicable open source license(s). You are solely responsible for complying with the terms and conditions of those open source licenses.
|
|
6
|
+
|
|
7
|
+
Salesforce grants you a worldwide, non-exclusive, no-charge, royalty-free copyright license to reproduce, revocable, publicly display, publicly perform, sublicense, and distribute the Software and derivative works subject to these Terms. These Terms shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
Subject to the limited rights expressly granted hereunder, Salesforce reserves all rights, title, and interest in and to all intellectual property subsisting in the Software. No rights are granted to you hereunder other than as expressly set forth herein. Users residing in countries on the United States Office of Foreign Assets Control sanction list, or which are otherwise subject to a US export embargo, may not use the Software.
|
|
10
|
+
|
|
11
|
+
Implementation of the Software may require development work, for which you are responsible. The Software may contain bugs, errors and incompatibilities and is made available on an AS IS basis without support, updates, or service level commitments.
|
|
12
|
+
|
|
13
|
+
Salesforce reserves the right at any time to modify, suspend, or discontinue, the Software (or any part thereof) with or without notice. You agree that Salesforce shall not be liable to you or to any third party for any modification, suspension, or discontinuance.
|
|
14
|
+
|
|
15
|
+
You agree to defend Salesforce against any claim, demand, suit or proceeding made or brought against Salesforce by a third party arising out of or accruing from (a) your use of the Software, and (b) any application you develop with the Software that infringes any copyright, trademark, trade secret, trade dress, patent, or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy (each a "Claim Against Salesforce"), and will indemnify Salesforce from any damages, attorney fees, and costs finally awarded against Salesforce as a result of, or for any amounts paid by Salesforce under a settlement approved by you in writing of, a Claim Against Salesforce, provided Salesforce (x) promptly gives you written notice of the Claim Against Salesforce, (y) gives you sole control of the defense and settlement of the Claim Against Salesforce (except that you may not settle any Claim Against Salesforce unless it unconditionally releases Salesforce of all liability), and (z) gives you all reasonable assistance, at your expense.
|
|
16
|
+
|
|
17
|
+
WITHOUT LIMITING THE GENERALITY OF THE FOREGOING, THE SOFTWARE IS NOT SUPPORTED AND IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. IN NO EVENT SHALL SALESFORCE HAVE ANY LIABILITY FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, INDIRECT, SPECIAL, INCIDENTAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES, OR DAMAGES BASED ON LOST PROFITS, DATA, OR USE, IN CONNECTION WITH THE SOFTWARE, HOWEVER CAUSED AND WHETHER IN CONTRACT, TORT, OR UNDER ANY OTHER THEORY OF LIABILITY, WHETHER OR NOT YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
|
18
|
+
|
|
19
|
+
These Terms of Use shall be governed exclusively by the internal laws of the State of California, without regard to its conflicts of laws rules. Each party hereby consents to the exclusive jurisdiction of the state and federal courts located in San Francisco County, California to adjudicate any dispute arising out of or relating to these Terms of Use and the download, installation, and/or use of the Software. Except as expressly stated herein, these Terms of Use constitute the entire agreement between the parties, and supersede all prior and contemporaneous agreements, proposals, or representations, written or oral, concerning their subject matter. No modification, amendment, or waiver of any provision of these Terms of Use shall be effective unless it is by an update to these Terms of Use that Salesforce makes available, or is in writing and signed by the party against whom the modification, amendment, or waiver is to be asserted.
|
|
20
|
+
|
|
21
|
+
Data Privacy: Salesforce may collect, process, and store device, system, and other information related to your use of the Software. This information includes, but is not limited to, IP address, user metrics, and other data ("Usage Data"). Salesforce may use Usage Data for analytics, product development, and marketing purposes. You acknowledge that files generated in conjunction with the Software may contain sensitive or confidential data, and you are solely responsible for anonymizing and protecting such data.
|
package/README.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# @salesforce/agentic-common
|
|
2
|
+
|
|
3
|
+
Shared primitives and common utilities for the Salesforce agentic DX packages. Provides a typed event bus, clock
|
|
4
|
+
abstraction, ID generation, error utilities, log record shape, thin log-emit helpers, and a Salesforce org connection
|
|
5
|
+
interface consumed by the other packages in this monorepo.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
> This package is not yet published to npm. See [DEVELOPING.md](../../DEVELOPING.md) to build from source.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { EventBus, LogBus } from '@salesforce/agentic-common';
|
|
13
|
+
|
|
14
|
+
type MyEvent = { type: 'hello'; name: string };
|
|
15
|
+
|
|
16
|
+
const bus = new EventBus<MyEvent>();
|
|
17
|
+
const unsubscribe = bus.on((event) => {
|
|
18
|
+
console.log(`hello, ${event.name}`);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
bus.emit({ type: 'hello', name: 'world' });
|
|
22
|
+
|
|
23
|
+
unsubscribe();
|
|
24
|
+
bus.dispose();
|
|
25
|
+
|
|
26
|
+
const logs = new LogBus();
|
|
27
|
+
logs.on((record) => console.log(`[${record.level}] ${record.message}`));
|
|
28
|
+
logs.info('ready', { agentId: 'a1' });
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## API Reference
|
|
32
|
+
|
|
33
|
+
### `OrgConnection` / `RealOrgConnection`
|
|
34
|
+
|
|
35
|
+
Interface for making authenticated HTTP requests to a Salesforce org, carrying identity metadata alongside transport
|
|
36
|
+
capabilities. `RealOrgConnection` is the production implementation backed by `@salesforce/core`.
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
interface OrgConnection {
|
|
40
|
+
request<T>(opts: {
|
|
41
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
42
|
+
url: string;
|
|
43
|
+
body?: string;
|
|
44
|
+
headers?: { [name: string]: string };
|
|
45
|
+
}): Promise<T>;
|
|
46
|
+
|
|
47
|
+
getAccessToken(): string;
|
|
48
|
+
getInstanceUrl(): string;
|
|
49
|
+
getUsername(): string;
|
|
50
|
+
getOrgId(): string;
|
|
51
|
+
getOrgAliasOrUsername(): string;
|
|
52
|
+
getInferredSfApiEnv(): SfApiEnv;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### `OrgConnectionFactory` / `RealOrgConnectionFactory`
|
|
57
|
+
|
|
58
|
+
Factory for creating authenticated `OrgConnection` instances. `RealOrgConnectionFactory` is the production
|
|
59
|
+
implementation backed by `@salesforce/core`.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
interface OrgConnectionFactory {
|
|
63
|
+
createFromOrgAliasOrUsername(orgAliasOrUsername: string): Promise<OrgConnection>;
|
|
64
|
+
createFromTargetOrg(options?: { projectRoot?: string }): Promise<OrgConnection>;
|
|
65
|
+
createFromCredentials(accessToken: string, instanceUrl: string): Promise<OrgConnection>;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `SfApiEnv`
|
|
70
|
+
|
|
71
|
+
Enum of Salesforce API environments used for gateway routing. Consumers access the inferred value via
|
|
72
|
+
`OrgConnection.getInferredSfApiEnv()` which uses the instance URL and the `SF_API_ENV` environment variable override.
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
enum SfApiEnv {
|
|
76
|
+
Dev = 'dev',
|
|
77
|
+
Perf = 'perf',
|
|
78
|
+
Prod = 'prod',
|
|
79
|
+
Stage = 'stage',
|
|
80
|
+
Test = 'test',
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### `Clock` / `RealClock`
|
|
85
|
+
|
|
86
|
+
Abstract time source for dependency injection. Use `RealClock` in production; extend `Clock` in tests for deterministic
|
|
87
|
+
time control.
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
abstract class Clock {
|
|
91
|
+
abstract now(): Date;
|
|
92
|
+
// Default: returns ts + 1ms. Override for test scenarios needing different stepping.
|
|
93
|
+
nextAfter(ts: Date): Date;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
class RealClock extends Clock {
|
|
97
|
+
now(): Date; // returns new Date()
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### `getErrorMessage(err: unknown): string`
|
|
102
|
+
|
|
103
|
+
Safely extracts a message string from any thrown value — `Error` instances, error-like objects with a `message`
|
|
104
|
+
property, or arbitrary values (coerced via `String()`).
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { getErrorMessage } from '@salesforce/agentic-common';
|
|
108
|
+
|
|
109
|
+
try { ... } catch (err) {
|
|
110
|
+
console.error(getErrorMessage(err));
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `isAbortError(err: unknown): boolean`
|
|
115
|
+
|
|
116
|
+
Returns true for either platform shape of an abort signal error: the DOM `AbortError` (`err.name === 'AbortError'`, e.g.
|
|
117
|
+
from `AbortController.abort()` against `fetch` / `undici.request`) or the Node `ABORT_ERR` (`err.code === 'ABORT_ERR'`).
|
|
118
|
+
Useful in retry loops and error handlers that need to distinguish caller-initiated cancellation from transport failures.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { isAbortError } from '@salesforce/agentic-common';
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
await someAsyncWork(signal);
|
|
125
|
+
} catch (err) {
|
|
126
|
+
if (isAbortError(err)) return; // caller cancelled; not an error to surface
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### `UniqueIDGenerator` / `UUIDGenerator`
|
|
132
|
+
|
|
133
|
+
Interface + default implementation for generating unique identifiers. Tests can inject deterministic implementations.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
interface UniqueIDGenerator {
|
|
137
|
+
getUniqueId(): string;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
class UUIDGenerator implements UniqueIDGenerator {
|
|
141
|
+
getUniqueId(): string; // wraps crypto.randomUUID()
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### `EventBus<T>`
|
|
146
|
+
|
|
147
|
+
A minimal, typed event bus. One type parameter, one emission channel. Error-isolated (a throwing listener never breaks
|
|
148
|
+
`emit` or other listeners).
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
class EventBus<T> {
|
|
152
|
+
readonly listenerCount: number;
|
|
153
|
+
|
|
154
|
+
on(callback: EventListener<T>): Unsubscribe;
|
|
155
|
+
emit(event: T): void;
|
|
156
|
+
forwardTo(target: EventBus<T>, enrich?: (event: T) => T): Unsubscribe;
|
|
157
|
+
dispose(): void;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
type EventListener<T> = (event: T) => void;
|
|
161
|
+
type Unsubscribe = () => void;
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
| Method | Description |
|
|
165
|
+
| ---------------------- | --------------------------------------------------------------------------------------------------- |
|
|
166
|
+
| `on(callback)` | Subscribe. Returns an `Unsubscribe` function — no need to hold the callback reference for removal. |
|
|
167
|
+
| `emit(event)` | Deliver to all listeners. Listener errors are caught and ignored so one bad listener can't cascade. |
|
|
168
|
+
| `forwardTo(target, ?)` | Subscribe to this bus and re-emit every event onto `target`. Optional `enrich` transforms events. |
|
|
169
|
+
| `dispose()` | Remove all listeners. Safe to call multiple times. |
|
|
170
|
+
| `listenerCount` | Current listener count (useful for leak-check assertions in tests). |
|
|
171
|
+
|
|
172
|
+
### `LogRecord` / `LogBus`
|
|
173
|
+
|
|
174
|
+
`LogRecord` is the structured log shape used across packages. `LogBus` extends `EventBus<LogRecord>` with level-named
|
|
175
|
+
convenience methods so emit sites read as `bus.warn('msg', { ctx })` instead of repeating the
|
|
176
|
+
`{ level, message, timestamp, ... }` literal.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
180
|
+
|
|
181
|
+
type LogRecord = {
|
|
182
|
+
level: LogLevel;
|
|
183
|
+
message: string;
|
|
184
|
+
timestamp: Date;
|
|
185
|
+
context?: Record<string, unknown>;
|
|
186
|
+
error?: Error;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
class LogBus extends EventBus<LogRecord> {
|
|
190
|
+
debug(message: string, context?: Record<string, unknown>): void;
|
|
191
|
+
info(message: string, context?: Record<string, unknown>): void;
|
|
192
|
+
warn(message: string, context?: Record<string, unknown>, error?: Error): void;
|
|
193
|
+
error(message: string, error?: Error, context?: Record<string, unknown>): void;
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### `Retryer` / `BackoffRetryer` / `NoOpRetryer`
|
|
198
|
+
|
|
199
|
+
Generic retry orchestration. Consumers depend on the `Retryer` interface and supply per-call decisions (which errors /
|
|
200
|
+
results are retryable, how to extract a server-driven `Retry-After`, what to log) via callbacks. Backoff timing,
|
|
201
|
+
jittering, deadline enforcement, and abort handling are construction details of `BackoffRetryer` — invisible to the
|
|
202
|
+
caller.
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
interface Retryer {
|
|
206
|
+
execute<T>(attemptFn: () => Promise<T>, callbacks?: RetryCallbacks<T>): Promise<T>;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
type RetryOptions = {
|
|
210
|
+
maxAttempts?: number; // Default: 3 (>= 1)
|
|
211
|
+
initialDelayMs?: number; // Default: 100
|
|
212
|
+
maxDelayMs?: number; // Default: 2000 (caps *computed* backoff only)
|
|
213
|
+
maxRetryAfterMs?: number; // Default: 60_000 (caps *server-driven* hints only)
|
|
214
|
+
backoffFactor?: number; // Default: 2
|
|
215
|
+
maxTotalElapsedMs?: number; // Default: Infinity
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
type RetryCallbacks<T> = {
|
|
219
|
+
signal?: AbortSignal;
|
|
220
|
+
isRetryableError?: (err: unknown) => boolean;
|
|
221
|
+
isRetryableResult?: (result: T) => boolean;
|
|
222
|
+
getRetryAfterMs?: (result: T) => number | undefined;
|
|
223
|
+
onRetry?: (info: { attempt: number; delayMs: number; error?: unknown; result?: T }) => void;
|
|
224
|
+
onExhausted?: (info: { attempts: number; error?: unknown; result?: T; reason: 'attempts' | 'deadline' }) => void;
|
|
225
|
+
drainResult?: (result: T) => Promise<void>;
|
|
226
|
+
};
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Use `BackoffRetryer` in production:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { BackoffRetryer } from '@salesforce/agentic-common';
|
|
233
|
+
|
|
234
|
+
const retryer = new BackoffRetryer({ maxAttempts: 3, initialDelayMs: 100 });
|
|
235
|
+
const response = await retryer.execute(() => fetch(url), {
|
|
236
|
+
isRetryableError: (err) => (err as { code?: string }).code === 'ECONNRESET',
|
|
237
|
+
isRetryableResult: (res) => res.status >= 500,
|
|
238
|
+
onRetry: ({ attempt, delayMs }) => log.warn(`retry ${attempt} in ${delayMs}ms`),
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Use `NoOpRetryer` in tests where retry behavior is irrelevant:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import { NoOpRetryer } from '@salesforce/agentic-common';
|
|
246
|
+
|
|
247
|
+
const client = new SomeClient(opts, { retryer: new NoOpRetryer() });
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### `getErrorMessageWithStack(err: unknown): string`
|
|
251
|
+
|
|
252
|
+
Like `getErrorMessage`, but returns the full stack trace for `Error` instances.
|
|
253
|
+
|
|
254
|
+
### `wrapError(err: unknown, message: string): Error`
|
|
255
|
+
|
|
256
|
+
Creates a new `Error` with a prefixed message and the original error as `cause`.
|
|
257
|
+
|
|
258
|
+
## Development
|
|
259
|
+
|
|
260
|
+
See [DEVELOPING.md](../../DEVELOPING.md) for build-from-source setup, scripts, and monorepo commands.
|
|
261
|
+
|
|
262
|
+
See [ARCHITECTURE.md](ARCHITECTURE.md) for implementation notes.
|
package/dist/clock.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare abstract class Clock {
|
|
2
|
+
abstract now(): Date;
|
|
3
|
+
/**
|
|
4
|
+
* Returns a `Date` strictly after the given timestamp. The default implementation adds one
|
|
5
|
+
* millisecond, which is enough for ordering invariants in storage layers that sort by
|
|
6
|
+
* `createdAt`. Override if you need different stepping behavior in a test.
|
|
7
|
+
*/
|
|
8
|
+
nextAfter(ts: Date): Date;
|
|
9
|
+
}
|
|
10
|
+
export declare class RealClock extends Clock {
|
|
11
|
+
now(): Date;
|
|
12
|
+
}
|
package/dist/clock.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026, Salesforce, Inc. All rights reserved.
|
|
3
|
+
* See LICENSE.txt for license terms.
|
|
4
|
+
*/
|
|
5
|
+
export class Clock {
|
|
6
|
+
/**
|
|
7
|
+
* Returns a `Date` strictly after the given timestamp. The default implementation adds one
|
|
8
|
+
* millisecond, which is enough for ordering invariants in storage layers that sort by
|
|
9
|
+
* `createdAt`. Override if you need different stepping behavior in a test.
|
|
10
|
+
*/
|
|
11
|
+
nextAfter(ts) {
|
|
12
|
+
return new Date(ts.getTime() + 1);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export class RealClock extends Clock {
|
|
16
|
+
now() {
|
|
17
|
+
return new Date();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=clock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clock.js","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,OAAgB,KAAK;IAGvB;;;;OAIG;IACI,SAAS,CAAC,EAAQ;QACrB,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IACtC,CAAC;CACJ;AAED,MAAM,OAAO,SAAU,SAAQ,KAAK;IACzB,GAAG;QACN,OAAO,IAAI,IAAI,EAAE,CAAC;IACtB,CAAC;CACJ"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { OrgConnection } from './connection.js';
|
|
2
|
+
/**
|
|
3
|
+
* Factory for creating authenticated {@link OrgConnection} instances to Salesforce orgs.
|
|
4
|
+
*
|
|
5
|
+
* Each method authenticates against the org, verifies the token is valid, and returns
|
|
6
|
+
* a ready-to-use connection with full identity metadata (orgId, username, instanceUrl).
|
|
7
|
+
*/
|
|
8
|
+
export interface OrgConnectionFactory {
|
|
9
|
+
/**
|
|
10
|
+
* Creates a connection by looking up stored auth for a specific org alias or username.
|
|
11
|
+
*
|
|
12
|
+
* Resolves the alias to a username (if it is an alias), loads the locally-stored
|
|
13
|
+
* auth credentials, and verifies the token via a lightweight refresh call.
|
|
14
|
+
*
|
|
15
|
+
* @param orgAliasOrUsername - An org alias (e.g. `"my-dev-org"`) or a full username
|
|
16
|
+
* (e.g. `"user@example.org"`). Aliases are resolved via the SF CLI alias store.
|
|
17
|
+
*/
|
|
18
|
+
createFromOrgAliasOrUsername(orgAliasOrUsername: string): Promise<OrgConnection>;
|
|
19
|
+
/**
|
|
20
|
+
* Creates a connection to the current target org, using the same resolution logic
|
|
21
|
+
* as the `sf` CLI:
|
|
22
|
+
*
|
|
23
|
+
* 1. If `projectRoot` is provided and is within a Salesforce DX project, uses the
|
|
24
|
+
* project-local `target-org` from `.sf/config.json`.
|
|
25
|
+
* 2. Otherwise falls back to the machine-global `target-org` config value.
|
|
26
|
+
*
|
|
27
|
+
* Throws if no target org is configured at either level.
|
|
28
|
+
*
|
|
29
|
+
* @param options.projectRoot - A path within a Salesforce DX project. Used to locate
|
|
30
|
+
* the project root and read its local config. If omitted or not within a project,
|
|
31
|
+
* only the global default is consulted.
|
|
32
|
+
*/
|
|
33
|
+
createFromTargetOrg(options?: {
|
|
34
|
+
projectRoot?: string;
|
|
35
|
+
}): Promise<OrgConnection>;
|
|
36
|
+
/**
|
|
37
|
+
* Creates a connection from a raw access token and instance URL (no local auth store).
|
|
38
|
+
*
|
|
39
|
+
* Useful for CI environments or programmatic access where credentials are provided
|
|
40
|
+
* via environment variables rather than the SF CLI keychain. Internally calls the
|
|
41
|
+
* Salesforce `/services/oauth2/userinfo` endpoint to resolve the username and orgId
|
|
42
|
+
* from the token.
|
|
43
|
+
*
|
|
44
|
+
* @param accessToken - A valid Salesforce OAuth access token.
|
|
45
|
+
* @param instanceUrl - The org's instance URL (e.g. `"https://myorg.my.salesforce.com"`).
|
|
46
|
+
*/
|
|
47
|
+
createFromCredentials(accessToken: string, instanceUrl: string): Promise<OrgConnection>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Production implementation of {@link OrgConnectionFactory} backed by `@salesforce/core`.
|
|
51
|
+
*/
|
|
52
|
+
export declare class RealOrgConnectionFactory implements OrgConnectionFactory {
|
|
53
|
+
createFromOrgAliasOrUsername(orgAliasOrUsername: string): Promise<OrgConnection>;
|
|
54
|
+
createFromTargetOrg(options?: {
|
|
55
|
+
projectRoot?: string;
|
|
56
|
+
}): Promise<OrgConnection>;
|
|
57
|
+
createFromCredentials(accessToken: string, instanceUrl: string): Promise<OrgConnection>;
|
|
58
|
+
private resolveAlias;
|
|
59
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026, Salesforce, Inc. All rights reserved.
|
|
3
|
+
* See LICENSE.txt for license terms.
|
|
4
|
+
*/
|
|
5
|
+
import { AuthInfo as SfCoreAuthInfo, ConfigAggregator, Connection as SfCoreConnection, OrgConfigProperties, SfProject, StateAggregator, } from '@salesforce/core';
|
|
6
|
+
import { RealOrgConnection } from './connection.js';
|
|
7
|
+
import { wrapError } from './error-utils.js';
|
|
8
|
+
/**
|
|
9
|
+
* Production implementation of {@link OrgConnectionFactory} backed by `@salesforce/core`.
|
|
10
|
+
*/
|
|
11
|
+
export class RealOrgConnectionFactory {
|
|
12
|
+
async createFromOrgAliasOrUsername(orgAliasOrUsername) {
|
|
13
|
+
try {
|
|
14
|
+
const username = await this.resolveAlias(orgAliasOrUsername);
|
|
15
|
+
const authInfo = await SfCoreAuthInfo.create({ username });
|
|
16
|
+
const sfConn = await SfCoreConnection.create({ authInfo });
|
|
17
|
+
// refreshAuth will throw if the access token is invalid.
|
|
18
|
+
await sfConn.refreshAuth();
|
|
19
|
+
const fields = sfConn.getConnectionOptions();
|
|
20
|
+
if (!fields.accessToken || !fields.instanceUrl) {
|
|
21
|
+
throw new Error('Missing accessToken or instanceUrl. Try re-authenticating.');
|
|
22
|
+
}
|
|
23
|
+
const orgId = authInfo.getFields(true).orgId;
|
|
24
|
+
if (!orgId) {
|
|
25
|
+
throw new Error('Missing orgId. Try re-authenticating.');
|
|
26
|
+
}
|
|
27
|
+
return new RealOrgConnection(sfConn, fields.accessToken, fields.instanceUrl, username, orgId, orgAliasOrUsername);
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
throw wrapError(err, `Failed to establish connection for "${orgAliasOrUsername}".`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async createFromTargetOrg(options) {
|
|
34
|
+
if (options?.projectRoot) {
|
|
35
|
+
try {
|
|
36
|
+
const projectPath = await SfProject.resolveProjectPath(options.projectRoot);
|
|
37
|
+
const projectAggregator = await ConfigAggregator.create({ projectPath });
|
|
38
|
+
const info = projectAggregator.getInfo(OrgConfigProperties.TARGET_ORG);
|
|
39
|
+
const aliasOrUsername = typeof info.value === 'string' ? info.value : undefined;
|
|
40
|
+
if (aliasOrUsername && info.isLocal()) {
|
|
41
|
+
return this.createFromOrgAliasOrUsername(aliasOrUsername);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Not in an SFDX project or config read failure — fall through to global default.
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const aggregator = await ConfigAggregator.create();
|
|
49
|
+
const aliasOrUsername = aggregator.getPropertyValue(OrgConfigProperties.TARGET_ORG);
|
|
50
|
+
if (!aliasOrUsername) {
|
|
51
|
+
throw new Error('No default org is configured. Set a default with `sf org login ... --set-default-org` or set the `target-org` config value.');
|
|
52
|
+
}
|
|
53
|
+
return this.createFromOrgAliasOrUsername(aliasOrUsername);
|
|
54
|
+
}
|
|
55
|
+
async createFromCredentials(accessToken, instanceUrl) {
|
|
56
|
+
try {
|
|
57
|
+
const authInfo = await SfCoreAuthInfo.create({
|
|
58
|
+
accessTokenOptions: { accessToken, instanceUrl },
|
|
59
|
+
});
|
|
60
|
+
const sfConn = await SfCoreConnection.create({ authInfo });
|
|
61
|
+
// Fail fast: issues a lightweight GET to verify the token is valid.
|
|
62
|
+
await sfConn.refreshAuth();
|
|
63
|
+
const username = authInfo.getUsername();
|
|
64
|
+
const orgId = authInfo.getFields(true).orgId;
|
|
65
|
+
if (!username) {
|
|
66
|
+
throw new Error('Could not determine username from credentials.');
|
|
67
|
+
}
|
|
68
|
+
if (!orgId) {
|
|
69
|
+
throw new Error('Could not determine orgId from credentials.');
|
|
70
|
+
}
|
|
71
|
+
return new RealOrgConnection(sfConn, accessToken, instanceUrl, username, orgId, username);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
throw wrapError(err, `Failed to establish connection for ${instanceUrl}.`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async resolveAlias(orgAliasOrUsername) {
|
|
78
|
+
const stateAggregator = await StateAggregator.getInstance();
|
|
79
|
+
return stateAggregator.aliases.resolveUsername(orgAliasOrUsername) ?? orgAliasOrUsername;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=connection-factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-factory.js","sourceRoot":"","sources":["../src/connection-factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Connection as SfCoreConnection } from '@salesforce/core';
|
|
2
|
+
import { type SfApiEnv } from './sf-api-env.js';
|
|
3
|
+
/**
|
|
4
|
+
* Interface for making authenticated HTTP requests to a Salesforce org.
|
|
5
|
+
* Carries identity metadata (orgId, username) alongside transport capabilities.
|
|
6
|
+
*/
|
|
7
|
+
export interface OrgConnection {
|
|
8
|
+
request<T>(opts: {
|
|
9
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
10
|
+
url: string;
|
|
11
|
+
body?: string;
|
|
12
|
+
headers?: {
|
|
13
|
+
[name: string]: string;
|
|
14
|
+
};
|
|
15
|
+
}): Promise<T>;
|
|
16
|
+
getAccessToken(): string;
|
|
17
|
+
getInstanceUrl(): string;
|
|
18
|
+
getUsername(): string;
|
|
19
|
+
getOrgId(): string;
|
|
20
|
+
getOrgAliasOrUsername(): string;
|
|
21
|
+
getInferredSfApiEnv(): SfApiEnv;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Production implementation of {@link OrgConnection} backed by `@salesforce/core`.
|
|
25
|
+
*
|
|
26
|
+
* Instances are created via {@link OrgConnectionFactory} — the constructor is not public.
|
|
27
|
+
*/
|
|
28
|
+
export declare class RealOrgConnection implements OrgConnection {
|
|
29
|
+
private readonly sfConnection;
|
|
30
|
+
private accessToken;
|
|
31
|
+
private readonly instanceUrl;
|
|
32
|
+
private readonly username;
|
|
33
|
+
private readonly orgId;
|
|
34
|
+
private readonly orgAliasOrUsername;
|
|
35
|
+
constructor(sfConnection: SfCoreConnection, accessToken: string, instanceUrl: string, username: string, orgId: string, orgAliasOrUsername: string);
|
|
36
|
+
getAccessToken(): string;
|
|
37
|
+
getInstanceUrl(): string;
|
|
38
|
+
getUsername(): string;
|
|
39
|
+
getOrgId(): string;
|
|
40
|
+
getOrgAliasOrUsername(): string;
|
|
41
|
+
getInferredSfApiEnv(): SfApiEnv;
|
|
42
|
+
request<T>(opts: {
|
|
43
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
44
|
+
url: string;
|
|
45
|
+
body?: string;
|
|
46
|
+
headers?: {
|
|
47
|
+
[name: string]: string;
|
|
48
|
+
};
|
|
49
|
+
}): Promise<T>;
|
|
50
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026, Salesforce, Inc. All rights reserved.
|
|
3
|
+
* See LICENSE.txt for license terms.
|
|
4
|
+
*/
|
|
5
|
+
import { Connection as SfCoreConnection } from '@salesforce/core';
|
|
6
|
+
import { inferSfApiEnv } from './sf-api-env.js';
|
|
7
|
+
/**
|
|
8
|
+
* Production implementation of {@link OrgConnection} backed by `@salesforce/core`.
|
|
9
|
+
*
|
|
10
|
+
* Instances are created via {@link OrgConnectionFactory} — the constructor is not public.
|
|
11
|
+
*/
|
|
12
|
+
export class RealOrgConnection {
|
|
13
|
+
sfConnection;
|
|
14
|
+
accessToken;
|
|
15
|
+
instanceUrl;
|
|
16
|
+
username;
|
|
17
|
+
orgId;
|
|
18
|
+
orgAliasOrUsername;
|
|
19
|
+
constructor(sfConnection, accessToken, instanceUrl, username, orgId, orgAliasOrUsername) {
|
|
20
|
+
this.sfConnection = sfConnection;
|
|
21
|
+
this.accessToken = accessToken;
|
|
22
|
+
this.instanceUrl = instanceUrl;
|
|
23
|
+
this.username = username;
|
|
24
|
+
this.orgId = orgId;
|
|
25
|
+
this.orgAliasOrUsername = orgAliasOrUsername;
|
|
26
|
+
sfConnection.on('refresh', (newToken) => {
|
|
27
|
+
this.accessToken = newToken;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
getAccessToken() {
|
|
31
|
+
return this.accessToken;
|
|
32
|
+
}
|
|
33
|
+
getInstanceUrl() {
|
|
34
|
+
return this.instanceUrl;
|
|
35
|
+
}
|
|
36
|
+
getUsername() {
|
|
37
|
+
return this.username;
|
|
38
|
+
}
|
|
39
|
+
getOrgId() {
|
|
40
|
+
return this.orgId;
|
|
41
|
+
}
|
|
42
|
+
getOrgAliasOrUsername() {
|
|
43
|
+
return this.orgAliasOrUsername;
|
|
44
|
+
}
|
|
45
|
+
getInferredSfApiEnv() {
|
|
46
|
+
return inferSfApiEnv(this.instanceUrl);
|
|
47
|
+
}
|
|
48
|
+
async request(opts) {
|
|
49
|
+
return this.sfConnection.request({
|
|
50
|
+
method: opts.method,
|
|
51
|
+
url: opts.url,
|
|
52
|
+
body: opts.body,
|
|
53
|
+
headers: opts.headers,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,IAAI,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAiB,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAsB/D;;;;GAIG;AACH,MAAM,OAAO,iBAAiB;IACT,YAAY,CAAmB;IACxC,WAAW,CAAS;IACX,WAAW,CAAS;IACpB,QAAQ,CAAS;IACjB,KAAK,CAAS;IACd,kBAAkB,CAAS;IAE5C,YACI,YAA8B,EAC9B,WAAmB,EACnB,WAAmB,EACnB,QAAgB,EAChB,KAAa,EACb,kBAA0B;QAE1B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,kBAAkB,GAAG,kBAAkB,CAAC;QAE7C,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,QAAgB,EAAE,EAAE;YAC5C,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC;QAChC,CAAC,CAAC,CAAC;IACP,CAAC;IAED,cAAc;QACV,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED,cAAc;QACV,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED,WAAW;QACP,OAAO,IAAI,CAAC,QAAQ,CAAC;IACzB,CAAC;IAED,QAAQ;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,qBAAqB;QACjB,OAAO,IAAI,CAAC,kBAAkB,CAAC;IACnC,CAAC;IAED,mBAAmB;QACf,OAAO,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,OAAO,CAAI,IAKhB;QACG,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAI;YAChC,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,OAAO;SACxB,CAAC,CAAC;IACP,CAAC;CACJ"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function getErrorMessage(err: unknown): string;
|
|
2
|
+
/**
|
|
3
|
+
* Returns true if the value is an abort signal error from either platform shape:
|
|
4
|
+
* - DOM `AbortError` (e.g. `AbortController.abort()` against `fetch`/`undici.request`)
|
|
5
|
+
* - Node `ABORT_ERR` (`code === 'ABORT_ERR'`)
|
|
6
|
+
*/
|
|
7
|
+
export declare function isAbortError(err: unknown): boolean;
|
|
8
|
+
export declare function getErrorMessageWithStack(err: unknown): string;
|
|
9
|
+
export declare function wrapError(err: unknown, message: string): Error;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026, Salesforce, Inc. All rights reserved.
|
|
3
|
+
* See LICENSE.txt for license terms.
|
|
4
|
+
*/
|
|
5
|
+
export function getErrorMessage(err) {
|
|
6
|
+
return err instanceof Error || (typeof err === 'object' && err !== null && 'message' in err)
|
|
7
|
+
? String(err.message)
|
|
8
|
+
: String(err);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Returns true if the value is an abort signal error from either platform shape:
|
|
12
|
+
* - DOM `AbortError` (e.g. `AbortController.abort()` against `fetch`/`undici.request`)
|
|
13
|
+
* - Node `ABORT_ERR` (`code === 'ABORT_ERR'`)
|
|
14
|
+
*/
|
|
15
|
+
export function isAbortError(err) {
|
|
16
|
+
if (typeof err !== 'object' || err === null) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
const name = err.name;
|
|
20
|
+
const code = err.code;
|
|
21
|
+
return name === 'AbortError' || code === 'ABORT_ERR';
|
|
22
|
+
}
|
|
23
|
+
export function getErrorMessageWithStack(err) {
|
|
24
|
+
if (err instanceof Error && err.stack) {
|
|
25
|
+
return err.stack;
|
|
26
|
+
}
|
|
27
|
+
return getErrorMessage(err);
|
|
28
|
+
}
|
|
29
|
+
export function wrapError(err, message) {
|
|
30
|
+
return new Error(`${message}\n${getErrorMessageWithStack(err)}`, { cause: err });
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=error-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-utils.js","sourceRoot":"","sources":["../src/error-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,UAAU,eAAe,CAAC,GAAY;IACxC,OAAO,GAAG,YAAY,KAAK,IAAI,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,SAAS,IAAI,GAAG,CAAC;QACxF,CAAC,CAAC,MAAM,CAAE,GAA4B,CAAC,OAAO,CAAC;QAC/C,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AACtB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,GAAY;IACrC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,MAAM,IAAI,GAAI,GAA0B,CAAC,IAAI,CAAC;IAC9C,MAAM,IAAI,GAAI,GAA0B,CAAC,IAAI,CAAC;IAC9C,OAAO,IAAI,KAAK,YAAY,IAAI,IAAI,KAAK,WAAW,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,GAAY;IACjD,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;QACpC,OAAO,GAAG,CAAC,KAAK,CAAC;IACrB,CAAC;IACD,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAY,EAAE,OAAe;IACnD,OAAO,IAAI,KAAK,CAAC,GAAG,OAAO,KAAK,wBAAwB,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;AACrF,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type EventListener<T> = (event: T) => void;
|
|
2
|
+
export type Unsubscribe = () => void;
|
|
3
|
+
/**
|
|
4
|
+
* Minimal, typed, object-bound event bus. Carries a single event shape `T`.
|
|
5
|
+
*
|
|
6
|
+
* Listener errors are isolated: a throwing callback never interrupts `emit()` or other listeners.
|
|
7
|
+
* `on()` returns an `Unsubscribe` closure so callers don't need to retain the original callback reference.
|
|
8
|
+
*/
|
|
9
|
+
export declare class EventBus<T> {
|
|
10
|
+
private readonly listeners;
|
|
11
|
+
/** Number of currently registered listeners. Useful for leak-check assertions in tests. */
|
|
12
|
+
get listenerCount(): number;
|
|
13
|
+
/**
|
|
14
|
+
* Subscribe to events. Returns a function that unregisters this specific callback.
|
|
15
|
+
* Safe to call the returned function multiple times — subsequent calls are no-ops.
|
|
16
|
+
*/
|
|
17
|
+
on(callback: EventListener<T>): Unsubscribe;
|
|
18
|
+
/** Emit an event to every registered listener. Listener errors are caught and discarded. */
|
|
19
|
+
emit(event: T): void;
|
|
20
|
+
/**
|
|
21
|
+
* Subscribe to this bus and re-emit every event onto `target`. If `enrich` is supplied, each event is
|
|
22
|
+
* passed through it before re-emission. Returns an `Unsubscribe` for the internal subscription.
|
|
23
|
+
*/
|
|
24
|
+
forwardTo(target: EventBus<T>, enrich?: (event: T) => T): Unsubscribe;
|
|
25
|
+
/** Remove all listeners. Idempotent. */
|
|
26
|
+
dispose(): void;
|
|
27
|
+
}
|