@orpc/experimental-durable-iterator 0.0.0-next.01f0b7a
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/LICENSE +21 -0
- package/README.md +81 -0
- package/dist/client/index.d.mts +69 -0
- package/dist/client/index.d.ts +69 -0
- package/dist/client/index.mjs +160 -0
- package/dist/durable-object/index.d.mts +286 -0
- package/dist/durable-object/index.d.ts +286 -0
- package/dist/durable-object/index.mjs +536 -0
- package/dist/index.d.mts +85 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.mjs +105 -0
- package/dist/shared/experimental-durable-iterator.B3M42lLK.mjs +29 -0
- package/dist/shared/experimental-durable-iterator.BRB0hiXN.mjs +15 -0
- package/dist/shared/experimental-durable-iterator.C6YPLbUA.d.ts +28 -0
- package/dist/shared/experimental-durable-iterator.DQjHfIr1.d.mts +42 -0
- package/dist/shared/experimental-durable-iterator.DQjHfIr1.d.ts +42 -0
- package/dist/shared/experimental-durable-iterator.DZOLL3sf.mjs +47 -0
- package/dist/shared/experimental-durable-iterator.DrenXxND.d.mts +28 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 oRPC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<image align="center" src="https://orpc.unnoq.com/logo.webp" width=280 alt="oRPC logo" />
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
<h1></h1>
|
|
6
|
+
|
|
7
|
+
<div align="center">
|
|
8
|
+
<a href="https://codecov.io/gh/unnoq/orpc">
|
|
9
|
+
<img alt="codecov" src="https://codecov.io/gh/unnoq/orpc/branch/main/graph/badge.svg">
|
|
10
|
+
</a>
|
|
11
|
+
<a href="https://www.npmjs.com/package/@orpc/experimental-durable-iterator">
|
|
12
|
+
<img alt="weekly downloads" src="https://img.shields.io/npm/dw/%40orpc%2Fexperimental-durable-iterator?logo=npm" />
|
|
13
|
+
</a>
|
|
14
|
+
<a href="https://github.com/unnoq/orpc/blob/main/LICENSE">
|
|
15
|
+
<img alt="MIT License" src="https://img.shields.io/github/license/unnoq/orpc?logo=open-source-initiative" />
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://discord.gg/TXEbwRBvQn">
|
|
18
|
+
<img alt="Discord" src="https://img.shields.io/discord/1308966753044398161?color=7389D8&label&logo=discord&logoColor=ffffff" />
|
|
19
|
+
</a>
|
|
20
|
+
<a href="https://deepwiki.com/unnoq/orpc">
|
|
21
|
+
<img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki">
|
|
22
|
+
</a>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<h3 align="center">Typesafe APIs Made Simple 🪄</h3>
|
|
26
|
+
|
|
27
|
+
**oRPC is a powerful combination of RPC and OpenAPI**, makes it easy to build APIs that are end-to-end type-safe and adhere to OpenAPI standards
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Highlights
|
|
32
|
+
|
|
33
|
+
- **🔗 End-to-End Type Safety**: Ensure type-safe inputs, outputs, and errors from client to server.
|
|
34
|
+
- **📘 First-Class OpenAPI**: Built-in support that fully adheres to the OpenAPI standard.
|
|
35
|
+
- **📝 Contract-First Development**: Optionally define your API contract before implementation.
|
|
36
|
+
- **🔍 First-Class OpenTelemetry**: Seamlessly integrate with OpenTelemetry for observability.
|
|
37
|
+
- **⚙️ Framework Integrations**: Seamlessly integrate with TanStack Query (React, Vue, Solid, Svelte, Angular), SWR, Pinia Colada, and more.
|
|
38
|
+
- **🚀 Server Actions**: Fully compatible with React Server Actions on Next.js, TanStack Start, and other platforms.
|
|
39
|
+
- **🔠 Standard Schema Support**: Works out of the box with Zod, Valibot, ArkType, and other schema validators.
|
|
40
|
+
- **🗃️ Native Types**: Supports native types like Date, File, Blob, BigInt, URL, and more.
|
|
41
|
+
- **⏱️ Lazy Router**: Enhance cold start times with our lazy routing feature.
|
|
42
|
+
- **📡 SSE & Streaming**: Enjoy full type-safe support for SSE and streaming.
|
|
43
|
+
- **🌍 Multi-Runtime Support**: Fast and lightweight on Cloudflare, Deno, Bun, Node.js, and beyond.
|
|
44
|
+
- **🔌 Extendability**: Easily extend functionality with plugins, middleware, and interceptors.
|
|
45
|
+
|
|
46
|
+
## Documentation
|
|
47
|
+
|
|
48
|
+
You can find the full documentation [here](https://orpc.unnoq.com).
|
|
49
|
+
|
|
50
|
+
## Packages
|
|
51
|
+
|
|
52
|
+
- [@orpc/contract](https://www.npmjs.com/package/@orpc/contract): Build your API contract.
|
|
53
|
+
- [@orpc/server](https://www.npmjs.com/package/@orpc/server): Build your API or implement API contract.
|
|
54
|
+
- [@orpc/client](https://www.npmjs.com/package/@orpc/client): Consume your API on the client with type-safety.
|
|
55
|
+
- [@orpc/openapi](https://www.npmjs.com/package/@orpc/openapi): Generate OpenAPI specs and handle OpenAPI requests.
|
|
56
|
+
- [@orpc/otel](https://www.npmjs.com/package/@orpc/otel): [OpenTelemetry](https://opentelemetry.io/) integration for observability.
|
|
57
|
+
- [@orpc/nest](https://www.npmjs.com/package/@orpc/nest): Deeply integrate oRPC with [NestJS](https://nestjs.com/).
|
|
58
|
+
- [@orpc/react](https://www.npmjs.com/package/@orpc/react): Utilities for integrating oRPC with React and React Server Actions.
|
|
59
|
+
- [@orpc/tanstack-query](https://www.npmjs.com/package/@orpc/tanstack-query): [TanStack Query](https://tanstack.com/query/latest) integration.
|
|
60
|
+
- [@orpc/experimental-react-swr](https://www.npmjs.com/package/@orpc/experimental-react-swr): [SWR](https://swr.vercel.app/) integration.
|
|
61
|
+
- [@orpc/vue-colada](https://www.npmjs.com/package/@orpc/vue-colada): Integration with [Pinia Colada](https://pinia-colada.esm.dev/).
|
|
62
|
+
- [@orpc/hey-api](https://www.npmjs.com/package/@orpc/hey-api): [Hey API](https://heyapi.dev/) integration.
|
|
63
|
+
- [@orpc/zod](https://www.npmjs.com/package/@orpc/zod): More schemas that [Zod](https://zod.dev/) doesn't support yet.
|
|
64
|
+
- [@orpc/valibot](https://www.npmjs.com/package/@orpc/valibot): OpenAPI spec generation from [Valibot](https://valibot.dev/).
|
|
65
|
+
- [@orpc/arktype](https://www.npmjs.com/package/@orpc/arktype): OpenAPI spec generation from [ArkType](https://arktype.io/).
|
|
66
|
+
|
|
67
|
+
## `@orpc/experimental-durable-iterator`
|
|
68
|
+
|
|
69
|
+
[Durable Objects](https://developers.cloudflare.com/durable-objects/) integration for oRPC.
|
|
70
|
+
|
|
71
|
+
## Sponsors
|
|
72
|
+
|
|
73
|
+
<p align="center">
|
|
74
|
+
<a href="https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg">
|
|
75
|
+
<img src='https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg'/>
|
|
76
|
+
</a>
|
|
77
|
+
</p>
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
Distributed under the MIT License. See [LICENSE](https://github.com/unnoq/orpc/blob/main/LICENSE) for more information.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export { C as ClientDurableIterator, b as ClientDurableIteratorRpc, a as ClientDurableIteratorRpcContext, c as CreateClientDurableIteratorOptions, d as createClientDurableIterator, g as getClientDurableIteratorToken } from '../shared/experimental-durable-iterator.DrenXxND.mjs';
|
|
2
|
+
import { ClientContext } from '@orpc/client';
|
|
3
|
+
import { StandardLinkInterceptorOptions, StandardLinkPlugin, StandardLinkOptions } from '@orpc/client/standard';
|
|
4
|
+
import { RPCLinkOptions } from '@orpc/client/websocket';
|
|
5
|
+
import { Value, Promisable } from '@orpc/shared';
|
|
6
|
+
import { b as DurableIteratorTokenPayload } from '../shared/experimental-durable-iterator.DQjHfIr1.mjs';
|
|
7
|
+
import '@orpc/client/plugins';
|
|
8
|
+
import 'valibot';
|
|
9
|
+
|
|
10
|
+
interface DurableIteratorLinkPluginContext {
|
|
11
|
+
isDurableIteratorResponse?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface DurableIteratorLinkPluginOptions<T extends ClientContext> extends Omit<RPCLinkOptions<object>, 'websocket'> {
|
|
14
|
+
/**
|
|
15
|
+
* The WebSocket URL to connect to the Durable Iterator Object.
|
|
16
|
+
*/
|
|
17
|
+
url: Value<Promisable<string | URL>, [tokenPayload: DurableIteratorTokenPayload, options: StandardLinkInterceptorOptions<T>]>;
|
|
18
|
+
/**
|
|
19
|
+
* Generates a unique, unguessable websocket identifier.
|
|
20
|
+
*
|
|
21
|
+
* This ID is attached to the WebSocket connection so the server can
|
|
22
|
+
* recognize the same client across reconnects. It is called **once per client**
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* - Use a strong random generator to avoid collisions or predictable IDs.
|
|
26
|
+
*
|
|
27
|
+
* @default (() => crypto.randomUUID())
|
|
28
|
+
*/
|
|
29
|
+
createId?: (tokenPayload: DurableIteratorTokenPayload, options: StandardLinkInterceptorOptions<T>) => Promisable<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Refresh the token this many seconds before it expires.
|
|
32
|
+
*
|
|
33
|
+
* @remarks
|
|
34
|
+
* - Pick a value larger than the expected token refresh time, network latency, and retry on failure
|
|
35
|
+
* to ensure a seamless refresh without reconnecting the WebSocket.
|
|
36
|
+
* - 300 seconds (5 minutes) is typically enough; 600 seconds (10 minutes) is safer
|
|
37
|
+
* - Use a infinite value to disable refreshing
|
|
38
|
+
*
|
|
39
|
+
* @default NaN (disabled)
|
|
40
|
+
*/
|
|
41
|
+
refreshTokenBeforeExpireInSeconds?: Value<Promisable<number>, [tokenPayload: DurableIteratorTokenPayload, options: StandardLinkInterceptorOptions<T>]>;
|
|
42
|
+
/**
|
|
43
|
+
* Minimum delay between token refresh attempts.
|
|
44
|
+
*
|
|
45
|
+
* @default 2 (seconds)
|
|
46
|
+
*/
|
|
47
|
+
refreshTokenDelayInSeconds?: Value<Promisable<number>, [tokenPayload: DurableIteratorTokenPayload, options: StandardLinkInterceptorOptions<T>]>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* @see {@link https://orpc.unnoq.com/docs/integrations/durable-iterator Durable Iterator Integration}
|
|
51
|
+
*/
|
|
52
|
+
declare class DurableIteratorLinkPlugin<T extends ClientContext> implements StandardLinkPlugin<T> {
|
|
53
|
+
readonly CONTEXT_SYMBOL: symbol;
|
|
54
|
+
/**
|
|
55
|
+
* run before (modify result after) retry plugin because it can break the special iterator
|
|
56
|
+
*/
|
|
57
|
+
order: number;
|
|
58
|
+
private readonly url;
|
|
59
|
+
private readonly createId;
|
|
60
|
+
private readonly refreshTokenBeforeExpireInSeconds;
|
|
61
|
+
private readonly refreshTokenDelayInSeconds;
|
|
62
|
+
private readonly linkOptions;
|
|
63
|
+
constructor({ url, refreshTokenBeforeExpireInSeconds, refreshTokenDelayInSeconds, ...options }: DurableIteratorLinkPluginOptions<T>);
|
|
64
|
+
init(options: StandardLinkOptions<T>): void;
|
|
65
|
+
private validateToken;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { DurableIteratorLinkPlugin };
|
|
69
|
+
export type { DurableIteratorLinkPluginContext, DurableIteratorLinkPluginOptions };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export { C as ClientDurableIterator, b as ClientDurableIteratorRpc, a as ClientDurableIteratorRpcContext, c as CreateClientDurableIteratorOptions, d as createClientDurableIterator, g as getClientDurableIteratorToken } from '../shared/experimental-durable-iterator.C6YPLbUA.js';
|
|
2
|
+
import { ClientContext } from '@orpc/client';
|
|
3
|
+
import { StandardLinkInterceptorOptions, StandardLinkPlugin, StandardLinkOptions } from '@orpc/client/standard';
|
|
4
|
+
import { RPCLinkOptions } from '@orpc/client/websocket';
|
|
5
|
+
import { Value, Promisable } from '@orpc/shared';
|
|
6
|
+
import { b as DurableIteratorTokenPayload } from '../shared/experimental-durable-iterator.DQjHfIr1.js';
|
|
7
|
+
import '@orpc/client/plugins';
|
|
8
|
+
import 'valibot';
|
|
9
|
+
|
|
10
|
+
interface DurableIteratorLinkPluginContext {
|
|
11
|
+
isDurableIteratorResponse?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface DurableIteratorLinkPluginOptions<T extends ClientContext> extends Omit<RPCLinkOptions<object>, 'websocket'> {
|
|
14
|
+
/**
|
|
15
|
+
* The WebSocket URL to connect to the Durable Iterator Object.
|
|
16
|
+
*/
|
|
17
|
+
url: Value<Promisable<string | URL>, [tokenPayload: DurableIteratorTokenPayload, options: StandardLinkInterceptorOptions<T>]>;
|
|
18
|
+
/**
|
|
19
|
+
* Generates a unique, unguessable websocket identifier.
|
|
20
|
+
*
|
|
21
|
+
* This ID is attached to the WebSocket connection so the server can
|
|
22
|
+
* recognize the same client across reconnects. It is called **once per client**
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* - Use a strong random generator to avoid collisions or predictable IDs.
|
|
26
|
+
*
|
|
27
|
+
* @default (() => crypto.randomUUID())
|
|
28
|
+
*/
|
|
29
|
+
createId?: (tokenPayload: DurableIteratorTokenPayload, options: StandardLinkInterceptorOptions<T>) => Promisable<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Refresh the token this many seconds before it expires.
|
|
32
|
+
*
|
|
33
|
+
* @remarks
|
|
34
|
+
* - Pick a value larger than the expected token refresh time, network latency, and retry on failure
|
|
35
|
+
* to ensure a seamless refresh without reconnecting the WebSocket.
|
|
36
|
+
* - 300 seconds (5 minutes) is typically enough; 600 seconds (10 minutes) is safer
|
|
37
|
+
* - Use a infinite value to disable refreshing
|
|
38
|
+
*
|
|
39
|
+
* @default NaN (disabled)
|
|
40
|
+
*/
|
|
41
|
+
refreshTokenBeforeExpireInSeconds?: Value<Promisable<number>, [tokenPayload: DurableIteratorTokenPayload, options: StandardLinkInterceptorOptions<T>]>;
|
|
42
|
+
/**
|
|
43
|
+
* Minimum delay between token refresh attempts.
|
|
44
|
+
*
|
|
45
|
+
* @default 2 (seconds)
|
|
46
|
+
*/
|
|
47
|
+
refreshTokenDelayInSeconds?: Value<Promisable<number>, [tokenPayload: DurableIteratorTokenPayload, options: StandardLinkInterceptorOptions<T>]>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* @see {@link https://orpc.unnoq.com/docs/integrations/durable-iterator Durable Iterator Integration}
|
|
51
|
+
*/
|
|
52
|
+
declare class DurableIteratorLinkPlugin<T extends ClientContext> implements StandardLinkPlugin<T> {
|
|
53
|
+
readonly CONTEXT_SYMBOL: symbol;
|
|
54
|
+
/**
|
|
55
|
+
* run before (modify result after) retry plugin because it can break the special iterator
|
|
56
|
+
*/
|
|
57
|
+
order: number;
|
|
58
|
+
private readonly url;
|
|
59
|
+
private readonly createId;
|
|
60
|
+
private readonly refreshTokenBeforeExpireInSeconds;
|
|
61
|
+
private readonly refreshTokenDelayInSeconds;
|
|
62
|
+
private readonly linkOptions;
|
|
63
|
+
constructor({ url, refreshTokenBeforeExpireInSeconds, refreshTokenDelayInSeconds, ...options }: DurableIteratorLinkPluginOptions<T>);
|
|
64
|
+
init(options: StandardLinkOptions<T>): void;
|
|
65
|
+
private validateToken;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { DurableIteratorLinkPlugin };
|
|
69
|
+
export type { DurableIteratorLinkPluginContext, DurableIteratorLinkPluginOptions };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { c as createClientDurableIterator } from '../shared/experimental-durable-iterator.B3M42lLK.mjs';
|
|
2
|
+
export { g as getClientDurableIteratorToken } from '../shared/experimental-durable-iterator.B3M42lLK.mjs';
|
|
3
|
+
import { createORPCClient } from '@orpc/client';
|
|
4
|
+
import { ClientRetryPlugin } from '@orpc/client/plugins';
|
|
5
|
+
import { RPCLink } from '@orpc/client/websocket';
|
|
6
|
+
import { fallback, value, toArray, AsyncIteratorClass, retry, stringifyJSON } from '@orpc/shared';
|
|
7
|
+
import { WebSocket } from 'partysocket';
|
|
8
|
+
import { d as DURABLE_ITERATOR_ID_PARAM, c as DURABLE_ITERATOR_TOKEN_PARAM, D as DurableIteratorError, b as DURABLE_ITERATOR_PLUGIN_HEADER_KEY, a as DURABLE_ITERATOR_PLUGIN_HEADER_VALUE, p as parseDurableIteratorToken } from '../shared/experimental-durable-iterator.DZOLL3sf.mjs';
|
|
9
|
+
import '@orpc/server/helpers';
|
|
10
|
+
import 'valibot';
|
|
11
|
+
|
|
12
|
+
class DurableIteratorLinkPlugin {
|
|
13
|
+
CONTEXT_SYMBOL = Symbol("ORPC_DURABLE_ITERATOR_LINK_PLUGIN_CONTEXT");
|
|
14
|
+
/**
|
|
15
|
+
* run before (modify result after) retry plugin because it can break the special iterator
|
|
16
|
+
*/
|
|
17
|
+
order = 15e5;
|
|
18
|
+
url;
|
|
19
|
+
createId;
|
|
20
|
+
refreshTokenBeforeExpireInSeconds;
|
|
21
|
+
refreshTokenDelayInSeconds;
|
|
22
|
+
linkOptions;
|
|
23
|
+
constructor({ url, refreshTokenBeforeExpireInSeconds, refreshTokenDelayInSeconds, ...options }) {
|
|
24
|
+
this.url = url;
|
|
25
|
+
this.createId = fallback(options.createId, () => crypto.randomUUID());
|
|
26
|
+
this.refreshTokenBeforeExpireInSeconds = fallback(refreshTokenBeforeExpireInSeconds, Number.NaN);
|
|
27
|
+
this.refreshTokenDelayInSeconds = fallback(refreshTokenDelayInSeconds, 2);
|
|
28
|
+
this.linkOptions = options;
|
|
29
|
+
}
|
|
30
|
+
init(options) {
|
|
31
|
+
options.interceptors ??= [];
|
|
32
|
+
options.clientInterceptors ??= [];
|
|
33
|
+
options.interceptors.push(async (options2) => {
|
|
34
|
+
const pluginContext = {};
|
|
35
|
+
const next = () => options2.next({
|
|
36
|
+
...options2,
|
|
37
|
+
context: {
|
|
38
|
+
[this.CONTEXT_SYMBOL]: pluginContext,
|
|
39
|
+
...options2.context
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
const output = await next();
|
|
43
|
+
if (!pluginContext.isDurableIteratorResponse) {
|
|
44
|
+
return output;
|
|
45
|
+
}
|
|
46
|
+
options2?.signal?.throwIfAborted();
|
|
47
|
+
let isFinished = false;
|
|
48
|
+
let tokenAndPayload = this.validateToken(output, options2.path);
|
|
49
|
+
const id = await this.createId(tokenAndPayload.payload, options2);
|
|
50
|
+
const websocket = new WebSocket(async () => {
|
|
51
|
+
const url = new URL(await value(this.url, tokenAndPayload.payload, options2));
|
|
52
|
+
url.searchParams.append(DURABLE_ITERATOR_ID_PARAM, id);
|
|
53
|
+
url.searchParams.append(DURABLE_ITERATOR_TOKEN_PARAM, tokenAndPayload.token);
|
|
54
|
+
return url.toString();
|
|
55
|
+
});
|
|
56
|
+
const durableClient = createORPCClient(new RPCLink({
|
|
57
|
+
...this.linkOptions,
|
|
58
|
+
websocket,
|
|
59
|
+
plugins: [
|
|
60
|
+
...toArray(this.linkOptions.plugins),
|
|
61
|
+
new ClientRetryPlugin()
|
|
62
|
+
]
|
|
63
|
+
}));
|
|
64
|
+
let refreshTokenBeforeExpireTimeoutId;
|
|
65
|
+
const refreshTokenBeforeExpire = async () => {
|
|
66
|
+
const beforeSeconds = await value(this.refreshTokenBeforeExpireInSeconds, tokenAndPayload.payload, options2);
|
|
67
|
+
const delayMilliseconds = await value(this.refreshTokenDelayInSeconds, tokenAndPayload.payload, options2) * 1e3;
|
|
68
|
+
if (isFinished || !Number.isFinite(beforeSeconds)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
refreshTokenBeforeExpireTimeoutId = setTimeout(
|
|
72
|
+
async () => {
|
|
73
|
+
const newTokenAndPayload = await retry({ times: Number.POSITIVE_INFINITY, delay: delayMilliseconds }, async (exit) => {
|
|
74
|
+
try {
|
|
75
|
+
const output2 = await next();
|
|
76
|
+
return this.validateToken(output2, options2.path);
|
|
77
|
+
} catch (err) {
|
|
78
|
+
if (isFinished) {
|
|
79
|
+
exit(err);
|
|
80
|
+
}
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
const canProactivelyUpdateToken = newTokenAndPayload.payload.chn === tokenAndPayload.payload.chn && stringifyJSON(newTokenAndPayload.payload.tags) === stringifyJSON(tokenAndPayload.payload.tags);
|
|
85
|
+
tokenAndPayload = newTokenAndPayload;
|
|
86
|
+
await refreshTokenBeforeExpire();
|
|
87
|
+
if (canProactivelyUpdateToken) {
|
|
88
|
+
await durableClient.updateToken({ token: tokenAndPayload.token });
|
|
89
|
+
} else {
|
|
90
|
+
websocket.reconnect();
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
Math.max(
|
|
94
|
+
refreshTokenBeforeExpireTimeoutId === void 0 ? 0 : delayMilliseconds,
|
|
95
|
+
(tokenAndPayload.payload.exp - beforeSeconds) * 1e3 - Date.now()
|
|
96
|
+
)
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
refreshTokenBeforeExpire();
|
|
100
|
+
const closeConnection = () => {
|
|
101
|
+
isFinished = true;
|
|
102
|
+
clearTimeout(refreshTokenBeforeExpireTimeoutId);
|
|
103
|
+
websocket.close();
|
|
104
|
+
};
|
|
105
|
+
options2?.signal?.addEventListener("abort", closeConnection, { once: true });
|
|
106
|
+
const iterator_ = await durableClient.subscribe(void 0, {
|
|
107
|
+
context: {
|
|
108
|
+
retry: Number.POSITIVE_INFINITY
|
|
109
|
+
},
|
|
110
|
+
lastEventId: options2.lastEventId
|
|
111
|
+
// we can use user provided lastEventId for initial connection
|
|
112
|
+
});
|
|
113
|
+
const cancelableIterator = new AsyncIteratorClass(
|
|
114
|
+
() => iterator_.next(),
|
|
115
|
+
async () => {
|
|
116
|
+
closeConnection();
|
|
117
|
+
options2.signal?.removeEventListener("abort", closeConnection);
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
const link = {
|
|
121
|
+
call(path, input, options3) {
|
|
122
|
+
return durableClient.call({
|
|
123
|
+
path,
|
|
124
|
+
// safely cast, server will validate later
|
|
125
|
+
input
|
|
126
|
+
}, options3);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const durableIterator = createClientDurableIterator(
|
|
130
|
+
cancelableIterator,
|
|
131
|
+
link,
|
|
132
|
+
{
|
|
133
|
+
getToken: () => tokenAndPayload.token
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
return durableIterator;
|
|
137
|
+
});
|
|
138
|
+
options.clientInterceptors.push(async (options2) => {
|
|
139
|
+
const pluginContext = options2.context[this.CONTEXT_SYMBOL];
|
|
140
|
+
if (!pluginContext) {
|
|
141
|
+
throw new DurableIteratorError("Plugin context has been corrupted or modified by another plugin or interceptor");
|
|
142
|
+
}
|
|
143
|
+
const response = await options2.next();
|
|
144
|
+
pluginContext.isDurableIteratorResponse = response.headers[DURABLE_ITERATOR_PLUGIN_HEADER_KEY] === DURABLE_ITERATOR_PLUGIN_HEADER_VALUE;
|
|
145
|
+
return response;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
validateToken(token, path) {
|
|
149
|
+
if (typeof token !== "string") {
|
|
150
|
+
throw new DurableIteratorError(`Expected valid token for procedure ${path.join(".")}`);
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
return { token, payload: parseDurableIteratorToken(token) };
|
|
154
|
+
} catch (error) {
|
|
155
|
+
throw new DurableIteratorError(`Expected valid token for procedure ${path.join(".")}`, { cause: error });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export { DurableIteratorLinkPlugin, createClientDurableIterator };
|