@orpc/experimental-durable-iterator 0.0.0-next.01f0b7a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,536 @@
1
+ import { getEventMeta, withEventMeta, implement, ORPCError } from '@orpc/server';
2
+ export { withEventMeta } from '@orpc/server';
3
+ import { HibernationEventIterator, encodeHibernationRPCEvent, HibernationPlugin } from '@orpc/server/hibernation';
4
+ import { RPCHandler } from '@orpc/server/websocket';
5
+ import { fallback, parseEmptyableJSON, stringifyJSON, get, toArray, intercept } from '@orpc/shared';
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';
7
+ import { d as durableIteratorContract } from '../shared/experimental-durable-iterator.BRB0hiXN.mjs';
8
+ import { StandardRPCJsonSerializer } from '@orpc/client/standard';
9
+ import { DurableObject } from 'cloudflare:workers';
10
+ import '@orpc/server/helpers';
11
+ import 'valibot';
12
+ import '@orpc/contract';
13
+
14
+ const websocketReferencesCache = /* @__PURE__ */ new WeakMap();
15
+ function toDurableIteratorWebsocket(original) {
16
+ if ("~orpc" in original) {
17
+ return original;
18
+ }
19
+ const cached = websocketReferencesCache.get(original);
20
+ if (cached) {
21
+ return cached;
22
+ }
23
+ const internal = {
24
+ original,
25
+ serializeId(id) {
26
+ original.serializeAttachment({
27
+ ...original.deserializeAttachment(),
28
+ id
29
+ });
30
+ },
31
+ deserializeId() {
32
+ const id = original.deserializeAttachment()?.id;
33
+ if (!id) {
34
+ throw new DurableIteratorError("ID not found, please call serializeId first");
35
+ }
36
+ return id;
37
+ },
38
+ serializeTokenPayload(payload) {
39
+ original.serializeAttachment({
40
+ ...original.deserializeAttachment(),
41
+ tp: payload
42
+ });
43
+ },
44
+ deserializeTokenPayload() {
45
+ const payload = original.deserializeAttachment()?.tp;
46
+ if (!payload) {
47
+ throw new DurableIteratorError("Token payload not found, please call serializeTokenPayload first");
48
+ }
49
+ return payload;
50
+ },
51
+ serializeHibernationId(id) {
52
+ original.serializeAttachment({
53
+ ...original.deserializeAttachment(),
54
+ hi: id
55
+ });
56
+ },
57
+ deserializeHibernationId() {
58
+ return original.deserializeAttachment()?.hi;
59
+ },
60
+ closeIfExpired() {
61
+ const payload = internal.deserializeTokenPayload();
62
+ if (payload.exp < Date.now() / 1e3) {
63
+ original.close(1008, "Token expired");
64
+ }
65
+ }
66
+ };
67
+ const serializeAttachment = (wa) => {
68
+ original.serializeAttachment({
69
+ ...original.deserializeAttachment(),
70
+ wa
71
+ });
72
+ };
73
+ const deserializeAttachment = () => {
74
+ return original.deserializeAttachment()?.wa;
75
+ };
76
+ const send = (data) => {
77
+ internal.closeIfExpired();
78
+ return original.send(data);
79
+ };
80
+ const proxy = new Proxy(original, {
81
+ get(_, prop) {
82
+ if (prop === "~orpc") {
83
+ return internal;
84
+ }
85
+ if (prop === "serializeAttachment") {
86
+ return serializeAttachment;
87
+ }
88
+ if (prop === "deserializeAttachment") {
89
+ return deserializeAttachment;
90
+ }
91
+ if (prop === "send") {
92
+ return send;
93
+ }
94
+ const v = Reflect.get(original, prop);
95
+ return typeof v === "function" ? v.bind(original) : v;
96
+ },
97
+ has(_, p) {
98
+ return p === "~orpc" || Reflect.has(original, p);
99
+ }
100
+ });
101
+ websocketReferencesCache.set(original, proxy);
102
+ return proxy;
103
+ }
104
+
105
+ function toDurableIteratorObjectState(original) {
106
+ if ("~orpc" in original) {
107
+ return original;
108
+ }
109
+ const internal = {
110
+ original
111
+ };
112
+ const getWebSockets = (...args) => {
113
+ return original.getWebSockets(...args).map((ws) => toDurableIteratorWebsocket(ws));
114
+ };
115
+ const proxy = new Proxy(original, {
116
+ get(_, prop) {
117
+ if (prop === "~orpc") {
118
+ return internal;
119
+ }
120
+ if (prop === "getWebSockets") {
121
+ return getWebSockets;
122
+ }
123
+ const v = Reflect.get(original, prop);
124
+ return typeof v === "function" ? v.bind(original) : v;
125
+ },
126
+ has(_, p) {
127
+ return p === "~orpc" || Reflect.has(original, p);
128
+ }
129
+ });
130
+ return proxy;
131
+ }
132
+
133
+ class EventResumeStorage {
134
+ constructor(durableState, options = {}) {
135
+ this.durableState = durableState;
136
+ this.retentionSeconds = fallback(options.resumeRetentionSeconds, Number.NaN);
137
+ this.schemaPrefix = fallback(options.resumeSchemaPrefix, "orpc:durable-iterator:resume:");
138
+ this.serializer = new StandardRPCJsonSerializer(options);
139
+ if (this.isEnabled) {
140
+ this.initSchema();
141
+ this.cleanupExpiredEvents();
142
+ }
143
+ }
144
+ serializer;
145
+ retentionSeconds;
146
+ schemaPrefix;
147
+ get isEnabled() {
148
+ return Number.isFinite(this.retentionSeconds) && this.retentionSeconds > 0;
149
+ }
150
+ /**
151
+ * Store an payload for resume capability.
152
+ *
153
+ * @returns The updated meta of the stored payload
154
+ */
155
+ store(payload, resumeFilter) {
156
+ if (!this.isEnabled) {
157
+ return payload;
158
+ }
159
+ this.cleanupExpiredEvents();
160
+ const serializedEvent = this.serializeEventPayload(payload);
161
+ const targetIds = resumeFilter.targets?.map(
162
+ (ws) => ws["~orpc"].deserializeId()
163
+ );
164
+ const excludeIds = resumeFilter.exclude?.map(
165
+ (ws) => ws["~orpc"].deserializeId()
166
+ );
167
+ const insertEvent = () => {
168
+ const insertResult = this.durableState.storage.sql.exec(
169
+ `INSERT INTO "${this.schemaPrefix}events" (payload, tags, target_ids, exclusion_ids) VALUES (?, ?, ?, ?) RETURNING CAST(id AS TEXT) as id`,
170
+ serializedEvent,
171
+ stringifyJSON(resumeFilter.tags),
172
+ stringifyJSON(targetIds),
173
+ stringifyJSON(excludeIds)
174
+ );
175
+ const id = insertResult.one()?.id;
176
+ return this.withEventId(payload, id);
177
+ };
178
+ try {
179
+ return insertEvent();
180
+ } catch {
181
+ this.resetSchema();
182
+ return insertEvent();
183
+ }
184
+ }
185
+ /**
186
+ * Get events after lastEventId for a specific websocket
187
+ */
188
+ get(websocket, lastEventId) {
189
+ if (!this.isEnabled) {
190
+ return [];
191
+ }
192
+ this.cleanupExpiredEvents();
193
+ const websocketTags = websocket["~orpc"].deserializeTokenPayload().tags;
194
+ const websocketId = websocket["~orpc"].deserializeId();
195
+ const resumeQuery = this.durableState.storage.sql.exec(`
196
+ SELECT CAST(id AS TEXT) as id, payload, tags, target_ids, exclusion_ids
197
+ FROM "${this.schemaPrefix}events"
198
+ WHERE id > ?
199
+ ORDER BY id ASC
200
+ `, lastEventId);
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
+ }
206
+ const resumeTargetIds = parseEmptyableJSON(resumeRecord.target_ids);
207
+ const resumeExclusionIds = parseEmptyableJSON(resumeRecord.exclusion_ids);
208
+ if (resumeTargetIds && !resumeTargetIds.includes(websocketId)) {
209
+ return false;
210
+ }
211
+ if (resumeExclusionIds && resumeExclusionIds.includes(websocketId)) {
212
+ return false;
213
+ }
214
+ return true;
215
+ }).map((resumeRecord) => this.withEventId(
216
+ this.deserializeEventPayload(resumeRecord.payload),
217
+ resumeRecord.id
218
+ ));
219
+ }
220
+ initSchema() {
221
+ this.durableState.storage.sql.exec(`
222
+ CREATE TABLE IF NOT EXISTS "${this.schemaPrefix}events" (
223
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
224
+ payload TEXT NOT NULL,
225
+ tags TEXT,
226
+ target_ids TEXT,
227
+ exclusion_ids TEXT,
228
+ stored_at INTEGER NOT NULL DEFAULT (unixepoch())
229
+ )
230
+ `);
231
+ this.durableState.storage.sql.exec(`
232
+ CREATE INDEX IF NOT EXISTS "${this.schemaPrefix}idx_events_id" ON "${this.schemaPrefix}events" (id)
233
+ `);
234
+ this.durableState.storage.sql.exec(`
235
+ CREATE INDEX IF NOT EXISTS "${this.schemaPrefix}idx_events_stored_at" ON "${this.schemaPrefix}events" (stored_at)
236
+ `);
237
+ }
238
+ resetSchema() {
239
+ this.durableState.storage.sql.exec(`
240
+ DROP TABLE IF EXISTS "${this.schemaPrefix}events"
241
+ `);
242
+ this.initSchema();
243
+ }
244
+ cleanupExpiredEvents() {
245
+ this.durableState.storage.sql.exec(`
246
+ DELETE FROM "${this.schemaPrefix}events" WHERE stored_at < unixepoch() - ?
247
+ `, this.retentionSeconds);
248
+ }
249
+ serializeEventPayload(payload) {
250
+ const eventMeta = getEventMeta(payload);
251
+ const [json, meta] = this.serializer.serialize({ payload, meta: eventMeta });
252
+ return stringifyJSON({ json, meta });
253
+ }
254
+ deserializeEventPayload(payload) {
255
+ const { json, meta } = JSON.parse(payload);
256
+ const { payload: deserializedPayload, meta: eventMeta } = this.serializer.deserialize(json, meta);
257
+ return eventMeta ? withEventMeta(deserializedPayload, eventMeta) : deserializedPayload;
258
+ }
259
+ withEventId(payload, id) {
260
+ return withEventMeta(payload, {
261
+ ...getEventMeta(payload),
262
+ id
263
+ });
264
+ }
265
+ }
266
+
267
+ const os = implement(durableIteratorContract);
268
+ const base = os.$context();
269
+ const router = base.router({
270
+ updateToken: base.updateToken.handler(async ({ context, input }) => {
271
+ const payload = await verifyDurableIteratorToken(context.options.signingKey, input.token);
272
+ if (!payload) {
273
+ throw new ORPCError("UNAUTHORIZED", { message: "Invalid Token" });
274
+ }
275
+ const old = context.websocket["~orpc"].deserializeTokenPayload();
276
+ if (payload.chn !== old.chn) {
277
+ throw new ORPCError("UNAUTHORIZED", { message: "Updated token must have the same channel with the original token" });
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
+ }
282
+ context.websocket["~orpc"].serializeTokenPayload(payload);
283
+ }),
284
+ subscribe: base.subscribe.handler(({ context, lastEventId }) => {
285
+ return new HibernationEventIterator((hibernationId) => {
286
+ context.websocket["~orpc"].serializeHibernationId(hibernationId);
287
+ if (typeof lastEventId === "string") {
288
+ const resumePayloads = context.resumeStorage.get(context.websocket, lastEventId);
289
+ try {
290
+ for (const payload of resumePayloads) {
291
+ context.websocket.send(
292
+ encodeHibernationRPCEvent(hibernationId, payload, context.options)
293
+ );
294
+ }
295
+ } catch {
296
+ }
297
+ }
298
+ context.options.onSubscribed?.(context.websocket, lastEventId);
299
+ });
300
+ }),
301
+ call: base.call.handler(({ context, input, signal, lastEventId }) => {
302
+ const allowMethods = context.websocket["~orpc"].deserializeTokenPayload().rpc;
303
+ const [method, ...path] = input.path;
304
+ if (!allowMethods?.includes(method)) {
305
+ throw new ORPCError("FORBIDDEN", {
306
+ message: `Method "${method}" is not allowed.`
307
+ });
308
+ }
309
+ const nestedClient = context.object[method](context.websocket);
310
+ const client = get(nestedClient, path);
311
+ return client(input.input, { signal, lastEventId });
312
+ })
313
+ });
314
+ class DurableIteratorObjectHandler {
315
+ constructor(ctx, object, options) {
316
+ this.object = object;
317
+ this.options = options;
318
+ this.ctx = toDurableIteratorObjectState(ctx);
319
+ this.resumeStorage = new EventResumeStorage(ctx, options);
320
+ this.handler = new RPCHandler(router, {
321
+ ...options,
322
+ plugins: [
323
+ ...toArray(options.plugins),
324
+ new HibernationPlugin()
325
+ ]
326
+ });
327
+ this.ctx.getWebSockets().forEach((ws) => ws["~orpc"].closeIfExpired());
328
+ }
329
+ "~eventPayloadType";
330
+ // Helps DurableIteratorObjectDef infer the type
331
+ handler;
332
+ resumeStorage;
333
+ /**
334
+ * Proxied, ensure you don't accidentally change internal state, and auto close if expired websockets before .send is called
335
+ */
336
+ ctx;
337
+ /**
338
+ * Publish an event to a set of clients.
339
+ */
340
+ publishEvent(payload, options = {}) {
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());
372
+ const excludeIds = exclude?.map((ws) => 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)) {
379
+ continue;
380
+ }
381
+ const hibernationId = ws["~orpc"].deserializeHibernationId();
382
+ if (typeof hibernationId !== "string") {
383
+ continue;
384
+ }
385
+ const data = encodeHibernationRPCEvent(hibernationId, payload, this.options);
386
+ try {
387
+ ws.send(data);
388
+ } catch {
389
+ }
390
+ }
391
+ }
392
+ /**
393
+ * This method is called when a HTTP request is received for upgrading to a WebSocket connection.
394
+ * Should mapping with corresponding `fetch` inside durable object
395
+ */
396
+ async fetch(request) {
397
+ const url = new URL(request.url);
398
+ const token = url.searchParams.getAll(DURABLE_ITERATOR_TOKEN_PARAM).at(-1);
399
+ const id = url.searchParams.getAll(DURABLE_ITERATOR_ID_PARAM).at(-1);
400
+ if (typeof id !== "string") {
401
+ return new Response("ID is required", { status: 401 });
402
+ }
403
+ if (typeof token !== "string") {
404
+ return new Response("Token is required", { status: 401 });
405
+ }
406
+ const payload = await verifyDurableIteratorToken(this.options.signingKey, token);
407
+ if (!payload) {
408
+ return new Response("Invalid Token", { status: 401 });
409
+ }
410
+ const { "0": client, "1": server } = new WebSocketPair();
411
+ if (payload.tags) {
412
+ this.ctx.acceptWebSocket(server, [...payload.tags]);
413
+ } else {
414
+ this.ctx.acceptWebSocket(server);
415
+ }
416
+ toDurableIteratorWebsocket(server)["~orpc"].serializeId(id);
417
+ toDurableIteratorWebsocket(server)["~orpc"].serializeTokenPayload(payload);
418
+ return new Response(null, {
419
+ status: 101,
420
+ webSocket: client
421
+ });
422
+ }
423
+ /**
424
+ * This method is called when a WebSocket message is received.
425
+ * Should mapping with corresponding `webSocketMessage` inside durable object
426
+ */
427
+ async webSocketMessage(websocket_, message) {
428
+ const websocket = toDurableIteratorWebsocket(websocket_);
429
+ websocket["~orpc"].closeIfExpired();
430
+ if (websocket.readyState !== WebSocket.OPEN) {
431
+ return;
432
+ }
433
+ await this.handler.message(websocket, message, {
434
+ context: {
435
+ websocket,
436
+ object: this.object,
437
+ resumeStorage: this.resumeStorage,
438
+ options: this.options
439
+ }
440
+ });
441
+ }
442
+ /**
443
+ * This method is called when a WebSocket connection is closed.
444
+ * Should mapping with corresponding `webSocketClose` inside durable object
445
+ */
446
+ webSocketClose(ws_, _code, _reason, _wasClean) {
447
+ const ws = toDurableIteratorWebsocket(ws_);
448
+ this.handler.close(ws);
449
+ }
450
+ }
451
+
452
+ class DurableIteratorObject extends DurableObject {
453
+ "~orpc";
454
+ /**
455
+ * Proxied, ensure you don't accidentally change internal state, and auto close if expired websockets before .send is called
456
+ */
457
+ ctx;
458
+ constructor(ctx, env, options) {
459
+ if (!options) {
460
+ throw new DurableIteratorError(`
461
+ Missing options (3rd argument) for DurableIteratorObject.
462
+ When extending DurableIteratorObject, you must define your own constructor
463
+ and call super(ctx, env, options).
464
+ `);
465
+ }
466
+ super(ctx, env);
467
+ this["~orpc"] = new DurableIteratorObjectHandler(ctx, this, options);
468
+ this.ctx = this["~orpc"].ctx;
469
+ }
470
+ /**
471
+ * Publish an event to clients
472
+ */
473
+ publishEvent(payload, options = {}) {
474
+ return this["~orpc"].publishEvent(payload, options);
475
+ }
476
+ /**
477
+ * Upgrades websocket connection
478
+ *
479
+ * @info You can safety intercept non-upgrade requests
480
+ * @warning No verification is done here, you should verify the token payload before calling this method.
481
+ */
482
+ fetch(request) {
483
+ return this["~orpc"].fetch(request);
484
+ }
485
+ /**
486
+ * Handle WebSocket messages
487
+ *
488
+ * @warning Use `toDurableIteratorWebsocket` to proxy the WebSocket when interacting
489
+ * to avoid accidentally modifying internal state, and auto close if expired before .send is called
490
+ */
491
+ webSocketMessage(websocket, message) {
492
+ return this["~orpc"].webSocketMessage(websocket, message);
493
+ }
494
+ /**
495
+ * Handle WebSocket close event
496
+ *
497
+ * @warning Use `toDurableIteratorWebsocket` to proxy the WebSocket when interacting
498
+ * to avoid accidentally modifying internal state, and auto close if expired before .send is called
499
+ */
500
+ webSocketClose(websocket, code, reason, wasClean) {
501
+ return this["~orpc"].webSocketClose(websocket, code, reason, wasClean);
502
+ }
503
+ }
504
+
505
+ async function upgradeDurableIteratorRequest(request, options) {
506
+ if (request.headers.get("upgrade") !== "websocket") {
507
+ return new Response("Expected WebSocket upgrade", {
508
+ status: 426
509
+ });
510
+ }
511
+ const url = new URL(request.url);
512
+ const token = url.searchParams.getAll(DURABLE_ITERATOR_TOKEN_PARAM).at(-1);
513
+ const id = url.searchParams.getAll(DURABLE_ITERATOR_ID_PARAM).at(-1);
514
+ if (typeof id !== "string") {
515
+ return new Response("ID is required", { status: 401 });
516
+ }
517
+ if (!token) {
518
+ return new Response("Token is required", { status: 401 });
519
+ }
520
+ const payload = await verifyDurableIteratorToken(options.signingKey, token);
521
+ if (!payload) {
522
+ return new Response("Invalid Token", { status: 401 });
523
+ }
524
+ return intercept(
525
+ toArray(options.interceptors),
526
+ { payload },
527
+ async ({ payload: payload2 }) => {
528
+ const namespace = options.namespace;
529
+ const id2 = namespace.idFromName(payload2.chn);
530
+ const stub = namespace.get(id2, options.namespaceGetOptions);
531
+ return stub.fetch(request);
532
+ }
533
+ );
534
+ }
535
+
536
+ export { DurableIteratorObject, DurableIteratorObjectHandler, EventResumeStorage, toDurableIteratorObjectState, toDurableIteratorWebsocket, upgradeDurableIteratorRequest };
@@ -0,0 +1,85 @@
1
+ import * as _orpc_contract from '@orpc/contract';
2
+ import { AsyncIteratorClass } from '@orpc/shared';
3
+ import * as v from 'valibot';
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
+ import { Context, Router } from '@orpc/server';
8
+ import { StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
9
+ import '@orpc/client';
10
+ import '@orpc/client/plugins';
11
+
12
+ declare const DURABLE_ITERATOR_TOKEN_PARAM: "token";
13
+ declare const DURABLE_ITERATOR_ID_PARAM: "id";
14
+ declare const DURABLE_ITERATOR_PLUGIN_HEADER_KEY: "x-orpc-dei";
15
+ declare const DURABLE_ITERATOR_PLUGIN_HEADER_VALUE: "1";
16
+
17
+ declare const durableIteratorContract: {
18
+ updateToken: _orpc_contract.ContractProcedureBuilderWithInput<v.ObjectSchema<{
19
+ readonly token: v.StringSchema<undefined>;
20
+ }, undefined>, _orpc_contract.Schema<unknown, unknown>, Record<never, never>, Record<never, never>>;
21
+ subscribe: _orpc_contract.ContractProcedureBuilderWithOutput<_orpc_contract.Schema<unknown, unknown>, _orpc_contract.Schema<AsyncIteratorClass<any, unknown, unknown>, AsyncIteratorClass<any, unknown, unknown>>, Record<never, never>, Record<never, never>>;
22
+ call: _orpc_contract.ContractProcedureBuilderWithInput<v.ObjectSchema<{
23
+ readonly path: v.TupleWithRestSchema<[v.StringSchema<undefined>], v.StringSchema<undefined>, undefined>;
24
+ readonly input: v.UnknownSchema;
25
+ }, undefined>, _orpc_contract.Schema<unknown, unknown>, Record<never, never>, Record<never, never>>;
26
+ };
27
+
28
+ declare class DurableIteratorError extends Error {
29
+ }
30
+
31
+ interface DurableIteratorOptions<T extends DurableIteratorObject<any>, RPC extends InferDurableIteratorObjectRPC<T>> {
32
+ /**
33
+ * The signing key used to sign the token
34
+ */
35
+ signingKey: string;
36
+ /**
37
+ * Time to live for the token in seconds.
38
+ * After expiration, the token will no longer be valid.
39
+ *
40
+ * @default 24 hours (60 * 60 * 24)
41
+ */
42
+ tokenTTLSeconds?: number;
43
+ /**
44
+ * Tags to attach to the token.
45
+ */
46
+ tags?: readonly string[];
47
+ /**
48
+ * Token's attachment
49
+ */
50
+ att?: unknown;
51
+ /**
52
+ * The methods that are allowed to be called remotely.
53
+ *
54
+ * @warning Please use .rpc method to set this field in case ts complains about value you pass
55
+ */
56
+ rpc?: readonly RPC[];
57
+ }
58
+ declare class DurableIterator<T extends DurableIteratorObject<any>, RPC extends InferDurableIteratorObjectRPC<T> = never> implements PromiseLike<ClientDurableIterator<T, RPC>> {
59
+ private readonly chn;
60
+ private readonly options;
61
+ constructor(chn: string, options: DurableIteratorOptions<T, RPC>);
62
+ /**
63
+ * List of methods that are allowed to be called remotely.
64
+ */
65
+ rpc<U extends InferDurableIteratorObjectRPC<T>>(...rpc: U[]): Omit<DurableIterator<T, U>, 'rpc'>;
66
+ then<TResult1 = ClientDurableIterator<T, RPC>, TResult2 = never>(onfulfilled?: ((value: ClientDurableIterator<T, RPC>) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined): PromiseLike<TResult1 | TResult2>;
67
+ }
68
+
69
+ interface DurableIteratorHandlerPluginContext {
70
+ isClientDurableIteratorOutput?: boolean;
71
+ }
72
+ /**
73
+ * @see {@link https://orpc.unnoq.com/docs/integrations/durable-iterator Durable Iterator Integration}
74
+ */
75
+ declare class DurableIteratorHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
76
+ readonly CONTEXT_SYMBOL: symbol;
77
+ /**
78
+ * make sure run after batch plugin
79
+ */
80
+ order: number;
81
+ init(options: StandardHandlerOptions<T>, _router: Router<any, T>): void;
82
+ }
83
+
84
+ export { DURABLE_ITERATOR_ID_PARAM, DURABLE_ITERATOR_PLUGIN_HEADER_KEY, DURABLE_ITERATOR_PLUGIN_HEADER_VALUE, DURABLE_ITERATOR_TOKEN_PARAM, DurableIterator, DurableIteratorError, DurableIteratorHandlerPlugin, DurableIteratorObject, InferDurableIteratorObjectRPC, durableIteratorContract };
85
+ export type { DurableIteratorHandlerPluginContext, DurableIteratorOptions };