@trpc/client 10.0.0-alpha.10 → 10.0.0-alpha.13
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/{httpUtils-1f9e4e3c.js → httpUtils-27640b42.js} +0 -0
- package/dist/{httpUtils-c09d7b79.mjs → httpUtils-b92552af.mjs} +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +215 -18
- package/dist/index.mjs +198 -13
- package/dist/internals/TRPCClient.d.ts +4 -2
- package/dist/internals/TRPCClient.d.ts.map +1 -1
- package/dist/links/dedupeLink.d.ts.map +1 -1
- package/dist/links/httpBatchLink.d.ts +1 -1
- package/dist/links/httpBatchLink.d.ts.map +1 -1
- package/dist/links/httpBatchLink.js +3 -3
- package/dist/links/httpBatchLink.mjs +3 -3
- package/dist/links/httpLink.d.ts +1 -1
- package/dist/links/httpLink.d.ts.map +1 -1
- package/dist/links/httpLink.js +3 -3
- package/dist/links/httpLink.mjs +3 -3
- package/dist/links/index.d.ts +11 -0
- package/dist/links/index.d.ts.map +1 -0
- package/dist/links/{httpUtils.d.ts → internals/httpUtils.d.ts} +1 -1
- package/dist/links/internals/httpUtils.d.ts.map +1 -0
- package/dist/links/loggerLink.d.ts.map +1 -1
- package/dist/links/loggerLink.js +4 -3
- package/dist/links/loggerLink.mjs +3 -2
- package/dist/links/retryLink.d.ts.map +1 -1
- package/dist/links/splitLink.js +3 -25
- package/dist/links/splitLink.mjs +2 -28
- package/dist/links/transformerLink.js +2 -2
- package/dist/links/transformerLink.mjs +2 -2
- package/dist/links/types.d.ts +4 -3
- package/dist/links/types.d.ts.map +1 -1
- package/dist/links/wsLink.d.ts +1 -2
- package/dist/links/wsLink.d.ts.map +1 -1
- package/dist/links/wsLink.js +11 -377
- package/dist/links/wsLink.mjs +9 -369
- package/dist/observable/index.d.ts +4 -0
- package/dist/observable/index.d.ts.map +1 -0
- package/dist/observable/index.js +40 -0
- package/dist/observable/index.mjs +33 -0
- package/dist/{rx.ts.js → observable/index.ts.js} +0 -0
- package/dist/observable/observable.d.ts +4 -0
- package/dist/observable/observable.d.ts.map +1 -0
- package/dist/observable/operators/index.d.ts +4 -0
- package/dist/observable/operators/index.d.ts.map +1 -0
- package/dist/observable/operators/map.d.ts +3 -0
- package/dist/observable/operators/map.d.ts.map +1 -0
- package/dist/observable/operators/share.d.ts +6 -0
- package/dist/observable/operators/share.d.ts.map +1 -0
- package/dist/observable/operators/tap.d.ts +3 -0
- package/dist/observable/operators/tap.d.ts.map +1 -0
- package/dist/observable/types.d.ts +29 -0
- package/dist/observable/types.d.ts.map +1 -0
- package/dist/observable/util/identity.d.ts +2 -0
- package/dist/observable/util/identity.d.ts.map +1 -0
- package/dist/observable/util/observableToPromise.d.ts +10 -0
- package/dist/observable/util/observableToPromise.d.ts.map +1 -0
- package/dist/observable/util/pipe.d.ts +4 -0
- package/dist/observable/util/pipe.d.ts.map +1 -0
- package/dist/observable-16fde241.mjs +102 -0
- package/dist/observable-9aaca086.js +104 -0
- package/dist/share-4afc084f.js +110 -0
- package/dist/share-9f12d26c.mjs +108 -0
- package/dist/splitLink-8635d455.js +52 -0
- package/dist/splitLink-a736378a.mjs +49 -0
- package/dist/tap-aacf31bf.js +32 -0
- package/dist/tap-f567ea97.mjs +30 -0
- package/dist/wsLink-a6a0f3a8.mjs +440 -0
- package/dist/wsLink-c9323097.js +455 -0
- package/package.json +11 -41
- package/src/index.ts +1 -1
- package/src/internals/TRPCClient.ts +8 -5
- package/src/links/dedupeLink.test.ts +3 -3
- package/src/links/dedupeLink.ts +27 -28
- package/src/links/httpBatchLink.ts +7 -3
- package/src/links/httpLink.ts +3 -3
- package/src/links/index.ts +10 -0
- package/src/links/internals/createChain.test.ts +7 -7
- package/src/links/internals/createChain.ts +4 -4
- package/src/links/{httpUtils.ts → internals/httpUtils.ts} +1 -1
- package/src/links/loggerLink.ts +3 -2
- package/src/links/retryLink.ts +3 -2
- package/src/links/splitLink.test.ts +3 -3
- package/src/links/splitLink.ts +2 -2
- package/src/links/transformerLink.ts +2 -2
- package/src/links/types.ts +4 -3
- package/src/links/wsLink.ts +3 -4
- package/src/observable/index.ts +3 -0
- package/src/observable/observable.test.ts +103 -0
- package/src/observable/observable.ts +70 -0
- package/src/observable/operators/index.ts +3 -0
- package/src/observable/operators/map.test.ts +92 -0
- package/src/observable/operators/map.ts +25 -0
- package/src/observable/operators/share.test.ts +60 -0
- package/src/observable/operators/share.ts +67 -0
- package/src/observable/operators/tap.ts +26 -0
- package/src/observable/types.ts +69 -0
- package/src/observable/util/identity.ts +3 -0
- package/src/{rx.ts → observable/util/observableToPromise.ts} +6 -7
- package/src/observable/util/pipe.ts +22 -0
- package/dist/TRPCClientError-17b117e1.mjs +0 -81
- package/dist/TRPCClientError-ec6bd2ca.js +0 -94
- package/dist/createChain-3b458753.js +0 -27
- package/dist/createChain-b7cb1f5a.mjs +0 -25
- package/dist/links/httpUtils.d.ts.map +0 -1
- package/dist/rx.d.ts +0 -11
- package/dist/rx.d.ts.map +0 -1
- package/dist/rx.js +0 -88
- package/dist/rx.mjs +0 -73
- package/links/httpBatchLink/dist/trpc-client-links-httpBatchLink.cjs.d.ts +0 -1
- package/links/httpBatchLink/dist/trpc-client-links-httpBatchLink.cjs.dev.js +0 -214
- package/links/httpBatchLink/dist/trpc-client-links-httpBatchLink.cjs.js +0 -7
- package/links/httpBatchLink/dist/trpc-client-links-httpBatchLink.cjs.prod.js +0 -214
- package/links/httpBatchLink/dist/trpc-client-links-httpBatchLink.esm.js +0 -210
- package/links/httpBatchLink/index.d.ts +0 -1
- package/links/httpBatchLink/index.js +0 -1
- package/links/httpLink/dist/trpc-client-links-httpLink.cjs.d.ts +0 -1
- package/links/httpLink/dist/trpc-client-links-httpLink.cjs.dev.js +0 -67
- package/links/httpLink/dist/trpc-client-links-httpLink.cjs.js +0 -7
- package/links/httpLink/dist/trpc-client-links-httpLink.cjs.prod.js +0 -67
- package/links/httpLink/dist/trpc-client-links-httpLink.esm.js +0 -63
- package/links/httpLink/index.d.ts +0 -1
- package/links/httpLink/index.js +0 -1
- package/links/loggerLink/dist/trpc-client-links-loggerLink.cjs.d.ts +0 -1
- package/links/loggerLink/dist/trpc-client-links-loggerLink.cjs.dev.js +0 -89
- package/links/loggerLink/dist/trpc-client-links-loggerLink.cjs.js +0 -7
- package/links/loggerLink/dist/trpc-client-links-loggerLink.cjs.prod.js +0 -89
- package/links/loggerLink/dist/trpc-client-links-loggerLink.esm.js +0 -85
- package/links/loggerLink/index.d.ts +0 -1
- package/links/loggerLink/index.js +0 -1
- package/links/splitLink/dist/trpc-client-links-splitLink.cjs.d.ts +0 -1
- package/links/splitLink/dist/trpc-client-links-splitLink.cjs.dev.js +0 -19
- package/links/splitLink/dist/trpc-client-links-splitLink.cjs.js +0 -7
- package/links/splitLink/dist/trpc-client-links-splitLink.cjs.prod.js +0 -19
- package/links/splitLink/dist/trpc-client-links-splitLink.esm.js +0 -15
- package/links/splitLink/index.d.ts +0 -1
- package/links/splitLink/index.js +0 -1
- package/links/transformerLink/dist/trpc-client-links-transformerLink.cjs.d.ts +0 -1
- package/links/transformerLink/dist/trpc-client-links-transformerLink.cjs.dev.js +0 -67
- package/links/transformerLink/dist/trpc-client-links-transformerLink.cjs.js +0 -7
- package/links/transformerLink/dist/trpc-client-links-transformerLink.cjs.prod.js +0 -67
- package/links/transformerLink/dist/trpc-client-links-transformerLink.esm.js +0 -63
- package/links/transformerLink/index.d.ts +0 -1
- package/links/transformerLink/index.js +0 -1
- package/links/wsLink/dist/trpc-client-links-wsLink.cjs.d.ts +0 -1
- package/links/wsLink/dist/trpc-client-links-wsLink.cjs.dev.js +0 -395
- package/links/wsLink/dist/trpc-client-links-wsLink.cjs.js +0 -7
- package/links/wsLink/dist/trpc-client-links-wsLink.cjs.prod.js +0 -395
- package/links/wsLink/dist/trpc-client-links-wsLink.esm.js +0 -390
- package/links/wsLink/index.d.ts +0 -1
- package/links/wsLink/index.js +0 -1
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { AnyRouter, ProcedureType } from '@trpc/server';
|
|
2
2
|
import { dataLoader } from '../internals/dataLoader';
|
|
3
|
-
import {
|
|
3
|
+
import { observable } from '../observable/observable';
|
|
4
4
|
import { TRPCLink } from './types';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
HTTPLinkOptions,
|
|
7
|
+
httpRequest,
|
|
8
|
+
ResponseShape,
|
|
9
|
+
} from './internals/httpUtils';
|
|
6
10
|
|
|
7
11
|
export interface HttpBatchLinkOptions extends HTTPLinkOptions {
|
|
8
12
|
maxBatchSize?: number;
|
|
@@ -52,7 +56,7 @@ export function httpBatchLink<TRouter extends AnyRouter>(
|
|
|
52
56
|
|
|
53
57
|
const loaders = { query, subscription, mutation };
|
|
54
58
|
return ({ op }) => {
|
|
55
|
-
return
|
|
59
|
+
return observable((observer) => {
|
|
56
60
|
const loader = loaders[op.type];
|
|
57
61
|
const { promise, cancel } = loader.load(op);
|
|
58
62
|
|
package/src/links/httpLink.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AnyRouter } from '@trpc/server';
|
|
2
|
-
import {
|
|
2
|
+
import { observable } from '../observable/observable';
|
|
3
3
|
import { TRPCLink } from './types';
|
|
4
|
-
import { HTTPLinkOptions, httpRequest } from './httpUtils';
|
|
4
|
+
import { HTTPLinkOptions, httpRequest } from './internals/httpUtils';
|
|
5
5
|
|
|
6
6
|
export function httpLink<TRouter extends AnyRouter>(
|
|
7
7
|
opts: HTTPLinkOptions,
|
|
@@ -9,7 +9,7 @@ export function httpLink<TRouter extends AnyRouter>(
|
|
|
9
9
|
const { url } = opts;
|
|
10
10
|
return (runtime) =>
|
|
11
11
|
({ op }) =>
|
|
12
|
-
|
|
12
|
+
observable((observer) => {
|
|
13
13
|
const { path, input, type } = op;
|
|
14
14
|
const { promise, cancel } = httpRequest({
|
|
15
15
|
runtime,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './dedupeLink';
|
|
2
|
+
export * from './httpBatchLink';
|
|
3
|
+
export * from './httpLink';
|
|
4
|
+
export * from './loggerLink';
|
|
5
|
+
export * from './loggerLink';
|
|
6
|
+
export * from './retryLink';
|
|
7
|
+
export * from './splitLink';
|
|
8
|
+
export * from './transformerLink';
|
|
9
|
+
export * from './wsLink';
|
|
10
|
+
export * from './types';
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { AnyRouter } from '@trpc/server/src';
|
|
2
2
|
import { createChain } from './createChain';
|
|
3
|
-
import {
|
|
3
|
+
import { observable } from '../../observable/observable';
|
|
4
4
|
|
|
5
5
|
describe('chain', () => {
|
|
6
6
|
test('trivial', () => {
|
|
7
7
|
const result$ = createChain<AnyRouter, unknown, unknown>({
|
|
8
8
|
links: [
|
|
9
9
|
({ next, op }) => {
|
|
10
|
-
return
|
|
10
|
+
return observable((observer) => {
|
|
11
11
|
const subscription = next(op).subscribe(observer);
|
|
12
12
|
return () => {
|
|
13
13
|
subscription.unsubscribe();
|
|
@@ -15,7 +15,7 @@ describe('chain', () => {
|
|
|
15
15
|
});
|
|
16
16
|
},
|
|
17
17
|
({ op }) => {
|
|
18
|
-
return
|
|
18
|
+
return observable((observer) => {
|
|
19
19
|
observer.next({
|
|
20
20
|
context: {},
|
|
21
21
|
data: {
|
|
@@ -51,7 +51,7 @@ describe('chain', () => {
|
|
|
51
51
|
const result$ = createChain<AnyRouter, unknown, unknown>({
|
|
52
52
|
links: [
|
|
53
53
|
({ next, op }) => {
|
|
54
|
-
return
|
|
54
|
+
return observable((observer) => {
|
|
55
55
|
observer.next({
|
|
56
56
|
context: {},
|
|
57
57
|
data: {
|
|
@@ -62,14 +62,14 @@ describe('chain', () => {
|
|
|
62
62
|
},
|
|
63
63
|
},
|
|
64
64
|
});
|
|
65
|
-
const
|
|
65
|
+
const subscription = next(op).subscribe(observer);
|
|
66
66
|
return () => {
|
|
67
|
-
|
|
67
|
+
subscription.unsubscribe();
|
|
68
68
|
};
|
|
69
69
|
});
|
|
70
70
|
},
|
|
71
71
|
({ op }) => {
|
|
72
|
-
return
|
|
72
|
+
return observable((observer) => {
|
|
73
73
|
observer.next({
|
|
74
74
|
data: {
|
|
75
75
|
id: null,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AnyRouter } from '@trpc/server';
|
|
2
|
-
import {
|
|
2
|
+
import { observable } from '../../observable/observable';
|
|
3
3
|
import { Operation, OperationLink, OperationResultObservable } from '../types';
|
|
4
4
|
|
|
5
5
|
/** @internal */
|
|
@@ -11,10 +11,10 @@ export function createChain<
|
|
|
11
11
|
links: OperationLink<TRouter, TInput, TOutput>[];
|
|
12
12
|
op: Operation<TInput>;
|
|
13
13
|
}): OperationResultObservable<TRouter, TOutput> {
|
|
14
|
-
return
|
|
14
|
+
return observable((observer) => {
|
|
15
15
|
function execute(index = 0, op = opts.op) {
|
|
16
16
|
const next = opts.links[index];
|
|
17
|
-
const
|
|
17
|
+
const subscription = next({
|
|
18
18
|
op,
|
|
19
19
|
next(nextOp) {
|
|
20
20
|
const nextObserver = execute(index + 1, nextOp);
|
|
@@ -22,7 +22,7 @@ export function createChain<
|
|
|
22
22
|
return nextObserver;
|
|
23
23
|
},
|
|
24
24
|
});
|
|
25
|
-
return
|
|
25
|
+
return subscription;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const obs$ = execute();
|
package/src/links/loggerLink.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AnyRouter } from '@trpc/server';
|
|
2
2
|
import { TRPCClientError } from '..';
|
|
3
|
-
import {
|
|
3
|
+
import { observable } from '../observable/observable';
|
|
4
|
+
import { tap } from '../observable/operators';
|
|
4
5
|
import { Operation, OperationResult, TRPCLink } from './types';
|
|
5
6
|
|
|
6
7
|
type ConsoleEsque = {
|
|
@@ -107,7 +108,7 @@ export function loggerLink<TRouter extends AnyRouter = AnyRouter>(
|
|
|
107
108
|
|
|
108
109
|
return () => {
|
|
109
110
|
return ({ op, next }) => {
|
|
110
|
-
return
|
|
111
|
+
return observable((observer) => {
|
|
111
112
|
// ->
|
|
112
113
|
enabled({ ...op, direction: 'up' }) &&
|
|
113
114
|
logger({
|
package/src/links/retryLink.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AnyRouter } from '@trpc/server';
|
|
2
|
-
import {
|
|
2
|
+
import { observable } from '../observable/observable';
|
|
3
|
+
import { Unsubscribable } from '../observable/types';
|
|
3
4
|
import { TRPCLink } from './types';
|
|
4
5
|
|
|
5
6
|
export function retryLink<TRouter extends AnyRouter = AnyRouter>(opts: {
|
|
@@ -10,7 +11,7 @@ export function retryLink<TRouter extends AnyRouter = AnyRouter>(opts: {
|
|
|
10
11
|
// initialized in app
|
|
11
12
|
return ({ op, next }) => {
|
|
12
13
|
// initialized for request
|
|
13
|
-
return
|
|
14
|
+
return observable((observer) => {
|
|
14
15
|
let next$: Unsubscribable | null = null;
|
|
15
16
|
let attempts = 0;
|
|
16
17
|
let isDone = false;
|
|
@@ -3,17 +3,17 @@ import { AnyRouter } from '../../../server/src';
|
|
|
3
3
|
import { createChain } from '../links/internals/createChain';
|
|
4
4
|
import { splitLink } from '../links/splitLink';
|
|
5
5
|
import { OperationLink, TRPCLink } from '../links/types';
|
|
6
|
-
import {
|
|
6
|
+
import { observable } from '../observable';
|
|
7
7
|
|
|
8
8
|
test('splitLink', () => {
|
|
9
9
|
const wsLinkSpy = jest.fn();
|
|
10
10
|
const wsLink: TRPCLink<any> = () => () =>
|
|
11
|
-
|
|
11
|
+
observable(() => {
|
|
12
12
|
wsLinkSpy();
|
|
13
13
|
});
|
|
14
14
|
const httpLinkSpy = jest.fn();
|
|
15
15
|
const httpLink: TRPCLink<any> = () => () =>
|
|
16
|
-
|
|
16
|
+
observable(() => {
|
|
17
17
|
httpLinkSpy();
|
|
18
18
|
});
|
|
19
19
|
const links: OperationLink<AnyRouter, any, any>[] = [
|
package/src/links/splitLink.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AnyRouter } from '@trpc/server';
|
|
2
|
-
import {
|
|
2
|
+
import { observable } from '../observable/observable';
|
|
3
3
|
import { TRPCLink, Operation } from './types';
|
|
4
4
|
import { createChain } from './internals/createChain';
|
|
5
5
|
|
|
@@ -21,7 +21,7 @@ export function splitLink<TRouter extends AnyRouter = AnyRouter>(opts: {
|
|
|
21
21
|
const yes = asArray(opts.true).map((link) => link(rt));
|
|
22
22
|
const no = asArray(opts.false).map((link) => link(rt));
|
|
23
23
|
return (props) => {
|
|
24
|
-
return
|
|
24
|
+
return observable((observer) => {
|
|
25
25
|
const links = opts.condition(props.op) ? yes : no;
|
|
26
26
|
return createChain({ op: props.op, links }).subscribe(observer);
|
|
27
27
|
});
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
ClientDataTransformerOptions,
|
|
4
4
|
DataTransformer,
|
|
5
5
|
} from '@trpc/server';
|
|
6
|
-
import {
|
|
6
|
+
import { observable } from '../observable/observable';
|
|
7
7
|
import { OperationResult, TRPCLink } from './types';
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -54,7 +54,7 @@ export function transformerLink<TRouter extends AnyRouter = AnyRouter>(
|
|
|
54
54
|
return () => {
|
|
55
55
|
return (props) => {
|
|
56
56
|
const input = _transformer.serialize(props.op.input);
|
|
57
|
-
return
|
|
57
|
+
return observable((observer) => {
|
|
58
58
|
const subscription = props.next({ ...props.op, input }).subscribe({
|
|
59
59
|
next(value) {
|
|
60
60
|
const transformed = transformOperationResult(value, _transformer);
|
package/src/links/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AnyRouter, inferRouterError, Dict } from '@trpc/server';
|
|
2
2
|
import { TRPCResponse } from '@trpc/server/rpc';
|
|
3
|
-
import { Observable, Observer } from '
|
|
3
|
+
import { Observable, Observer } from '../observable/types';
|
|
4
|
+
import { TRPCClientError } from '../TRPCClientError';
|
|
4
5
|
|
|
5
6
|
export type CancelFn = () => void;
|
|
6
7
|
|
|
@@ -43,12 +44,12 @@ export interface OperationResult<TRouter extends AnyRouter, TOutput> {
|
|
|
43
44
|
export type OperationResultObservable<
|
|
44
45
|
TRouter extends AnyRouter,
|
|
45
46
|
TOutput,
|
|
46
|
-
> = Observable<OperationResult<TRouter, TOutput>>;
|
|
47
|
+
> = Observable<OperationResult<TRouter, TOutput>, TRPCClientError<TRouter>>;
|
|
47
48
|
|
|
48
49
|
export type OperationResultObserver<
|
|
49
50
|
TRouter extends AnyRouter,
|
|
50
51
|
TOutput,
|
|
51
|
-
> = Observer<OperationResult<TRouter, TOutput>>;
|
|
52
|
+
> = Observer<OperationResult<TRouter, TOutput>, TRPCClientError<TRouter>>;
|
|
52
53
|
|
|
53
54
|
export type OperationLink<
|
|
54
55
|
TRouter extends AnyRouter,
|
package/src/links/wsLink.ts
CHANGED
|
@@ -6,12 +6,11 @@ import {
|
|
|
6
6
|
TRPCResponse,
|
|
7
7
|
} from '@trpc/server/rpc';
|
|
8
8
|
import { retryDelay } from '../internals/retryDelay';
|
|
9
|
-
|
|
10
|
-
import {
|
|
9
|
+
import { UnsubscribeFn } from '../observable';
|
|
10
|
+
import { observable } from '../observable/observable';
|
|
11
11
|
import { TRPCClientError } from '../TRPCClientError';
|
|
12
12
|
import { Operation, OperationResultObserver, TRPCLink } from './types';
|
|
13
13
|
|
|
14
|
-
type UnsubscribeFn = () => void;
|
|
15
14
|
export interface WebSocketClientOptions {
|
|
16
15
|
url: string;
|
|
17
16
|
WebSocket?: WebSocket;
|
|
@@ -283,7 +282,7 @@ export function wsLink<TRouter extends AnyRouter>(
|
|
|
283
282
|
return () => {
|
|
284
283
|
const { client } = opts;
|
|
285
284
|
return ({ op }) => {
|
|
286
|
-
return
|
|
285
|
+
return observable((observer) => {
|
|
287
286
|
const { type, input, path, id, context } = op;
|
|
288
287
|
|
|
289
288
|
let isDone = false;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
2
|
+
import { observable } from './observable';
|
|
3
|
+
import { tap } from './operators';
|
|
4
|
+
import { share } from './operators/share';
|
|
5
|
+
|
|
6
|
+
test('vanilla observable - complete()', () => {
|
|
7
|
+
const obs = observable<number, Error>((observer) => {
|
|
8
|
+
observer.next(1);
|
|
9
|
+
observer.complete();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const next = jest.fn();
|
|
13
|
+
const error = jest.fn();
|
|
14
|
+
const complete = jest.fn();
|
|
15
|
+
obs.subscribe({
|
|
16
|
+
next,
|
|
17
|
+
error,
|
|
18
|
+
complete,
|
|
19
|
+
});
|
|
20
|
+
expect(next.mock.calls).toHaveLength(1);
|
|
21
|
+
expect(complete.mock.calls).toHaveLength(1);
|
|
22
|
+
expect(error.mock.calls).toHaveLength(0);
|
|
23
|
+
expect(next.mock.calls[0][0]).toBe(1);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('vanilla observable - unsubscribe()', () => {
|
|
27
|
+
const obs$ = observable<number, Error>((observer) => {
|
|
28
|
+
observer.next(1);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const next = jest.fn();
|
|
32
|
+
const error = jest.fn();
|
|
33
|
+
const complete = jest.fn();
|
|
34
|
+
const sub = obs$.subscribe({
|
|
35
|
+
next,
|
|
36
|
+
error,
|
|
37
|
+
complete,
|
|
38
|
+
});
|
|
39
|
+
sub.unsubscribe();
|
|
40
|
+
expect(next.mock.calls).toHaveLength(1);
|
|
41
|
+
expect(complete.mock.calls).toHaveLength(0);
|
|
42
|
+
expect(error.mock.calls).toHaveLength(0);
|
|
43
|
+
expect(next.mock.calls[0][0]).toBe(1);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('pipe - combine operators', () => {
|
|
47
|
+
const taps = {
|
|
48
|
+
next: jest.fn(),
|
|
49
|
+
complete: jest.fn(),
|
|
50
|
+
error: jest.fn(),
|
|
51
|
+
};
|
|
52
|
+
const obs = observable<number, Error>((observer) => {
|
|
53
|
+
observer.next(1);
|
|
54
|
+
}).pipe(
|
|
55
|
+
// operators:
|
|
56
|
+
share(),
|
|
57
|
+
tap(taps),
|
|
58
|
+
);
|
|
59
|
+
{
|
|
60
|
+
const next = jest.fn();
|
|
61
|
+
const error = jest.fn();
|
|
62
|
+
const complete = jest.fn();
|
|
63
|
+
obs.subscribe({
|
|
64
|
+
next,
|
|
65
|
+
error,
|
|
66
|
+
complete,
|
|
67
|
+
});
|
|
68
|
+
expect(next.mock.calls).toHaveLength(1);
|
|
69
|
+
expect(complete.mock.calls).toHaveLength(0);
|
|
70
|
+
expect(error.mock.calls).toHaveLength(0);
|
|
71
|
+
expect(next.mock.calls[0][0]).toBe(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
{
|
|
75
|
+
const next = jest.fn();
|
|
76
|
+
const error = jest.fn();
|
|
77
|
+
const complete = jest.fn();
|
|
78
|
+
obs.subscribe({
|
|
79
|
+
next,
|
|
80
|
+
error,
|
|
81
|
+
complete,
|
|
82
|
+
});
|
|
83
|
+
expect(next.mock.calls).toHaveLength(0);
|
|
84
|
+
expect(complete.mock.calls).toHaveLength(0);
|
|
85
|
+
expect(error.mock.calls).toHaveLength(0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
expect({
|
|
89
|
+
next: taps.next.mock.calls,
|
|
90
|
+
error: taps.error.mock.calls,
|
|
91
|
+
complete: taps.complete.mock.calls,
|
|
92
|
+
}).toMatchInlineSnapshot(`
|
|
93
|
+
Object {
|
|
94
|
+
"complete": Array [],
|
|
95
|
+
"error": Array [],
|
|
96
|
+
"next": Array [
|
|
97
|
+
Array [
|
|
98
|
+
1,
|
|
99
|
+
],
|
|
100
|
+
],
|
|
101
|
+
}
|
|
102
|
+
`);
|
|
103
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { pipeFromArray } from './util/pipe';
|
|
2
|
+
import { Observable, Observer, OperatorFunction, TeardownLogic } from './types';
|
|
3
|
+
|
|
4
|
+
export type inferObservableValue<TObservable extends Observable<any, any>> =
|
|
5
|
+
TObservable extends Observable<infer TValue, any> ? TValue : never;
|
|
6
|
+
|
|
7
|
+
export function observable<TValue, TError = unknown>(
|
|
8
|
+
subscribe: (observer: Observer<TValue, TError>) => TeardownLogic,
|
|
9
|
+
): Observable<TValue, TError> {
|
|
10
|
+
const self: Observable<TValue, TError> = {
|
|
11
|
+
subscribe(observer) {
|
|
12
|
+
let teardownRef: TeardownLogic | null = null;
|
|
13
|
+
let isDone = false;
|
|
14
|
+
let unsubscribed = false;
|
|
15
|
+
let teardownImmediately = false;
|
|
16
|
+
function unsubscribe() {
|
|
17
|
+
if (teardownRef === null) {
|
|
18
|
+
teardownImmediately = true;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (unsubscribed) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
unsubscribed = true;
|
|
25
|
+
|
|
26
|
+
if (typeof teardownRef === 'function') {
|
|
27
|
+
teardownRef();
|
|
28
|
+
} else if (teardownRef) {
|
|
29
|
+
teardownRef.unsubscribe();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
teardownRef = subscribe({
|
|
33
|
+
next(value) {
|
|
34
|
+
if (isDone) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
observer.next?.(value);
|
|
38
|
+
},
|
|
39
|
+
error(err) {
|
|
40
|
+
if (isDone) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
isDone = true;
|
|
44
|
+
observer.error?.(err);
|
|
45
|
+
unsubscribe();
|
|
46
|
+
},
|
|
47
|
+
complete() {
|
|
48
|
+
if (isDone) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
isDone = true;
|
|
52
|
+
observer.complete?.();
|
|
53
|
+
unsubscribe();
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
if (teardownImmediately) {
|
|
57
|
+
unsubscribe();
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
unsubscribe,
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
pipe(
|
|
64
|
+
...operations: OperatorFunction<any, any, any, any>[]
|
|
65
|
+
): Observable<any, any> {
|
|
66
|
+
return pipeFromArray(operations)(self) as any;
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
return self;
|
|
70
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { expectTypeOf } from 'expect-type';
|
|
2
|
+
import { observable } from '../observable';
|
|
3
|
+
import { map } from '.';
|
|
4
|
+
import { EventEmitter } from 'events';
|
|
5
|
+
|
|
6
|
+
interface SubscriptionEvents<TOutput> {
|
|
7
|
+
data: (data: TOutput) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
declare interface CustomEventEmitter<TOutput> {
|
|
11
|
+
on<U extends keyof SubscriptionEvents<TOutput>>(
|
|
12
|
+
event: U,
|
|
13
|
+
listener: SubscriptionEvents<TOutput>[U],
|
|
14
|
+
): this;
|
|
15
|
+
|
|
16
|
+
once<U extends keyof SubscriptionEvents<TOutput>>(
|
|
17
|
+
event: U,
|
|
18
|
+
listener: SubscriptionEvents<TOutput>[U],
|
|
19
|
+
): this;
|
|
20
|
+
|
|
21
|
+
emit<U extends keyof SubscriptionEvents<TOutput>>(
|
|
22
|
+
event: U,
|
|
23
|
+
...args: Parameters<SubscriptionEvents<TOutput>[U]>
|
|
24
|
+
): boolean;
|
|
25
|
+
}
|
|
26
|
+
class CustomEventEmitter<TOutput>
|
|
27
|
+
extends EventEmitter
|
|
28
|
+
implements CustomEventEmitter<TOutput> {}
|
|
29
|
+
test('map', () => {
|
|
30
|
+
type EventShape = { num: number };
|
|
31
|
+
const ee = new CustomEventEmitter<EventShape>();
|
|
32
|
+
const eventObservable = observable<EventShape, unknown>((observer) => {
|
|
33
|
+
const callback = (data: EventShape) => {
|
|
34
|
+
observer.next(data);
|
|
35
|
+
};
|
|
36
|
+
ee.on('data', callback);
|
|
37
|
+
|
|
38
|
+
return () => {
|
|
39
|
+
ee.off('data', callback);
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
const pipeCalls = jest.fn();
|
|
43
|
+
const piped = eventObservable.pipe(
|
|
44
|
+
map((...args) => {
|
|
45
|
+
pipeCalls(...args);
|
|
46
|
+
const [value] = args;
|
|
47
|
+
return value.num;
|
|
48
|
+
}),
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const next = jest.fn();
|
|
52
|
+
const subscription = piped.subscribe({
|
|
53
|
+
next(value) {
|
|
54
|
+
expectTypeOf<number>(value);
|
|
55
|
+
next(value);
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
expect(next).not.toHaveBeenCalled();
|
|
59
|
+
ee.emit('data', { num: 1 });
|
|
60
|
+
ee.emit('data', { num: 2 });
|
|
61
|
+
expect(next).toHaveBeenCalledTimes(2);
|
|
62
|
+
expect(next.mock.calls).toMatchInlineSnapshot(`
|
|
63
|
+
Array [
|
|
64
|
+
Array [
|
|
65
|
+
1,
|
|
66
|
+
],
|
|
67
|
+
Array [
|
|
68
|
+
2,
|
|
69
|
+
],
|
|
70
|
+
]
|
|
71
|
+
`);
|
|
72
|
+
expect(pipeCalls.mock.calls).toMatchInlineSnapshot(`
|
|
73
|
+
Array [
|
|
74
|
+
Array [
|
|
75
|
+
Object {
|
|
76
|
+
"num": 1,
|
|
77
|
+
},
|
|
78
|
+
0,
|
|
79
|
+
],
|
|
80
|
+
Array [
|
|
81
|
+
Object {
|
|
82
|
+
"num": 2,
|
|
83
|
+
},
|
|
84
|
+
1,
|
|
85
|
+
],
|
|
86
|
+
]
|
|
87
|
+
`);
|
|
88
|
+
|
|
89
|
+
expect(ee.listeners('data')).toHaveLength(1);
|
|
90
|
+
subscription.unsubscribe();
|
|
91
|
+
expect(ee.listeners('data')).toHaveLength(0);
|
|
92
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { OperatorFunction } from '../types';
|
|
2
|
+
|
|
3
|
+
export function map<TValueBefore, TError, TValueAfter>(
|
|
4
|
+
project: (value: TValueBefore, index: number) => TValueAfter,
|
|
5
|
+
): OperatorFunction<TValueBefore, TError, TValueAfter, TError> {
|
|
6
|
+
return (originalObserver) => {
|
|
7
|
+
return {
|
|
8
|
+
subscribe(observer) {
|
|
9
|
+
let index = 0;
|
|
10
|
+
const subscription = originalObserver.subscribe({
|
|
11
|
+
next(value) {
|
|
12
|
+
observer.next?.(project(value, index++));
|
|
13
|
+
},
|
|
14
|
+
error(error) {
|
|
15
|
+
observer.error?.(error);
|
|
16
|
+
},
|
|
17
|
+
complete() {
|
|
18
|
+
observer.complete?.();
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
return subscription;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* eslint-disable no-var */
|
|
2
|
+
import { observable } from '../observable';
|
|
3
|
+
import { share } from './share';
|
|
4
|
+
|
|
5
|
+
test('share', () => {
|
|
6
|
+
const obs = share()(
|
|
7
|
+
observable<number, Error>((observer) => {
|
|
8
|
+
observer.next(1);
|
|
9
|
+
}),
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
const next = jest.fn();
|
|
14
|
+
const error = jest.fn();
|
|
15
|
+
const complete = jest.fn();
|
|
16
|
+
var subscription1 = obs.subscribe({
|
|
17
|
+
next,
|
|
18
|
+
error,
|
|
19
|
+
complete,
|
|
20
|
+
});
|
|
21
|
+
expect(next.mock.calls).toHaveLength(1);
|
|
22
|
+
expect(complete.mock.calls).toHaveLength(0);
|
|
23
|
+
expect(error.mock.calls).toHaveLength(0);
|
|
24
|
+
expect(next.mock.calls[0][0]).toBe(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
{
|
|
28
|
+
// subscribe again - it's shared so should not propagate any results
|
|
29
|
+
const next = jest.fn();
|
|
30
|
+
const error = jest.fn();
|
|
31
|
+
const complete = jest.fn();
|
|
32
|
+
var subscription2 = obs.subscribe({
|
|
33
|
+
next,
|
|
34
|
+
error,
|
|
35
|
+
complete,
|
|
36
|
+
});
|
|
37
|
+
expect(next.mock.calls).toHaveLength(0);
|
|
38
|
+
expect(complete.mock.calls).toHaveLength(0);
|
|
39
|
+
expect(error.mock.calls).toHaveLength(0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
subscription1.unsubscribe();
|
|
43
|
+
subscription2.unsubscribe();
|
|
44
|
+
// now it should be reset so we can do a new subscription
|
|
45
|
+
{
|
|
46
|
+
const next = jest.fn();
|
|
47
|
+
const error = jest.fn();
|
|
48
|
+
const complete = jest.fn();
|
|
49
|
+
var subscription3 = obs.subscribe({
|
|
50
|
+
next,
|
|
51
|
+
error,
|
|
52
|
+
complete,
|
|
53
|
+
});
|
|
54
|
+
expect(next.mock.calls).toHaveLength(1);
|
|
55
|
+
expect(complete.mock.calls).toHaveLength(0);
|
|
56
|
+
expect(error.mock.calls).toHaveLength(0);
|
|
57
|
+
expect(next.mock.calls[0][0]).toBe(1);
|
|
58
|
+
subscription3.unsubscribe();
|
|
59
|
+
}
|
|
60
|
+
});
|