import VelociousWebsocketChannel from "../http-server/websocket-channel.js"; export type FrontendModelLifecycleBroadcastBody = { action?: string; id?: import("../utils/model-primary-key.js").ModelPrimaryKeyValue; matchedEventFilterKeys?: string[]; previousId?: import("../utils/model-primary-key.js").ModelPrimaryKeyValue; record?: import("./query.js").FrontendModelTransportValue; [key: string]: import("./query.js").FrontendModelTransportValue | string[] | undefined; }; export type DestroyAuthorizationRecord = Record; export type FrontendModelWebsocketUpgradeRequest = { headers?: () => Record; remoteAddress?: () => string | undefined; }; export type FrontendModelWebsocketSyntheticRequest = { headers: () => Record; header: (name: string) => string | string[] | undefined; metadata: (key?: string) => Record | import("./query.js").FrontendModelTransportValue | undefined; path: () => string; httpMethod: () => string; remoteAddress: () => string | undefined; origin: () => string | string[] | undefined; }; /** * Per-session channel subscription for frontend-model lifecycle events. * Replaces the legacy `FrontendModelWebsocketChannel` (Phase 3). * * `canSubscribe` resolves the caller's ability once and requires a read rule * for the requested model class. Create/update delivery then reloads each * record through that ability and serializes it through the subscribed * frontend resource. Subscriber-provided event filters can further narrow * those authorized events. * * Wire: subscribe with `subscribeChannel("frontend-models", {params: {model: ModelName}})`. * Backend publishes `{action, id, record}` via * `configuration.broadcastToChannel("frontend-models", {model: ModelName}, body)`; * `matches()` routes by model name. */ export default class FrontendModelWebsocketChannel extends VelociousWebsocketChannel { /** * Ability. * @type {import("../authorization/ability.js").default | null} */ _ability: import("../authorization/ability.js").default | null; /** * Runs can subscribe. * @returns {Promise} Whether the frontend-model subscription is authorized. */ canSubscribe(): Promise; /** * Resolves a subscription name through frontend resources before falling back to a backing model name. * @param {string} modelName - Frontend resource name. * @returns {typeof import("../database/record/index.js").default | undefined} - Backing model class. */ _modelClass(modelName: string): typeof import("../database/record/index.js").default | undefined; /** * Runs deliver broadcast. * @param {FrontendModelLifecycleBroadcastBody} body - Broadcast body. * @param {import("../http-server/websocket-channel.js").WebsocketBroadcastMetadata} [meta] - Optional server-side broadcast metadata. * @returns {Promise} Resolves after delivery. */ deliverBroadcast(body: FrontendModelLifecycleBroadcastBody, meta?: import("../http-server/websocket-channel.js").WebsocketBroadcastMetadata): Promise; /** * Runs deliver broadcast. * @param {FrontendModelLifecycleBroadcastBody} body - Broadcast body. * @param {import("../http-server/websocket-channel.js").WebsocketBroadcastMetadata} [meta] - Optional server-side broadcast metadata. * @returns {Promise} Resolves after delivery. */ _deliverBroadcast(body: FrontendModelLifecycleBroadcastBody, meta?: import("../http-server/websocket-channel.js").WebsocketBroadcastMetadata): Promise; /** * Requires a resync for relevant destroy events because their authorization * snapshots are intentionally excluded from the persisted replay payload. * @param {import("../http-server/websocket-channel.js").WebsocketJsonValue} body - Persisted broadcast payload. * @returns {boolean} - Whether replay cannot safely authorize this event. */ _requiresReplayGap(body: import("../http-server/websocket-channel.js").WebsocketJsonValue): boolean; /** * Checks a destroy against the subscriber's ordinary authorized query by * replacing the deleted backing table with the captured pre-delete row. Values * are quoted on this trusted database connection; no broadcast-provided SQL is run. * @param {FrontendModelLifecycleBroadcastBody} body - Destroy broadcast body. * @param {typeof import("../frontend-model-controller.js").default} FrontendModelController - Server-side frontend-model controller class. * @param {import("./query.js").FrontendModelTransportValue | undefined} destroyAuthorizationRecord - Server-only pre-delete record from live broadcast metadata. * @returns {Promise} - Whether the subscriber could read the record before deletion. */ _destroyEventIsAuthorized(body: FrontendModelLifecycleBroadcastBody, FrontendModelController: typeof import("../frontend-model-controller.js").default, destroyAuthorizationRecord: import("./query.js").FrontendModelTransportValue | undefined): Promise; /** * Builds a backing-model query whose source is the captured pre-delete row. * @param {typeof import("../database/record/index.js").default} ModelClass - Backing model class. * @param {DestroyAuthorizationRecord} destroyAuthorizationRecord - Captured pre-delete record. * @returns {import("../database/query/model-class-query.js").default} - One-row model query. */ _destroyAuthorizationQuery(ModelClass: typeof import("../database/record/index.js").default, destroyAuthorizationRecord: DestroyAuthorizationRecord): import("../database/query/model-class-query.js").default; /** * Replaces a query's backing table with a safely quoted one-row derived table. * @param {import("../database/query/model-class-query.js").default} query - Query to update. * @param {typeof import("../database/record/index.js").default} ModelClass - Backing model class. * @param {DestroyAuthorizationRecord} destroyAuthorizationRecord - Captured pre-delete record. * @returns {void} */ _applyDestroyAuthorizationRecordToQuery(query: import("../database/query/model-class-query.js").default, ModelClass: typeof import("../database/record/index.js").default, destroyAuthorizationRecord: DestroyAuthorizationRecord): void; /** * Runs matches. * @param {Record} broadcastParams - Params from `broadcastToChannel`. * @returns {boolean} Whether the broadcast matches this subscriber's model. */ matches(broadcastParams: Record): boolean; /** * Drops the server-only destroy-authorization snapshot before replay * persistence. The snapshot is what makes replayed destroy events * require a client resync, and the pre-delete row it captures must * never be stored. * @param {Record | null | undefined} broadcastParams - Params from `broadcastToChannel`. * @returns {Record | null} - Persistable routing params. */ static replayableBroadcastParams(broadcastParams: Record | null | undefined): Record | null; /** * Runs debug snapshot. * @returns {Record>} Debug-safe subscription details. */ debugSnapshot(): Record>; /** * Runs model name. * @returns {string | null} - Requested frontend-model name or null. */ _modelName(): string | null; /** * Runs has event filter params. * @returns {boolean} - Whether this subscription requested event query filters. */ _hasEventFilterParams(): boolean; /** * Runs has unfiltered event delivery. * @returns {boolean} - Whether unfiltered callbacks should receive every event. */ _hasUnfilteredEventDelivery(): boolean; /** * Runs has destroy event delivery. * @returns {boolean} - Whether id-only destroy events should be delivered with event filters. */ _hasDestroyEventDelivery(): boolean; /** * Runs event filters. * @returns {import("./query.js").FrontendModelEventFilterPayloadEntry[]} - Valid event filters. */ _eventFilters(): import("./query.js").FrontendModelEventFilterPayloadEntry[]; /** * Runs frontend model controller class. * @returns {Promise} - Frontend model controller class. */ _frontendModelControllerClass(): Promise; /** * Runs frontend model controller. * @param {typeof import("../frontend-model-controller.js").default} FrontendModelController - Server-side frontend-model controller class. * @param {Record>} [params] - Optional params override. * @returns {import("../frontend-model-controller.js").default} - Synthetic controller used for resource serialization. */ _frontendModelController(FrontendModelController: typeof import("../frontend-model-controller.js").default, params?: Record>): import("../frontend-model-controller.js").default; /** * Resolves tenant for event. * @param {import("../utils/model-primary-key.js").ModelPrimaryKeyValue} id - Event record id. * @returns {Promise>} - Resolved tenant. */ _resolveEventTenant(id: import("../utils/model-primary-key.js").ModelPrimaryKeyValue): Promise>; /** * Resolves the subscriber's tenant for the broadcast record and runs `callback` inside that tenant * context. Broadcast delivery runs in whatever ambient tenant context the publisher left behind. For * multi-tenant records that ambient tenant may have been resolved without the subscriber's request * (e.g. a relay endpoint or background job mutating the row), so it lacks the subscriber's per-record * access flags and the per-event authorization query wrongly finds nothing. Re-resolving the tenant * from the event record id plus the subscriber's request makes the authorization queries run against * the subscriber's own tenant/ability scope. When no tenant resolves (non-multitenant configs), the * callback runs directly so the ambient context is preserved. * @template T * @param {import("../utils/model-primary-key.js").ModelPrimaryKeyValue} id - Event record id. * @param {() => Promise} callback - Authorized-query callback. * @returns {Promise} - Callback result. */ _withEventTenant(id: import("../utils/model-primary-key.js").ModelPrimaryKeyValue, callback: () => Promise): Promise; /** * Runs matched event filter keys for event id. * @param {import("../utils/model-primary-key.js").ModelPrimaryKeyValue} id - Event record id. * @param {typeof import("../frontend-model-controller.js").default} FrontendModelController - Server-side frontend-model controller class. * @returns {Promise} - Event filter keys matched by the record. */ _matchedEventFilterKeysForEventId(id: import("../utils/model-primary-key.js").ModelPrimaryKeyValue, FrontendModelController: typeof import("../frontend-model-controller.js").default): Promise; /** * Runs event matches filter. * @param {object} args - Filter args. * @param {typeof import("../frontend-model-controller.js").default} args.FrontendModelController - Server-side frontend-model controller class. * @param {import("./query.js").FrontendModelEventFilterPayloadEntry} args.eventFilter - Event filter payload. * @param {import("../utils/model-primary-key.js").ModelPrimaryKeyValue} args.id - Event record id. * @returns {Promise} Whether the record matches the filter. */ _eventMatchesFilter({ FrontendModelController, eventFilter, id }: { FrontendModelController: typeof import("../frontend-model-controller.js").default; eventFilter: import("./query.js").FrontendModelEventFilterPayloadEntry; id: import("../utils/model-primary-key.js").ModelPrimaryKeyValue; }): Promise; /** * Runs projected record for event id. * @param {import("../utils/model-primary-key.js").ModelPrimaryKeyValue} id - Event record id. * @param {typeof import("../frontend-model-controller.js").default} FrontendModelController - Server-side frontend-model controller class. * @returns {Promise | null>} - Serialized projected record. */ _projectedRecordForEventId(id: import("../utils/model-primary-key.js").ModelPrimaryKeyValue, FrontendModelController: typeof import("../frontend-model-controller.js").default): Promise | null>; /** * Minimal Request-like stub used only for ability resolution. Avoids * importing `WebsocketRequest` here because its `node:querystring` * dependency would pull server-only code into browser bundles via * the `configuration → logger → websocket-publishers` import chain. * Header names are normalized to lowercase so `header("cookie")` * finds a value regardless of whether the upgrade-request headers * map uses `"Cookie"` or `"cookie"`. Session metadata stays separate * from headers and is exposed through `metadata(...)` for ability * resolvers that need websocket-delivered session data. * @returns {FrontendModelWebsocketSyntheticRequest} Request-like object for ability resolution. */ _syntheticRequest(): FrontendModelWebsocketSyntheticRequest; } //# sourceMappingURL=websocket-channel.d.ts.map