@bigmistqke/rpc 0.1.3 → 0.1.4

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,24 @@
1
+ /** Type for values returned by handle() - unwrapped to RPC<T> by RPC system */
2
+ type Handled<T extends object> = T & {
3
+ readonly ['__rpc_handled__']: T;
4
+ };
5
+ /**
6
+ * Mark methods to be returned as a sub-proxy from an RPC method.
7
+ * Use this when a method needs to return an object with callable methods.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * expose({
12
+ * init(canvas: OffscreenCanvas) {
13
+ * const renderer = createRenderer(canvas)
14
+ * return handle({
15
+ * render: () => renderer.render(),
16
+ * resize: (w, h) => renderer.resize(w, h),
17
+ * })
18
+ * }
19
+ * })
20
+ * ```
21
+ */
22
+ declare function handle<T extends object>(methods: T): Handled<T>;
23
+
24
+ export { Handled as H, handle as h };
@@ -1,5 +1,6 @@
1
1
  import * as v from 'valibot';
2
- import { R as RPC } from './types-a5ce9c9a.js';
2
+ import { R as RPC } from './types-9f54da43.js';
3
+ export { H as Handled, h as handle } from './handle-18d6fe9b.js';
3
4
 
4
5
  declare const requestSchema: v.ObjectSchema<{
5
6
  readonly RPC_PROXY_REQUEST: v.NumberSchema<undefined>;
@@ -37,12 +38,32 @@ declare function createResponder(messenger: Messenger, callback: (data: RequestD
37
38
  /**********************************************************************************/
38
39
  /**********************************************************************************/
39
40
  /**
40
- * Exposes a set of methods as an RPC endpoint over the given messenger.
41
+ * Exposes methods as an RPC endpoint over the given messenger.
41
42
  *
42
- * @param methods - An object containing functions to expose
43
+ * @param methods - Object containing methods to expose
43
44
  * @param options - Optional target Messenger and abort signal
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * // Worker side - simple methods
49
+ * expose({
50
+ * add: (a, b) => a + b,
51
+ * multiply: (a, b) => a * b,
52
+ * })
53
+ *
54
+ * // Worker side - with initialization returning sub-proxy
55
+ * expose({
56
+ * init(canvas: OffscreenCanvas) {
57
+ * const renderer = createRenderer(canvas)
58
+ * return handle({
59
+ * render: () => renderer.render(),
60
+ * resize: (w, h) => renderer.resize(w, h),
61
+ * })
62
+ * }
63
+ * })
64
+ * ```
44
65
  */
45
- declare function expose<T extends object>(methods: T, { to, signal }?: {
66
+ declare function expose<TMethods extends object>(methods: TMethods, { to, signal }?: {
46
67
  to?: Messenger;
47
68
  signal?: AbortSignal;
48
69
  }): void;
@@ -51,18 +72,19 @@ declare function expose<T extends object>(methods: T, { to, signal }?: {
51
72
  *
52
73
  * @param messenger - The Messenger to communicate with (e.g. Worker or Window)
53
74
  * @param options - Optional abort signal
54
- * @returns A proxy object that lets you call methods remotely
75
+ * @returns A proxy object for calling remote methods
55
76
  *
56
77
  * @example
57
78
  * ```ts
58
- * const worker = new Worker('worker.js')
59
- * const api = rpc<WorkerAPI>(worker)
79
+ * // Create RPC proxy (synchronous)
80
+ * const worker = rpc<WorkerMethods>(new Worker('worker.js'))
60
81
  *
61
- * // Call remote methods
62
- * await api.doSomething()
82
+ * // Call methods that return handle() get sub-proxies
83
+ * const renderer = await worker.init(transfer(canvas))
84
+ * await renderer.render()
63
85
  *
64
86
  * // Access underlying messenger
65
- * api[$MESSENGER].terminate()
87
+ * worker[$MESSENGER].terminate()
66
88
  * ```
67
89
  */
68
90
  declare function rpc<T extends object>(messenger: Worker, options?: {
@@ -96,4 +118,4 @@ declare function rpc<T extends object, M extends Messenger = Messenger>(messenge
96
118
  [$MESSENGER]: M;
97
119
  };
98
120
 
99
- export { $MESSENGER, $TRANSFER, RPC, Transferred, createResponder, expose, rpc, transfer };
121
+ export { $MESSENGER, $TRANSFER, Transferred, createResponder, expose, rpc, transfer };
package/dist/messenger.js CHANGED
@@ -1,3 +1,17 @@
1
+ // src/handle.ts
2
+ var $HANDLE_MARKER = Symbol("RPC-HANDLE-MARKER");
3
+ function handle(methods) {
4
+ return { [$HANDLE_MARKER]: true, methods };
5
+ }
6
+ function isHandleMarker(value) {
7
+ return !!value && typeof value === "object" && $HANDLE_MARKER in value;
8
+ }
9
+ var HANDLE_NAMESPACE_PREFIX = "__rpc_handle_";
10
+ var handleNamespaceCounter = 0;
11
+ function nextHandleNamespaceId() {
12
+ return `${HANDLE_NAMESPACE_PREFIX}${handleNamespaceCounter++}`;
13
+ }
14
+
1
15
  // ../../node_modules/.pnpm/valibot@1.0.0_typescript@5.7.2/node_modules/valibot/dist/index.js
2
16
  var store;
3
17
  function getGlobalConfig(config2) {
@@ -292,6 +306,32 @@ function object(entries, message) {
292
306
  }
293
307
  };
294
308
  }
309
+ function optional(wrapped, default_) {
310
+ return {
311
+ kind: "schema",
312
+ type: "optional",
313
+ reference: optional,
314
+ expects: `(${wrapped.expects} | undefined)`,
315
+ async: false,
316
+ wrapped,
317
+ default: default_,
318
+ get "~standard"() {
319
+ return _getStandardProps(this);
320
+ },
321
+ "~run"(dataset, config2) {
322
+ if (dataset.value === void 0) {
323
+ if (this.default !== void 0) {
324
+ dataset.value = getDefault(this, dataset, config2);
325
+ }
326
+ if (dataset.value === void 0) {
327
+ dataset.typed = true;
328
+ return dataset;
329
+ }
330
+ }
331
+ return this.wrapped["~run"](dataset, config2);
332
+ }
333
+ };
334
+ }
295
335
  function string(message) {
296
336
  return {
297
337
  kind: "schema",
@@ -379,40 +419,14 @@ function defer() {
379
419
  reject
380
420
  };
381
421
  }
382
- function createCommander(apply) {
383
- function _createCommander(topics, apply2) {
384
- return new Proxy(function() {
385
- }, {
386
- get(target, topic) {
387
- if (typeof topic === "symbol")
388
- return target[topic];
389
- return _createCommander([...topics, topic], apply2);
390
- },
391
- apply(_, __, args) {
392
- return apply2(topics, args);
393
- }
394
- });
395
- }
396
- return _createCommander([], apply);
397
- }
398
422
  function createShape(schema, create) {
399
423
  return {
400
424
  validate: (value) => safeParse(schema, value).success,
401
425
  create
402
426
  };
403
427
  }
404
- function callMethod(methods, topics, args) {
405
- const method = topics.reduce((acc, topic) => {
406
- const result = acc?.[topic];
407
- return result;
408
- }, methods);
409
- if (typeof method !== "function") {
410
- throw new Error(`Topics did not resolve to a function: [${topics.join(",")}]`);
411
- }
412
- return method(...args);
413
- }
414
428
 
415
- // src/message-protocol.ts
429
+ // src/protocol.ts
416
430
  var $MESSENGER_REQUEST = "RPC_PROXY_REQUEST";
417
431
  var requestSchema = object({
418
432
  [$MESSENGER_REQUEST]: number(),
@@ -426,7 +440,7 @@ var $MESSENGER_RESPONSE = "RPC_PROXY_RESPONSE";
426
440
  var ResponseShape = createShape(
427
441
  object({
428
442
  [$MESSENGER_RESPONSE]: number(),
429
- payload: unknown()
443
+ payload: optional(unknown())
430
444
  }),
431
445
  (request, payload) => ({
432
446
  [$MESSENGER_RESPONSE]: request[$MESSENGER_REQUEST],
@@ -453,10 +467,89 @@ var RPCPayloadShape = createShape(
453
467
  }),
454
468
  (topics, args) => ({ [$MESSENGER_RPC_REQUEST]: true, topics, args })
455
469
  );
470
+ var $MESSENGER_HANDLE = "RPC_PROXY_HANDLE";
471
+ var HandleResponseShape = createShape(
472
+ object({
473
+ [$MESSENGER_HANDLE]: string()
474
+ // namespace ID
475
+ }),
476
+ (namespaceId) => ({ [$MESSENGER_HANDLE]: namespaceId })
477
+ );
456
478
 
457
- // src/messenger.ts
479
+ // src/core.ts
480
+ function createCommander(apply) {
481
+ function _createCommander(topics, apply2) {
482
+ return new Proxy(function() {
483
+ }, {
484
+ get(target, topic) {
485
+ if (typeof topic === "symbol")
486
+ return target[topic];
487
+ if (topic === "then")
488
+ return void 0;
489
+ return _createCommander([...topics, topic], apply2);
490
+ },
491
+ apply(_, __, args) {
492
+ return apply2(topics, args);
493
+ }
494
+ });
495
+ }
496
+ return _createCommander([], apply);
497
+ }
498
+ function callMethod(methods, topics, args) {
499
+ const method = topics.reduce((acc, topic) => {
500
+ const result = acc?.[topic];
501
+ return result;
502
+ }, methods);
503
+ if (typeof method !== "function") {
504
+ throw new Error(`Topics did not resolve to a function: [${topics.join(",")}]`);
505
+ }
506
+ return method(...args);
507
+ }
508
+ function isHandleResponse(value) {
509
+ return !!value && typeof value === "object" && $MESSENGER_HANDLE in value && typeof value[$MESSENGER_HANDLE] === "string";
510
+ }
511
+ function createExposeRequestHandler(methods) {
512
+ const namespaceHandlers = /* @__PURE__ */ new Map();
513
+ const processResult = (result) => {
514
+ if (isHandleMarker(result)) {
515
+ const namespaceId = nextHandleNamespaceId();
516
+ namespaceHandlers.set(namespaceId, result.methods);
517
+ return HandleResponseShape.create(namespaceId);
518
+ }
519
+ return result;
520
+ };
521
+ return async (topics, args) => {
522
+ const firstTopic = topics[0];
523
+ if (firstTopic && firstTopic.startsWith(HANDLE_NAMESPACE_PREFIX)) {
524
+ const handler = namespaceHandlers.get(firstTopic);
525
+ if (!handler) {
526
+ throw new Error(`Unknown namespace: ${firstTopic}`);
527
+ }
528
+ const result2 = await callMethod(handler, topics.slice(1), args);
529
+ return processResult(result2);
530
+ }
531
+ const result = await callMethod(methods, topics, args);
532
+ return processResult(result);
533
+ };
534
+ }
535
+ function createRpcCommander(request, topicPrefix = []) {
536
+ return createCommander((topics, methodArgs) => {
537
+ const fullTopics = [...topicPrefix, ...topics];
538
+ return request(fullTopics, methodArgs).then((result) => {
539
+ if (isHandleResponse(result)) {
540
+ return createRpcCommander(
541
+ request,
542
+ result[$MESSENGER_HANDLE] ? [result[$MESSENGER_HANDLE]] : []
543
+ );
544
+ }
545
+ return result;
546
+ });
547
+ });
548
+ }
549
+
550
+ // src/messenger/index.ts
458
551
  var $TRANSFER = "RPC-TRANSFER";
459
- var $MESSENGER = Symbol("RPC-MESSENGER");
552
+ var $MESSENGER = Symbol.for("RPC-MESSENGER");
460
553
  function transfer(value) {
461
554
  return Object.assign(value, { [$TRANSFER]: true });
462
555
  }
@@ -529,7 +622,10 @@ function createResponder(messenger, callback, options = {}) {
529
622
  if (RequestShape.validate(data)) {
530
623
  try {
531
624
  const result = await callback(data);
532
- const { args: [processedResult], transferables } = extractTransferables([result]);
625
+ const {
626
+ args: [processedResult],
627
+ transferables
628
+ } = extractTransferables([result]);
533
629
  postMessage(ResponseShape.create(data, processedResult), transferables);
534
630
  } catch (error) {
535
631
  postMessage(ErrorShape.create(data, error));
@@ -543,24 +639,37 @@ function createResponder(messenger, callback, options = {}) {
543
639
  }
544
640
  }
545
641
  function expose(methods, { to = self, signal } = {}) {
546
- createResponder(
547
- to,
548
- (data) => {
549
- if (RPCPayloadShape.validate(data.payload)) {
642
+ const postMessage = usePostMessage(to);
643
+ const handleRequest = createExposeRequestHandler(methods);
644
+ to.addEventListener(
645
+ "message",
646
+ async (event) => {
647
+ const data = event.data;
648
+ if (RequestShape.validate(data)) {
550
649
  try {
551
- const { topics, args } = data.payload;
552
- return callMethod(methods, topics, args);
650
+ if (RPCPayloadShape.validate(data.payload)) {
651
+ const result = await handleRequest(data.payload.topics, data.payload.args);
652
+ const {
653
+ args: [finalResult],
654
+ transferables
655
+ } = extractTransferables([result]);
656
+ postMessage(ResponseShape.create(data, finalResult), transferables);
657
+ }
553
658
  } catch (error) {
554
- console.error("Error while processing rpc request:", error, data.payload, methods);
659
+ console.error("Error while processing rpc request:", error, data.payload);
660
+ postMessage(ErrorShape.create(data, error));
555
661
  }
556
662
  }
557
663
  },
558
664
  { signal }
559
665
  );
666
+ if ("start" in to) {
667
+ to.start?.();
668
+ }
560
669
  }
561
670
  function rpc(messenger, options) {
562
671
  const request = createRequester(messenger, options);
563
- const proxy = createCommander((topics, args) => {
672
+ const proxy = createRpcCommander((topics, args) => {
564
673
  const { args: processedArgs, transferables } = extractTransferables(args);
565
674
  return request(RPCPayloadShape.create(topics, processedArgs), transferables);
566
675
  });
@@ -571,6 +680,7 @@ export {
571
680
  $TRANSFER,
572
681
  createResponder,
573
682
  expose,
683
+ handle,
574
684
  rpc,
575
685
  transfer
576
686
  };