wrangler
Version:
Command-line interface for all things Cloudflare Workers
1,688 lines • 74 kB
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} < ${refc