2025-11-14 06:32:36 -07:00

2500 lines
72 KiB
JavaScript

// ../../node_modules/.pnpm/capnweb@0.1.0/node_modules/capnweb/dist/index.js
if (!Symbol.dispose) {
Symbol.dispose = Symbol.for("dispose");
}
if (!Symbol.asyncDispose) {
Symbol.asyncDispose = Symbol.for("asyncDispose");
}
var workersModuleName = navigator.userAgent === "Cloudflare-Workers" ? "cloudflare:workers" : null;
var workersModule;
if (workersModuleName) {
workersModule = await import(
/* @vite-ignore */
workersModuleName
);
}
var RpcTarget = workersModule ? workersModule.RpcTarget : class {
};
function typeForRpc(value) {
switch (typeof value) {
case "boolean":
case "number":
case "string":
return "primitive";
case "undefined":
return "undefined";
case "object":
case "function":
break;
case "bigint":
return "bigint";
default:
return "unsupported";
}
if (value === null) {
return "primitive";
}
let prototype = Object.getPrototypeOf(value);
switch (prototype) {
case Object.prototype:
return "object";
case Function.prototype:
return "function";
case Array.prototype:
return "array";
case Date.prototype:
return "date";
case Uint8Array.prototype:
return "bytes";
// TODO: All other structured clone types.
case RpcStub.prototype:
return "stub";
case RpcPromise.prototype:
return "rpc-promise";
// TODO: Promise<T> or thenable
default:
if (workersModule) {
if (prototype == workersModule.RpcStub.prototype || value instanceof workersModule.ServiceStub) {
return "rpc-target";
} else if (prototype == workersModule.RpcPromise.prototype || prototype == workersModule.RpcProperty.prototype) {
return "rpc-thenable";
}
}
if (value instanceof RpcTarget) {
return "rpc-target";
}
if (value instanceof Error) {
return "error";
}
return "unsupported";
}
}
function mapNotLoaded() {
throw new Error("RPC map() implementation was not loaded.");
}
var mapImpl = { applyMap: mapNotLoaded, sendMap: mapNotLoaded };
var StubHook = class {
};
var ErrorStubHook = class extends StubHook {
constructor(error) {
super();
this.error = error;
}
call(path, args) {
return this;
}
map(path, captures, instructions) {
return this;
}
get(path) {
return this;
}
dup() {
return this;
}
pull() {
return Promise.reject(this.error);
}
ignoreUnhandledRejections() {
}
dispose() {
}
onBroken(callback) {
try {
callback(this.error);
} catch (err) {
Promise.resolve(err);
}
}
};
var DISPOSED_HOOK = new ErrorStubHook(
new Error("Attempted to use RPC stub after it has been disposed.")
);
var doCall = (hook, path, params) => {
return hook.call(path, params);
};
function withCallInterceptor(interceptor, callback) {
let oldValue = doCall;
doCall = interceptor;
try {
return callback();
} finally {
doCall = oldValue;
}
}
var RAW_STUB = Symbol("realStub");
var PROXY_HANDLERS = {
apply(target, thisArg, argumentsList) {
let stub = target.raw;
return new RpcPromise(doCall(
stub.hook,
stub.pathIfPromise || [],
RpcPayload.fromAppParams(argumentsList)
), []);
},
get(target, prop, receiver) {
let stub = target.raw;
if (prop === RAW_STUB) {
return stub;
} else if (prop in RpcPromise.prototype) {
return stub[prop];
} else if (typeof prop === "string") {
return new RpcPromise(
stub.hook,
stub.pathIfPromise ? [...stub.pathIfPromise, prop] : [prop]
);
} else if (prop === Symbol.dispose && (!stub.pathIfPromise || stub.pathIfPromise.length == 0)) {
return () => {
stub.hook.dispose();
stub.hook = DISPOSED_HOOK;
};
} else {
return void 0;
}
},
has(target, prop) {
let stub = target.raw;
if (prop === RAW_STUB) {
return true;
} else if (prop in RpcPromise.prototype) {
return prop in stub;
} else if (typeof prop === "string") {
return true;
} else if (prop === Symbol.dispose && (!stub.pathIfPromise || stub.pathIfPromise.length == 0)) {
return true;
} else {
return false;
}
},
construct(target, args) {
throw new Error("An RPC stub cannot be used as a constructor.");
},
defineProperty(target, property, attributes) {
throw new Error("Can't define properties on RPC stubs.");
},
deleteProperty(target, p) {
throw new Error("Can't delete properties on RPC stubs.");
},
getOwnPropertyDescriptor(target, p) {
return void 0;
},
getPrototypeOf(target) {
return Object.getPrototypeOf(target.raw);
},
isExtensible(target) {
return false;
},
ownKeys(target) {
return [];
},
preventExtensions(target) {
return true;
},
set(target, p, newValue, receiver) {
throw new Error("Can't assign properties on RPC stubs.");
},
setPrototypeOf(target, v) {
throw new Error("Can't override prototype of RPC stubs.");
}
};
var RpcStub = class _RpcStub extends RpcTarget {
// Although `hook` and `path` are declared `public` here, they are effectively hidden by the
// proxy.
constructor(hook, pathIfPromise) {
super();
if (!(hook instanceof StubHook)) {
let value = hook;
if (value instanceof RpcTarget || value instanceof Function) {
hook = TargetStubHook.create(value, void 0);
} else {
hook = new PayloadStubHook(RpcPayload.fromAppReturn(value));
}
if (pathIfPromise) {
throw new TypeError("RpcStub constructor expected one argument, received two.");
}
}
this.hook = hook;
this.pathIfPromise = pathIfPromise;
let func = () => {
};
func.raw = this;
return new Proxy(func, PROXY_HANDLERS);
}
hook;
pathIfPromise;
dup() {
let target = this[RAW_STUB];
if (target.pathIfPromise) {
return new _RpcStub(target.hook.get(target.pathIfPromise));
} else {
return new _RpcStub(target.hook.dup());
}
}
onRpcBroken(callback) {
this[RAW_STUB].hook.onBroken(callback);
}
map(func) {
let { hook, pathIfPromise } = this[RAW_STUB];
return mapImpl.sendMap(hook, pathIfPromise || [], func);
}
};
var RpcPromise = class extends RpcStub {
// TODO: Support passing target value or promise to constructor.
constructor(hook, pathIfPromise) {
super(hook, pathIfPromise);
}
then(onfulfilled, onrejected) {
return pullPromise(this).then(...arguments);
}
catch(onrejected) {
return pullPromise(this).catch(...arguments);
}
finally(onfinally) {
return pullPromise(this).finally(...arguments);
}
};
function unwrapStubTakingOwnership(stub) {
let { hook, pathIfPromise } = stub[RAW_STUB];
if (pathIfPromise && pathIfPromise.length > 0) {
return hook.get(pathIfPromise);
} else {
return hook;
}
}
function unwrapStubAndDup(stub) {
let { hook, pathIfPromise } = stub[RAW_STUB];
if (pathIfPromise) {
return hook.get(pathIfPromise);
} else {
return hook.dup();
}
}
function unwrapStubNoProperties(stub) {
let { hook, pathIfPromise } = stub[RAW_STUB];
if (pathIfPromise && pathIfPromise.length > 0) {
return void 0;
}
return hook;
}
function unwrapStubOrParent(stub) {
return stub[RAW_STUB].hook;
}
function unwrapStubAndPath(stub) {
return stub[RAW_STUB];
}
async function pullPromise(promise) {
let { hook, pathIfPromise } = promise[RAW_STUB];
if (pathIfPromise.length > 0) {
hook = hook.get(pathIfPromise);
}
let payload = await hook.pull();
return payload.deliverResolve();
}
var RpcPayload = class _RpcPayload {
// Private constructor; use factory functions above to construct.
constructor(value, source, stubs, promises) {
this.value = value;
this.source = source;
this.stubs = stubs;
this.promises = promises;
}
// Create a payload from a value passed as params to an RPC from the app.
//
// The payload does NOT take ownership of any stubs in `value`, and but promises not to modify
// `value`. If the payload is delivered locally, `value` will be deep-copied first, so as not
// to have the sender and recipient end up sharing the same mutable object. `value` will not be
// touched again after the call returns synchronously (returns a promise) -- by that point,
// the value has either been copied or serialized to the wire.
static fromAppParams(value) {
return new _RpcPayload(value, "params");
}
// Create a payload from a value return from an RPC implementation by the app.
//
// Unlike fromAppParams(), in this case the payload takes ownership of all stubs in `value`, and
// may hold onto `value` for an arbitarily long time (e.g. to serve pipelined requests). It
// will still avoid modifying `value` and will make a deep copy if it is delivered locally.
static fromAppReturn(value) {
return new _RpcPayload(value, "return");
}
// Combine an array of payloads into a single payload whose value is an array. Ownership of all
// stubs is transferred from the inputs to the outputs, hence if the output is disposed, the
// inputs should not be. (In case of exception, nothing is disposed, though.)
static fromArray(array) {
let stubs = [];
let promises = [];
let resultArray = [];
for (let payload of array) {
payload.ensureDeepCopied();
for (let stub of payload.stubs) {
stubs.push(stub);
}
for (let promise of payload.promises) {
if (promise.parent === payload) {
promise = {
parent: resultArray,
property: resultArray.length,
promise: promise.promise
};
}
promises.push(promise);
}
resultArray.push(payload.value);
}
return new _RpcPayload(resultArray, "owned", stubs, promises);
}
// Create a payload from a value parsed off the wire using Evaluator.evaluate().
//
// A payload is constructed with a null value and the given stubs and promises arrays. The value
// is expected to be filled in by the evaluator, and the stubs and promises arrays are expected
// to be extended with stubs found during parsing. (This weird usage model is necessary so that
// if the root value turns out to be a promise, its `parent` in `promises` can be the payload
// object itself.)
//
// When done, the payload takes ownership of the final value and all the stubs within. It may
// modify the value in preparation for delivery, and may deliver the value directly to the app
// without copying.
static forEvaluate(stubs, promises) {
return new _RpcPayload(null, "owned", stubs, promises);
}
// Deep-copy the given value, including dup()ing all stubs.
//
// If `value` is a function, it should be bound to `oldParent` as its `this`.
//
// If deep-copying from a branch of some other RpcPayload, it must be provided, to make sure
// RpcTargets found within don't get duplicate stubs.
static deepCopyFrom(value, oldParent, owner) {
let result = new _RpcPayload(null, "owned", [], []);
result.value = result.deepCopy(
value,
oldParent,
"value",
result,
/*dupStubs=*/
true,
owner
);
return result;
}
// For `soruce === "return"` payloads only, this tracks any StubHooks created around RpcTargets
// found in the payload at the time that it is serialized (or deep-copied) for return, so that we
// can make sure they are not disposed before the pipeline ends.
//
// This is initialized on first use.
rpcTargets;
// Get the StubHook representing the given RpcTarget found inside this payload.
getHookForRpcTarget(target, parent, dupStubs = true) {
if (this.source === "params") {
return TargetStubHook.create(target, parent);
} else if (this.source === "return") {
let hook = this.rpcTargets?.get(target);
if (hook) {
if (dupStubs) {
return hook.dup();
} else {
this.rpcTargets?.delete(target);
return hook;
}
} else {
hook = TargetStubHook.create(target, parent);
if (dupStubs) {
if (!this.rpcTargets) {
this.rpcTargets = /* @__PURE__ */ new Map();
}
this.rpcTargets.set(target, hook);
return hook.dup();
} else {
return hook;
}
}
} else {
throw new Error("owned payload shouldn't contain raw RpcTargets");
}
}
deepCopy(value, oldParent, property, parent, dupStubs, owner) {
let kind = typeForRpc(value);
switch (kind) {
case "unsupported":
return value;
case "primitive":
case "bigint":
case "date":
case "bytes":
case "error":
case "undefined":
return value;
case "array": {
let array = value;
let len = array.length;
let result = new Array(len);
for (let i = 0; i < len; i++) {
result[i] = this.deepCopy(array[i], array, i, result, dupStubs, owner);
}
return result;
}
case "object": {
let result = {};
let object = value;
for (let i in object) {
result[i] = this.deepCopy(object[i], object, i, result, dupStubs, owner);
}
return result;
}
case "stub":
case "rpc-promise": {
let stub = value;
let hook;
if (dupStubs) {
hook = unwrapStubAndDup(stub);
} else {
hook = unwrapStubTakingOwnership(stub);
}
if (stub instanceof RpcPromise) {
let promise = new RpcPromise(hook, []);
this.promises.push({ parent, property, promise });
return promise;
} else {
let newStub = new RpcStub(hook);
this.stubs.push(newStub);
return newStub;
}
}
case "function":
case "rpc-target": {
let target = value;
let stub;
if (owner) {
stub = new RpcStub(owner.getHookForRpcTarget(target, oldParent, dupStubs));
} else {
stub = new RpcStub(TargetStubHook.create(target, oldParent));
}
this.stubs.push(stub);
return stub;
}
case "rpc-thenable": {
let target = value;
let promise;
if (owner) {
promise = new RpcPromise(owner.getHookForRpcTarget(target, oldParent, dupStubs), []);
} else {
promise = new RpcPromise(TargetStubHook.create(target, oldParent), []);
}
this.promises.push({ parent, property, promise });
return promise;
}
default:
throw new Error("unreachable");
}
}
// Ensures that if the value originally came from an unowned source, we have replaced it with a
// deep copy.
ensureDeepCopied() {
if (this.source !== "owned") {
let dupStubs = this.source === "params";
this.stubs = [];
this.promises = [];
try {
this.value = this.deepCopy(this.value, void 0, "value", this, dupStubs, this);
} catch (err) {
this.stubs = void 0;
this.promises = void 0;
throw err;
}
this.source = "owned";
if (this.rpcTargets && this.rpcTargets.size > 0) {
throw new Error("Not all rpcTargets were accounted for in deep-copy?");
}
this.rpcTargets = void 0;
}
}
// Resolve all promises in this payload and then assign the final value into `parent[property]`.
deliverTo(parent, property, promises) {
this.ensureDeepCopied();
if (this.value instanceof RpcPromise) {
_RpcPayload.deliverRpcPromiseTo(this.value, parent, property, promises);
} else {
parent[property] = this.value;
for (let record of this.promises) {
_RpcPayload.deliverRpcPromiseTo(record.promise, record.parent, record.property, promises);
}
}
}
static deliverRpcPromiseTo(promise, parent, property, promises) {
let hook = unwrapStubNoProperties(promise);
if (!hook) {
throw new Error("property promises should have been resolved earlier");
}
let inner = hook.pull();
if (inner instanceof _RpcPayload) {
inner.deliverTo(parent, property, promises);
} else {
promises.push(inner.then((payload) => {
let subPromises = [];
payload.deliverTo(parent, property, subPromises);
if (subPromises.length > 0) {
return Promise.all(subPromises);
}
}));
}
}
// Call the given function with the payload as an argument. The call is made synchronously if
// possible, in order to maintain e-order. However, if any RpcPromises exist in the payload,
// they are awaited and substituted before calling the function. The result of the call is
// wrapped into another payload.
//
// The payload is automatically disposed after the call completes. The caller should not call
// dispose().
async deliverCall(func, thisArg) {
try {
let promises = [];
this.deliverTo(this, "value", promises);
if (promises.length > 0) {
await Promise.all(promises);
}
let result = Function.prototype.apply.call(func, thisArg, this.value);
if (result instanceof RpcPromise) {
return _RpcPayload.fromAppReturn(result);
} else {
return _RpcPayload.fromAppReturn(await result);
}
} finally {
this.dispose();
}
}
// Produce a promise for this payload for return to the application. Any RpcPromises in the
// payload are awaited and substituted with their results first.
//
// The returned object will have a disposer which disposes the payload. The caller should not
// separately dispose it.
async deliverResolve() {
try {
let promises = [];
this.deliverTo(this, "value", promises);
if (promises.length > 0) {
await Promise.all(promises);
}
let result = this.value;
if (result instanceof Object) {
if (!(Symbol.dispose in result)) {
Object.defineProperty(result, Symbol.dispose, {
// NOTE: Using `this.dispose.bind(this)` here causes Playwright's build of
// Chromium 140.0.7339.16 to fail when the object is assigned to a `using` variable,
// with the error:
// TypeError: Symbol(Symbol.dispose) is not a function
// I cannot reproduce this problem in Chrome 140.0.7339.127 nor in Node or workerd,
// so maybe it was a short-lived V8 bug or something. To be safe, though, we use
// `() => this.dispose()`, which seems to always work.
value: () => this.dispose(),
writable: true,
enumerable: false,
configurable: true
});
}
}
return result;
} catch (err) {
this.dispose();
throw err;
}
}
dispose() {
if (this.source === "owned") {
this.stubs.forEach((stub) => stub[Symbol.dispose]());
this.promises.forEach((promise) => promise.promise[Symbol.dispose]());
} else if (this.source === "return") {
this.disposeImpl(this.value, void 0);
if (this.rpcTargets && this.rpcTargets.size > 0) {
throw new Error("Not all rpcTargets were accounted for in disposeImpl()?");
}
} else ;
this.source = "owned";
this.stubs = [];
this.promises = [];
}
// Recursive dispose, called only when `source` is "return".
disposeImpl(value, parent) {
let kind = typeForRpc(value);
switch (kind) {
case "unsupported":
case "primitive":
case "bigint":
case "bytes":
case "date":
case "error":
case "undefined":
return;
case "array": {
let array = value;
let len = array.length;
for (let i = 0; i < len; i++) {
this.disposeImpl(array[i], array);
}
return;
}
case "object": {
let object = value;
for (let i in object) {
this.disposeImpl(object[i], object);
}
return;
}
case "stub":
case "rpc-promise": {
let stub = value;
let hook = unwrapStubNoProperties(stub);
if (hook) {
hook.dispose();
}
return;
}
case "function":
case "rpc-target": {
let target = value;
let hook = this.rpcTargets?.get(target);
if (hook) {
hook.dispose();
this.rpcTargets.delete(target);
} else {
disposeRpcTarget(target);
}
return;
}
case "rpc-thenable":
return;
default:
return;
}
}
// Ignore unhandled rejections in all promises in this payload -- that is, all promises that
// *would* be awaited if this payload were to be delivered. See the similarly-named method of
// StubHook for explanation.
ignoreUnhandledRejections() {
if (this.stubs) {
this.stubs.forEach((stub) => {
unwrapStubOrParent(stub).ignoreUnhandledRejections();
});
this.promises.forEach(
(promise) => unwrapStubOrParent(promise.promise).ignoreUnhandledRejections()
);
} else {
this.ignoreUnhandledRejectionsImpl(this.value);
}
}
ignoreUnhandledRejectionsImpl(value) {
let kind = typeForRpc(value);
switch (kind) {
case "unsupported":
case "primitive":
case "bigint":
case "bytes":
case "date":
case "error":
case "undefined":
case "function":
case "rpc-target":
return;
case "array": {
let array = value;
let len = array.length;
for (let i = 0; i < len; i++) {
this.ignoreUnhandledRejectionsImpl(array[i]);
}
return;
}
case "object": {
let object = value;
for (let i in object) {
this.ignoreUnhandledRejectionsImpl(object[i]);
}
return;
}
case "stub":
case "rpc-promise":
unwrapStubOrParent(value).ignoreUnhandledRejections();
return;
case "rpc-thenable":
value.then((_) => {
}, (_) => {
});
return;
default:
return;
}
}
};
function followPath(value, parent, path, owner) {
for (let i = 0; i < path.length; i++) {
parent = value;
let part = path[i];
if (part in Object.prototype) {
value = void 0;
continue;
}
let kind = typeForRpc(value);
switch (kind) {
case "object":
case "function":
if (Object.hasOwn(value, part)) {
value = value[part];
} else {
value = void 0;
}
break;
case "array":
if (Number.isInteger(part) && part >= 0) {
value = value[part];
} else {
value = void 0;
}
break;
case "rpc-target":
case "rpc-thenable": {
if (Object.hasOwn(value, part)) {
value = void 0;
} else {
value = value[part];
}
owner = null;
break;
}
case "stub":
case "rpc-promise": {
let { hook, pathIfPromise } = unwrapStubAndPath(value);
return { hook, remainingPath: pathIfPromise ? pathIfPromise.concat(path.slice(i)) : path.slice(i) };
}
case "primitive":
case "bigint":
case "bytes":
case "date":
case "error":
value = void 0;
break;
case "undefined":
value = value[part];
break;
case "unsupported": {
if (i === 0) {
throw new TypeError(`RPC stub points at a non-serializable type.`);
} else {
let prefix = path.slice(0, i).join(".");
let remainder = path.slice(0, i).join(".");
throw new TypeError(
`'${prefix}' is not a serializable type, so property ${remainder} cannot be accessed.`
);
}
}
default:
throw new TypeError("unreachable");
}
}
if (value instanceof RpcPromise) {
let { hook, pathIfPromise } = unwrapStubAndPath(value);
return { hook, remainingPath: pathIfPromise || [] };
}
return {
value,
parent,
owner
};
}
var ValueStubHook = class extends StubHook {
call(path, args) {
try {
let { value, owner } = this.getValue();
let followResult = followPath(value, void 0, path, owner);
if (followResult.hook) {
return followResult.hook.call(followResult.remainingPath, args);
}
if (typeof followResult.value != "function") {
throw new TypeError(`'${path.join(".")}' is not a function.`);
}
let promise = args.deliverCall(followResult.value, followResult.parent);
return new PromiseStubHook(promise.then((payload) => {
return new PayloadStubHook(payload);
}));
} catch (err) {
return new ErrorStubHook(err);
}
}
map(path, captures, instructions) {
try {
let followResult;
try {
let { value, owner } = this.getValue();
followResult = followPath(value, void 0, path, owner);
;
} catch (err) {
for (let cap of captures) {
cap.dispose();
}
throw err;
}
if (followResult.hook) {
return followResult.hook.map(followResult.remainingPath, captures, instructions);
}
return mapImpl.applyMap(
followResult.value,
followResult.parent,
followResult.owner,
captures,
instructions
);
} catch (err) {
return new ErrorStubHook(err);
}
}
get(path) {
try {
let { value, owner } = this.getValue();
if (path.length === 0 && owner === null) {
throw new Error("Can't dup an RpcTarget stub as a promise.");
}
let followResult = followPath(value, void 0, path, owner);
if (followResult.hook) {
return followResult.hook.get(followResult.remainingPath);
}
return new PayloadStubHook(RpcPayload.deepCopyFrom(
followResult.value,
followResult.parent,
followResult.owner
));
} catch (err) {
return new ErrorStubHook(err);
}
}
};
var PayloadStubHook = class _PayloadStubHook extends ValueStubHook {
constructor(payload) {
super();
this.payload = payload;
}
payload;
// cleared when disposed
getPayload() {
if (this.payload) {
return this.payload;
} else {
throw new Error("Attempted to use an RPC StubHook after it was disposed.");
}
}
getValue() {
let payload = this.getPayload();
return { value: payload.value, owner: payload };
}
dup() {
let thisPayload = this.getPayload();
return new _PayloadStubHook(RpcPayload.deepCopyFrom(
thisPayload.value,
void 0,
thisPayload
));
}
pull() {
return this.getPayload();
}
ignoreUnhandledRejections() {
if (this.payload) {
this.payload.ignoreUnhandledRejections();
}
}
dispose() {
if (this.payload) {
this.payload.dispose();
this.payload = void 0;
}
}
onBroken(callback) {
if (this.payload) {
if (this.payload.value instanceof RpcStub) {
this.payload.value.onRpcBroken(callback);
}
}
}
};
function disposeRpcTarget(target) {
if (Symbol.dispose in target) {
try {
target[Symbol.dispose]();
} catch (err) {
Promise.reject(err);
}
}
}
var TargetStubHook = class _TargetStubHook extends ValueStubHook {
// Constructs a TargetStubHook that is not duplicated from an existing hook.
//
// If `value` is a function, `parent` is bound as its "this".
static create(value, parent) {
if (typeof value !== "function") {
parent = void 0;
}
return new _TargetStubHook(value, parent);
}
constructor(target, parent, dupFrom) {
super();
this.target = target;
this.parent = parent;
if (dupFrom) {
if (dupFrom.refcount) {
this.refcount = dupFrom.refcount;
++this.refcount.count;
}
} else if (Symbol.dispose in target) {
this.refcount = { count: 1 };
}
}
target;
// cleared when disposed
parent;
// `this` parameter when calling `target`
refcount;
// undefined if not needed (because target has no disposer)
getTarget() {
if (this.target) {
return this.target;
} else {
throw new Error("Attempted to use an RPC StubHook after it was disposed.");
}
}
getValue() {
return { value: this.getTarget(), owner: null };
}
dup() {
return new _TargetStubHook(this.getTarget(), this.parent, this);
}
pull() {
let target = this.getTarget();
if ("then" in target) {
return Promise.resolve(target).then((resolution) => {
return RpcPayload.fromAppReturn(resolution);
});
} else {
return Promise.reject(new Error("Tried to resolve a non-promise stub."));
}
}
ignoreUnhandledRejections() {
}
dispose() {
if (this.target) {
if (this.refcount) {
if (--this.refcount.count == 0) {
disposeRpcTarget(this.target);
}
}
this.target = void 0;
}
}
onBroken(callback) {
}
};
var PromiseStubHook = class _PromiseStubHook extends StubHook {
promise;
resolution;
constructor(promise) {
super();
this.promise = promise.then((res) => {
this.resolution = res;
return res;
});
}
call(path, args) {
args.ensureDeepCopied();
return new _PromiseStubHook(this.promise.then((hook) => hook.call(path, args)));
}
map(path, captures, instructions) {
return new _PromiseStubHook(this.promise.then(
(hook) => hook.map(path, captures, instructions),
(err) => {
for (let cap of captures) {
cap.dispose();
}
throw err;
}
));
}
get(path) {
return new _PromiseStubHook(this.promise.then((hook) => hook.get(path)));
}
dup() {
if (this.resolution) {
return this.resolution.dup();
} else {
return new _PromiseStubHook(this.promise.then((hook) => hook.dup()));
}
}
pull() {
if (this.resolution) {
return this.resolution.pull();
} else {
return this.promise.then((hook) => hook.pull());
}
}
ignoreUnhandledRejections() {
if (this.resolution) {
this.resolution.ignoreUnhandledRejections();
} else {
this.promise.then((res) => {
res.ignoreUnhandledRejections();
}, (err) => {
});
}
}
dispose() {
if (this.resolution) {
this.resolution.dispose();
} else {
this.promise.then((hook) => {
hook.dispose();
}, (err) => {
});
}
}
onBroken(callback) {
if (this.resolution) {
this.resolution.onBroken(callback);
} else {
this.promise.then((hook) => {
hook.onBroken(callback);
}, callback);
}
}
};
var NullExporter = class {
exportStub(stub) {
throw new Error("Cannot serialize RPC stubs without an RPC session.");
}
exportPromise(stub) {
throw new Error("Cannot serialize RPC stubs without an RPC session.");
}
getImport(hook) {
return void 0;
}
unexport(ids) {
}
onSendError(error) {
}
};
var NULL_EXPORTER = new NullExporter();
var ERROR_TYPES = {
Error,
EvalError,
RangeError,
ReferenceError,
SyntaxError,
TypeError,
URIError,
AggregateError
// TODO: DOMError? Others?
};
var Devaluator = class _Devaluator {
constructor(exporter, source) {
this.exporter = exporter;
this.source = source;
}
// Devaluate the given value.
// * value: The value to devaluate.
// * parent: The value's parent object, which would be used as `this` if the value were called
// as a function.
// * exporter: Callbacks to the RPC session for exporting capabilities found in this message.
// * source: The RpcPayload which contains the value, and therefore owns stubs within.
//
// Returns: The devaluated value, ready to be JSON-serialized.
static devaluate(value, parent, exporter = NULL_EXPORTER, source) {
let devaluator = new _Devaluator(exporter, source);
try {
return devaluator.devaluateImpl(value, parent, 0);
} catch (err) {
if (devaluator.exports) {
try {
exporter.unexport(devaluator.exports);
} catch (err2) {
}
}
throw err;
}
}
exports;
devaluateImpl(value, parent, depth) {
if (depth >= 64) {
throw new Error(
"Serialization exceeded maximum allowed depth. (Does the message contain cycles?)"
);
}
let kind = typeForRpc(value);
switch (kind) {
case "unsupported": {
let msg;
try {
msg = `Cannot serialize value: ${value}`;
} catch (err) {
msg = "Cannot serialize value: (couldn't stringify value)";
}
throw new TypeError(msg);
}
case "primitive":
return value;
case "object": {
let object = value;
let result = {};
for (let key in object) {
result[key] = this.devaluateImpl(object[key], object, depth + 1);
}
return result;
}
case "array": {
let array = value;
let len = array.length;
let result = new Array(len);
for (let i = 0; i < len; i++) {
result[i] = this.devaluateImpl(array[i], array, depth + 1);
}
return [result];
}
case "bigint":
return ["bigint", value.toString()];
case "date":
return ["date", value.getTime()];
case "bytes": {
let bytes = value;
if (bytes.toBase64) {
return ["bytes", bytes.toBase64({ omitPadding: true })];
} else {
return [
"bytes",
btoa(String.fromCharCode.apply(null, bytes).replace(/=*$/, ""))
];
}
}
case "error": {
let e = value;
let rewritten = this.exporter.onSendError(e);
if (rewritten) {
e = rewritten;
}
let result = ["error", e.name, e.message];
if (rewritten && rewritten.stack) {
result.push(rewritten.stack);
}
return result;
}
case "undefined":
return ["undefined"];
case "stub":
case "rpc-promise": {
if (!this.source) {
throw new Error("Can't serialize RPC stubs in this context.");
}
let { hook, pathIfPromise } = unwrapStubAndPath(value);
let importId = this.exporter.getImport(hook);
if (importId !== void 0) {
if (pathIfPromise) {
if (pathIfPromise.length > 0) {
return ["pipeline", importId, pathIfPromise];
} else {
return ["pipeline", importId];
}
} else {
return ["import", importId];
}
}
if (pathIfPromise) {
hook = hook.get(pathIfPromise);
} else {
hook = hook.dup();
}
return this.devaluateHook(pathIfPromise ? "promise" : "export", hook);
}
case "function":
case "rpc-target": {
if (!this.source) {
throw new Error("Can't serialize RPC stubs in this context.");
}
let hook = this.source.getHookForRpcTarget(value, parent);
return this.devaluateHook("export", hook);
}
case "rpc-thenable": {
if (!this.source) {
throw new Error("Can't serialize RPC stubs in this context.");
}
let hook = this.source.getHookForRpcTarget(value, parent);
return this.devaluateHook("promise", hook);
}
default:
throw new Error("unreachable");
}
}
devaluateHook(type, hook) {
if (!this.exports) this.exports = [];
let exportId = type === "promise" ? this.exporter.exportPromise(hook) : this.exporter.exportStub(hook);
this.exports.push(exportId);
return [type, exportId];
}
};
var NullImporter = class {
importStub(idx) {
throw new Error("Cannot deserialize RPC stubs without an RPC session.");
}
importPromise(idx) {
throw new Error("Cannot deserialize RPC stubs without an RPC session.");
}
getExport(idx) {
return void 0;
}
};
var NULL_IMPORTER = new NullImporter();
var Evaluator = class _Evaluator {
constructor(importer) {
this.importer = importer;
}
stubs = [];
promises = [];
evaluate(value) {
let payload = RpcPayload.forEvaluate(this.stubs, this.promises);
try {
payload.value = this.evaluateImpl(value, payload, "value");
return payload;
} catch (err) {
payload.dispose();
throw err;
}
}
// Evaluate the value without destroying it.
evaluateCopy(value) {
return this.evaluate(structuredClone(value));
}
evaluateImpl(value, parent, property) {
if (value instanceof Array) {
if (value.length == 1 && value[0] instanceof Array) {
let result = value[0];
for (let i = 0; i < result.length; i++) {
result[i] = this.evaluateImpl(result[i], result, i);
}
return result;
} else switch (value[0]) {
case "bigint":
if (typeof value[1] == "string") {
return BigInt(value[1]);
}
break;
case "date":
if (typeof value[1] == "number") {
return new Date(value[1]);
}
break;
case "bytes": {
let b64 = Uint8Array;
if (typeof value[1] == "string") {
if (b64.fromBase64) {
return b64.fromBase64(value[1]);
} else {
let bs = atob(value[1]);
let len = bs.length;
let bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = bs.charCodeAt(i);
}
return bytes;
}
}
break;
}
case "error":
if (value.length >= 3 && typeof value[1] === "string" && typeof value[2] === "string") {
let cls = ERROR_TYPES[value[1]] || Error;
let result = new cls(value[2]);
if (typeof value[3] === "string") {
result.stack = value[3];
}
return result;
}
break;
case "undefined":
if (value.length === 1) {
return void 0;
}
break;
case "import":
case "pipeline": {
if (value.length < 2 || value.length > 4) {
break;
}
if (typeof value[1] != "number") {
break;
}
let hook = this.importer.getExport(value[1]);
if (!hook) {
throw new Error(`no such entry on exports table: ${value[1]}`);
}
let isPromise = value[0] == "pipeline";
let addStub = (hook2) => {
if (isPromise) {
let promise = new RpcPromise(hook2, []);
this.promises.push({ promise, parent, property });
return promise;
} else {
let stub = new RpcPromise(hook2, []);
this.stubs.push(stub);
return stub;
}
};
if (value.length == 2) {
if (isPromise) {
return addStub(hook.get([]));
} else {
return addStub(hook.dup());
}
}
let path = value[2];
if (!(path instanceof Array)) {
break;
}
if (!path.every(
(part) => {
return typeof part == "string" || typeof part == "number";
}
)) {
break;
}
if (value.length == 3) {
return addStub(hook.get(path));
}
let args = value[3];
if (!(args instanceof Array)) {
break;
}
let subEval = new _Evaluator(this.importer);
args = subEval.evaluate([args]);
return addStub(hook.call(path, args));
}
case "remap": {
if (value.length !== 5 || typeof value[1] !== "number" || !(value[2] instanceof Array) || !(value[3] instanceof Array) || !(value[4] instanceof Array)) {
break;
}
let hook = this.importer.getExport(value[1]);
if (!hook) {
throw new Error(`no such entry on exports table: ${value[1]}`);
}
let path = value[2];
if (!path.every(
(part) => {
return typeof part == "string" || typeof part == "number";
}
)) {
break;
}
let captures = value[3].map((cap) => {
if (!(cap instanceof Array) || cap.length !== 2 || cap[0] !== "import" && cap[0] !== "export" || typeof cap[1] !== "number") {
throw new TypeError(`unknown map capture: ${JSON.stringify(cap)}`);
}
if (cap[0] === "export") {
return this.importer.importStub(cap[1]);
} else {
let exp = this.importer.getExport(cap[1]);
if (!exp) {
throw new Error(`no such entry on exports table: ${cap[1]}`);
}
return exp.dup();
}
});
let instructions = value[4];
let resultHook = hook.map(path, captures, instructions);
let promise = new RpcPromise(resultHook, []);
this.promises.push({ promise, parent, property });
return promise;
}
case "export":
case "promise":
if (typeof value[1] == "number") {
if (value[0] == "promise") {
let hook = this.importer.importPromise(value[1]);
let promise = new RpcPromise(hook, []);
this.promises.push({ parent, property, promise });
return promise;
} else {
let hook = this.importer.importStub(value[1]);
let stub = new RpcStub(hook);
this.stubs.push(stub);
return stub;
}
}
break;
}
throw new TypeError(`unknown special value: ${JSON.stringify(value)}`);
} else if (value instanceof Object) {
let result = value;
for (let key in result) {
if (key in Object.prototype || key === "toJSON") {
this.evaluateImpl(result[key], result, key);
delete result[key];
} else {
result[key] = this.evaluateImpl(result[key], result, key);
}
}
return result;
} else {
return value;
}
}
};
var ImportTableEntry = class {
constructor(session, importId, pulling) {
this.session = session;
this.importId = importId;
if (pulling) {
this.activePull = Promise.withResolvers();
}
}
localRefcount = 0;
remoteRefcount = 1;
activePull;
resolution;
// List of integer indexes into session.onBrokenCallbacks which are callbacks registered on
// this import. Initialized on first use (so `undefined` is the same as an empty list).
onBrokenRegistrations;
resolve(resolution) {
if (this.localRefcount == 0) {
resolution.dispose();
return;
}
this.resolution = resolution;
this.sendRelease();
if (this.onBrokenRegistrations) {
for (let i of this.onBrokenRegistrations) {
let callback = this.session.onBrokenCallbacks[i];
let endIndex = this.session.onBrokenCallbacks.length;
resolution.onBroken(callback);
if (this.session.onBrokenCallbacks[endIndex] === callback) {
delete this.session.onBrokenCallbacks[endIndex];
} else {
delete this.session.onBrokenCallbacks[i];
}
}
this.onBrokenRegistrations = void 0;
}
if (this.activePull) {
this.activePull.resolve();
this.activePull = void 0;
}
}
async awaitResolution() {
if (!this.activePull) {
this.session.sendPull(this.importId);
this.activePull = Promise.withResolvers();
}
await this.activePull.promise;
return this.resolution.pull();
}
dispose() {
if (this.resolution) {
this.resolution.dispose();
} else {
this.abort(new Error("RPC was canceled because the RpcPromise was disposed."));
this.sendRelease();
}
}
abort(error) {
if (!this.resolution) {
this.resolution = new ErrorStubHook(error);
if (this.activePull) {
this.activePull.reject(error);
this.activePull = void 0;
}
this.onBrokenRegistrations = void 0;
}
}
onBroken(callback) {
if (this.resolution) {
this.resolution.onBroken(callback);
} else {
let index = this.session.onBrokenCallbacks.length;
this.session.onBrokenCallbacks.push(callback);
if (!this.onBrokenRegistrations) this.onBrokenRegistrations = [];
this.onBrokenRegistrations.push(index);
}
}
sendRelease() {
if (this.remoteRefcount > 0) {
this.session.sendRelease(this.importId, this.remoteRefcount);
this.remoteRefcount = 0;
}
}
};
var RpcImportHook = class _RpcImportHook extends StubHook {
// undefined when we're disposed
// `pulling` is true if we already expect that this import is going to be resolved later, and
// null if this import is not allowed to be pulled (i.e. it's a stub not a promise).
constructor(isPromise, entry) {
super();
this.isPromise = isPromise;
++entry.localRefcount;
this.entry = entry;
}
entry;
collectPath(path) {
return this;
}
getEntry() {
if (this.entry) {
return this.entry;
} else {
throw new Error("This RpcImportHook was already disposed.");
}
}
// -------------------------------------------------------------------------------------
// implements StubHook
call(path, args) {
let entry = this.getEntry();
if (entry.resolution) {
return entry.resolution.call(path, args);
} else {
return entry.session.sendCall(entry.importId, path, args);
}
}
map(path, captures, instructions) {
let entry;
try {
entry = this.getEntry();
} catch (err) {
for (let cap of captures) {
cap.dispose();
}
throw err;
}
if (entry.resolution) {
return entry.resolution.map(path, captures, instructions);
} else {
return entry.session.sendMap(entry.importId, path, captures, instructions);
}
}
get(path) {
let entry = this.getEntry();
if (entry.resolution) {
return entry.resolution.get(path);
} else {
return entry.session.sendCall(entry.importId, path);
}
}
dup() {
return new _RpcImportHook(false, this.getEntry());
}
pull() {
let entry = this.getEntry();
if (!this.isPromise) {
throw new Error("Can't pull this hook because it's not a promise hook.");
}
if (entry.resolution) {
return entry.resolution.pull();
}
return entry.awaitResolution();
}
ignoreUnhandledRejections() {
}
dispose() {
let entry = this.entry;
this.entry = void 0;
if (entry) {
if (--entry.localRefcount === 0) {
entry.dispose();
}
}
}
onBroken(callback) {
if (this.entry) {
this.entry.onBroken(callback);
}
}
};
var RpcMainHook = class extends RpcImportHook {
session;
constructor(entry) {
super(false, entry);
this.session = entry.session;
}
dispose() {
if (this.session) {
let session = this.session;
this.session = void 0;
session.shutdown();
}
}
};
var RpcSessionImpl = class {
constructor(transport, mainHook, options) {
this.transport = transport;
this.options = options;
this.exports.push({ hook: mainHook, refcount: 1 });
this.imports.push(new ImportTableEntry(this, 0, false));
let rejectFunc;
let abortPromise = new Promise((resolve, reject) => {
rejectFunc = reject;
});
this.cancelReadLoop = rejectFunc;
this.readLoop(abortPromise).catch((err) => this.abort(err));
}
exports = [];
reverseExports = /* @__PURE__ */ new Map();
imports = [];
abortReason;
cancelReadLoop;
// We assign positive numbers to imports we initiate, and negative numbers to exports we
// initiate. So the next import ID is just `imports.length`, but the next export ID needs
// to be tracked explicitly.
nextExportId = -1;
// If set, call this when all incoming calls are complete.
onBatchDone;
// How many promises is our peer expecting us to resolve?
pullCount = 0;
// Sparse array of onBrokenCallback registrations. Items are strictly appended to the end but
// may be deleted from the middle (hence leaving the array sparse).
onBrokenCallbacks = [];
// Should only be called once immediately after construction.
getMainImport() {
return new RpcMainHook(this.imports[0]);
}
shutdown() {
this.abort(new Error("RPC session was shut down by disposing the main stub"), false);
}
exportStub(hook) {
if (this.abortReason) throw this.abortReason;
let existingExportId = this.reverseExports.get(hook);
if (existingExportId !== void 0) {
++this.exports[existingExportId].refcount;
return existingExportId;
} else {
let exportId = this.nextExportId--;
this.exports[exportId] = { hook, refcount: 1 };
this.reverseExports.set(hook, exportId);
return exportId;
}
}
exportPromise(hook) {
if (this.abortReason) throw this.abortReason;
let exportId = this.nextExportId--;
this.exports[exportId] = { hook, refcount: 1 };
this.reverseExports.set(hook, exportId);
this.ensureResolvingExport(exportId);
return exportId;
}
unexport(ids) {
for (let id of ids) {
this.releaseExport(id, 1);
}
}
releaseExport(exportId, refcount) {
let entry = this.exports[exportId];
if (!entry) {
throw new Error(`no such export ID: ${exportId}`);
}
if (entry.refcount < refcount) {
throw new Error(`refcount would go negative: ${entry.refcount} < ${refcount}`);
}
entry.refcount -= refcount;
if (entry.refcount === 0) {
delete this.exports[exportId];
this.reverseExports.delete(entry.hook);
entry.hook.dispose();
}
}
onSendError(error) {
if (this.options.onSendError) {
return this.options.onSendError(error);
}
}
ensureResolvingExport(exportId) {
let exp = this.exports[exportId];
if (!exp) {
throw new Error(`no such export ID: ${exportId}`);
}
if (!exp.pull) {
let resolve = async () => {
let hook = exp.hook;
for (; ; ) {
let payload = await hook.pull();
if (payload.value instanceof RpcStub) {
let { hook: inner, pathIfPromise } = unwrapStubAndPath(payload.value);
if (pathIfPromise && pathIfPromise.length == 0) {
if (this.getImport(hook) === void 0) {
hook = inner;
continue;
}
}
}
return payload;
}
};
++this.pullCount;
exp.pull = resolve().then(
(payload) => {
let value = Devaluator.devaluate(payload.value, void 0, this, payload);
this.send(["resolve", exportId, value]);
},
(error) => {
this.send(["reject", exportId, Devaluator.devaluate(error, void 0, this)]);
}
).catch(
(error) => {
try {
this.send(["reject", exportId, Devaluator.devaluate(error, void 0, this)]);
} catch (error2) {
this.abort(error2);
}
}
).finally(() => {
if (--this.pullCount === 0) {
if (this.onBatchDone) {
this.onBatchDone.resolve();
}
}
});
}
}
getImport(hook) {
if (hook instanceof RpcImportHook && hook.entry && hook.entry.session === this) {
return hook.entry.importId;
} else {
return void 0;
}
}
importStub(idx) {
if (this.abortReason) throw this.abortReason;
let entry = this.imports[idx];
if (!entry) {
entry = new ImportTableEntry(this, idx, false);
this.imports[idx] = entry;
}
return new RpcImportHook(
/*isPromise=*/
false,
entry
);
}
importPromise(idx) {
if (this.abortReason) throw this.abortReason;
if (this.imports[idx]) {
return new ErrorStubHook(new Error(
"Bug in RPC system: The peer sent a promise reusing an existing export ID."
));
}
let entry = new ImportTableEntry(this, idx, true);
this.imports[idx] = entry;
return new RpcImportHook(
/*isPromise=*/
true,
entry
);
}
getExport(idx) {
return this.exports[idx]?.hook;
}
send(msg) {
if (this.abortReason !== void 0) {
return;
}
let msgText;
try {
msgText = JSON.stringify(msg);
} catch (err) {
try {
this.abort(err);
} catch (err2) {
}
throw err;
}
this.transport.send(msgText).catch((err) => this.abort(err, false));
}
sendCall(id, path, args) {
if (this.abortReason) throw this.abortReason;
let value = ["pipeline", id, path];
if (args) {
let devalue = Devaluator.devaluate(args.value, void 0, this, args);
value.push(devalue[0]);
}
this.send(["push", value]);
let entry = new ImportTableEntry(this, this.imports.length, false);
this.imports.push(entry);
return new RpcImportHook(
/*isPromise=*/
true,
entry
);
}
sendMap(id, path, captures, instructions) {
if (this.abortReason) {
for (let cap of captures) {
cap.dispose();
}
throw this.abortReason;
}
let devaluedCaptures = captures.map((hook) => {
let importId = this.getImport(hook);
if (importId !== void 0) {
return ["import", importId];
} else {
return ["export", this.exportStub(hook)];
}
});
let value = ["remap", id, path, devaluedCaptures, instructions];
this.send(["push", value]);
let entry = new ImportTableEntry(this, this.imports.length, false);
this.imports.push(entry);
return new RpcImportHook(
/*isPromise=*/
true,
entry
);
}
sendPull(id) {
if (this.abortReason) throw this.abortReason;
this.send(["pull", id]);
}
sendRelease(id, remoteRefcount) {
if (this.abortReason) return;
this.send(["release", id, remoteRefcount]);
delete this.imports[id];
}
abort(error, trySendAbortMessage = true) {
if (this.abortReason !== void 0) return;
this.cancelReadLoop(error);
if (trySendAbortMessage) {
try {
this.transport.send(JSON.stringify(["abort", Devaluator.devaluate(error, void 0, this)])).catch((err) => {
});
} catch (err) {
}
}
if (error === void 0) {
error = "undefined";
}
this.abortReason = error;
if (this.onBatchDone) {
this.onBatchDone.reject(error);
}
if (this.transport.abort) {
try {
this.transport.abort(error);
} catch (err) {
Promise.resolve(err);
}
}
for (let i in this.onBrokenCallbacks) {
try {
this.onBrokenCallbacks[i](error);
} catch (err) {
Promise.resolve(err);
}
}
for (let i in this.imports) {
this.imports[i].abort(error);
}
for (let i in this.exports) {
this.exports[i].hook.dispose();
}
}
async readLoop(abortPromise) {
while (!this.abortReason) {
let msg = JSON.parse(await Promise.race([this.transport.receive(), abortPromise]));
if (this.abortReason) break;
if (msg instanceof Array) {
switch (msg[0]) {
case "push":
if (msg.length > 1) {
let payload = new Evaluator(this).evaluate(msg[1]);
let hook = new PayloadStubHook(payload);
hook.ignoreUnhandledRejections();
this.exports.push({ hook, refcount: 1 });
continue;
}
break;
case "pull": {
let exportId = msg[1];
if (typeof exportId == "number") {
this.ensureResolvingExport(exportId);
continue;
}
break;
}
case "resolve":
// ["resolve", ExportId, Expression]
case "reject": {
let importId = msg[1];
if (typeof importId == "number" && msg.length > 2) {
let imp = this.imports[importId];
if (imp) {
if (msg[0] == "resolve") {
imp.resolve(new PayloadStubHook(new Evaluator(this).evaluate(msg[2])));
} else {
let payload = new Evaluator(this).evaluate(msg[2]);
payload.dispose();
imp.resolve(new ErrorStubHook(payload.value));
}
} else {
if (msg[0] == "resolve") {
new Evaluator(this).evaluate(msg[2]).dispose();
}
}
continue;
}
break;
}
case "release": {
let exportId = msg[1];
let refcount = msg[2];
if (typeof exportId == "number" && typeof refcount == "number") {
this.releaseExport(exportId, refcount);
continue;
}
break;
}
case "abort": {
let payload = new Evaluator(this).evaluate(msg[1]);
payload.dispose();
this.abort(payload, false);
break;
}
}
}
throw new Error(`bad RPC message: ${JSON.stringify(msg)}`);
}
}
async drain() {
if (this.abortReason) {
throw this.abortReason;
}
if (this.pullCount > 0) {
let { promise, resolve, reject } = Promise.withResolvers();
this.onBatchDone = { resolve, reject };
await promise;
}
}
getStats() {
let result = { imports: 0, exports: 0 };
for (let i in this.imports) {
++result.imports;
}
for (let i in this.exports) {
++result.exports;
}
return result;
}
};
var RpcSession = class {
#session;
#mainStub;
constructor(transport, localMain, options = {}) {
let mainHook;
if (localMain) {
mainHook = new PayloadStubHook(RpcPayload.fromAppReturn(localMain));
} else {
mainHook = new ErrorStubHook(new Error("This connection has no main object."));
}
this.#session = new RpcSessionImpl(transport, mainHook, options);
this.#mainStub = new RpcStub(this.#session.getMainImport());
}
getRemoteMain() {
return this.#mainStub;
}
getStats() {
return this.#session.getStats();
}
drain() {
return this.#session.drain();
}
};
function newWebSocketRpcSession(webSocket, localMain, options) {
if (typeof webSocket === "string") {
webSocket = new WebSocket(webSocket);
}
let transport = new WebSocketTransport(webSocket);
let rpc = new RpcSession(transport, localMain, options);
return rpc.getRemoteMain();
}
function newWorkersWebSocketRpcResponse(request, localMain, options) {
if (request.headers.get("Upgrade")?.toLowerCase() !== "websocket") {
return new Response("This endpoint only accepts WebSocket requests.", { status: 400 });
}
let pair = new WebSocketPair();
let server = pair[0];
server.accept();
newWebSocketRpcSession(server, localMain, options);
return new Response(null, {
status: 101,
webSocket: pair[1]
});
}
var WebSocketTransport = class {
constructor(webSocket) {
this.#webSocket = webSocket;
if (webSocket.readyState === WebSocket.CONNECTING) {
this.#sendQueue = [];
webSocket.addEventListener("open", (event) => {
try {
for (let message of this.#sendQueue) {
webSocket.send(message);
}
} catch (err) {
this.#receivedError(err);
}
this.#sendQueue = void 0;
});
}
webSocket.addEventListener("message", (event) => {
if (this.#error) ;
else if (typeof event.data === "string") {
if (this.#receiveResolver) {
this.#receiveResolver(event.data);
this.#receiveResolver = void 0;
this.#receiveRejecter = void 0;
} else {
this.#receiveQueue.push(event.data);
}
} else {
this.#receivedError(new TypeError("Received non-string message from WebSocket."));
}
});
webSocket.addEventListener("close", (event) => {
this.#receivedError(new Error(`Peer closed WebSocket: ${event.code} ${event.reason}`));
});
webSocket.addEventListener("error", (event) => {
this.#receivedError(new Error(`WebSocket connection failed.`));
});
}
#webSocket;
#sendQueue;
// only if not opened yet
#receiveResolver;
#receiveRejecter;
#receiveQueue = [];
#error;
async send(message) {
if (this.#sendQueue === void 0) {
this.#webSocket.send(message);
} else {
this.#sendQueue.push(message);
}
}
async receive() {
if (this.#receiveQueue.length > 0) {
return this.#receiveQueue.shift();
} else if (this.#error) {
throw this.#error;
} else {
return new Promise((resolve, reject) => {
this.#receiveResolver = resolve;
this.#receiveRejecter = reject;
});
}
}
abort(reason) {
let message;
if (reason instanceof Error) {
message = reason.message;
} else {
message = `${reason}`;
}
this.#webSocket.close(3e3, message);
if (!this.#error) {
this.#error = reason;
}
}
#receivedError(reason) {
if (!this.#error) {
this.#error = reason;
if (this.#receiveRejecter) {
this.#receiveRejecter(reason);
this.#receiveResolver = void 0;
this.#receiveRejecter = void 0;
}
}
}
};
var BatchServerTransport = class {
constructor(batch) {
this.#batchToReceive = batch;
}
#batchToSend = [];
#batchToReceive;
#allReceived = Promise.withResolvers();
async send(message) {
this.#batchToSend.push(message);
}
async receive() {
let msg = this.#batchToReceive.shift();
if (msg !== void 0) {
return msg;
} else {
this.#allReceived.resolve();
return new Promise((r) => {
});
}
}
abort(reason) {
this.#allReceived.reject(reason);
}
whenAllReceived() {
return this.#allReceived.promise;
}
getResponseBody() {
return this.#batchToSend.join("\n");
}
};
async function newHttpBatchRpcResponse(request, localMain, options) {
if (request.method !== "POST") {
return new Response("This endpoint only accepts POST requests.", { status: 405 });
}
let body = await request.text();
let batch = body === "" ? [] : body.split("\n");
let transport = new BatchServerTransport(batch);
let rpc = new RpcSession(transport, localMain, options);
await transport.whenAllReceived();
await rpc.drain();
return new Response(transport.getResponseBody());
}
var currentMapBuilder;
var MapBuilder = class {
context;
captureMap = /* @__PURE__ */ new Map();
instructions = [];
constructor(subject, path) {
if (currentMapBuilder) {
this.context = {
parent: currentMapBuilder,
captures: [],
subject: currentMapBuilder.capture(subject),
path
};
} else {
this.context = {
parent: void 0,
captures: [],
subject,
path
};
}
currentMapBuilder = this;
}
unregister() {
currentMapBuilder = this.context.parent;
}
makeInput() {
return new MapVariableHook(this, 0);
}
makeOutput(result) {
let devalued;
try {
devalued = Devaluator.devaluate(result.value, void 0, this, result);
} finally {
result.dispose();
}
this.instructions.push(devalued);
if (this.context.parent) {
this.context.parent.instructions.push(
[
"remap",
this.context.subject,
this.context.path,
this.context.captures.map((cap) => ["import", cap]),
this.instructions
]
);
return new MapVariableHook(this.context.parent, this.context.parent.instructions.length);
} else {
return this.context.subject.map(this.context.path, this.context.captures, this.instructions);
}
}
pushCall(hook, path, params) {
let devalued = Devaluator.devaluate(params.value, void 0, this, params);
devalued = devalued[0];
let subject = this.capture(hook.dup());
this.instructions.push(["pipeline", subject, path, devalued]);
return new MapVariableHook(this, this.instructions.length);
}
pushGet(hook, path) {
let subject = this.capture(hook.dup());
this.instructions.push(["pipeline", subject, path]);
return new MapVariableHook(this, this.instructions.length);
}
capture(hook) {
if (hook instanceof MapVariableHook && hook.mapper === this) {
return hook.idx;
}
let result = this.captureMap.get(hook);
if (result === void 0) {
if (this.context.parent) {
let parentIdx = this.context.parent.capture(hook);
this.context.captures.push(parentIdx);
} else {
this.context.captures.push(hook);
}
result = -this.context.captures.length;
this.captureMap.set(hook, result);
}
return result;
}
// ---------------------------------------------------------------------------
// implements Exporter
exportStub(hook) {
throw new Error(
"Can't construct an RpcTarget or RPC callback inside a mapper function. Try creating a new RpcStub outside the callback first, then using it inside the callback."
);
}
exportPromise(hook) {
return this.exportStub(hook);
}
getImport(hook) {
return this.capture(hook);
}
unexport(ids) {
}
onSendError(error) {
}
};
mapImpl.sendMap = (hook, path, func) => {
let builder = new MapBuilder(hook, path);
let result;
try {
result = RpcPayload.fromAppReturn(withCallInterceptor(builder.pushCall.bind(builder), () => {
return func(new RpcPromise(builder.makeInput(), []));
}));
} finally {
builder.unregister();
}
if (result instanceof Promise) {
result.catch((err) => {
});
throw new Error("RPC map() callbacks cannot be async.");
}
return new RpcPromise(builder.makeOutput(result), []);
};
function throwMapperBuilderUseError() {
throw new Error(
"Attempted to use an abstract placeholder from a mapper function. Please make sure your map function has no side effects."
);
}
var MapVariableHook = class extends StubHook {
constructor(mapper, idx) {
super();
this.mapper = mapper;
this.idx = idx;
}
// We don't have anything we actually need to dispose, so dup() can just return the same hook.
dup() {
return this;
}
dispose() {
}
get(path) {
if (path.length == 0) {
return this;
} else if (currentMapBuilder) {
return currentMapBuilder.pushGet(this, path);
} else {
throwMapperBuilderUseError();
}
}
// Other methods should never be called.
call(path, args) {
throwMapperBuilderUseError();
}
map(path, captures, instructions) {
throwMapperBuilderUseError();
}
pull() {
throwMapperBuilderUseError();
}
ignoreUnhandledRejections() {
}
onBroken(callback) {
throwMapperBuilderUseError();
}
};
var MapApplicator = class {
constructor(captures, input) {
this.captures = captures;
this.variables = [input];
}
variables;
dispose() {
for (let variable of this.variables) {
variable.dispose();
}
}
apply(instructions) {
try {
if (instructions.length < 1) {
throw new Error("Invalid empty mapper function.");
}
for (let instruction of instructions.slice(0, -1)) {
let payload = new Evaluator(this).evaluateCopy(instruction);
if (payload.value instanceof RpcStub) {
let hook = unwrapStubNoProperties(payload.value);
if (hook) {
this.variables.push(hook);
continue;
}
}
this.variables.push(new PayloadStubHook(payload));
}
return new Evaluator(this).evaluateCopy(instructions[instructions.length - 1]);
} finally {
for (let variable of this.variables) {
variable.dispose();
}
}
}
importStub(idx) {
throw new Error("A mapper function cannot refer to exports.");
}
importPromise(idx) {
return this.importStub(idx);
}
getExport(idx) {
if (idx < 0) {
return this.captures[-idx - 1];
} else {
return this.variables[idx];
}
}
};
function applyMapToElement(input, parent, owner, captures, instructions) {
let inputHook = new PayloadStubHook(RpcPayload.deepCopyFrom(input, parent, owner));
let mapper = new MapApplicator(captures, inputHook);
try {
return mapper.apply(instructions);
} finally {
mapper.dispose();
}
}
mapImpl.applyMap = (input, parent, owner, captures, instructions) => {
try {
let result;
if (input instanceof RpcPromise) {
throw new Error("applyMap() can't be called on RpcPromise");
} else if (input instanceof Array) {
let payloads = [];
try {
for (let elem of input) {
payloads.push(applyMapToElement(elem, input, owner, captures, instructions));
}
} catch (err) {
for (let payload of payloads) {
payload.dispose();
}
throw err;
}
result = RpcPayload.fromArray(payloads);
} else if (input === null || input === void 0) {
result = RpcPayload.fromAppReturn(input);
} else {
result = applyMapToElement(input, parent, owner, captures, instructions);
}
return new PayloadStubHook(result);
} finally {
for (let cap of captures) {
cap.dispose();
}
}
};
async function newWorkersRpcResponse(request, localMain) {
if (request.method === "POST") {
let response = await newHttpBatchRpcResponse(request, localMain);
response.headers.set("Access-Control-Allow-Origin", "*");
return response;
} else if (request.headers.get("Upgrade")?.toLowerCase() === "websocket") {
return newWorkersWebSocketRpcResponse(request, localMain);
} else {
return new Response("This endpoint only accepts POST or WebSocket requests.", { status: 400 });
}
}
// templates/remoteBindings/ProxyServerWorker.ts
import { EmailMessage } from "cloudflare:email";
var BindingNotFoundError = class extends Error {
constructor(name) {
super(`Binding ${name ? `"${name}"` : ""} not found`);
}
};
function getExposedJSRPCBinding(request, env) {
const url = new URL(request.url);
const bindingName = url.searchParams.get("MF-Binding");
if (!bindingName) {
throw new BindingNotFoundError();
}
const targetBinding = env[bindingName];
if (!targetBinding) {
throw new BindingNotFoundError(bindingName);
}
if (targetBinding.constructor.name === "SendEmail") {
return {
async send(e) {
const message = new EmailMessage(e.from, e.to, e["EmailMessage::raw"]);
return targetBinding.send(message);
}
};
}
if (url.searchParams.has("MF-Dispatch-Namespace-Options")) {
const { name, args, options } = JSON.parse(
url.searchParams.get("MF-Dispatch-Namespace-Options")
);
return targetBinding.get(name, args, options);
}
return targetBinding;
}
function getExposedFetcher(request, env) {
const bindingName = request.headers.get("MF-Binding");
if (!bindingName) {
throw new BindingNotFoundError();
}
const targetBinding = env[bindingName];
if (!targetBinding) {
throw new BindingNotFoundError(bindingName);
}
const dispatchNamespaceOptions = request.headers.get(
"MF-Dispatch-Namespace-Options"
);
if (dispatchNamespaceOptions) {
const { name, args, options } = JSON.parse(dispatchNamespaceOptions);
return targetBinding.get(name, args, options);
}
return targetBinding;
}
function isJSRPCBinding(request) {
const url = new URL(request.url);
return request.headers.has("Upgrade") && url.searchParams.has("MF-Binding");
}
var ProxyServerWorker_default = {
async fetch(request, env) {
try {
if (isJSRPCBinding(request)) {
return newWorkersRpcResponse(
request,
getExposedJSRPCBinding(request, env)
);
} else {
const fetcher = getExposedFetcher(request, env);
const originalHeaders = new Headers();
for (const [name, value] of request.headers) {
if (name.startsWith("mf-header-")) {
originalHeaders.set(name.slice("mf-header-".length), value);
} else if (name === "upgrade") {
originalHeaders.set(name, value);
}
}
return fetcher.fetch(
request.headers.get("MF-URL") ?? "http://example.com",
new Request(request, {
redirect: "manual",
headers: originalHeaders
})
);
}
} catch (e) {
if (e instanceof BindingNotFoundError) {
return new Response(e.message, { status: 400 });
}
return new Response(e.message, { status: 500 });
}
}
};
export {
ProxyServerWorker_default as default
};