(function () {
const { core, primordials } = __bootstrap;
const { ERR_INVALID_ARG_TYPE } = core.loadExtScript(
"ext:deno_node/internal/errors.ts",
);
const { nextTick } = core.loadExtScript("ext:deno_node/_next_tick.ts");
const { validateFunction } = core.loadExtScript(
"ext:deno_node/internal/validators.mjs",
);
const {
ArrayPrototypeAt,
ArrayPrototypeIndexOf,
ArrayPrototypePush,
ArrayPrototypePushApply,
ArrayPrototypeSlice,
ArrayPrototypeSplice,
ObjectDefineProperty,
ObjectGetPrototypeOf,
ObjectPrototypeIsPrototypeOf,
ObjectSetPrototypeOf,
PromisePrototype,
PromisePrototypeThen,
PromiseReject,
PromiseResolve,
ReflectApply,
SafeArrayIterator,
SafeFinalizationRegistry,
SafeMap,
SafeMapIterator,
SymbolHasInstance,
} = primordials;
const { WeakReference } = core.loadExtScript(
"ext:deno_node/internal/util.mjs",
);
class WeakRefMap extends SafeMap {
#finalizers = new SafeFinalizationRegistry((key) => {
if (!this.has(key)) this.delete(key);
});
set(key, value) {
this.#finalizers.register(value, key);
return super.set(key, new WeakReference(value));
}
get(key) {
return super.get(key)?.get();
}
has(key) {
return !!this.get(key);
}
incRef(key) {
return super.get(key)?.incRef();
}
decRef(key) {
return super.get(key)?.decRef();
}
}
function markActive(channel) {
ObjectSetPrototypeOf(channel, ActiveChannel.prototype);
channel._subscribers = [];
channel._stores = new SafeMap();
}
function maybeMarkInactive(channel) {
if (!channel._subscribers.length && !channel._stores.size) {
ObjectSetPrototypeOf(channel, Channel.prototype);
channel._subscribers = undefined;
channel._stores = undefined;
}
}
function defaultTransform(data) {
return data;
}
function wrapStoreRun(store, data, next, transform = defaultTransform) {
return () => {
let context;
try {
context = transform(data);
} catch (err) {
nextTick(() => {
throw err;
});
return next();
}
return store.run(context, next);
};
}
class ActiveChannel {
subscribe(subscription) {
validateFunction(subscription, "subscription");
this._subscribers = ArrayPrototypeSlice(this._subscribers);
ArrayPrototypePush(this._subscribers, subscription);
channels.incRef(this.name);
}
unsubscribe(subscription) {
const index = ArrayPrototypeIndexOf(this._subscribers, subscription);
if (index === -1) return false;
const before = ArrayPrototypeSlice(this._subscribers, 0, index);
const after = ArrayPrototypeSlice(this._subscribers, index + 1);
this._subscribers = before;
ArrayPrototypePushApply(this._subscribers, after);
channels.decRef(this.name);
maybeMarkInactive(this);
return true;
}
bindStore(store, transform) {
const replacing = this._stores.has(store);
if (!replacing) channels.incRef(this.name);
this._stores.set(store, transform);
}
unbindStore(store) {
if (!this._stores.has(store)) {
return false;
}
this._stores.delete(store);
channels.decRef(this.name);
maybeMarkInactive(this);
return true;
}
get hasSubscribers() {
return true;
}
publish(data) {
const subscribers = this._subscribers;
for (let i = 0; i < (subscribers?.length || 0); i++) {
try {
const onMessage = subscribers[i];
onMessage(data, this.name);
} catch (err) {
nextTick(() => {
throw err;
});
}
}
}
runStores(data, fn, thisArg, ...args) {
let run = () => {
this.publish(data);
return ReflectApply(fn, thisArg, args);
};
for (const entry of new SafeMapIterator(this._stores)) {
const store = entry[0];
const transform = entry[1];
run = wrapStoreRun(store, data, run, transform);
}
return run();
}
}
class Channel {
constructor(name) {
this._subscribers = undefined;
this._stores = undefined;
this.name = name;
channels.set(name, this);
}
static [SymbolHasInstance](instance) {
const prototype = ObjectGetPrototypeOf(instance);
return prototype === Channel.prototype ||
prototype === ActiveChannel.prototype;
}
subscribe(subscription) {
markActive(this);
this.subscribe(subscription);
}
unsubscribe() {
return false;
}
bindStore(store, transform) {
markActive(this);
this.bindStore(store, transform);
}
unbindStore() {
return false;
}
get hasSubscribers() {
return false;
}
publish() {}
runStores(_data, fn, thisArg, ...args) {
return ReflectApply(fn, thisArg, args);
}
}
const channels = new WeakRefMap();
function channel(name) {
const ch = channels.get(name);
if (ch) return ch;
if (typeof name !== "string" && typeof name !== "symbol") {
throw new ERR_INVALID_ARG_TYPE("channel", ["string", "symbol"], name);
}
return new Channel(name);
}
function subscribe(name, subscription) {
return channel(name).subscribe(subscription);
}
function unsubscribe(name, subscription) {
return channel(name).unsubscribe(subscription);
}
function hasSubscribers(name) {
const ch = channels.get(name);
if (!ch) return false;
return ch.hasSubscribers;
}
const traceEvents = [
"start",
"end",
"asyncStart",
"asyncEnd",
"error",
];
function assertChannel(value, name) {
if (!(value instanceof Channel)) {
throw new ERR_INVALID_ARG_TYPE(name, ["Channel"], value);
}
}
function tracingChannelFrom(nameOrChannels, name) {
if (typeof nameOrChannels === "string") {
return channel(`tracing:${nameOrChannels}:${name}`);
}
if (typeof nameOrChannels === "object" && nameOrChannels !== null) {
const ch = nameOrChannels[name];
assertChannel(ch, `nameOrChannels.${name}`);
return ch;
}
throw new ERR_INVALID_ARG_TYPE("nameOrChannels", [
"string",
"object",
"TracingChannel",
], nameOrChannels);
}
class TracingChannel {
constructor(nameOrChannels) {
for (const eventName of new SafeArrayIterator(traceEvents)) {
ObjectDefineProperty(this, eventName, {
__proto__: null,
value: tracingChannelFrom(nameOrChannels, eventName),
});
}
}
get hasSubscribers() {
return this.start.hasSubscribers ||
this.end.hasSubscribers ||
this.asyncStart.hasSubscribers ||
this.asyncEnd.hasSubscribers ||
this.error.hasSubscribers;
}
subscribe(handlers) {
for (const name of new SafeArrayIterator(traceEvents)) {
if (!handlers[name]) continue;
this[name]?.subscribe(handlers[name]);
}
}
unsubscribe(handlers) {
let done = true;
for (const name of new SafeArrayIterator(traceEvents)) {
if (!handlers[name]) continue;
if (!this[name]?.unsubscribe(handlers[name])) {
done = false;
}
}
return done;
}
traceSync(fn, context = { __proto__: null }, thisArg, ...args) {
if (!this.hasSubscribers) {
return ReflectApply(fn, thisArg, args);
}
const { start, end, error } = this;
return start.runStores(context, () => {
try {
const result = ReflectApply(fn, thisArg, args);
context.result = result;
return result;
} catch (err) {
context.error = err;
error.publish(context);
throw err;
} finally {
end.publish(context);
}
});
}
tracePromise(fn, context = { __proto__: null }, thisArg, ...args) {
if (!this.hasSubscribers) {
return ReflectApply(fn, thisArg, args);
}
const { start, end, asyncStart, asyncEnd, error } = this;
function reject(err) {
context.error = err;
error.publish(context);
asyncStart.runStores(context, () => {});
asyncEnd.publish(context);
return PromiseReject(err);
}
function resolve(result) {
context.result = result;
asyncStart.runStores(context, () => {});
asyncEnd.publish(context);
return result;
}
return start.runStores(context, () => {
try {
let promise = ReflectApply(fn, thisArg, args);
if (!ObjectPrototypeIsPrototypeOf(PromisePrototype, promise)) {
promise = PromiseResolve(promise);
}
return PromisePrototypeThen(promise, resolve, reject);
} catch (err) {
context.error = err;
error.publish(context);
throw err;
} finally {
end.publish(context);
}
});
}
traceCallback(
fn,
position = -1,
context = { __proto__: null },
thisArg,
...args
) {
if (!this.hasSubscribers) {
return ReflectApply(fn, thisArg, args);
}
const { start, end, asyncStart, asyncEnd, error } = this;
function wrappedCallback(err, res) {
if (err) {
context.error = err;
error.publish(context);
} else {
context.result = res;
}
asyncStart.runStores(context, () => {
try {
return ReflectApply(callback, this, arguments);
} finally {
asyncEnd.publish(context);
}
});
}
const callback = ArrayPrototypeAt(args, position);
validateFunction(callback, "callback");
ArrayPrototypeSplice(args, position, 1, wrappedCallback);
return start.runStores(context, () => {
try {
return ReflectApply(fn, thisArg, args);
} catch (err) {
context.error = err;
error.publish(context);
throw err;
} finally {
end.publish(context);
}
});
}
}
function tracingChannel(nameOrChannels) {
return new TracingChannel(nameOrChannels);
}
return {
default: {
channel,
hasSubscribers,
subscribe,
tracingChannel,
unsubscribe,
Channel,
},
channel,
hasSubscribers,
subscribe,
tracingChannel,
unsubscribe,
Channel,
};
})();