@adobe/uix-core 0.6.5 → 0.7.1-nightly.20230114

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.
Files changed (94) hide show
  1. package/dist/__helpers__/jest.messagechannel.d.cts +2 -0
  2. package/dist/__helpers__/jest.messagechannel.d.cts.map +1 -0
  3. package/dist/__mocks__/mock-finalization-registry.d.ts +11 -0
  4. package/dist/__mocks__/mock-finalization-registry.d.ts.map +1 -0
  5. package/dist/__mocks__/mock-weak-ref.d.ts +7 -0
  6. package/dist/__mocks__/mock-weak-ref.d.ts.map +1 -0
  7. package/dist/constants.d.ts +8 -0
  8. package/dist/constants.d.ts.map +1 -0
  9. package/dist/cross-realm-object.d.ts +44 -0
  10. package/dist/cross-realm-object.d.ts.map +1 -0
  11. package/dist/debuglog.d.ts +11 -0
  12. package/dist/debuglog.d.ts.map +1 -1
  13. package/dist/index.d.ts +4 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +906 -7
  16. package/dist/index.js.map +1 -1
  17. package/dist/message-wrapper.d.ts +9 -0
  18. package/dist/message-wrapper.d.ts.map +1 -0
  19. package/dist/object-simulator.d.ts +28 -0
  20. package/dist/object-simulator.d.ts.map +1 -0
  21. package/dist/object-simulator.test.d.ts +2 -0
  22. package/dist/object-simulator.test.d.ts.map +1 -0
  23. package/dist/object-walker.d.ts +30 -0
  24. package/dist/object-walker.d.ts.map +1 -0
  25. package/dist/promises/index.d.ts +3 -0
  26. package/dist/promises/index.d.ts.map +1 -0
  27. package/dist/promises/promise-wrappers.test.d.ts +2 -0
  28. package/dist/promises/promise-wrappers.test.d.ts.map +1 -0
  29. package/dist/promises/timed.d.ts +15 -0
  30. package/dist/promises/timed.d.ts.map +1 -0
  31. package/dist/promises/wait.d.ts +7 -0
  32. package/dist/promises/wait.d.ts.map +1 -0
  33. package/dist/remote-subject.d.ts +70 -0
  34. package/dist/remote-subject.d.ts.map +1 -0
  35. package/dist/rpc/call-receiver.d.ts +4 -0
  36. package/dist/rpc/call-receiver.d.ts.map +1 -0
  37. package/dist/rpc/call-receiver.test.d.ts +2 -0
  38. package/dist/rpc/call-receiver.test.d.ts.map +1 -0
  39. package/dist/rpc/call-sender.d.ts +4 -0
  40. package/dist/rpc/call-sender.d.ts.map +1 -0
  41. package/dist/rpc/call-sender.test.d.ts +2 -0
  42. package/dist/rpc/call-sender.test.d.ts.map +1 -0
  43. package/dist/rpc/index.d.ts +3 -0
  44. package/dist/rpc/index.d.ts.map +1 -0
  45. package/dist/tickets.d.ts +34 -0
  46. package/dist/tickets.d.ts.map +1 -0
  47. package/dist/tunnel/index.d.ts +2 -0
  48. package/dist/tunnel/index.d.ts.map +1 -0
  49. package/dist/tunnel/tunnel-messenger.d.ts +25 -0
  50. package/dist/tunnel/tunnel-messenger.d.ts.map +1 -0
  51. package/dist/tunnel/tunnel-messenger.test.d.ts +2 -0
  52. package/dist/tunnel/tunnel-messenger.test.d.ts.map +1 -0
  53. package/dist/tunnel/tunnel.d.ts +62 -0
  54. package/dist/tunnel/tunnel.d.ts.map +1 -0
  55. package/dist/tunnel/tunnel.test.d.ts +2 -0
  56. package/dist/tunnel/tunnel.test.d.ts.map +1 -0
  57. package/dist/types.d.ts +1 -4
  58. package/dist/types.d.ts.map +1 -1
  59. package/dist/value-assertions.d.ts +13 -0
  60. package/dist/value-assertions.d.ts.map +1 -0
  61. package/package.json +1 -1
  62. package/src/__helpers__/jest.messagechannel.cjs +3 -0
  63. package/src/__mocks__/mock-finalization-registry.ts +13 -0
  64. package/src/__mocks__/mock-weak-ref.ts +10 -0
  65. package/src/constants.ts +10 -0
  66. package/src/cross-realm-object.ts +117 -0
  67. package/src/debuglog.ts +1 -1
  68. package/src/index.ts +4 -1
  69. package/src/message-wrapper.ts +35 -0
  70. package/src/object-simulator.test.ts +328 -0
  71. package/src/object-simulator.ts +145 -0
  72. package/src/object-walker.ts +132 -0
  73. package/src/promises/index.ts +2 -0
  74. package/src/promises/promise-wrappers.test.ts +63 -0
  75. package/src/promises/timed.ts +41 -0
  76. package/src/promises/wait.ts +10 -0
  77. package/src/remote-subject.ts +185 -0
  78. package/src/rpc/call-receiver.test.ts +90 -0
  79. package/src/rpc/call-receiver.ts +29 -0
  80. package/src/rpc/call-sender.test.ts +73 -0
  81. package/src/rpc/call-sender.ts +72 -0
  82. package/src/rpc/index.ts +2 -0
  83. package/src/tickets.ts +71 -0
  84. package/src/tunnel/index.ts +1 -0
  85. package/src/tunnel/tunnel-messenger.test.ts +183 -0
  86. package/src/tunnel/tunnel-messenger.ts +99 -0
  87. package/src/tunnel/tunnel.test.ts +211 -0
  88. package/src/tunnel/tunnel.ts +322 -0
  89. package/src/types.ts +3 -5
  90. package/src/value-assertions.ts +58 -0
  91. package/tsconfig.json +2 -6
  92. package/dist/timeout-promise.d.ts +0 -12
  93. package/dist/timeout-promise.d.ts.map +0 -1
  94. package/src/timeout-promise.ts +0 -36
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Promise that resolves after a specific time
3
+ *
4
+ * @internal
5
+ */
6
+ export function wait(ms: number) {
7
+ return new Promise((resolve) => {
8
+ setTimeout(resolve, ms);
9
+ });
10
+ }
@@ -0,0 +1,185 @@
1
+ import type {
2
+ CallArgsTicket,
3
+ CallTicket,
4
+ DefTicket,
5
+ RejectTicket,
6
+ ResolveTicket,
7
+ RespondTicket,
8
+ CleanupTicket,
9
+ } from "./tickets";
10
+ import type { Materialized, Simulated } from "./object-walker";
11
+ import EventEmitter from "eventemitter3";
12
+
13
+ type EvTypeDef = `${string}_f`;
14
+ type EvTypeGC = `${string}_g`;
15
+ type EvTypeCall = `${string}_c`;
16
+ type EvTypeRespond = `${string}_r`;
17
+ type EvTypeDestroyed = "destroyed";
18
+ type EvTypeConnected = "connected";
19
+ type EvTypeError = "error";
20
+
21
+ type RemoteDefEvent = {
22
+ type: EvTypeDef;
23
+ payload: DefTicket;
24
+ };
25
+ type RemoteCallEvent = {
26
+ type: EvTypeCall;
27
+ payload: CallArgsTicket;
28
+ };
29
+ type RemoteResolveEvent = {
30
+ type: EvTypeRespond;
31
+ payload: ResolveTicket;
32
+ };
33
+ type RemoteRejectEvent = {
34
+ type: EvTypeRespond;
35
+ payload: RejectTicket;
36
+ };
37
+ type RemoteCleanupEvent = {
38
+ type: EvTypeGC;
39
+ payload: CleanupTicket;
40
+ };
41
+ type RemoteReconnectedEvent = {
42
+ type: EvTypeConnected;
43
+ payload: void;
44
+ };
45
+ type RemoteDestroyedEvent = {
46
+ type: EvTypeDestroyed;
47
+ payload: void;
48
+ };
49
+ type RemoteErrorEvent = {
50
+ type: EvTypeError;
51
+ payload: Error;
52
+ };
53
+
54
+ export type RemoteEvents =
55
+ | RemoteDefEvent
56
+ | RemoteCallEvent
57
+ | RemoteResolveEvent
58
+ | RemoteRejectEvent
59
+ | RemoteCleanupEvent
60
+ | RemoteReconnectedEvent
61
+ | RemoteDestroyedEvent
62
+ | RemoteErrorEvent;
63
+
64
+ type Simulates = <T>(localObject: T) => Simulated<T>;
65
+ type Materializes = <T>(simulatedObject: T) => Materialized<T>;
66
+
67
+ export interface Simulator {
68
+ // #region Properties
69
+
70
+ materialize: Materializes;
71
+ simulate: Simulates;
72
+
73
+ // #endregion Properties
74
+ }
75
+
76
+ type Mapper = Simulates | Materializes;
77
+
78
+ export class RemoteSubject {
79
+ // #region Properties
80
+
81
+ private emitter: EventEmitter;
82
+ private simulator: Simulator;
83
+
84
+ // #endregion Properties
85
+
86
+ // #region Constructors
87
+
88
+ constructor(emitter: EventEmitter, simulator: Simulator) {
89
+ this.emitter = emitter;
90
+ this.simulator = simulator;
91
+ }
92
+
93
+ // #endregion Constructors
94
+
95
+ // #region Public Methods
96
+
97
+ notifyCleanup(ticket: DefTicket) {
98
+ return this.emitter.emit(`${ticket.fnId}_g`, {});
99
+ }
100
+
101
+ notifyConnect() {
102
+ return this.emitter.emit("connected");
103
+ }
104
+
105
+ notifyDestroy() {
106
+ return this.emitter.emit("destroyed");
107
+ }
108
+
109
+ onCall(ticket: DefTicket, handler: (ticket: CallArgsTicket) => void) {
110
+ return this.subscribe(`${ticket.fnId}_c`, (ticket: CallArgsTicket) =>
111
+ handler(this.processCallTicket(ticket, this.simulator.materialize))
112
+ );
113
+ }
114
+
115
+ onConnected(handler: () => void) {
116
+ return this.subscribe("connected", handler);
117
+ }
118
+
119
+ onDestroyed(handler: () => void) {
120
+ return this.subscribe("destroyed", handler);
121
+ }
122
+
123
+ onOutOfScope(ticket: DefTicket, handler: () => void) {
124
+ return this.subscribeOnce(`${ticket.fnId}_g`, handler);
125
+ }
126
+
127
+ onRespond(ticket: CallTicket, handler: (ticket: RespondTicket) => void) {
128
+ const fnAndCall = `${ticket.fnId}${ticket.callId}`;
129
+ return this.subscribeOnce(`${fnAndCall}_r`, (ticket: RespondTicket) =>
130
+ handler(this.processResponseTicket(ticket, this.simulator.materialize))
131
+ );
132
+ }
133
+
134
+ respond(ticket: RespondTicket) {
135
+ const fnAndCall = `${ticket.fnId}${ticket.callId}`;
136
+ return this.emitter.emit(
137
+ `${fnAndCall}_r`,
138
+ this.processResponseTicket(ticket, this.simulator.simulate)
139
+ );
140
+ }
141
+
142
+ send(ticket: CallArgsTicket) {
143
+ return this.emitter.emit(
144
+ `${ticket.fnId}_c`,
145
+ this.processCallTicket(ticket, this.simulator.simulate)
146
+ );
147
+ }
148
+
149
+ // #endregion Public Methods
150
+
151
+ // #region Private Methods
152
+
153
+ private processCallTicket(
154
+ { args, ...ticket }: CallArgsTicket,
155
+ mapper: Mapper
156
+ ) {
157
+ return {
158
+ ...ticket,
159
+ args: args.map(mapper),
160
+ };
161
+ }
162
+
163
+ private processResponseTicket(ticket: RespondTicket, mapper: Mapper) {
164
+ return ticket.status === "resolve"
165
+ ? { ...ticket, value: mapper(ticket.value) }
166
+ : ticket;
167
+ }
168
+
169
+ private subscribe(type: string, handler: (arg: unknown) => void) {
170
+ this.emitter.on(type, handler);
171
+ return () => {
172
+ this.emitter.off(type, handler);
173
+ };
174
+ }
175
+
176
+ private subscribeOnce(type: string, handler: (arg: unknown) => void) {
177
+ const once = (arg: unknown) => {
178
+ this.emitter.off(type, once);
179
+ handler(arg);
180
+ };
181
+ return this.subscribe(type, once);
182
+ }
183
+
184
+ // #endregion Private Methods
185
+ }
@@ -0,0 +1,90 @@
1
+ import { wait } from "../promises/wait";
2
+ import EventEmitter from "eventemitter3";
3
+ import { RemoteSubject } from "../remote-subject";
4
+ import { receiveCalls } from "./call-receiver";
5
+ import { FakeFinalizationRegistry } from "../__mocks__/mock-finalization-registry";
6
+ import { FakeWeakRef } from "../__mocks__/mock-weak-ref";
7
+ import { ObjectSimulator } from "../object-simulator";
8
+
9
+ describe("a listener for remote calls to a local function", () => {
10
+ const MURMURS = ["baa", "moo", "ahoy"];
11
+ const FARAWAY_SOUND = "(distant) riiiiicolaaaa";
12
+ const ECHOES = "(echoes) riiiiicolaaaa";
13
+ const village = jest.fn().mockReturnValue(MURMURS);
14
+ const villageId = "village_1";
15
+ const villageTicket = { fnId: villageId };
16
+ let emitter: EventEmitter;
17
+ let subject: RemoteSubject;
18
+ let simulator: ObjectSimulator;
19
+ beforeEach(() => {
20
+ village.mockClear();
21
+ jest.spyOn(console, "error").mockImplementation(() => {});
22
+ emitter = new EventEmitter();
23
+ simulator = ObjectSimulator.create(emitter, FakeFinalizationRegistry);
24
+ subject = simulator.subject;
25
+ receiveCalls(village, villageTicket, new FakeWeakRef(subject));
26
+ });
27
+ it("turns fn_call events into calls to local function", async () => {
28
+ const responder = jest.fn();
29
+ const call4Ticket = {
30
+ ...villageTicket,
31
+ callId: 4,
32
+ };
33
+ subject.onRespond(call4Ticket, responder);
34
+ subject.send({
35
+ ...call4Ticket,
36
+ args: [FARAWAY_SOUND, ECHOES],
37
+ });
38
+ await expect(village).toHaveBeenCalledWith(FARAWAY_SOUND, ECHOES);
39
+ expect(responder).toHaveBeenCalledWith(
40
+ expect.objectContaining({
41
+ ...call4Ticket,
42
+ status: "resolve",
43
+ value: expect.arrayContaining(["baa", "moo", "ahoy"]),
44
+ })
45
+ );
46
+ });
47
+ it("sends events notifying of rejections", async () => {
48
+ const responder = jest.fn();
49
+ const call500Ticket = {
50
+ ...villageTicket,
51
+ callId: 500,
52
+ };
53
+ subject.onRespond(call500Ticket, responder);
54
+ village.mockRejectedValueOnce(new Error("what is that infernal noise"));
55
+ subject.send({
56
+ ...call500Ticket,
57
+ args: [FARAWAY_SOUND],
58
+ });
59
+ await expect(village).toHaveBeenCalledWith(FARAWAY_SOUND);
60
+ await wait(100);
61
+ expect(responder).toHaveBeenCalledWith(
62
+ expect.objectContaining({
63
+ ...call500Ticket,
64
+ status: "reject",
65
+ error: expect.any(Error),
66
+ })
67
+ );
68
+ });
69
+ it.skip("unsubscribes itself when receiving a cleanup event", async () => {
70
+ const responder = jest.fn();
71
+ const call76Ticket = {
72
+ ...villageTicket,
73
+ callId: 76,
74
+ };
75
+ subject.onRespond(call76Ticket, responder);
76
+ village.mockRejectedValueOnce(new Error("what is that infernal noise"));
77
+
78
+ subject.notifyCleanup(call76Ticket);
79
+
80
+ await wait(100);
81
+
82
+ subject.send({
83
+ ...call76Ticket,
84
+ args: [FARAWAY_SOUND],
85
+ });
86
+
87
+ await wait(100);
88
+ expect(responder).not.toHaveBeenCalled();
89
+ });
90
+ });
@@ -0,0 +1,29 @@
1
+ import type { CallArgsTicket, DefTicket } from "../tickets";
2
+ import type { RemoteSubject } from "../remote-subject";
3
+
4
+ export function receiveCalls(
5
+ fn: CallableFunction,
6
+ ticket: DefTicket,
7
+ remote: WeakRef<RemoteSubject>
8
+ ) {
9
+ const responder = async ({ fnId, callId, args }: CallArgsTicket) => {
10
+ /* istanbul ignore next: should never happen */
11
+ try {
12
+ const value = await fn(...args);
13
+ remote.deref().respond({
14
+ fnId,
15
+ callId,
16
+ value,
17
+ status: "resolve",
18
+ });
19
+ } catch (error) {
20
+ remote.deref().respond({
21
+ fnId,
22
+ callId,
23
+ status: "reject",
24
+ error,
25
+ });
26
+ }
27
+ };
28
+ return remote.deref().onCall(ticket, responder);
29
+ }
@@ -0,0 +1,73 @@
1
+ import { RemoteSubject } from "../remote-subject";
2
+ import { makeCallSender } from "./call-sender";
3
+ import { ObjectSimulator } from "../object-simulator";
4
+ import { FakeFinalizationRegistry } from "../__mocks__/mock-finalization-registry";
5
+ import { FakeWeakRef } from "../__mocks__/mock-weak-ref";
6
+ import { wait } from "../promises/wait";
7
+ import EventEmitter from "eventemitter3";
8
+
9
+ describe("an proxy representing a function in the other realm", () => {
10
+ const SOUND = "RIIIICOLAAAA";
11
+ const FARAWAY_SOUND = "(distant) riiiiicolaaaa";
12
+ const alpenhorn = jest.fn().mockReturnValue(SOUND);
13
+ const alpenhornId = "alpenhorn_1";
14
+ let simulator: ObjectSimulator;
15
+ let emitter;
16
+ let subject: RemoteSubject;
17
+ let remoteAlpenhorn: ((...args: any[]) => Promise<unknown>) | (() => any);
18
+ beforeEach(() => {
19
+ alpenhorn.mockClear();
20
+ emitter = new EventEmitter();
21
+ simulator = ObjectSimulator.create(emitter, FakeFinalizationRegistry);
22
+ subject = simulator.subject;
23
+ remoteAlpenhorn = makeCallSender(
24
+ { fnId: alpenhornId },
25
+ new FakeWeakRef(subject)
26
+ );
27
+ jest.spyOn(console, "error").mockImplementation(() => {});
28
+ });
29
+ it("resolves through the emitter", async () => {
30
+ subject.onCall(
31
+ {
32
+ fnId: alpenhornId,
33
+ },
34
+ (callTicket) => {
35
+ const { callId, fnId } = callTicket;
36
+ subject.respond({
37
+ callId,
38
+ fnId,
39
+ status: "resolve",
40
+ value: FARAWAY_SOUND,
41
+ });
42
+ }
43
+ );
44
+ await expect(remoteAlpenhorn()).resolves.toBe(FARAWAY_SOUND);
45
+ });
46
+ it("rejects through the emitter", async () => {
47
+ subject.onCall({ fnId: alpenhornId }, (callTicket) => {
48
+ const { callId, fnId } = callTicket;
49
+ subject.respond({
50
+ callId,
51
+ fnId,
52
+ status: "reject",
53
+ error: new Error("bonk"),
54
+ });
55
+ });
56
+ await expect(remoteAlpenhorn()).rejects.toThrowError("bonk");
57
+ });
58
+ it("destroys itself on disconnect", async () => {
59
+ subject.onCall({ fnId: alpenhornId }, async (callTicket) => {
60
+ const { callId, fnId } = callTicket;
61
+ subject.notifyDestroy();
62
+ await wait(100);
63
+ subject.respond({
64
+ callId,
65
+ fnId,
66
+ status: "reject",
67
+ error: new Error("bonk"),
68
+ });
69
+ });
70
+ await expect(remoteAlpenhorn()).rejects.toThrowError("destroyed");
71
+ await expect(remoteAlpenhorn()).rejects.toThrowError("destroyed");
72
+ });
73
+ });
@@ -0,0 +1,72 @@
1
+ import type { CallArgsTicket, DefTicket } from "../tickets";
2
+ import type { RemoteSubject } from "../remote-subject";
3
+
4
+ type RejectionPool = Set<(e: Error) => unknown>;
5
+
6
+ class DisconnectionError extends Error {
7
+ constructor() {
8
+ super(
9
+ "Function belongs to a simulated remote object which has been disconnected! The tunnel may have been destroyed by page navigation or reload."
10
+ );
11
+ }
12
+ }
13
+
14
+ function dispatch(
15
+ subject: RemoteSubject,
16
+ callTicket: CallArgsTicket,
17
+ rejectionPool: RejectionPool,
18
+ resolve: { (value: unknown): void; (arg0: any): void },
19
+ reject: { (reason?: string): void; (arg0: any): void }
20
+ ) {
21
+ subject.onRespond(callTicket, (responseTicket) => {
22
+ rejectionPool.delete(reject);
23
+ if (responseTicket.status === "resolve") {
24
+ resolve(responseTicket.value);
25
+ } else {
26
+ reject(responseTicket.error);
27
+ }
28
+ });
29
+ subject.send(callTicket);
30
+ }
31
+
32
+ export function makeCallSender(
33
+ { fnId }: DefTicket,
34
+ subjectRef: WeakRef<RemoteSubject>
35
+ ) {
36
+ let callCounter = 0;
37
+ const rejectionPool: RejectionPool = new Set();
38
+ let sender = function (...args: unknown[]) {
39
+ return new Promise((resolve, reject) => {
40
+ rejectionPool.add(reject);
41
+ const callId = ++callCounter;
42
+ const callTicket: CallArgsTicket = {
43
+ fnId,
44
+ callId,
45
+ args,
46
+ };
47
+ return dispatch(
48
+ subjectRef.deref(),
49
+ callTicket,
50
+ rejectionPool,
51
+ resolve,
52
+ reject
53
+ );
54
+ });
55
+ };
56
+ const destroy = () => {
57
+ subjectRef = null;
58
+ sender = () => {
59
+ throw new DisconnectionError();
60
+ };
61
+ for (const reject of rejectionPool) {
62
+ reject(new DisconnectionError());
63
+ }
64
+ rejectionPool.clear();
65
+ };
66
+ subjectRef.deref().onDestroyed(destroy);
67
+ const facade = async function (...args: unknown[]) {
68
+ return sender(...args);
69
+ };
70
+ Object.defineProperty(facade, "name", { value: fnId });
71
+ return facade;
72
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./call-receiver";
2
+ export * from "./call-sender";
package/src/tickets.ts ADDED
@@ -0,0 +1,71 @@
1
+ import { INIT_CALLBACK } from "./constants";
2
+
3
+ export interface HandshakeAcceptedTicket {
4
+ // #region Properties
5
+
6
+ accepts: string;
7
+ version: string;
8
+
9
+ // #endregion Properties
10
+ }
11
+ export interface HandshakeOfferedTicket {
12
+ // #region Properties
13
+
14
+ offers: string;
15
+ version: string;
16
+
17
+ // #endregion Properties
18
+ }
19
+
20
+ /** @internal */
21
+ export interface DefTicket {
22
+ // #region Properties
23
+
24
+ fnId: string;
25
+
26
+ // #endregion Properties
27
+ }
28
+ export interface InitTicket extends DefTicket {
29
+ // #region Properties
30
+
31
+ fnId: typeof INIT_CALLBACK;
32
+
33
+ // #endregion Properties
34
+ }
35
+ export interface CallTicket extends DefTicket {
36
+ // #region Properties
37
+
38
+ callId: number;
39
+
40
+ // #endregion Properties
41
+ }
42
+ export interface CallArgsTicket extends CallTicket {
43
+ // #region Properties
44
+
45
+ args: any[];
46
+
47
+ // #endregion Properties
48
+ }
49
+ export interface ResolveTicket extends CallTicket {
50
+ // #region Properties
51
+
52
+ status: "resolve";
53
+ value: any;
54
+
55
+ // #endregion Properties
56
+ }
57
+ export interface RejectTicket extends CallTicket {
58
+ // #region Properties
59
+
60
+ error: Error;
61
+ status: "reject";
62
+
63
+ // #endregion Properties
64
+ }
65
+ export type RespondTicket = ResolveTicket | RejectTicket;
66
+
67
+ export type CleanupTicket = {};
68
+
69
+ export const INIT_TICKET: InitTicket = {
70
+ fnId: INIT_CALLBACK,
71
+ };
@@ -0,0 +1 @@
1
+ export * from "./tunnel";