@orpc/experimental-durable-iterator 0.0.0 → 1.9.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 +3 -3
- package/dist/client/index.d.mts +8 -5
- package/dist/client/index.d.ts +8 -5
- package/dist/client/index.mjs +18 -15
- package/dist/durable-object/index.d.mts +33 -18
- package/dist/durable-object/index.d.ts +33 -18
- package/dist/durable-object/index.mjs +58 -12
- package/dist/index.d.mts +11 -4
- package/dist/index.d.ts +11 -4
- package/dist/index.mjs +8 -5
- package/dist/shared/{experimental-durable-iterator.CP6Ouvmj.mjs → experimental-durable-iterator.B3M42lLK.mjs} +1 -1
- package/dist/shared/{experimental-durable-iterator.B0U3VKpv.d.ts → experimental-durable-iterator.C6YPLbUA.d.ts} +2 -2
- package/dist/shared/{experimental-durable-iterator.Bh_-Jj0k.d.mts → experimental-durable-iterator.DQjHfIr1.d.mts} +1 -0
- package/dist/shared/{experimental-durable-iterator.Bh_-Jj0k.d.ts → experimental-durable-iterator.DQjHfIr1.d.ts} +1 -0
- package/dist/shared/{experimental-durable-iterator.BBUZixu_.mjs → experimental-durable-iterator.DZOLL3sf.mjs} +1 -0
- package/dist/shared/{experimental-durable-iterator.DDJKw0d4.d.mts → experimental-durable-iterator.DrenXxND.d.mts} +2 -2
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
<a href="https://codecov.io/gh/unnoq/orpc">
|
|
9
9
|
<img alt="codecov" src="https://codecov.io/gh/unnoq/orpc/branch/main/graph/badge.svg">
|
|
10
10
|
</a>
|
|
11
|
-
<a href="https://www.npmjs.com/package/@orpc/durable-
|
|
12
|
-
<img alt="weekly downloads" src="https://img.shields.io/npm/dw/%40orpc%
|
|
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
13
|
</a>
|
|
14
14
|
<a href="https://github.com/unnoq/orpc/blob/main/LICENSE">
|
|
15
15
|
<img alt="MIT License" src="https://img.shields.io/github/license/unnoq/orpc?logo=open-source-initiative" />
|
|
@@ -61,7 +61,7 @@ You can find the full documentation [here](https://orpc.unnoq.com).
|
|
|
61
61
|
- [@orpc/valibot](https://www.npmjs.com/package/@orpc/valibot): OpenAPI spec generation from [Valibot](https://valibot.dev/).
|
|
62
62
|
- [@orpc/arktype](https://www.npmjs.com/package/@orpc/arktype): OpenAPI spec generation from [ArkType](https://arktype.io/).
|
|
63
63
|
|
|
64
|
-
## `@orpc/durable-
|
|
64
|
+
## `@orpc/experimental-durable-iterator`
|
|
65
65
|
|
|
66
66
|
[Durable Objects](https://developers.cloudflare.com/durable-objects/) integration for oRPC.
|
|
67
67
|
|
package/dist/client/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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.
|
|
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
2
|
import { ClientContext } from '@orpc/client';
|
|
3
3
|
import { StandardLinkInterceptorOptions, StandardLinkPlugin, StandardLinkOptions } from '@orpc/client/standard';
|
|
4
4
|
import { RPCLinkOptions } from '@orpc/client/websocket';
|
|
5
5
|
import { Value, Promisable } from '@orpc/shared';
|
|
6
|
-
import { b as DurableIteratorTokenPayload } from '../shared/experimental-durable-iterator.
|
|
6
|
+
import { b as DurableIteratorTokenPayload } from '../shared/experimental-durable-iterator.DQjHfIr1.mjs';
|
|
7
7
|
import '@orpc/client/plugins';
|
|
8
8
|
import 'valibot';
|
|
9
9
|
|
|
@@ -12,7 +12,7 @@ interface DurableIteratorLinkPluginContext {
|
|
|
12
12
|
}
|
|
13
13
|
interface DurableIteratorLinkPluginOptions<T extends ClientContext> extends Omit<RPCLinkOptions<object>, 'websocket'> {
|
|
14
14
|
/**
|
|
15
|
-
* The WebSocket URL to connect to the Durable
|
|
15
|
+
* The WebSocket URL to connect to the Durable Iterator Object.
|
|
16
16
|
*/
|
|
17
17
|
url: Value<Promisable<string | URL>, [tokenPayload: DurableIteratorTokenPayload, options: StandardLinkInterceptorOptions<T>]>;
|
|
18
18
|
/**
|
|
@@ -24,7 +24,7 @@ interface DurableIteratorLinkPluginOptions<T extends ClientContext> extends Omit
|
|
|
24
24
|
* @remarks
|
|
25
25
|
* - Use a strong random generator to avoid collisions or predictable IDs.
|
|
26
26
|
*
|
|
27
|
-
* @default (() =>
|
|
27
|
+
* @default (() => crypto.randomUUID())
|
|
28
28
|
*/
|
|
29
29
|
createId?: (tokenPayload: DurableIteratorTokenPayload, options: StandardLinkInterceptorOptions<T>) => Promisable<string>;
|
|
30
30
|
/**
|
|
@@ -41,10 +41,13 @@ interface DurableIteratorLinkPluginOptions<T extends ClientContext> extends Omit
|
|
|
41
41
|
refreshTokenBeforeExpireInSeconds?: Value<Promisable<number>, [tokenPayload: DurableIteratorTokenPayload, options: StandardLinkInterceptorOptions<T>]>;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
* @see {@link https://orpc.unnoq.com/docs/integrations/durable-
|
|
44
|
+
* @see {@link https://orpc.unnoq.com/docs/integrations/durable-iterator Durable Iterator Integration}
|
|
45
45
|
*/
|
|
46
46
|
declare class DurableIteratorLinkPlugin<T extends ClientContext> implements StandardLinkPlugin<T> {
|
|
47
47
|
readonly CONTEXT_SYMBOL: symbol;
|
|
48
|
+
/**
|
|
49
|
+
* run before (modify result after) retry plugin because it can break the special iterator
|
|
50
|
+
*/
|
|
48
51
|
order: number;
|
|
49
52
|
private readonly url;
|
|
50
53
|
private readonly createId;
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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.
|
|
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
2
|
import { ClientContext } from '@orpc/client';
|
|
3
3
|
import { StandardLinkInterceptorOptions, StandardLinkPlugin, StandardLinkOptions } from '@orpc/client/standard';
|
|
4
4
|
import { RPCLinkOptions } from '@orpc/client/websocket';
|
|
5
5
|
import { Value, Promisable } from '@orpc/shared';
|
|
6
|
-
import { b as DurableIteratorTokenPayload } from '../shared/experimental-durable-iterator.
|
|
6
|
+
import { b as DurableIteratorTokenPayload } from '../shared/experimental-durable-iterator.DQjHfIr1.js';
|
|
7
7
|
import '@orpc/client/plugins';
|
|
8
8
|
import 'valibot';
|
|
9
9
|
|
|
@@ -12,7 +12,7 @@ interface DurableIteratorLinkPluginContext {
|
|
|
12
12
|
}
|
|
13
13
|
interface DurableIteratorLinkPluginOptions<T extends ClientContext> extends Omit<RPCLinkOptions<object>, 'websocket'> {
|
|
14
14
|
/**
|
|
15
|
-
* The WebSocket URL to connect to the Durable
|
|
15
|
+
* The WebSocket URL to connect to the Durable Iterator Object.
|
|
16
16
|
*/
|
|
17
17
|
url: Value<Promisable<string | URL>, [tokenPayload: DurableIteratorTokenPayload, options: StandardLinkInterceptorOptions<T>]>;
|
|
18
18
|
/**
|
|
@@ -24,7 +24,7 @@ interface DurableIteratorLinkPluginOptions<T extends ClientContext> extends Omit
|
|
|
24
24
|
* @remarks
|
|
25
25
|
* - Use a strong random generator to avoid collisions or predictable IDs.
|
|
26
26
|
*
|
|
27
|
-
* @default (() =>
|
|
27
|
+
* @default (() => crypto.randomUUID())
|
|
28
28
|
*/
|
|
29
29
|
createId?: (tokenPayload: DurableIteratorTokenPayload, options: StandardLinkInterceptorOptions<T>) => Promisable<string>;
|
|
30
30
|
/**
|
|
@@ -41,10 +41,13 @@ interface DurableIteratorLinkPluginOptions<T extends ClientContext> extends Omit
|
|
|
41
41
|
refreshTokenBeforeExpireInSeconds?: Value<Promisable<number>, [tokenPayload: DurableIteratorTokenPayload, options: StandardLinkInterceptorOptions<T>]>;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
* @see {@link https://orpc.unnoq.com/docs/integrations/durable-
|
|
44
|
+
* @see {@link https://orpc.unnoq.com/docs/integrations/durable-iterator Durable Iterator Integration}
|
|
45
45
|
*/
|
|
46
46
|
declare class DurableIteratorLinkPlugin<T extends ClientContext> implements StandardLinkPlugin<T> {
|
|
47
47
|
readonly CONTEXT_SYMBOL: symbol;
|
|
48
|
+
/**
|
|
49
|
+
* run before (modify result after) retry plugin because it can break the special iterator
|
|
50
|
+
*/
|
|
48
51
|
order: number;
|
|
49
52
|
private readonly url;
|
|
50
53
|
private readonly createId;
|
package/dist/client/index.mjs
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
import { c as createClientDurableIterator } from '../shared/experimental-durable-iterator.
|
|
2
|
-
export { g as getClientDurableIteratorToken } from '../shared/experimental-durable-iterator.
|
|
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
3
|
import { createORPCClient } from '@orpc/client';
|
|
4
4
|
import { ClientRetryPlugin } from '@orpc/client/plugins';
|
|
5
5
|
import { RPCLink } from '@orpc/client/websocket';
|
|
6
|
-
import { fallback, value, toArray, AsyncIteratorClass, retry } from '@orpc/shared';
|
|
6
|
+
import { fallback, value, toArray, AsyncIteratorClass, retry, stringifyJSON } from '@orpc/shared';
|
|
7
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.
|
|
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
9
|
import '@orpc/server/helpers';
|
|
10
10
|
import 'valibot';
|
|
11
11
|
|
|
12
12
|
class DurableIteratorLinkPlugin {
|
|
13
13
|
CONTEXT_SYMBOL = Symbol("ORPC_DURABLE_ITERATOR_LINK_PLUGIN_CONTEXT");
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* run before (modify result after) retry plugin because it can break the special iterator
|
|
16
|
+
*/
|
|
17
|
+
order = 15e5;
|
|
16
18
|
url;
|
|
17
19
|
createId;
|
|
18
20
|
refreshTokenBeforeExpireInSeconds;
|
|
19
21
|
linkOptions;
|
|
20
22
|
constructor({ url, refreshTokenBeforeExpireInSeconds, ...options }) {
|
|
21
23
|
this.url = url;
|
|
22
|
-
this.createId = fallback(options.createId, () =>
|
|
24
|
+
this.createId = fallback(options.createId, () => crypto.randomUUID());
|
|
23
25
|
this.refreshTokenBeforeExpireInSeconds = fallback(refreshTokenBeforeExpireInSeconds, Number.NaN);
|
|
24
26
|
this.linkOptions = options;
|
|
25
27
|
}
|
|
@@ -65,15 +67,10 @@ class DurableIteratorLinkPlugin {
|
|
|
65
67
|
}
|
|
66
68
|
const nowInSeconds = Math.floor(Date.now() / 1e3);
|
|
67
69
|
refreshTokenBeforeExpireTimeoutId = setTimeout(async () => {
|
|
68
|
-
await retry({ times: Number.POSITIVE_INFINITY, delay: 2e3 }, async (exit) => {
|
|
70
|
+
const newTokenAndPayload = await retry({ times: Number.POSITIVE_INFINITY, delay: 2e3 }, async (exit) => {
|
|
69
71
|
try {
|
|
70
72
|
const output2 = await next();
|
|
71
|
-
|
|
72
|
-
if (newTokenAndPayload.payload.chn !== tokenAndPayload.payload.chn) {
|
|
73
|
-
exit(new DurableIteratorError("Refreshed token must have the same channel with the original token"));
|
|
74
|
-
} else {
|
|
75
|
-
tokenAndPayload = newTokenAndPayload;
|
|
76
|
-
}
|
|
73
|
+
return this.validateToken(output2, options2.path);
|
|
77
74
|
} catch (err) {
|
|
78
75
|
if (isFinished) {
|
|
79
76
|
exit(err);
|
|
@@ -81,8 +78,14 @@ class DurableIteratorLinkPlugin {
|
|
|
81
78
|
throw err;
|
|
82
79
|
}
|
|
83
80
|
});
|
|
81
|
+
const canProactivelyUpdateToken = newTokenAndPayload.payload.chn === tokenAndPayload.payload.chn && stringifyJSON(newTokenAndPayload.payload.tags) === stringifyJSON(tokenAndPayload.payload.tags);
|
|
82
|
+
tokenAndPayload = newTokenAndPayload;
|
|
84
83
|
await refreshTokenBeforeExpire();
|
|
85
|
-
|
|
84
|
+
if (canProactivelyUpdateToken) {
|
|
85
|
+
await durableClient.updateToken({ token: tokenAndPayload.token });
|
|
86
|
+
} else {
|
|
87
|
+
websocket.reconnect();
|
|
88
|
+
}
|
|
86
89
|
}, (tokenAndPayload.payload.exp - nowInSeconds - beforeSeconds) * 1e3);
|
|
87
90
|
};
|
|
88
91
|
refreshTokenBeforeExpire();
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { RPCHandlerOptions } from '@orpc/server/websocket';
|
|
2
2
|
import { DurableObject } from 'cloudflare:workers';
|
|
3
|
-
import { b as DurableIteratorTokenPayload, a as DurableIteratorObjectDef, D as DurableIteratorObject$1 } from '../shared/experimental-durable-iterator.
|
|
3
|
+
import { b as DurableIteratorTokenPayload, a as DurableIteratorObjectDef, D as DurableIteratorObject$1 } from '../shared/experimental-durable-iterator.DQjHfIr1.mjs';
|
|
4
4
|
import { StandardRPCJsonSerializerOptions } from '@orpc/client/standard';
|
|
5
5
|
import { Interceptor } from '@orpc/shared';
|
|
6
|
+
export { withEventMeta } from '@orpc/server';
|
|
6
7
|
import '@orpc/client';
|
|
7
8
|
import 'valibot';
|
|
8
9
|
|
|
@@ -54,12 +55,12 @@ interface DurableIteratorWebsocketInternal {
|
|
|
54
55
|
}
|
|
55
56
|
interface DurableIteratorWebsocket extends WebSocket {
|
|
56
57
|
/**
|
|
57
|
-
* Durable Event
|
|
58
|
+
* Durable Event internal apis
|
|
58
59
|
*/
|
|
59
60
|
['~orpc']: DurableIteratorWebsocketInternal;
|
|
60
61
|
}
|
|
61
62
|
/**
|
|
62
|
-
* Create a Durable
|
|
63
|
+
* Create a Durable Iterator WebSocket from a regular WebSocket
|
|
63
64
|
*
|
|
64
65
|
* @info The websocket automatically closes if expired before sending data
|
|
65
66
|
*/
|
|
@@ -73,7 +74,7 @@ interface DurableIteratorObjectStateInternal {
|
|
|
73
74
|
*/
|
|
74
75
|
original: DurableObjectState;
|
|
75
76
|
}
|
|
76
|
-
interface DurableIteratorObjectState extends DurableObjectState {
|
|
77
|
+
interface DurableIteratorObjectState<TProps> extends DurableObjectState<TProps> {
|
|
77
78
|
/**
|
|
78
79
|
* DurableIteratorObjectState internal apis
|
|
79
80
|
*/
|
|
@@ -84,7 +85,7 @@ interface DurableIteratorObjectState extends DurableObjectState {
|
|
|
84
85
|
*/
|
|
85
86
|
'getWebSockets'(...args: Parameters<DurableObjectState['getWebSockets']>): DurableIteratorWebsocket[];
|
|
86
87
|
}
|
|
87
|
-
declare function toDurableIteratorObjectState(original: DurableObjectState): DurableIteratorObjectState
|
|
88
|
+
declare function toDurableIteratorObjectState<TProps>(original: DurableObjectState<TProps>): DurableIteratorObjectState<TProps>;
|
|
88
89
|
|
|
89
90
|
interface EventResumeStorageOptions extends StandardRPCJsonSerializerOptions {
|
|
90
91
|
/**
|
|
@@ -105,13 +106,15 @@ interface EventResumeStorageOptions extends StandardRPCJsonSerializerOptions {
|
|
|
105
106
|
*
|
|
106
107
|
* @default 'orpc:durable-iterator:resume:'
|
|
107
108
|
*/
|
|
108
|
-
|
|
109
|
+
resumeSchemaPrefix?: string;
|
|
109
110
|
}
|
|
110
111
|
interface ResumeEventFilter {
|
|
112
|
+
/** Only websockets with these tags will receive the event */
|
|
113
|
+
tags?: readonly string[];
|
|
111
114
|
/** Only websockets that are in this list will receive the event */
|
|
112
|
-
targets?: DurableIteratorWebsocket[];
|
|
115
|
+
targets?: readonly DurableIteratorWebsocket[];
|
|
113
116
|
/** Websockets that are in this list will not receive the event */
|
|
114
|
-
exclude?: DurableIteratorWebsocket[];
|
|
117
|
+
exclude?: readonly DurableIteratorWebsocket[];
|
|
115
118
|
}
|
|
116
119
|
declare class EventResumeStorage<T extends object> {
|
|
117
120
|
private readonly durableState;
|
|
@@ -139,28 +142,36 @@ declare class EventResumeStorage<T extends object> {
|
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
type DurableIteratorObjectRouterContext = {
|
|
142
|
-
object: DurableObject<any>;
|
|
145
|
+
object: DurableObject<any, any>;
|
|
143
146
|
resumeStorage: EventResumeStorage<any>;
|
|
144
147
|
websocket: DurableIteratorWebsocket;
|
|
145
148
|
options: DurableIteratorObjectHandlerOptions;
|
|
146
149
|
};
|
|
147
150
|
interface PublishEventOptions {
|
|
151
|
+
/**
|
|
152
|
+
* Deliver the event only to websockets that have the specified tags.
|
|
153
|
+
*/
|
|
154
|
+
tags?: readonly string[];
|
|
148
155
|
/**
|
|
149
156
|
* Restrict the event to a specific set of websockets.
|
|
150
157
|
*
|
|
158
|
+
* Accept a list of websockets or a filter function.
|
|
159
|
+
*
|
|
151
160
|
* Use this when security is important — only the listed websockets
|
|
152
161
|
* will ever receive the event. Newly connected websockets are not
|
|
153
162
|
* included unless explicitly added here.
|
|
154
163
|
*/
|
|
155
|
-
targets?: WebSocket[];
|
|
164
|
+
targets?: readonly WebSocket[] | ((ws: DurableIteratorWebsocket) => boolean);
|
|
156
165
|
/**
|
|
157
166
|
* Exclude certain websockets from receiving the event.
|
|
158
167
|
*
|
|
168
|
+
* Accept a list of websockets or a filter function.
|
|
169
|
+
*
|
|
159
170
|
* Use this when broadcasting widely but skipping a few clients
|
|
160
171
|
* (e.g., the sender). Newly connected websockets may still receive
|
|
161
172
|
* the event if not listed here, so this is less strict than `targets`.
|
|
162
173
|
*/
|
|
163
|
-
exclude?: WebSocket[];
|
|
174
|
+
exclude?: readonly WebSocket[] | ((ws: DurableIteratorWebsocket) => boolean);
|
|
164
175
|
}
|
|
165
176
|
interface DurableIteratorObjectHandlerOptions extends RPCHandlerOptions<DurableIteratorObjectRouterContext>, EventResumeStorageOptions {
|
|
166
177
|
/**
|
|
@@ -176,7 +187,7 @@ interface DurableIteratorObjectHandlerOptions extends RPCHandlerOptions<DurableI
|
|
|
176
187
|
*/
|
|
177
188
|
onSubscribed?: (websocket: DurableIteratorWebsocket, lastEventId: string | undefined) => void;
|
|
178
189
|
}
|
|
179
|
-
declare class DurableIteratorObjectHandler<T extends object> implements DurableIteratorObjectDef<T> {
|
|
190
|
+
declare class DurableIteratorObjectHandler<T extends object, TProps> implements DurableIteratorObjectDef<T> {
|
|
180
191
|
private readonly object;
|
|
181
192
|
private readonly options;
|
|
182
193
|
'~eventPayloadType'?: {
|
|
@@ -187,8 +198,8 @@ declare class DurableIteratorObjectHandler<T extends object> implements DurableI
|
|
|
187
198
|
/**
|
|
188
199
|
* Proxied, ensure you don't accidentally change internal state, and auto close if expired websockets before .send is called
|
|
189
200
|
*/
|
|
190
|
-
ctx: DurableIteratorObjectState
|
|
191
|
-
constructor(ctx: DurableObjectState
|
|
201
|
+
ctx: DurableIteratorObjectState<TProps>;
|
|
202
|
+
constructor(ctx: DurableObjectState<TProps>, object: DurableObject<any, TProps>, options: DurableIteratorObjectHandlerOptions);
|
|
192
203
|
/**
|
|
193
204
|
* Publish an event to a set of clients.
|
|
194
205
|
*/
|
|
@@ -210,13 +221,13 @@ declare class DurableIteratorObjectHandler<T extends object> implements DurableI
|
|
|
210
221
|
webSocketClose(ws_: WebSocket, _code: number, _reason: string, _wasClean: boolean): void | Promise<void>;
|
|
211
222
|
}
|
|
212
223
|
|
|
213
|
-
declare class DurableIteratorObject<T extends object, TEnv =
|
|
214
|
-
'~orpc': DurableIteratorObjectHandler<T>;
|
|
224
|
+
declare class DurableIteratorObject<T extends object, TEnv = Cloudflare.Env, TProps = {}> extends DurableObject<TEnv, TProps> implements DurableIteratorObject$1<T> {
|
|
225
|
+
'~orpc': DurableIteratorObjectHandler<T, TProps>;
|
|
215
226
|
/**
|
|
216
227
|
* Proxied, ensure you don't accidentally change internal state, and auto close if expired websockets before .send is called
|
|
217
228
|
*/
|
|
218
|
-
protected ctx: DurableIteratorObjectState
|
|
219
|
-
constructor(ctx: DurableObjectState
|
|
229
|
+
protected ctx: DurableIteratorObjectState<TProps>;
|
|
230
|
+
constructor(ctx: DurableObjectState<TProps>, env: TEnv, options: DurableIteratorObjectHandlerOptions);
|
|
220
231
|
/**
|
|
221
232
|
* Publish an event to clients
|
|
222
233
|
*/
|
|
@@ -253,6 +264,10 @@ interface UpgradeDurableIteratorRequestOptions {
|
|
|
253
264
|
* The durable object namespace
|
|
254
265
|
*/
|
|
255
266
|
namespace: DurableObjectNamespace<any>;
|
|
267
|
+
/**
|
|
268
|
+
* The options to use when getting the durable object stub
|
|
269
|
+
*/
|
|
270
|
+
namespaceGetOptions?: DurableObjectNamespaceGetDurableObjectOptions;
|
|
256
271
|
/**
|
|
257
272
|
* intercept upgrade process
|
|
258
273
|
*/
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { RPCHandlerOptions } from '@orpc/server/websocket';
|
|
2
2
|
import { DurableObject } from 'cloudflare:workers';
|
|
3
|
-
import { b as DurableIteratorTokenPayload, a as DurableIteratorObjectDef, D as DurableIteratorObject$1 } from '../shared/experimental-durable-iterator.
|
|
3
|
+
import { b as DurableIteratorTokenPayload, a as DurableIteratorObjectDef, D as DurableIteratorObject$1 } from '../shared/experimental-durable-iterator.DQjHfIr1.js';
|
|
4
4
|
import { StandardRPCJsonSerializerOptions } from '@orpc/client/standard';
|
|
5
5
|
import { Interceptor } from '@orpc/shared';
|
|
6
|
+
export { withEventMeta } from '@orpc/server';
|
|
6
7
|
import '@orpc/client';
|
|
7
8
|
import 'valibot';
|
|
8
9
|
|
|
@@ -54,12 +55,12 @@ interface DurableIteratorWebsocketInternal {
|
|
|
54
55
|
}
|
|
55
56
|
interface DurableIteratorWebsocket extends WebSocket {
|
|
56
57
|
/**
|
|
57
|
-
* Durable Event
|
|
58
|
+
* Durable Event internal apis
|
|
58
59
|
*/
|
|
59
60
|
['~orpc']: DurableIteratorWebsocketInternal;
|
|
60
61
|
}
|
|
61
62
|
/**
|
|
62
|
-
* Create a Durable
|
|
63
|
+
* Create a Durable Iterator WebSocket from a regular WebSocket
|
|
63
64
|
*
|
|
64
65
|
* @info The websocket automatically closes if expired before sending data
|
|
65
66
|
*/
|
|
@@ -73,7 +74,7 @@ interface DurableIteratorObjectStateInternal {
|
|
|
73
74
|
*/
|
|
74
75
|
original: DurableObjectState;
|
|
75
76
|
}
|
|
76
|
-
interface DurableIteratorObjectState extends DurableObjectState {
|
|
77
|
+
interface DurableIteratorObjectState<TProps> extends DurableObjectState<TProps> {
|
|
77
78
|
/**
|
|
78
79
|
* DurableIteratorObjectState internal apis
|
|
79
80
|
*/
|
|
@@ -84,7 +85,7 @@ interface DurableIteratorObjectState extends DurableObjectState {
|
|
|
84
85
|
*/
|
|
85
86
|
'getWebSockets'(...args: Parameters<DurableObjectState['getWebSockets']>): DurableIteratorWebsocket[];
|
|
86
87
|
}
|
|
87
|
-
declare function toDurableIteratorObjectState(original: DurableObjectState): DurableIteratorObjectState
|
|
88
|
+
declare function toDurableIteratorObjectState<TProps>(original: DurableObjectState<TProps>): DurableIteratorObjectState<TProps>;
|
|
88
89
|
|
|
89
90
|
interface EventResumeStorageOptions extends StandardRPCJsonSerializerOptions {
|
|
90
91
|
/**
|
|
@@ -105,13 +106,15 @@ interface EventResumeStorageOptions extends StandardRPCJsonSerializerOptions {
|
|
|
105
106
|
*
|
|
106
107
|
* @default 'orpc:durable-iterator:resume:'
|
|
107
108
|
*/
|
|
108
|
-
|
|
109
|
+
resumeSchemaPrefix?: string;
|
|
109
110
|
}
|
|
110
111
|
interface ResumeEventFilter {
|
|
112
|
+
/** Only websockets with these tags will receive the event */
|
|
113
|
+
tags?: readonly string[];
|
|
111
114
|
/** Only websockets that are in this list will receive the event */
|
|
112
|
-
targets?: DurableIteratorWebsocket[];
|
|
115
|
+
targets?: readonly DurableIteratorWebsocket[];
|
|
113
116
|
/** Websockets that are in this list will not receive the event */
|
|
114
|
-
exclude?: DurableIteratorWebsocket[];
|
|
117
|
+
exclude?: readonly DurableIteratorWebsocket[];
|
|
115
118
|
}
|
|
116
119
|
declare class EventResumeStorage<T extends object> {
|
|
117
120
|
private readonly durableState;
|
|
@@ -139,28 +142,36 @@ declare class EventResumeStorage<T extends object> {
|
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
type DurableIteratorObjectRouterContext = {
|
|
142
|
-
object: DurableObject<any>;
|
|
145
|
+
object: DurableObject<any, any>;
|
|
143
146
|
resumeStorage: EventResumeStorage<any>;
|
|
144
147
|
websocket: DurableIteratorWebsocket;
|
|
145
148
|
options: DurableIteratorObjectHandlerOptions;
|
|
146
149
|
};
|
|
147
150
|
interface PublishEventOptions {
|
|
151
|
+
/**
|
|
152
|
+
* Deliver the event only to websockets that have the specified tags.
|
|
153
|
+
*/
|
|
154
|
+
tags?: readonly string[];
|
|
148
155
|
/**
|
|
149
156
|
* Restrict the event to a specific set of websockets.
|
|
150
157
|
*
|
|
158
|
+
* Accept a list of websockets or a filter function.
|
|
159
|
+
*
|
|
151
160
|
* Use this when security is important — only the listed websockets
|
|
152
161
|
* will ever receive the event. Newly connected websockets are not
|
|
153
162
|
* included unless explicitly added here.
|
|
154
163
|
*/
|
|
155
|
-
targets?: WebSocket[];
|
|
164
|
+
targets?: readonly WebSocket[] | ((ws: DurableIteratorWebsocket) => boolean);
|
|
156
165
|
/**
|
|
157
166
|
* Exclude certain websockets from receiving the event.
|
|
158
167
|
*
|
|
168
|
+
* Accept a list of websockets or a filter function.
|
|
169
|
+
*
|
|
159
170
|
* Use this when broadcasting widely but skipping a few clients
|
|
160
171
|
* (e.g., the sender). Newly connected websockets may still receive
|
|
161
172
|
* the event if not listed here, so this is less strict than `targets`.
|
|
162
173
|
*/
|
|
163
|
-
exclude?: WebSocket[];
|
|
174
|
+
exclude?: readonly WebSocket[] | ((ws: DurableIteratorWebsocket) => boolean);
|
|
164
175
|
}
|
|
165
176
|
interface DurableIteratorObjectHandlerOptions extends RPCHandlerOptions<DurableIteratorObjectRouterContext>, EventResumeStorageOptions {
|
|
166
177
|
/**
|
|
@@ -176,7 +187,7 @@ interface DurableIteratorObjectHandlerOptions extends RPCHandlerOptions<DurableI
|
|
|
176
187
|
*/
|
|
177
188
|
onSubscribed?: (websocket: DurableIteratorWebsocket, lastEventId: string | undefined) => void;
|
|
178
189
|
}
|
|
179
|
-
declare class DurableIteratorObjectHandler<T extends object> implements DurableIteratorObjectDef<T> {
|
|
190
|
+
declare class DurableIteratorObjectHandler<T extends object, TProps> implements DurableIteratorObjectDef<T> {
|
|
180
191
|
private readonly object;
|
|
181
192
|
private readonly options;
|
|
182
193
|
'~eventPayloadType'?: {
|
|
@@ -187,8 +198,8 @@ declare class DurableIteratorObjectHandler<T extends object> implements DurableI
|
|
|
187
198
|
/**
|
|
188
199
|
* Proxied, ensure you don't accidentally change internal state, and auto close if expired websockets before .send is called
|
|
189
200
|
*/
|
|
190
|
-
ctx: DurableIteratorObjectState
|
|
191
|
-
constructor(ctx: DurableObjectState
|
|
201
|
+
ctx: DurableIteratorObjectState<TProps>;
|
|
202
|
+
constructor(ctx: DurableObjectState<TProps>, object: DurableObject<any, TProps>, options: DurableIteratorObjectHandlerOptions);
|
|
192
203
|
/**
|
|
193
204
|
* Publish an event to a set of clients.
|
|
194
205
|
*/
|
|
@@ -210,13 +221,13 @@ declare class DurableIteratorObjectHandler<T extends object> implements DurableI
|
|
|
210
221
|
webSocketClose(ws_: WebSocket, _code: number, _reason: string, _wasClean: boolean): void | Promise<void>;
|
|
211
222
|
}
|
|
212
223
|
|
|
213
|
-
declare class DurableIteratorObject<T extends object, TEnv =
|
|
214
|
-
'~orpc': DurableIteratorObjectHandler<T>;
|
|
224
|
+
declare class DurableIteratorObject<T extends object, TEnv = Cloudflare.Env, TProps = {}> extends DurableObject<TEnv, TProps> implements DurableIteratorObject$1<T> {
|
|
225
|
+
'~orpc': DurableIteratorObjectHandler<T, TProps>;
|
|
215
226
|
/**
|
|
216
227
|
* Proxied, ensure you don't accidentally change internal state, and auto close if expired websockets before .send is called
|
|
217
228
|
*/
|
|
218
|
-
protected ctx: DurableIteratorObjectState
|
|
219
|
-
constructor(ctx: DurableObjectState
|
|
229
|
+
protected ctx: DurableIteratorObjectState<TProps>;
|
|
230
|
+
constructor(ctx: DurableObjectState<TProps>, env: TEnv, options: DurableIteratorObjectHandlerOptions);
|
|
220
231
|
/**
|
|
221
232
|
* Publish an event to clients
|
|
222
233
|
*/
|
|
@@ -253,6 +264,10 @@ interface UpgradeDurableIteratorRequestOptions {
|
|
|
253
264
|
* The durable object namespace
|
|
254
265
|
*/
|
|
255
266
|
namespace: DurableObjectNamespace<any>;
|
|
267
|
+
/**
|
|
268
|
+
* The options to use when getting the durable object stub
|
|
269
|
+
*/
|
|
270
|
+
namespaceGetOptions?: DurableObjectNamespaceGetDurableObjectOptions;
|
|
256
271
|
/**
|
|
257
272
|
* intercept upgrade process
|
|
258
273
|
*/
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getEventMeta, withEventMeta, implement, ORPCError } from '@orpc/server';
|
|
2
|
+
export { withEventMeta } from '@orpc/server';
|
|
2
3
|
import { HibernationEventIterator, encodeHibernationRPCEvent, HibernationPlugin } from '@orpc/server/hibernation';
|
|
3
4
|
import { RPCHandler } from '@orpc/server/websocket';
|
|
4
5
|
import { fallback, parseEmptyableJSON, stringifyJSON, get, toArray, intercept } from '@orpc/shared';
|
|
5
|
-
import { D as DurableIteratorError, v as verifyDurableIteratorToken, c as DURABLE_ITERATOR_TOKEN_PARAM, d as DURABLE_ITERATOR_ID_PARAM } from '../shared/experimental-durable-iterator.
|
|
6
|
+
import { D as DurableIteratorError, v as verifyDurableIteratorToken, c as DURABLE_ITERATOR_TOKEN_PARAM, d as DURABLE_ITERATOR_ID_PARAM } from '../shared/experimental-durable-iterator.DZOLL3sf.mjs';
|
|
6
7
|
import { d as durableIteratorContract } from '../shared/experimental-durable-iterator.BRB0hiXN.mjs';
|
|
7
8
|
import { StandardRPCJsonSerializer } from '@orpc/client/standard';
|
|
8
9
|
import { DurableObject } from 'cloudflare:workers';
|
|
@@ -133,7 +134,7 @@ class EventResumeStorage {
|
|
|
133
134
|
constructor(durableState, options = {}) {
|
|
134
135
|
this.durableState = durableState;
|
|
135
136
|
this.retentionSeconds = fallback(options.resumeRetentionSeconds, Number.NaN);
|
|
136
|
-
this.schemaPrefix = fallback(options.
|
|
137
|
+
this.schemaPrefix = fallback(options.resumeSchemaPrefix, "orpc:durable-iterator:resume:");
|
|
137
138
|
this.serializer = new StandardRPCJsonSerializer(options);
|
|
138
139
|
if (this.isEnabled) {
|
|
139
140
|
this.initSchema();
|
|
@@ -165,8 +166,9 @@ class EventResumeStorage {
|
|
|
165
166
|
);
|
|
166
167
|
const insertEvent = () => {
|
|
167
168
|
const insertResult = this.durableState.storage.sql.exec(
|
|
168
|
-
`INSERT INTO "${this.schemaPrefix}events" (payload, target_ids, exclusion_ids) VALUES (?, ?, ?) RETURNING CAST(id AS TEXT) as id`,
|
|
169
|
+
`INSERT INTO "${this.schemaPrefix}events" (payload, tags, target_ids, exclusion_ids) VALUES (?, ?, ?, ?) RETURNING CAST(id AS TEXT) as id`,
|
|
169
170
|
serializedEvent,
|
|
171
|
+
stringifyJSON(resumeFilter.tags),
|
|
170
172
|
stringifyJSON(targetIds),
|
|
171
173
|
stringifyJSON(excludeIds)
|
|
172
174
|
);
|
|
@@ -188,14 +190,19 @@ class EventResumeStorage {
|
|
|
188
190
|
return [];
|
|
189
191
|
}
|
|
190
192
|
this.cleanupExpiredEvents();
|
|
193
|
+
const websocketTags = websocket["~orpc"].deserializeTokenPayload().tags;
|
|
191
194
|
const websocketId = websocket["~orpc"].deserializeId();
|
|
192
195
|
const resumeQuery = this.durableState.storage.sql.exec(`
|
|
193
|
-
SELECT CAST(id AS TEXT) as id, payload, target_ids, exclusion_ids
|
|
196
|
+
SELECT CAST(id AS TEXT) as id, payload, tags, target_ids, exclusion_ids
|
|
194
197
|
FROM "${this.schemaPrefix}events"
|
|
195
198
|
WHERE id > ?
|
|
196
199
|
ORDER BY id ASC
|
|
197
200
|
`, lastEventId);
|
|
198
201
|
return resumeQuery.toArray().filter((resumeRecord) => {
|
|
202
|
+
const tags = parseEmptyableJSON(resumeRecord.tags);
|
|
203
|
+
if (tags && !tags.some((tag) => websocketTags?.includes(tag))) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
199
206
|
const resumeTargetIds = parseEmptyableJSON(resumeRecord.target_ids);
|
|
200
207
|
const resumeExclusionIds = parseEmptyableJSON(resumeRecord.exclusion_ids);
|
|
201
208
|
if (resumeTargetIds && !resumeTargetIds.includes(websocketId)) {
|
|
@@ -215,6 +222,7 @@ class EventResumeStorage {
|
|
|
215
222
|
CREATE TABLE IF NOT EXISTS "${this.schemaPrefix}events" (
|
|
216
223
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
217
224
|
payload TEXT NOT NULL,
|
|
225
|
+
tags TEXT,
|
|
218
226
|
target_ids TEXT,
|
|
219
227
|
exclusion_ids TEXT,
|
|
220
228
|
stored_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
@@ -268,6 +276,9 @@ const router = base.router({
|
|
|
268
276
|
if (payload.chn !== old.chn) {
|
|
269
277
|
throw new ORPCError("UNAUTHORIZED", { message: "Updated token must have the same channel with the original token" });
|
|
270
278
|
}
|
|
279
|
+
if (stringifyJSON(payload.tags) !== stringifyJSON(old.tags)) {
|
|
280
|
+
throw new ORPCError("UNAUTHORIZED", { message: "Updated token must have the exact same tags with the original token" });
|
|
281
|
+
}
|
|
271
282
|
context.websocket["~orpc"].serializeTokenPayload(payload);
|
|
272
283
|
}),
|
|
273
284
|
subscribe: base.subscribe.handler(({ context, lastEventId }) => {
|
|
@@ -327,13 +338,44 @@ class DurableIteratorObjectHandler {
|
|
|
327
338
|
* Publish an event to a set of clients.
|
|
328
339
|
*/
|
|
329
340
|
publishEvent(payload, options = {}) {
|
|
330
|
-
|
|
331
|
-
const
|
|
332
|
-
|
|
341
|
+
let targets = Array.isArray(options.targets) ? options.targets.map(toDurableIteratorWebsocket) : void 0;
|
|
342
|
+
const websocketsFilteredByTags = (() => {
|
|
343
|
+
if (targets) {
|
|
344
|
+
const uniqueTargets = targets.filter((ws, index) => {
|
|
345
|
+
const id = ws["~orpc"].deserializeId();
|
|
346
|
+
return targets?.findIndex((ws2) => ws2["~orpc"].deserializeId() === id) === index;
|
|
347
|
+
});
|
|
348
|
+
if (!options.tags) {
|
|
349
|
+
return uniqueTargets;
|
|
350
|
+
}
|
|
351
|
+
return uniqueTargets.filter(
|
|
352
|
+
(ws) => ws["~orpc"].deserializeTokenPayload().tags?.some((tag) => options.tags?.includes(tag))
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
if (options.tags) {
|
|
356
|
+
const websockets = options.tags.map((tag) => this.ctx.getWebSockets(tag)).flat();
|
|
357
|
+
const uniqueWebsockets = websockets.filter((ws, index) => {
|
|
358
|
+
const id = ws["~orpc"].deserializeId();
|
|
359
|
+
return websockets.findIndex((ws2) => ws2["~orpc"].deserializeId() === id) === index;
|
|
360
|
+
});
|
|
361
|
+
return uniqueWebsockets;
|
|
362
|
+
} else {
|
|
363
|
+
return this.ctx.getWebSockets();
|
|
364
|
+
}
|
|
365
|
+
})();
|
|
366
|
+
if (typeof options.targets === "function") {
|
|
367
|
+
targets = websocketsFilteredByTags.filter(options.targets);
|
|
368
|
+
}
|
|
369
|
+
const exclude = Array.isArray(options.exclude) ? options.exclude.map(toDurableIteratorWebsocket) : typeof options.exclude === "function" ? websocketsFilteredByTags.filter(options.exclude) : void 0;
|
|
370
|
+
payload = this.resumeStorage.store(payload, { tags: options.tags, targets, exclude });
|
|
371
|
+
const targetIds = targets?.map((ws) => ws["~orpc"].deserializeId());
|
|
333
372
|
const excludeIds = exclude?.map((ws) => ws["~orpc"].deserializeId());
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
if (
|
|
373
|
+
for (const ws of websocketsFilteredByTags) {
|
|
374
|
+
const wsId = ws["~orpc"].deserializeId();
|
|
375
|
+
if (targetIds && !targetIds.includes(wsId)) {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
if (excludeIds?.includes(wsId)) {
|
|
337
379
|
continue;
|
|
338
380
|
}
|
|
339
381
|
const hibernationId = ws["~orpc"].deserializeHibernationId();
|
|
@@ -366,7 +408,11 @@ class DurableIteratorObjectHandler {
|
|
|
366
408
|
return new Response("Invalid Token", { status: 401 });
|
|
367
409
|
}
|
|
368
410
|
const { "0": client, "1": server } = new WebSocketPair();
|
|
369
|
-
|
|
411
|
+
if (payload.tags) {
|
|
412
|
+
this.ctx.acceptWebSocket(server, [...payload.tags]);
|
|
413
|
+
} else {
|
|
414
|
+
this.ctx.acceptWebSocket(server);
|
|
415
|
+
}
|
|
370
416
|
toDurableIteratorWebsocket(server)["~orpc"].serializeId(id);
|
|
371
417
|
toDurableIteratorWebsocket(server)["~orpc"].serializeTokenPayload(payload);
|
|
372
418
|
return new Response(null, {
|
|
@@ -481,7 +527,7 @@ async function upgradeDurableIteratorRequest(request, options) {
|
|
|
481
527
|
async ({ payload: payload2 }) => {
|
|
482
528
|
const namespace = options.namespace;
|
|
483
529
|
const id2 = namespace.idFromName(payload2.chn);
|
|
484
|
-
const stub = namespace.get(id2);
|
|
530
|
+
const stub = namespace.get(id2, options.namespaceGetOptions);
|
|
485
531
|
return stub.fetch(request);
|
|
486
532
|
}
|
|
487
533
|
);
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as _orpc_contract from '@orpc/contract';
|
|
2
2
|
import { AsyncIteratorClass } from '@orpc/shared';
|
|
3
3
|
import * as v from 'valibot';
|
|
4
|
-
import { C as ClientDurableIterator } from './shared/experimental-durable-iterator.
|
|
5
|
-
import { D as DurableIteratorObject, I as InferDurableIteratorObjectRPC } from './shared/experimental-durable-iterator.
|
|
6
|
-
export { a as DurableIteratorObjectDef, b as DurableIteratorTokenPayload, p as parseDurableIteratorToken, s as signDurableIteratorToken, v as verifyDurableIteratorToken } from './shared/experimental-durable-iterator.
|
|
4
|
+
import { C as ClientDurableIterator } from './shared/experimental-durable-iterator.DrenXxND.mjs';
|
|
5
|
+
import { D as DurableIteratorObject, I as InferDurableIteratorObjectRPC } from './shared/experimental-durable-iterator.DQjHfIr1.mjs';
|
|
6
|
+
export { a as DurableIteratorObjectDef, b as DurableIteratorTokenPayload, p as parseDurableIteratorToken, s as signDurableIteratorToken, v as verifyDurableIteratorToken } from './shared/experimental-durable-iterator.DQjHfIr1.mjs';
|
|
7
7
|
import { Context, Router } from '@orpc/server';
|
|
8
8
|
import { StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
|
|
9
9
|
import '@orpc/client';
|
|
@@ -40,6 +40,10 @@ interface DurableIteratorOptions<T extends DurableIteratorObject<any>, RPC exten
|
|
|
40
40
|
* @default 24 hours (60 * 60 * 24)
|
|
41
41
|
*/
|
|
42
42
|
tokenTTLSeconds?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Tags to attach to the token.
|
|
45
|
+
*/
|
|
46
|
+
tags?: readonly string[];
|
|
43
47
|
/**
|
|
44
48
|
* Token's attachment
|
|
45
49
|
*/
|
|
@@ -68,10 +72,13 @@ interface DurableIteratorHandlerPluginContext {
|
|
|
68
72
|
isClientDurableIteratorOutput?: boolean;
|
|
69
73
|
}
|
|
70
74
|
/**
|
|
71
|
-
* @see {@link https://orpc.unnoq.com/docs/integrations/durable-
|
|
75
|
+
* @see {@link https://orpc.unnoq.com/docs/integrations/durable-iterator Durable Iterator Integration}
|
|
72
76
|
*/
|
|
73
77
|
declare class DurableIteratorHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
|
|
74
78
|
readonly CONTEXT_SYMBOL: symbol;
|
|
79
|
+
/**
|
|
80
|
+
* make sure run after batch plugin
|
|
81
|
+
*/
|
|
75
82
|
order: number;
|
|
76
83
|
init(options: StandardHandlerOptions<T>, _router: Router<any, T>): void;
|
|
77
84
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as _orpc_contract from '@orpc/contract';
|
|
2
2
|
import { AsyncIteratorClass } from '@orpc/shared';
|
|
3
3
|
import * as v from 'valibot';
|
|
4
|
-
import { C as ClientDurableIterator } from './shared/experimental-durable-iterator.
|
|
5
|
-
import { D as DurableIteratorObject, I as InferDurableIteratorObjectRPC } from './shared/experimental-durable-iterator.
|
|
6
|
-
export { a as DurableIteratorObjectDef, b as DurableIteratorTokenPayload, p as parseDurableIteratorToken, s as signDurableIteratorToken, v as verifyDurableIteratorToken } from './shared/experimental-durable-iterator.
|
|
4
|
+
import { C as ClientDurableIterator } from './shared/experimental-durable-iterator.C6YPLbUA.js';
|
|
5
|
+
import { D as DurableIteratorObject, I as InferDurableIteratorObjectRPC } from './shared/experimental-durable-iterator.DQjHfIr1.js';
|
|
6
|
+
export { a as DurableIteratorObjectDef, b as DurableIteratorTokenPayload, p as parseDurableIteratorToken, s as signDurableIteratorToken, v as verifyDurableIteratorToken } from './shared/experimental-durable-iterator.DQjHfIr1.js';
|
|
7
7
|
import { Context, Router } from '@orpc/server';
|
|
8
8
|
import { StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
|
|
9
9
|
import '@orpc/client';
|
|
@@ -40,6 +40,10 @@ interface DurableIteratorOptions<T extends DurableIteratorObject<any>, RPC exten
|
|
|
40
40
|
* @default 24 hours (60 * 60 * 24)
|
|
41
41
|
*/
|
|
42
42
|
tokenTTLSeconds?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Tags to attach to the token.
|
|
45
|
+
*/
|
|
46
|
+
tags?: readonly string[];
|
|
43
47
|
/**
|
|
44
48
|
* Token's attachment
|
|
45
49
|
*/
|
|
@@ -68,10 +72,13 @@ interface DurableIteratorHandlerPluginContext {
|
|
|
68
72
|
isClientDurableIteratorOutput?: boolean;
|
|
69
73
|
}
|
|
70
74
|
/**
|
|
71
|
-
* @see {@link https://orpc.unnoq.com/docs/integrations/durable-
|
|
75
|
+
* @see {@link https://orpc.unnoq.com/docs/integrations/durable-iterator Durable Iterator Integration}
|
|
72
76
|
*/
|
|
73
77
|
declare class DurableIteratorHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
|
|
74
78
|
readonly CONTEXT_SYMBOL: symbol;
|
|
79
|
+
/**
|
|
80
|
+
* make sure run after batch plugin
|
|
81
|
+
*/
|
|
75
82
|
order: number;
|
|
76
83
|
init(options: StandardHandlerOptions<T>, _router: Router<any, T>): void;
|
|
77
84
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { s as signDurableIteratorToken, D as DurableIteratorError, a as DURABLE_ITERATOR_PLUGIN_HEADER_VALUE, b as DURABLE_ITERATOR_PLUGIN_HEADER_KEY } from './shared/experimental-durable-iterator.
|
|
2
|
-
export { d as DURABLE_ITERATOR_ID_PARAM, c as DURABLE_ITERATOR_TOKEN_PARAM, p as parseDurableIteratorToken, v as verifyDurableIteratorToken } from './shared/experimental-durable-iterator.
|
|
1
|
+
import { s as signDurableIteratorToken, D as DurableIteratorError, a as DURABLE_ITERATOR_PLUGIN_HEADER_VALUE, b as DURABLE_ITERATOR_PLUGIN_HEADER_KEY } from './shared/experimental-durable-iterator.DZOLL3sf.mjs';
|
|
2
|
+
export { d as DURABLE_ITERATOR_ID_PARAM, c as DURABLE_ITERATOR_TOKEN_PARAM, p as parseDurableIteratorToken, v as verifyDurableIteratorToken } from './shared/experimental-durable-iterator.DZOLL3sf.mjs';
|
|
3
3
|
export { d as durableIteratorContract } from './shared/experimental-durable-iterator.BRB0hiXN.mjs';
|
|
4
4
|
import { AsyncIteratorClass } from '@orpc/shared';
|
|
5
|
-
import { c as createClientDurableIterator, g as getClientDurableIteratorToken } from './shared/experimental-durable-iterator.
|
|
5
|
+
import { c as createClientDurableIterator, g as getClientDurableIteratorToken } from './shared/experimental-durable-iterator.B3M42lLK.mjs';
|
|
6
6
|
import '@orpc/client';
|
|
7
7
|
import '@orpc/client/plugins';
|
|
8
8
|
import '@orpc/client/websocket';
|
|
@@ -31,6 +31,7 @@ class DurableIterator {
|
|
|
31
31
|
const nowInSeconds = Math.floor(Date.now() / 1e3);
|
|
32
32
|
const token = await signDurableIteratorToken(this.options.signingKey, {
|
|
33
33
|
chn: this.chn,
|
|
34
|
+
tags: this.options.tags,
|
|
34
35
|
att: this.options.att,
|
|
35
36
|
rpc: this.options.rpc,
|
|
36
37
|
iat: nowInSeconds,
|
|
@@ -55,8 +56,10 @@ class DurableIterator {
|
|
|
55
56
|
|
|
56
57
|
class DurableIteratorHandlerPlugin {
|
|
57
58
|
CONTEXT_SYMBOL = Symbol("ORPC_DURABLE_ITERATOR_HANDLER_PLUGIN_CONTEXT");
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
/**
|
|
60
|
+
* make sure run after batch plugin
|
|
61
|
+
*/
|
|
62
|
+
order = 15e5;
|
|
60
63
|
init(options, _router) {
|
|
61
64
|
options.interceptors ??= [];
|
|
62
65
|
options.clientInterceptors ??= [];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createORPCClient } from '@orpc/client';
|
|
2
2
|
import { isAsyncIteratorObject } from '@orpc/shared';
|
|
3
|
-
import { p as parseDurableIteratorToken } from './experimental-durable-iterator.
|
|
3
|
+
import { p as parseDurableIteratorToken } from './experimental-durable-iterator.DZOLL3sf.mjs';
|
|
4
4
|
|
|
5
5
|
const CLIENT_DURABLE_ITERATOR_TOKEN_SYMBOL = Symbol("ORPC_CLIENT_DURABLE_ITERATOR_TOKEN");
|
|
6
6
|
function createClientDurableIterator(iterator, link, options) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NestedClient, Client, ThrowableError, ClientLink } from '@orpc/client';
|
|
2
2
|
import { ClientRetryPluginContext } from '@orpc/client/plugins';
|
|
3
3
|
import { AsyncIteratorClass } from '@orpc/shared';
|
|
4
|
-
import { D as DurableIteratorObject, I as InferDurableIteratorObjectRPC } from './experimental-durable-iterator.
|
|
4
|
+
import { D as DurableIteratorObject, I as InferDurableIteratorObjectRPC } from './experimental-durable-iterator.DQjHfIr1.js';
|
|
5
5
|
|
|
6
6
|
interface ClientDurableIteratorRpcContext extends ClientRetryPluginContext {
|
|
7
7
|
}
|
|
@@ -20,7 +20,7 @@ interface CreateClientDurableIteratorOptions {
|
|
|
20
20
|
}
|
|
21
21
|
declare function createClientDurableIterator<T extends DurableIteratorObject<any>, RPC extends InferDurableIteratorObjectRPC<T>>(iterator: AsyncIteratorClass<T>, link: ClientLink<object>, options: CreateClientDurableIteratorOptions): ClientDurableIterator<T, RPC>;
|
|
22
22
|
/**
|
|
23
|
-
* If return a token if the client is a Client Durable
|
|
23
|
+
* If return a token if the client is a Client Durable Iterator.
|
|
24
24
|
*/
|
|
25
25
|
declare function getClientDurableIteratorToken(client: unknown): string | undefined;
|
|
26
26
|
|
|
@@ -17,6 +17,7 @@ type InferDurableIteratorObjectRPC<T extends DurableIteratorObject<any>> = Exclu
|
|
|
17
17
|
type DurableIteratorTokenPayload = v.InferOutput<typeof DurableIteratorTokenPayloadSchema>;
|
|
18
18
|
declare const DurableIteratorTokenPayloadSchema: v.ObjectSchema<{
|
|
19
19
|
readonly chn: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.DescriptionAction<string, "Channel name">]>;
|
|
20
|
+
readonly tags: v.SchemaWithPipe<readonly [v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>, v.ReadonlyAction<string[] | undefined>, v.DescriptionAction<readonly string[] | undefined, "Tags">]>;
|
|
20
21
|
readonly att: v.SchemaWithPipe<readonly [v.OptionalSchema<v.UnknownSchema, undefined>, v.DescriptionAction<unknown, "Attachment">]>;
|
|
21
22
|
readonly rpc: v.SchemaWithPipe<readonly [v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>, v.ReadonlyAction<string[] | undefined>, v.DescriptionAction<readonly string[] | undefined, "Allowed remote methods">]>;
|
|
22
23
|
readonly iat: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.DescriptionAction<number, "Issued at time in seconds">]>;
|
|
@@ -17,6 +17,7 @@ type InferDurableIteratorObjectRPC<T extends DurableIteratorObject<any>> = Exclu
|
|
|
17
17
|
type DurableIteratorTokenPayload = v.InferOutput<typeof DurableIteratorTokenPayloadSchema>;
|
|
18
18
|
declare const DurableIteratorTokenPayloadSchema: v.ObjectSchema<{
|
|
19
19
|
readonly chn: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.DescriptionAction<string, "Channel name">]>;
|
|
20
|
+
readonly tags: v.SchemaWithPipe<readonly [v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>, v.ReadonlyAction<string[] | undefined>, v.DescriptionAction<readonly string[] | undefined, "Tags">]>;
|
|
20
21
|
readonly att: v.SchemaWithPipe<readonly [v.OptionalSchema<v.UnknownSchema, undefined>, v.DescriptionAction<unknown, "Attachment">]>;
|
|
21
22
|
readonly rpc: v.SchemaWithPipe<readonly [v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>, v.ReadonlyAction<string[] | undefined>, v.DescriptionAction<readonly string[] | undefined, "Allowed remote methods">]>;
|
|
22
23
|
readonly iat: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.DescriptionAction<number, "Issued at time in seconds">]>;
|
|
@@ -12,6 +12,7 @@ class DurableIteratorError extends Error {
|
|
|
12
12
|
|
|
13
13
|
const DurableIteratorTokenPayloadSchema = v.object({
|
|
14
14
|
chn: v.pipe(v.string(), v.description("Channel name")),
|
|
15
|
+
tags: v.pipe(v.optional(v.array(v.string())), v.readonly(), v.description("Tags")),
|
|
15
16
|
att: v.pipe(v.optional(v.unknown()), v.description("Attachment")),
|
|
16
17
|
rpc: v.pipe(v.optional(v.array(v.string())), v.readonly(), v.description("Allowed remote methods")),
|
|
17
18
|
iat: v.pipe(v.number(), v.description("Issued at time in seconds")),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NestedClient, Client, ThrowableError, ClientLink } from '@orpc/client';
|
|
2
2
|
import { ClientRetryPluginContext } from '@orpc/client/plugins';
|
|
3
3
|
import { AsyncIteratorClass } from '@orpc/shared';
|
|
4
|
-
import { D as DurableIteratorObject, I as InferDurableIteratorObjectRPC } from './experimental-durable-iterator.
|
|
4
|
+
import { D as DurableIteratorObject, I as InferDurableIteratorObjectRPC } from './experimental-durable-iterator.DQjHfIr1.mjs';
|
|
5
5
|
|
|
6
6
|
interface ClientDurableIteratorRpcContext extends ClientRetryPluginContext {
|
|
7
7
|
}
|
|
@@ -20,7 +20,7 @@ interface CreateClientDurableIteratorOptions {
|
|
|
20
20
|
}
|
|
21
21
|
declare function createClientDurableIterator<T extends DurableIteratorObject<any>, RPC extends InferDurableIteratorObjectRPC<T>>(iterator: AsyncIteratorClass<T>, link: ClientLink<object>, options: CreateClientDurableIteratorOptions): ClientDurableIterator<T, RPC>;
|
|
22
22
|
/**
|
|
23
|
-
* If return a token if the client is a Client Durable
|
|
23
|
+
* If return a token if the client is a Client Durable Iterator.
|
|
24
24
|
*/
|
|
25
25
|
declare function getClientDurableIteratorToken(client: unknown): string | undefined;
|
|
26
26
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orpc/experimental-durable-iterator",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "1.9.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://orpc.unnoq.com",
|
|
7
7
|
"repository": {
|
|
@@ -36,15 +36,15 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"partysocket": "^1.1.5",
|
|
38
38
|
"valibot": "^1.1.0",
|
|
39
|
-
"@orpc/client": "1.
|
|
40
|
-
"@orpc/
|
|
41
|
-
"@orpc/
|
|
42
|
-
"@orpc/
|
|
39
|
+
"@orpc/client": "1.9.0",
|
|
40
|
+
"@orpc/server": "1.9.0",
|
|
41
|
+
"@orpc/shared": "1.9.0",
|
|
42
|
+
"@orpc/contract": "1.9.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@cloudflare/workers-types": "^4.
|
|
45
|
+
"@cloudflare/workers-types": "^4.20250922.0",
|
|
46
46
|
"@types/node": "^22.15.30",
|
|
47
|
-
"@orpc/standard-server-peer": "1.
|
|
47
|
+
"@orpc/standard-server-peer": "1.9.0"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "unbuild",
|