@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 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-event-iterator">
12
- <img alt="weekly downloads" src="https://img.shields.io/npm/dw/%40orpc%2Fdurable-event-iterator?logo=npm" />
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-event-iterator`
64
+ ## `@orpc/experimental-durable-iterator`
65
65
 
66
66
  [Durable Objects](https://developers.cloudflare.com/durable-objects/) integration for oRPC.
67
67
 
@@ -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.DDJKw0d4.mjs';
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.Bh_-Jj0k.mjs';
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 Event Iterator Object.
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 (() => `${crypto.randomUUID()}-${crypto.randomUUID()}`)
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-event-iterator Durable Event Iterator Integration}
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;
@@ -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.B0U3VKpv.js';
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.Bh_-Jj0k.js';
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 Event Iterator Object.
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 (() => `${crypto.randomUUID()}-${crypto.randomUUID()}`)
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-event-iterator Durable Event Iterator Integration}
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;
@@ -1,25 +1,27 @@
1
- import { c as createClientDurableIterator } from '../shared/experimental-durable-iterator.CP6Ouvmj.mjs';
2
- export { g as getClientDurableIteratorToken } from '../shared/experimental-durable-iterator.CP6Ouvmj.mjs';
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.BBUZixu_.mjs';
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
- order = 21e5;
15
- // make sure execute before the batch plugin and after client retry plugin
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, () => `${crypto.randomUUID()}-${crypto.randomUUID()}`);
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
- const newTokenAndPayload = this.validateToken(output2, options2.path);
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
- await durableClient.updateToken({ token: tokenAndPayload.token });
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.Bh_-Jj0k.mjs';
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 Iterator internal apis
58
+ * Durable Event internal apis
58
59
  */
59
60
  ['~orpc']: DurableIteratorWebsocketInternal;
60
61
  }
61
62
  /**
62
- * Create a Durable Event Iterator WebSocket from a regular WebSocket
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
- resumeTablePrefix?: string;
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, object: DurableObject<any>, options: DurableIteratorObjectHandlerOptions);
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 = unknown> extends DurableObject<TEnv> implements DurableIteratorObject$1<T> {
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, env: TEnv, options: DurableIteratorObjectHandlerOptions);
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.Bh_-Jj0k.js';
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 Iterator internal apis
58
+ * Durable Event internal apis
58
59
  */
59
60
  ['~orpc']: DurableIteratorWebsocketInternal;
60
61
  }
61
62
  /**
62
- * Create a Durable Event Iterator WebSocket from a regular WebSocket
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
- resumeTablePrefix?: string;
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, object: DurableObject<any>, options: DurableIteratorObjectHandlerOptions);
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 = unknown> extends DurableObject<TEnv> implements DurableIteratorObject$1<T> {
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, env: TEnv, options: DurableIteratorObjectHandlerOptions);
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.BBUZixu_.mjs';
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.resumeTablePrefix, "orpc:durable-iterator:resume:");
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
- const targets = options.targets?.map((ws) => toDurableIteratorWebsocket(ws));
331
- const exclude = options.exclude?.map((ws) => toDurableIteratorWebsocket(ws));
332
- payload = this.resumeStorage.store(payload, { targets, exclude });
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 fallbackTargets = targets ?? this.ctx.getWebSockets().map((ws) => toDurableIteratorWebsocket(ws));
335
- for (const ws of fallbackTargets) {
336
- if (excludeIds?.includes(ws["~orpc"].deserializeId())) {
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
- this.ctx.acceptWebSocket(server);
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.DDJKw0d4.mjs';
5
- import { D as DurableIteratorObject, I as InferDurableIteratorObjectRPC } from './shared/experimental-durable-iterator.Bh_-Jj0k.mjs';
6
- export { a as DurableIteratorObjectDef, b as DurableIteratorTokenPayload, p as parseDurableIteratorToken, s as signDurableIteratorToken, v as verifyDurableIteratorToken } from './shared/experimental-durable-iterator.Bh_-Jj0k.mjs';
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-event-iterator Durable Event Iterator Integration}
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.B0U3VKpv.js';
5
- import { D as DurableIteratorObject, I as InferDurableIteratorObjectRPC } from './shared/experimental-durable-iterator.Bh_-Jj0k.js';
6
- export { a as DurableIteratorObjectDef, b as DurableIteratorTokenPayload, p as parseDurableIteratorToken, s as signDurableIteratorToken, v as verifyDurableIteratorToken } from './shared/experimental-durable-iterator.Bh_-Jj0k.js';
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-event-iterator Durable Event Iterator Integration}
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.BBUZixu_.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.BBUZixu_.mjs';
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.CP6Ouvmj.mjs';
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
- order = 21e5;
59
- // make sure execute after the batch plugin
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.BBUZixu_.mjs';
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.Bh_-Jj0k.js';
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 Event Iterator.
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.Bh_-Jj0k.mjs';
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 Event Iterator.
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": "0.0.0",
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.8.7",
40
- "@orpc/contract": "1.8.7",
41
- "@orpc/server": "1.8.7",
42
- "@orpc/shared": "1.8.7"
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.20250903.0",
45
+ "@cloudflare/workers-types": "^4.20250922.0",
46
46
  "@types/node": "^22.15.30",
47
- "@orpc/standard-server-peer": "1.8.7"
47
+ "@orpc/standard-server-peer": "1.9.0"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "unbuild",