import Configuration from "../configuration.js"; import Logger from "../logger.js"; /** * Declarative server-side sync publisher — the server mirror of the client's * track-by-default mutation tracking. * * Server models declare what to publish through `static sync`'s `publish` * key, and Velocious writes every committed server-side change to the sync * change feed (model-backed Sync-row upsert with server re-sequencing) and * broadcasts the standard sync envelope (`{echoOrigin, syncs: [...]}`) on the * framework sync channel ({@link VELOCIOUS_SYNC_CHANNEL}) scoped by the * change's derived scope-partition values, so devices receive server-origin * changes without app code declaring channels or calling manual * upsert/broadcast helpers: * * static sync = {publish: true} // default payload (attributes) + default scope partition * static sync = {publish: {serialize: (record) => ({id: record.id(), pin: record.pin()})}} * * The scope partition comes from the sync model's `static * syncScopeAttributes` declaration (for example `["eventId"]` or * `["accountId"]` — Velocious has no built-in partition name): each declared * scope attribute reads the record's attribute of the same name when the * model has one, else the record's own id (scope-root models), overridable * per model through `publish: {scopeAttributes: {accountId: "ownerId"}}`. * The pre-framework-channel `broadcasts` list and the `eventId` * string/resolver-function declaration forms keep working but are deprecated. * * Replayed device mutations never double-publish: the framework's routed * replay apply marks its written records through `markServerApply(record)` * (see sync-publish-suppression.js), and app code applying already-synced * data can use `markServerApply`/`withoutPublishing` the same way. */ export default class SyncPublisher { /** @type {{actorForeignKeyColumn: string, broadcaster: import("./sync-publisher-types.js").SyncPublisherOptions["broadcaster"], configuration: Configuration, onError: import("./sync-publisher-types.js").SyncPublisherOptions["onError"], resources: Record, syncModel: ReturnType}} */ config: { actorForeignKeyColumn: string; broadcaster: import("./sync-publisher-types.js").SyncPublisherOptions["broadcaster"]; configuration: Configuration; onError: import("./sync-publisher-types.js").SyncPublisherOptions["onError"]; resources: Record; syncModel: ReturnType; }; /** @type {Array<{callback: (record: ReturnType) => Promise, callbackName: "afterCreate" | "afterUpdate" | "afterDestroy", modelClass: ReturnType}>} */ _publishedCallbacks: Array<{ callback: (record: ReturnType) => Promise; callbackName: "afterCreate" | "afterUpdate" | "afterDestroy"; modelClass: ReturnType; }>; /** @type {Logger | null} */ _logger: Logger | null; _started: boolean; /** * Builds the sync publisher by deriving published resources from the * configuration's registered models: every model declaring `static sync` * with a `publish` declaration becomes a published resource * (`publish: false` opts out). The sync/change model is the registered * "Sync" model and broadcasts default to the configuration's channel * broadcast. * @param {import("./sync-publisher-types.js").SyncPublisherOptions} [options] - Optional overrides. */ constructor(options?: import("./sync-publisher-types.js").SyncPublisherOptions); /** * Builds a sync publisher derived from the given configuration. Alias for * `new SyncPublisher({configuration, ...options})`. * @param {Configuration} [configuration] - Configuration owning the registered models. Defaults to the current configuration. * @param {Omit} [options] - Optional overrides. * @returns {SyncPublisher} Sync publisher derived from the configuration. */ static fromConfiguration(configuration?: Configuration, options?: Omit): SyncPublisher; /** * Starts (and memoizes per configuration) the sync publisher for a server * boot: no-op when no registered model declares a publish config, guarded so * repeated boots with the same configuration register the publish callbacks * only once. * @param {Configuration} configuration - Configuration owning the registered models. * @returns {Promise} Started publisher, or null when no models declare publish. */ static startFromConfiguration(configuration: Configuration): Promise; /** * Registers the publish callbacks for every published resource: server-side * creates and updates (destroys when opted in) upsert a sync change row and * fan out the declared broadcasts once their transaction commits. * @returns {Promise} */ start(): Promise; /** * Unregisters all publish callbacks (tests, shutdown). * @returns {void} */ stop(): void; /** * Builds the lifecycle callback publishing one server-side mutation. The * published payload (declaration `serialize`), event scope, and sync type * are snapshotted at mutation-callback time, so afterSave hooks assigning * unsaved attributes (or any later drift on the record) cannot change what * gets published vs what was committed. Persisting and broadcasting are * deferred through the model connection's afterCommit hook so they only run * once the mutation's transaction has committed (immediately when no * transaction is open) - rolled-back mutations never publish. Post-commit * publish failures are reported without rethrowing into the driver's * afterCommit chain (see reportAfterCommitError). * @param {{operation: "create" | "update" | "destroy", resourceConfig: import("./sync-publisher-types.js").SyncPublisherResourceConfig}} args - Operation and resource config. * @returns {(record: ReturnType) => Promise} Lifecycle callback. */ publishedMutationCallback({ operation, resourceConfig }: { operation: "create" | "update" | "destroy"; resourceConfig: import("./sync-publisher-types.js").SyncPublisherResourceConfig; }): (record: ReturnType) => Promise; /** * Resolves the scope-partition values for one published mutation from the * resource's derived scope plan: each entry reads its record attribute (or * the record's own id for scope-root models, or the deprecated resolver * function). The values are persisted onto the sync row's partition columns * and broadcast as the framework sync channel's scoping params. * @param {{record: ReturnType, resourceConfig: import("./sync-publisher-types.js").SyncPublisherResourceConfig}} args - Mutated record and resource config. * @returns {Promise<{columns: Record, params: Record}>} Scope values keyed by sync-row column and by scope attribute. */ publishedScopeValues({ record, resourceConfig }: { record: ReturnType; resourceConfig: import("./sync-publisher-types.js").SyncPublisherResourceConfig; }): Promise<{ columns: Record; params: Record; }>; /** * Resolves and validates one computed scope-attributes declaration. The * resolver runs once per published mutation with the exact connection that * owns that mutation; every declared scope value is then reused for row * persistence and broadcast routing so those two identities cannot drift. * @param {{record: ReturnType, resourceConfig: import("./sync-publisher-types.js").SyncPublisherResourceConfig}} args - Record and resource configuration. * @returns {Promise>} Complete computed scope values. */ resolveComputedScopeAttributes({ record, resourceConfig }: { record: ReturnType; resourceConfig: import("./sync-publisher-types.js").SyncPublisherResourceConfig; }): Promise>; /** * Builds the framework sync channel entry for one published change: the * snapshotted payload plus the persisted sync row's public exact-row metadata * (id, server sequence, updated-at, and declared scope-partition attributes). * Uses the sync model's generated typed accessors and follows the * change-feed serializer's public field convention. * @param {{data: Record>, resourceConfig: import("./sync-publisher-types.js").SyncPublisherResourceConfig, resourceId: string, syncRow: ReturnType, syncType: string}} args - Publish args. * @returns {Record>} Broadcast sync entry. */ publishedSyncEntry({ data, resourceConfig, resourceId, syncRow, syncType }: { data: Record>; resourceConfig: import("./sync-publisher-types.js").SyncPublisherResourceConfig; resourceId: string; syncRow: ReturnType; syncType: string; }): Record>; /** * Returns the broadcaster delivering declared broadcasts: the injected one, * or the configuration's channel broadcast awaited through the pending * broadcast queue. * @returns {NonNullable} Broadcast deliverer. */ broadcaster(): NonNullable; /** * Reports a post-commit publish failure. The transaction has already * committed when afterCommit callbacks run, so rethrowing here would poison * the driver's awaited afterCommit chain (breaking unrelated callbacks) - * instead the failure goes to the configured onError hook, or is emitted on * the configuration's framework-error/all-error channels (so production bug * reporting via `configuration.getErrorEvents()` sees a broken publish * path) and logged loudly through the publisher's logger when none is * configured. * @param {Error} error - Post-commit publish failure. * @returns {Promise} */ reportAfterCommitError(error: Error): Promise; /** * Returns the lazily built publisher logger. * @returns {Logger} Publisher logger. */ logger(): Logger; } //# sourceMappingURL=sync-publisher.d.ts.map