@pellux/goodvibes-transport-http 0.30.3 → 0.33.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 +5 -5
- package/dist/auth.d.ts +16 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +17 -1
- package/dist/backoff.d.ts +4 -4
- package/dist/backoff.d.ts.map +1 -1
- package/dist/client-plumbing.d.ts +1 -1
- package/dist/client-plumbing.d.ts.map +1 -1
- package/dist/client-plumbing.js +48 -11
- package/dist/contract-client.d.ts +4 -5
- package/dist/contract-client.d.ts.map +1 -1
- package/dist/contract-client.js +0 -6
- package/dist/http-core.d.ts +46 -27
- package/dist/http-core.d.ts.map +1 -1
- package/dist/http-core.js +83 -37
- package/dist/http.d.ts +2 -2
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/paths.d.ts +14 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +82 -1
- package/dist/reconnect.d.ts +1 -1
- package/dist/reconnect.d.ts.map +1 -1
- package/dist/retry.d.ts +8 -8
- package/dist/retry.d.ts.map +1 -1
- package/dist/sse-stream.d.ts +10 -10
- package/dist/sse-stream.d.ts.map +1 -1
- package/dist/sse-stream.js +9 -6
- package/dist/sse.d.ts.map +1 -1
- package/dist/sse.js +2 -1
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @pellux/goodvibes-transport-http
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Public GoodVibes HTTP transport package for JSON requests, auth headers, retry/backoff, SSE streams, and contract route invocation.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Most applications should install `@pellux/goodvibes-sdk` and import `@pellux/goodvibes-sdk/transport-http`. Install this package directly when you only need the HTTP transport subset.
|
|
6
6
|
|
|
7
7
|
Exports include:
|
|
8
8
|
- contract route invocation helpers
|
|
@@ -15,11 +15,11 @@ Exports include:
|
|
|
15
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
16
|
|
|
17
17
|
```ts
|
|
18
|
-
import { createJsonRequestInit,
|
|
18
|
+
import { createJsonRequestInit, requestJsonRaw } from '@pellux/goodvibes-sdk/transport-http';
|
|
19
19
|
|
|
20
|
-
const body = await
|
|
20
|
+
const body = await requestJsonRaw(
|
|
21
21
|
fetch,
|
|
22
|
-
'http://127.0.0.1:
|
|
22
|
+
'http://127.0.0.1:3421/api/control-plane/auth',
|
|
23
23
|
createJsonRequestInit(process.env.GOODVIBES_TOKEN ?? null),
|
|
24
24
|
);
|
|
25
25
|
```
|
package/dist/auth.d.ts
CHANGED
|
@@ -8,7 +8,23 @@ export type AuthTokenInput = string | {
|
|
|
8
8
|
readonly token: string;
|
|
9
9
|
} | AuthTokenResolver | (() => string | null | undefined | Promise<string | null | undefined>) | undefined;
|
|
10
10
|
export type HeaderResolver = () => MaybePromise<HeadersInit | undefined>;
|
|
11
|
+
/**
|
|
12
|
+
* Merge header inputs into a `Headers` instance.
|
|
13
|
+
*
|
|
14
|
+
* Use this when a streaming/browser API expects mutable `Headers`, such as
|
|
15
|
+
* EventSource-style setup or abort-aware fetch helpers. For ordinary JSON HTTP
|
|
16
|
+
* requests, prefer `mergeHeaderRecord` so callers receive a plain object that is
|
|
17
|
+
* cheap to serialize, inspect, and pass through middleware.
|
|
18
|
+
*/
|
|
11
19
|
export declare function mergeHeaders(...sources: Array<HeadersInit | undefined>): Headers;
|
|
20
|
+
/**
|
|
21
|
+
* Merge header inputs into a lower-case plain record.
|
|
22
|
+
*
|
|
23
|
+
* Preferred for normal HTTP transport requests because the result is a stable
|
|
24
|
+
* `Record<string, string>` for middleware, trace injection, and JSON request
|
|
25
|
+
* construction. Use `mergeHeaders` only when a consumer specifically needs a
|
|
26
|
+
* platform `Headers` object.
|
|
27
|
+
*/
|
|
12
28
|
export declare function mergeHeaderRecord(...sources: Array<HeadersInit | undefined>): Record<string, string>;
|
|
13
29
|
/**
|
|
14
30
|
* Accepts any supported auth token form and returns a canonical async resolver.
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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"}
|
|
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;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC,GAAG,OAAO,CAMhF;AAED;;;;;;;GAOG;AACH,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
|
@@ -39,6 +39,14 @@ function appendHeaderRecord(target, headers) {
|
|
|
39
39
|
target[key.toLowerCase()] = value;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Merge header inputs into a `Headers` instance.
|
|
44
|
+
*
|
|
45
|
+
* Use this when a streaming/browser API expects mutable `Headers`, such as
|
|
46
|
+
* EventSource-style setup or abort-aware fetch helpers. For ordinary JSON HTTP
|
|
47
|
+
* requests, prefer `mergeHeaderRecord` so callers receive a plain object that is
|
|
48
|
+
* cheap to serialize, inspect, and pass through middleware.
|
|
49
|
+
*/
|
|
42
50
|
export function mergeHeaders(...sources) {
|
|
43
51
|
const headers = new Headers();
|
|
44
52
|
for (const source of sources) {
|
|
@@ -46,6 +54,14 @@ export function mergeHeaders(...sources) {
|
|
|
46
54
|
}
|
|
47
55
|
return headers;
|
|
48
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Merge header inputs into a lower-case plain record.
|
|
59
|
+
*
|
|
60
|
+
* Preferred for normal HTTP transport requests because the result is a stable
|
|
61
|
+
* `Record<string, string>` for middleware, trace injection, and JSON request
|
|
62
|
+
* construction. Use `mergeHeaders` only when a consumer specifically needs a
|
|
63
|
+
* platform `Headers` object.
|
|
64
|
+
*/
|
|
49
65
|
export function mergeHeaderRecord(...sources) {
|
|
50
66
|
const headers = {};
|
|
51
67
|
for (const source of sources)
|
|
@@ -69,7 +85,7 @@ export function normalizeAuthToken(input) {
|
|
|
69
85
|
return async () => input;
|
|
70
86
|
}
|
|
71
87
|
if (typeof input === 'function') {
|
|
72
|
-
return async () => await input() ?? undefined;
|
|
88
|
+
return async () => (await input()) ?? undefined;
|
|
73
89
|
}
|
|
74
90
|
// { token: string } wrapper object
|
|
75
91
|
return async () => input.token;
|
package/dist/backoff.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export interface BackoffPolicy {
|
|
2
|
-
readonly maxAttempts?: number;
|
|
3
|
-
readonly baseDelayMs?: number;
|
|
4
|
-
readonly maxDelayMs?: number;
|
|
5
|
-
readonly backoffFactor?: number;
|
|
2
|
+
readonly maxAttempts?: number | undefined;
|
|
3
|
+
readonly baseDelayMs?: number | undefined;
|
|
4
|
+
readonly maxDelayMs?: number | undefined;
|
|
5
|
+
readonly backoffFactor?: number | undefined;
|
|
6
6
|
}
|
|
7
7
|
export interface ResolvedBackoffPolicy {
|
|
8
8
|
readonly maxAttempts: number;
|
package/dist/backoff.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
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,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7C;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"}
|
|
@@ -28,5 +28,5 @@ export interface JsonSchemaValidationFailure {
|
|
|
28
28
|
readonly expected: string;
|
|
29
29
|
readonly received: string;
|
|
30
30
|
}
|
|
31
|
-
export declare function firstJsonSchemaFailure(schema: Record<string, unknown>, value: unknown, path?: string, root?: Record<string, unknown
|
|
31
|
+
export declare function firstJsonSchemaFailure(schema: Record<string, unknown>, value: unknown, path?: string, root?: Record<string, unknown>, _depth?: number): JsonSchemaValidationFailure | undefined;
|
|
32
32
|
//# sourceMappingURL=client-plumbing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-plumbing.d.ts","sourceRoot":"","sources":["../src/client-plumbing.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client-plumbing.d.ts","sourceRoot":"","sources":["../src/client-plumbing.ts"],"names":[],"mappings":"AAYA,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,CAMrD;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;AAID,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,EACtC,MAAM,SAAI,GACT,2BAA2B,GAAG,SAAS,CA+FzC"}
|
package/dist/client-plumbing.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import { ContractError } from '@pellux/goodvibes-errors';
|
|
2
|
+
const MAX_SCHEMA_PATTERN_CHARS = 512;
|
|
3
|
+
const MAX_SCHEMA_PATTERN_INPUT_CHARS = 50_000;
|
|
4
|
+
const RISKY_SCHEMA_PATTERN_CHECKS = [
|
|
5
|
+
/(^|[^\\])\\[1-9]/,
|
|
6
|
+
/\((?:[^()\\]|\\.)*[+*{][^)]*\)\s*[+*{]/,
|
|
7
|
+
/\.\*(?:[^|)]{0,64})\.\*/,
|
|
8
|
+
// Lookbehind assertions (?<=...) can also produce pathological backtracking.
|
|
9
|
+
/\(\?<[=!]/,
|
|
10
|
+
];
|
|
1
11
|
/**
|
|
2
12
|
* Splits a generated client helper's rest tuple into input and options.
|
|
3
13
|
* The tuple type already proves the argument shape; runtime work is only the
|
|
@@ -7,6 +17,7 @@ export function splitClientArgs(args) {
|
|
|
7
17
|
if (args.length > 2) {
|
|
8
18
|
throw new ContractError(`Contract client helper expected at most 2 arguments but received ${args.length}.`);
|
|
9
19
|
}
|
|
20
|
+
// drop misleading non-null assertions — args[0]/args[1] may be undefined.
|
|
10
21
|
return [args[0], args[1]];
|
|
11
22
|
}
|
|
12
23
|
/** Convert a typed client input object into the record shape required by contract route helpers. */
|
|
@@ -22,26 +33,30 @@ export function mergeClientInput(fixed, input) {
|
|
|
22
33
|
...(clientInputRecord(input) ?? {}),
|
|
23
34
|
};
|
|
24
35
|
}
|
|
25
|
-
|
|
36
|
+
const MAX_SCHEMA_WALK_DEPTH = 32;
|
|
37
|
+
export function firstJsonSchemaFailure(schema, value, path = '$', root = schema, _depth = 0) {
|
|
38
|
+
// Guard against cyclic $ref chains.
|
|
39
|
+
if (_depth >= MAX_SCHEMA_WALK_DEPTH)
|
|
40
|
+
return undefined;
|
|
26
41
|
if (typeof schema.$ref === 'string') {
|
|
27
42
|
const resolved = resolveLocalSchemaRef(root, schema.$ref);
|
|
28
|
-
return resolved ? firstJsonSchemaFailure(resolved, value, path, root) : undefined;
|
|
43
|
+
return resolved ? firstJsonSchemaFailure(resolved, value, path, root, _depth + 1) : undefined;
|
|
29
44
|
}
|
|
30
45
|
const allOf = readSchemaList(schema.allOf);
|
|
31
46
|
for (const child of allOf) {
|
|
32
|
-
const failure = firstJsonSchemaFailure(child, value, path, root);
|
|
47
|
+
const failure = firstJsonSchemaFailure(child, value, path, root, _depth + 1);
|
|
33
48
|
if (failure)
|
|
34
49
|
return failure;
|
|
35
50
|
}
|
|
36
51
|
const anyOf = readSchemaList(schema.anyOf);
|
|
37
52
|
if (anyOf.length > 0) {
|
|
38
|
-
const failures = anyOf.map((child) => firstJsonSchemaFailure(child, value, path, root));
|
|
53
|
+
const failures = anyOf.map((child) => firstJsonSchemaFailure(child, value, path, root, _depth + 1));
|
|
39
54
|
if (failures.every(Boolean))
|
|
40
55
|
return bestSchemaFailure(failures) ?? { path, expected: 'one matching schema', received: typeOfJsonValue(value) };
|
|
41
56
|
}
|
|
42
57
|
const oneOf = readSchemaList(schema.oneOf);
|
|
43
58
|
if (oneOf.length > 0) {
|
|
44
|
-
const matches = oneOf.filter((child) => !firstJsonSchemaFailure(child, value, path, root)).length;
|
|
59
|
+
const matches = oneOf.filter((child) => !firstJsonSchemaFailure(child, value, path, root, _depth + 1)).length;
|
|
45
60
|
if (matches !== 1)
|
|
46
61
|
return { path, expected: 'exactly one matching schema', received: `${matches} matches` };
|
|
47
62
|
}
|
|
@@ -73,8 +88,8 @@ export function firstJsonSchemaFailure(schema, value, path = '$', root = schema)
|
|
|
73
88
|
return { path, expected: `length <= ${maxLength}`, received: `length ${value.length}` };
|
|
74
89
|
}
|
|
75
90
|
if (typeof value === 'string' && typeof schema.pattern === 'string') {
|
|
76
|
-
const pattern =
|
|
77
|
-
if (!pattern
|
|
91
|
+
const pattern = compileContractPattern(schema.pattern);
|
|
92
|
+
if (!contractPatternMatches(pattern, value))
|
|
78
93
|
return { path, expected: `pattern ${schema.pattern}`, received: 'non-matching string' };
|
|
79
94
|
}
|
|
80
95
|
if (typeof value === 'string' && typeof schema.format === 'string' && !stringMatchesJsonSchemaFormat(value, schema.format)) {
|
|
@@ -86,7 +101,8 @@ export function firstJsonSchemaFailure(schema, value, path = '$', root = schema)
|
|
|
86
101
|
const itemSchema = schema.items;
|
|
87
102
|
if (itemSchema && typeof itemSchema === 'object' && !Array.isArray(itemSchema)) {
|
|
88
103
|
for (let index = 0; index < value.length; index++) {
|
|
89
|
-
|
|
104
|
+
// pass _depth + 1 so depth-reset through array items is prevented.
|
|
105
|
+
const failure = firstJsonSchemaFailure(itemSchema, value[index], `${path}[${index}]`, root, _depth + 1);
|
|
90
106
|
if (failure)
|
|
91
107
|
return failure;
|
|
92
108
|
}
|
|
@@ -113,7 +129,8 @@ export function firstJsonSchemaFailure(schema, value, path = '$', root = schema)
|
|
|
113
129
|
continue;
|
|
114
130
|
if (!propertySchema || typeof propertySchema !== 'object' || Array.isArray(propertySchema))
|
|
115
131
|
continue;
|
|
116
|
-
|
|
132
|
+
// pass _depth + 1 so nested property recursion respects the walk depth limit.
|
|
133
|
+
const failure = firstJsonSchemaFailure(propertySchema, objectValue[key], `${path}.${key}`, root, _depth + 1);
|
|
117
134
|
if (failure)
|
|
118
135
|
return failure;
|
|
119
136
|
}
|
|
@@ -158,6 +175,24 @@ function readSchemaTypes(type) {
|
|
|
158
175
|
return type.filter((entry) => typeof entry === 'string');
|
|
159
176
|
return [];
|
|
160
177
|
}
|
|
178
|
+
function compileContractPattern(source) {
|
|
179
|
+
if (source.length > MAX_SCHEMA_PATTERN_CHARS) {
|
|
180
|
+
throw new ContractError(`Contract schema pattern exceeds ${MAX_SCHEMA_PATTERN_CHARS} characters.`);
|
|
181
|
+
}
|
|
182
|
+
for (const pattern of RISKY_SCHEMA_PATTERN_CHECKS) {
|
|
183
|
+
if (pattern.test(source)) {
|
|
184
|
+
throw new ContractError('Contract schema pattern is too expensive to evaluate safely.');
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return new RegExp(source);
|
|
188
|
+
}
|
|
189
|
+
function contractPatternMatches(pattern, value) {
|
|
190
|
+
if (value.length > MAX_SCHEMA_PATTERN_INPUT_CHARS) {
|
|
191
|
+
throw new ContractError(`Contract schema pattern input exceeds ${MAX_SCHEMA_PATTERN_INPUT_CHARS} characters.`);
|
|
192
|
+
}
|
|
193
|
+
pattern.lastIndex = 0;
|
|
194
|
+
return pattern.test(value);
|
|
195
|
+
}
|
|
161
196
|
function valueMatchesJsonType(value, type) {
|
|
162
197
|
switch (type) {
|
|
163
198
|
case 'null': return value === null;
|
|
@@ -173,7 +208,10 @@ function valueMatchesJsonType(value, type) {
|
|
|
173
208
|
function stringMatchesJsonSchemaFormat(value, format) {
|
|
174
209
|
switch (format) {
|
|
175
210
|
case 'date-time':
|
|
176
|
-
|
|
211
|
+
// JSON Schema date-time requires a full date/time separator; Date.parse
|
|
212
|
+
// alone accepts date-only strings in some runtimes.
|
|
213
|
+
return /^\d{4}-\d{2}-\d{2}T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(value)
|
|
214
|
+
&& !Number.isNaN(Date.parse(value));
|
|
177
215
|
case 'date':
|
|
178
216
|
return /^\d{4}-\d{2}-\d{2}$/.test(value) && !Number.isNaN(Date.parse(`${value}T00:00:00.000Z`));
|
|
179
217
|
case 'time':
|
|
@@ -240,4 +278,3 @@ function typeOfJsonValue(value) {
|
|
|
240
278
|
return 'array';
|
|
241
279
|
return typeof value;
|
|
242
280
|
}
|
|
243
|
-
import { ContractError } from '@pellux/goodvibes-errors';
|
|
@@ -8,23 +8,22 @@ export interface ContractRouteDefinition {
|
|
|
8
8
|
export interface ContractRouteLike {
|
|
9
9
|
readonly id: string;
|
|
10
10
|
/** When true, this route is safe to retry on 5xx even for mutating HTTP verbs. */
|
|
11
|
-
readonly idempotent?: boolean;
|
|
11
|
+
readonly idempotent?: boolean | undefined;
|
|
12
12
|
}
|
|
13
13
|
export interface ContractInvokeOptions {
|
|
14
|
-
readonly signal?: AbortSignal;
|
|
15
|
-
readonly headers?: HeadersInit;
|
|
14
|
+
readonly signal?: AbortSignal | undefined;
|
|
15
|
+
readonly headers?: HeadersInit | undefined;
|
|
16
16
|
/**
|
|
17
17
|
* Optional Zod v4 schema to validate the parsed response body against.
|
|
18
18
|
* When provided, a failed parse throws a {@link ContractError} with
|
|
19
19
|
* field-level detail: operation, field path, expected type, and a
|
|
20
20
|
* recovery hint.
|
|
21
21
|
*/
|
|
22
|
-
readonly responseSchema?: ZodType;
|
|
22
|
+
readonly responseSchema?: ZodType | undefined;
|
|
23
23
|
}
|
|
24
24
|
export interface ContractStreamOptions extends ContractInvokeOptions {
|
|
25
25
|
readonly handlers: ServerSentEventHandlers;
|
|
26
26
|
}
|
|
27
|
-
export declare function buildContractInput(primaryKey: string, primaryValue: string, input?: Record<string, unknown>): Record<string, unknown>;
|
|
28
27
|
export declare function requireContractRoute<TRoute extends ContractRouteLike>(routes: readonly TRoute[], routeId: string, kind: string): TRoute;
|
|
29
28
|
export declare function invokeContractRoute<T = unknown>(transport: HttpTransport, route: ContractRouteDefinition & ContractRouteLike, input?: Record<string, unknown>, options?: ContractInvokeOptions): Promise<T>;
|
|
30
29
|
export declare function openContractRouteStream(transport: HttpTransport, route: ContractRouteDefinition, input: Record<string, unknown> | undefined, options: ContractStreamOptions): Promise<() => void>;
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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,GAAG,SAAS,CAAC;CAC3C;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC/C;AAED,MAAM,WAAW,qBAAsB,SAAQ,qBAAqB;IAClE,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;CAC5C;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"}
|
package/dist/contract-client.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { ContractError, GoodVibesSdkError } from '@pellux/goodvibes-errors';
|
|
2
2
|
import { openRawServerSentEventStream } from './sse-stream.js';
|
|
3
|
-
export function buildContractInput(primaryKey, primaryValue, input) {
|
|
4
|
-
return {
|
|
5
|
-
[primaryKey]: primaryValue,
|
|
6
|
-
...(input ?? {}),
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
3
|
export function requireContractRoute(routes, routeId, kind) {
|
|
10
4
|
const route = routes.find((candidate) => candidate.id === routeId);
|
|
11
5
|
if (!route) {
|
package/dist/http-core.d.ts
CHANGED
|
@@ -5,9 +5,8 @@ import { type TransportPaths } from './paths.js';
|
|
|
5
5
|
import { type TransportMiddleware, type TransportObserver } from '@pellux/goodvibes-transport-core';
|
|
6
6
|
export type { HttpRetryPolicy, PerMethodRetryPolicy } from './retry.js';
|
|
7
7
|
export type { TransportContext, TransportMiddleware } from '@pellux/goodvibes-transport-core';
|
|
8
|
-
export type JsonValue
|
|
9
|
-
|
|
10
|
-
} | readonly JsonValue[];
|
|
8
|
+
export type { JsonValue } from '@pellux/goodvibes-contracts';
|
|
9
|
+
import type { JsonValue } from '@pellux/goodvibes-contracts';
|
|
11
10
|
export type JsonObject = {
|
|
12
11
|
readonly [key: string]: JsonValue;
|
|
13
12
|
};
|
|
@@ -19,53 +18,71 @@ export type JsonObject = {
|
|
|
19
18
|
export declare function generateIdempotencyKey(): string;
|
|
20
19
|
export interface HttpJsonTransportOptions {
|
|
21
20
|
readonly baseUrl: string;
|
|
22
|
-
readonly authToken?: string | null;
|
|
23
|
-
readonly getAuthToken?: AuthTokenResolver;
|
|
24
|
-
readonly fetch?: typeof fetch;
|
|
25
|
-
readonly fetchImpl?: typeof fetch;
|
|
26
|
-
readonly headers?: HeadersInit;
|
|
27
|
-
readonly getHeaders?: HeaderResolver;
|
|
28
|
-
readonly retry?: HttpRetryPolicy;
|
|
29
|
-
readonly observer?: TransportObserver;
|
|
21
|
+
readonly authToken?: string | null | undefined;
|
|
22
|
+
readonly getAuthToken?: AuthTokenResolver | undefined;
|
|
23
|
+
readonly fetch?: typeof fetch | undefined;
|
|
24
|
+
readonly fetchImpl?: typeof fetch | undefined;
|
|
25
|
+
readonly headers?: HeadersInit | undefined;
|
|
26
|
+
readonly getHeaders?: HeaderResolver | undefined;
|
|
27
|
+
readonly retry?: HttpRetryPolicy | undefined;
|
|
28
|
+
readonly observer?: TransportObserver | undefined;
|
|
30
29
|
/** Middleware chain applied to every HTTP request/response cycle. */
|
|
31
|
-
readonly middleware?: readonly TransportMiddleware[];
|
|
30
|
+
readonly middleware?: readonly TransportMiddleware[] | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Optional callback invoked when the retry loop decides to back off.
|
|
33
|
+
* Fires immediately before the sleep delay. Use this to emit TRANSPORT_RETRY_SCHEDULED.
|
|
34
|
+
*/
|
|
35
|
+
readonly onRetryScheduled?: ((info: {
|
|
36
|
+
attempt: number;
|
|
37
|
+
maxAttempts: number;
|
|
38
|
+
backoffMs: number;
|
|
39
|
+
reason: string;
|
|
40
|
+
}) => void) | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Optional callback invoked when the sleep delay completes and the next attempt is
|
|
43
|
+
* about to start. Fires immediately after the sleep. Use to emit TRANSPORT_RETRY_EXECUTED.
|
|
44
|
+
*/
|
|
45
|
+
readonly onRetryExecuted?: ((info: {
|
|
46
|
+
attempt: number;
|
|
47
|
+
maxAttempts: number;
|
|
48
|
+
}) => void) | undefined;
|
|
32
49
|
}
|
|
33
50
|
export interface HttpJsonRequestOptions {
|
|
34
|
-
readonly method?: string;
|
|
35
|
-
readonly body?: unknown;
|
|
36
|
-
readonly headers?: HeadersInit;
|
|
37
|
-
readonly signal?: AbortSignal;
|
|
38
|
-
readonly retry?: false | HttpRetryPolicy;
|
|
51
|
+
readonly method?: string | undefined;
|
|
52
|
+
readonly body?: unknown | undefined;
|
|
53
|
+
readonly headers?: HeadersInit | undefined;
|
|
54
|
+
readonly signal?: AbortSignal | undefined;
|
|
55
|
+
readonly retry?: false | HttpRetryPolicy | undefined;
|
|
39
56
|
/**
|
|
40
57
|
* Contract method / endpoint ID used to look up per-method retry policy overrides.
|
|
41
58
|
* Populated automatically by `invokeContractRoute`; callers outside the contract
|
|
42
59
|
* layer may also set this to opt into `perMethodPolicy` overrides.
|
|
43
60
|
*/
|
|
44
|
-
readonly methodId?: string;
|
|
61
|
+
readonly methodId?: string | undefined;
|
|
45
62
|
/**
|
|
46
63
|
* When `true`, this call is considered idempotent even if the HTTP verb is a
|
|
47
64
|
* mutating method (POST/PUT/PATCH/DELETE). Enables retry-on-5xx for the call.
|
|
48
65
|
* Populated automatically from `contract.idempotent`; takes lower precedence
|
|
49
66
|
* than an explicit `perMethodPolicy` override.
|
|
50
67
|
*/
|
|
51
|
-
readonly idempotent?: boolean;
|
|
68
|
+
readonly idempotent?: boolean | undefined;
|
|
52
69
|
}
|
|
53
70
|
export interface ResolvedContractRequest {
|
|
54
71
|
readonly url: string;
|
|
55
72
|
readonly method: string;
|
|
56
|
-
readonly body?: Record<string, unknown
|
|
73
|
+
readonly body?: Record<string, unknown> | undefined;
|
|
57
74
|
}
|
|
58
75
|
export interface TransportJsonError {
|
|
59
76
|
readonly status: number;
|
|
60
77
|
readonly body: unknown;
|
|
61
78
|
readonly url: string;
|
|
62
79
|
readonly method: string;
|
|
63
|
-
readonly retryAfterMs?: number;
|
|
64
|
-
readonly cause?: unknown;
|
|
80
|
+
readonly retryAfterMs?: number | undefined;
|
|
81
|
+
readonly cause?: unknown | undefined;
|
|
65
82
|
}
|
|
66
83
|
export interface HttpJsonTransport {
|
|
67
84
|
readonly baseUrl: string;
|
|
68
|
-
readonly authToken?: string | null;
|
|
85
|
+
readonly authToken?: string | null | undefined;
|
|
69
86
|
readonly fetchImpl: typeof fetch;
|
|
70
87
|
readonly paths: TransportPaths;
|
|
71
88
|
buildUrl(path: string): string;
|
|
@@ -87,10 +104,12 @@ export declare const createJsonInit: typeof createJsonRequestInit;
|
|
|
87
104
|
export declare function createFetch(fetchImpl?: typeof fetch, fallbackFetch?: typeof fetch): typeof fetch;
|
|
88
105
|
export declare function readJsonBody(response: Response): Promise<unknown>;
|
|
89
106
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
107
|
+
* Low-level one-shot JSON request helper.
|
|
108
|
+
*
|
|
109
|
+
* This deliberately bypasses transport auth, middleware, retry policy,
|
|
110
|
+
* idempotency keys, and observers. Prefer
|
|
111
|
+
* `createHttpTransport(...).requestJson()` for normal application code.
|
|
93
112
|
*/
|
|
94
|
-
export declare function
|
|
113
|
+
export declare function requestJsonRaw<T>(fetchImpl: typeof fetch, url: string, init?: RequestInit): Promise<T>;
|
|
95
114
|
export declare function createHttpJsonTransport(options: HttpJsonTransportOptions): HttpJsonTransport;
|
|
96
115
|
//# sourceMappingURL=http-core.d.ts.map
|
package/dist/http-core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-core.d.ts","sourceRoot":"","sources":["../src/http-core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwD,eAAe,EAAyB,MAAM,0BAA0B,CAAC;AAExI,OAAO,
|
|
1
|
+
{"version":3,"file":"http-core.d.ts","sourceRoot":"","sources":["../src/http-core.ts"],"names":[],"mappings":"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"}
|
package/dist/http-core.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ConfigurationError, ContractError, GoodVibesSdkError, HttpStatusError, createHttpStatusError } from '@pellux/goodvibes-errors';
|
|
2
2
|
import { sleepWithSignal } from './backoff.js';
|
|
3
|
-
import { mergeHeaderRecord, normalizeAuthToken,
|
|
3
|
+
import { mergeHeaderRecord, normalizeAuthToken, resolveHeaders } from './auth.js';
|
|
4
4
|
import { applyPerMethodPolicy, getHttpRetryDelay, isRetryableHttpStatus, isRetryableNetworkError, resolveHttpRetryPolicy, } from './retry.js';
|
|
5
|
-
import { buildUrl, createTransportPaths } from './paths.js';
|
|
5
|
+
import { assertSameOriginAbsoluteUrl, buildUrl, createTransportPaths } from './paths.js';
|
|
6
6
|
import { composeMiddleware, createUuidV4, injectTraceparentAsync, invokeTransportObserver, transportErrorFromUnknown, } from '@pellux/goodvibes-transport-core';
|
|
7
7
|
/**
|
|
8
8
|
* Generate a UUID v4 idempotency key.
|
|
@@ -57,10 +57,15 @@ export function createNetworkTransportError(error, url, method) {
|
|
|
57
57
|
? error.message.trim()
|
|
58
58
|
: `Transport request failed before receiving a response for ${url}`;
|
|
59
59
|
const hint = `Transport could not reach ${url}. Verify the baseUrl is reachable.`;
|
|
60
|
+
// only mark network/connection errors as recoverable (i.e. fetch-thrown).
|
|
61
|
+
// TypeError or other programmer errors should not trigger retries.
|
|
62
|
+
const isNetworkError = error instanceof TypeError
|
|
63
|
+
|| (error instanceof Error && /^(?:EAI_AGAIN|ECONNRESET|ECONNREFUSED|ETIMEDOUT|ENOTFOUND|EPIPE|ECONNABORTED)$/.test(error.code ?? ''))
|
|
64
|
+
|| (error instanceof Error && /^UND_ERR_/.test(error.code ?? ''));
|
|
60
65
|
const networkError = new HttpStatusError(message, {
|
|
61
66
|
category: 'network',
|
|
62
67
|
source: 'transport',
|
|
63
|
-
recoverable:
|
|
68
|
+
recoverable: isNetworkError,
|
|
64
69
|
url,
|
|
65
70
|
method,
|
|
66
71
|
body: { error: message },
|
|
@@ -92,11 +97,11 @@ function addQueryValue(url, key, value) {
|
|
|
92
97
|
return;
|
|
93
98
|
}
|
|
94
99
|
if (typeof value === 'object') {
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
// object query values cannot be reliably round-tripped through URL
|
|
101
|
+
// query strings (no daemon route parses JSON-stringified query params).
|
|
102
|
+
// Throw a ContractError instead of implicitly serialising; callers must
|
|
103
|
+
// decompose objects into primitive fields before passing as query parameters.
|
|
104
|
+
throw new ContractError(`Contract query parameter "${key}" is an object, which cannot be safely serialised as a URL query value. Decompose it into primitive fields.`);
|
|
100
105
|
}
|
|
101
106
|
url.searchParams.append(key, String(value));
|
|
102
107
|
}
|
|
@@ -106,10 +111,16 @@ function hasHeader(headers, name) {
|
|
|
106
111
|
}
|
|
107
112
|
function splitContractInput(path, input = {}) {
|
|
108
113
|
const remaining = { ...input };
|
|
109
|
-
|
|
114
|
+
// forbid '.' in path-param names to prevent ambiguous flat-key lookup.
|
|
115
|
+
// Contract generators must not emit dotted param names like {foo.bar}.
|
|
116
|
+
const interpolatedPath = path.replace(/\{([A-Za-z_][A-Za-z0-9_-]*)\}/g, (_match, key) => {
|
|
110
117
|
const value = toStringValue(remaining[key], key);
|
|
111
118
|
delete remaining[key];
|
|
112
|
-
|
|
119
|
+
// encodeURIComponent leaves RFC 3986 sub-delimiters (!'()*~) unencoded.
|
|
120
|
+
// If the server uses regex routing, an unencoded `!`, `(`, `)`, `*`, `~`, or
|
|
121
|
+
// `'` in a path segment could match against a route pattern unexpectedly.
|
|
122
|
+
// Encode those characters explicitly after the standard percent-encoding pass.
|
|
123
|
+
return encodeURIComponent(value).replace(/[!'()*~]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
113
124
|
});
|
|
114
125
|
if (/[{}]/.test(interpolatedPath)) {
|
|
115
126
|
throw new ContractError(`Malformed contract path "${path}". Path parameters must use "{name}" with identifier-like names.`);
|
|
@@ -120,7 +131,7 @@ export function createJsonRequestInit(token, body, method = 'GET', headers = {},
|
|
|
120
131
|
return {
|
|
121
132
|
method,
|
|
122
133
|
credentials: 'include',
|
|
123
|
-
signal,
|
|
134
|
+
...(signal !== undefined ? { signal } : {}),
|
|
124
135
|
headers: mergeHeaderRecord(defaultHeaders, token ? { Authorization: `Bearer ${token}` } : undefined, body !== undefined ? { 'Content-Type': 'application/json' } : undefined, headers),
|
|
125
136
|
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
126
137
|
};
|
|
@@ -133,20 +144,21 @@ export function createFetch(fetchImpl, fallbackFetch) {
|
|
|
133
144
|
}
|
|
134
145
|
return resolved.bind(globalThis);
|
|
135
146
|
}
|
|
147
|
+
const MAX_RETRY_AFTER_MS = 10 * 60 * 1000; // 10 minutes
|
|
136
148
|
function parseRetryAfterMs(headers) {
|
|
137
149
|
const retryAfter = headers.get('retry-after');
|
|
138
150
|
if (!retryAfter)
|
|
139
151
|
return undefined;
|
|
140
|
-
// Numeric seconds
|
|
152
|
+
// Numeric seconds — cap to prevent hostile/buggy huge values.
|
|
141
153
|
const seconds = Number(retryAfter);
|
|
142
|
-
if (!Number.isNaN(seconds) && seconds
|
|
143
|
-
return Math.ceil(seconds * 1000);
|
|
154
|
+
if (!Number.isNaN(seconds) && seconds > 0) {
|
|
155
|
+
return Math.min(Math.ceil(seconds * 1000), MAX_RETRY_AFTER_MS);
|
|
144
156
|
}
|
|
145
157
|
// HTTP-date
|
|
146
158
|
const date = new Date(retryAfter);
|
|
147
159
|
if (!Number.isNaN(date.getTime())) {
|
|
148
160
|
const ms = date.getTime() - Date.now();
|
|
149
|
-
return ms > 0 ? ms : 0;
|
|
161
|
+
return ms > 0 ? Math.min(ms, MAX_RETRY_AFTER_MS) : 0;
|
|
150
162
|
}
|
|
151
163
|
return undefined;
|
|
152
164
|
}
|
|
@@ -157,17 +169,18 @@ export async function readJsonBody(response) {
|
|
|
157
169
|
try {
|
|
158
170
|
return JSON.parse(text);
|
|
159
171
|
}
|
|
160
|
-
catch
|
|
161
|
-
void error;
|
|
172
|
+
catch {
|
|
162
173
|
return text;
|
|
163
174
|
}
|
|
164
175
|
}
|
|
165
176
|
/**
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
177
|
+
* Low-level one-shot JSON request helper.
|
|
178
|
+
*
|
|
179
|
+
* This deliberately bypasses transport auth, middleware, retry policy,
|
|
180
|
+
* idempotency keys, and observers. Prefer
|
|
181
|
+
* `createHttpTransport(...).requestJson()` for normal application code.
|
|
169
182
|
*/
|
|
170
|
-
export async function
|
|
183
|
+
export async function requestJsonRaw(fetchImpl, url, init = {}) {
|
|
171
184
|
let response;
|
|
172
185
|
try {
|
|
173
186
|
response = await fetchImpl(url, init);
|
|
@@ -192,11 +205,13 @@ export function createHttpJsonTransport(options) {
|
|
|
192
205
|
const retryPolicy = options.retry;
|
|
193
206
|
const paths = createTransportPaths(baseUrl);
|
|
194
207
|
const observer = options.observer;
|
|
208
|
+
const onRetryScheduled = options.onRetryScheduled;
|
|
209
|
+
const onRetryExecuted = options.onRetryExecuted;
|
|
195
210
|
// Persistent middleware chain — mutated via use().
|
|
196
211
|
const middlewareChain = [...(options.middleware ?? [])];
|
|
197
212
|
const requestJsonForTransport = async (pathOrUrl, requestOptions = {}) => {
|
|
198
213
|
const url = pathOrUrl.startsWith('http://') || pathOrUrl.startsWith('https://')
|
|
199
|
-
? pathOrUrl
|
|
214
|
+
? assertSameOriginAbsoluteUrl(pathOrUrl, baseUrl)
|
|
200
215
|
: buildUrl(baseUrl, pathOrUrl);
|
|
201
216
|
const method = requestOptions.method ?? (requestOptions.body === undefined ? 'GET' : 'POST');
|
|
202
217
|
const methodId = requestOptions.methodId;
|
|
@@ -208,7 +223,17 @@ export function createHttpJsonTransport(options) {
|
|
|
208
223
|
// Determine idempotency: non-GET mutations without an explicit idempotent flag do NOT retry.
|
|
209
224
|
// This is enforced below by gating the retry check on method type.
|
|
210
225
|
const isMutatingMethod = IDEMPOTENCY_KEY_METHODS.has(method.toUpperCase());
|
|
211
|
-
|
|
226
|
+
// pin traceparent once before the retry loop so all retries share one trace span.
|
|
227
|
+
const pinnedTraceHeaders = {};
|
|
228
|
+
await injectTraceparentAsync(pinnedTraceHeaders);
|
|
229
|
+
// only generate an idempotency key when the call is actually idempotent
|
|
230
|
+
// (contract-marked or has a per-method policy override). Sending keys on all
|
|
231
|
+
// mutating methods could cause duplicate suppression for a non-retried request
|
|
232
|
+
// if a proxy retries it outside the SDK's control.
|
|
233
|
+
const hasPerMethodOverride = methodId !== undefined && resolveHttpRetryPolicy(retryPolicy, requestOptions.retry).perMethodPolicy[methodId] !== undefined;
|
|
234
|
+
const idempotencyKey = isMutatingMethod && (contractIdempotent || hasPerMethodOverride)
|
|
235
|
+
? generateIdempotencyKey()
|
|
236
|
+
: undefined;
|
|
212
237
|
let attempt = 0;
|
|
213
238
|
while (true) {
|
|
214
239
|
attempt += 1;
|
|
@@ -222,15 +247,17 @@ export function createHttpJsonTransport(options) {
|
|
|
222
247
|
if (requestOptions.body !== undefined) {
|
|
223
248
|
mergedHeaders['Content-Type'] = 'application/json';
|
|
224
249
|
}
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
|
|
250
|
+
// merge pre-pinned traceparent headers (captured once before the retry loop)
|
|
251
|
+
// so all retry attempts share a single logical trace span.
|
|
252
|
+
for (const [k, v] of Object.entries(pinnedTraceHeaders)) {
|
|
253
|
+
mergedHeaders[k] = v;
|
|
254
|
+
}
|
|
228
255
|
// Inject idempotency key for mutating methods.
|
|
229
256
|
if (idempotencyKey && !hasHeader(mergedHeaders, 'Idempotency-Key')) {
|
|
230
257
|
mergedHeaders['Idempotency-Key'] = idempotencyKey;
|
|
231
258
|
}
|
|
232
259
|
// Notify observer before dispatching the request.
|
|
233
|
-
invokeTransportObserver(() => observer?.onTransportActivity?.({ direction: 'send', url, kind: 'http' }));
|
|
260
|
+
invokeTransportObserver(() => observer?.onTransportActivity?.({ direction: 'send', url, kind: 'http' }), observer?.onObserverError);
|
|
234
261
|
const sendAt = Date.now();
|
|
235
262
|
// Build the middleware context for this attempt.
|
|
236
263
|
const ctx = {
|
|
@@ -241,12 +268,14 @@ export function createHttpJsonTransport(options) {
|
|
|
241
268
|
options: requestOptions,
|
|
242
269
|
signal: requestOptions.signal,
|
|
243
270
|
};
|
|
271
|
+
// stash parsed body here so the no-middleware fast path avoids re-parsing.
|
|
272
|
+
let _parsedBodyCache = undefined;
|
|
244
273
|
// Build the inner fetch that middleware wraps (and also used directly without middleware).
|
|
245
274
|
const innerFetch = async (c) => {
|
|
246
275
|
const init = {
|
|
247
276
|
method: c.method,
|
|
248
277
|
credentials: 'include',
|
|
249
|
-
signal: c.signal,
|
|
278
|
+
...(c.signal !== undefined ? { signal: c.signal } : {}),
|
|
250
279
|
headers: c.headers,
|
|
251
280
|
...(c.body !== undefined ? { body: JSON.stringify(c.body) } : {}),
|
|
252
281
|
};
|
|
@@ -262,8 +291,17 @@ export function createHttpJsonTransport(options) {
|
|
|
262
291
|
const retryAfterMs = parseRetryAfterMs(response.headers);
|
|
263
292
|
throw createTransportError(response.status, c.url, c.method, body, retryAfterMs);
|
|
264
293
|
}
|
|
265
|
-
//
|
|
266
|
-
|
|
294
|
+
// stash the already-parsed body so the no-middleware fast path can
|
|
295
|
+
// read it directly without a JSON.stringify → JSON.parse round-trip.
|
|
296
|
+
_parsedBodyCache = body;
|
|
297
|
+
// Also expose on ctx so middleware callers can read parsedBody directly.
|
|
298
|
+
ctx.parsedBody = body;
|
|
299
|
+
// Return synthetic Response carrying parsed body so callers (middleware) can .json().
|
|
300
|
+
return new Response(JSON.stringify(body), {
|
|
301
|
+
status: response.status,
|
|
302
|
+
statusText: response.statusText,
|
|
303
|
+
headers: response.headers,
|
|
304
|
+
});
|
|
267
305
|
};
|
|
268
306
|
try {
|
|
269
307
|
if (middlewareChain.length > 0) {
|
|
@@ -281,25 +319,27 @@ export function createHttpJsonTransport(options) {
|
|
|
281
319
|
method,
|
|
282
320
|
});
|
|
283
321
|
}
|
|
284
|
-
|
|
322
|
+
// use parsedBody stashed by innerFetch to avoid a JSON round-trip.
|
|
323
|
+
const result = (ctx.parsedBody !== undefined ? ctx.parsedBody : await ctx.response.json());
|
|
285
324
|
invokeTransportObserver(() => observer?.onTransportActivity?.({
|
|
286
325
|
direction: 'recv',
|
|
287
326
|
url,
|
|
288
327
|
kind: 'http',
|
|
289
328
|
durationMs: ctx.durationMs,
|
|
290
|
-
}));
|
|
329
|
+
}), observer?.onObserverError);
|
|
291
330
|
return result;
|
|
292
331
|
}
|
|
293
332
|
// No-middleware fast path — directly invoke innerFetch with ctx.
|
|
294
|
-
|
|
295
|
-
|
|
333
|
+
await innerFetch(ctx);
|
|
334
|
+
// use the cached parsed body (set by innerFetch) to avoid JSON round-trip.
|
|
335
|
+
const result = _parsedBodyCache;
|
|
296
336
|
// Notify observer after a successful response.
|
|
297
337
|
invokeTransportObserver(() => observer?.onTransportActivity?.({
|
|
298
338
|
direction: 'recv',
|
|
299
339
|
url,
|
|
300
340
|
kind: 'http',
|
|
301
341
|
durationMs: Date.now() - sendAt,
|
|
302
|
-
}));
|
|
342
|
+
}), observer?.onObserverError);
|
|
303
343
|
return result;
|
|
304
344
|
}
|
|
305
345
|
catch (error) {
|
|
@@ -323,7 +363,7 @@ export function createHttpJsonTransport(options) {
|
|
|
323
363
|
return error;
|
|
324
364
|
})();
|
|
325
365
|
// Notify observer of the transport error before deciding to retry or rethrow.
|
|
326
|
-
invokeTransportObserver(() => observer?.onError?.(transportErrorFromUnknown(wrappedError, 'HTTP transport error')));
|
|
366
|
+
invokeTransportObserver(() => observer?.onError?.(transportErrorFromUnknown(wrappedError, 'HTTP transport error')), observer?.onObserverError);
|
|
327
367
|
const status = typeof wrappedError === 'object' && wrappedError !== null && 'transport' in wrappedError
|
|
328
368
|
? wrappedError.transport?.status
|
|
329
369
|
: undefined;
|
|
@@ -337,7 +377,13 @@ export function createHttpJsonTransport(options) {
|
|
|
337
377
|
if (!shouldRetry) {
|
|
338
378
|
throw wrappedError;
|
|
339
379
|
}
|
|
340
|
-
|
|
380
|
+
const backoffMs = getHttpRetryDelay(attempt, resolvedRetry);
|
|
381
|
+
const retryReason = typeof status === 'number' && status > 0 ? `http-${status}` : 'network-error';
|
|
382
|
+
// Notify callers before the retry sleep starts.
|
|
383
|
+
onRetryScheduled?.({ attempt, maxAttempts: resolvedRetry.maxAttempts, backoffMs, reason: retryReason });
|
|
384
|
+
await sleepWithSignal(backoffMs, requestOptions.signal);
|
|
385
|
+
// Notify callers after the retry sleep completes.
|
|
386
|
+
onRetryExecuted?.({ attempt, maxAttempts: resolvedRetry.maxAttempts });
|
|
341
387
|
}
|
|
342
388
|
}
|
|
343
389
|
};
|
package/dist/http.d.ts
CHANGED
|
@@ -2,11 +2,11 @@ import { type AuthTokenResolver, type HeaderResolver, type MaybePromise, mergeHe
|
|
|
2
2
|
import { type BackoffPolicy, type ResolvedBackoffPolicy, computeBackoffDelay, normalizeBackoffPolicy, sleepWithSignal } from './backoff.js';
|
|
3
3
|
import { type HttpRetryPolicy, type ResolvedHttpRetryPolicy, DEFAULT_HTTP_RETRY_POLICY, getHttpRetryDelay, isRetryableHttpStatus, isRetryableNetworkError, normalizeHttpRetryPolicy, resolveHttpRetryPolicy } from './retry.js';
|
|
4
4
|
import { type ResolvedStreamReconnectPolicy, type StreamReconnectPolicy, DEFAULT_STREAM_RECONNECT_POLICY, getStreamReconnectDelay, normalizeStreamReconnectPolicy } from './reconnect.js';
|
|
5
|
-
import { createFetch, createJsonInit, createJsonRequestInit, generateIdempotencyKey, readJsonBody,
|
|
5
|
+
import { createFetch, createJsonInit, createJsonRequestInit, generateIdempotencyKey, readJsonBody, requestJsonRaw, type HttpJsonRequestOptions, type HttpJsonTransport, type HttpJsonTransportOptions, type JsonObject, type JsonValue, type ResolvedContractRequest, type TransportContext, type TransportMiddleware, type TransportJsonError } from './http-core.js';
|
|
6
6
|
export type { AuthTokenResolver, BackoffPolicy, HeaderResolver, HttpJsonRequestOptions, HttpRetryPolicy, JsonObject, JsonValue, MaybePromise, ResolvedBackoffPolicy, ResolvedContractRequest, ResolvedHttpRetryPolicy, ResolvedStreamReconnectPolicy, StreamReconnectPolicy, TransportContext, TransportJsonError, TransportMiddleware, };
|
|
7
7
|
export type HttpTransportOptions = HttpJsonTransportOptions;
|
|
8
8
|
export type HttpTransport = HttpJsonTransport;
|
|
9
|
-
export { createFetch, createJsonInit, createJsonRequestInit, generateIdempotencyKey, readJsonBody,
|
|
9
|
+
export { createFetch, createJsonInit, createJsonRequestInit, generateIdempotencyKey, readJsonBody, requestJsonRaw, mergeHeaders, resolveAuthToken, resolveHeaders, computeBackoffDelay, normalizeBackoffPolicy, sleepWithSignal, DEFAULT_HTTP_RETRY_POLICY, getHttpRetryDelay, isRetryableHttpStatus, isRetryableNetworkError, normalizeHttpRetryPolicy, resolveHttpRetryPolicy, DEFAULT_STREAM_RECONNECT_POLICY, getStreamReconnectDelay, normalizeStreamReconnectPolicy, };
|
|
10
10
|
export declare function normalizeTransportError(error: unknown): Error;
|
|
11
11
|
export declare function createHttpTransport(options: HttpTransportOptions): HttpTransport;
|
|
12
12
|
//# sourceMappingURL=http.d.ts.map
|
package/dist/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACf,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,mBAAmB,EACnB,sBAAsB,EACtB,eAAe,EAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,yBAAyB,EACzB,iBAAiB,EACjB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,EAC1B,+BAA+B,EAC/B,uBAAuB,EACvB,8BAA8B,EAC/B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,WAAW,EAEX,cAAc,EACd,qBAAqB,EACrB,sBAAsB,EAEtB,YAAY,EACZ,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACf,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,mBAAmB,EACnB,sBAAsB,EACtB,eAAe,EAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,yBAAyB,EACzB,iBAAiB,EACjB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,EAC1B,+BAA+B,EAC/B,uBAAuB,EACvB,8BAA8B,EAC/B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,WAAW,EAEX,cAAc,EACd,qBAAqB,EACrB,sBAAsB,EAEtB,YAAY,EACZ,cAAc,EACd,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAExB,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,sBAAsB,EACtB,eAAe,EACf,UAAU,EACV,SAAS,EACT,YAAY,EACZ,qBAAqB,EACrB,uBAAuB,EACvB,uBAAuB,EACvB,6BAA6B,EAC7B,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,GACpB,CAAC;AACF,MAAM,MAAM,oBAAoB,GAAG,wBAAwB,CAAC;AAC5D,MAAM,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAC9C,OAAO,EACL,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,mBAAmB,EACnB,sBAAsB,EACtB,eAAe,EACf,yBAAyB,EACzB,iBAAiB,EACjB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EACtB,+BAA+B,EAC/B,uBAAuB,EACvB,8BAA8B,GAC/B,CAAC;AAwBF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CA8D7D;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAmBhF"}
|
package/dist/http.js
CHANGED
|
@@ -3,8 +3,8 @@ import { mergeHeaders, resolveAuthToken, resolveHeaders, } from './auth.js';
|
|
|
3
3
|
import { computeBackoffDelay, normalizeBackoffPolicy, sleepWithSignal, } from './backoff.js';
|
|
4
4
|
import { DEFAULT_HTTP_RETRY_POLICY, getHttpRetryDelay, isRetryableHttpStatus, isRetryableNetworkError, normalizeHttpRetryPolicy, resolveHttpRetryPolicy, } from './retry.js';
|
|
5
5
|
import { DEFAULT_STREAM_RECONNECT_POLICY, getStreamReconnectDelay, normalizeStreamReconnectPolicy, } from './reconnect.js';
|
|
6
|
-
import { createFetch, createHttpJsonTransport, createJsonInit, createJsonRequestInit, generateIdempotencyKey, inferTransportHint, readJsonBody,
|
|
7
|
-
export { createFetch, createJsonInit, createJsonRequestInit, generateIdempotencyKey, readJsonBody,
|
|
6
|
+
import { createFetch, createHttpJsonTransport, createJsonInit, createJsonRequestInit, generateIdempotencyKey, inferTransportHint, readJsonBody, requestJsonRaw, } from './http-core.js';
|
|
7
|
+
export { createFetch, createJsonInit, createJsonRequestInit, generateIdempotencyKey, readJsonBody, requestJsonRaw, mergeHeaders, resolveAuthToken, resolveHeaders, computeBackoffDelay, normalizeBackoffPolicy, sleepWithSignal, DEFAULT_HTTP_RETRY_POLICY, getHttpRetryDelay, isRetryableHttpStatus, isRetryableNetworkError, normalizeHttpRetryPolicy, resolveHttpRetryPolicy, DEFAULT_STREAM_RECONNECT_POLICY, getStreamReconnectDelay, normalizeStreamReconnectPolicy, };
|
|
8
8
|
function isTransportError(error) {
|
|
9
9
|
return Boolean(error
|
|
10
10
|
&& typeof error === 'object'
|
|
@@ -51,7 +51,7 @@ export function normalizeTransportError(error) {
|
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
53
|
if (error instanceof Error) {
|
|
54
|
-
// Defensive string-match
|
|
54
|
+
// Defensive string-match path for non-SDK errors that slip through.
|
|
55
55
|
// With structured throws in http-core.ts, these paths are rarely exercised.
|
|
56
56
|
if (error.message === 'Fetch implementation is required' || error.message === 'Transport baseUrl is required') {
|
|
57
57
|
return new ConfigurationError(error.message);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export type { ContractInvokeOptions, ContractRouteDefinition, ContractRouteLike, ContractStreamOptions, } from './contract-client.js';
|
|
2
|
-
export {
|
|
2
|
+
export { invokeContractRoute, openContractRouteStream, requireContractRoute, } from './contract-client.js';
|
|
3
3
|
export type { JsonSchemaValidationFailure, MethodArgs, RequiredKeys, WithoutKeys } from './client-plumbing.js';
|
|
4
4
|
export { clientInputRecord, firstJsonSchemaFailure, mergeClientInput, splitClientArgs } from './client-plumbing.js';
|
|
5
5
|
export type { HttpJsonRequestOptions, HttpTransport, HttpTransportOptions, JsonObject, JsonValue, ResolvedContractRequest, TransportJsonError, } from './http.js';
|
|
6
|
-
export { createFetch, createHttpTransport, createJsonInit, createJsonRequestInit, normalizeTransportError, readJsonBody,
|
|
7
|
-
export type { TransportContext, TransportMiddleware } from './http-core.js';
|
|
6
|
+
export { createFetch, createHttpTransport, createJsonInit, createJsonRequestInit, normalizeTransportError, readJsonBody, requestJsonRaw, } from './http.js';
|
|
7
|
+
export type { HttpJsonTransport, HttpJsonTransportOptions, TransportContext, TransportMiddleware } from './http-core.js';
|
|
8
8
|
export { generateIdempotencyKey } from './http-core.js';
|
|
9
9
|
export type { ServerSentEventHandlers, ServerSentEventOptions } from './sse.js';
|
|
10
10
|
export { openServerSentEventStream } from './sse.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,qBAAqB,EACrB,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,qBAAqB,EACrB,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,2BAA2B,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC/G,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACpH,YAAY,EACV,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,UAAU,EACV,SAAS,EACT,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,YAAY,EACZ,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACzH,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAChF,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AACrD,YAAY,EAAE,uBAAuB,IAAI,0BAA0B,EAAE,sBAAsB,IAAI,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAClJ,OAAO,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AAC/D,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACjG,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAClH,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC5F,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACjG,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAClM,YAAY,EAAE,qBAAqB,EAAE,6BAA6B,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,+BAA+B,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAC;AAC1H,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { invokeContractRoute, openContractRouteStream, requireContractRoute, } from './contract-client.js';
|
|
2
2
|
export { clientInputRecord, firstJsonSchemaFailure, mergeClientInput, splitClientArgs } from './client-plumbing.js';
|
|
3
|
-
export { createFetch, createHttpTransport, createJsonInit, createJsonRequestInit, normalizeTransportError, readJsonBody,
|
|
3
|
+
export { createFetch, createHttpTransport, createJsonInit, createJsonRequestInit, normalizeTransportError, readJsonBody, requestJsonRaw, } from './http.js';
|
|
4
4
|
export { generateIdempotencyKey } from './http-core.js';
|
|
5
5
|
export { openServerSentEventStream } from './sse.js';
|
|
6
6
|
export { openRawServerSentEventStream } from './sse-stream.js';
|
package/dist/paths.d.ts
CHANGED
|
@@ -25,8 +25,22 @@ export interface TransportPaths {
|
|
|
25
25
|
readonly peerRequestsUrl: string;
|
|
26
26
|
readonly peerListUrl: string;
|
|
27
27
|
readonly remoteWorkUrl: string;
|
|
28
|
+
/**
|
|
29
|
+
* Alias for `controlPlaneUrl`. Provided for convenience.
|
|
30
|
+
*/
|
|
31
|
+
readonly controlUrl: string;
|
|
28
32
|
}
|
|
29
33
|
export declare function normalizeBaseUrl(baseUrl: string): string;
|
|
30
34
|
export declare function buildUrl(baseUrl: string, path: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Assert that an absolute URL has the same origin as a reference URL (or baseUrl).
|
|
37
|
+
*
|
|
38
|
+
* Used by transport call sites that allow absolute URLs as a convenience but
|
|
39
|
+
* MUST NOT leak the bearer Authorization header to a different origin. Throws
|
|
40
|
+
* `ConfigurationError SDK_TRANSPORT_CROSS_ORIGIN` when origins diverge.
|
|
41
|
+
*
|
|
42
|
+
* Returns the validated absolute URL unchanged on success.
|
|
43
|
+
*/
|
|
44
|
+
export declare function assertSameOriginAbsoluteUrl(absoluteUrl: string, originReferenceUrl: string): string;
|
|
31
45
|
export declare function createTransportPaths(baseUrl: string): TransportPaths;
|
|
32
46
|
//# sourceMappingURL=paths.d.ts.map
|
package/dist/paths.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B;;OAEG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoCxD;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAU9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,GAAG,MAAM,CA6BnG;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CA+BpE"}
|
package/dist/paths.js
CHANGED
|
@@ -4,11 +4,91 @@ export function normalizeBaseUrl(baseUrl) {
|
|
|
4
4
|
if (!normalized) {
|
|
5
5
|
throw new ConfigurationError('Transport baseUrl is required. Pass a non-empty baseUrl string to your transport or SDK options.', { code: 'SDK_TRANSPORT_BASE_URL_REQUIRED' });
|
|
6
6
|
}
|
|
7
|
+
let parsed;
|
|
8
|
+
try {
|
|
9
|
+
parsed = new URL(normalized);
|
|
10
|
+
}
|
|
11
|
+
catch (cause) {
|
|
12
|
+
throw new ConfigurationError('Transport baseUrl must be an absolute URL.', {
|
|
13
|
+
code: 'SDK_TRANSPORT_BASE_URL_INVALID',
|
|
14
|
+
source: 'transport',
|
|
15
|
+
hint: 'Pass a full URL such as https://goodvibes.example.com.',
|
|
16
|
+
cause,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
const protocol = parsed.protocol;
|
|
20
|
+
if (protocol !== 'https:' && protocol !== 'wss:' && protocol !== 'http:' && protocol !== 'ws:') {
|
|
21
|
+
throw new ConfigurationError(`Unsupported transport baseUrl protocol: ${protocol}`, {
|
|
22
|
+
code: 'SDK_TRANSPORT_BASE_URL_PROTOCOL_UNSUPPORTED',
|
|
23
|
+
source: 'transport',
|
|
24
|
+
hint: 'Use https:// or wss://. http:// and ws:// are accepted only for local development or explicit insecure deployments.',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const host = parsed.hostname.toLowerCase();
|
|
28
|
+
const local = host === 'localhost' || host === '::1' || host === '127.0.0.1' || host.startsWith('127.');
|
|
29
|
+
const runtimeProcess = globalThis.process;
|
|
30
|
+
const allowInsecure = runtimeProcess?.env?.GOODVIBES_ALLOW_INSECURE_TRANSPORT === 'true';
|
|
31
|
+
if ((protocol === 'http:' || protocol === 'ws:') && !local && !allowInsecure) {
|
|
32
|
+
throw new ConfigurationError('Refusing insecure non-local GoodVibes transport baseUrl.', {
|
|
33
|
+
code: 'SDK_TRANSPORT_INSECURE_BASE_URL',
|
|
34
|
+
source: 'transport',
|
|
35
|
+
hint: 'Use https:// or wss://, or set GOODVIBES_ALLOW_INSECURE_TRANSPORT=true for an intentional non-local development deployment.',
|
|
36
|
+
});
|
|
37
|
+
}
|
|
7
38
|
return normalized.replace(/\/+$/, '');
|
|
8
39
|
}
|
|
9
40
|
export function buildUrl(baseUrl, path) {
|
|
10
41
|
const normalized = normalizeBaseUrl(baseUrl);
|
|
11
|
-
|
|
42
|
+
if (/^[a-z][a-z0-9+\-.]*:/i.test(path)) {
|
|
43
|
+
throw new ConfigurationError(`Absolute path not allowed: ${path}`, { code: 'SDK_TRANSPORT_PATH_ABSOLUTE' });
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
return new URL(path, `${normalized}/`).toString();
|
|
47
|
+
}
|
|
48
|
+
catch (cause) {
|
|
49
|
+
throw new ConfigurationError(`Invalid transport path: ${path}`, { code: 'SDK_TRANSPORT_PATH_INVALID', cause });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Assert that an absolute URL has the same origin as a reference URL (or baseUrl).
|
|
54
|
+
*
|
|
55
|
+
* Used by transport call sites that allow absolute URLs as a convenience but
|
|
56
|
+
* MUST NOT leak the bearer Authorization header to a different origin. Throws
|
|
57
|
+
* `ConfigurationError SDK_TRANSPORT_CROSS_ORIGIN` when origins diverge.
|
|
58
|
+
*
|
|
59
|
+
* Returns the validated absolute URL unchanged on success.
|
|
60
|
+
*/
|
|
61
|
+
export function assertSameOriginAbsoluteUrl(absoluteUrl, originReferenceUrl) {
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = new URL(absoluteUrl);
|
|
65
|
+
}
|
|
66
|
+
catch (cause) {
|
|
67
|
+
throw new ConfigurationError(`Invalid absolute transport URL: ${absoluteUrl}`, {
|
|
68
|
+
code: 'SDK_TRANSPORT_URL_INVALID',
|
|
69
|
+
source: 'transport',
|
|
70
|
+
cause,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
let referenceOrigin;
|
|
74
|
+
try {
|
|
75
|
+
referenceOrigin = new URL(originReferenceUrl).origin;
|
|
76
|
+
}
|
|
77
|
+
catch (cause) {
|
|
78
|
+
throw new ConfigurationError(`Invalid transport origin reference URL: ${originReferenceUrl}`, {
|
|
79
|
+
code: 'SDK_TRANSPORT_URL_INVALID',
|
|
80
|
+
source: 'transport',
|
|
81
|
+
cause,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (parsed.origin !== referenceOrigin) {
|
|
85
|
+
throw new ConfigurationError(`Cross-origin transport request rejected: ${parsed.origin} does not match transport baseUrl origin ${referenceOrigin}`, {
|
|
86
|
+
code: 'SDK_TRANSPORT_CROSS_ORIGIN',
|
|
87
|
+
source: 'transport',
|
|
88
|
+
hint: 'The transport bearer token would be sent to a different origin. Use a same-origin URL or open a separate transport for the cross-origin endpoint.',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return absoluteUrl;
|
|
12
92
|
}
|
|
13
93
|
export function createTransportPaths(baseUrl) {
|
|
14
94
|
const normalized = normalizeBaseUrl(baseUrl);
|
|
@@ -39,5 +119,6 @@ export function createTransportPaths(baseUrl) {
|
|
|
39
119
|
peerRequestsUrl: buildUrl(normalized, '/api/remote/pair/requests'),
|
|
40
120
|
peerListUrl: buildUrl(normalized, '/api/remote/peers'),
|
|
41
121
|
remoteWorkUrl: buildUrl(normalized, '/api/remote/work'),
|
|
122
|
+
controlUrl: buildUrl(normalized, '/api/control-plane'),
|
|
42
123
|
};
|
|
43
124
|
}
|
package/dist/reconnect.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type BackoffPolicy, type ResolvedBackoffPolicy } from './backoff.js';
|
|
2
2
|
export interface StreamReconnectPolicy extends BackoffPolicy {
|
|
3
|
-
readonly enabled?: boolean;
|
|
3
|
+
readonly enabled?: boolean | undefined;
|
|
4
4
|
}
|
|
5
5
|
export interface ResolvedStreamReconnectPolicy extends ResolvedBackoffPolicy {
|
|
6
6
|
readonly enabled: boolean;
|
package/dist/reconnect.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconnect.d.ts","sourceRoot":"","sources":["../src/reconnect.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+C,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE3H,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAC1D,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"reconnect.d.ts","sourceRoot":"","sources":["../src/reconnect.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+C,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE3H,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAC1D,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACxC;AAED,MAAM,WAAW,6BAA8B,SAAQ,qBAAqB;IAC1E,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,gGAAgG;AAChG,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C,eAAO,MAAM,+BAA+B,EAAE,6BAM7C,CAAC;AAEF,wBAAgB,8BAA8B,CAC5C,MAAM,CAAC,EAAE,qBAAqB,GAC7B,6BAA6B,CAM/B;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,6BAA6B,GACpC,MAAM,CAER"}
|
package/dist/retry.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { type BackoffPolicy, type ResolvedBackoffPolicy } from './backoff.js';
|
|
2
2
|
export interface PerMethodRetryPolicy {
|
|
3
|
-
readonly maxAttempts?: number;
|
|
4
|
-
readonly baseDelayMs?: number;
|
|
5
|
-
readonly maxDelayMs?: number;
|
|
6
|
-
readonly backoffFactor?: number;
|
|
3
|
+
readonly maxAttempts?: number | undefined;
|
|
4
|
+
readonly baseDelayMs?: number | undefined;
|
|
5
|
+
readonly maxDelayMs?: number | undefined;
|
|
6
|
+
readonly backoffFactor?: number | undefined;
|
|
7
7
|
}
|
|
8
8
|
export interface HttpRetryPolicy extends BackoffPolicy {
|
|
9
|
-
readonly retryOnStatuses?: readonly number[];
|
|
10
|
-
readonly retryOnMethods?: readonly string[];
|
|
11
|
-
readonly retryOnNetworkError?: boolean;
|
|
9
|
+
readonly retryOnStatuses?: readonly number[] | undefined;
|
|
10
|
+
readonly retryOnMethods?: readonly string[] | undefined;
|
|
11
|
+
readonly retryOnNetworkError?: boolean | undefined;
|
|
12
12
|
/** Per-method retry policy overrides keyed by method ID. */
|
|
13
|
-
readonly perMethodPolicy?: Readonly<Record<string, PerMethodRetryPolicy
|
|
13
|
+
readonly perMethodPolicy?: Readonly<Record<string, PerMethodRetryPolicy>> | undefined;
|
|
14
14
|
}
|
|
15
15
|
export interface ResolvedHttpRetryPolicy extends ResolvedBackoffPolicy {
|
|
16
16
|
readonly retryOnStatuses: readonly number[];
|
package/dist/retry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AACA,OAAO,EAA+C,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE3H,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AACA,OAAO,EAA+C,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE3H,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACzD,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACxD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnD,4DAA4D;IAC5D,QAAQ,CAAC,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,GAAG,SAAS,CAAC;CACvF;AAED,MAAM,WAAW,uBAAwB,SAAQ,qBAAqB;IACpE,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,4DAA4D;IAC5D,QAAQ,CAAC,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;CAC1E;AAED,eAAO,MAAM,yBAAyB,EAAE,uBASvC,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,MAAM,CAAC,EAAE,eAAe,GACvB,uBAAuB,CASzB;AAED,wBAAgB,sBAAsB,CACpC,aAAa,CAAC,EAAE,eAAe,EAC/B,QAAQ,CAAC,EAAE,KAAK,GAAG,eAAe,GACjC,uBAAuB,CAczB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,uBAAuB,EAC7B,QAAQ,EAAE,MAAM,GACf,uBAAuB,CAUzB;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,uBAAuB,GAC9B,MAAM,CAER;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAGT;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAET"}
|
package/dist/sse-stream.d.ts
CHANGED
|
@@ -2,26 +2,26 @@ import { type AuthTokenResolver } from './auth.js';
|
|
|
2
2
|
import { type StreamReconnectPolicy } from './reconnect.js';
|
|
3
3
|
import { isAbortError } from '@pellux/goodvibes-transport-core';
|
|
4
4
|
export interface ServerSentEventHandlers {
|
|
5
|
-
readonly onEvent?: (eventName: string, payload: unknown) => void;
|
|
6
|
-
readonly onReady?: (payload: unknown) => void;
|
|
7
|
-
readonly onError?: (error: unknown) => void;
|
|
5
|
+
readonly onEvent?: ((eventName: string, payload: unknown) => void) | undefined;
|
|
6
|
+
readonly onReady?: ((payload: unknown) => void) | undefined;
|
|
7
|
+
readonly onError?: ((error: unknown) => void) | undefined;
|
|
8
8
|
readonly onReconnect?: (input: {
|
|
9
9
|
readonly attempt: number;
|
|
10
10
|
readonly delayMs: number;
|
|
11
11
|
}) => void;
|
|
12
|
-
readonly onClose?: () => void;
|
|
12
|
+
readonly onClose?: (() => void) | undefined;
|
|
13
13
|
readonly onTerminate?: (input: {
|
|
14
14
|
readonly error: unknown;
|
|
15
15
|
readonly reconnectAttempts: number;
|
|
16
16
|
}) => void;
|
|
17
17
|
}
|
|
18
18
|
export interface ServerSentEventOptions {
|
|
19
|
-
readonly signal?: AbortSignal;
|
|
20
|
-
readonly headers?: HeadersInit;
|
|
21
|
-
readonly authToken?: string | null;
|
|
22
|
-
readonly getAuthToken?: AuthTokenResolver;
|
|
23
|
-
readonly lastEventId?: string | null;
|
|
24
|
-
readonly reconnect?: StreamReconnectPolicy;
|
|
19
|
+
readonly signal?: AbortSignal | undefined;
|
|
20
|
+
readonly headers?: HeadersInit | undefined;
|
|
21
|
+
readonly authToken?: string | null | undefined;
|
|
22
|
+
readonly getAuthToken?: AuthTokenResolver | undefined;
|
|
23
|
+
readonly lastEventId?: string | null | undefined;
|
|
24
|
+
readonly reconnect?: StreamReconnectPolicy | undefined;
|
|
25
25
|
}
|
|
26
26
|
export { isAbortError };
|
|
27
27
|
export { openRawServerSentEventStream as openServerSentEventStream };
|
package/dist/sse-stream.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse-stream.d.ts","sourceRoot":"","sources":["../src/sse-stream.ts"],"names":[],"mappings":"AACA,OAAO,EAAkC,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAGhE,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"sse-stream.d.ts","sourceRoot":"","sources":["../src/sse-stream.ts"],"names":[],"mappings":"AACA,OAAO,EAAkC,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAGhE,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC/E,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC5D,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1D,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/F,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACzG;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACtD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACjD,QAAQ,CAAC,SAAS,CAAC,EAAE,qBAAqB,GAAG,SAAS,CAAC;CACxD;AAWD,OAAO,EAAE,YAAY,EAAE,CAAC;AAwCxB,OAAO,EAAE,4BAA4B,IAAI,yBAAyB,EAAE,CAAC;AAMrE,wBAAsB,4BAA4B,CAChD,SAAS,EAAE,OAAO,KAAK,EACvB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,uBAAuB,EACjC,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,MAAM,IAAI,CAAC,CA6LrB"}
|
package/dist/sse-stream.js
CHANGED
|
@@ -4,13 +4,12 @@ import { getStreamReconnectDelay, normalizeStreamReconnectPolicy, } from './reco
|
|
|
4
4
|
import { createHttpStatusError, HttpStatusError } from '@pellux/goodvibes-errors';
|
|
5
5
|
import { isAbortError } from '@pellux/goodvibes-transport-core';
|
|
6
6
|
function readEventPayload(data) {
|
|
7
|
-
if (!data.
|
|
7
|
+
if (!data.trimEnd())
|
|
8
8
|
return null;
|
|
9
9
|
try {
|
|
10
10
|
return JSON.parse(data);
|
|
11
11
|
}
|
|
12
|
-
catch
|
|
13
|
-
void error;
|
|
12
|
+
catch {
|
|
14
13
|
return data;
|
|
15
14
|
}
|
|
16
15
|
}
|
|
@@ -137,18 +136,18 @@ export async function openRawServerSentEventStream(fetchImpl, url, handlers, opt
|
|
|
137
136
|
if (line.startsWith(':'))
|
|
138
137
|
return;
|
|
139
138
|
if (line.startsWith('id:')) {
|
|
140
|
-
const candidate = line.slice(3).
|
|
139
|
+
const candidate = line.slice(3).replace(/^ /, '');
|
|
141
140
|
if (candidate) {
|
|
142
141
|
lastEventId = candidate;
|
|
143
142
|
}
|
|
144
143
|
return;
|
|
145
144
|
}
|
|
146
145
|
if (line.startsWith('event:')) {
|
|
147
|
-
eventName = line.slice(6).
|
|
146
|
+
eventName = line.slice(6).replace(/^ /, '');
|
|
148
147
|
return;
|
|
149
148
|
}
|
|
150
149
|
if (line.startsWith('data:')) {
|
|
151
|
-
data += `${data ? '\n' : ''}${line.slice(5).
|
|
150
|
+
data += `${data ? '\n' : ''}${line.slice(5).replace(/^ /, '')}`;
|
|
152
151
|
}
|
|
153
152
|
};
|
|
154
153
|
try {
|
|
@@ -169,6 +168,10 @@ export async function openRawServerSentEventStream(fetchImpl, url, handlers, opt
|
|
|
169
168
|
consumeLine(buffer.replace(/\r$/, ''));
|
|
170
169
|
flush();
|
|
171
170
|
}
|
|
171
|
+
// Abort and remote-close can land in the same microtask. Yield once so
|
|
172
|
+
// the abort listener can mark the stream stopped before reconnect logic
|
|
173
|
+
// treats the close as unexpected.
|
|
174
|
+
await Promise.resolve();
|
|
172
175
|
if (reconnectPolicy.enabled && !controller.signal.aborted && !outerController.signal.aborted && !stopped) {
|
|
173
176
|
throw createStreamError(response.status, url, 'Stream closed unexpectedly');
|
|
174
177
|
}
|
package/dist/sse.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AACA,OAAO,EAAgC,KAAK,uBAAuB,EAAE,KAAK,sBAAsB,IAAI,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AACxJ,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,YAAY,EAAE,uBAAuB,EAAE,CAAC;AACxC,MAAM,WAAW,sBAAuB,SAAQ,IAAI,CAAC,0BAA0B,EAAE,WAAW,CAAC;CAAG;AAEhG,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,aAAa,EACxB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,uBAAuB,EACjC,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,MAAM,IAAI,CAAC,CASrB"}
|
package/dist/sse.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { assertSameOriginAbsoluteUrl } from './paths.js';
|
|
1
2
|
import { openRawServerSentEventStream } from './sse-stream.js';
|
|
2
3
|
export async function openServerSentEventStream(transport, pathOrUrl, handlers, options = {}) {
|
|
3
4
|
const url = pathOrUrl.startsWith('http://') || pathOrUrl.startsWith('https://')
|
|
4
|
-
? pathOrUrl
|
|
5
|
+
? assertSameOriginAbsoluteUrl(pathOrUrl, transport.buildUrl('/'))
|
|
5
6
|
: transport.buildUrl(pathOrUrl);
|
|
6
7
|
return await openRawServerSentEventStream(transport.fetchImpl, url, handlers, {
|
|
7
8
|
...options,
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-transport-http",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"engines": {
|
|
5
|
-
"
|
|
5
|
+
"bun": "1.3.10",
|
|
6
|
+
"node": ">=22.0.0"
|
|
6
7
|
},
|
|
7
8
|
"description": "HTTP, JSON, path, and SSE transport primitives for GoodVibes client integrations.",
|
|
8
9
|
"type": "module",
|
|
@@ -80,8 +81,9 @@
|
|
|
80
81
|
"transport"
|
|
81
82
|
],
|
|
82
83
|
"dependencies": {
|
|
83
|
-
"@pellux/goodvibes-
|
|
84
|
-
"@pellux/goodvibes-
|
|
84
|
+
"@pellux/goodvibes-contracts": "0.33.0",
|
|
85
|
+
"@pellux/goodvibes-errors": "0.33.0",
|
|
86
|
+
"@pellux/goodvibes-transport-core": "0.33.0",
|
|
85
87
|
"zod": "^4.3.6"
|
|
86
88
|
},
|
|
87
89
|
"publishConfig": {
|