@trpc/client 11.0.0-next-beta.205 → 11.0.0-next-beta.208
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{TRPCClientError-3414c3d5.mjs → TRPCClientError.mjs} +1 -1
- package/dist/bundle-analysis.json +61 -61
- package/dist/createTRPCClient.js +50 -0
- package/dist/createTRPCClient.mjs +45 -0
- package/dist/createTRPCUntypedClient.js +10 -0
- package/dist/createTRPCUntypedClient.mjs +7 -0
- package/dist/getFetch.js +17 -0
- package/dist/getFetch.mjs +15 -0
- package/dist/index.js +32 -368
- package/dist/index.mjs +9 -352
- package/dist/internals/TRPCUntypedClient.js +113 -0
- package/dist/internals/TRPCUntypedClient.mjs +111 -0
- package/dist/{httpBatchLink-0ee76b31.mjs → internals/dataLoader.js} +2 -117
- package/dist/{httpBatchLink-6c1c898e.js → internals/dataLoader.mjs} +1 -121
- package/dist/internals/getAbortController.js +18 -0
- package/dist/internals/getAbortController.mjs +16 -0
- package/dist/links/httpBatchLink.js +37 -9
- package/dist/links/httpBatchLink.mjs +39 -5
- package/dist/links/httpBatchStreamLink.js +43 -0
- package/dist/links/httpBatchStreamLink.mjs +41 -0
- package/dist/links/httpFormDataLink.js +31 -0
- package/dist/links/httpFormDataLink.mjs +29 -0
- package/dist/links/httpLink.js +2 -4
- package/dist/links/httpLink.mjs +2 -2
- package/dist/{splitLink-18238436.js → links/internals/createChain.js} +0 -22
- package/dist/{splitLink-13989f7f.mjs → links/internals/createChain.mjs} +1 -22
- package/dist/links/internals/createHTTPBatchLink.js +86 -0
- package/dist/links/internals/createHTTPBatchLink.mjs +84 -0
- package/dist/links/internals/getTextDecoder.js +18 -0
- package/dist/links/internals/getTextDecoder.mjs +16 -0
- package/dist/{httpUtils-60af4c3d.js → links/internals/httpUtils.js} +5 -33
- package/dist/{httpUtils-82ae6a64.mjs → links/internals/httpUtils.mjs} +4 -31
- package/dist/links/internals/parseJSONStream.js +118 -0
- package/dist/links/internals/parseJSONStream.mjs +115 -0
- package/dist/links/loggerLink.js +4 -2
- package/dist/links/loggerLink.mjs +4 -0
- package/dist/links/splitLink.js +23 -6
- package/dist/links/splitLink.mjs +25 -2
- package/dist/links/wsLink.js +1 -3
- package/dist/links/wsLink.mjs +1 -1
- package/package.json +4 -4
- package/dist/TRPCClientError-27d80214.js +0 -61
- package/dist/httpBatchLink-bc1c3273.js +0 -247
- package/dist/httpUtils-49fa3edc.js +0 -151
- package/dist/splitLink-bd4bf809.js +0 -41
- /package/dist/{TRPCClientError-67aefe1c.js → TRPCClientError.js} +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@trpc/core');
|
|
4
|
+
var observable = require('@trpc/core/observable');
|
|
5
|
+
var dataLoader = require('../../internals/dataLoader.js');
|
|
6
|
+
var TRPCClientError = require('../../TRPCClientError.js');
|
|
7
|
+
var httpUtils = require('./httpUtils.js');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @internal
|
|
11
|
+
*/ function createHTTPBatchLink(requester) {
|
|
12
|
+
return function httpBatchLink(opts) {
|
|
13
|
+
const resolvedOpts = httpUtils.resolveHTTPLinkOptions(opts);
|
|
14
|
+
const maxURLLength = opts.maxURLLength ?? Infinity;
|
|
15
|
+
// initialized config
|
|
16
|
+
return (runtime)=>{
|
|
17
|
+
const batchLoader = (type)=>{
|
|
18
|
+
const validate = (batchOps)=>{
|
|
19
|
+
if (maxURLLength === Infinity) {
|
|
20
|
+
// escape hatch for quick calcs
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
const path = batchOps.map((op)=>op.path).join(',');
|
|
24
|
+
const inputs = batchOps.map((op)=>op.input);
|
|
25
|
+
const url = httpUtils.getUrl({
|
|
26
|
+
...resolvedOpts,
|
|
27
|
+
runtime,
|
|
28
|
+
type,
|
|
29
|
+
path,
|
|
30
|
+
inputs
|
|
31
|
+
});
|
|
32
|
+
return url.length <= maxURLLength;
|
|
33
|
+
};
|
|
34
|
+
const fetch = requester({
|
|
35
|
+
...resolvedOpts,
|
|
36
|
+
runtime,
|
|
37
|
+
type,
|
|
38
|
+
opts
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
validate,
|
|
42
|
+
fetch
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
const query = dataLoader.dataLoader(batchLoader('query'));
|
|
46
|
+
const mutation = dataLoader.dataLoader(batchLoader('mutation'));
|
|
47
|
+
const subscription = dataLoader.dataLoader(batchLoader('subscription'));
|
|
48
|
+
const loaders = {
|
|
49
|
+
query,
|
|
50
|
+
subscription,
|
|
51
|
+
mutation
|
|
52
|
+
};
|
|
53
|
+
return ({ op })=>{
|
|
54
|
+
return observable.observable((observer)=>{
|
|
55
|
+
const loader = loaders[op.type];
|
|
56
|
+
const { promise , cancel } = loader.load(op);
|
|
57
|
+
let _res = undefined;
|
|
58
|
+
promise.then((res)=>{
|
|
59
|
+
_res = res;
|
|
60
|
+
const transformed = core.transformResult(res.json, runtime.transformer);
|
|
61
|
+
if (!transformed.ok) {
|
|
62
|
+
observer.error(TRPCClientError.TRPCClientError.from(transformed.error, {
|
|
63
|
+
meta: res.meta
|
|
64
|
+
}));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
observer.next({
|
|
68
|
+
context: res.meta,
|
|
69
|
+
result: transformed.result
|
|
70
|
+
});
|
|
71
|
+
observer.complete();
|
|
72
|
+
}).catch((err)=>{
|
|
73
|
+
observer.error(TRPCClientError.TRPCClientError.from(err, {
|
|
74
|
+
meta: _res?.meta
|
|
75
|
+
}));
|
|
76
|
+
});
|
|
77
|
+
return ()=>{
|
|
78
|
+
cancel();
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
exports.createHTTPBatchLink = createHTTPBatchLink;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { transformResult } from '@trpc/core';
|
|
2
|
+
import { observable } from '@trpc/core/observable';
|
|
3
|
+
import { dataLoader } from '../../internals/dataLoader.mjs';
|
|
4
|
+
import { TRPCClientError } from '../../TRPCClientError.mjs';
|
|
5
|
+
import { resolveHTTPLinkOptions, getUrl } from './httpUtils.mjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @internal
|
|
9
|
+
*/ function createHTTPBatchLink(requester) {
|
|
10
|
+
return function httpBatchLink(opts) {
|
|
11
|
+
const resolvedOpts = resolveHTTPLinkOptions(opts);
|
|
12
|
+
const maxURLLength = opts.maxURLLength ?? Infinity;
|
|
13
|
+
// initialized config
|
|
14
|
+
return (runtime)=>{
|
|
15
|
+
const batchLoader = (type)=>{
|
|
16
|
+
const validate = (batchOps)=>{
|
|
17
|
+
if (maxURLLength === Infinity) {
|
|
18
|
+
// escape hatch for quick calcs
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
const path = batchOps.map((op)=>op.path).join(',');
|
|
22
|
+
const inputs = batchOps.map((op)=>op.input);
|
|
23
|
+
const url = getUrl({
|
|
24
|
+
...resolvedOpts,
|
|
25
|
+
runtime,
|
|
26
|
+
type,
|
|
27
|
+
path,
|
|
28
|
+
inputs
|
|
29
|
+
});
|
|
30
|
+
return url.length <= maxURLLength;
|
|
31
|
+
};
|
|
32
|
+
const fetch = requester({
|
|
33
|
+
...resolvedOpts,
|
|
34
|
+
runtime,
|
|
35
|
+
type,
|
|
36
|
+
opts
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
validate,
|
|
40
|
+
fetch
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
const query = dataLoader(batchLoader('query'));
|
|
44
|
+
const mutation = dataLoader(batchLoader('mutation'));
|
|
45
|
+
const subscription = dataLoader(batchLoader('subscription'));
|
|
46
|
+
const loaders = {
|
|
47
|
+
query,
|
|
48
|
+
subscription,
|
|
49
|
+
mutation
|
|
50
|
+
};
|
|
51
|
+
return ({ op })=>{
|
|
52
|
+
return observable((observer)=>{
|
|
53
|
+
const loader = loaders[op.type];
|
|
54
|
+
const { promise , cancel } = loader.load(op);
|
|
55
|
+
let _res = undefined;
|
|
56
|
+
promise.then((res)=>{
|
|
57
|
+
_res = res;
|
|
58
|
+
const transformed = transformResult(res.json, runtime.transformer);
|
|
59
|
+
if (!transformed.ok) {
|
|
60
|
+
observer.error(TRPCClientError.from(transformed.error, {
|
|
61
|
+
meta: res.meta
|
|
62
|
+
}));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
observer.next({
|
|
66
|
+
context: res.meta,
|
|
67
|
+
result: transformed.result
|
|
68
|
+
});
|
|
69
|
+
observer.complete();
|
|
70
|
+
}).catch((err)=>{
|
|
71
|
+
observer.error(TRPCClientError.from(err, {
|
|
72
|
+
meta: _res?.meta
|
|
73
|
+
}));
|
|
74
|
+
});
|
|
75
|
+
return ()=>{
|
|
76
|
+
cancel();
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { createHTTPBatchLink };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function getTextDecoder(customTextDecoder) {
|
|
4
|
+
if (customTextDecoder) {
|
|
5
|
+
return customTextDecoder;
|
|
6
|
+
}
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
|
8
|
+
if (typeof window !== 'undefined' && window.TextDecoder) {
|
|
9
|
+
return new window.TextDecoder();
|
|
10
|
+
}
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
|
12
|
+
if (typeof globalThis !== 'undefined' && globalThis.TextDecoder) {
|
|
13
|
+
return new globalThis.TextDecoder();
|
|
14
|
+
}
|
|
15
|
+
throw new Error('No TextDecoder implementation found');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
exports.getTextDecoder = getTextDecoder;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function getTextDecoder(customTextDecoder) {
|
|
2
|
+
if (customTextDecoder) {
|
|
3
|
+
return customTextDecoder;
|
|
4
|
+
}
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
|
6
|
+
if (typeof window !== 'undefined' && window.TextDecoder) {
|
|
7
|
+
return new window.TextDecoder();
|
|
8
|
+
}
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
|
10
|
+
if (typeof globalThis !== 'undefined' && globalThis.TextDecoder) {
|
|
11
|
+
return new globalThis.TextDecoder();
|
|
12
|
+
}
|
|
13
|
+
throw new Error('No TextDecoder implementation found');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { getTextDecoder };
|
|
@@ -1,41 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
function getFetch(customFetchImpl) {
|
|
7
|
-
if (customFetchImpl) {
|
|
8
|
-
return customFetchImpl;
|
|
9
|
-
}
|
|
10
|
-
if (typeof window !== 'undefined' && isFunction(window.fetch)) {
|
|
11
|
-
return window.fetch;
|
|
12
|
-
}
|
|
13
|
-
if (typeof globalThis !== 'undefined' && isFunction(globalThis.fetch)) {
|
|
14
|
-
return globalThis.fetch;
|
|
15
|
-
}
|
|
16
|
-
throw new Error('No fetch implementation found');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function getAbortController(customAbortControllerImpl) {
|
|
20
|
-
if (customAbortControllerImpl) {
|
|
21
|
-
return customAbortControllerImpl;
|
|
22
|
-
}
|
|
23
|
-
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
|
24
|
-
if (typeof window !== 'undefined' && window.AbortController) {
|
|
25
|
-
return window.AbortController;
|
|
26
|
-
}
|
|
27
|
-
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
|
28
|
-
if (typeof globalThis !== 'undefined' && globalThis.AbortController) {
|
|
29
|
-
return globalThis.AbortController;
|
|
30
|
-
}
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
3
|
+
var getFetch = require('../../getFetch.js');
|
|
4
|
+
var getAbortController = require('../../internals/getAbortController.js');
|
|
5
|
+
var TRPCClientError = require('../../TRPCClientError.js');
|
|
33
6
|
|
|
34
7
|
function resolveHTTPLinkOptions(opts) {
|
|
35
8
|
return {
|
|
36
9
|
url: opts.url.toString().replace(/\/$/, ''),
|
|
37
10
|
fetch: opts.fetch,
|
|
38
|
-
AbortController: getAbortController(opts.AbortController)
|
|
11
|
+
AbortController: getAbortController.getAbortController(opts.AbortController)
|
|
39
12
|
};
|
|
40
13
|
}
|
|
41
14
|
// https://github.com/trpc/trpc/pull/669
|
|
@@ -109,7 +82,7 @@ async function fetchHTTPResponse(opts, ac) {
|
|
|
109
82
|
} : {},
|
|
110
83
|
...resolvedHeaders
|
|
111
84
|
};
|
|
112
|
-
return getFetch(opts.fetch)(url, {
|
|
85
|
+
return getFetch.getFetch(opts.fetch)(url, {
|
|
113
86
|
method: METHOD[type],
|
|
114
87
|
signal: ac?.signal,
|
|
115
88
|
body: body,
|
|
@@ -151,7 +124,6 @@ function httpRequest(opts) {
|
|
|
151
124
|
|
|
152
125
|
exports.fetchHTTPResponse = fetchHTTPResponse;
|
|
153
126
|
exports.getBody = getBody;
|
|
154
|
-
exports.getFetch = getFetch;
|
|
155
127
|
exports.getUrl = getUrl;
|
|
156
128
|
exports.httpRequest = httpRequest;
|
|
157
129
|
exports.jsonHttpRequester = jsonHttpRequester;
|
|
@@ -1,33 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
function getFetch(customFetchImpl) {
|
|
5
|
-
if (customFetchImpl) {
|
|
6
|
-
return customFetchImpl;
|
|
7
|
-
}
|
|
8
|
-
if (typeof window !== 'undefined' && isFunction(window.fetch)) {
|
|
9
|
-
return window.fetch;
|
|
10
|
-
}
|
|
11
|
-
if (typeof globalThis !== 'undefined' && isFunction(globalThis.fetch)) {
|
|
12
|
-
return globalThis.fetch;
|
|
13
|
-
}
|
|
14
|
-
throw new Error('No fetch implementation found');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function getAbortController(customAbortControllerImpl) {
|
|
18
|
-
if (customAbortControllerImpl) {
|
|
19
|
-
return customAbortControllerImpl;
|
|
20
|
-
}
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
|
22
|
-
if (typeof window !== 'undefined' && window.AbortController) {
|
|
23
|
-
return window.AbortController;
|
|
24
|
-
}
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
|
|
26
|
-
if (typeof globalThis !== 'undefined' && globalThis.AbortController) {
|
|
27
|
-
return globalThis.AbortController;
|
|
28
|
-
}
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
1
|
+
import { getFetch } from '../../getFetch.mjs';
|
|
2
|
+
import { getAbortController } from '../../internals/getAbortController.mjs';
|
|
3
|
+
import { TRPCClientError } from '../../TRPCClientError.mjs';
|
|
31
4
|
|
|
32
5
|
function resolveHTTPLinkOptions(opts) {
|
|
33
6
|
return {
|
|
@@ -147,4 +120,4 @@ function httpRequest(opts) {
|
|
|
147
120
|
};
|
|
148
121
|
}
|
|
149
122
|
|
|
150
|
-
export {
|
|
123
|
+
export { fetchHTTPResponse, getBody, getUrl, httpRequest, jsonHttpRequester, resolveHTTPLinkOptions };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var httpUtils = require('./httpUtils.js');
|
|
4
|
+
|
|
5
|
+
// Stream parsing adapted from https://www.loginradius.com/blog/engineering/guest-post/http-streaming-with-nodejs-and-fetch-api/
|
|
6
|
+
/**
|
|
7
|
+
* @internal
|
|
8
|
+
* @description Take a stream of bytes and call `onLine` with
|
|
9
|
+
* a JSON object for each line in the stream. Expected stream
|
|
10
|
+
* format is:
|
|
11
|
+
* ```json
|
|
12
|
+
* {"1": {...}
|
|
13
|
+
* ,"0": {...}
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/ async function parseJSONStream(opts) {
|
|
17
|
+
const parse = opts.parse ?? JSON.parse;
|
|
18
|
+
const onLine = (line)=>{
|
|
19
|
+
if (opts.signal?.aborted) return;
|
|
20
|
+
if (!line || line === '}') {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* At this point, `line` can be one of two things:
|
|
25
|
+
* - The first line of the stream `{"2":{...}`
|
|
26
|
+
* - A line in the middle of the stream `,"2":{...}`
|
|
27
|
+
*/ const indexOfColon = line.indexOf(':');
|
|
28
|
+
const indexAsStr = line.substring(2, indexOfColon - 1);
|
|
29
|
+
const text = line.substring(indexOfColon + 1);
|
|
30
|
+
opts.onSingle(Number(indexAsStr), parse(text));
|
|
31
|
+
};
|
|
32
|
+
await readLines(opts.readableStream, onLine, opts.textDecoder);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Handle transforming a stream of bytes into lines of text.
|
|
36
|
+
* To avoid using AsyncIterators / AsyncGenerators,
|
|
37
|
+
* we use a callback for each line.
|
|
38
|
+
*
|
|
39
|
+
* @param readableStream can be a NodeJS stream or a WebAPI stream
|
|
40
|
+
* @param onLine will be called for every line ('\n' delimited) in the stream
|
|
41
|
+
*/ async function readLines(readableStream, onLine, textDecoder) {
|
|
42
|
+
let partOfLine = '';
|
|
43
|
+
const onChunk = (chunk)=>{
|
|
44
|
+
const chunkText = textDecoder.decode(chunk);
|
|
45
|
+
const chunkLines = chunkText.split('\n');
|
|
46
|
+
if (chunkLines.length === 1) {
|
|
47
|
+
partOfLine += chunkLines[0];
|
|
48
|
+
} else if (chunkLines.length > 1) {
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- length checked on line above
|
|
50
|
+
onLine(partOfLine + chunkLines[0]);
|
|
51
|
+
for(let i = 1; i < chunkLines.length - 1; i++){
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- length checked on line above
|
|
53
|
+
onLine(chunkLines[i]);
|
|
54
|
+
}
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- length doesn't change, so is necessarily > 1
|
|
56
|
+
partOfLine = chunkLines[chunkLines.length - 1];
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
// we handle 2 different types of streams, this if where we figure out which one we have
|
|
60
|
+
if ('getReader' in readableStream) {
|
|
61
|
+
await readStandardChunks(readableStream, onChunk);
|
|
62
|
+
} else {
|
|
63
|
+
await readNodeChunks(readableStream, onChunk);
|
|
64
|
+
}
|
|
65
|
+
onLine(partOfLine);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Handle NodeJS stream
|
|
69
|
+
*/ function readNodeChunks(stream, onChunk) {
|
|
70
|
+
return new Promise((resolve)=>{
|
|
71
|
+
stream.on('data', onChunk);
|
|
72
|
+
stream.on('end', resolve);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Handle WebAPI stream
|
|
77
|
+
*/ async function readStandardChunks(stream, onChunk) {
|
|
78
|
+
const reader = stream.getReader();
|
|
79
|
+
let readResult = await reader.read();
|
|
80
|
+
while(!readResult.done){
|
|
81
|
+
onChunk(readResult.value);
|
|
82
|
+
readResult = await reader.read();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const streamingJsonHttpRequester = (opts, onSingle)=>{
|
|
86
|
+
const ac = opts.AbortController ? new opts.AbortController() : null;
|
|
87
|
+
const responsePromise = httpUtils.fetchHTTPResponse({
|
|
88
|
+
...opts,
|
|
89
|
+
contentTypeHeader: 'application/json',
|
|
90
|
+
batchModeHeader: 'stream',
|
|
91
|
+
getUrl: httpUtils.getUrl,
|
|
92
|
+
getBody: httpUtils.getBody
|
|
93
|
+
}, ac);
|
|
94
|
+
const cancel = ()=>ac?.abort();
|
|
95
|
+
const promise = responsePromise.then(async (res)=>{
|
|
96
|
+
if (!res.body) throw new Error('Received response without body');
|
|
97
|
+
const meta = {
|
|
98
|
+
response: res
|
|
99
|
+
};
|
|
100
|
+
return parseJSONStream({
|
|
101
|
+
readableStream: res.body,
|
|
102
|
+
onSingle,
|
|
103
|
+
parse: (string)=>({
|
|
104
|
+
json: JSON.parse(string),
|
|
105
|
+
meta
|
|
106
|
+
}),
|
|
107
|
+
signal: ac?.signal,
|
|
108
|
+
textDecoder: opts.textDecoder
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
cancel,
|
|
113
|
+
promise
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
exports.parseJSONStream = parseJSONStream;
|
|
118
|
+
exports.streamingJsonHttpRequester = streamingJsonHttpRequester;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { fetchHTTPResponse, getUrl, getBody } from './httpUtils.mjs';
|
|
2
|
+
|
|
3
|
+
// Stream parsing adapted from https://www.loginradius.com/blog/engineering/guest-post/http-streaming-with-nodejs-and-fetch-api/
|
|
4
|
+
/**
|
|
5
|
+
* @internal
|
|
6
|
+
* @description Take a stream of bytes and call `onLine` with
|
|
7
|
+
* a JSON object for each line in the stream. Expected stream
|
|
8
|
+
* format is:
|
|
9
|
+
* ```json
|
|
10
|
+
* {"1": {...}
|
|
11
|
+
* ,"0": {...}
|
|
12
|
+
* }
|
|
13
|
+
* ```
|
|
14
|
+
*/ async function parseJSONStream(opts) {
|
|
15
|
+
const parse = opts.parse ?? JSON.parse;
|
|
16
|
+
const onLine = (line)=>{
|
|
17
|
+
if (opts.signal?.aborted) return;
|
|
18
|
+
if (!line || line === '}') {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* At this point, `line` can be one of two things:
|
|
23
|
+
* - The first line of the stream `{"2":{...}`
|
|
24
|
+
* - A line in the middle of the stream `,"2":{...}`
|
|
25
|
+
*/ const indexOfColon = line.indexOf(':');
|
|
26
|
+
const indexAsStr = line.substring(2, indexOfColon - 1);
|
|
27
|
+
const text = line.substring(indexOfColon + 1);
|
|
28
|
+
opts.onSingle(Number(indexAsStr), parse(text));
|
|
29
|
+
};
|
|
30
|
+
await readLines(opts.readableStream, onLine, opts.textDecoder);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Handle transforming a stream of bytes into lines of text.
|
|
34
|
+
* To avoid using AsyncIterators / AsyncGenerators,
|
|
35
|
+
* we use a callback for each line.
|
|
36
|
+
*
|
|
37
|
+
* @param readableStream can be a NodeJS stream or a WebAPI stream
|
|
38
|
+
* @param onLine will be called for every line ('\n' delimited) in the stream
|
|
39
|
+
*/ async function readLines(readableStream, onLine, textDecoder) {
|
|
40
|
+
let partOfLine = '';
|
|
41
|
+
const onChunk = (chunk)=>{
|
|
42
|
+
const chunkText = textDecoder.decode(chunk);
|
|
43
|
+
const chunkLines = chunkText.split('\n');
|
|
44
|
+
if (chunkLines.length === 1) {
|
|
45
|
+
partOfLine += chunkLines[0];
|
|
46
|
+
} else if (chunkLines.length > 1) {
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- length checked on line above
|
|
48
|
+
onLine(partOfLine + chunkLines[0]);
|
|
49
|
+
for(let i = 1; i < chunkLines.length - 1; i++){
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- length checked on line above
|
|
51
|
+
onLine(chunkLines[i]);
|
|
52
|
+
}
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- length doesn't change, so is necessarily > 1
|
|
54
|
+
partOfLine = chunkLines[chunkLines.length - 1];
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
// we handle 2 different types of streams, this if where we figure out which one we have
|
|
58
|
+
if ('getReader' in readableStream) {
|
|
59
|
+
await readStandardChunks(readableStream, onChunk);
|
|
60
|
+
} else {
|
|
61
|
+
await readNodeChunks(readableStream, onChunk);
|
|
62
|
+
}
|
|
63
|
+
onLine(partOfLine);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Handle NodeJS stream
|
|
67
|
+
*/ function readNodeChunks(stream, onChunk) {
|
|
68
|
+
return new Promise((resolve)=>{
|
|
69
|
+
stream.on('data', onChunk);
|
|
70
|
+
stream.on('end', resolve);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Handle WebAPI stream
|
|
75
|
+
*/ async function readStandardChunks(stream, onChunk) {
|
|
76
|
+
const reader = stream.getReader();
|
|
77
|
+
let readResult = await reader.read();
|
|
78
|
+
while(!readResult.done){
|
|
79
|
+
onChunk(readResult.value);
|
|
80
|
+
readResult = await reader.read();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const streamingJsonHttpRequester = (opts, onSingle)=>{
|
|
84
|
+
const ac = opts.AbortController ? new opts.AbortController() : null;
|
|
85
|
+
const responsePromise = fetchHTTPResponse({
|
|
86
|
+
...opts,
|
|
87
|
+
contentTypeHeader: 'application/json',
|
|
88
|
+
batchModeHeader: 'stream',
|
|
89
|
+
getUrl,
|
|
90
|
+
getBody
|
|
91
|
+
}, ac);
|
|
92
|
+
const cancel = ()=>ac?.abort();
|
|
93
|
+
const promise = responsePromise.then(async (res)=>{
|
|
94
|
+
if (!res.body) throw new Error('Received response without body');
|
|
95
|
+
const meta = {
|
|
96
|
+
response: res
|
|
97
|
+
};
|
|
98
|
+
return parseJSONStream({
|
|
99
|
+
readableStream: res.body,
|
|
100
|
+
onSingle,
|
|
101
|
+
parse: (string)=>({
|
|
102
|
+
json: JSON.parse(string),
|
|
103
|
+
meta
|
|
104
|
+
}),
|
|
105
|
+
signal: ac?.signal,
|
|
106
|
+
textDecoder: opts.textDecoder
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
cancel,
|
|
111
|
+
promise
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export { parseJSONStream, streamingJsonHttpRequester };
|
package/dist/links/loggerLink.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
3
|
var observable = require('@trpc/core/observable');
|
|
6
4
|
|
|
7
5
|
/// <reference lib="dom.iterable" />
|
|
6
|
+
// `dom.iterable` types are explicitly required for extracting `FormData` values,
|
|
7
|
+
// as all implementations of `Symbol.iterable` are separated from the main `dom` types.
|
|
8
|
+
// Using triple-slash directive makes sure that it will be available,
|
|
9
|
+
// even if end-user `tsconfig.json` omits it in the `lib` array.
|
|
8
10
|
function isFormData(value) {
|
|
9
11
|
if (typeof FormData === 'undefined') {
|
|
10
12
|
// FormData is not supported
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { observable, tap } from '@trpc/core/observable';
|
|
2
2
|
|
|
3
3
|
/// <reference lib="dom.iterable" />
|
|
4
|
+
// `dom.iterable` types are explicitly required for extracting `FormData` values,
|
|
5
|
+
// as all implementations of `Symbol.iterable` are separated from the main `dom` types.
|
|
6
|
+
// Using triple-slash directive makes sure that it will be available,
|
|
7
|
+
// even if end-user `tsconfig.json` omits it in the `lib` array.
|
|
4
8
|
function isFormData(value) {
|
|
5
9
|
if (typeof FormData === 'undefined') {
|
|
6
10
|
// FormData is not supported
|
package/dist/links/splitLink.js
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
var observable = require('@trpc/core/observable');
|
|
4
|
+
var createChain = require('./internals/createChain.js');
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
function asArray(value) {
|
|
7
|
+
return Array.isArray(value) ? value : [
|
|
8
|
+
value
|
|
9
|
+
];
|
|
10
|
+
}
|
|
11
|
+
function splitLink(opts) {
|
|
12
|
+
return (runtime)=>{
|
|
13
|
+
const yes = asArray(opts.true).map((link)=>link(runtime));
|
|
14
|
+
const no = asArray(opts.false).map((link)=>link(runtime));
|
|
15
|
+
return (props)=>{
|
|
16
|
+
return observable.observable((observer)=>{
|
|
17
|
+
const links = opts.condition(props.op) ? yes : no;
|
|
18
|
+
return createChain.createChain({
|
|
19
|
+
op: props.op,
|
|
20
|
+
links
|
|
21
|
+
}).subscribe(observer);
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
}
|
|
7
26
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
exports.splitLink = links_splitLink.splitLink;
|
|
27
|
+
exports.splitLink = splitLink;
|
package/dist/links/splitLink.mjs
CHANGED
|
@@ -1,2 +1,25 @@
|
|
|
1
|
-
import '@trpc/core/observable';
|
|
2
|
-
|
|
1
|
+
import { observable } from '@trpc/core/observable';
|
|
2
|
+
import { createChain } from './internals/createChain.mjs';
|
|
3
|
+
|
|
4
|
+
function asArray(value) {
|
|
5
|
+
return Array.isArray(value) ? value : [
|
|
6
|
+
value
|
|
7
|
+
];
|
|
8
|
+
}
|
|
9
|
+
function splitLink(opts) {
|
|
10
|
+
return (runtime)=>{
|
|
11
|
+
const yes = asArray(opts.true).map((link)=>link(runtime));
|
|
12
|
+
const no = asArray(opts.false).map((link)=>link(runtime));
|
|
13
|
+
return (props)=>{
|
|
14
|
+
return observable((observer)=>{
|
|
15
|
+
const links = opts.condition(props.op) ? yes : no;
|
|
16
|
+
return createChain({
|
|
17
|
+
op: props.op,
|
|
18
|
+
links
|
|
19
|
+
}).subscribe(observer);
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { splitLink };
|
package/dist/links/wsLink.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
3
|
var core = require('@trpc/core');
|
|
6
4
|
var observable = require('@trpc/core/observable');
|
|
7
|
-
var TRPCClientError = require('../TRPCClientError
|
|
5
|
+
var TRPCClientError = require('../TRPCClientError.js');
|
|
8
6
|
|
|
9
7
|
const run = (fn)=>fn();
|
|
10
8
|
const exponentialBackoff = (attemptIndex)=>attemptIndex === 0 ? 0 : Math.min(1000 * 2 ** attemptIndex, 30000);
|
package/dist/links/wsLink.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { transformResult } from '@trpc/core';
|
|
2
2
|
import { observable } from '@trpc/core/observable';
|
|
3
|
-
import {
|
|
3
|
+
import { TRPCClientError } from '../TRPCClientError.mjs';
|
|
4
4
|
|
|
5
5
|
const run = (fn)=>fn();
|
|
6
6
|
const exponentialBackoff = (attemptIndex)=>attemptIndex === 0 ? 0 : Math.min(1000 * 2 ** attemptIndex, 30000);
|