@adobe/data-rpc 0.10.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +159 -0
- package/dist/arg-async.test.d.ts +1 -0
- package/dist/arg-async.test.js +147 -0
- package/dist/arg-async.test.js.map +1 -0
- package/dist/arg-marshal.d.ts +34 -0
- package/dist/arg-marshal.js +90 -0
- package/dist/arg-marshal.js.map +1 -0
- package/dist/arg-observe.test.d.ts +1 -0
- package/dist/arg-observe.test.js +114 -0
- package/dist/arg-observe.test.js.map +1 -0
- package/dist/consume/caller-context.d.ts +47 -0
- package/dist/consume/caller-context.js +3 -0
- package/dist/consume/caller-context.js.map +1 -0
- package/dist/consume/make-generator.d.ts +16 -0
- package/dist/consume/make-generator.js +75 -0
- package/dist/consume/make-generator.js.map +1 -0
- package/dist/consume/make-observe.d.ts +16 -0
- package/dist/consume/make-observe.js +29 -0
- package/dist/consume/make-observe.js.map +1 -0
- package/dist/consume/make-promise.d.ts +12 -0
- package/dist/consume/make-promise.js +32 -0
- package/dist/consume/make-promise.js.map +1 -0
- package/dist/consume/make-void.d.ts +12 -0
- package/dist/consume/make-void.js +17 -0
- package/dist/consume/make-void.js.map +1 -0
- package/dist/consume/reconstruct-error.d.ts +7 -0
- package/dist/consume/reconstruct-error.js +14 -0
- package/dist/consume/reconstruct-error.js.map +1 -0
- package/dist/consume/synthesize-service.d.ts +10 -0
- package/dist/consume/synthesize-service.js +77 -0
- package/dist/consume/synthesize-service.js.map +1 -0
- package/dist/create-endpoint.d.ts +8 -0
- package/dist/create-endpoint.js +462 -0
- package/dist/create-endpoint.js.map +1 -0
- package/dist/endpoint.d.ts +64 -0
- package/dist/endpoint.js +3 -0
- package/dist/endpoint.js.map +1 -0
- package/dist/host/host-context.d.ts +28 -0
- package/dist/host/host-context.js +3 -0
- package/dist/host/host-context.js.map +1 -0
- package/dist/host/marshal-error.d.ts +8 -0
- package/dist/host/marshal-error.js +15 -0
- package/dist/host/marshal-error.js.map +1 -0
- package/dist/host/register-service.d.ts +10 -0
- package/dist/host/register-service.js +169 -0
- package/dist/host/register-service.js.map +1 -0
- package/dist/id.d.ts +7 -0
- package/dist/id.js +12 -0
- package/dist/id.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/nested.test.d.ts +1 -0
- package/dist/nested.test.js +128 -0
- package/dist/nested.test.js.map +1 -0
- package/dist/protocol.d.ts +157 -0
- package/dist/protocol.js +18 -0
- package/dist/protocol.js.map +1 -0
- package/dist/review-fixes.test.d.ts +1 -0
- package/dist/review-fixes.test.js +200 -0
- package/dist/review-fixes.test.js.map +1 -0
- package/dist/rpc.test.d.ts +1 -0
- package/dist/rpc.test.js +250 -0
- package/dist/rpc.test.js.map +1 -0
- package/dist/transport.d.ts +32 -0
- package/dist/transport.js +3 -0
- package/dist/transport.js.map +1 -0
- package/dist/transports/loopback-transport.d.ts +13 -0
- package/dist/transports/loopback-transport.js +102 -0
- package/dist/transports/loopback-transport.js.map +1 -0
- package/dist/transports/loopback-transport.test.d.ts +1 -0
- package/dist/transports/loopback-transport.test.js +42 -0
- package/dist/transports/loopback-transport.test.js.map +1 -0
- package/dist/transports/message-port-transport.d.ts +14 -0
- package/dist/transports/message-port-transport.js +63 -0
- package/dist/transports/message-port-transport.js.map +1 -0
- package/dist/transports/message-port-transport.test.d.ts +1 -0
- package/dist/transports/message-port-transport.test.js +49 -0
- package/dist/transports/message-port-transport.test.js.map +1 -0
- package/dist/transports/window-transport.d.ts +20 -0
- package/dist/transports/window-transport.js +69 -0
- package/dist/transports/window-transport.js.map +1 -0
- package/dist/transports/window-transport.test.d.ts +1 -0
- package/dist/transports/window-transport.test.js +47 -0
- package/dist/transports/window-transport.test.js.map +1 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Adobe, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# @adobe/data-rpc
|
|
2
|
+
|
|
3
|
+
Schema-driven, **bidirectional** projection of `@adobe/data` async data services
|
|
4
|
+
across a boundary — an iframe, a Worker, a `MessagePort`, anything with a
|
|
5
|
+
message channel. Each side can both **expose** its own services and **consume**
|
|
6
|
+
the peer's, over a single connection.
|
|
7
|
+
|
|
8
|
+
## The core idea
|
|
9
|
+
|
|
10
|
+
An `AsyncDataService` is fully described by a pure-JSON `Schema`, and every one
|
|
11
|
+
of its members is one of five shapes:
|
|
12
|
+
|
|
13
|
+
| member | schema | wire behavior |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `Observe<Data>` | `observe` | subscribe → streamed `Data` values |
|
|
16
|
+
| `(...Data[]) => Observe<Data>` | `function` / `observe` | args + subscribe → streamed `Data` |
|
|
17
|
+
| `(...Data[]) => Promise<Data \| void>` | `function` / `promise` | request → one `Data` reply |
|
|
18
|
+
| `(...Data[]) => AsyncGenerator<Data>` | `function` / `generator` | pull-based stream of `Data` |
|
|
19
|
+
| `(...Data[]) => void` | `function` (no `returns`) | fire-and-forget |
|
|
20
|
+
|
|
21
|
+
**Only `Data` ever crosses the wire.** We never serialize an `Observe`,
|
|
22
|
+
`Promise`, `AsyncGenerator`, or function. The consuming side *synthesizes* an
|
|
23
|
+
equivalent-shaped service locally whose members are thin wrappers translating
|
|
24
|
+
to/from `Data`-only messages. Gating `expose` on `AsyncDataService.IsValid`
|
|
25
|
+
guarantees there is nothing but `Data` to marshal (an `Observe` is a function,
|
|
26
|
+
so higher-order nesting is rejected at compile time).
|
|
27
|
+
|
|
28
|
+
**Nested services** work too: a member whose schema is a child object with its
|
|
29
|
+
own member `properties` is shimmed recursively on both sides (to any depth), and
|
|
30
|
+
each leaf is addressed by its full path from the service root — so
|
|
31
|
+
`remote.child.deep.greet(name)` round-trips exactly like a top-level member.
|
|
32
|
+
|
|
33
|
+
**Constructor-typed arguments** (`Observe`, `Promise`, `AsyncGenerator`) work too,
|
|
34
|
+
in reverse: one passed as — or nested inside — a call argument is a channel from
|
|
35
|
+
the *caller* to the *callee*. The caller sends a ref instead of the value and
|
|
36
|
+
services it over a reverse channel (an `Observe` streams on subscribe; a `Promise`
|
|
37
|
+
settles once; an `AsyncGenerator` is pulled by the callee); the callee reconstructs
|
|
38
|
+
a local value. For example, all of these round-trip:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
display(foo: { alpha: Observe<number>; beta: Observe<string> }): Observe<string>
|
|
42
|
+
awaitAndDouble(p: Promise<number>): Promise<number>
|
|
43
|
+
sumStream(nums: AsyncGenerator<number>): Promise<number>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Describe such an argument with the matching `observe`/`promise`/`generator` schema
|
|
47
|
+
at that position. Providers are released when the owning call/subscription ends and
|
|
48
|
+
on endpoint close (a promise/generator arg consumed after its call has returned is
|
|
49
|
+
cancelled). Function/callback arguments are not supported, and a `void` member
|
|
50
|
+
cannot take constructor-typed arguments (it has no completion signal to release
|
|
51
|
+
them) — doing so throws.
|
|
52
|
+
|
|
53
|
+
## Define a service
|
|
54
|
+
|
|
55
|
+
A projected service is an ordinary `@adobe/data` `AsyncDataService`: an interface
|
|
56
|
+
of the five member shapes above, plus a sideloaded `Schema` that describes it
|
|
57
|
+
exactly. The schema is the single source of truth — it drives the wrapping on
|
|
58
|
+
both sides.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import type { Observe } from "@adobe/data/observe";
|
|
62
|
+
import { type Schema } from "@adobe/data/schema";
|
|
63
|
+
import { AsyncDataService, type Service } from "@adobe/data/service";
|
|
64
|
+
import type { Assert } from "@adobe/data/types";
|
|
65
|
+
|
|
66
|
+
export interface CounterService extends Service {
|
|
67
|
+
count: Observe<number>; // observe
|
|
68
|
+
add: (n: number) => Promise<number>; // promise
|
|
69
|
+
reset: () => void; // void
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export namespace CounterService {
|
|
73
|
+
export const schema = {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: {
|
|
76
|
+
count: { type: "observe", value: { type: "number" } },
|
|
77
|
+
add: { type: "function", signature: { parameters: [{ type: "number" }], returns: { type: "promise", value: { type: "number" } } } },
|
|
78
|
+
reset: { type: "function" },
|
|
79
|
+
},
|
|
80
|
+
required: ["count", "add", "reset"],
|
|
81
|
+
additionalProperties: false,
|
|
82
|
+
} as const satisfies Schema;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Compile-time guard: the schema describes the service exactly (only Data crosses).
|
|
86
|
+
type _Check = Assert<AsyncDataService.IsValidWithCompleteSchema<CounterService, typeof CounterService.schema>>;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Usage
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { createRpcEndpoint, createMessagePortTransport } from "@adobe/data-rpc";
|
|
93
|
+
|
|
94
|
+
// Each side over one MessagePort:
|
|
95
|
+
const endpoint = createRpcEndpoint(createMessagePortTransport(port));
|
|
96
|
+
|
|
97
|
+
// Expose a local service (schema must exactly describe it — compile-time checked):
|
|
98
|
+
endpoint.expose("clock", myClockService, ClockService.schema);
|
|
99
|
+
|
|
100
|
+
// Consume the peer's service with a compile-time schema → fully typed, sync:
|
|
101
|
+
const remote = endpoint.consume("weather", WeatherService.schema);
|
|
102
|
+
remote.temperature((t) => render(t)); // observe
|
|
103
|
+
const forecast = await remote.forecast("NYC"); // promise
|
|
104
|
+
for await (const d of remote.days(7)) { … } // generator (pull-based)
|
|
105
|
+
remote.refresh(); // void
|
|
106
|
+
|
|
107
|
+
// …or fetch the schema at runtime (resolves once the peer exposes it):
|
|
108
|
+
const remote2 = await endpoint.consume("weather");
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### iframe handshake
|
|
112
|
+
|
|
113
|
+
The main frame hands the iframe one end of a `MessageChannel`, then both switch
|
|
114
|
+
to a `MessagePort` transport:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const channel = new MessageChannel();
|
|
118
|
+
iframe.addEventListener("load", () =>
|
|
119
|
+
iframe.contentWindow!.postMessage({ type: "port" }, origin, [channel.port2]),
|
|
120
|
+
);
|
|
121
|
+
const endpoint = createRpcEndpoint(createMessagePortTransport(channel.port1));
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
See the `data-rpc-iframe` sample for a complete main + sub frame demonstrating
|
|
125
|
+
all four kinds in both directions.
|
|
126
|
+
|
|
127
|
+
## Transports
|
|
128
|
+
|
|
129
|
+
- `createMessagePortTransport(port)` — the primary iframe/worker transport.
|
|
130
|
+
- `createWindowTransport(target, { allowedOrigins, targetOrigin? })` — `window.postMessage`,
|
|
131
|
+
for the initial port handoff, with origin allowlisting. `targetOrigin` is inferred
|
|
132
|
+
when there is exactly one allowed origin; otherwise it must be given explicitly
|
|
133
|
+
(outbound never defaults to `"*"`).
|
|
134
|
+
- `createRpcLoopbackTransport()` — in-process pair for tests and single-process demos.
|
|
135
|
+
|
|
136
|
+
`RpcTransport` is a small `send`/`onMessage`/`onClose`/`close` interface; bring
|
|
137
|
+
your own for any other channel.
|
|
138
|
+
|
|
139
|
+
## Trust (v1)
|
|
140
|
+
|
|
141
|
+
Transport-level: origin allowlisting on the window transport, and the host only
|
|
142
|
+
dispatches to explicitly `expose`d services. Incoming `Data` arguments are
|
|
143
|
+
validated against `signature.parameters` before dispatch. A `canInvoke(service,
|
|
144
|
+
member)` seam (default allow) is where per-member policy — e.g.
|
|
145
|
+
`Schema.resolveExternalInvocation` — drops in later.
|
|
146
|
+
|
|
147
|
+
## Semantics & limitations
|
|
148
|
+
|
|
149
|
+
- **Observe first value is asynchronous** (a round-trip), unlike a local
|
|
150
|
+
`Observe.fromConstant`/`createState` which notifies synchronously.
|
|
151
|
+
- **Generators are strictly pull-based** — the host advances one value per
|
|
152
|
+
`next()`; no unbounded buffering.
|
|
153
|
+
- **Errors** are marshaled as `{ name, message, stack }` and reconstructed as a
|
|
154
|
+
plain `Error`; the remote subclass is not preserved. A throw in a `void`
|
|
155
|
+
handler has no wire channel — it is routed to the endpoint's `onError`.
|
|
156
|
+
- **On close**, pending promise calls reject, consumed generators end, and all
|
|
157
|
+
host subscriptions/generators are released. A dropped subscription has no error
|
|
158
|
+
channel through `Observe<Data>`.
|
|
159
|
+
- **Reconnection is out of scope** for v1 — a closed port ends all subscriptions.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
import { describe, it, expect } from "vitest";
|
|
3
|
+
import { Observe } from "@adobe/data/observe";
|
|
4
|
+
import { createRpcEndpoint } from "./create-endpoint.js";
|
|
5
|
+
import { createRpcLoopbackTransport } from "./transports/loopback-transport.js";
|
|
6
|
+
import { createMessagePortTransport } from "./transports/message-port-transport.js";
|
|
7
|
+
var AsyncArgService;
|
|
8
|
+
(function (AsyncArgService) {
|
|
9
|
+
const promiseNum = { type: "promise", value: { type: "number" } };
|
|
10
|
+
const genNum = { type: "generator", value: { type: "number" } };
|
|
11
|
+
AsyncArgService.schema = {
|
|
12
|
+
type: "object",
|
|
13
|
+
properties: {
|
|
14
|
+
awaitAndDouble: { type: "function", signature: { parameters: [promiseNum], returns: promiseNum } },
|
|
15
|
+
sumStream: { type: "function", signature: { parameters: [genNum], returns: promiseNum } },
|
|
16
|
+
first: { type: "function", signature: { parameters: [genNum], returns: promiseNum } },
|
|
17
|
+
combine: {
|
|
18
|
+
type: "function",
|
|
19
|
+
signature: {
|
|
20
|
+
parameters: [
|
|
21
|
+
{
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: { label: { type: "promise", value: { type: "string" } }, nums: genNum },
|
|
24
|
+
required: ["label", "nums"],
|
|
25
|
+
additionalProperties: false,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
returns: { type: "promise", value: { type: "string" } },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
required: ["awaitAndDouble", "sumStream", "first", "combine"],
|
|
33
|
+
additionalProperties: false,
|
|
34
|
+
};
|
|
35
|
+
})(AsyncArgService || (AsyncArgService = {}));
|
|
36
|
+
function makeAsyncArg() {
|
|
37
|
+
return {
|
|
38
|
+
serviceName: "async-arg",
|
|
39
|
+
schema: Observe.fromConstant(AsyncArgService.schema),
|
|
40
|
+
awaitAndDouble: async (p) => (await p) * 2,
|
|
41
|
+
sumStream: async (g) => {
|
|
42
|
+
let sum = 0;
|
|
43
|
+
for await (const n of g)
|
|
44
|
+
sum += n;
|
|
45
|
+
return sum;
|
|
46
|
+
},
|
|
47
|
+
first: async (g) => {
|
|
48
|
+
for await (const n of g)
|
|
49
|
+
return n; // early exit → the arg generator's return() runs
|
|
50
|
+
return -1;
|
|
51
|
+
},
|
|
52
|
+
combine: async (foo) => {
|
|
53
|
+
const label = await foo.label;
|
|
54
|
+
let sum = 0;
|
|
55
|
+
for await (const n of foo.nums)
|
|
56
|
+
sum += n;
|
|
57
|
+
return `${label}:${sum}`;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const flush = async (times = 10) => {
|
|
62
|
+
for (let i = 0; i < times; i++)
|
|
63
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
64
|
+
};
|
|
65
|
+
function setup() {
|
|
66
|
+
const { a, b } = createRpcLoopbackTransport();
|
|
67
|
+
const ea = createRpcEndpoint(a);
|
|
68
|
+
const eb = createRpcEndpoint(b);
|
|
69
|
+
ea.expose("async", makeAsyncArg(), AsyncArgService.schema);
|
|
70
|
+
const svc = eb.consume("async", AsyncArgService.schema);
|
|
71
|
+
return { ea, eb, svc };
|
|
72
|
+
}
|
|
73
|
+
describe("Promise arguments", () => {
|
|
74
|
+
it("resolves a promise argument from the caller", async () => {
|
|
75
|
+
const { ea, eb, svc } = setup();
|
|
76
|
+
expect(await svc.awaitAndDouble(Promise.resolve(21))).toBe(42);
|
|
77
|
+
ea.close();
|
|
78
|
+
eb.close();
|
|
79
|
+
});
|
|
80
|
+
it("propagates a rejected promise argument", async () => {
|
|
81
|
+
const { ea, eb, svc } = setup();
|
|
82
|
+
await expect(svc.awaitAndDouble(Promise.reject(new Error("bad input")))).rejects.toThrowError("bad input");
|
|
83
|
+
ea.close();
|
|
84
|
+
eb.close();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe("AsyncGenerator arguments", () => {
|
|
88
|
+
it("streams a generator argument from the caller (host consumes fully)", async () => {
|
|
89
|
+
const { ea, eb, svc } = setup();
|
|
90
|
+
async function* nums() {
|
|
91
|
+
yield 1;
|
|
92
|
+
yield 2;
|
|
93
|
+
yield 3;
|
|
94
|
+
}
|
|
95
|
+
expect(await svc.sumStream(nums())).toBe(6);
|
|
96
|
+
ea.close();
|
|
97
|
+
eb.close();
|
|
98
|
+
});
|
|
99
|
+
it("runs the caller generator's finally when the host ends it early", async () => {
|
|
100
|
+
const { ea, eb, svc } = setup();
|
|
101
|
+
let ranFinally = false;
|
|
102
|
+
async function* provider() {
|
|
103
|
+
try {
|
|
104
|
+
yield 10;
|
|
105
|
+
yield 20;
|
|
106
|
+
yield 30;
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
ranFinally = true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
expect(await svc.first(provider())).toBe(10);
|
|
113
|
+
await flush();
|
|
114
|
+
expect(ranFinally).toBe(true); // host's early return propagated back to the caller
|
|
115
|
+
ea.close();
|
|
116
|
+
eb.close();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
describe("mixed constructor arguments in a nested object", () => {
|
|
120
|
+
it("services a Promise and an AsyncGenerator inside one object argument", async () => {
|
|
121
|
+
const { ea, eb, svc } = setup();
|
|
122
|
+
async function* nums() {
|
|
123
|
+
yield 4;
|
|
124
|
+
yield 5;
|
|
125
|
+
}
|
|
126
|
+
const result = await svc.combine({ label: Promise.resolve("total"), nums: nums() });
|
|
127
|
+
expect(result).toBe("total:9");
|
|
128
|
+
ea.close();
|
|
129
|
+
eb.close();
|
|
130
|
+
});
|
|
131
|
+
it("works over a real MessageChannel", async () => {
|
|
132
|
+
const { port1, port2 } = new MessageChannel();
|
|
133
|
+
const ea = createRpcEndpoint(createMessagePortTransport(port1));
|
|
134
|
+
const eb = createRpcEndpoint(createMessagePortTransport(port2));
|
|
135
|
+
ea.expose("async", makeAsyncArg(), AsyncArgService.schema);
|
|
136
|
+
const svc = eb.consume("async", AsyncArgService.schema);
|
|
137
|
+
async function* nums() {
|
|
138
|
+
yield 7;
|
|
139
|
+
yield 8;
|
|
140
|
+
}
|
|
141
|
+
expect(await svc.awaitAndDouble(Promise.resolve(5))).toBe(10);
|
|
142
|
+
expect(await svc.sumStream(nums())).toBe(15);
|
|
143
|
+
ea.close();
|
|
144
|
+
eb.close();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
//# sourceMappingURL=arg-async.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arg-async.test.js","sourceRoot":"","sources":["../src/arg-async.test.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAG9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,0BAA0B,EAAE,MAAM,wCAAwC,CAAC;AAWpF,IAAU,eAAe,CA2BxB;AA3BD,WAAU,eAAe;IACrB,MAAM,UAAU,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAW,CAAC;IAC3E,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAW,CAAC;IAC5D,sBAAM,GAAG;QAClB,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACR,cAAc,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE;YAClG,SAAS,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE;YACzF,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE;YACrF,OAAO,EAAE;gBACL,IAAI,EAAE,UAAU;gBAChB,SAAS,EAAE;oBACP,UAAU,EAAE;wBACR;4BACI,IAAI,EAAE,QAAQ;4BACd,UAAU,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;4BACnF,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;4BAC3B,oBAAoB,EAAE,KAAK;yBAC9B;qBACJ;oBACD,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;iBAC1D;aACJ;SACJ;QACD,QAAQ,EAAE,CAAC,gBAAgB,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,CAAC;QAC7D,oBAAoB,EAAE,KAAK;KACJ,CAAC;AAChC,CAAC,EA3BS,eAAe,KAAf,eAAe,QA2BxB;AAED,SAAS,YAAY;IACjB,OAAO;QACH,WAAW,EAAE,WAAW;QACxB,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,eAAe,CAAC,MAAM,CAAC;QACpD,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC;QAC1C,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YACnB,IAAI,GAAG,GAAG,CAAC,CAAC;YACZ,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC;gBAAE,GAAG,IAAI,CAAC,CAAC;YAClC,OAAO,GAAG,CAAC;QACf,CAAC;QACD,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YACf,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC;gBAAE,OAAO,CAAC,CAAC,CAAC,iDAAiD;YACpF,OAAO,CAAC,CAAC,CAAC;QACd,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACnB,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC;YAC9B,IAAI,GAAG,GAAG,CAAC,CAAC;YACZ,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,GAAG,CAAC,IAAI;gBAAE,GAAG,IAAI,CAAC,CAAC;YACzC,OAAO,GAAG,KAAK,IAAI,GAAG,EAAE,CAAC;QAC7B,CAAC;KACJ,CAAC;AACN,CAAC;AAED,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,GAAG,EAAE,EAAE,EAAE;IAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE;QAAE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/E,CAAC,CAAC;AAEF,SAAS,KAAK;IACV,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,0BAA0B,EAAE,CAAC;IAC9C,MAAM,EAAE,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAChC,MAAM,EAAE,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IAC3D,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IACxD,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;AAC3B,CAAC;AAED,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QAChC,MAAM,CAAC,MAAM,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/D,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,EAAE,CAAC,KAAK,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QAChC,MAAM,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;QAC3G,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,EAAE,CAAC,KAAK,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QAChC,KAAK,SAAS,CAAC,CAAC,IAAI;YAChB,MAAM,CAAC,CAAC;YACR,MAAM,CAAC,CAAC;YACR,MAAM,CAAC,CAAC;QACZ,CAAC;QACD,MAAM,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5C,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,EAAE,CAAC,KAAK,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC7E,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QAChC,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,KAAK,SAAS,CAAC,CAAC,QAAQ;YACpB,IAAI,CAAC;gBACD,MAAM,EAAE,CAAC;gBACT,MAAM,EAAE,CAAC;gBACT,MAAM,EAAE,CAAC;YACb,CAAC;oBAAS,CAAC;gBACP,UAAU,GAAG,IAAI,CAAC;YACtB,CAAC;QACL,CAAC;QACD,MAAM,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7C,MAAM,KAAK,EAAE,CAAC;QACd,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,oDAAoD;QACnF,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,EAAE,CAAC,KAAK,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC5D,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QAChC,KAAK,SAAS,CAAC,CAAC,IAAI;YAChB,MAAM,CAAC,CAAC;YACR,MAAM,CAAC,CAAC;QACZ,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QACpF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/B,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,EAAE,CAAC,KAAK,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAC9C,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,cAAc,EAAE,CAAC;QAC9C,MAAM,EAAE,GAAG,iBAAiB,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC,CAAC;QAChE,MAAM,EAAE,GAAG,iBAAiB,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC,CAAC;QAChE,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;QAC3D,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;QAExD,KAAK,SAAS,CAAC,CAAC,IAAI;YAChB,MAAM,CAAC,CAAC;YACR,MAAM,CAAC,CAAC;QACZ,CAAC;QACD,MAAM,CAAC,MAAM,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9D,MAAM,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAE7C,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,EAAE,CAAC,KAAK,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Data } from "@adobe/data";
|
|
2
|
+
import type { Observe } from "@adobe/data/observe";
|
|
3
|
+
import type { Schema } from "@adobe/data/schema";
|
|
4
|
+
/**
|
|
5
|
+
* Constructor-typed values (`Observe`/`Promise`/`AsyncGenerator`) passed as — or
|
|
6
|
+
* nested inside — a call argument flow the "wrong" way: from the CALLER to the
|
|
7
|
+
* CALLEE. They can't be cloned onto the wire, so the caller replaces each with a
|
|
8
|
+
* numeric ref and services it over a reverse channel; the callee reconstructs a
|
|
9
|
+
* local value from the ref. These pure walkers find the constructor positions in
|
|
10
|
+
* an argument using its parameter schema and delegate to the caller's handlers.
|
|
11
|
+
*
|
|
12
|
+
* Only `Data` ever crosses; the constructor's payload values are themselves
|
|
13
|
+
* `Data`. Function (callback) arguments are not supported.
|
|
14
|
+
*/
|
|
15
|
+
export interface MarshalHandlers {
|
|
16
|
+
readonly observe: (obs: Observe<Data>) => number;
|
|
17
|
+
readonly promise: (promise: Promise<Data>) => number;
|
|
18
|
+
readonly generator: (generator: AsyncGenerator<Data>) => number;
|
|
19
|
+
}
|
|
20
|
+
export interface UnmarshalHandlers {
|
|
21
|
+
readonly observe: (ref: number) => Observe<Data>;
|
|
22
|
+
readonly promise: (ref: number) => Promise<Data>;
|
|
23
|
+
readonly generator: (ref: number) => AsyncGenerator<Data>;
|
|
24
|
+
}
|
|
25
|
+
/** Replace every constructor-typed position in `value` (guided by `schema`) with a ref. */
|
|
26
|
+
export declare function marshalArgValue(value: unknown, schema: Schema, h: MarshalHandlers): Data;
|
|
27
|
+
/** Reconstruct every constructor-typed position (a ref) in `value` into a local value. */
|
|
28
|
+
export declare function unmarshalArgValue(value: unknown, schema: Schema, h: UnmarshalHandlers): unknown;
|
|
29
|
+
/** Map each argument through the marshaller using its parameter schema. */
|
|
30
|
+
export declare function marshalArgsWith(args: readonly unknown[], params: readonly Schema[] | undefined, h: MarshalHandlers): Data[];
|
|
31
|
+
/** Map each argument through the unmarshaller using its parameter schema. */
|
|
32
|
+
export declare function unmarshalArgsWith(args: readonly Data[], params: readonly Schema[] | undefined, h: UnmarshalHandlers): unknown[];
|
|
33
|
+
/** True if the schema tree contains no type-constructor (observe/promise/generator/function). */
|
|
34
|
+
export declare function isPureDataSchema(schema: Schema): boolean;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
// Refs travel as a one-key tagged envelope so a literal value at a constructor
|
|
3
|
+
// position (a caller type violation) can never be mistaken for a ref.
|
|
4
|
+
const REF_KEY = "$rpcRef";
|
|
5
|
+
function isPlainObject(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
function isRefEnvelope(value) {
|
|
9
|
+
return isPlainObject(value) && typeof value[REF_KEY] === "number";
|
|
10
|
+
}
|
|
11
|
+
function isThenable(value) {
|
|
12
|
+
return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
|
|
13
|
+
}
|
|
14
|
+
function isAsyncIterable(value) {
|
|
15
|
+
return value !== null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
|
|
16
|
+
}
|
|
17
|
+
/** Replace every constructor-typed position in `value` (guided by `schema`) with a ref. */
|
|
18
|
+
export function marshalArgValue(value, schema, h) {
|
|
19
|
+
if (schema.type === "observe")
|
|
20
|
+
return typeof value === "function" ? { [REF_KEY]: h.observe(value) } : value;
|
|
21
|
+
if (schema.type === "promise")
|
|
22
|
+
return isThenable(value) ? { [REF_KEY]: h.promise(value) } : value;
|
|
23
|
+
if (schema.type === "generator")
|
|
24
|
+
return isAsyncIterable(value) ? { [REF_KEY]: h.generator(value) } : value;
|
|
25
|
+
if ((schema.type === "object" || schema.properties !== undefined) && isPlainObject(value)) {
|
|
26
|
+
const props = schema.properties ?? {};
|
|
27
|
+
const out = { ...value };
|
|
28
|
+
for (const [key, childSchema] of Object.entries(props)) {
|
|
29
|
+
if (key in value)
|
|
30
|
+
out[key] = marshalArgValue(value[key], childSchema, h);
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
if ((schema.type === "array" || schema.items !== undefined) && Array.isArray(value) && schema.items !== undefined) {
|
|
35
|
+
const items = schema.items;
|
|
36
|
+
return value.map((v) => marshalArgValue(v, items, h));
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
/** Reconstruct every constructor-typed position (a ref) in `value` into a local value. */
|
|
41
|
+
export function unmarshalArgValue(value, schema, h) {
|
|
42
|
+
if (schema.type === "observe")
|
|
43
|
+
return isRefEnvelope(value) ? h.observe(value[REF_KEY]) : value;
|
|
44
|
+
if (schema.type === "promise")
|
|
45
|
+
return isRefEnvelope(value) ? h.promise(value[REF_KEY]) : value;
|
|
46
|
+
if (schema.type === "generator")
|
|
47
|
+
return isRefEnvelope(value) ? h.generator(value[REF_KEY]) : value;
|
|
48
|
+
if ((schema.type === "object" || schema.properties !== undefined) && isPlainObject(value)) {
|
|
49
|
+
const props = schema.properties ?? {};
|
|
50
|
+
const out = { ...value };
|
|
51
|
+
for (const [key, childSchema] of Object.entries(props)) {
|
|
52
|
+
if (key in value)
|
|
53
|
+
out[key] = unmarshalArgValue(value[key], childSchema, h);
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
if ((schema.type === "array" || schema.items !== undefined) && Array.isArray(value) && schema.items !== undefined) {
|
|
58
|
+
const items = schema.items;
|
|
59
|
+
return value.map((v) => unmarshalArgValue(v, items, h));
|
|
60
|
+
}
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
/** Map each argument through the marshaller using its parameter schema. */
|
|
64
|
+
export function marshalArgsWith(args, params, h) {
|
|
65
|
+
if (params === undefined)
|
|
66
|
+
return args;
|
|
67
|
+
return args.map((arg, i) => (params[i] === undefined ? arg : marshalArgValue(arg, params[i], h)));
|
|
68
|
+
}
|
|
69
|
+
/** Map each argument through the unmarshaller using its parameter schema. */
|
|
70
|
+
export function unmarshalArgsWith(args, params, h) {
|
|
71
|
+
if (params === undefined)
|
|
72
|
+
return args;
|
|
73
|
+
return args.map((arg, i) => (params[i] === undefined ? arg : unmarshalArgValue(arg, params[i], h)));
|
|
74
|
+
}
|
|
75
|
+
/** True if the schema tree contains no type-constructor (observe/promise/generator/function). */
|
|
76
|
+
export function isPureDataSchema(schema) {
|
|
77
|
+
if (schema.type === "observe" || schema.type === "promise" || schema.type === "generator" || schema.type === "function") {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
if (schema.properties !== undefined) {
|
|
81
|
+
for (const child of Object.values(schema.properties)) {
|
|
82
|
+
if (!isPureDataSchema(child))
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (schema.items !== undefined && !isPureDataSchema(schema.items))
|
|
87
|
+
return false;
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=arg-marshal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arg-marshal.js","sourceRoot":"","sources":["../src/arg-marshal.ts"],"names":[],"mappings":"AAAA,uDAAuD;AA8BvD,+EAA+E;AAC/E,sEAAsE;AACtE,MAAM,OAAO,GAAG,SAAS,CAAC;AAG1B,SAAS,aAAa,CAAC,KAAc;IACjC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAChF,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACjC,OAAO,aAAa,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;AACtE,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IAC9B,OAAO,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,UAAU,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAQ,KAA4B,CAAC,IAAI,KAAK,UAAU,CAAC;AACpJ,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACnC,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAQ,KAA8C,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,UAAU,CAAC;AACtJ,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,eAAe,CAAC,KAAc,EAAE,MAAc,EAAE,CAAkB;IAC9E,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,KAAsB,CAAC,EAAE,CAAC,CAAC,CAAE,KAAc,CAAC;IACvI,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAE,KAAc,CAAC;IAC5G,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW;QAAE,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAE,KAAc,CAAC;IACrH,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QACxF,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC;QACtC,MAAM,GAAG,GAAyB,EAAE,GAAI,KAA8B,EAAE,CAAC;QACzE,KAAK,MAAM,CAAC,GAAG,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACrD,IAAI,GAAG,IAAI,KAAK;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;QAC7E,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,OAAO,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAChH,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC3B,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,KAAa,CAAC;AACzB,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,iBAAiB,CAAC,KAAc,EAAE,MAAc,EAAE,CAAoB;IAClF,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/F,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/F,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW;QAAE,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACnG,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QACxF,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC;QACtC,MAAM,GAAG,GAA4B,EAAE,GAAG,KAAK,EAAE,CAAC;QAClD,KAAK,MAAM,CAAC,GAAG,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACrD,IAAI,GAAG,IAAI,KAAK;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,OAAO,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAChH,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC3B,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,eAAe,CAAC,IAAwB,EAAE,MAAqC,EAAE,CAAkB;IAC/G,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,IAAc,CAAC;IAChD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAE,GAAY,CAAC,CAAC,CAAC,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAChH,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,iBAAiB,CAAC,IAAqB,EAAE,MAAqC,EAAE,CAAoB;IAChH,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,IAAiB,CAAC;IACnD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACxG,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC3C,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACtH,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QAClC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;YACnD,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAC;QAC/C,CAAC;IACL,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAChF,OAAO,IAAI,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
import { describe, it, expect } from "vitest";
|
|
3
|
+
import { Observe } from "@adobe/data/observe";
|
|
4
|
+
import { createRpcEndpoint } from "./create-endpoint.js";
|
|
5
|
+
import { createRpcLoopbackTransport } from "./transports/loopback-transport.js";
|
|
6
|
+
import { createMessagePortTransport } from "./transports/message-port-transport.js";
|
|
7
|
+
var DisplayService;
|
|
8
|
+
(function (DisplayService) {
|
|
9
|
+
DisplayService.schema = {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {
|
|
12
|
+
display: {
|
|
13
|
+
type: "function",
|
|
14
|
+
signature: {
|
|
15
|
+
parameters: [
|
|
16
|
+
{
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {
|
|
19
|
+
alpha: { type: "observe", value: { type: "number" } },
|
|
20
|
+
beta: { type: "observe", value: { type: "string" } },
|
|
21
|
+
},
|
|
22
|
+
required: ["alpha", "beta"],
|
|
23
|
+
additionalProperties: false,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
returns: { type: "observe", value: { type: "string" } },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ["display"],
|
|
31
|
+
additionalProperties: false,
|
|
32
|
+
};
|
|
33
|
+
})(DisplayService || (DisplayService = {}));
|
|
34
|
+
// The callee combines the two caller-side observes into a derived string observe.
|
|
35
|
+
function makeDisplay() {
|
|
36
|
+
return {
|
|
37
|
+
serviceName: "display",
|
|
38
|
+
schema: Observe.fromConstant(DisplayService.schema),
|
|
39
|
+
display: (foo) => Observe.withMap(Observe.fromProperties({ a: foo.alpha, b: foo.beta }), ({ a, b }) => `${b}=${a}`),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const flush = async (times = 10) => {
|
|
43
|
+
for (let i = 0; i < times; i++)
|
|
44
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
45
|
+
};
|
|
46
|
+
/** A value observe that also counts how many active subscriptions it has. */
|
|
47
|
+
function tracked(initial) {
|
|
48
|
+
const [raw, set] = Observe.createState(initial);
|
|
49
|
+
let active = 0;
|
|
50
|
+
const observe = (notify) => {
|
|
51
|
+
active++;
|
|
52
|
+
const un = raw(notify);
|
|
53
|
+
return () => {
|
|
54
|
+
active--;
|
|
55
|
+
un();
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
return { observe, set, active: () => active };
|
|
59
|
+
}
|
|
60
|
+
describe("observe values as named object arguments", () => {
|
|
61
|
+
it("streams caller-side arg observes to the callee and reacts to updates", async () => {
|
|
62
|
+
const { a, b } = createRpcLoopbackTransport();
|
|
63
|
+
const ea = createRpcEndpoint(a);
|
|
64
|
+
const eb = createRpcEndpoint(b);
|
|
65
|
+
ea.expose("display", makeDisplay(), DisplayService.schema);
|
|
66
|
+
const svc = eb.consume("display", DisplayService.schema);
|
|
67
|
+
const alpha = tracked(1);
|
|
68
|
+
const beta = tracked("x");
|
|
69
|
+
const seen = [];
|
|
70
|
+
const un = svc.display({ alpha: alpha.observe, beta: beta.observe })((v) => seen.push(v));
|
|
71
|
+
await flush();
|
|
72
|
+
expect(seen.at(-1)).toBe("x=1");
|
|
73
|
+
expect(alpha.active()).toBe(1); // callee subscribed back to the caller's arg observe
|
|
74
|
+
expect(beta.active()).toBe(1);
|
|
75
|
+
alpha.set(2);
|
|
76
|
+
await flush();
|
|
77
|
+
expect(seen.at(-1)).toBe("x=2");
|
|
78
|
+
beta.set("y");
|
|
79
|
+
await flush();
|
|
80
|
+
expect(seen.at(-1)).toBe("y=2");
|
|
81
|
+
// Unsubscribing the returned observe tears down the reverse subscriptions too.
|
|
82
|
+
un();
|
|
83
|
+
await flush();
|
|
84
|
+
expect(alpha.active()).toBe(0);
|
|
85
|
+
expect(beta.active()).toBe(0);
|
|
86
|
+
const count = seen.length;
|
|
87
|
+
alpha.set(99);
|
|
88
|
+
await flush();
|
|
89
|
+
expect(seen.length).toBe(count); // no further values after teardown
|
|
90
|
+
ea.close();
|
|
91
|
+
eb.close();
|
|
92
|
+
});
|
|
93
|
+
it("works over a real MessageChannel and releases providers on channel close", async () => {
|
|
94
|
+
const { port1, port2 } = new MessageChannel();
|
|
95
|
+
const ea = createRpcEndpoint(createMessagePortTransport(port1));
|
|
96
|
+
const eb = createRpcEndpoint(createMessagePortTransport(port2));
|
|
97
|
+
ea.expose("display", makeDisplay(), DisplayService.schema);
|
|
98
|
+
const svc = eb.consume("display", DisplayService.schema);
|
|
99
|
+
const alpha = tracked(10);
|
|
100
|
+
const beta = tracked("v");
|
|
101
|
+
const seen = [];
|
|
102
|
+
svc.display({ alpha: alpha.observe, beta: beta.observe })((v) => seen.push(v));
|
|
103
|
+
await flush();
|
|
104
|
+
expect(seen.at(-1)).toBe("v=10");
|
|
105
|
+
expect(alpha.active()).toBe(1);
|
|
106
|
+
// Closing the consuming endpoint releases the callee's reverse subscriptions.
|
|
107
|
+
eb.close();
|
|
108
|
+
await flush();
|
|
109
|
+
expect(alpha.active()).toBe(0);
|
|
110
|
+
expect(beta.active()).toBe(0);
|
|
111
|
+
ea.close();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
//# sourceMappingURL=arg-observe.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arg-observe.test.js","sourceRoot":"","sources":["../src/arg-observe.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Data } from "@adobe/data";
|
|
2
|
+
import type { Notify } from "@adobe/data/observe";
|
|
3
|
+
import type { Schema } from "@adobe/data/schema";
|
|
4
|
+
import type { RpcMessage } from "../protocol.js";
|
|
5
|
+
/** One in-flight promise call, awaiting a `resolve`/`reject` for its id. */
|
|
6
|
+
export interface CallSlot {
|
|
7
|
+
readonly resolve: (value: Data) => void;
|
|
8
|
+
readonly reject: (error: unknown) => void;
|
|
9
|
+
timer?: ReturnType<typeof setTimeout>;
|
|
10
|
+
}
|
|
11
|
+
/** The result delivered to one awaited generator `pull`. */
|
|
12
|
+
export type PullResult = {
|
|
13
|
+
readonly done: false;
|
|
14
|
+
readonly value: Data;
|
|
15
|
+
} | {
|
|
16
|
+
readonly done: true;
|
|
17
|
+
readonly value: Data | undefined;
|
|
18
|
+
} | {
|
|
19
|
+
readonly error: Error;
|
|
20
|
+
};
|
|
21
|
+
/** One live consumed generator: a FIFO of pull resolvers awaiting yield/done/throw. */
|
|
22
|
+
export interface CallerGenSlot {
|
|
23
|
+
readonly pulls: Array<(result: PullResult) => void>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The caller-side surface the consume wrappers write into. The endpoint owns
|
|
27
|
+
* these tables and settles them from incoming response messages; the wrappers
|
|
28
|
+
* only allocate an id, register a slot, and send a request. All tables are keyed
|
|
29
|
+
* by a LOCAL id — unique per-originator, separate from the host-side tables.
|
|
30
|
+
*/
|
|
31
|
+
export interface CallerContext {
|
|
32
|
+
readonly send: (msg: RpcMessage) => void;
|
|
33
|
+
readonly nextId: () => number;
|
|
34
|
+
readonly callPending: Map<number, CallSlot>;
|
|
35
|
+
readonly callerSubs: Map<number, Notify<Data>>;
|
|
36
|
+
readonly callerGens: Map<number, CallerGenSlot>;
|
|
37
|
+
readonly defaultTimeoutMs?: number;
|
|
38
|
+
readonly isClosed: () => boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Transform outgoing args: replace any `Observe` (guided by `params`) with a
|
|
41
|
+
* ref the caller streams back on demand, tracked under `opId` for cleanup.
|
|
42
|
+
* Returns wire-safe `Data` args.
|
|
43
|
+
*/
|
|
44
|
+
readonly marshalArgs: (args: readonly unknown[], params: readonly Schema[] | undefined, opId: number) => Data[];
|
|
45
|
+
/** Release the arg-observe providers registered for `opId` (call/subscription done). */
|
|
46
|
+
readonly releaseArgRefs: (opId: number) => void;
|
|
47
|
+
}
|