@beignet/core
Version:
Core framework primitives for Beignet
213 lines • 9.26 kB
JavaScript
import "../server-only.js";
import { BroadcastValidationError, broadcastLimits, channelKey, parseChannelEvent, parseChannelParams, } from "./index.js";
const clientIdPattern = /^[a-zA-Z0-9_-]{22,128}$/;
function parseOrigin(value) {
if (!value || typeof value !== "object")
return undefined;
const candidate = value;
if (candidate.version !== 1 ||
typeof candidate.clientId !== "string" ||
!clientIdPattern.test(candidate.clientId) ||
typeof candidate.scope !== "string" ||
candidate.scope.length === 0 ||
candidate.scope.length > 2_048)
return undefined;
return { version: 1, clientId: candidate.clientId, scope: candidate.scope };
}
/** Validate an origin already captured by a trusted server for a job payload. */
export const broadcastOriginSchema = {
"~standard": {
version: 1,
vendor: "beignet",
validate(value) {
const origin = parseOrigin(value);
return origin
? { value: origin }
: { issues: [{ message: "Invalid broadcast origin" }] };
},
},
};
/** Never derive principal, tenant, or namespace from the optional client header. */
export function resolveBroadcastOrigin(options) {
if (!options.principalId || !options.namespace)
return undefined;
return parseOrigin({
version: 1,
clientId: options.headers.get("X-Beignet-Broadcast-Client"),
scope: JSON.stringify([
options.namespace,
options.tenantId ?? null,
options.principalId,
]),
});
}
/** Shared validation and cleanup for provider implementations. */
export function createBroadcastPort(transport) {
return {
async publish(channel, publication) {
const params = await parseChannelParams(channel, publication.params);
const event = await parseChannelEvent(channel, publication);
const excludeOrigin = publication.excludeOrigin === undefined
? undefined
: parseOrigin(publication.excludeOrigin);
if (publication.excludeOrigin !== undefined && !excludeOrigin)
throw new BroadcastValidationError("Invalid broadcast origin");
await transport.publish(channelKey(channel.name, params), {
version: 1,
channel: channel.name,
params,
...event,
...(excludeOrigin ? { excludeOrigin } : {}),
});
},
subscribe(channel, options) {
let stopped = false;
let failed = false;
let subscription;
let queued = 0;
let queuedBytes = 0;
let pending = Promise.resolve();
let cancel;
let cleanup;
const release = () => (cleanup ??= subscription?.unsubscribe() ?? Promise.resolve());
const disconnect = () => {
if (stopped || failed)
return;
failed = true;
try {
options.onDisconnect();
}
catch {
/* Consumer callbacks cannot break provider lifecycle. */
}
};
const initialize = (async () => {
const params = await parseChannelParams(channel, options.params);
if (stopped)
return;
const key = channelKey(channel.name, params);
subscription = transport.subscribe(key, {
onDisconnect: disconnect,
onMessage(value) {
if (stopped || failed)
return;
let bytes;
try {
bytes = new TextEncoder().encode(JSON.stringify(value)).byteLength;
}
catch {
disconnect();
return;
}
if (bytes >
broadcastLimits.payloadBytes +
broadcastLimits.requestBytes +
16_384 ||
queuedBytes + bytes > broadcastLimits.bufferedBytes) {
disconnect();
return;
}
if (++queued > 128) {
disconnect();
return;
}
queuedBytes += bytes;
pending = pending
.then(async () => {
if (stopped || failed)
return;
if (!value || typeof value !== "object")
throw new BroadcastValidationError("Invalid broadcast envelope");
const envelope = value;
if (envelope.version !== 1 || envelope.channel !== channel.name)
throw new BroadcastValidationError("Invalid broadcast envelope");
const receivedParams = await parseChannelParams(channel, envelope.params);
if (channelKey(channel.name, receivedParams) !== key)
throw new BroadcastValidationError("Broadcast channel mismatch");
const event = await parseChannelEvent(channel, envelope);
const excludeOrigin = envelope.excludeOrigin === undefined
? undefined
: parseOrigin(envelope.excludeOrigin);
if (envelope.excludeOrigin !== undefined && !excludeOrigin)
throw new BroadcastValidationError("Invalid broadcast origin");
if (!stopped && !failed)
await options.onEvent(event, { excludeOrigin });
})
.catch(disconnect)
.finally(() => {
queued--;
queuedBytes -= bytes;
});
},
});
await subscription.ready;
if (stopped)
await release();
if (failed)
throw new Error("Broadcast subscription lost continuity before readiness");
})();
let timeout;
const ready = Promise.race([
initialize,
new Promise((_, reject) => {
cancel = () => reject(new Error("Broadcast subscription cancelled"));
timeout = setTimeout(() => reject(new Error("Broadcast subscription readiness timed out")), broadcastLimits.readyTimeoutMs);
}),
])
.catch((error) => {
stopped = true;
void release().catch(() => undefined);
throw error;
})
.finally(() => {
if (timeout !== undefined)
clearTimeout(timeout);
cancel = undefined;
});
// Callers still observe rejection through ready; early cancellation must not leak a rejection.
void ready.catch(() => undefined);
return {
ready,
async unsubscribe() {
stopped = true;
cancel?.();
await ready.catch(() => undefined);
await release();
},
};
},
};
}
/** Bind every channel explicitly, including public channels. */
export function createBroadcasting() {
function defineChannelBinding(channel, options) {
return Object.freeze({
kind: "channel-binding",
channel,
authorize: ({ ctx, params, }) => options.authorize({ ctx, params: params }),
});
}
function defineChannelRegistry(bindings) {
const byName = new Map();
for (const binding of bindings) {
if (byName.has(binding.channel.name))
throw new BroadcastValidationError(`Duplicate channel ${binding.channel.name}`);
byName.set(binding.channel.name, binding);
}
return Object.freeze({
kind: "channel-registry",
bindings: Object.freeze([...bindings]),
get: (name) => byName.get(name),
});
}
return { defineChannelBinding, defineChannelRegistry };
}
/** Validate a configurable deadline without allowing an unbounded stream. */
export function broadcastLifetime(value = broadcastLimits.defaultLifetimeMs) {
if (!Number.isSafeInteger(value) ||
value < 1 ||
value > broadcastLimits.maxLifetimeMs)
throw new BroadcastValidationError(`Broadcast maxLifetimeMs must be a safe integer between 1 and ${broadcastLimits.maxLifetimeMs} milliseconds`);
return value;
}
//# sourceMappingURL=server.js.map