import { MusterContext } from "../domain/ports/common.mjs"; import { SubjectBridge } from "../domain/ports/subject.bridge.mjs"; import { ScheduleBridge } from "../domain/ports/schedule.bridge.mjs"; import { AdmissionBridge } from "../domain/ports/admission.bridge.mjs"; import { OutcomePolicy } from "../domain/ports/outcome.policy.mjs"; import { MusterModels } from "../models/create-models.mjs"; import { AttendanceRecordRepository } from "../repositories/attendance-record.repository.mjs"; import { AttendanceSessionRepository } from "../repositories/attendance-session.repository.mjs"; import { AttendanceCorrectionRepository } from "../repositories/attendance-correction.repository.mjs"; import { MultiTenantOptions, PluginType } from "@classytic/mongokit"; import { ResolvedTenantConfig, TenantConfig } from "@classytic/repo-core/tenant"; import { Connection } from "mongoose"; import { EventTransport } from "@classytic/primitives/events"; import { OutboxStore } from "@classytic/primitives/outbox"; //#region src/engine/engine-types.d.ts /** * Tenant configuration — the canonical `TenantConfig` from * `@classytic/repo-core/tenant` (resolved via `resolveTenantConfig`, P11) * plus mongokit's plugin-level runtime knobs (`skipWhen`, `resolveContext`, * `allowDataInjection`, `onMismatch`, `skipOperations`) forwarded to * `multiTenantPlugin` unchanged. Mongokit 3.16 defaults are fail-closed: * `onMismatch: 'throw'`, `allowDataInjection: false` — opt back in * deliberately, per call site, when the trust model justifies it. */ type MusterTenantOptions = TenantConfig & Pick; interface MusterBridges { /** Host-defined resolver for polymorphic subject refs. */ subject?: SubjectBridge | undefined; /** Optional scheduling system — shifts, class timetable, appointments. */ schedule?: ScheduleBridge | undefined; /** * Server-side house rules per scope. Wire this and admission stops being * something the caller opts into — see {@link AdmissionBridge}. */ admission?: AdmissionBridge | undefined; } /** * Policy hooks — let hosts inject per-modality behavior without forking * the engine. A single muster instance can serve corporate HR, classroom, * gym, and appointment use-cases simultaneously by reading `sessionKind` * inside the policy functions. */ interface MusterPolicies { /** Host-supplied outcome resolver — called at checkout. */ outcome?: OutcomePolicy | undefined; } interface MusterLogger { error(message: string, ...args: unknown[]): void; } /** * DESCRIBE-time shape — everything that determines the persistence SHAPE or * is immutable, inspectable config (STANDARDIZATION-PLAN §6.2). Nothing here * opens a socket, reads a secret, or holds a live collaborator: safe to build * at import / module-composition time. */ interface MusterShape { /** * Tenant scoping. `false` disables it (single-tenant deployment); * omitted/`true` uses the repo-core defaults (`organizationId`, * `fieldType: 'objectId'`, `ref: 'organization'`, required). Pass an * object to override fields or forward plugin knobs (see * {@link MusterTenantOptions}). */ tenant?: MusterTenantOptions | boolean | undefined; idPrefixes?: { /** Default: 'ATT' → ATT-2026-0001. */ session?: string | undefined; /** Default: 'COR' → COR-2026-0001. */ correction?: string | undefined; } | undefined; idPartition?: 'yearly' | 'monthly' | 'daily' | undefined; /** * IANA zone (`'Asia/Dhaka'`, `'America/New_York'`) the session * `businessDate` (roster day) is derived in — via * `@classytic/primitives/timezone.civilDateOf`, so a night-shift check-in * at 23:30 local lands on THAT local day, DST-exact. Validated at describe * time (invalid zone throws — pure, no I/O). Default `'UTC'`. */ timezone?: string | undefined; /** * `punch()` double-tap window in ms (default 60 000). A second punch this * close to the session start — or to a just-closed session's end — is * finger bounce on the device, not a sub-minute shift; it resolves as * `debounced` with no write. */ punchDebounceMs?: number | undefined; /** Forwarded to Mongoose `autoIndex` per model. Default `true`. */ autoIndex?: boolean | undefined; /** * Optional prefix prepended to every physical collection this package * creates (see PACKAGE_RULES.md §20.1). Unset → default names * (`attendance_sessions`, `attendance_records`, `attendance_corrections`). * Model names and `ref:` populate are unaffected. */ collectionPrefix?: string | undefined; /** * When true, existing Mongoose models with muster's names are re-registered * on the bind connection before use. Default `false` — a collision throws * mongokit's `ModelCollisionError`. Set `true` only for hot-reload / test * fixtures. Hosts that need two muster engines should use two Mongoose * connections (`mongoose.createConnection`), not share one. */ forceRecreate?: boolean | undefined; } /** * BIND-time runtime collaborators — the live connection is the first `bind` * arg; these are the rest. Transports, stores, loggers, bridges and policy * ports belong here, not in the shape. */ interface MusterRuntime { bridges?: MusterBridges | undefined; /** Per-modality policy hooks (outcome derivation, …). */ policies?: MusterPolicies | undefined; /** * Additional MongoKit plugins per repo. Stacked AFTER the built-in * plugins (multi-tenant + customId + softDelete where applicable). */ repositoryPlugins?: { session?: PluginType[] | undefined; record?: PluginType[] | undefined; correction?: PluginType[] | undefined; } | undefined; /** Host-provided event transport. Defaults to `InProcessMusterBus`. */ eventTransport?: EventTransport | undefined; /** * Optional host-provided outbox for durable event delivery per * PACKAGE_RULES §5.5. When supplied, muster calls `outbox.save(event, * { session })` alongside `eventTransport.publish(event)`. Host wires * the relay (arc `EventOutbox`, custom Kafka worker, etc.). */ outbox?: OutboxStore | undefined; logger?: MusterLogger | undefined; /** * When `true`, multi-write domain verbs fall back to NON-transactional * execution on standalone Mongo deployments (the host knowingly accepts * the partial-write risk). Default `false`: muster requires a replica * set / sharded cluster for atomicity guarantees on `checkIn`, * `checkOut`, `breakStart`, `breakEnd`, `cancel`, and `correction.approve`. * * Set to `true` only for local-dev convenience on standalone Mongo. * Production deployments MUST leave this `false`. */ allowNonTransactional?: boolean | undefined; } /** * Frozen, defaults-applied configuration exposed as `engine.config`. */ interface ResolvedMusterConfig { connection: Connection; tenant: ResolvedTenantConfig; idPartition: 'yearly' | 'monthly' | 'daily'; timezone: string; punchDebounceMs: number; autoIndex: boolean; collectionPrefix: string | undefined; forceRecreate: boolean; allowNonTransactional: boolean; } interface MusterRepositories { attendanceSession: AttendanceSessionRepository; attendanceRecord: AttendanceRecordRepository; attendanceCorrection: AttendanceCorrectionRepository; } interface MusterEngine { readonly config: Readonly; readonly models: MusterModels; readonly repositories: MusterRepositories; readonly events: EventTransport; readonly outbox: OutboxStore | undefined; readonly bridges: MusterBridges; /** * Run a unit of work that may include multiple muster verbs and/or * cross-package writes inside a single MongoDB transaction. Muster * allocates the event queue, threads it (plus the `ClientSession`) * through the inner `MusterContext`, opens the transaction, and * publishes domain events ONLY after commit. Retries (driver-level * `TransientTransactionError`) reset the queue per attempt. * * Pass the inner `txCtx` to every muster verb call inside the body so * they enroll in this transaction. For cross-package writes, pass * `txCtx.session` to the foreign repository. * * @example * ```ts * await muster.withTransaction(ctx, async (txCtx) => { * const session = await muster.repositories.attendanceSession.checkIn(input, txCtx); * await orderRepo.create(orderData, { session: txCtx.session }); * return session; * }); * ``` */ withTransaction(ctx: MusterContext, body: (txCtx: MusterContext) => Promise): Promise; /** * Build/sync indexes for every muster model — call from a deploy script * (PACKAGE_RULES rule 35). Production boots pass `autoIndex: false` and * run this once per deploy instead of building on `Model.init()`. */ syncIndexes(): Promise; /** * Release resources owned by this engine. Idempotent. The kernel-standard * lifecycle name — only the in-process bus muster CREATED is closed; a * host-supplied transport is left alone (its lifecycle is the host's). */ close(): Promise; } //#endregion export { MusterBridges, MusterEngine, MusterLogger, MusterPolicies, MusterRepositories, MusterRuntime, MusterShape, MusterTenantOptions, ResolvedMusterConfig };