@revenexx/integrations-node-sdk 1.9.0 → 1.9.1
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/dist/conditions.d.ts +16 -0
- package/dist/credentialType.d.ts +17 -0
- package/dist/credentials.d.ts +162 -0
- package/dist/errors.d.ts +5 -0
- package/dist/extract.d.ts +5 -0
- package/dist/fetch.d.ts +111 -0
- package/dist/images.d.ts +28 -0
- package/dist/index.d.ts +14 -1254
- package/dist/localized.d.ts +15 -0
- package/dist/manifest.d.ts +83 -0
- package/dist/nodeErrors.d.ts +61 -0
- package/dist/operators.d.ts +27 -0
- package/dist/retry.d.ts +75 -0
- package/dist/ssrf.d.ts +88 -0
- package/dist/types.d.ts +620 -0
- package/package.json +4 -4
- package/dist/index.d.cts +0 -1254
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LocalizedString } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reduce a {@link LocalizedString} to a single plain string.
|
|
4
|
+
*
|
|
5
|
+
* Every consumer (the UI, the Laravel side) repeats the same "is it a
|
|
6
|
+
* string or a localized map?" branch to render a name/label; this centralizes
|
|
7
|
+
* it so the rules are defined once:
|
|
8
|
+
*
|
|
9
|
+
* - a plain string is trimmed and returned (or `undefined` if blank);
|
|
10
|
+
* - a localized map prefers `fallbackLang` (default `en`), then the first
|
|
11
|
+
* non-empty value;
|
|
12
|
+
* - a missing, empty, or blank-only value yields `undefined`, so the caller
|
|
13
|
+
* can apply its own fallback.
|
|
14
|
+
*/
|
|
15
|
+
export declare function normalizeLocalized(value: LocalizedString | undefined | null, fallbackLang?: string): string | undefined;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { ICredential, ICredentialDescription, INode, INodeDescription, ITemplateDescription } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Envelope version expected by the integrations server-side
|
|
4
|
+
* `TarballInspector`. Earlier (pre-registry) builds emitted a bare array
|
|
5
|
+
* without any `manifestVersion` field; `v0-draft` is currently the only
|
|
6
|
+
* accepted value (see `SchemaServiceProvider::DOMAIN_NODE` in the
|
|
7
|
+
* integrations service). Keeping it versioned lets the registry evolve the
|
|
8
|
+
* shape without breaking older publishers.
|
|
9
|
+
*/
|
|
10
|
+
export declare const MANIFEST_VERSION = "v0-draft";
|
|
11
|
+
export interface NodeManifest {
|
|
12
|
+
manifestVersion: typeof MANIFEST_VERSION;
|
|
13
|
+
nodes: INodeDescription[];
|
|
14
|
+
/**
|
|
15
|
+
* Credential types this package publishes. Optional and additive: packages
|
|
16
|
+
* that ship no credentials omit it (older publishers never set it).
|
|
17
|
+
*/
|
|
18
|
+
credentials?: ICredentialDescription[];
|
|
19
|
+
/**
|
|
20
|
+
* Workflow templates this package publishes. Optional and additive: packages
|
|
21
|
+
* that ship no templates omit it. Templates are plain data (no executable
|
|
22
|
+
* code), so they are carried verbatim from the package's `TEMPLATES` export.
|
|
23
|
+
*/
|
|
24
|
+
templates?: ITemplateDescription[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The registry-relevant fields of a node package's `package.json`, as read by
|
|
28
|
+
* the CLI. `name`/`version` identify the package; `displayName` is the
|
|
29
|
+
* human-readable bundle label shown in the editor's node palette (e.g.
|
|
30
|
+
* „Business Central"); `icon` marks its folder there. All four are read straight
|
|
31
|
+
* from `package.json` by the integrations server on upload — the CLI reads them
|
|
32
|
+
* only to warn about a missing label, to annotate the manifest log line, and to
|
|
33
|
+
* stop on an icon the server would refuse.
|
|
34
|
+
*
|
|
35
|
+
* The label lives under a namespaced `revenexx` group in `package.json`
|
|
36
|
+
* (`{ "revenexx": { "displayName": "…" } }`), not a bespoke top-level key, so
|
|
37
|
+
* it can't collide with unrelated tooling that squats on `displayName`.
|
|
38
|
+
*/
|
|
39
|
+
export interface NodePackageMeta {
|
|
40
|
+
name: string;
|
|
41
|
+
version: string;
|
|
42
|
+
/** Human-readable bundle label (e.g. „Business Central"); optional. */
|
|
43
|
+
displayName?: string;
|
|
44
|
+
/**
|
|
45
|
+
* The glyph that marks the package's folder in the studio's palette, as
|
|
46
|
+
* `lucide:<kebab-name>` (e.g. `lucide:bell`); optional. Declared by a package
|
|
47
|
+
* holding several services, which has no one vendor logo to mark its folder
|
|
48
|
+
* with — a package that is one vendor leaves it out. Trimmed, and a value that
|
|
49
|
+
* is not a string counts as none, as the server reads it; whether it is well
|
|
50
|
+
* formed is {@link packageIconProblem}'s question.
|
|
51
|
+
*/
|
|
52
|
+
icon?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Why a declared `revenexx.icon` would be refused by the registry, or
|
|
56
|
+
* `undefined` when it is fine (or absent). Lets the build stop where the author
|
|
57
|
+
* can still fix it, rather than at upload. Takes the icon as
|
|
58
|
+
* {@link parsePackageMeta} returns it — trimmed as the registry trims it; an
|
|
59
|
+
* untrimmed raw value can be refused here although the registry accepts it.
|
|
60
|
+
*/
|
|
61
|
+
export declare function packageIconProblem(icon: string | undefined): string | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* Extracts {@link NodePackageMeta} from parsed `package.json` contents, keeping
|
|
64
|
+
* only the registry-relevant fields and coercing anything malformed to a safe
|
|
65
|
+
* shape. All four fields are trimmed — the icon of ASCII whitespace only, as the
|
|
66
|
+
* server's PHP `trim()` does, so a pasted no-break space is refused here as it
|
|
67
|
+
* is there rather than trimmed away; a blank or whitespace-only value becomes
|
|
68
|
+
* `''` (`name`/`version`) or `undefined` (`displayName`, `icon`, matching how the
|
|
69
|
+
* server treats them), so whitespace can't masquerade as a present value in
|
|
70
|
+
* tooling. The bundle label and the folder icon are read from the `revenexx`
|
|
71
|
+
* group (`revenexx.displayName`, `revenexx.icon`).
|
|
72
|
+
* Does not validate that `name`/`version` are present — the integrations server
|
|
73
|
+
* enforces that on upload; this is a typed, lenient read for tooling.
|
|
74
|
+
*/
|
|
75
|
+
export declare function parsePackageMeta(raw: unknown): NodePackageMeta;
|
|
76
|
+
/**
|
|
77
|
+
* Builds the manifest envelope from a package's `NODES` (and optional
|
|
78
|
+
* `CREDENTIALS` / `TEMPLATES`) exports. The result is what gets written to
|
|
79
|
+
* `dist/manifest.json` and uploaded to the registry inside the tarball. The
|
|
80
|
+
* bundle label is NOT carried here — the integrations server reads it straight
|
|
81
|
+
* from `package.json` (`revenexx.displayName`).
|
|
82
|
+
*/
|
|
83
|
+
export declare function buildManifest(nodes: INode[], credentials?: ICredential[], templates?: ITemplateDescription[]): NodeManifest;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { INodeResult, IOutputPort } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Canonical error-output shape emitted on a node's `error` port. It lives here
|
|
4
|
+
* rather than in a node package because all four of them were building it
|
|
5
|
+
* separately — `integrations-nodes-core` in an internal module, `deepl` in a
|
|
6
|
+
* copy of that module, Business Central in `bc-fetch.ts` and Pipedrive in
|
|
7
|
+
* `pipedrive-client.ts` — so the workflow editor's guarantee of one
|
|
8
|
+
* `{ code, message, status }` payload rested on four files agreeing by hand
|
|
9
|
+
* (PO-442; asked for by PO-134 in June and not done then).
|
|
10
|
+
*/
|
|
11
|
+
export interface NodeErrorOutput {
|
|
12
|
+
/** Machine-readable error code, e.g. `HTTP_ERROR`, `TIMEOUT`, `REQUEST_FAILED`. */
|
|
13
|
+
code: string;
|
|
14
|
+
message: string;
|
|
15
|
+
/** HTTP status code, or `0` for timeouts / network / non-HTTP errors. */
|
|
16
|
+
status: number;
|
|
17
|
+
/** Optional response body — HTTP-specific. */
|
|
18
|
+
body?: unknown;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Normalize any caught value into the canonical {@link NodeErrorOutput} shape.
|
|
22
|
+
* Use this in a node's `catch` block before routing to the `error` port.
|
|
23
|
+
*/
|
|
24
|
+
export declare function toErrorOutput(err: unknown): NodeErrorOutput;
|
|
25
|
+
/** Wrap an error output into an `INodeResult` routed to the `error` port. */
|
|
26
|
+
export declare function errorResult(output: NodeErrorOutput, port?: string): INodeResult;
|
|
27
|
+
/**
|
|
28
|
+
* Catch-block shortcut: normalize any thrown value and route it to the failure
|
|
29
|
+
* output. The one helper every node's `catch` funnels into, so the
|
|
30
|
+
* `{ code, message, status }` mapping lives in a single place instead of being
|
|
31
|
+
* re-implemented per protocol (FTP/SFTP) or per package.
|
|
32
|
+
*
|
|
33
|
+
* `port` is the output taken, `error` unless the node declares its failure
|
|
34
|
+
* output under another name (`{ ...errorPort(), name: 'failed' }`) — pass the
|
|
35
|
+
* same name here, or the branch names a port that node does not have.
|
|
36
|
+
*/
|
|
37
|
+
export declare function toErrorResult(err: unknown, port?: string): INodeResult;
|
|
38
|
+
/**
|
|
39
|
+
* Catch-block shortcut for the HTTP-style nodes whose failure output additionally
|
|
40
|
+
* declares `body`: the canonical `{ code, message, status }` mapping plus the
|
|
41
|
+
* response body a {@link NodeError} carries in `meta.body` (`null` when the
|
|
42
|
+
* failure never reached a response). `port` names the output as above.
|
|
43
|
+
*/
|
|
44
|
+
export declare function httpErrorResult(err: unknown, port?: string): INodeResult;
|
|
45
|
+
/**
|
|
46
|
+
* Shared `error` output port — the uniform `{ code, message, status }` triple
|
|
47
|
+
* declared on every node that has an `error` branch. Pass `body: true` for
|
|
48
|
+
* HTTP-style nodes that additionally emit a response body on the port.
|
|
49
|
+
*
|
|
50
|
+
* The sentences travel with the port. The three values mean the same thing
|
|
51
|
+
* behind every node, so they are written here once instead of per node; a node
|
|
52
|
+
* whose failures are worth naming more precisely spreads the port and replaces
|
|
53
|
+
* only its `description` (`{ ...errorPort(), description: … }`).
|
|
54
|
+
*
|
|
55
|
+
* The second sentence of the description is the rule this port exists under,
|
|
56
|
+
* and it is promised in `specs/error-handling.md`: a fault in the author's own
|
|
57
|
+
* configuration ends the run, a fault met while running is offered here.
|
|
58
|
+
*/
|
|
59
|
+
export declare function errorPort(opts?: {
|
|
60
|
+
body?: boolean;
|
|
61
|
+
}): IOutputPort;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The comparison vocabulary, in one place because it is read in three:
|
|
3
|
+
* a node that compares two values (a condition, a filter), a settings condition
|
|
4
|
+
* saying when a field applies (`IConfigField.showIf`), and the editor drawing
|
|
5
|
+
* that field. An author who has met these words in one of the three must not
|
|
6
|
+
* have to learn a second set for the others.
|
|
7
|
+
*
|
|
8
|
+
* The names are the vocabulary; what each one *means* is {@link evaluate}, and
|
|
9
|
+
* the answer table in `operators.test.ts` is what a second implementation of it
|
|
10
|
+
* — the editor, the platform's workflow validator — is checked against.
|
|
11
|
+
*/
|
|
12
|
+
export declare const OPERATORS: readonly ['equals', 'notEquals', 'contains', 'notContains', 'startsWith', 'endsWith', 'greaterThan', 'greaterThanOrEqual', 'lessThan', 'lessThanOrEqual', 'exists', 'notExists', 'isEmpty', 'isNotEmpty'];
|
|
13
|
+
export type Operator = (typeof OPERATORS)[number];
|
|
14
|
+
/** The four that read whether a value is there rather than what it is. */
|
|
15
|
+
export declare const VALUELESS_OPERATORS: readonly Operator[];
|
|
16
|
+
export declare function isOperator(value: unknown): value is Operator;
|
|
17
|
+
/** Whether an operator reads a second value at all, or only the first. */
|
|
18
|
+
export declare function takesValue(op: Operator): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* What an operator means.
|
|
21
|
+
*
|
|
22
|
+
* Equality is deliberately lenient across the string/number line: a `select`
|
|
23
|
+
* carries `1` where a text box carries `'1'`, and an author means one thing by
|
|
24
|
+
* both. A second implementation has to reproduce that — and has to be careful
|
|
25
|
+
* with booleans, whose default stringification differs between languages.
|
|
26
|
+
*/
|
|
27
|
+
export declare function evaluate(left: unknown, op: Operator, right: unknown): boolean;
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport-agnostic retry/backoff primitive (PO-139).
|
|
3
|
+
*
|
|
4
|
+
* Wraps *any* async operation — raw `fetch`, an SDK client (axios), a future
|
|
5
|
+
* transport — with exponential backoff + full jitter, honouring a
|
|
6
|
+
* server-dictated delay (HTTP `Retry-After`) when the caller supplies one.
|
|
7
|
+
*
|
|
8
|
+
* The retry decision lives with the connector: it throws a {@link RetryableError}
|
|
9
|
+
* only when an attempt failed *and* is worth retrying. Everything else is
|
|
10
|
+
* rethrown as-is, and terminal API errors that connectors model as *values*
|
|
11
|
+
* (e.g. a `{ kind: 'http-error' }` union) simply flow back as the return value.
|
|
12
|
+
*
|
|
13
|
+
* This is deliberately NOT a circuit breaker, rate limiter, or HTTP client, and
|
|
14
|
+
* it does not replace Temporal activity-level retries — it is the fine-grained,
|
|
15
|
+
* idempotency-aware, `Retry-After`-honouring layer inside a single activity.
|
|
16
|
+
*/
|
|
17
|
+
/** Thrown by the wrapped fn to signal "this attempt failed but is retryable". */
|
|
18
|
+
export declare class RetryableError extends Error {
|
|
19
|
+
/** Server-dictated delay before the next attempt, e.g. parsed from `Retry-After`. */
|
|
20
|
+
readonly retryAfterMs?: number;
|
|
21
|
+
constructor(message: string, opts?: {
|
|
22
|
+
retryAfterMs?: number;
|
|
23
|
+
cause?: unknown;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export interface RetryPolicy {
|
|
27
|
+
/** Total tries including the first. */
|
|
28
|
+
maxAttempts: number;
|
|
29
|
+
/** Base delay for the exponential backoff. */
|
|
30
|
+
baseDelayMs: number;
|
|
31
|
+
/** Upper bound for any single computed delay. */
|
|
32
|
+
maxDelayMs: number;
|
|
33
|
+
/** Exponential growth factor. */
|
|
34
|
+
factor: number;
|
|
35
|
+
/** Apply full jitter to the computed backoff. */
|
|
36
|
+
jitter: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare const DEFAULT_RETRY_POLICY: RetryPolicy;
|
|
39
|
+
export interface RetryHooks {
|
|
40
|
+
/** Pass `ctx.signal` so cancelling a workflow cancels the wait (and stops retrying) immediately. */
|
|
41
|
+
signal: AbortSignal;
|
|
42
|
+
logger?: {
|
|
43
|
+
warn(msg: string, meta?: Record<string, unknown>): void;
|
|
44
|
+
};
|
|
45
|
+
onRetry?(info: {
|
|
46
|
+
attempt: number;
|
|
47
|
+
delayMs: number;
|
|
48
|
+
error: unknown;
|
|
49
|
+
}): void;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Pure backoff math, exported for testing/reuse. Returns the delay before the
|
|
53
|
+
* retry that follows the given (1-based) failed `attempt`: an exponential term
|
|
54
|
+
* `baseDelayMs * factor^(attempt-1)` capped at `maxDelayMs`, with full jitter
|
|
55
|
+
* (`random() * cap`) when `policy.jitter` is set.
|
|
56
|
+
*/
|
|
57
|
+
export declare function backoffDelay(attempt: number, policy: RetryPolicy): number;
|
|
58
|
+
/**
|
|
59
|
+
* Abort-aware sleep. Resolves after `ms`, or rejects with the signal's reason
|
|
60
|
+
* (a DOMException `AbortError` by default) as soon as `signal` aborts — so a
|
|
61
|
+
* caller's retry loop stops cleanly instead of finishing the wait.
|
|
62
|
+
*/
|
|
63
|
+
export declare function sleepWithSignal(ms: number, signal: AbortSignal): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Runs `fn`; if it throws a {@link RetryableError} and attempts remain, waits
|
|
66
|
+
* (`RetryableError.retryAfterMs` when present, else the computed
|
|
67
|
+
* {@link backoffDelay}) respecting `signal`, then retries. Any other throw is
|
|
68
|
+
* rethrown immediately; on the final attempt the RetryableError itself is
|
|
69
|
+
* thrown (preserving its `retryAfterMs`/`cause`), not its `.cause`.
|
|
70
|
+
*
|
|
71
|
+
* Distinct from `safeFetch`'s built-in fixed-delay `retry` option, which is
|
|
72
|
+
* unchanged: that is a simple in-fetch retry; this is the richer, transport-
|
|
73
|
+
* agnostic, `Retry-After`-aware primitive connectors wrap around any operation.
|
|
74
|
+
*/
|
|
75
|
+
export declare function withRetry<T>(fn: (attempt: number) => Promise<T>, policy: Partial<RetryPolicy>, hooks: RetryHooks): Promise<T>;
|
package/dist/ssrf.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { NodeError } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* A resolved DNS address, mirroring the shape of Node's `dns.LookupAddress`.
|
|
4
|
+
* `family` is `4` or `6`; only `address` is consulted by the guard.
|
|
5
|
+
*/
|
|
6
|
+
export interface LookupAddress {
|
|
7
|
+
address: string;
|
|
8
|
+
family: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolves a hostname to every address it maps to. Modelled on
|
|
12
|
+
* `dns.lookup(host, { all: true })`. Injectable so tests can drive the guard
|
|
13
|
+
* deterministically without real DNS — see {@link ssrfResolver}.
|
|
14
|
+
*/
|
|
15
|
+
export type LookupFn = (hostname: string) => Promise<LookupAddress[]>;
|
|
16
|
+
/**
|
|
17
|
+
* The DNS resolver the SSRF guard uses when a caller does not pass an explicit
|
|
18
|
+
* `lookup`. It is a mutable holder (rather than a bare function) purely so tests
|
|
19
|
+
* can swap `ssrfResolver.lookup` via a spy and restore it afterwards — the guard
|
|
20
|
+
* itself always runs. **Do not repoint this in production.**
|
|
21
|
+
*/
|
|
22
|
+
export declare const ssrfResolver: {
|
|
23
|
+
lookup: LookupFn;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Return `true` when `ip` (a literal IPv4/IPv6 address) points at a private,
|
|
27
|
+
* loopback, link-local, carrier-grade NAT, multicast, broadcast, reserved,
|
|
28
|
+
* unallocated or otherwise non-public target that a server-side fetch must never be
|
|
29
|
+
* steered to.
|
|
30
|
+
*
|
|
31
|
+
* The verdict is an allow-list: the address is refused unless the classification
|
|
32
|
+
* calls its range public unicast and — for IPv6, where that verdict is a default
|
|
33
|
+
* rather than a determination — it also sits inside the block IANA has allocated
|
|
34
|
+
* (see {@link GLOBAL_UNICAST_V6}). The IPv6 forms that embed an IPv4 address are
|
|
35
|
+
* unwrapped and the embedded address judged in its place, so a public host stays
|
|
36
|
+
* reachable when it is written that way. An address we cannot parse is treated as
|
|
37
|
+
* blocked (fail-closed).
|
|
38
|
+
*
|
|
39
|
+
* Promised behaviour: specs/ssrf-guard.md (AC-9, AC-11, AC-19).
|
|
40
|
+
*/
|
|
41
|
+
export declare function isBlockedAddress(ip: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Put `url`'s host **and port** under the connect-time guard and return the
|
|
44
|
+
* release for it. Ref-counted, so concurrent calls to one target do not release
|
|
45
|
+
* each other. Host and port together are as narrow as the scope can be made: the
|
|
46
|
+
* `connected` message carries the target and not the caller, so this says which
|
|
47
|
+
* connections are *candidates* for judgement, never which one is ours.
|
|
48
|
+
*
|
|
49
|
+
* The window is the call, not the connection. A connect that completes after the
|
|
50
|
+
* release — the fetch it belonged to having already timed out or aborted — finds
|
|
51
|
+
* its target unregistered and is not judged; see the gap in
|
|
52
|
+
* `specs/ssrf-guard.md`.
|
|
53
|
+
*
|
|
54
|
+
* Deliberately **not** re-exported from `index.ts`. The guard is engaged by
|
|
55
|
+
* `safeFetch`, which is the one sanctioned way out to the network (PO-185); an
|
|
56
|
+
* exported handle would be a second one, and one that is easy to hold wrongly —
|
|
57
|
+
* the judgement it enables lasts exactly as long as the registration does.
|
|
58
|
+
*/
|
|
59
|
+
export declare function guardConnectionsTo(url: URL): () => void;
|
|
60
|
+
/**
|
|
61
|
+
* The refusal this guard raised on a socket, as it comes back out of `fetch`.
|
|
62
|
+
* undici reports a destroyed connection as `TypeError('fetch failed')` carrying
|
|
63
|
+
* the destroy reason as its `cause`, so the caller sees a generic network
|
|
64
|
+
* failure — which `safeFetch` would then *retry*. Unwrap it, so a blocked target
|
|
65
|
+
* surfaces as the deterministic `BLOCKED_ADDRESS` it is.
|
|
66
|
+
*/
|
|
67
|
+
export declare function connectionRefusal(err: unknown): NodeError | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Assert that `url` is safe for a server-side fetch: an http(s) URL whose target
|
|
70
|
+
* resolves only to public addresses. Rejects non-http(s) protocols, empty hosts
|
|
71
|
+
* and `localhost`, checks literal-IP hosts directly, and otherwise resolves the
|
|
72
|
+
* hostname (via the injectable `lookup`, defaulting to {@link ssrfResolver}) and
|
|
73
|
+
* rejects if **any** resolved address is private/reserved. Throws
|
|
74
|
+
* `NodeError('BLOCKED_ADDRESS', …, { status: 0 })` on rejection.
|
|
75
|
+
*
|
|
76
|
+
* This is the pre-flight half of the guard: the connection that follows resolves
|
|
77
|
+
* the name again on its own, so `safeFetch` also judges the address the socket
|
|
78
|
+
* actually reached (PO-184). A caller that opens its own request after
|
|
79
|
+
* `assertPublicUrl` gets the check and not that second half.
|
|
80
|
+
*
|
|
81
|
+
* Pass `signal` (the per-request timeout/cancellation budget) so a hung or
|
|
82
|
+
* hostile DNS resolve cannot block the guard past that budget — see
|
|
83
|
+
* {@link resolveHost}.
|
|
84
|
+
*/
|
|
85
|
+
export declare function assertPublicUrl(url: string | URL, opts?: {
|
|
86
|
+
lookup?: LookupFn;
|
|
87
|
+
signal?: AbortSignal;
|
|
88
|
+
}): Promise<void>;
|