@beignet/core
Version:
Core framework primitives for Beignet
224 lines (209 loc) • 6.58 kB
text/typescript
import type { EventPublishOptions } from "./index.js";
import type { EventTransportValue } from "./transport.js";
const PARSED_EVENT_PAYLOAD = Symbol.for("beignet.eventPayload.parsed");
const TRANSPORT_STABLE_EVENT_PAYLOAD = Symbol.for(
"beignet.eventPayload.transportStable",
);
type ParsedEventPublishOptions = EventPublishOptions & {
[PARSED_EVENT_PAYLOAD]?: object;
[TRANSPORT_STABLE_EVENT_PAYLOAD]?: object;
};
interface ParsedPayloadState {
readonly eventName: string;
readonly payloadSchema: object;
readonly validate: unknown;
readonly payload: unknown;
}
type PayloadSnapshot =
| { readonly kind: "primitive"; readonly value: unknown }
| {
readonly kind: "array";
readonly value: object;
readonly prototype: object | null;
readonly items: readonly PayloadSnapshot[];
}
| {
readonly kind: "object";
readonly value: object;
readonly prototype: object | null;
readonly entries: readonly (readonly [string, PayloadSnapshot])[];
};
interface TransportStablePayloadState {
readonly eventName: string;
readonly payloadSchema: object;
readonly validate: unknown;
readonly payload: PayloadSnapshot;
readonly fingerprint: string;
}
interface EventPayloadIdentity {
readonly name: string;
readonly payload: {
readonly "~standard": {
readonly validate: unknown;
};
};
}
const transportStablePayloadStates = new WeakMap<
object,
TransportStablePayloadState
>();
const parsedPayloadStates = new WeakMap<object, ParsedPayloadState>();
function transportFingerprint(value: EventTransportValue): string {
return JSON.stringify(value);
}
function capturePayloadSnapshot(value: unknown): PayloadSnapshot {
if (value === null || typeof value !== "object") {
return { kind: "primitive", value };
}
if (Array.isArray(value)) {
const items: PayloadSnapshot[] = [];
for (let index = 0; index < value.length; index += 1) {
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
if (!descriptor || !("value" in descriptor)) {
throw new TypeError(
"Transport-stable array state must contain data properties.",
);
}
items.push(capturePayloadSnapshot(descriptor.value));
}
return {
kind: "array",
value,
prototype: Object.getPrototypeOf(value),
items,
};
}
return {
kind: "object",
value,
prototype: Object.getPrototypeOf(value),
entries: Reflect.ownKeys(value).map((key) => {
const descriptor = Object.getOwnPropertyDescriptor(value, key);
if (typeof key !== "string" || !descriptor || !("value" in descriptor)) {
throw new TypeError(
"Transport-stable payload state must contain data properties.",
);
}
return [key, capturePayloadSnapshot(descriptor.value)] as const;
}),
};
}
function matchesPayloadSnapshot(
snapshot: PayloadSnapshot,
value: unknown,
): boolean {
if (snapshot.kind === "primitive") return Object.is(snapshot.value, value);
if (snapshot.value !== value || value === null || typeof value !== "object") {
return false;
}
if (Object.getPrototypeOf(value) !== snapshot.prototype) return false;
if (snapshot.kind === "array") {
return (
Array.isArray(value) &&
value.length === snapshot.items.length &&
snapshot.items.every((item, index) => {
const descriptor = Object.getOwnPropertyDescriptor(
value,
String(index),
);
return (
descriptor !== undefined &&
"value" in descriptor &&
matchesPayloadSnapshot(item, descriptor.value)
);
})
);
}
if (Array.isArray(value)) return false;
const keys = Reflect.ownKeys(value);
return (
keys.length === snapshot.entries.length &&
snapshot.entries.every(([key, item], index) => {
const descriptor = Object.getOwnPropertyDescriptor(value, key);
return (
keys[index] === key &&
descriptor !== undefined &&
"value" in descriptor &&
matchesPayloadSnapshot(item, descriptor.value)
);
})
);
}
export function markEventPayloadParsed(
event: EventPayloadIdentity,
payload: unknown,
options?: EventPublishOptions,
): EventPublishOptions {
const token = {};
parsedPayloadStates.set(token, {
eventName: event.name,
payloadSchema: event.payload,
validate: event.payload["~standard"].validate,
payload,
});
return {
...options,
[PARSED_EVENT_PAYLOAD]: token,
} as ParsedEventPublishOptions;
}
export function isEventPayloadParsed(
event: EventPayloadIdentity,
payload: unknown,
options?: EventPublishOptions,
): boolean {
const token = (options as ParsedEventPublishOptions | undefined)?.[
PARSED_EVENT_PAYLOAD
];
const state = token ? parsedPayloadStates.get(token) : undefined;
return (
state?.eventName === event.name &&
state.payloadSchema === event.payload &&
state.validate === event.payload["~standard"].validate &&
Object.is(state.payload, payload)
);
}
export function markEventPayloadTransportStable(
event: EventPayloadIdentity,
payload: unknown,
transportValue: EventTransportValue,
options?: EventPublishOptions,
): EventPublishOptions {
const parsedToken = {};
parsedPayloadStates.set(parsedToken, {
eventName: event.name,
payloadSchema: event.payload,
validate: event.payload["~standard"].validate,
payload,
});
const stableToken = {};
transportStablePayloadStates.set(stableToken, {
eventName: event.name,
payloadSchema: event.payload,
validate: event.payload["~standard"].validate,
payload: capturePayloadSnapshot(payload),
fingerprint: transportFingerprint(transportValue),
});
return {
...options,
[PARSED_EVENT_PAYLOAD]: parsedToken,
[TRANSPORT_STABLE_EVENT_PAYLOAD]: stableToken,
} as ParsedEventPublishOptions;
}
export function isEventPayloadTransportStable(
event: EventPayloadIdentity,
payload: unknown,
transportValue: EventTransportValue,
options?: EventPublishOptions,
): boolean {
const token = (options as ParsedEventPublishOptions | undefined)?.[
TRANSPORT_STABLE_EVENT_PAYLOAD
];
const state = token ? transportStablePayloadStates.get(token) : undefined;
return (
state?.eventName === event.name &&
state.payloadSchema === event.payload &&
state.validate === event.payload["~standard"].validate &&
matchesPayloadSnapshot(state.payload, payload) &&
state.fingerprint === transportFingerprint(transportValue)
);
}