@pellux/goodvibes-transport-http 0.18.3 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,25 @@
1
1
  # @pellux/goodvibes-transport-http
2
2
 
3
- HTTP, JSON, path, and SSE helpers for GoodVibes SDK clients.
3
+ Internal workspace package backing `@pellux/goodvibes-sdk/transport-http`.
4
4
 
5
- Install:
6
-
7
- ```bash
8
- npm install @pellux/goodvibes-transport-http
9
- ```
5
+ Consumers should install `@pellux/goodvibes-sdk` and import this surface from the umbrella package.
10
6
 
11
7
  Exports include:
12
8
  - contract route invocation helpers
13
9
  - HTTP transport creation
10
+ - direct JSON request helpers
14
11
  - header and auth token resolution helpers
15
12
  - retry/backoff helpers
16
13
  - SSE streaming helpers
17
14
 
18
- Use this package when you need lower-level HTTP/SSE control or when you are building a custom GoodVibes client on top of the synced contracts.
15
+ Use this surface when you need lower-level HTTP/SSE control or when you are building a custom GoodVibes client on top of the synced contracts.
16
+
17
+ ```ts
18
+ import { createJsonRequestInit, requestJson } from '@pellux/goodvibes-sdk/transport-http';
19
+
20
+ const body = await requestJson(
21
+ fetch,
22
+ 'http://127.0.0.1:3210/api/control-plane/auth',
23
+ createJsonRequestInit(process.env.GOODVIBES_TOKEN ?? null),
24
+ );
25
+ ```
package/dist/auth.d.ts CHANGED
@@ -1,7 +1,32 @@
1
1
  export type MaybePromise<T> = T | Promise<T>;
2
2
  export type AuthTokenResolver = () => MaybePromise<string | null | undefined>;
3
+ /**
4
+ * Any supported public auth-token input form.
5
+ * Normalised to `AuthTokenResolver` at SDK/transport boundaries via `normalizeAuthToken`.
6
+ */
7
+ export type AuthTokenInput = string | {
8
+ readonly token: string;
9
+ } | AuthTokenResolver | (() => string | null | undefined | Promise<string | null | undefined>) | undefined;
3
10
  export type HeaderResolver = () => MaybePromise<HeadersInit | undefined>;
4
11
  export declare function mergeHeaders(...sources: Array<HeadersInit | undefined>): Headers;
12
+ export declare function mergeHeaderRecord(...sources: Array<HeadersInit | undefined>): Record<string, string>;
13
+ /**
14
+ * Accepts any supported auth token form and returns a canonical async resolver.
15
+ *
16
+ * - `string` → resolver that always returns the string
17
+ * - `{ token: string }` → resolver that always returns `input.token`
18
+ * - sync function → resolver that awaits `input()` (errors propagate)
19
+ * - async function → passes through as-is
20
+ * - `undefined` → resolver that returns `undefined`
21
+ */
22
+ export declare function normalizeAuthToken(input: AuthTokenInput): AuthTokenResolver;
23
+ /**
24
+ * Resolve the token for an outbound transport request.
25
+ *
26
+ * This helper is intentionally transport-facing: it does not read process
27
+ * environment variables, config files, or global SDK state. Higher layers are
28
+ * responsible for choosing a token source and passing it here explicitly.
29
+ */
5
30
  export declare function resolveAuthToken(authToken: string | null | undefined, getAuthToken?: AuthTokenResolver): Promise<string | null>;
6
31
  export declare function resolveHeaders(headers: HeadersInit | undefined, getHeaders?: HeaderResolver): Promise<Headers>;
7
32
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAE7C,MAAM,MAAM,iBAAiB,GAAG,MAAM,YAAY,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAE9E,MAAM,MAAM,cAAc,GAAG,MAAM,YAAY,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;AAuBzE,wBAAgB,YAAY,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC,GAAG,OAAO,CAMhF;AAED,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACpC,YAAY,CAAC,EAAE,iBAAiB,GAC/B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,WAAW,GAAG,SAAS,EAChC,UAAU,CAAC,EAAE,cAAc,GAC1B,OAAO,CAAC,OAAO,CAAC,CAGlB"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAE7C,MAAM,MAAM,iBAAiB,GAAG,MAAM,YAAY,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAE9E;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB,MAAM,GACN;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1B,iBAAiB,GACjB,CAAC,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,GACtE,SAAS,CAAC;AAEd,MAAM,MAAM,cAAc,GAAG,MAAM,YAAY,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;AA0CzE,wBAAgB,YAAY,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC,GAAG,OAAO,CAMhF;AAED,wBAAgB,iBAAiB,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIpG;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,GAAG,iBAAiB,CAY3E;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACpC,YAAY,CAAC,EAAE,iBAAiB,GAC/B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,WAAW,GAAG,SAAS,EAChC,UAAU,CAAC,EAAE,cAAc,GAC1B,OAAO,CAAC,OAAO,CAAC,CAGlB"}
package/dist/auth.js CHANGED
@@ -19,6 +19,26 @@ function appendHeaders(target, headers) {
19
19
  }
20
20
  }
21
21
  }
22
+ function appendHeaderRecord(target, headers) {
23
+ if (!headers)
24
+ return;
25
+ if (headers instanceof Headers) {
26
+ headers.forEach((value, key) => {
27
+ target[key.toLowerCase()] = value;
28
+ });
29
+ return;
30
+ }
31
+ if (Array.isArray(headers)) {
32
+ for (const [key, value] of headers) {
33
+ target[key.toLowerCase()] = value;
34
+ }
35
+ return;
36
+ }
37
+ for (const [key, value] of Object.entries(headers)) {
38
+ if (value !== undefined)
39
+ target[key.toLowerCase()] = value;
40
+ }
41
+ }
22
42
  export function mergeHeaders(...sources) {
23
43
  const headers = new Headers();
24
44
  for (const source of sources) {
@@ -26,6 +46,41 @@ export function mergeHeaders(...sources) {
26
46
  }
27
47
  return headers;
28
48
  }
49
+ export function mergeHeaderRecord(...sources) {
50
+ const headers = {};
51
+ for (const source of sources)
52
+ appendHeaderRecord(headers, source);
53
+ return headers;
54
+ }
55
+ /**
56
+ * Accepts any supported auth token form and returns a canonical async resolver.
57
+ *
58
+ * - `string` → resolver that always returns the string
59
+ * - `{ token: string }` → resolver that always returns `input.token`
60
+ * - sync function → resolver that awaits `input()` (errors propagate)
61
+ * - async function → passes through as-is
62
+ * - `undefined` → resolver that returns `undefined`
63
+ */
64
+ export function normalizeAuthToken(input) {
65
+ if (input === undefined) {
66
+ return async () => undefined;
67
+ }
68
+ if (typeof input === 'string') {
69
+ return async () => input;
70
+ }
71
+ if (typeof input === 'function') {
72
+ return async () => await input() ?? undefined;
73
+ }
74
+ // { token: string } wrapper object
75
+ return async () => input.token;
76
+ }
77
+ /**
78
+ * Resolve the token for an outbound transport request.
79
+ *
80
+ * This helper is intentionally transport-facing: it does not read process
81
+ * environment variables, config files, or global SDK state. Higher layers are
82
+ * responsible for choosing a token source and passing it here explicitly.
83
+ */
29
84
  export async function resolveAuthToken(authToken, getAuthToken) {
30
85
  if (getAuthToken) {
31
86
  const resolved = await getAuthToken();
package/dist/backoff.d.ts CHANGED
@@ -11,6 +11,13 @@ export interface ResolvedBackoffPolicy {
11
11
  readonly backoffFactor: number;
12
12
  }
13
13
  export declare function normalizeBackoffPolicy(policy: BackoffPolicy | undefined, defaults: ResolvedBackoffPolicy): ResolvedBackoffPolicy;
14
+ /**
15
+ * Return the retry delay for a one-based attempt number.
16
+ *
17
+ * Attempt `1` is the first retry and uses `baseDelayMs`; attempt `2` applies
18
+ * one backoff factor; attempt `0` or negative values return `0` so defensive
19
+ * callers do not schedule negative or NaN timers.
20
+ */
14
21
  export declare function computeBackoffDelay(attempt: number, policy: ResolvedBackoffPolicy): number;
15
22
  export declare function sleepWithSignal(delayMs: number, signal?: AbortSignal): Promise<void>;
16
23
  //# sourceMappingURL=backoff.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"backoff.d.ts","sourceRoot":"","sources":["../src/backoff.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,QAAQ,EAAE,qBAAqB,GAC9B,qBAAqB,CAOvB;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,qBAAqB,GAC5B,MAAM,CAKR;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAoBf"}
1
+ {"version":3,"file":"backoff.d.ts","sourceRoot":"","sources":["../src/backoff.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,QAAQ,EAAE,qBAAqB,GAC9B,qBAAqB,CAOvB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,qBAAqB,GAC5B,MAAM,CAKR;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAqBf"}
package/dist/backoff.js CHANGED
@@ -6,10 +6,17 @@ export function normalizeBackoffPolicy(policy, defaults) {
6
6
  backoffFactor: Math.max(1, policy?.backoffFactor ?? defaults.backoffFactor),
7
7
  };
8
8
  }
9
+ /**
10
+ * Return the retry delay for a one-based attempt number.
11
+ *
12
+ * Attempt `1` is the first retry and uses `baseDelayMs`; attempt `2` applies
13
+ * one backoff factor; attempt `0` or negative values return `0` so defensive
14
+ * callers do not schedule negative or NaN timers.
15
+ */
9
16
  export function computeBackoffDelay(attempt, policy) {
10
- if (attempt <= 1)
17
+ if (attempt <= 0)
11
18
  return 0;
12
- const exponent = Math.max(0, attempt - 2);
19
+ const exponent = Math.max(0, attempt - 1);
13
20
  const delay = policy.baseDelayMs * (policy.backoffFactor ** exponent);
14
21
  return Math.min(policy.maxDelayMs, Math.max(0, Math.floor(delay)));
15
22
  }
@@ -24,6 +31,7 @@ export async function sleepWithSignal(delayMs, signal) {
24
31
  cleanup();
25
32
  resolve();
26
33
  }, delayMs);
34
+ timer.unref?.();
27
35
  const onAbort = () => {
28
36
  clearTimeout(timer);
29
37
  cleanup();
@@ -0,0 +1,32 @@
1
+ export type RequiredKeys<T extends object> = {
2
+ [K in keyof T]-?: {} extends Pick<T, K> ? never : K;
3
+ }[keyof T];
4
+ /**
5
+ * Maps a contract input object to the public client method argument tuple.
6
+ * Required input fields make the first argument required; fully optional input
7
+ * shapes keep it optional; `undefined` inputs expose only the options argument.
8
+ */
9
+ export type MethodArgs<TInput, TOptions> = [
10
+ TInput
11
+ ] extends [undefined] ? [input?: undefined, options?: TOptions] : TInput extends object ? [RequiredKeys<TInput>] extends [never] ? [input?: TInput, options?: TOptions] : [input: TInput, options?: TOptions] : [input: TInput, options?: TOptions];
12
+ /** Remove path-bound keys from a contract input before exposing method helpers. */
13
+ export type WithoutKeys<TInput, TKeys extends PropertyKey> = [
14
+ TInput
15
+ ] extends [undefined] ? undefined : TInput extends object ? Omit<TInput, Extract<keyof TInput, TKeys>> : TInput;
16
+ /**
17
+ * Splits a generated client helper's rest tuple into input and options.
18
+ * The tuple type already proves the argument shape; runtime work is only the
19
+ * array-position split used by generated path helpers.
20
+ */
21
+ export declare function splitClientArgs<TInput, TOptions>(args: readonly unknown[]): readonly [TInput | undefined, TOptions | undefined];
22
+ /** Convert a typed client input object into the record shape required by contract route helpers. */
23
+ export declare function clientInputRecord<TInput>(input: TInput | undefined): Record<string, unknown> | undefined;
24
+ /** Merge fixed path fields with the optional typed client input record. */
25
+ export declare function mergeClientInput<TInput>(fixed: Record<string, unknown>, input: TInput | undefined): Record<string, unknown>;
26
+ export interface JsonSchemaValidationFailure {
27
+ readonly path: string;
28
+ readonly expected: string;
29
+ readonly received: string;
30
+ }
31
+ export declare function firstJsonSchemaFailure(schema: Record<string, unknown>, value: unknown, path?: string, root?: Record<string, unknown>): JsonSchemaValidationFailure | undefined;
32
+ //# sourceMappingURL=client-plumbing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-plumbing.d.ts","sourceRoot":"","sources":["../src/client-plumbing.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,MAAM,IAAI;KAC1C,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC;CACpD,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX;;;;GAIG;AACH,MAAM,MAAM,UAAU,CAAC,MAAM,EAAE,QAAQ,IACrC;IAAC,MAAM;CAAC,SAAS,CAAC,SAAS,CAAC,GACxB,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,GACvC,MAAM,SAAS,MAAM,GACnB,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACpC,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,GACpC,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,GACrC,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAE5C,mFAAmF;AACnF,MAAM,MAAM,WAAW,CAAC,MAAM,EAAE,KAAK,SAAS,WAAW,IACvD;IAAC,MAAM;CAAC,SAAS,CAAC,SAAS,CAAC,GACxB,SAAS,GACT,MAAM,SAAS,MAAM,GACnB,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,MAAM,EAAE,KAAK,CAAC,CAAC,GAC1C,MAAM,CAAC;AAEf;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,EAC9C,IAAI,EAAE,SAAS,OAAO,EAAE,GACvB,SAAS,CAAC,MAAM,GAAG,SAAS,EAAE,QAAQ,GAAG,SAAS,CAAC,CAKrD;AAED,oGAAoG;AACpG,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAIxG;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAAC,MAAM,EACrC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzB;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,KAAK,EAAE,OAAO,EACd,IAAI,SAAM,EACV,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAU,GACrC,2BAA2B,GAAG,SAAS,CA2FzC"}
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Splits a generated client helper's rest tuple into input and options.
3
+ * The tuple type already proves the argument shape; runtime work is only the
4
+ * array-position split used by generated path helpers.
5
+ */
6
+ export function splitClientArgs(args) {
7
+ if (args.length > 2) {
8
+ throw new ContractError(`Contract client helper expected at most 2 arguments but received ${args.length}.`);
9
+ }
10
+ return [args[0], args[1]];
11
+ }
12
+ /** Convert a typed client input object into the record shape required by contract route helpers. */
13
+ export function clientInputRecord(input) {
14
+ return input && typeof input === 'object' && !Array.isArray(input)
15
+ ? input
16
+ : undefined;
17
+ }
18
+ /** Merge fixed path fields with the optional typed client input record. */
19
+ export function mergeClientInput(fixed, input) {
20
+ return {
21
+ ...fixed,
22
+ ...(clientInputRecord(input) ?? {}),
23
+ };
24
+ }
25
+ export function firstJsonSchemaFailure(schema, value, path = '$', root = schema) {
26
+ if (typeof schema.$ref === 'string') {
27
+ const resolved = resolveLocalSchemaRef(root, schema.$ref);
28
+ return resolved ? firstJsonSchemaFailure(resolved, value, path, root) : undefined;
29
+ }
30
+ const allOf = readSchemaList(schema.allOf);
31
+ for (const child of allOf) {
32
+ const failure = firstJsonSchemaFailure(child, value, path, root);
33
+ if (failure)
34
+ return failure;
35
+ }
36
+ const anyOf = readSchemaList(schema.anyOf);
37
+ if (anyOf.length > 0) {
38
+ const failures = anyOf.map((child) => firstJsonSchemaFailure(child, value, path, root));
39
+ if (failures.every(Boolean))
40
+ return bestSchemaFailure(failures) ?? { path, expected: 'one matching schema', received: typeOfJsonValue(value) };
41
+ }
42
+ const oneOf = readSchemaList(schema.oneOf);
43
+ if (oneOf.length > 0) {
44
+ const matches = oneOf.filter((child) => !firstJsonSchemaFailure(child, value, path, root)).length;
45
+ if (matches !== 1)
46
+ return { path, expected: 'exactly one matching schema', received: `${matches} matches` };
47
+ }
48
+ const enumValues = schema.enum;
49
+ if (Array.isArray(enumValues) && !enumValues.some((candidate) => Object.is(candidate, value))) {
50
+ return { path, expected: `one of ${enumValues.map(String).join(', ')}`, received: typeOfJsonValue(value) };
51
+ }
52
+ if ('const' in schema && !Object.is(schema.const, value)) {
53
+ return { path, expected: JSON.stringify(schema.const), received: typeOfJsonValue(value) };
54
+ }
55
+ const types = readSchemaTypes(schema.type);
56
+ if (types.length > 0 && !types.some((type) => valueMatchesJsonType(value, type))) {
57
+ return { path, expected: types.join(' | '), received: typeOfJsonValue(value) };
58
+ }
59
+ const minimum = typeof schema.minimum === 'number' ? schema.minimum : undefined;
60
+ if (typeof value === 'number' && minimum !== undefined && value < minimum) {
61
+ return { path, expected: `>= ${minimum}`, received: String(value) };
62
+ }
63
+ const maximum = typeof schema.maximum === 'number' ? schema.maximum : undefined;
64
+ if (typeof value === 'number' && maximum !== undefined && value > maximum) {
65
+ return { path, expected: `<= ${maximum}`, received: String(value) };
66
+ }
67
+ const minLength = typeof schema.minLength === 'number' ? schema.minLength : undefined;
68
+ if (typeof value === 'string' && minLength !== undefined && value.length < minLength) {
69
+ return { path, expected: `length >= ${minLength}`, received: `length ${value.length}` };
70
+ }
71
+ const maxLength = typeof schema.maxLength === 'number' ? schema.maxLength : undefined;
72
+ if (typeof value === 'string' && maxLength !== undefined && value.length > maxLength) {
73
+ return { path, expected: `length <= ${maxLength}`, received: `length ${value.length}` };
74
+ }
75
+ if (typeof value === 'string' && typeof schema.pattern === 'string') {
76
+ const pattern = new RegExp(schema.pattern);
77
+ if (!pattern.test(value))
78
+ return { path, expected: `pattern ${schema.pattern}`, received: 'non-matching string' };
79
+ }
80
+ if (typeof value === 'string' && typeof schema.format === 'string' && !stringMatchesJsonSchemaFormat(value, schema.format)) {
81
+ return { path, expected: `format ${schema.format}`, received: 'non-matching string' };
82
+ }
83
+ if (value === null || value === undefined)
84
+ return undefined;
85
+ if (Array.isArray(value)) {
86
+ const itemSchema = schema.items;
87
+ if (itemSchema && typeof itemSchema === 'object' && !Array.isArray(itemSchema)) {
88
+ for (let index = 0; index < value.length; index++) {
89
+ const failure = firstJsonSchemaFailure(itemSchema, value[index], `${path}[${index}]`, root);
90
+ if (failure)
91
+ return failure;
92
+ }
93
+ }
94
+ const minItems = typeof schema.minItems === 'number' ? schema.minItems : undefined;
95
+ if (minItems !== undefined && value.length < minItems)
96
+ return { path, expected: `items >= ${minItems}`, received: `${value.length} items` };
97
+ const maxItems = typeof schema.maxItems === 'number' ? schema.maxItems : undefined;
98
+ if (maxItems !== undefined && value.length > maxItems)
99
+ return { path, expected: `items <= ${maxItems}`, received: `${value.length} items` };
100
+ return undefined;
101
+ }
102
+ if (typeof value === 'object') {
103
+ const objectValue = value;
104
+ const required = Array.isArray(schema.required) ? schema.required.filter((entry) => typeof entry === 'string') : [];
105
+ for (const key of required) {
106
+ if (!(key in objectValue))
107
+ return { path: `${path}.${key}`, expected: 'required field', received: 'missing' };
108
+ }
109
+ const properties = schema.properties;
110
+ if (properties && typeof properties === 'object' && !Array.isArray(properties)) {
111
+ for (const [key, propertySchema] of Object.entries(properties)) {
112
+ if (!(key in objectValue))
113
+ continue;
114
+ if (!propertySchema || typeof propertySchema !== 'object' || Array.isArray(propertySchema))
115
+ continue;
116
+ const failure = firstJsonSchemaFailure(propertySchema, objectValue[key], `${path}.${key}`, root);
117
+ if (failure)
118
+ return failure;
119
+ }
120
+ }
121
+ if (schema.additionalProperties === false && properties && typeof properties === 'object' && !Array.isArray(properties)) {
122
+ const allowed = new Set(Object.keys(properties));
123
+ const extra = Object.keys(objectValue).find((key) => !allowed.has(key));
124
+ if (extra)
125
+ return { path: `${path}.${extra}`, expected: 'no additional property', received: 'present' };
126
+ }
127
+ }
128
+ return undefined;
129
+ }
130
+ function resolveLocalSchemaRef(root, ref) {
131
+ if (!ref.startsWith('#/'))
132
+ return undefined;
133
+ let current = root;
134
+ for (const token of ref.slice(2).split('/')) {
135
+ if (!current || typeof current !== 'object' || Array.isArray(current))
136
+ return undefined;
137
+ const key = token.replace(/~1/g, '/').replace(/~0/g, '~');
138
+ current = current[key];
139
+ }
140
+ return current && typeof current === 'object' && !Array.isArray(current)
141
+ ? current
142
+ : undefined;
143
+ }
144
+ function bestSchemaFailure(failures) {
145
+ return failures
146
+ .filter((failure) => Boolean(failure))
147
+ .sort((left, right) => right.path.length - left.path.length)[0];
148
+ }
149
+ function readSchemaList(value) {
150
+ if (!Array.isArray(value))
151
+ return [];
152
+ return value.filter((entry) => Boolean(entry && typeof entry === 'object' && !Array.isArray(entry)));
153
+ }
154
+ function readSchemaTypes(type) {
155
+ if (typeof type === 'string')
156
+ return [type];
157
+ if (Array.isArray(type))
158
+ return type.filter((entry) => typeof entry === 'string');
159
+ return [];
160
+ }
161
+ function valueMatchesJsonType(value, type) {
162
+ switch (type) {
163
+ case 'null': return value === null;
164
+ case 'array': return Array.isArray(value);
165
+ case 'object': return typeof value === 'object' && value !== null && !Array.isArray(value);
166
+ case 'integer': return Number.isInteger(value);
167
+ case 'number': return typeof value === 'number';
168
+ case 'string': return typeof value === 'string';
169
+ case 'boolean': return typeof value === 'boolean';
170
+ default: return true;
171
+ }
172
+ }
173
+ function stringMatchesJsonSchemaFormat(value, format) {
174
+ switch (format) {
175
+ case 'date-time':
176
+ return !Number.isNaN(Date.parse(value)) && /\dT\d/.test(value);
177
+ case 'date':
178
+ return /^\d{4}-\d{2}-\d{2}$/.test(value) && !Number.isNaN(Date.parse(`${value}T00:00:00.000Z`));
179
+ case 'time':
180
+ return /^(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)?$/.test(value);
181
+ case 'duration':
182
+ return /^P(?!$)(?:\d+Y)?(?:\d+M)?(?:\d+W)?(?:\d+D)?(?:T(?:\d+H)?(?:\d+M)?(?:\d+(?:\.\d+)?S)?)?$/.test(value);
183
+ case 'email':
184
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
185
+ case 'hostname':
186
+ return isValidHostname(value);
187
+ case 'ipv4':
188
+ return isValidIpv4(value);
189
+ case 'ipv6':
190
+ return isValidIpv6(value);
191
+ case 'uri':
192
+ case 'url':
193
+ return isValidUrl(value);
194
+ case 'uuid':
195
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
196
+ default:
197
+ return true;
198
+ }
199
+ }
200
+ function isValidUrl(value) {
201
+ try {
202
+ new URL(value);
203
+ return true;
204
+ }
205
+ catch {
206
+ return false;
207
+ }
208
+ }
209
+ function isValidHostname(value) {
210
+ if (value.length === 0 || value.length > 253)
211
+ return false;
212
+ return value.split('.').every((label) => (label.length > 0
213
+ && label.length <= 63
214
+ && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(label)));
215
+ }
216
+ function isValidIpv4(value) {
217
+ const parts = value.split('.');
218
+ return parts.length === 4 && parts.every((part) => {
219
+ if (!/^\d{1,3}$/.test(part))
220
+ return false;
221
+ if (part.length > 1 && part.startsWith('0'))
222
+ return false;
223
+ const value = Number(part);
224
+ return value >= 0 && value <= 255;
225
+ });
226
+ }
227
+ function isValidIpv6(value) {
228
+ try {
229
+ new URL(`http://[${value}]`);
230
+ return true;
231
+ }
232
+ catch {
233
+ return false;
234
+ }
235
+ }
236
+ function typeOfJsonValue(value) {
237
+ if (value === null)
238
+ return 'null';
239
+ if (Array.isArray(value))
240
+ return 'array';
241
+ return typeof value;
242
+ }
243
+ import { ContractError } from '@pellux/goodvibes-errors';
@@ -1,3 +1,4 @@
1
+ import type { ZodType } from 'zod/v4';
1
2
  import type { HttpTransport } from './http.js';
2
3
  import { type ServerSentEventHandlers } from './sse-stream.js';
3
4
  export interface ContractRouteDefinition {
@@ -6,16 +7,25 @@ export interface ContractRouteDefinition {
6
7
  }
7
8
  export interface ContractRouteLike {
8
9
  readonly id: string;
10
+ /** When true, this route is safe to retry on 5xx even for mutating HTTP verbs. */
11
+ readonly idempotent?: boolean;
9
12
  }
10
13
  export interface ContractInvokeOptions {
11
14
  readonly signal?: AbortSignal;
12
15
  readonly headers?: HeadersInit;
16
+ /**
17
+ * Optional Zod v4 schema to validate the parsed response body against.
18
+ * When provided, a failed parse throws a {@link ContractError} with
19
+ * field-level detail: operation, field path, expected type, and a
20
+ * recovery hint.
21
+ */
22
+ readonly responseSchema?: ZodType;
13
23
  }
14
24
  export interface ContractStreamOptions extends ContractInvokeOptions {
15
25
  readonly handlers: ServerSentEventHandlers;
16
26
  }
17
27
  export declare function buildContractInput(primaryKey: string, primaryValue: string, input?: Record<string, unknown>): Record<string, unknown>;
18
28
  export declare function requireContractRoute<TRoute extends ContractRouteLike>(routes: readonly TRoute[], routeId: string, kind: string): TRoute;
19
- export declare function invokeContractRoute<T = unknown>(transport: HttpTransport, route: ContractRouteDefinition, input?: Record<string, unknown>, options?: ContractInvokeOptions): Promise<T>;
29
+ export declare function invokeContractRoute<T = unknown>(transport: HttpTransport, route: ContractRouteDefinition & ContractRouteLike, input?: Record<string, unknown>, options?: ContractInvokeOptions): Promise<T>;
20
30
  export declare function openContractRouteStream(transport: HttpTransport, route: ContractRouteDefinition, input: Record<string, unknown> | undefined, options: ContractStreamOptions): Promise<() => void>;
21
31
  //# sourceMappingURL=contract-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"contract-client.d.ts","sourceRoot":"","sources":["../src/contract-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAA6B,KAAK,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1F,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC;CAChC;AAED,MAAM,WAAW,qBAAsB,SAAQ,qBAAqB;IAClE,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;CAC5C;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzB;AAED,wBAAgB,oBAAoB,CAAC,MAAM,SAAS,iBAAiB,EACnE,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACX,MAAM,CAMR;AAED,wBAAgB,mBAAmB,CAAC,CAAC,GAAG,OAAO,EAC7C,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,uBAAuB,EAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,CAAC,CAAC,CAQZ;AAED,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,uBAAuB,EAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC1C,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,MAAM,IAAI,CAAC,CAYrB"}
1
+ {"version":3,"file":"contract-client.d.ts","sourceRoot":"","sources":["../src/contract-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAgC,KAAK,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE7F,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,qBAAsB,SAAQ,qBAAqB;IAClE,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;CAC5C;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzB;AAED,wBAAgB,oBAAoB,CAAC,MAAM,SAAS,iBAAiB,EACnE,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACX,MAAM,CAMR;AAED,wBAAsB,mBAAmB,CAAC,CAAC,GAAG,OAAO,EACnD,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,uBAAuB,GAAG,iBAAiB,EAClD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,CAAC,CAAC,CAyBZ;AAED,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,uBAAuB,EAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC1C,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,MAAM,IAAI,CAAC,CAYrB"}
@@ -1,4 +1,5 @@
1
- import { openServerSentEventStream } from './sse-stream.js';
1
+ import { ContractError, GoodVibesSdkError } from '@pellux/goodvibes-errors';
2
+ import { openRawServerSentEventStream } from './sse-stream.js';
2
3
  export function buildContractInput(primaryKey, primaryValue, input) {
3
4
  return {
4
5
  [primaryKey]: primaryValue,
@@ -8,22 +9,36 @@ export function buildContractInput(primaryKey, primaryValue, input) {
8
9
  export function requireContractRoute(routes, routeId, kind) {
9
10
  const route = routes.find((candidate) => candidate.id === routeId);
10
11
  if (!route) {
11
- throw new Error(`Unknown ${kind} "${routeId}"`);
12
+ throw new GoodVibesSdkError(`Unknown ${kind} "${routeId}". Verify the method/route id is correct and that your contract manifest is up to date.`, { category: 'contract', source: 'contract', recoverable: false });
12
13
  }
13
14
  return route;
14
15
  }
15
- export function invokeContractRoute(transport, route, input, options = {}) {
16
+ export async function invokeContractRoute(transport, route, input, options = {}) {
16
17
  const resolved = transport.resolveContractRequest(route.method, route.path, input);
17
- return transport.requestJson(resolved.url, {
18
+ const body = await transport.requestJson(resolved.url, {
18
19
  method: resolved.method,
19
20
  body: resolved.body,
20
21
  headers: options.headers,
21
22
  signal: options.signal,
23
+ methodId: route.id,
24
+ idempotent: route.idempotent === true,
22
25
  });
26
+ if (options.responseSchema) {
27
+ const result = options.responseSchema.safeParse(body);
28
+ if (!result.success) {
29
+ const issue = result.error.issues[0];
30
+ const fieldPath = issue ? issue.path.join('.') || '(root)' : '(unknown)';
31
+ const expected = issue ? issue.expected ?? issue.code : 'unknown';
32
+ const received = issue ? issue.received ?? 'unknown' : 'unknown';
33
+ throw new ContractError(`Response validation failed for "${route.method} ${route.path}": field "${fieldPath}" expected ${expected} but received ${received}. Ensure the server is running the matching GoodVibes contract version.`, { source: 'contract' });
34
+ }
35
+ return result.data;
36
+ }
37
+ return body;
23
38
  }
24
39
  export async function openContractRouteStream(transport, route, input, options) {
25
40
  const resolved = transport.resolveContractRequest(route.method, route.path, input);
26
- return await openServerSentEventStream(transport.fetchImpl, resolved.url, options.handlers, {
41
+ return await openRawServerSentEventStream(transport.fetchImpl, resolved.url, options.handlers, {
27
42
  authToken: transport.authToken,
28
43
  headers: options.headers,
29
44
  signal: options.signal,
@@ -1,13 +1,22 @@
1
+ import { HttpStatusError } from '@pellux/goodvibes-errors';
1
2
  import { type AuthTokenResolver, type HeaderResolver } from './auth.js';
2
3
  import { type HttpRetryPolicy } from './retry.js';
3
4
  import { type TransportPaths } from './paths.js';
4
- export type { HttpRetryPolicy } from './retry.js';
5
+ import { type TransportMiddleware, type TransportObserver } from '@pellux/goodvibes-transport-core';
6
+ export type { HttpRetryPolicy, PerMethodRetryPolicy } from './retry.js';
7
+ export type { TransportContext, TransportMiddleware } from '@pellux/goodvibes-transport-core';
5
8
  export type JsonValue = string | number | boolean | null | {
6
9
  readonly [key: string]: JsonValue;
7
10
  } | readonly JsonValue[];
8
11
  export type JsonObject = {
9
12
  readonly [key: string]: JsonValue;
10
13
  };
14
+ /**
15
+ * Generate a UUID v4 idempotency key.
16
+ * Uses `crypto.randomUUID()` when available (Bun, browsers, Workers, Node 14.17+).
17
+ * Falls back to a manual RFC 4122 v4 implementation otherwise.
18
+ */
19
+ export declare function generateIdempotencyKey(): string;
11
20
  export interface HttpJsonTransportOptions {
12
21
  readonly baseUrl: string;
13
22
  readonly authToken?: string | null;
@@ -17,6 +26,9 @@ export interface HttpJsonTransportOptions {
17
26
  readonly headers?: HeadersInit;
18
27
  readonly getHeaders?: HeaderResolver;
19
28
  readonly retry?: HttpRetryPolicy;
29
+ readonly observer?: TransportObserver;
30
+ /** Middleware chain applied to every HTTP request/response cycle. */
31
+ readonly middleware?: readonly TransportMiddleware[];
20
32
  }
21
33
  export interface HttpJsonRequestOptions {
22
34
  readonly method?: string;
@@ -24,6 +36,19 @@ export interface HttpJsonRequestOptions {
24
36
  readonly headers?: HeadersInit;
25
37
  readonly signal?: AbortSignal;
26
38
  readonly retry?: false | HttpRetryPolicy;
39
+ /**
40
+ * Contract method / endpoint ID used to look up per-method retry policy overrides.
41
+ * Populated automatically by `invokeContractRoute`; callers outside the contract
42
+ * layer may also set this to opt into `perMethodPolicy` overrides.
43
+ */
44
+ readonly methodId?: string;
45
+ /**
46
+ * When `true`, this call is considered idempotent even if the HTTP verb is a
47
+ * mutating method (POST/PUT/PATCH/DELETE). Enables retry-on-5xx for the call.
48
+ * Populated automatically from `contract.idempotent`; takes lower precedence
49
+ * than an explicit `perMethodPolicy` override.
50
+ */
51
+ readonly idempotent?: boolean;
27
52
  }
28
53
  export interface ResolvedContractRequest {
29
54
  readonly url: string;
@@ -35,6 +60,8 @@ export interface TransportJsonError {
35
60
  readonly body: unknown;
36
61
  readonly url: string;
37
62
  readonly method: string;
63
+ readonly retryAfterMs?: number;
64
+ readonly cause?: unknown;
38
65
  }
39
66
  export interface HttpJsonTransport {
40
67
  readonly baseUrl: string;
@@ -45,11 +72,25 @@ export interface HttpJsonTransport {
45
72
  getAuthToken(): Promise<string | null>;
46
73
  requestJson<T>(pathOrUrl: string, options?: HttpJsonRequestOptions): Promise<T>;
47
74
  resolveContractRequest(method: string, path: string, input?: Record<string, unknown>): ResolvedContractRequest;
75
+ /** Append a middleware to the transport's middleware chain. */
76
+ use(middleware: TransportMiddleware): void;
48
77
  }
78
+ export declare function inferTransportHint(status: number, url: string, retryAfterMs?: number): string | undefined;
79
+ export declare function createTransportError(status: number, url: string, method: string, body: unknown, retryAfterMs?: number): HttpStatusError & {
80
+ readonly transport: TransportJsonError;
81
+ };
82
+ export declare function createNetworkTransportError(error: unknown, url: string, method: string): HttpStatusError & {
83
+ readonly transport: TransportJsonError;
84
+ };
49
85
  export declare function createJsonRequestInit(token: string | null | undefined, body?: unknown, method?: string, headers?: HeadersInit, signal?: AbortSignal, defaultHeaders?: HeadersInit): RequestInit;
50
86
  export declare const createJsonInit: typeof createJsonRequestInit;
51
87
  export declare function createFetch(fetchImpl?: typeof fetch, fallbackFetch?: typeof fetch): typeof fetch;
52
88
  export declare function readJsonBody(response: Response): Promise<unknown>;
89
+ /**
90
+ * @internal Low-level one-shot helper retained for legacy internal callers.
91
+ * Public code should use `createHttpTransport(...).requestJson()` so auth,
92
+ * middleware, retry policy, idempotency keys, and observers are applied.
93
+ */
53
94
  export declare function requestJson<T>(fetchImpl: typeof fetch, url: string, init?: RequestInit): Promise<T>;
54
95
  export declare function createHttpJsonTransport(options: HttpJsonTransportOptions): HttpJsonTransport;
55
96
  //# sourceMappingURL=http-core.d.ts.map