@happyvertical/smrt-timesheets 0.60.4 → 0.60.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attendance.js +2 -2
- package/dist/chunks/{attendance-Cp0t1SuH.js → attendance-C6Fuh51b.js} +3 -3
- package/dist/chunks/{attendance-Cp0t1SuH.js.map → attendance-C6Fuh51b.js.map} +1 -1
- package/dist/chunks/{attendance-BKVUjEh9.js → attendance-ZxVRX667.js} +3 -3
- package/dist/chunks/{attendance-BKVUjEh9.js.map → attendance-ZxVRX667.js.map} +1 -1
- package/dist/chunks/{models-BUrhmIoT.js → models-XfFhUJm8.js} +2 -2
- package/dist/chunks/{models-BUrhmIoT.js.map → models-XfFhUJm8.js.map} +1 -1
- package/dist/chunks/{rollup-DFr6lpas.js → rollup-DlG54TX4.js} +3 -3
- package/dist/chunks/{rollup-DFr6lpas.js.map → rollup-DlG54TX4.js.map} +1 -1
- package/dist/chunks/{service-time-entry-x7yzpAMZ.js → service-time-entry-CxYkofRF.js} +5 -5
- package/dist/chunks/{service-time-entry-x7yzpAMZ.js.map → service-time-entry-CxYkofRF.js.map} +1 -1
- package/dist/chunks/{time-entry-actions-3NaHAHIc.js → time-entry-actions-BfTx6s-b.js} +2 -2
- package/dist/chunks/{time-entry-actions-3NaHAHIc.js.map → time-entry-actions-BfTx6s-b.js.map} +1 -1
- package/dist/index.js +5 -5
- package/dist/manifest.json +1 -1
- package/dist/rollup.js +1 -1
- package/dist/smrt-knowledge.json +3 -3
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models-BUrhmIoT.js","names":[],"sources":["../../src/attendance/write.ts","../../src/attendance/models.ts"],"sourcesContent":["import type { SmrtObject } from '@happyvertical/smrt-core';\n\n// Per-instance capability, deliberately absent from the package export surface:\n// the only code that can lift closed-interval immutability is the correction\n// service, and only for the single save it is performing.\nconst permitted = new WeakSet<SmrtObject>();\n\n/** True while `object` is being saved by `AttendanceCorrectionService`. */\nexport function isCorrectionWrite(object: SmrtObject): boolean {\n return permitted.has(object);\n}\n\n/** Save `object` with closed-interval immutability lifted for this one write. */\nexport async function persistCorrection<T extends SmrtObject>(\n object: T,\n): Promise<T> {\n permitted.add(object);\n try {\n await object.save();\n return object;\n } finally {\n permitted.delete(object);\n }\n}\n\n/** Delete a closed break or segment through the correction capability. */\nexport async function deleteCorrection<T extends SmrtObject>(\n object: T,\n): Promise<void> {\n permitted.add(object);\n try {\n await object.delete();\n } finally {\n permitted.delete(object);\n }\n}\n\n// Same pattern for the auto-close note: the reason a punch was closed by the\n// scheduler is evidence, writable only by `AttendanceService.autoClose`.\nconst autoClosing = new WeakSet<SmrtObject>();\n\n/** True while `AttendanceService.autoClose` is saving `object`. */\nexport function isAutoCloseWrite(object: SmrtObject): boolean {\n return autoClosing.has(object);\n}\n\n/** Save `object` with its auto-close reason writable for this one write. */\nexport async function persistAutoClose<T extends SmrtObject>(\n object: T,\n): Promise<T> {\n autoClosing.add(object);\n try {\n await object.save();\n return object;\n } finally {\n autoClosing.delete(object);\n }\n}\n","import {\n crossPackageRef,\n field,\n foreignKey,\n isEmbeddedDatabase,\n isPostgresDatabase,\n SmrtCollection,\n SmrtObject,\n type SmrtSaveOptions,\n smrt,\n withEmbeddedWriteTransaction,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { parseTimesheetJson } from '../models/json.js';\nimport { isAutoCloseWrite, isCorrectionWrite } from './write.js';\n\n// Punch closes and break writes share one atomic parent boundary, including direct ORM saves.\nasync function saveWithPunchLock<T extends SmrtObject>(\n model: T,\n punchId: string | null | undefined,\n tenant: string,\n save: () => Promise<T>,\n): Promise<T> {\n return withEmbeddedWriteTransaction(\n model.db,\n isEmbeddedDatabase(model.db),\n (db) =>\n model.withDatabase(db, async () => {\n if (punchId && isPostgresDatabase(db))\n await db.query(\n 'SELECT id FROM attendance_punches WHERE id = $1 AND tenant_id = $2 FOR UPDATE',\n punchId,\n tenant,\n );\n return save();\n }),\n true,\n );\n}\n\n/** A tenant-scoped interval of attendance, independent of billable work. */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'attendance_punches',\n indexes: [\n {\n name: 'attendance_punches_open_owner_key',\n columns: ['tenantId', 'profileId', 'openSlot'],\n unique: true,\n },\n { name: 'attendance_punches_source_idx', columns: ['tenantId', 'source'] },\n {\n name: 'attendance_punches_device_idx',\n columns: ['tenantId', 'deviceRefType', 'deviceRefId'],\n },\n ],\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n})\nexport class AttendancePunch extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile') profileId = '';\n startedAt: Date = new Date();\n endedAt: Date | null = null;\n durationSeconds = 0;\n unpaidBreakSeconds = 0;\n reviewRequired = false;\n /** Why the scheduler closed this punch; empty unless `autoClose` did. Write-once. */\n autoCloseReason = '';\n recordedOffline = false;\n /** Optional short consumer-owned capture label (`device`, `web`, `import`, ...); `''` when unknown. */\n source = '';\n /** Qualified type of the device that captured the punch; both device columns or neither. */\n deviceRefType = '';\n deviceRefId = '';\n // Resolve the active table family, including smrt-support's same-named subtype.\n @foreignKey('ServiceTimeEntry', { nullable: true }) serviceTimeEntryId:\n | string\n | null = null;\n /** Unique while open; closed rows retain independent non-null slots on every dialect. */\n @field({ required: true }) openSlot = '';\n\n override async save(options?: SmrtSaveOptions): Promise<this> {\n return saveWithPunchLock(this, this.id, this.tenantId, () =>\n this.saveConsistent(options),\n );\n }\n\n private async saveConsistent(options?: SmrtSaveOptions): Promise<this> {\n if (!this.tenantId || !this.profileId)\n throw new Error('Attendance requires tenantId and profileId.');\n if (\n !Number.isFinite(this.startedAt?.getTime()) ||\n (this.endedAt &&\n (!Number.isFinite(this.endedAt.getTime()) ||\n this.endedAt < this.startedAt))\n )\n throw new Error('Invalid attendance interval.');\n if (\n ![this.durationSeconds, this.unpaidBreakSeconds].every(\n (value) => Number.isSafeInteger(value) && value >= 0,\n )\n )\n throw new Error(\n 'Attendance durations must be nonnegative safe integer seconds.',\n );\n // Rows that predate the additive columns may load NULL for them.\n this.source ??= '';\n this.deviceRefType ??= '';\n this.deviceRefId ??= '';\n this.autoCloseReason ??= '';\n if (\n typeof this.source !== 'string' ||\n this.source.length > 64 ||\n this.source !== this.source.trim() ||\n typeof this.deviceRefType !== 'string' ||\n this.deviceRefType.length > 64 ||\n this.deviceRefType !== this.deviceRefType.trim() ||\n typeof this.deviceRefId !== 'string' ||\n this.deviceRefId.length > 64 ||\n this.deviceRefId !== this.deviceRefId.trim() ||\n !!this.deviceRefType !== !!this.deviceRefId\n )\n throw new Error('Invalid attendance punch source or device.');\n const prior = this.id\n ? await this.db.get(this.tableName, { id: this.id })\n : null;\n const corrected = isCorrectionWrite(this);\n if (\n typeof this.autoCloseReason !== 'string' ||\n this.autoCloseReason.length > 200\n )\n throw new Error(\n 'autoCloseReason must be a string of at most 200 characters.',\n );\n if (\n this.autoCloseReason !== ((prior?.auto_close_reason as string) ?? '') &&\n (!isAutoCloseWrite(this) || (prior && prior.auto_close_reason))\n )\n throw new Error('autoCloseReason is written only by autoClose, once.');\n if (\n prior &&\n (prior.tenant_id !== this.tenantId ||\n prior.profile_id !== this.profileId ||\n (!corrected &&\n (new Date(prior.started_at as string).getTime() !==\n this.startedAt.getTime() ||\n (prior.source ?? '') !== this.source ||\n (prior.device_ref_type ?? '') !== this.deviceRefType ||\n (prior.device_ref_id ?? '') !== this.deviceRefId)))\n )\n throw new Error('Attendance ownership and start are immutable.');\n if (\n !corrected &&\n prior?.ended_at &&\n (new Date(prior.ended_at as string).getTime() !==\n this.endedAt?.getTime() ||\n Number(prior.duration_seconds) !== this.durationSeconds ||\n Number(prior.unpaid_break_seconds) !== this.unpaidBreakSeconds)\n )\n throw new Error('Closed attendance evidence is immutable.');\n if (this.id && this.endedAt) {\n const segments = await this.db.list('attendance_segments', {\n tenant_id: this.tenantId,\n punch_id: this.id,\n });\n if (\n segments.some(\n (item) =>\n !item.ended_at ||\n new Date(item.started_at as string).getTime() <\n this.startedAt.getTime() ||\n new Date(item.ended_at as string).getTime() >\n this.endedAt!.getTime(),\n )\n )\n throw new Error('Work segment must be inside its tenant-owned punch.');\n const breaks = await this.db.list('attendance_breaks', {\n tenant_id: this.tenantId,\n punch_id: this.id,\n });\n if (\n breaks.some(\n (item) =>\n !item.ended_at ||\n new Date(item.started_at as string).getTime() <\n this.startedAt.getTime() ||\n new Date(item.ended_at as string).getTime() >\n this.endedAt!.getTime(),\n )\n )\n throw new Error('Break must be inside its tenant-owned punch.');\n }\n this.id ??= crypto.randomUUID();\n this.openSlot = this.endedAt ? JSON.stringify(['closed', this.id]) : 'open';\n return super.save(options);\n }\n}\n\n/** A paid or unpaid break inside one attendance punch. */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'attendance_breaks',\n indexes: [\n {\n name: 'attendance_breaks_open_punch_key',\n columns: ['tenantId', 'punchId', 'openSlot'],\n unique: true,\n },\n ],\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n})\nexport class AttendanceBreak extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @foreignKey(AttendancePunch) punchId = '';\n startedAt: Date = new Date();\n endedAt: Date | null = null;\n paid = false;\n /** Optional short consumer-owned label (`meal`, `rest`, ...); `''` when unspecified. */\n kind = '';\n @field({ required: true }) openSlot = '';\n\n override async save(options?: SmrtSaveOptions): Promise<this> {\n return saveWithPunchLock(this, this.punchId, this.tenantId, () =>\n this.saveConsistent(options),\n );\n }\n\n private async saveConsistent(options?: SmrtSaveOptions): Promise<this> {\n this.kind ??= '';\n if (\n !this.tenantId ||\n !this.punchId ||\n typeof this.paid !== 'boolean' ||\n typeof this.kind !== 'string' ||\n this.kind.length > 64 ||\n this.kind !== this.kind.trim()\n )\n throw new Error('Invalid attendance break.');\n if (\n !Number.isFinite(this.startedAt?.getTime()) ||\n (this.endedAt &&\n (!Number.isFinite(this.endedAt.getTime()) ||\n this.endedAt < this.startedAt))\n )\n throw new Error('Invalid attendance break interval.');\n const parent = await this.db.get('attendance_punches', {\n id: this.punchId,\n tenant_id: this.tenantId,\n });\n if (\n !parent ||\n this.startedAt.getTime() <\n new Date(parent.started_at as string).getTime() ||\n (parent.ended_at &&\n (!this.endedAt ||\n this.endedAt.getTime() >\n new Date(parent.ended_at as string).getTime()))\n )\n throw new Error('Break must be inside its tenant-owned punch.');\n const prior = this.id\n ? await this.db.get(this.tableName, { id: this.id })\n : null;\n const corrected = isCorrectionWrite(this);\n if (!prior && parent.ended_at && !corrected)\n throw new Error(\n 'A closed punch changes only through AttendanceCorrectionService.',\n );\n if (\n prior &&\n (prior.tenant_id !== this.tenantId ||\n prior.punch_id !== this.punchId ||\n (!corrected &&\n (new Date(prior.started_at as string).getTime() !==\n this.startedAt.getTime() ||\n Boolean(prior.paid) !== this.paid ||\n (prior.kind ?? '') !== this.kind)))\n )\n throw new Error('Break ownership, start and pay policy are immutable.');\n if (\n !corrected &&\n prior?.ended_at &&\n new Date(prior.ended_at as string).getTime() !== this.endedAt?.getTime()\n )\n throw new Error('Closed breaks are immutable.');\n const brStart = this.startedAt.getTime();\n const brEnd = this.endedAt?.getTime() ?? Number.POSITIVE_INFINITY;\n if (\n (\n await this.db.list('attendance_segments', {\n tenant_id: this.tenantId,\n punch_id: this.punchId,\n })\n ).some(\n (row) =>\n brStart <\n (row.ended_at\n ? new Date(row.ended_at as string).getTime()\n : Number.POSITIVE_INFINITY) &&\n brEnd > new Date(row.started_at as string).getTime(),\n )\n )\n throw new Error('Break must not overlap a work segment.');\n this.id ??= crypto.randomUUID();\n this.openSlot = this.endedAt ? JSON.stringify(['closed', this.id]) : 'open';\n return super.save(options);\n }\n\n override async delete(): Promise<void> {\n // Judge the persisted row, not the mutable instance, and do it under the\n // parent punch lock so a concurrent punchOut cannot close the row between\n // the check and the delete.\n await saveWithPunchLock(this, this.punchId, this.tenantId, async () => {\n const prior = this.id\n ? await this.db.get(this.tableName, { id: this.id })\n : null;\n if ((this.endedAt || prior?.ended_at) && !isCorrectionWrite(this))\n throw new Error(\n 'Closed breaks are immutable; remove one through AttendanceCorrectionService.',\n );\n await super.delete();\n return this;\n });\n }\n}\n\nconst SEGMENT_CLOSE_REASONS = ['stop', 'switch', 'break', 'punchOut'] as const;\n/** Why a work segment ended; `''` while it is open. */\nexport type AttendanceSegmentCloseReason =\n | (typeof SEGMENT_CLOSE_REASONS)[number]\n | '';\n\n/**\n * A running unit of work inside one attendance punch: a work reference plus an\n * optional second (task) reference as queryable columns, so job costing can\n * index and join them. At most one segment is open per punch (`openSlot`). A\n * segment never spans a break; `startBreak` and `punchOut` close it. Segments\n * are attendance evidence and are not themselves service time entries.\n */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'attendance_segments',\n indexes: [\n {\n name: 'attendance_segments_open_punch_key',\n columns: ['tenantId', 'punchId', 'openSlot'],\n unique: true,\n },\n {\n name: 'attendance_segments_work_idx',\n columns: ['tenantId', 'workRefType', 'workRefId'],\n },\n {\n name: 'attendance_segments_task_idx',\n columns: ['tenantId', 'taskRefType', 'taskRefId'],\n },\n {\n name: 'attendance_segments_profile_idx',\n columns: ['tenantId', 'profileId', 'startedAt'],\n },\n ],\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n})\nexport class AttendanceSegment extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @foreignKey(AttendancePunch) punchId = '';\n /**\n * The person whose time this is: its punch's `profileId`, set by the\n * attendance and correction services on creation and immutable. Rows\n * written before the column existed may read `null` (see the README backfill).\n */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile') profileId = '';\n /** Qualified class name of the work context (for example a work order). */\n workRefType = '';\n workRefId = '';\n /** Optional second reference (for example an operation); both or neither. */\n taskRefType = '';\n taskRefId = '';\n /** Optional short consumer lane label. */\n kind = '';\n description = '';\n startedAt: Date = new Date();\n endedAt: Date | null = null;\n /** Elapsed whole seconds, set when the segment closes. */\n durationSeconds = 0;\n /** `stop`, `switch`, `break` or `punchOut` once closed; `''` while open. */\n closedReason: AttendanceSegmentCloseReason = '';\n /** Set when a replay time policy asked for review of this segment's boundary. */\n reviewRequired = false;\n @field({ required: true }) openSlot = '';\n\n override async save(options?: SmrtSaveOptions): Promise<this> {\n return saveWithPunchLock(this, this.punchId, this.tenantId, () =>\n this.saveConsistent(options),\n );\n }\n\n private async saveConsistent(options?: SmrtSaveOptions): Promise<this> {\n if (\n !this.tenantId ||\n !this.punchId ||\n !this.workRefType ||\n !this.workRefId ||\n !!this.taskRefType !== !!this.taskRefId\n )\n throw new Error('Invalid attendance work segment.');\n if (\n !Number.isFinite(this.startedAt?.getTime()) ||\n (this.endedAt &&\n (!Number.isFinite(this.endedAt.getTime()) ||\n this.endedAt < this.startedAt))\n )\n throw new Error('Invalid attendance segment interval.');\n if (!this.endedAt !== !this.closedReason)\n throw new Error(\n 'A segment has a close reason exactly when it is closed.',\n );\n if (this.closedReason && !SEGMENT_CLOSE_REASONS.includes(this.closedReason))\n throw new Error('Invalid segment close reason.');\n const parent = await this.db.get('attendance_punches', {\n id: this.punchId,\n tenant_id: this.tenantId,\n });\n if (\n !parent ||\n this.startedAt.getTime() <\n new Date(parent.started_at as string).getTime() ||\n (parent.ended_at &&\n (!this.endedAt ||\n this.endedAt.getTime() >\n new Date(parent.ended_at as string).getTime()))\n )\n throw new Error('Segment must be inside its tenant-owned punch.');\n const prior = this.id\n ? await this.db.get(this.tableName, { id: this.id })\n : null;\n const corrected = isCorrectionWrite(this);\n if (!prior && parent.ended_at && !corrected)\n throw new Error(\n 'A closed punch changes only through AttendanceCorrectionService.',\n );\n if (!prior && this.profileId !== (parent.profile_id as string))\n throw new Error(\"Segment profileId must be its punch's profileId.\");\n if (\n prior &&\n (prior.tenant_id !== this.tenantId ||\n prior.punch_id !== this.punchId ||\n ((prior.profile_id as string | null) ?? '') !==\n (this.profileId ?? '') ||\n (!corrected &&\n (prior.work_ref_type !== this.workRefType ||\n prior.work_ref_id !== this.workRefId ||\n prior.task_ref_type !== this.taskRefType ||\n prior.task_ref_id !== this.taskRefId ||\n prior.kind !== this.kind ||\n prior.description !== this.description ||\n new Date(prior.started_at as string).getTime() !==\n this.startedAt.getTime())))\n )\n throw new Error('Segment ownership, references and start are immutable.');\n if (\n !corrected &&\n prior?.ended_at &&\n (new Date(prior.ended_at as string).getTime() !==\n this.endedAt?.getTime() ||\n prior.closed_reason !== this.closedReason)\n )\n throw new Error('Closed segments are immutable.');\n const start = this.startedAt.getTime();\n const end = this.endedAt?.getTime() ?? Number.POSITIVE_INFINITY;\n const overlaps = (row: Record<string, unknown>) =>\n start <\n (row.ended_at\n ? new Date(row.ended_at as string).getTime()\n : Number.POSITIVE_INFINITY) &&\n end > new Date(row.started_at as string).getTime();\n const where = { tenant_id: this.tenantId, punch_id: this.punchId };\n if ((await this.db.list('attendance_breaks', where)).some(overlaps))\n throw new Error('Segment must not overlap a break.');\n if (\n (await this.db.list(this.tableName, where)).some(\n (row) => row.id !== this.id && overlaps(row),\n )\n )\n throw new Error('Segments must not overlap.');\n this.durationSeconds = this.endedAt ? Math.round((end - start) / 1000) : 0;\n this.id ??= crypto.randomUUID();\n this.openSlot = this.endedAt ? JSON.stringify(['closed', this.id]) : 'open';\n return super.save(options);\n }\n\n override async delete(): Promise<void> {\n // Judge the persisted row, not the mutable instance, and do it under the\n // parent punch lock so a concurrent punchOut cannot close the row between\n // the check and the delete.\n await saveWithPunchLock(this, this.punchId, this.tenantId, async () => {\n const prior = this.id\n ? await this.db.get(this.tableName, { id: this.id })\n : null;\n if ((this.endedAt || prior?.ended_at) && !isCorrectionWrite(this))\n throw new Error(\n 'Closed segments are immutable; remove one through AttendanceCorrectionService.',\n );\n await super.delete();\n return this;\n });\n }\n}\n\n/** Durable success or conflict; conflicts do not mutate attendance. */\nexport interface AttendanceReplayOutcome {\n clientId: string;\n punchId?: string;\n /** Work segment a replayed work action started, switched to, stopped or resumed. */\n segmentId?: string;\n /** Effective time stored when a `resolveAt` policy changed it (ISO 8601). */\n resolvedAt?: string;\n /** Refusal message (with `code`); present exactly when the tap was refused. */\n error?: string;\n /** Typed refusal reason; see `AttendanceRefusalCode`. */\n code?: string;\n}\n\nfunction validReplayOutcome(value: unknown): value is AttendanceReplayOutcome {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const result = value as Record<string, unknown>;\n return (\n typeof result.clientId === 'string' &&\n result.clientId.length > 0 &&\n (result.code === undefined ||\n (typeof result.code === 'string' &&\n result.code.length > 0 &&\n result.error !== undefined)) &&\n (result.segmentId === undefined || typeof result.segmentId === 'string') &&\n (result.resolvedAt === undefined ||\n typeof result.resolvedAt === 'string') &&\n ((typeof result.punchId === 'string' &&\n result.punchId.length > 0 &&\n result.error === undefined) ||\n (typeof result.error === 'string' &&\n result.error.length > 0 &&\n result.punchId === undefined))\n );\n}\n\n/** Durable offline-tap outcome; never exposed through generated operations. */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'attendance_replays',\n sensitive: true,\n api: { include: [] },\n cli: { include: [] },\n mcp: { include: [] },\n})\nexport class AttendanceReplay extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @field({ required: true, unique: true }) replayKey = '';\n @field({ type: 'text' }) content = '';\n @field({ type: 'text' }) outcome = '{}';\n\n /** Read a durable receipt, failing closed rather than re-executing a corrupt tap. */\n getOutcome(): AttendanceReplayOutcome {\n const value = parseTimesheetJson<unknown>(this.outcome, null);\n if (!validReplayOutcome(value))\n throw new Error('Invalid stored attendance replay outcome.');\n return value;\n }\n\n /** Store the JSON representation of a validated success or refusal. */\n setOutcome(value: AttendanceReplayOutcome): void {\n if (!validReplayOutcome(value))\n throw new Error('Invalid attendance replay outcome.');\n this.outcome = JSON.stringify(value);\n }\n\n override async save(options?: SmrtSaveOptions): Promise<this> {\n this.getOutcome();\n return super.save(options);\n }\n}\n\n/** Punches for attendance and period rollup. */\nexport class AttendancePunchCollection extends SmrtCollection<AttendancePunch> {\n static readonly _itemClass = AttendancePunch;\n}\n/** Break intervals for net duration and period clipping. */\nexport class AttendanceBreakCollection extends SmrtCollection<AttendanceBreak> {\n static readonly _itemClass = AttendanceBreak;\n}\n/** Work segments for job costing and attendance review. */\nexport class AttendanceSegmentCollection extends SmrtCollection<AttendanceSegment> {\n static readonly _itemClass = AttendanceSegment;\n}\n/** Internal durable replay ledger. */\nexport class AttendanceReplayCollection extends SmrtCollection<AttendanceReplay> {\n static readonly _itemClass = AttendanceReplay;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"models-XfFhUJm8.js","names":[],"sources":["../../src/attendance/write.ts","../../src/attendance/models.ts"],"sourcesContent":["import type { SmrtObject } from '@happyvertical/smrt-core';\n\n// Per-instance capability, deliberately absent from the package export surface:\n// the only code that can lift closed-interval immutability is the correction\n// service, and only for the single save it is performing.\nconst permitted = new WeakSet<SmrtObject>();\n\n/** True while `object` is being saved by `AttendanceCorrectionService`. */\nexport function isCorrectionWrite(object: SmrtObject): boolean {\n return permitted.has(object);\n}\n\n/** Save `object` with closed-interval immutability lifted for this one write. */\nexport async function persistCorrection<T extends SmrtObject>(\n object: T,\n): Promise<T> {\n permitted.add(object);\n try {\n await object.save();\n return object;\n } finally {\n permitted.delete(object);\n }\n}\n\n/** Delete a closed break or segment through the correction capability. */\nexport async function deleteCorrection<T extends SmrtObject>(\n object: T,\n): Promise<void> {\n permitted.add(object);\n try {\n await object.delete();\n } finally {\n permitted.delete(object);\n }\n}\n\n// Same pattern for the auto-close note: the reason a punch was closed by the\n// scheduler is evidence, writable only by `AttendanceService.autoClose`.\nconst autoClosing = new WeakSet<SmrtObject>();\n\n/** True while `AttendanceService.autoClose` is saving `object`. */\nexport function isAutoCloseWrite(object: SmrtObject): boolean {\n return autoClosing.has(object);\n}\n\n/** Save `object` with its auto-close reason writable for this one write. */\nexport async function persistAutoClose<T extends SmrtObject>(\n object: T,\n): Promise<T> {\n autoClosing.add(object);\n try {\n await object.save();\n return object;\n } finally {\n autoClosing.delete(object);\n }\n}\n","import {\n crossPackageRef,\n field,\n foreignKey,\n isEmbeddedDatabase,\n isPostgresDatabase,\n SmrtCollection,\n SmrtObject,\n type SmrtSaveOptions,\n smrt,\n withEmbeddedWriteTransaction,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { parseTimesheetJson } from '../models/json.js';\nimport { isAutoCloseWrite, isCorrectionWrite } from './write.js';\n\n// Punch closes and break writes share one atomic parent boundary, including direct ORM saves.\nasync function saveWithPunchLock<T extends SmrtObject>(\n model: T,\n punchId: string | null | undefined,\n tenant: string,\n save: () => Promise<T>,\n): Promise<T> {\n return withEmbeddedWriteTransaction(\n model.db,\n isEmbeddedDatabase(model.db),\n (db) =>\n model.withDatabase(db, async () => {\n if (punchId && isPostgresDatabase(db))\n await db.query(\n 'SELECT id FROM attendance_punches WHERE id = $1 AND tenant_id = $2 FOR UPDATE',\n punchId,\n tenant,\n );\n return save();\n }),\n true,\n );\n}\n\n/** A tenant-scoped interval of attendance, independent of billable work. */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'attendance_punches',\n indexes: [\n {\n name: 'attendance_punches_open_owner_key',\n columns: ['tenantId', 'profileId', 'openSlot'],\n unique: true,\n },\n { name: 'attendance_punches_source_idx', columns: ['tenantId', 'source'] },\n {\n name: 'attendance_punches_device_idx',\n columns: ['tenantId', 'deviceRefType', 'deviceRefId'],\n },\n ],\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n})\nexport class AttendancePunch extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile') profileId = '';\n startedAt: Date = new Date();\n endedAt: Date | null = null;\n durationSeconds = 0;\n unpaidBreakSeconds = 0;\n reviewRequired = false;\n /** Why the scheduler closed this punch; empty unless `autoClose` did. Write-once. */\n autoCloseReason = '';\n recordedOffline = false;\n /** Optional short consumer-owned capture label (`device`, `web`, `import`, ...); `''` when unknown. */\n source = '';\n /** Qualified type of the device that captured the punch; both device columns or neither. */\n deviceRefType = '';\n deviceRefId = '';\n // Resolve the active table family, including smrt-support's same-named subtype.\n @foreignKey('ServiceTimeEntry', { nullable: true }) serviceTimeEntryId:\n | string\n | null = null;\n /** Unique while open; closed rows retain independent non-null slots on every dialect. */\n @field({ required: true }) openSlot = '';\n\n override async save(options?: SmrtSaveOptions): Promise<this> {\n return saveWithPunchLock(this, this.id, this.tenantId, () =>\n this.saveConsistent(options),\n );\n }\n\n private async saveConsistent(options?: SmrtSaveOptions): Promise<this> {\n if (!this.tenantId || !this.profileId)\n throw new Error('Attendance requires tenantId and profileId.');\n if (\n !Number.isFinite(this.startedAt?.getTime()) ||\n (this.endedAt &&\n (!Number.isFinite(this.endedAt.getTime()) ||\n this.endedAt < this.startedAt))\n )\n throw new Error('Invalid attendance interval.');\n if (\n ![this.durationSeconds, this.unpaidBreakSeconds].every(\n (value) => Number.isSafeInteger(value) && value >= 0,\n )\n )\n throw new Error(\n 'Attendance durations must be nonnegative safe integer seconds.',\n );\n // Rows that predate the additive columns may load NULL for them.\n this.source ??= '';\n this.deviceRefType ??= '';\n this.deviceRefId ??= '';\n this.autoCloseReason ??= '';\n if (\n typeof this.source !== 'string' ||\n this.source.length > 64 ||\n this.source !== this.source.trim() ||\n typeof this.deviceRefType !== 'string' ||\n this.deviceRefType.length > 64 ||\n this.deviceRefType !== this.deviceRefType.trim() ||\n typeof this.deviceRefId !== 'string' ||\n this.deviceRefId.length > 64 ||\n this.deviceRefId !== this.deviceRefId.trim() ||\n !!this.deviceRefType !== !!this.deviceRefId\n )\n throw new Error('Invalid attendance punch source or device.');\n const prior = this.id\n ? await this.db.get(this.tableName, { id: this.id })\n : null;\n const corrected = isCorrectionWrite(this);\n if (\n typeof this.autoCloseReason !== 'string' ||\n this.autoCloseReason.length > 200\n )\n throw new Error(\n 'autoCloseReason must be a string of at most 200 characters.',\n );\n if (\n this.autoCloseReason !== ((prior?.auto_close_reason as string) ?? '') &&\n (!isAutoCloseWrite(this) || (prior && prior.auto_close_reason))\n )\n throw new Error('autoCloseReason is written only by autoClose, once.');\n if (\n prior &&\n (prior.tenant_id !== this.tenantId ||\n prior.profile_id !== this.profileId ||\n (!corrected &&\n (new Date(prior.started_at as string).getTime() !==\n this.startedAt.getTime() ||\n (prior.source ?? '') !== this.source ||\n (prior.device_ref_type ?? '') !== this.deviceRefType ||\n (prior.device_ref_id ?? '') !== this.deviceRefId)))\n )\n throw new Error('Attendance ownership and start are immutable.');\n if (\n !corrected &&\n prior?.ended_at &&\n (new Date(prior.ended_at as string).getTime() !==\n this.endedAt?.getTime() ||\n Number(prior.duration_seconds) !== this.durationSeconds ||\n Number(prior.unpaid_break_seconds) !== this.unpaidBreakSeconds)\n )\n throw new Error('Closed attendance evidence is immutable.');\n if (this.id && this.endedAt) {\n const segments = await this.db.list('attendance_segments', {\n tenant_id: this.tenantId,\n punch_id: this.id,\n });\n if (\n segments.some(\n (item) =>\n !item.ended_at ||\n new Date(item.started_at as string).getTime() <\n this.startedAt.getTime() ||\n new Date(item.ended_at as string).getTime() >\n this.endedAt!.getTime(),\n )\n )\n throw new Error('Work segment must be inside its tenant-owned punch.');\n const breaks = await this.db.list('attendance_breaks', {\n tenant_id: this.tenantId,\n punch_id: this.id,\n });\n if (\n breaks.some(\n (item) =>\n !item.ended_at ||\n new Date(item.started_at as string).getTime() <\n this.startedAt.getTime() ||\n new Date(item.ended_at as string).getTime() >\n this.endedAt!.getTime(),\n )\n )\n throw new Error('Break must be inside its tenant-owned punch.');\n }\n this.id ??= crypto.randomUUID();\n this.openSlot = this.endedAt ? JSON.stringify(['closed', this.id]) : 'open';\n return super.save(options);\n }\n}\n\n/** A paid or unpaid break inside one attendance punch. */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'attendance_breaks',\n indexes: [\n {\n name: 'attendance_breaks_open_punch_key',\n columns: ['tenantId', 'punchId', 'openSlot'],\n unique: true,\n },\n ],\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n})\nexport class AttendanceBreak extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @foreignKey(AttendancePunch) punchId = '';\n startedAt: Date = new Date();\n endedAt: Date | null = null;\n paid = false;\n /** Optional short consumer-owned label (`meal`, `rest`, ...); `''` when unspecified. */\n kind = '';\n @field({ required: true }) openSlot = '';\n\n override async save(options?: SmrtSaveOptions): Promise<this> {\n return saveWithPunchLock(this, this.punchId, this.tenantId, () =>\n this.saveConsistent(options),\n );\n }\n\n private async saveConsistent(options?: SmrtSaveOptions): Promise<this> {\n this.kind ??= '';\n if (\n !this.tenantId ||\n !this.punchId ||\n typeof this.paid !== 'boolean' ||\n typeof this.kind !== 'string' ||\n this.kind.length > 64 ||\n this.kind !== this.kind.trim()\n )\n throw new Error('Invalid attendance break.');\n if (\n !Number.isFinite(this.startedAt?.getTime()) ||\n (this.endedAt &&\n (!Number.isFinite(this.endedAt.getTime()) ||\n this.endedAt < this.startedAt))\n )\n throw new Error('Invalid attendance break interval.');\n const parent = await this.db.get('attendance_punches', {\n id: this.punchId,\n tenant_id: this.tenantId,\n });\n if (\n !parent ||\n this.startedAt.getTime() <\n new Date(parent.started_at as string).getTime() ||\n (parent.ended_at &&\n (!this.endedAt ||\n this.endedAt.getTime() >\n new Date(parent.ended_at as string).getTime()))\n )\n throw new Error('Break must be inside its tenant-owned punch.');\n const prior = this.id\n ? await this.db.get(this.tableName, { id: this.id })\n : null;\n const corrected = isCorrectionWrite(this);\n if (!prior && parent.ended_at && !corrected)\n throw new Error(\n 'A closed punch changes only through AttendanceCorrectionService.',\n );\n if (\n prior &&\n (prior.tenant_id !== this.tenantId ||\n prior.punch_id !== this.punchId ||\n (!corrected &&\n (new Date(prior.started_at as string).getTime() !==\n this.startedAt.getTime() ||\n Boolean(prior.paid) !== this.paid ||\n (prior.kind ?? '') !== this.kind)))\n )\n throw new Error('Break ownership, start and pay policy are immutable.');\n if (\n !corrected &&\n prior?.ended_at &&\n new Date(prior.ended_at as string).getTime() !== this.endedAt?.getTime()\n )\n throw new Error('Closed breaks are immutable.');\n const brStart = this.startedAt.getTime();\n const brEnd = this.endedAt?.getTime() ?? Number.POSITIVE_INFINITY;\n if (\n (\n await this.db.list('attendance_segments', {\n tenant_id: this.tenantId,\n punch_id: this.punchId,\n })\n ).some(\n (row) =>\n brStart <\n (row.ended_at\n ? new Date(row.ended_at as string).getTime()\n : Number.POSITIVE_INFINITY) &&\n brEnd > new Date(row.started_at as string).getTime(),\n )\n )\n throw new Error('Break must not overlap a work segment.');\n this.id ??= crypto.randomUUID();\n this.openSlot = this.endedAt ? JSON.stringify(['closed', this.id]) : 'open';\n return super.save(options);\n }\n\n override async delete(): Promise<void> {\n // Judge the persisted row, not the mutable instance, and do it under the\n // parent punch lock so a concurrent punchOut cannot close the row between\n // the check and the delete.\n await saveWithPunchLock(this, this.punchId, this.tenantId, async () => {\n const prior = this.id\n ? await this.db.get(this.tableName, { id: this.id })\n : null;\n if ((this.endedAt || prior?.ended_at) && !isCorrectionWrite(this))\n throw new Error(\n 'Closed breaks are immutable; remove one through AttendanceCorrectionService.',\n );\n await super.delete();\n return this;\n });\n }\n}\n\nconst SEGMENT_CLOSE_REASONS = ['stop', 'switch', 'break', 'punchOut'] as const;\n/** Why a work segment ended; `''` while it is open. */\nexport type AttendanceSegmentCloseReason =\n | (typeof SEGMENT_CLOSE_REASONS)[number]\n | '';\n\n/**\n * A running unit of work inside one attendance punch: a work reference plus an\n * optional second (task) reference as queryable columns, so job costing can\n * index and join them. At most one segment is open per punch (`openSlot`). A\n * segment never spans a break; `startBreak` and `punchOut` close it. Segments\n * are attendance evidence and are not themselves service time entries.\n */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'attendance_segments',\n indexes: [\n {\n name: 'attendance_segments_open_punch_key',\n columns: ['tenantId', 'punchId', 'openSlot'],\n unique: true,\n },\n {\n name: 'attendance_segments_work_idx',\n columns: ['tenantId', 'workRefType', 'workRefId'],\n },\n {\n name: 'attendance_segments_task_idx',\n columns: ['tenantId', 'taskRefType', 'taskRefId'],\n },\n {\n name: 'attendance_segments_profile_idx',\n columns: ['tenantId', 'profileId', 'startedAt'],\n },\n ],\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n})\nexport class AttendanceSegment extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @foreignKey(AttendancePunch) punchId = '';\n /**\n * The person whose time this is: its punch's `profileId`, set by the\n * attendance and correction services on creation and immutable. Rows\n * written before the column existed may read `null` (see the README backfill).\n */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile') profileId = '';\n /** Qualified class name of the work context (for example a work order). */\n workRefType = '';\n workRefId = '';\n /** Optional second reference (for example an operation); both or neither. */\n taskRefType = '';\n taskRefId = '';\n /** Optional short consumer lane label. */\n kind = '';\n description = '';\n startedAt: Date = new Date();\n endedAt: Date | null = null;\n /** Elapsed whole seconds, set when the segment closes. */\n durationSeconds = 0;\n /** `stop`, `switch`, `break` or `punchOut` once closed; `''` while open. */\n closedReason: AttendanceSegmentCloseReason = '';\n /** Set when a replay time policy asked for review of this segment's boundary. */\n reviewRequired = false;\n @field({ required: true }) openSlot = '';\n\n override async save(options?: SmrtSaveOptions): Promise<this> {\n return saveWithPunchLock(this, this.punchId, this.tenantId, () =>\n this.saveConsistent(options),\n );\n }\n\n private async saveConsistent(options?: SmrtSaveOptions): Promise<this> {\n if (\n !this.tenantId ||\n !this.punchId ||\n !this.workRefType ||\n !this.workRefId ||\n !!this.taskRefType !== !!this.taskRefId\n )\n throw new Error('Invalid attendance work segment.');\n if (\n !Number.isFinite(this.startedAt?.getTime()) ||\n (this.endedAt &&\n (!Number.isFinite(this.endedAt.getTime()) ||\n this.endedAt < this.startedAt))\n )\n throw new Error('Invalid attendance segment interval.');\n if (!this.endedAt !== !this.closedReason)\n throw new Error(\n 'A segment has a close reason exactly when it is closed.',\n );\n if (this.closedReason && !SEGMENT_CLOSE_REASONS.includes(this.closedReason))\n throw new Error('Invalid segment close reason.');\n const parent = await this.db.get('attendance_punches', {\n id: this.punchId,\n tenant_id: this.tenantId,\n });\n if (\n !parent ||\n this.startedAt.getTime() <\n new Date(parent.started_at as string).getTime() ||\n (parent.ended_at &&\n (!this.endedAt ||\n this.endedAt.getTime() >\n new Date(parent.ended_at as string).getTime()))\n )\n throw new Error('Segment must be inside its tenant-owned punch.');\n const prior = this.id\n ? await this.db.get(this.tableName, { id: this.id })\n : null;\n const corrected = isCorrectionWrite(this);\n if (!prior && parent.ended_at && !corrected)\n throw new Error(\n 'A closed punch changes only through AttendanceCorrectionService.',\n );\n if (!prior && this.profileId !== (parent.profile_id as string))\n throw new Error(\"Segment profileId must be its punch's profileId.\");\n if (\n prior &&\n (prior.tenant_id !== this.tenantId ||\n prior.punch_id !== this.punchId ||\n ((prior.profile_id as string | null) ?? '') !==\n (this.profileId ?? '') ||\n (!corrected &&\n (prior.work_ref_type !== this.workRefType ||\n prior.work_ref_id !== this.workRefId ||\n prior.task_ref_type !== this.taskRefType ||\n prior.task_ref_id !== this.taskRefId ||\n prior.kind !== this.kind ||\n prior.description !== this.description ||\n new Date(prior.started_at as string).getTime() !==\n this.startedAt.getTime())))\n )\n throw new Error('Segment ownership, references and start are immutable.');\n if (\n !corrected &&\n prior?.ended_at &&\n (new Date(prior.ended_at as string).getTime() !==\n this.endedAt?.getTime() ||\n prior.closed_reason !== this.closedReason)\n )\n throw new Error('Closed segments are immutable.');\n const start = this.startedAt.getTime();\n const end = this.endedAt?.getTime() ?? Number.POSITIVE_INFINITY;\n const overlaps = (row: Record<string, unknown>) =>\n start <\n (row.ended_at\n ? new Date(row.ended_at as string).getTime()\n : Number.POSITIVE_INFINITY) &&\n end > new Date(row.started_at as string).getTime();\n const where = { tenant_id: this.tenantId, punch_id: this.punchId };\n if ((await this.db.list('attendance_breaks', where)).some(overlaps))\n throw new Error('Segment must not overlap a break.');\n if (\n (await this.db.list(this.tableName, where)).some(\n (row) => row.id !== this.id && overlaps(row),\n )\n )\n throw new Error('Segments must not overlap.');\n this.durationSeconds = this.endedAt ? Math.round((end - start) / 1000) : 0;\n this.id ??= crypto.randomUUID();\n this.openSlot = this.endedAt ? JSON.stringify(['closed', this.id]) : 'open';\n return super.save(options);\n }\n\n override async delete(): Promise<void> {\n // Judge the persisted row, not the mutable instance, and do it under the\n // parent punch lock so a concurrent punchOut cannot close the row between\n // the check and the delete.\n await saveWithPunchLock(this, this.punchId, this.tenantId, async () => {\n const prior = this.id\n ? await this.db.get(this.tableName, { id: this.id })\n : null;\n if ((this.endedAt || prior?.ended_at) && !isCorrectionWrite(this))\n throw new Error(\n 'Closed segments are immutable; remove one through AttendanceCorrectionService.',\n );\n await super.delete();\n return this;\n });\n }\n}\n\n/** Durable success or conflict; conflicts do not mutate attendance. */\nexport interface AttendanceReplayOutcome {\n clientId: string;\n punchId?: string;\n /** Work segment a replayed work action started, switched to, stopped or resumed. */\n segmentId?: string;\n /** Effective time stored when a `resolveAt` policy changed it (ISO 8601). */\n resolvedAt?: string;\n /** Refusal message (with `code`); present exactly when the tap was refused. */\n error?: string;\n /** Typed refusal reason; see `AttendanceRefusalCode`. */\n code?: string;\n}\n\nfunction validReplayOutcome(value: unknown): value is AttendanceReplayOutcome {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const result = value as Record<string, unknown>;\n return (\n typeof result.clientId === 'string' &&\n result.clientId.length > 0 &&\n (result.code === undefined ||\n (typeof result.code === 'string' &&\n result.code.length > 0 &&\n result.error !== undefined)) &&\n (result.segmentId === undefined || typeof result.segmentId === 'string') &&\n (result.resolvedAt === undefined ||\n typeof result.resolvedAt === 'string') &&\n ((typeof result.punchId === 'string' &&\n result.punchId.length > 0 &&\n result.error === undefined) ||\n (typeof result.error === 'string' &&\n result.error.length > 0 &&\n result.punchId === undefined))\n );\n}\n\n/** Durable offline-tap outcome; never exposed through generated operations. */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'attendance_replays',\n sensitive: true,\n api: { include: [] },\n cli: { include: [] },\n mcp: { include: [] },\n})\nexport class AttendanceReplay extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @field({ required: true, unique: true }) replayKey = '';\n @field({ type: 'text' }) content = '';\n @field({ type: 'text' }) outcome = '{}';\n\n /** Read a durable receipt, failing closed rather than re-executing a corrupt tap. */\n getOutcome(): AttendanceReplayOutcome {\n const value = parseTimesheetJson<unknown>(this.outcome, null);\n if (!validReplayOutcome(value))\n throw new Error('Invalid stored attendance replay outcome.');\n return value;\n }\n\n /** Store the JSON representation of a validated success or refusal. */\n setOutcome(value: AttendanceReplayOutcome): void {\n if (!validReplayOutcome(value))\n throw new Error('Invalid attendance replay outcome.');\n this.outcome = JSON.stringify(value);\n }\n\n override async save(options?: SmrtSaveOptions): Promise<this> {\n this.getOutcome();\n return super.save(options);\n }\n}\n\n/** Punches for attendance and period rollup. */\nexport class AttendancePunchCollection extends SmrtCollection<AttendancePunch> {\n static readonly _itemClass = AttendancePunch;\n}\n/** Break intervals for net duration and period clipping. */\nexport class AttendanceBreakCollection extends SmrtCollection<AttendanceBreak> {\n static readonly _itemClass = AttendanceBreak;\n}\n/** Work segments for job costing and attendance review. */\nexport class AttendanceSegmentCollection extends SmrtCollection<AttendanceSegment> {\n static readonly _itemClass = AttendanceSegment;\n}\n/** Internal durable replay ledger. */\nexport class AttendanceReplayCollection extends SmrtCollection<AttendanceReplay> {\n static readonly _itemClass = AttendanceReplay;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as ServiceTimeEntryCollection } from "./service-time-entry-
|
|
1
|
+
import { i as ServiceTimeEntryCollection } from "./service-time-entry-CxYkofRF.js";
|
|
2
2
|
import { SmrtCollection, SmrtObject, crossPackageRef, field, foreignKey, isEmbeddedDatabase, isPostgresDatabase, smrt, withEmbeddedWriteTransaction } from "@happyvertical/smrt-core";
|
|
3
3
|
import { TenantScoped, getTenantId, tenantId, withTenant } from "@happyvertical/smrt-tenancy";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
@@ -361,7 +361,7 @@ var PeriodRollupService = class {
|
|
|
361
361
|
let sources = [];
|
|
362
362
|
let linkedEntryIds = /* @__PURE__ */ new Set();
|
|
363
363
|
if (this.options.attendance) {
|
|
364
|
-
const { attendanceSources } = await import("./attendance-
|
|
364
|
+
const { attendanceSources } = await import("./attendance-C6Fuh51b.js");
|
|
365
365
|
({sources, linkedEntryIds} = await attendanceSources(db, this.actor, period, this.options.attribution));
|
|
366
366
|
}
|
|
367
367
|
const evidence = await (await ServiceTimeEntryCollection.create({ db })).list({ where: {
|
|
@@ -645,4 +645,4 @@ var PeriodRollupService = class {
|
|
|
645
645
|
//#endregion
|
|
646
646
|
export { confirmedTimecardGuard as a, TimecardAdjustment as c, TimecardReopeningCollection as d, integer as i, TimecardAdjustmentCollection as l, attributeSource as n, PeriodTimecard as o, instant as r, PeriodTimecardCollection as s, PeriodRollupService as t, TimecardReopening as u };
|
|
647
647
|
|
|
648
|
-
//# sourceMappingURL=rollup-
|
|
648
|
+
//# sourceMappingURL=rollup-DlG54TX4.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rollup-DFr6lpas.js","names":["id"],"sources":["../../src/rollup/write.ts","../../src/rollup/models.ts","../../src/rollup/correction-guard.ts","../../src/rollup/calculation.ts","../../src/rollup/service.ts"],"sourcesContent":["import type { SmrtObject } from '@happyvertical/smrt-core';\n\n// Per-instance capability, deliberately absent from the package export surface.\nconst permitted = new WeakSet<SmrtObject>();\nexport function assertRollupWrite(object: SmrtObject): void {\n if (!permitted.has(object))\n throw new Error(\n 'Timecards and adjustments are writable only through PeriodRollupService.',\n );\n}\nexport async function persistRollup<T extends SmrtObject>(\n object: T,\n): Promise<T> {\n permitted.add(object);\n try {\n await object.save();\n return object;\n } finally {\n permitted.delete(object);\n }\n}\n\n// Second capability: only reopen() may move a confirmed/submitted card back.\nconst reopening = new WeakSet<SmrtObject>();\nexport function isReopenWrite(object: SmrtObject): boolean {\n return reopening.has(object);\n}\nexport async function persistReopen<T extends SmrtObject>(\n object: T,\n): Promise<T> {\n reopening.add(object);\n try {\n return await persistRollup(object);\n } finally {\n reopening.delete(object);\n }\n}\n","import {\n crossPackageRef,\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n type SmrtSaveOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { TimecardSource } from './types.js';\nimport { assertRollupWrite, isReopenWrite } from './write.js';\n\n/** `open` is the draft state; `submitted` is optional (see `PeriodRollupService`). */\nexport type TimecardStatus = 'open' | 'submitted' | 'confirmed';\n\n/** One person's immutable-on-confirmation period snapshot, in integer seconds. */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'period_timecards',\n sensitive: true,\n api: { include: [] },\n cli: { include: [] },\n mcp: { include: [] },\n})\nexport class PeriodTimecard extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n profileId = '';\n startsAt: Date = new Date(0);\n endsAt: Date = new Date(0);\n timezone = '';\n rulesVersion = '';\n @field({ type: 'text' }) rulesSnapshot = '{}';\n @field({ type: 'text' }) sourceSnapshot = '[]';\n regularSeconds = 0;\n overtimeSeconds = 0;\n totalSeconds = 0;\n @field({ type: 'text' }) status: TimecardStatus = 'open';\n /** Set by `submit()`; cleared when the card is reopened. */\n submittedAt: Date | null = null;\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n nullable: true,\n onDelete: 'RESTRICT',\n })\n submittedByProfileId: string | null = null;\n confirmedAt: Date | null = null;\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n nullable: true,\n onDelete: 'RESTRICT',\n })\n confirmedByProfileId: string | null = null;\n\n /** Return a detached source snapshot; corrupt persisted JSON fails closed. */\n getSources(): TimecardSource[] {\n const result: unknown = JSON.parse(this.sourceSnapshot);\n if (!Array.isArray(result))\n throw new Error('Invalid timecard source snapshot.');\n return result as TimecardSource[];\n }\n /** Return the consumer policy frozen with this rollup. */\n getRules(): Record<string, unknown> {\n const result: unknown = JSON.parse(this.rulesSnapshot);\n if (!result || typeof result !== 'object' || Array.isArray(result))\n throw new Error('Invalid timecard rules snapshot.');\n return result as Record<string, unknown>;\n }\n override async save(options?: SmrtSaveOptions): Promise<this> {\n assertRollupWrite(this);\n const prior = this.id\n ? await this.db.get(this.tableName, { id: this.id })\n : null;\n if (prior?.status === 'confirmed' && !isReopenWrite(this))\n throw new Error(\n 'Confirmed timecards are immutable; append an adjustment.',\n );\n if (prior?.status === 'submitted' && !isReopenWrite(this)) {\n // A submitted card only moves forward to confirmed (or back via reopen).\n if (this.status !== 'confirmed' && this.status !== 'submitted')\n throw new Error(\n 'Submitted timecards can only be confirmed or reopened.',\n );\n }\n if (\n prior &&\n (prior.tenant_id !== this.tenantId ||\n prior.profile_id !== this.profileId ||\n new Date(prior.starts_at as string).getTime() !==\n this.startsAt.getTime())\n )\n throw new Error('Timecard identity is immutable.');\n return (await super.save(options)) as this;\n }\n override async delete(): Promise<void> {\n throw new Error('Timecard history cannot be deleted.');\n }\n}\n\n/** Attributed append-only signed seconds; the confirmed base card never changes. */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'timecard_adjustments',\n sensitive: true,\n api: { include: [] },\n cli: { include: [] },\n mcp: { include: [] },\n})\nexport class TimecardAdjustment extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @foreignKey(PeriodTimecard, { onDelete: 'RESTRICT' }) timecardId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n profileId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n actorProfileId = '';\n operationId = '';\n regularSeconds = 0;\n overtimeSeconds = 0;\n reason = '';\n override async save(options?: SmrtSaveOptions): Promise<this> {\n assertRollupWrite(this);\n if (this.id && (await this.db.get(this.tableName, { id: this.id })))\n throw new Error('Timecard adjustments are append-only.');\n this.requireInsertOnSave();\n return (await super.save(options)) as this;\n }\n override async delete(): Promise<void> {\n throw new Error('Timecard adjustments cannot be deleted.');\n }\n}\n/**\n * Append-only audit row for one reopening of a submitted or confirmed card: who\n * reopened whose period, why, and the totals it had at that moment (base,\n * adjustments and effective, in integer seconds). Written only by\n * `PeriodRollupService.reopen()` in the same transaction as the status change.\n */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'timecard_reopenings',\n sensitive: true,\n indexes: [\n {\n name: 'timecard_reopenings_card_idx',\n columns: ['tenantId', 'timecardId'],\n },\n ],\n api: { include: [] },\n cli: { include: [] },\n mcp: { include: [] },\n})\nexport class TimecardReopening extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @foreignKey(PeriodTimecard, { onDelete: 'RESTRICT' }) timecardId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n profileId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n actorProfileId = '';\n operationId = '';\n @field({ type: 'text' }) fromStatus: TimecardStatus = 'confirmed';\n @field({ type: 'text' }) reason = '';\n /** JSON: base/adjustment/effective seconds, confirmation and submission attribution. */\n @field({ type: 'text' }) previousState = '{}';\n /** The totals and attribution the card had when it was reopened. */\n getPrevious<T = Record<string, unknown>>(): T {\n const result: unknown = JSON.parse(this.previousState);\n if (!result || typeof result !== 'object' || Array.isArray(result))\n throw new Error('Invalid timecard reopening snapshot.');\n return result as T;\n }\n override async save(options?: SmrtSaveOptions): Promise<this> {\n assertRollupWrite(this);\n if (this.id && (await this.db.get(this.tableName, { id: this.id })))\n throw new Error('Timecard reopenings are append-only.');\n if (!this.reason?.trim() || !this.operationId?.trim())\n throw new Error('Reopening requires an operationId and reason.');\n this.requireInsertOnSave();\n return (await super.save(options)) as this;\n }\n override async delete(): Promise<void> {\n throw new Error('Timecard reopenings cannot be deleted.');\n }\n}\n/** Scoped persistence collection; mutations must use PeriodRollupService. */\nexport class PeriodTimecardCollection extends SmrtCollection<PeriodTimecard> {\n static readonly _itemClass = PeriodTimecard;\n}\n/** Scoped append-only adjustment collection. */\nexport class TimecardAdjustmentCollection extends SmrtCollection<TimecardAdjustment> {\n static readonly _itemClass = TimecardAdjustment;\n}\n/** Scoped append-only reopening ledger. */\nexport class TimecardReopeningCollection extends SmrtCollection<TimecardReopening> {\n static readonly _itemClass = TimecardReopening;\n}\n","import type { AttendanceCorrectionGuard } from '../attendance/correction-service.js';\nimport { PeriodTimecardCollection } from './models.js';\n\n/**\n * An `AttendanceCorrectionService` guard that refuses to change attendance\n * inside a period a `PeriodTimecard` has confirmed. A confirmed card never\n * changes, so correcting its sources would leave card and attendance\n * disagreeing; reopen the card first (`PeriodRollupService.reopen`), correct,\n * then roll up again. A submitted card is frozen too. A reopened card is `open`\n * again, so corrections inside it are allowed.\n * Needs the `period_timecards` table, which is why it is opt-in.\n */\nexport const confirmedTimecardGuard: AttendanceCorrectionGuard = async (\n tx,\n subject,\n intervals,\n) => {\n const cards = await PeriodTimecardCollection.create({ db: tx.db });\n const locked = (\n await cards.list({\n where: { tenantId: subject.tenantId, profileId: subject.profileId },\n })\n ).filter(\n (card) => card.status === 'confirmed' || card.status === 'submitted',\n );\n const hit = locked.find((card) =>\n intervals.some(\n (i) =>\n i.startsAt.getTime() < card.endsAt.getTime() &&\n card.startsAt.getTime() < i.endsAt.getTime(),\n ),\n );\n return hit\n ? `Attendance overlaps ${hit.status} timecard ${hit.id}; reopen it before correcting.`\n : null;\n};\n","import type {\n RollupAttribution,\n TimecardPeriod,\n TimecardSource,\n} from './types.js';\n\nexport function integer(value: number, signed = false): number {\n if (!Number.isSafeInteger(value) || (!signed && value < 0))\n throw new Error('Timecard seconds must be safe integers and nonnegative.');\n return value;\n}\nexport function instant(value: Date): number {\n if (!(value instanceof Date) || !Number.isFinite(value.getTime()))\n throw new Error('Timecard timestamps must be valid dates.');\n return value.getTime();\n}\nexport function validatePeriod(\n value: TimecardPeriod,\n at: Date,\n): TimecardPeriod {\n if (\n !value ||\n instant(value.startsAt) > instant(at) ||\n instant(value.endsAt) <= instant(at) ||\n instant(value.startsAt) >= instant(value.endsAt)\n )\n throw new Error('Resolver period must contain the requested instant.');\n if (!value.timezone || !value.version?.trim())\n throw new Error('Resolver must supply a timezone and policy version.');\n new Intl.DateTimeFormat('en', { timeZone: value.timezone });\n if (\n !value.rules ||\n typeof value.rules !== 'object' ||\n Array.isArray(value.rules)\n )\n throw new Error('Resolver rules must be a JSON object.');\n // JSON round-trip detaches consumer-owned state; fail rather than silently lose non-JSON values.\n const json = JSON.stringify(value.rules, (_key, item) => {\n if (\n typeof item === 'function' ||\n typeof item === 'undefined' ||\n typeof item === 'symbol' ||\n (typeof item === 'number' && !Number.isFinite(item))\n )\n throw new Error('Resolver rules must be JSON serializable.');\n return item;\n });\n const rules: unknown = JSON.parse(json);\n if (!rules || typeof rules !== 'object' || Array.isArray(rules))\n throw new Error('Resolver rules must serialize to a JSON object.');\n return {\n startsAt: new Date(value.startsAt),\n endsAt: new Date(value.endsAt),\n timezone: value.timezone,\n version: value.version,\n rules: rules as Record<string, unknown>,\n };\n}\n/** Prefix allocation preserves integer seconds across adjacent half-open periods. */\nexport function clipSource(\n source: TimecardSource,\n period: TimecardPeriod,\n): TimecardSource | null {\n const start = instant(new Date(source.startsAt));\n const end = instant(new Date(source.endsAt));\n integer(source.seconds);\n if (end < start) throw new Error('Invalid source interval.');\n const left = Math.max(start, period.startsAt.getTime());\n const right = Math.min(end, period.endsAt.getTime());\n if (start === end)\n return start >= period.startsAt.getTime() && start < period.endsAt.getTime()\n ? { ...source }\n : null;\n if (left >= right) return null;\n const span = integer(end - start);\n const prefix = (elapsed: number) =>\n Number((BigInt(source.seconds) * BigInt(integer(elapsed))) / BigInt(span));\n const seconds = prefix(right - start) - prefix(left - start);\n return {\n ...source,\n startsAt: new Date(left).toISOString(),\n endsAt: new Date(right).toISOString(),\n seconds: integer(seconds),\n };\n}\n\n/**\n * Attribute a source to a period. `'clip'` prorates it to the period; `'start'`\n * keeps it whole when `ownerStart` (the start of the punch or entry it belongs\n * to, in epoch ms) falls inside the period, and drops it otherwise.\n */\nexport function attributeSource(\n source: TimecardSource,\n period: TimecardPeriod,\n attribution: RollupAttribution,\n ownerStart: number,\n): TimecardSource | null {\n if (attribution === 'clip') return clipSource(source, period);\n integer(source.seconds);\n if (instant(new Date(source.endsAt)) < instant(new Date(source.startsAt)))\n throw new Error('Invalid source interval.');\n return ownerStart >= period.startsAt.getTime() &&\n ownerStart < period.endsAt.getTime()\n ? { ...source }\n : null;\n}\n","import { createHash } from 'node:crypto';\nimport {\n isEmbeddedDatabase,\n isPostgresDatabase,\n withEmbeddedWriteTransaction,\n} from '@happyvertical/smrt-core';\nimport { getTenantId, withTenant } from '@happyvertical/smrt-tenancy';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport { ServiceTimeEntryCollection } from '../models/service-time-entry.js';\nimport {\n attributeSource,\n instant,\n integer,\n validatePeriod,\n} from './calculation.js';\nimport {\n PeriodTimecard,\n PeriodTimecardCollection,\n TimecardAdjustment,\n TimecardAdjustmentCollection,\n TimecardReopening,\n TimecardReopeningCollection,\n} from './models.js';\nimport type {\n PeriodRollupOptions,\n PeriodRulesResolver,\n TimecardActor,\n TimecardAdjustmentInput,\n TimecardReopenInput,\n TimecardSource,\n} from './types.js';\nimport { persistReopen, persistRollup } from './write.js';\n\nfunction identity(parts: string[]): string {\n const bytes = createHash('sha256').update(JSON.stringify(parts)).digest();\n bytes[6] = (bytes[6] & 15) | 80;\n bytes[8] = (bytes[8] & 63) | 128;\n const hex = bytes.subarray(0, 16).toString('hex');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\nfunction adjustedSeconds(\n base: number,\n rows: TimecardAdjustment[],\n field: 'regularSeconds' | 'overtimeSeconds',\n): number {\n const total = rows.reduce(\n (sum, row) => sum + BigInt(integer(row[field], true)),\n BigInt(integer(base)),\n );\n return integer(Number(total));\n}\n\n/**\n * Server-side, trusted tenant/person capability. Consumers authorize this scope\n * before construction. Reads and writes reject conflicting ambient tenancy.\n * SQLite and PostgreSQL are supported; policy and payroll remain consumer-owned.\n */\nexport class PeriodRollupService {\n constructor(\n private readonly db: DatabaseInterface,\n private readonly actor: TimecardActor,\n private readonly resolver: PeriodRulesResolver,\n private readonly options: PeriodRollupOptions = {},\n ) {\n if (!actor.tenantId?.trim() || !actor.profileId?.trim())\n throw new Error('Timecards require a trusted tenant/profile actor.');\n this.actor = Object.freeze({\n tenantId: this.identity(actor.tenantId),\n profileId: this.identity(actor.profileId),\n });\n if (\n options.attribution !== undefined &&\n options.attribution !== 'clip' &&\n options.attribution !== 'start'\n )\n throw new Error(\"Attribution must be 'clip' or 'start'.\");\n if (options.actorProfileId !== undefined && !options.actorProfileId.trim())\n throw new Error('Actor attribution must be a nonempty profile id.');\n this.options = {\n attendance: options.attendance,\n attribution: options.attribution ?? 'clip',\n requireSubmission: options.requireSubmission === true,\n actorProfileId: this.identity(\n options.actorProfileId ?? this.actor.profileId,\n ),\n };\n }\n\n private identity(value: string): string {\n if (!isPostgresDatabase(this.db)) return value;\n const hex = value.replace(/[{}-]/g, '').toLowerCase();\n if (!/^[0-9a-f]{32}$/.test(hex))\n throw new Error('PostgreSQL timecard identities must be UUIDs.');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n }\n private scope<T>(run: () => Promise<T>): Promise<T> {\n const active = getTenantId();\n if (active && this.identity(active) !== this.actor.tenantId)\n throw new Error('Timecard actor differs from active tenant.');\n return withTenant({ tenantId: this.actor.tenantId }, run);\n }\n private transact<T>(run: (db: DatabaseInterface) => Promise<T>): Promise<T> {\n return this.scope(() =>\n withEmbeddedWriteTransaction(\n this.db,\n isEmbeddedDatabase(this.db),\n async (db) => {\n if (isPostgresDatabase(db))\n await db.query(\n 'SELECT pg_advisory_xact_lock(hashtextextended($1, 0))',\n JSON.stringify([\n 'smrt.rollup',\n this.actor.tenantId,\n this.actor.profileId,\n ]),\n );\n if (this.options.attendance && isPostgresDatabase(db))\n await db.query(\n 'SELECT pg_advisory_xact_lock(hashtextextended($1, 0))',\n JSON.stringify([\n 'smrt.attendance',\n this.actor.tenantId,\n this.actor.profileId,\n ]),\n );\n return run(db);\n },\n true,\n ),\n );\n }\n private async card(\n db: DatabaseInterface,\n id: string,\n ): Promise<PeriodTimecard> {\n const collection = await PeriodTimecardCollection.create({ db });\n const [card] = await collection.list({\n where: { ...this.actor, id },\n limit: 1,\n });\n if (!card) throw new Error('Timecard not found in actor scope.');\n return card;\n }\n /** Scoped read on the root executor, including after transaction completion. */\n async get(id: string): Promise<PeriodTimecard> {\n return this.scope(() => this.card(this.db, id));\n }\n /** Rebuild one open card from a consistent transaction-bound source snapshot. */\n async rollup(at: Date): Promise<PeriodTimecard> {\n at = new Date(instant(at));\n const id = await this.transact(async (db) => {\n const period = validatePeriod(\n await this.resolver.periodFor(new Date(at), this.actor),\n at,\n );\n const id = identity([\n 'smrt.period',\n this.actor.tenantId,\n this.actor.profileId,\n period.startsAt.toISOString(),\n ]);\n const collection = await PeriodTimecardCollection.create({ db });\n const [prior] = await collection.list({\n where: { ...this.actor, id },\n limit: 1,\n });\n if (prior?.status === 'confirmed')\n throw new Error(\n 'Confirmed timecards are immutable; append an adjustment.',\n );\n // A policy change must not create overlapping cards for the same person.\n for (const other of await collection.list({ where: { ...this.actor } })) {\n if (\n other.id !== id &&\n other.startsAt < period.endsAt &&\n other.endsAt > period.startsAt\n )\n throw new Error('Resolver period overlaps an existing timecard.');\n }\n if (prior?.status === 'submitted')\n throw new Error('Submitted timecards are frozen; reopen first.');\n let sources: TimecardSource[] = [];\n let linkedEntryIds = new Set<string>();\n if (this.options.attendance) {\n const { attendanceSources } = await import('./attendance.js');\n ({ sources, linkedEntryIds } = await attendanceSources(\n db,\n this.actor,\n period,\n this.options.attribution,\n ));\n }\n const entries = await ServiceTimeEntryCollection.create({ db });\n const evidence = await entries.list({\n where: {\n tenantId: this.actor.tenantId,\n participantProfileId: this.actor.profileId,\n participantKind: 'human',\n },\n });\n const byId = new Map(evidence.map((entry) => [String(entry.id), entry]));\n const approvedRoots = new Set<string>();\n for (const entry of evidence) {\n if (entry.status !== 'approved') continue;\n let linked = linkedEntryIds.has(String(entry.id));\n let ancestor = entry;\n const seen = new Set([String(entry.id)]);\n while (ancestor.correctionOfId) {\n const parentId = ancestor.correctionOfId;\n if (seen.has(parentId))\n throw new Error('Cyclic evidence correction chain.');\n seen.add(parentId);\n const parent = byId.get(parentId);\n if (!parent || parent.status !== 'corrected')\n throw new Error(\n 'Correction requires corrected evidence in the same actor scope.',\n );\n linked ||= linkedEntryIds.has(parentId);\n ancestor = parent;\n }\n const rootId = String(ancestor.id);\n if (approvedRoots.has(rootId))\n throw new Error('Ambiguous approved evidence correction chain.');\n approvedRoots.add(rootId);\n if (linked) continue;\n // Duration-only manual evidence belongs to its approval instant. A\n // bounded interval allocates its actual duration by elapsed fraction.\n const start = entry.startedAt ?? entry.approvedAt;\n if (!start)\n throw new Error(\n 'Approved evidence needs a start or approval timestamp.',\n );\n // Hours-only sources are unbounded and attributed at approval. An\n // unsupported quantity in another period must not block this one.\n if (\n entry.durationSeconds === null &&\n (instant(start) < period.startsAt.getTime() ||\n instant(start) >= period.endsAt.getTime())\n )\n continue;\n const end = entry.startedAt && entry.endedAt ? entry.endedAt : start;\n const source = attributeSource(\n {\n kind: 'entry',\n id: String(entry.id),\n startsAt: new Date(instant(start)).toISOString(),\n endsAt: new Date(instant(end)).toISOString(),\n seconds: integer(entry.requireDurationSeconds()),\n },\n period,\n this.options.attribution ?? 'clip',\n instant(start),\n );\n if (source?.seconds) sources.push(source);\n }\n sources.sort(\n (a, b) =>\n a.startsAt.localeCompare(b.startsAt) ||\n a.kind.localeCompare(b.kind) ||\n a.id.localeCompare(b.id),\n );\n const totalSeconds = sources.reduce(\n (sum, source) => integer(sum + source.seconds),\n 0,\n );\n // Resolver receives detached snapshots so it cannot alter persisted evidence.\n const classified = await this.resolver.classify(\n structuredClone(period),\n structuredClone(sources),\n this.actor,\n );\n const regularSeconds = integer(classified?.regularSeconds);\n const overtimeSeconds = integer(classified?.overtimeSeconds);\n if (integer(regularSeconds + overtimeSeconds) !== totalSeconds)\n throw new Error('Resolver classification must conserve total seconds.');\n const card =\n prior ??\n (await new PeriodTimecard({ db, id, _skipLoad: true }).initialize());\n if (!prior) card.requireInsertOnSave();\n Object.assign(card, this.actor, {\n startsAt: period.startsAt,\n endsAt: period.endsAt,\n timezone: period.timezone,\n rulesVersion: period.version,\n rulesSnapshot: JSON.stringify(period.rules),\n sourceSnapshot: JSON.stringify(sources),\n regularSeconds,\n overtimeSeconds,\n totalSeconds,\n });\n await persistRollup(card);\n return id;\n });\n return this.get(id);\n }\n /**\n * Optional step: freeze an open card for review (open -> submitted). The\n * actor is the submitter; repeating it on a submitted card is idempotent.\n */\n async submit(id: string): Promise<PeriodTimecard> {\n id = this.identity(id);\n await this.transact(async (db) => {\n const card = await this.card(db, id);\n if (card.status === 'submitted') return;\n if (card.status !== 'open')\n throw new Error('Only an open timecard can be submitted.');\n card.status = 'submitted';\n card.submittedAt = new Date();\n card.submittedByProfileId = this.options.actorProfileId!;\n await persistRollup(card);\n });\n return this.get(id);\n }\n /**\n * Confirm the current snapshot; repeated confirmation is idempotent. With\n * `requireSubmission` the card must be submitted first.\n */\n async confirm(id: string): Promise<PeriodTimecard> {\n await this.transact(async (db) => {\n const card = await this.card(db, id);\n if (card.status === 'confirmed') return;\n if (card.status === 'open' && this.options.requireSubmission)\n throw new Error('Timecards must be submitted before confirmation.');\n card.status = 'confirmed';\n card.confirmedAt = new Date();\n card.confirmedByProfileId = this.options.actorProfileId!;\n await persistRollup(card);\n });\n return this.get(id);\n }\n /**\n * Return a submitted or confirmed card to `open` so its sources can be\n * corrected and `rollup()` run again, then confirmed anew. Needs a reason and\n * an operation key; appends a `TimecardReopening` with the totals the card had\n * (base, adjustments, effective). Adjustments stay on the ledger and keep\n * counting in `totals()`: after a re-roll, offset them with a new adjustment\n * if the correction made them redundant. The consumer authorizes the actor\n * (give reopening its own permission); a retry with the same key and reason\n * is a no-op.\n */\n async reopen(\n id: string,\n input: TimecardReopenInput,\n ): Promise<PeriodTimecard> {\n id = this.identity(id);\n input = { operationId: input.operationId, reason: input.reason };\n if (!input.operationId?.trim() || !input.reason?.trim())\n throw new Error('Reopening requires an operationId and reason.');\n const reopeningId = identity([\n 'smrt.reopening',\n this.actor.tenantId,\n this.actor.profileId,\n id,\n input.operationId,\n ]);\n await this.transact(async (db) => {\n const ledger = await TimecardReopeningCollection.create({ db });\n const [done] = await ledger.list({\n where: { ...this.actor, timecardId: id, id: reopeningId },\n limit: 1,\n });\n if (done) {\n if (\n done.reason !== input.reason ||\n done.actorProfileId !== this.options.actorProfileId\n )\n throw new Error(\n 'Reopening operationId reused with a different payload.',\n );\n return;\n }\n const card = await this.card(db, id);\n if (card.status === 'open')\n throw new Error(\n 'Only a submitted or confirmed timecard can be reopened.',\n );\n const adjustments = await (\n await TimecardAdjustmentCollection.create({ db })\n ).list({ where: { ...this.actor, timecardId: id } });\n const regular = adjustedSeconds(\n card.regularSeconds,\n adjustments,\n 'regularSeconds',\n );\n const overtime = adjustedSeconds(\n card.overtimeSeconds,\n adjustments,\n 'overtimeSeconds',\n );\n const row = await new TimecardReopening({\n db,\n id: reopeningId,\n _skipLoad: true,\n }).initialize();\n Object.assign(row, this.actor, {\n timecardId: id,\n actorProfileId: this.options.actorProfileId!,\n operationId: input.operationId,\n fromStatus: card.status,\n reason: input.reason,\n previousState: JSON.stringify({\n rulesVersion: card.rulesVersion,\n base: {\n regularSeconds: card.regularSeconds,\n overtimeSeconds: card.overtimeSeconds,\n totalSeconds: card.totalSeconds,\n },\n adjustments: {\n count: adjustments.length,\n regularSeconds: integer(regular - card.regularSeconds, true),\n overtimeSeconds: integer(overtime - card.overtimeSeconds, true),\n },\n effective: {\n regularSeconds: regular,\n overtimeSeconds: overtime,\n totalSeconds: integer(regular + overtime),\n },\n submittedAt: card.submittedAt?.toISOString() ?? null,\n submittedByProfileId: card.submittedByProfileId,\n confirmedAt: card.confirmedAt?.toISOString() ?? null,\n confirmedByProfileId: card.confirmedByProfileId,\n }),\n });\n await persistRollup(row);\n card.status = 'open';\n card.submittedAt = null;\n card.submittedByProfileId = null;\n card.confirmedAt = null;\n card.confirmedByProfileId = null;\n await persistReopen(card);\n });\n return this.get(id);\n }\n /** The append-only reopening history of one card, oldest first. */\n async reopenings(id: string): Promise<TimecardReopening[]> {\n id = this.identity(id);\n return this.scope(async () => {\n await this.card(this.db, id);\n return (await TimecardReopeningCollection.create({ db: this.db })).list({\n where: { ...this.actor, timecardId: id },\n orderBy: 'created_at ASC',\n });\n });\n }\n /** Append a correction. An operation key retry must have exactly the same payload. */\n async adjust(\n id: string,\n input: TimecardAdjustmentInput,\n ): Promise<TimecardAdjustment> {\n id = this.identity(id);\n input = {\n operationId: input.operationId,\n reason: input.reason,\n regularSeconds: input.regularSeconds,\n overtimeSeconds: input.overtimeSeconds,\n };\n integer(input.regularSeconds, true);\n integer(input.overtimeSeconds, true);\n if (!input.operationId?.trim() || !input.reason?.trim())\n throw new Error('Adjustment requires an operationId and reason.');\n const adjustmentId = identity([\n 'smrt.adjustment',\n this.actor.tenantId,\n this.actor.profileId,\n id,\n input.operationId,\n ]);\n await this.transact(async (db) => {\n const card = await this.card(db, id);\n if (card.status !== 'confirmed')\n throw new Error('Adjustments require a confirmed timecard.');\n const collection = await TimecardAdjustmentCollection.create({ db });\n const rows = await collection.list({\n where: { ...this.actor, timecardId: id },\n });\n const prior = rows.find((row) => row.id === adjustmentId);\n if (prior) {\n if (\n prior.regularSeconds !== input.regularSeconds ||\n prior.overtimeSeconds !== input.overtimeSeconds ||\n prior.reason !== input.reason ||\n prior.actorProfileId !== this.options.actorProfileId\n )\n throw new Error(\n 'Adjustment operationId reused with a different payload.',\n );\n return;\n }\n const regular = adjustedSeconds(\n card.regularSeconds,\n rows,\n 'regularSeconds',\n );\n const overtime = adjustedSeconds(\n card.overtimeSeconds,\n rows,\n 'overtimeSeconds',\n );\n integer(\n integer(regular + input.regularSeconds) +\n integer(overtime + input.overtimeSeconds),\n );\n const adjustment = await new TimecardAdjustment({\n db,\n id: adjustmentId,\n _skipLoad: true,\n }).initialize();\n Object.assign(adjustment, this.actor, {\n timecardId: id,\n actorProfileId: this.options.actorProfileId!,\n ...input,\n });\n await persistRollup(adjustment);\n });\n return this.scope(async () => {\n const collection = await TimecardAdjustmentCollection.create({\n db: this.db,\n });\n const [row] = await collection.list({\n where: { ...this.actor, id: adjustmentId },\n limit: 1,\n });\n if (!row) throw new Error('Committed adjustment disappeared.');\n return row;\n });\n }\n /** Effective totals combine the frozen base with append-only corrections. */\n async totals(id: string): Promise<{\n regularSeconds: number;\n overtimeSeconds: number;\n totalSeconds: number;\n }> {\n return this.transact(async (db) => {\n const card = await this.card(db, id);\n const collection = await TimecardAdjustmentCollection.create({ db });\n const rows = await collection.list({\n where: { ...this.actor, timecardId: id },\n });\n const regularSeconds = adjustedSeconds(\n card.regularSeconds,\n rows,\n 'regularSeconds',\n );\n const overtimeSeconds = adjustedSeconds(\n card.overtimeSeconds,\n rows,\n 'overtimeSeconds',\n );\n return {\n regularSeconds,\n overtimeSeconds,\n totalSeconds: integer(regularSeconds + overtimeSeconds),\n };\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rollup-DlG54TX4.js","names":["id"],"sources":["../../src/rollup/write.ts","../../src/rollup/models.ts","../../src/rollup/correction-guard.ts","../../src/rollup/calculation.ts","../../src/rollup/service.ts"],"sourcesContent":["import type { SmrtObject } from '@happyvertical/smrt-core';\n\n// Per-instance capability, deliberately absent from the package export surface.\nconst permitted = new WeakSet<SmrtObject>();\nexport function assertRollupWrite(object: SmrtObject): void {\n if (!permitted.has(object))\n throw new Error(\n 'Timecards and adjustments are writable only through PeriodRollupService.',\n );\n}\nexport async function persistRollup<T extends SmrtObject>(\n object: T,\n): Promise<T> {\n permitted.add(object);\n try {\n await object.save();\n return object;\n } finally {\n permitted.delete(object);\n }\n}\n\n// Second capability: only reopen() may move a confirmed/submitted card back.\nconst reopening = new WeakSet<SmrtObject>();\nexport function isReopenWrite(object: SmrtObject): boolean {\n return reopening.has(object);\n}\nexport async function persistReopen<T extends SmrtObject>(\n object: T,\n): Promise<T> {\n reopening.add(object);\n try {\n return await persistRollup(object);\n } finally {\n reopening.delete(object);\n }\n}\n","import {\n crossPackageRef,\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n type SmrtSaveOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { TimecardSource } from './types.js';\nimport { assertRollupWrite, isReopenWrite } from './write.js';\n\n/** `open` is the draft state; `submitted` is optional (see `PeriodRollupService`). */\nexport type TimecardStatus = 'open' | 'submitted' | 'confirmed';\n\n/** One person's immutable-on-confirmation period snapshot, in integer seconds. */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'period_timecards',\n sensitive: true,\n api: { include: [] },\n cli: { include: [] },\n mcp: { include: [] },\n})\nexport class PeriodTimecard extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n profileId = '';\n startsAt: Date = new Date(0);\n endsAt: Date = new Date(0);\n timezone = '';\n rulesVersion = '';\n @field({ type: 'text' }) rulesSnapshot = '{}';\n @field({ type: 'text' }) sourceSnapshot = '[]';\n regularSeconds = 0;\n overtimeSeconds = 0;\n totalSeconds = 0;\n @field({ type: 'text' }) status: TimecardStatus = 'open';\n /** Set by `submit()`; cleared when the card is reopened. */\n submittedAt: Date | null = null;\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n nullable: true,\n onDelete: 'RESTRICT',\n })\n submittedByProfileId: string | null = null;\n confirmedAt: Date | null = null;\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n nullable: true,\n onDelete: 'RESTRICT',\n })\n confirmedByProfileId: string | null = null;\n\n /** Return a detached source snapshot; corrupt persisted JSON fails closed. */\n getSources(): TimecardSource[] {\n const result: unknown = JSON.parse(this.sourceSnapshot);\n if (!Array.isArray(result))\n throw new Error('Invalid timecard source snapshot.');\n return result as TimecardSource[];\n }\n /** Return the consumer policy frozen with this rollup. */\n getRules(): Record<string, unknown> {\n const result: unknown = JSON.parse(this.rulesSnapshot);\n if (!result || typeof result !== 'object' || Array.isArray(result))\n throw new Error('Invalid timecard rules snapshot.');\n return result as Record<string, unknown>;\n }\n override async save(options?: SmrtSaveOptions): Promise<this> {\n assertRollupWrite(this);\n const prior = this.id\n ? await this.db.get(this.tableName, { id: this.id })\n : null;\n if (prior?.status === 'confirmed' && !isReopenWrite(this))\n throw new Error(\n 'Confirmed timecards are immutable; append an adjustment.',\n );\n if (prior?.status === 'submitted' && !isReopenWrite(this)) {\n // A submitted card only moves forward to confirmed (or back via reopen).\n if (this.status !== 'confirmed' && this.status !== 'submitted')\n throw new Error(\n 'Submitted timecards can only be confirmed or reopened.',\n );\n }\n if (\n prior &&\n (prior.tenant_id !== this.tenantId ||\n prior.profile_id !== this.profileId ||\n new Date(prior.starts_at as string).getTime() !==\n this.startsAt.getTime())\n )\n throw new Error('Timecard identity is immutable.');\n return (await super.save(options)) as this;\n }\n override async delete(): Promise<void> {\n throw new Error('Timecard history cannot be deleted.');\n }\n}\n\n/** Attributed append-only signed seconds; the confirmed base card never changes. */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'timecard_adjustments',\n sensitive: true,\n api: { include: [] },\n cli: { include: [] },\n mcp: { include: [] },\n})\nexport class TimecardAdjustment extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @foreignKey(PeriodTimecard, { onDelete: 'RESTRICT' }) timecardId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n profileId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n actorProfileId = '';\n operationId = '';\n regularSeconds = 0;\n overtimeSeconds = 0;\n reason = '';\n override async save(options?: SmrtSaveOptions): Promise<this> {\n assertRollupWrite(this);\n if (this.id && (await this.db.get(this.tableName, { id: this.id })))\n throw new Error('Timecard adjustments are append-only.');\n this.requireInsertOnSave();\n return (await super.save(options)) as this;\n }\n override async delete(): Promise<void> {\n throw new Error('Timecard adjustments cannot be deleted.');\n }\n}\n/**\n * Append-only audit row for one reopening of a submitted or confirmed card: who\n * reopened whose period, why, and the totals it had at that moment (base,\n * adjustments and effective, in integer seconds). Written only by\n * `PeriodRollupService.reopen()` in the same transaction as the status change.\n */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'timecard_reopenings',\n sensitive: true,\n indexes: [\n {\n name: 'timecard_reopenings_card_idx',\n columns: ['tenantId', 'timecardId'],\n },\n ],\n api: { include: [] },\n cli: { include: [] },\n mcp: { include: [] },\n})\nexport class TimecardReopening extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-timesheets\";\n @tenantId() tenantId = '';\n @foreignKey(PeriodTimecard, { onDelete: 'RESTRICT' }) timecardId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n profileId = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', {\n onDelete: 'RESTRICT',\n })\n actorProfileId = '';\n operationId = '';\n @field({ type: 'text' }) fromStatus: TimecardStatus = 'confirmed';\n @field({ type: 'text' }) reason = '';\n /** JSON: base/adjustment/effective seconds, confirmation and submission attribution. */\n @field({ type: 'text' }) previousState = '{}';\n /** The totals and attribution the card had when it was reopened. */\n getPrevious<T = Record<string, unknown>>(): T {\n const result: unknown = JSON.parse(this.previousState);\n if (!result || typeof result !== 'object' || Array.isArray(result))\n throw new Error('Invalid timecard reopening snapshot.');\n return result as T;\n }\n override async save(options?: SmrtSaveOptions): Promise<this> {\n assertRollupWrite(this);\n if (this.id && (await this.db.get(this.tableName, { id: this.id })))\n throw new Error('Timecard reopenings are append-only.');\n if (!this.reason?.trim() || !this.operationId?.trim())\n throw new Error('Reopening requires an operationId and reason.');\n this.requireInsertOnSave();\n return (await super.save(options)) as this;\n }\n override async delete(): Promise<void> {\n throw new Error('Timecard reopenings cannot be deleted.');\n }\n}\n/** Scoped persistence collection; mutations must use PeriodRollupService. */\nexport class PeriodTimecardCollection extends SmrtCollection<PeriodTimecard> {\n static readonly _itemClass = PeriodTimecard;\n}\n/** Scoped append-only adjustment collection. */\nexport class TimecardAdjustmentCollection extends SmrtCollection<TimecardAdjustment> {\n static readonly _itemClass = TimecardAdjustment;\n}\n/** Scoped append-only reopening ledger. */\nexport class TimecardReopeningCollection extends SmrtCollection<TimecardReopening> {\n static readonly _itemClass = TimecardReopening;\n}\n","import type { AttendanceCorrectionGuard } from '../attendance/correction-service.js';\nimport { PeriodTimecardCollection } from './models.js';\n\n/**\n * An `AttendanceCorrectionService` guard that refuses to change attendance\n * inside a period a `PeriodTimecard` has confirmed. A confirmed card never\n * changes, so correcting its sources would leave card and attendance\n * disagreeing; reopen the card first (`PeriodRollupService.reopen`), correct,\n * then roll up again. A submitted card is frozen too. A reopened card is `open`\n * again, so corrections inside it are allowed.\n * Needs the `period_timecards` table, which is why it is opt-in.\n */\nexport const confirmedTimecardGuard: AttendanceCorrectionGuard = async (\n tx,\n subject,\n intervals,\n) => {\n const cards = await PeriodTimecardCollection.create({ db: tx.db });\n const locked = (\n await cards.list({\n where: { tenantId: subject.tenantId, profileId: subject.profileId },\n })\n ).filter(\n (card) => card.status === 'confirmed' || card.status === 'submitted',\n );\n const hit = locked.find((card) =>\n intervals.some(\n (i) =>\n i.startsAt.getTime() < card.endsAt.getTime() &&\n card.startsAt.getTime() < i.endsAt.getTime(),\n ),\n );\n return hit\n ? `Attendance overlaps ${hit.status} timecard ${hit.id}; reopen it before correcting.`\n : null;\n};\n","import type {\n RollupAttribution,\n TimecardPeriod,\n TimecardSource,\n} from './types.js';\n\nexport function integer(value: number, signed = false): number {\n if (!Number.isSafeInteger(value) || (!signed && value < 0))\n throw new Error('Timecard seconds must be safe integers and nonnegative.');\n return value;\n}\nexport function instant(value: Date): number {\n if (!(value instanceof Date) || !Number.isFinite(value.getTime()))\n throw new Error('Timecard timestamps must be valid dates.');\n return value.getTime();\n}\nexport function validatePeriod(\n value: TimecardPeriod,\n at: Date,\n): TimecardPeriod {\n if (\n !value ||\n instant(value.startsAt) > instant(at) ||\n instant(value.endsAt) <= instant(at) ||\n instant(value.startsAt) >= instant(value.endsAt)\n )\n throw new Error('Resolver period must contain the requested instant.');\n if (!value.timezone || !value.version?.trim())\n throw new Error('Resolver must supply a timezone and policy version.');\n new Intl.DateTimeFormat('en', { timeZone: value.timezone });\n if (\n !value.rules ||\n typeof value.rules !== 'object' ||\n Array.isArray(value.rules)\n )\n throw new Error('Resolver rules must be a JSON object.');\n // JSON round-trip detaches consumer-owned state; fail rather than silently lose non-JSON values.\n const json = JSON.stringify(value.rules, (_key, item) => {\n if (\n typeof item === 'function' ||\n typeof item === 'undefined' ||\n typeof item === 'symbol' ||\n (typeof item === 'number' && !Number.isFinite(item))\n )\n throw new Error('Resolver rules must be JSON serializable.');\n return item;\n });\n const rules: unknown = JSON.parse(json);\n if (!rules || typeof rules !== 'object' || Array.isArray(rules))\n throw new Error('Resolver rules must serialize to a JSON object.');\n return {\n startsAt: new Date(value.startsAt),\n endsAt: new Date(value.endsAt),\n timezone: value.timezone,\n version: value.version,\n rules: rules as Record<string, unknown>,\n };\n}\n/** Prefix allocation preserves integer seconds across adjacent half-open periods. */\nexport function clipSource(\n source: TimecardSource,\n period: TimecardPeriod,\n): TimecardSource | null {\n const start = instant(new Date(source.startsAt));\n const end = instant(new Date(source.endsAt));\n integer(source.seconds);\n if (end < start) throw new Error('Invalid source interval.');\n const left = Math.max(start, period.startsAt.getTime());\n const right = Math.min(end, period.endsAt.getTime());\n if (start === end)\n return start >= period.startsAt.getTime() && start < period.endsAt.getTime()\n ? { ...source }\n : null;\n if (left >= right) return null;\n const span = integer(end - start);\n const prefix = (elapsed: number) =>\n Number((BigInt(source.seconds) * BigInt(integer(elapsed))) / BigInt(span));\n const seconds = prefix(right - start) - prefix(left - start);\n return {\n ...source,\n startsAt: new Date(left).toISOString(),\n endsAt: new Date(right).toISOString(),\n seconds: integer(seconds),\n };\n}\n\n/**\n * Attribute a source to a period. `'clip'` prorates it to the period; `'start'`\n * keeps it whole when `ownerStart` (the start of the punch or entry it belongs\n * to, in epoch ms) falls inside the period, and drops it otherwise.\n */\nexport function attributeSource(\n source: TimecardSource,\n period: TimecardPeriod,\n attribution: RollupAttribution,\n ownerStart: number,\n): TimecardSource | null {\n if (attribution === 'clip') return clipSource(source, period);\n integer(source.seconds);\n if (instant(new Date(source.endsAt)) < instant(new Date(source.startsAt)))\n throw new Error('Invalid source interval.');\n return ownerStart >= period.startsAt.getTime() &&\n ownerStart < period.endsAt.getTime()\n ? { ...source }\n : null;\n}\n","import { createHash } from 'node:crypto';\nimport {\n isEmbeddedDatabase,\n isPostgresDatabase,\n withEmbeddedWriteTransaction,\n} from '@happyvertical/smrt-core';\nimport { getTenantId, withTenant } from '@happyvertical/smrt-tenancy';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport { ServiceTimeEntryCollection } from '../models/service-time-entry.js';\nimport {\n attributeSource,\n instant,\n integer,\n validatePeriod,\n} from './calculation.js';\nimport {\n PeriodTimecard,\n PeriodTimecardCollection,\n TimecardAdjustment,\n TimecardAdjustmentCollection,\n TimecardReopening,\n TimecardReopeningCollection,\n} from './models.js';\nimport type {\n PeriodRollupOptions,\n PeriodRulesResolver,\n TimecardActor,\n TimecardAdjustmentInput,\n TimecardReopenInput,\n TimecardSource,\n} from './types.js';\nimport { persistReopen, persistRollup } from './write.js';\n\nfunction identity(parts: string[]): string {\n const bytes = createHash('sha256').update(JSON.stringify(parts)).digest();\n bytes[6] = (bytes[6] & 15) | 80;\n bytes[8] = (bytes[8] & 63) | 128;\n const hex = bytes.subarray(0, 16).toString('hex');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\nfunction adjustedSeconds(\n base: number,\n rows: TimecardAdjustment[],\n field: 'regularSeconds' | 'overtimeSeconds',\n): number {\n const total = rows.reduce(\n (sum, row) => sum + BigInt(integer(row[field], true)),\n BigInt(integer(base)),\n );\n return integer(Number(total));\n}\n\n/**\n * Server-side, trusted tenant/person capability. Consumers authorize this scope\n * before construction. Reads and writes reject conflicting ambient tenancy.\n * SQLite and PostgreSQL are supported; policy and payroll remain consumer-owned.\n */\nexport class PeriodRollupService {\n constructor(\n private readonly db: DatabaseInterface,\n private readonly actor: TimecardActor,\n private readonly resolver: PeriodRulesResolver,\n private readonly options: PeriodRollupOptions = {},\n ) {\n if (!actor.tenantId?.trim() || !actor.profileId?.trim())\n throw new Error('Timecards require a trusted tenant/profile actor.');\n this.actor = Object.freeze({\n tenantId: this.identity(actor.tenantId),\n profileId: this.identity(actor.profileId),\n });\n if (\n options.attribution !== undefined &&\n options.attribution !== 'clip' &&\n options.attribution !== 'start'\n )\n throw new Error(\"Attribution must be 'clip' or 'start'.\");\n if (options.actorProfileId !== undefined && !options.actorProfileId.trim())\n throw new Error('Actor attribution must be a nonempty profile id.');\n this.options = {\n attendance: options.attendance,\n attribution: options.attribution ?? 'clip',\n requireSubmission: options.requireSubmission === true,\n actorProfileId: this.identity(\n options.actorProfileId ?? this.actor.profileId,\n ),\n };\n }\n\n private identity(value: string): string {\n if (!isPostgresDatabase(this.db)) return value;\n const hex = value.replace(/[{}-]/g, '').toLowerCase();\n if (!/^[0-9a-f]{32}$/.test(hex))\n throw new Error('PostgreSQL timecard identities must be UUIDs.');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n }\n private scope<T>(run: () => Promise<T>): Promise<T> {\n const active = getTenantId();\n if (active && this.identity(active) !== this.actor.tenantId)\n throw new Error('Timecard actor differs from active tenant.');\n return withTenant({ tenantId: this.actor.tenantId }, run);\n }\n private transact<T>(run: (db: DatabaseInterface) => Promise<T>): Promise<T> {\n return this.scope(() =>\n withEmbeddedWriteTransaction(\n this.db,\n isEmbeddedDatabase(this.db),\n async (db) => {\n if (isPostgresDatabase(db))\n await db.query(\n 'SELECT pg_advisory_xact_lock(hashtextextended($1, 0))',\n JSON.stringify([\n 'smrt.rollup',\n this.actor.tenantId,\n this.actor.profileId,\n ]),\n );\n if (this.options.attendance && isPostgresDatabase(db))\n await db.query(\n 'SELECT pg_advisory_xact_lock(hashtextextended($1, 0))',\n JSON.stringify([\n 'smrt.attendance',\n this.actor.tenantId,\n this.actor.profileId,\n ]),\n );\n return run(db);\n },\n true,\n ),\n );\n }\n private async card(\n db: DatabaseInterface,\n id: string,\n ): Promise<PeriodTimecard> {\n const collection = await PeriodTimecardCollection.create({ db });\n const [card] = await collection.list({\n where: { ...this.actor, id },\n limit: 1,\n });\n if (!card) throw new Error('Timecard not found in actor scope.');\n return card;\n }\n /** Scoped read on the root executor, including after transaction completion. */\n async get(id: string): Promise<PeriodTimecard> {\n return this.scope(() => this.card(this.db, id));\n }\n /** Rebuild one open card from a consistent transaction-bound source snapshot. */\n async rollup(at: Date): Promise<PeriodTimecard> {\n at = new Date(instant(at));\n const id = await this.transact(async (db) => {\n const period = validatePeriod(\n await this.resolver.periodFor(new Date(at), this.actor),\n at,\n );\n const id = identity([\n 'smrt.period',\n this.actor.tenantId,\n this.actor.profileId,\n period.startsAt.toISOString(),\n ]);\n const collection = await PeriodTimecardCollection.create({ db });\n const [prior] = await collection.list({\n where: { ...this.actor, id },\n limit: 1,\n });\n if (prior?.status === 'confirmed')\n throw new Error(\n 'Confirmed timecards are immutable; append an adjustment.',\n );\n // A policy change must not create overlapping cards for the same person.\n for (const other of await collection.list({ where: { ...this.actor } })) {\n if (\n other.id !== id &&\n other.startsAt < period.endsAt &&\n other.endsAt > period.startsAt\n )\n throw new Error('Resolver period overlaps an existing timecard.');\n }\n if (prior?.status === 'submitted')\n throw new Error('Submitted timecards are frozen; reopen first.');\n let sources: TimecardSource[] = [];\n let linkedEntryIds = new Set<string>();\n if (this.options.attendance) {\n const { attendanceSources } = await import('./attendance.js');\n ({ sources, linkedEntryIds } = await attendanceSources(\n db,\n this.actor,\n period,\n this.options.attribution,\n ));\n }\n const entries = await ServiceTimeEntryCollection.create({ db });\n const evidence = await entries.list({\n where: {\n tenantId: this.actor.tenantId,\n participantProfileId: this.actor.profileId,\n participantKind: 'human',\n },\n });\n const byId = new Map(evidence.map((entry) => [String(entry.id), entry]));\n const approvedRoots = new Set<string>();\n for (const entry of evidence) {\n if (entry.status !== 'approved') continue;\n let linked = linkedEntryIds.has(String(entry.id));\n let ancestor = entry;\n const seen = new Set([String(entry.id)]);\n while (ancestor.correctionOfId) {\n const parentId = ancestor.correctionOfId;\n if (seen.has(parentId))\n throw new Error('Cyclic evidence correction chain.');\n seen.add(parentId);\n const parent = byId.get(parentId);\n if (!parent || parent.status !== 'corrected')\n throw new Error(\n 'Correction requires corrected evidence in the same actor scope.',\n );\n linked ||= linkedEntryIds.has(parentId);\n ancestor = parent;\n }\n const rootId = String(ancestor.id);\n if (approvedRoots.has(rootId))\n throw new Error('Ambiguous approved evidence correction chain.');\n approvedRoots.add(rootId);\n if (linked) continue;\n // Duration-only manual evidence belongs to its approval instant. A\n // bounded interval allocates its actual duration by elapsed fraction.\n const start = entry.startedAt ?? entry.approvedAt;\n if (!start)\n throw new Error(\n 'Approved evidence needs a start or approval timestamp.',\n );\n // Hours-only sources are unbounded and attributed at approval. An\n // unsupported quantity in another period must not block this one.\n if (\n entry.durationSeconds === null &&\n (instant(start) < period.startsAt.getTime() ||\n instant(start) >= period.endsAt.getTime())\n )\n continue;\n const end = entry.startedAt && entry.endedAt ? entry.endedAt : start;\n const source = attributeSource(\n {\n kind: 'entry',\n id: String(entry.id),\n startsAt: new Date(instant(start)).toISOString(),\n endsAt: new Date(instant(end)).toISOString(),\n seconds: integer(entry.requireDurationSeconds()),\n },\n period,\n this.options.attribution ?? 'clip',\n instant(start),\n );\n if (source?.seconds) sources.push(source);\n }\n sources.sort(\n (a, b) =>\n a.startsAt.localeCompare(b.startsAt) ||\n a.kind.localeCompare(b.kind) ||\n a.id.localeCompare(b.id),\n );\n const totalSeconds = sources.reduce(\n (sum, source) => integer(sum + source.seconds),\n 0,\n );\n // Resolver receives detached snapshots so it cannot alter persisted evidence.\n const classified = await this.resolver.classify(\n structuredClone(period),\n structuredClone(sources),\n this.actor,\n );\n const regularSeconds = integer(classified?.regularSeconds);\n const overtimeSeconds = integer(classified?.overtimeSeconds);\n if (integer(regularSeconds + overtimeSeconds) !== totalSeconds)\n throw new Error('Resolver classification must conserve total seconds.');\n const card =\n prior ??\n (await new PeriodTimecard({ db, id, _skipLoad: true }).initialize());\n if (!prior) card.requireInsertOnSave();\n Object.assign(card, this.actor, {\n startsAt: period.startsAt,\n endsAt: period.endsAt,\n timezone: period.timezone,\n rulesVersion: period.version,\n rulesSnapshot: JSON.stringify(period.rules),\n sourceSnapshot: JSON.stringify(sources),\n regularSeconds,\n overtimeSeconds,\n totalSeconds,\n });\n await persistRollup(card);\n return id;\n });\n return this.get(id);\n }\n /**\n * Optional step: freeze an open card for review (open -> submitted). The\n * actor is the submitter; repeating it on a submitted card is idempotent.\n */\n async submit(id: string): Promise<PeriodTimecard> {\n id = this.identity(id);\n await this.transact(async (db) => {\n const card = await this.card(db, id);\n if (card.status === 'submitted') return;\n if (card.status !== 'open')\n throw new Error('Only an open timecard can be submitted.');\n card.status = 'submitted';\n card.submittedAt = new Date();\n card.submittedByProfileId = this.options.actorProfileId!;\n await persistRollup(card);\n });\n return this.get(id);\n }\n /**\n * Confirm the current snapshot; repeated confirmation is idempotent. With\n * `requireSubmission` the card must be submitted first.\n */\n async confirm(id: string): Promise<PeriodTimecard> {\n await this.transact(async (db) => {\n const card = await this.card(db, id);\n if (card.status === 'confirmed') return;\n if (card.status === 'open' && this.options.requireSubmission)\n throw new Error('Timecards must be submitted before confirmation.');\n card.status = 'confirmed';\n card.confirmedAt = new Date();\n card.confirmedByProfileId = this.options.actorProfileId!;\n await persistRollup(card);\n });\n return this.get(id);\n }\n /**\n * Return a submitted or confirmed card to `open` so its sources can be\n * corrected and `rollup()` run again, then confirmed anew. Needs a reason and\n * an operation key; appends a `TimecardReopening` with the totals the card had\n * (base, adjustments, effective). Adjustments stay on the ledger and keep\n * counting in `totals()`: after a re-roll, offset them with a new adjustment\n * if the correction made them redundant. The consumer authorizes the actor\n * (give reopening its own permission); a retry with the same key and reason\n * is a no-op.\n */\n async reopen(\n id: string,\n input: TimecardReopenInput,\n ): Promise<PeriodTimecard> {\n id = this.identity(id);\n input = { operationId: input.operationId, reason: input.reason };\n if (!input.operationId?.trim() || !input.reason?.trim())\n throw new Error('Reopening requires an operationId and reason.');\n const reopeningId = identity([\n 'smrt.reopening',\n this.actor.tenantId,\n this.actor.profileId,\n id,\n input.operationId,\n ]);\n await this.transact(async (db) => {\n const ledger = await TimecardReopeningCollection.create({ db });\n const [done] = await ledger.list({\n where: { ...this.actor, timecardId: id, id: reopeningId },\n limit: 1,\n });\n if (done) {\n if (\n done.reason !== input.reason ||\n done.actorProfileId !== this.options.actorProfileId\n )\n throw new Error(\n 'Reopening operationId reused with a different payload.',\n );\n return;\n }\n const card = await this.card(db, id);\n if (card.status === 'open')\n throw new Error(\n 'Only a submitted or confirmed timecard can be reopened.',\n );\n const adjustments = await (\n await TimecardAdjustmentCollection.create({ db })\n ).list({ where: { ...this.actor, timecardId: id } });\n const regular = adjustedSeconds(\n card.regularSeconds,\n adjustments,\n 'regularSeconds',\n );\n const overtime = adjustedSeconds(\n card.overtimeSeconds,\n adjustments,\n 'overtimeSeconds',\n );\n const row = await new TimecardReopening({\n db,\n id: reopeningId,\n _skipLoad: true,\n }).initialize();\n Object.assign(row, this.actor, {\n timecardId: id,\n actorProfileId: this.options.actorProfileId!,\n operationId: input.operationId,\n fromStatus: card.status,\n reason: input.reason,\n previousState: JSON.stringify({\n rulesVersion: card.rulesVersion,\n base: {\n regularSeconds: card.regularSeconds,\n overtimeSeconds: card.overtimeSeconds,\n totalSeconds: card.totalSeconds,\n },\n adjustments: {\n count: adjustments.length,\n regularSeconds: integer(regular - card.regularSeconds, true),\n overtimeSeconds: integer(overtime - card.overtimeSeconds, true),\n },\n effective: {\n regularSeconds: regular,\n overtimeSeconds: overtime,\n totalSeconds: integer(regular + overtime),\n },\n submittedAt: card.submittedAt?.toISOString() ?? null,\n submittedByProfileId: card.submittedByProfileId,\n confirmedAt: card.confirmedAt?.toISOString() ?? null,\n confirmedByProfileId: card.confirmedByProfileId,\n }),\n });\n await persistRollup(row);\n card.status = 'open';\n card.submittedAt = null;\n card.submittedByProfileId = null;\n card.confirmedAt = null;\n card.confirmedByProfileId = null;\n await persistReopen(card);\n });\n return this.get(id);\n }\n /** The append-only reopening history of one card, oldest first. */\n async reopenings(id: string): Promise<TimecardReopening[]> {\n id = this.identity(id);\n return this.scope(async () => {\n await this.card(this.db, id);\n return (await TimecardReopeningCollection.create({ db: this.db })).list({\n where: { ...this.actor, timecardId: id },\n orderBy: 'created_at ASC',\n });\n });\n }\n /** Append a correction. An operation key retry must have exactly the same payload. */\n async adjust(\n id: string,\n input: TimecardAdjustmentInput,\n ): Promise<TimecardAdjustment> {\n id = this.identity(id);\n input = {\n operationId: input.operationId,\n reason: input.reason,\n regularSeconds: input.regularSeconds,\n overtimeSeconds: input.overtimeSeconds,\n };\n integer(input.regularSeconds, true);\n integer(input.overtimeSeconds, true);\n if (!input.operationId?.trim() || !input.reason?.trim())\n throw new Error('Adjustment requires an operationId and reason.');\n const adjustmentId = identity([\n 'smrt.adjustment',\n this.actor.tenantId,\n this.actor.profileId,\n id,\n input.operationId,\n ]);\n await this.transact(async (db) => {\n const card = await this.card(db, id);\n if (card.status !== 'confirmed')\n throw new Error('Adjustments require a confirmed timecard.');\n const collection = await TimecardAdjustmentCollection.create({ db });\n const rows = await collection.list({\n where: { ...this.actor, timecardId: id },\n });\n const prior = rows.find((row) => row.id === adjustmentId);\n if (prior) {\n if (\n prior.regularSeconds !== input.regularSeconds ||\n prior.overtimeSeconds !== input.overtimeSeconds ||\n prior.reason !== input.reason ||\n prior.actorProfileId !== this.options.actorProfileId\n )\n throw new Error(\n 'Adjustment operationId reused with a different payload.',\n );\n return;\n }\n const regular = adjustedSeconds(\n card.regularSeconds,\n rows,\n 'regularSeconds',\n );\n const overtime = adjustedSeconds(\n card.overtimeSeconds,\n rows,\n 'overtimeSeconds',\n );\n integer(\n integer(regular + input.regularSeconds) +\n integer(overtime + input.overtimeSeconds),\n );\n const adjustment = await new TimecardAdjustment({\n db,\n id: adjustmentId,\n _skipLoad: true,\n }).initialize();\n Object.assign(adjustment, this.actor, {\n timecardId: id,\n actorProfileId: this.options.actorProfileId!,\n ...input,\n });\n await persistRollup(adjustment);\n });\n return this.scope(async () => {\n const collection = await TimecardAdjustmentCollection.create({\n db: this.db,\n });\n const [row] = await collection.list({\n where: { ...this.actor, id: adjustmentId },\n limit: 1,\n });\n if (!row) throw new Error('Committed adjustment disappeared.');\n return row;\n });\n }\n /** Effective totals combine the frozen base with append-only corrections. */\n async totals(id: string): Promise<{\n regularSeconds: number;\n overtimeSeconds: number;\n totalSeconds: number;\n }> {\n return this.transact(async (db) => {\n const card = await this.card(db, id);\n const collection = await TimecardAdjustmentCollection.create({ db });\n const rows = await collection.list({\n where: { ...this.actor, timecardId: id },\n });\n const regularSeconds = adjustedSeconds(\n card.regularSeconds,\n rows,\n 'regularSeconds',\n );\n const overtimeSeconds = adjustedSeconds(\n card.overtimeSeconds,\n rows,\n 'overtimeSeconds',\n );\n return {\n regularSeconds,\n overtimeSeconds,\n totalSeconds: integer(regularSeconds + overtimeSeconds),\n };\n });\n }\n}\n"],"mappings":";;;;;AAGA,IAAM,4BAAY,IAAI,QAAoB;AACnC,SAAS,kBAAkB,QAA0B;CAC1D,IAAI,CAAC,UAAU,IAAI,MAAM,GACvB,MAAM,IAAI,MACR,0EACF;AACJ;AACA,eAAsB,cACpB,QACY;CACZ,UAAU,IAAI,MAAM;CACpB,IAAI;EACF,MAAM,OAAO,KAAK;EAClB,OAAO;CACT,UAAE;EACA,UAAU,OAAO,MAAM;CACzB;AACF;AAGA,IAAM,4BAAY,IAAI,QAAoB;AACnC,SAAS,cAAc,QAA6B;CACzD,OAAO,UAAU,IAAI,MAAM;AAC7B;AACA,eAAsB,cACpB,QACY;CACZ,UAAU,IAAI,MAAM;CACpB,IAAI;EACF,OAAO,MAAM,cAAc,MAAM;CACnC,UAAE;EACA,UAAU,OAAO,MAAM;CACzB;AACF;;;;;;;;;;;;;;;;;;ACXO,IAAM,iBAAN,cAA6B,WAAW;CACjC,WAAW;CAIvB,YAAY;CACZ,2BAAiB,IAAI,KAAK,CAAC;CAC3B,yBAAe,IAAI,KAAK,CAAC;CACzB,WAAW;CACX,eAAe;CACU,gBAAgB;CAChB,iBAAiB;CAC1C,iBAAiB;CACjB,kBAAkB;CAClB,eAAe;CACU,SAAyB;;CAElD,cAA2B;CAK3B,uBAAsC;CACtC,cAA2B;CAK3B,uBAAsC;;CAGtC,aAA+B;EAC7B,MAAM,SAAkB,KAAK,MAAM,KAAK,cAAc;EACtD,IAAI,CAAC,MAAM,QAAQ,MAAM,GACvB,MAAM,IAAI,MAAM,mCAAmC;EACrD,OAAO;CACT;;CAEA,WAAoC;EAClC,MAAM,SAAkB,KAAK,MAAM,KAAK,aAAa;EACrD,IAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAC/D,MAAM,IAAI,MAAM,kCAAkC;EACpD,OAAO;CACT;CACA,MAAe,KAAK,SAA0C;EAC5D,kBAAkB,IAAI;EACtB,MAAM,QAAQ,KAAK,KACf,MAAM,KAAK,GAAG,IAAI,KAAK,WAAW,EAAE,IAAI,KAAK,GAAG,CAAC,IACjD;EACJ,IAAI,OAAO,WAAW,eAAe,CAAC,cAAc,IAAI,GACtD,MAAM,IAAI,MACR,0DACF;EACF,IAAI,OAAO,WAAW,eAAe,CAAC,cAAc,IAAI;OAElD,KAAK,WAAW,eAAe,KAAK,WAAW,aACjD,MAAM,IAAI,MACR,wDACF;EAAA;EAEJ,IACE,UACC,MAAM,cAAc,KAAK,YACxB,MAAM,eAAe,KAAK,aAC1B,IAAI,KAAK,MAAM,SAAmB,CAAA,CAAE,QAAQ,MAC1C,KAAK,SAAS,QAAQ,IAE1B,MAAM,IAAI,MAAM,iCAAiC;EACnD,OAAQ,MAAM,MAAM,KAAK,OAAO;CAClC;CACA,MAAe,SAAwB;EACrC,MAAM,IAAI,MAAM,qCAAqC;CACvD;AACF;AAzEiD,cAApC,gBAA2C,mBAAkB,gCAAA;AAC5D,gBAAA,CAAX,SAAS,CAAA,GADC,eACC,WAAA,YAAA,CAAA;AAIZ,gBAAA,CAHC,gBAAgB,wCAAwC,EACvD,UAAU,WACZ,CAAC,CAAA,GAJU,eAKX,WAAA,aAAA,CAAA;AAKyB,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAVZ,eAUc,WAAA,iBAAA,CAAA;AACA,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAXZ,eAWc,WAAA,kBAAA,CAAA;AAIA,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAfZ,eAec,WAAA,UAAA,CAAA;AAOzB,gBAAA,CAJC,gBAAgB,wCAAwC;CACvD,UAAU;CACV,UAAU;AACZ,CAAC,CAAA,GArBU,eAsBX,WAAA,wBAAA,CAAA;AAMA,gBAAA,CAJC,gBAAgB,wCAAwC;CACvD,UAAU;CACV,UAAU;AACZ,CAAC,CAAA,GA3BU,eA4BX,WAAA,wBAAA,CAAA;AA5BW,iBAAN,gBAAA,CARN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,WAAW;CACX,WAAW;CACX,KAAK,EAAE,SAAS,CAAC,EAAE;CACnB,KAAK,EAAE,SAAS,CAAC,EAAE;CACnB,KAAK,EAAE,SAAS,CAAC,EAAE;AACrB,CAAC,CAAA,GACY,cAAA;AAoFN,IAAM,qBAAN,cAAiC,WAAW;CACrC,WAAW;CAC+B,aAAa;CAInE,YAAY;CAIZ,iBAAiB;CACjB,cAAc;CACd,iBAAiB;CACjB,kBAAkB;CAClB,SAAS;CACT,MAAe,KAAK,SAA0C;EAC5D,kBAAkB,IAAI;EACtB,IAAI,KAAK,MAAO,MAAM,KAAK,GAAG,IAAI,KAAK,WAAW,EAAE,IAAI,KAAK,GAAG,CAAC,GAC/D,MAAM,IAAI,MAAM,uCAAuC;EACzD,KAAK,oBAAoB;EACzB,OAAQ,MAAM,MAAM,KAAK,OAAO;CAClC;CACA,MAAe,SAAwB;EACrC,MAAM,IAAI,MAAM,yCAAyC;CAC3D;AACF;AAzBqD,cAAxC,oBAA+C,mBAAkB,gCAAA;AAChE,gBAAA,CAAX,SAAS,CAAA,GADC,mBACC,WAAA,YAAA,CAAA;AAC0C,gBAAA,CAArD,WAAW,gBAAgB,EAAE,UAAU,WAAW,CAAC,CAAA,GAFzC,mBAE2C,WAAA,cAAA,CAAA;AAItD,gBAAA,CAHC,gBAAgB,wCAAwC,EACvD,UAAU,WACZ,CAAC,CAAA,GALU,mBAMX,WAAA,aAAA,CAAA;AAIA,gBAAA,CAHC,gBAAgB,wCAAwC,EACvD,UAAU,WACZ,CAAC,CAAA,GATU,mBAUX,WAAA,kBAAA,CAAA;AAVW,qBAAN,gBAAA,CARN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,WAAW;CACX,WAAW;CACX,KAAK,EAAE,SAAS,CAAC,EAAE;CACnB,KAAK,EAAE,SAAS,CAAC,EAAE;CACnB,KAAK,EAAE,SAAS,CAAC,EAAE;AACrB,CAAC,CAAA,GACY,kBAAA;AA8CN,IAAM,oBAAN,cAAgC,WAAW;CACpC,WAAW;CAC+B,aAAa;CAInE,YAAY;CAIZ,iBAAiB;CACjB,cAAc;CACW,aAA6B;CAC7B,SAAS;CAET,gBAAgB;;CAEzC,cAA8C;EAC5C,MAAM,SAAkB,KAAK,MAAM,KAAK,aAAa;EACrD,IAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAC/D,MAAM,IAAI,MAAM,sCAAsC;EACxD,OAAO;CACT;CACA,MAAe,KAAK,SAA0C;EAC5D,kBAAkB,IAAI;EACtB,IAAI,KAAK,MAAO,MAAM,KAAK,GAAG,IAAI,KAAK,WAAW,EAAE,IAAI,KAAK,GAAG,CAAC,GAC/D,MAAM,IAAI,MAAM,sCAAsC;EACxD,IAAI,CAAC,KAAK,QAAQ,KAAK,KAAK,CAAC,KAAK,aAAa,KAAK,GAClD,MAAM,IAAI,MAAM,+CAA+C;EACjE,KAAK,oBAAoB;EACzB,OAAQ,MAAM,MAAM,KAAK,OAAO;CAClC;CACA,MAAe,SAAwB;EACrC,MAAM,IAAI,MAAM,wCAAwC;CAC1D;AACF;AAnCoD,cAAvC,mBAA8C,mBAAkB,gCAAA;AAC/D,gBAAA,CAAX,SAAS,CAAA,GADC,kBACC,WAAA,YAAA,CAAA;AAC0C,gBAAA,CAArD,WAAW,gBAAgB,EAAE,UAAU,WAAW,CAAC,CAAA,GAFzC,kBAE2C,WAAA,cAAA,CAAA;AAItD,gBAAA,CAHC,gBAAgB,wCAAwC,EACvD,UAAU,WACZ,CAAC,CAAA,GALU,kBAMX,WAAA,aAAA,CAAA;AAIA,gBAAA,CAHC,gBAAgB,wCAAwC,EACvD,UAAU,WACZ,CAAC,CAAA,GATU,kBAUX,WAAA,kBAAA,CAAA;AAEyB,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAZZ,kBAYc,WAAA,cAAA,CAAA;AACA,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAbZ,kBAac,WAAA,UAAA,CAAA;AAEA,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAfZ,kBAec,WAAA,iBAAA,CAAA;AAfd,oBAAN,gBAAA,CAdN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,WAAW;CACX,WAAW;CACX,SAAS,CACP;EACE,MAAM;EACN,SAAS,CAAC,YAAY,YAAY;CACpC,CACF;CACA,KAAK,EAAE,SAAS,CAAC,EAAE;CACnB,KAAK,EAAE,SAAS,CAAC,EAAE;CACnB,KAAK,EAAE,SAAS,CAAC,EAAE;AACrB,CAAC,CAAA,GACY,iBAAA;AAqCN,IAAM,2BAAN,cAAuC,eAA+B;CAC3E,OAAgB,aAAa;AAC/B;AAEO,IAAM,+BAAN,cAA2C,eAAmC;CACnF,OAAgB,aAAa;AAC/B;AAEO,IAAM,8BAAN,cAA0C,eAAkC;CACjF,OAAgB,aAAa;AAC/B;;;AC9LO,IAAM,yBAAoD,OAC/D,IACA,SACA,cACG;CASH,MAAM,OANJ,OAAM,MAFY,yBAAyB,OAAO,EAAE,IAAI,GAAG,GAAG,CAAC,EAAA,CAEnD,KAAK,EACf,OAAO;EAAE,UAAU,QAAQ;EAAU,WAAW,QAAQ;CAAU,EACpE,CAAC,EAAA,CACD,QACC,SAAS,KAAK,WAAW,eAAe,KAAK,WAAW,WAE/C,CAAA,CAAO,MAAM,SACvB,UAAU,MACP,MACC,EAAE,SAAS,QAAQ,IAAI,KAAK,OAAO,QAAQ,KAC3C,KAAK,SAAS,QAAQ,IAAI,EAAE,OAAO,QAAQ,CAC/C,CACF;CACA,OAAO,MACH,uBAAuB,IAAI,OAAM,YAAa,IAAI,GAAE,kCACpD;AACN;;;AC7BO,SAAS,QAAQ,OAAe,SAAS,OAAe;CAC7D,IAAI,CAAC,OAAO,cAAc,KAAK,KAAM,CAAC,UAAU,QAAQ,GACtD,MAAM,IAAI,MAAM,yDAAyD;CAC3E,OAAO;AACT;AACO,SAAS,QAAQ,OAAqB;CAC3C,IAAI,EAAE,iBAAiB,SAAS,CAAC,OAAO,SAAS,MAAM,QAAQ,CAAC,GAC9D,MAAM,IAAI,MAAM,0CAA0C;CAC5D,OAAO,MAAM,QAAQ;AACvB;AACO,SAAS,eACd,OACA,IACgB;CAChB,IACE,CAAC,SACD,QAAQ,MAAM,QAAQ,IAAI,QAAQ,EAAE,KACpC,QAAQ,MAAM,MAAM,KAAK,QAAQ,EAAE,KACnC,QAAQ,MAAM,QAAQ,KAAK,QAAQ,MAAM,MAAM,GAE/C,MAAM,IAAI,MAAM,qDAAqD;CACvE,IAAI,CAAC,MAAM,YAAY,CAAC,MAAM,SAAS,KAAK,GAC1C,MAAM,IAAI,MAAM,qDAAqD;CACvE,IAAI,KAAK,eAAe,MAAM,EAAE,UAAU,MAAM,SAAS,CAAC;CAC1D,IACE,CAAC,MAAM,SACP,OAAO,MAAM,UAAU,YACvB,MAAM,QAAQ,MAAM,KAAK,GAEzB,MAAM,IAAI,MAAM,uCAAuC;CAEzD,MAAM,OAAO,KAAK,UAAU,MAAM,QAAQ,MAAM,SAAS;EACvD,IACE,OAAO,SAAS,cAChB,OAAO,SAAS,eAChB,OAAO,SAAS,YACf,OAAO,SAAS,YAAY,CAAC,OAAO,SAAS,IAAI,GAElD,MAAM,IAAI,MAAM,2CAA2C;EAC7D,OAAO;CACT,CAAC;CACD,MAAM,QAAiB,KAAK,MAAM,IAAI;CACtC,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAC5D,MAAM,IAAI,MAAM,iDAAiD;CACnE,OAAO;EACL,UAAU,IAAI,KAAK,MAAM,QAAQ;EACjC,QAAQ,IAAI,KAAK,MAAM,MAAM;EAC7B,UAAU,MAAM;EAChB,SAAS,MAAM;EACf;CACF;AACF;AAEO,SAAS,WACd,QACA,QACuB;CACvB,MAAM,QAAQ,QAAQ,IAAI,KAAK,OAAO,QAAQ,CAAC;CAC/C,MAAM,MAAM,QAAQ,IAAI,KAAK,OAAO,MAAM,CAAC;CAC3C,QAAQ,OAAO,OAAO;CACtB,IAAI,MAAM,OAAO,MAAM,IAAI,MAAM,0BAA0B;CAC3D,MAAM,OAAO,KAAK,IAAI,OAAO,OAAO,SAAS,QAAQ,CAAC;CACtD,MAAM,QAAQ,KAAK,IAAI,KAAK,OAAO,OAAO,QAAQ,CAAC;CACnD,IAAI,UAAU,KACZ,OAAO,SAAS,OAAO,SAAS,QAAQ,KAAK,QAAQ,OAAO,OAAO,QAAQ,IACvE,EAAE,GAAG,OAAO,IACZ;CACN,IAAI,QAAQ,OAAO,OAAO;CAC1B,MAAM,OAAO,QAAQ,MAAM,KAAK;CAChC,MAAM,UAAU,YACd,OAAQ,OAAO,OAAO,OAAO,IAAI,OAAO,QAAQ,OAAO,CAAC,IAAK,OAAO,IAAI,CAAC;CAC3E,MAAM,UAAU,OAAO,QAAQ,KAAK,IAAI,OAAO,OAAO,KAAK;CAC3D,OAAO;EACL,GAAG;EACH,UAAU,IAAI,KAAK,IAAI,CAAA,CAAE,YAAY;EACrC,QAAQ,IAAI,KAAK,KAAK,CAAA,CAAE,YAAY;EACpC,SAAS,QAAQ,OAAO;CAC1B;AACF;AAOO,SAAS,gBACd,QACA,QACA,aACA,YACuB;CACvB,IAAI,gBAAgB,QAAQ,OAAO,WAAW,QAAQ,MAAM;CAC5D,QAAQ,OAAO,OAAO;CACtB,IAAI,QAAQ,IAAI,KAAK,OAAO,MAAM,CAAC,IAAI,QAAQ,IAAI,KAAK,OAAO,QAAQ,CAAC,GACtE,MAAM,IAAI,MAAM,0BAA0B;CAC5C,OAAO,cAAc,OAAO,SAAS,QAAQ,KAC3C,aAAa,OAAO,OAAO,QAAQ,IACjC,EAAE,GAAG,OAAO,IACZ;AACN;;;ACxEA,SAAS,SAAS,OAAyB;CACzC,MAAM,QAAQ,WAAW,QAAQ,CAAA,CAAE,OAAO,KAAK,UAAU,KAAK,CAAC,CAAA,CAAE,OAAO;CACxE,MAAM,KAAM,MAAM,KAAK,KAAM;CAC7B,MAAM,KAAM,MAAM,KAAK,KAAM;CAC7B,MAAM,MAAM,MAAM,SAAS,GAAG,EAAE,CAAA,CAAE,SAAS,KAAK;CAChD,OAAO,GAAG,IAAI,MAAM,GAAG,CAAC,EAAC,GAAI,IAAI,MAAM,GAAG,EAAE,EAAC,GAAI,IAAI,MAAM,IAAI,EAAE,EAAC,GAAI,IAAI,MAAM,IAAI,EAAE,EAAC,GAAI,IAAI,MAAM,EAAE;AACzG;AAEA,SAAS,gBACP,MACA,MACA,OACQ;CACR,MAAM,QAAQ,KAAK,QAChB,KAAK,QAAQ,MAAM,OAAO,QAAQ,IAAI,QAAQ,IAAI,CAAC,GACpD,OAAO,QAAQ,IAAI,CAAC,CACtB;CACA,OAAO,QAAQ,OAAO,KAAK,CAAC;AAC9B;AAOO,IAAM,sBAAN,MAA0B;CAC/B,YACmB,IACA,OACA,UACA,UAA+B,CAAC,GACjD;EAJiB,KAAA,KAAA;EACA,KAAA,QAAA;EACA,KAAA,WAAA;EACA,KAAA,UAAA;EAEjB,IAAI,CAAC,MAAM,UAAU,KAAK,KAAK,CAAC,MAAM,WAAW,KAAK,GACpD,MAAM,IAAI,MAAM,mDAAmD;EACrE,KAAK,QAAQ,OAAO,OAAO;GACzB,UAAU,KAAK,SAAS,MAAM,QAAQ;GACtC,WAAW,KAAK,SAAS,MAAM,SAAS;EAC1C,CAAC;EACD,IACE,QAAQ,gBAAgB,KAAA,KACxB,QAAQ,gBAAgB,UACxB,QAAQ,gBAAgB,SAExB,MAAM,IAAI,MAAM,wCAAwC;EAC1D,IAAI,QAAQ,mBAAmB,KAAA,KAAa,CAAC,QAAQ,eAAe,KAAK,GACvE,MAAM,IAAI,MAAM,kDAAkD;EACpE,KAAK,UAAU;GACb,YAAY,QAAQ;GACpB,aAAa,QAAQ,eAAe;GACpC,mBAAmB,QAAQ,sBAAsB;GACjD,gBAAgB,KAAK,SACnB,QAAQ,kBAAkB,KAAK,MAAM,SACvC;EACF;CACF;CA3BmB;CACA;CACA;CACA;CA0BX,SAAS,OAAuB;EACtC,IAAI,CAAC,mBAAmB,KAAK,EAAE,GAAG,OAAO;EACzC,MAAM,MAAM,MAAM,QAAQ,UAAU,EAAE,CAAA,CAAE,YAAY;EACpD,IAAI,CAAC,iBAAiB,KAAK,GAAG,GAC5B,MAAM,IAAI,MAAM,+CAA+C;EACjE,OAAO,GAAG,IAAI,MAAM,GAAG,CAAC,EAAC,GAAI,IAAI,MAAM,GAAG,EAAE,EAAC,GAAI,IAAI,MAAM,IAAI,EAAE,EAAC,GAAI,IAAI,MAAM,IAAI,EAAE,EAAC,GAAI,IAAI,MAAM,EAAE;CACzG;CACQ,MAAS,KAAmC;EAClD,MAAM,SAAS,YAAY;EAC3B,IAAI,UAAU,KAAK,SAAS,MAAM,MAAM,KAAK,MAAM,UACjD,MAAM,IAAI,MAAM,4CAA4C;EAC9D,OAAO,WAAW,EAAE,UAAU,KAAK,MAAM,SAAS,GAAG,GAAG;CAC1D;CACQ,SAAY,KAAwD;EAC1E,OAAO,KAAK,YACV,6BACE,KAAK,IACL,mBAAmB,KAAK,EAAE,GAC1B,OAAO,OAAO;GACZ,IAAI,mBAAmB,EAAE,GACvB,MAAM,GAAG,MACP,yDACA,KAAK,UAAU;IACb;IACA,KAAK,MAAM;IACX,KAAK,MAAM;GACb,CAAC,CACH;GACF,IAAI,KAAK,QAAQ,cAAc,mBAAmB,EAAE,GAClD,MAAM,GAAG,MACP,yDACA,KAAK,UAAU;IACb;IACA,KAAK,MAAM;IACX,KAAK,MAAM;GACb,CAAC,CACH;GACF,OAAO,IAAI,EAAE;EACf,GACA,IACF,CACF;CACF;CACA,MAAc,KACZ,IACA,IACyB;EAEzB,MAAM,CAAC,QAAQ,OAAM,MADI,yBAAyB,OAAO,EAAE,GAAG,CAAC,EAAA,CAC/B,KAAK;GACnC,OAAO;IAAE,GAAG,KAAK;IAAO;GAAG;GAC3B,OAAO;EACT,CAAC;EACD,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,oCAAoC;EAC/D,OAAO;CACT;;CAEA,MAAM,IAAI,IAAqC;EAC7C,OAAO,KAAK,YAAY,KAAK,KAAK,KAAK,IAAI,EAAE,CAAC;CAChD;;CAEA,MAAM,OAAO,IAAmC;EAC9C,KAAK,IAAI,KAAK,QAAQ,EAAE,CAAC;EACzB,MAAM,KAAK,MAAM,KAAK,SAAS,OAAO,OAAO;GAC3C,MAAM,SAAS,eACb,MAAM,KAAK,SAAS,UAAU,IAAI,KAAK,EAAE,GAAG,KAAK,KAAK,GACtD,EACF;GACA,MAAMA,MAAK,SAAS;IAClB;IACA,KAAK,MAAM;IACX,KAAK,MAAM;IACX,OAAO,SAAS,YAAY;GAC9B,CAAC;GACD,MAAM,aAAa,MAAM,yBAAyB,OAAO,EAAE,GAAG,CAAC;GAC/D,MAAM,CAAC,SAAS,MAAM,WAAW,KAAK;IACpC,OAAO;KAAE,GAAG,KAAK;KAAO,IAAAA;IAAG;IAC3B,OAAO;GACT,CAAC;GACD,IAAI,OAAO,WAAW,aACpB,MAAM,IAAI,MACR,0DACF;GAEF,KAAA,MAAW,SAAS,MAAM,WAAW,KAAK,EAAE,OAAO,EAAE,GAAG,KAAK,MAAM,EAAE,CAAC,GACpE,IACE,MAAM,OAAOA,OACb,MAAM,WAAW,OAAO,UACxB,MAAM,SAAS,OAAO,UAEtB,MAAM,IAAI,MAAM,gDAAgD;GAEpE,IAAI,OAAO,WAAW,aACpB,MAAM,IAAI,MAAM,+CAA+C;GACjE,IAAI,UAA4B,CAAC;GACjC,IAAI,iCAAiB,IAAI,IAAY;GACrC,IAAI,KAAK,QAAQ,YAAY;IAC3B,MAAM,EAAE,sBAAsB,MAAM,OAAO;IAC3C,CAAC,CAAE,SAAS,kBAAmB,MAAM,kBACnC,IACA,KAAK,OACL,QACA,KAAK,QAAQ,WACf;GACF;GAEA,MAAM,WAAW,OAAM,MADD,2BAA2B,OAAO,EAAE,GAAG,CAAC,EAAA,CAC/B,KAAK,EAClC,OAAO;IACL,UAAU,KAAK,MAAM;IACrB,sBAAsB,KAAK,MAAM;IACjC,iBAAiB;GACnB,EACF,CAAC;GACD,MAAM,OAAO,IAAI,IAAI,SAAS,KAAK,UAAU,CAAC,OAAO,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC;GACvE,MAAM,gCAAgB,IAAI,IAAY;GACtC,KAAA,MAAW,SAAS,UAAU;IAC5B,IAAI,MAAM,WAAW,YAAY;IACjC,IAAI,SAAS,eAAe,IAAI,OAAO,MAAM,EAAE,CAAC;IAChD,IAAI,WAAW;IACf,MAAM,uBAAO,IAAI,IAAI,CAAC,OAAO,MAAM,EAAE,CAAC,CAAC;IACvC,OAAO,SAAS,gBAAgB;KAC9B,MAAM,WAAW,SAAS;KAC1B,IAAI,KAAK,IAAI,QAAQ,GACnB,MAAM,IAAI,MAAM,mCAAmC;KACrD,KAAK,IAAI,QAAQ;KACjB,MAAM,SAAS,KAAK,IAAI,QAAQ;KAChC,IAAI,CAAC,UAAU,OAAO,WAAW,aAC/B,MAAM,IAAI,MACR,iEACF;KACF,WAAW,eAAe,IAAI,QAAQ;KACtC,WAAW;IACb;IACA,MAAM,SAAS,OAAO,SAAS,EAAE;IACjC,IAAI,cAAc,IAAI,MAAM,GAC1B,MAAM,IAAI,MAAM,+CAA+C;IACjE,cAAc,IAAI,MAAM;IACxB,IAAI,QAAQ;IAGZ,MAAM,QAAQ,MAAM,aAAa,MAAM;IACvC,IAAI,CAAC,OACH,MAAM,IAAI,MACR,wDACF;IAGF,IACE,MAAM,oBAAoB,SACzB,QAAQ,KAAK,IAAI,OAAO,SAAS,QAAQ,KACxC,QAAQ,KAAK,KAAK,OAAO,OAAO,QAAQ,IAE1C;IACF,MAAM,MAAM,MAAM,aAAa,MAAM,UAAU,MAAM,UAAU;IAC/D,MAAM,SAAS,gBACb;KACE,MAAM;KACN,IAAI,OAAO,MAAM,EAAE;KACnB,UAAU,IAAI,KAAK,QAAQ,KAAK,CAAC,CAAA,CAAE,YAAY;KAC/C,QAAQ,IAAI,KAAK,QAAQ,GAAG,CAAC,CAAA,CAAE,YAAY;KAC3C,SAAS,QAAQ,MAAM,uBAAuB,CAAC;IACjD,GACA,QACA,KAAK,QAAQ,eAAe,QAC5B,QAAQ,KAAK,CACf;IACA,IAAI,QAAQ,SAAS,QAAQ,KAAK,MAAM;GAC1C;GACA,QAAQ,MACL,GAAG,MACF,EAAE,SAAS,cAAc,EAAE,QAAQ,KACnC,EAAE,KAAK,cAAc,EAAE,IAAI,KAC3B,EAAE,GAAG,cAAc,EAAE,EAAE,CAC3B;GACA,MAAM,eAAe,QAAQ,QAC1B,KAAK,WAAW,QAAQ,MAAM,OAAO,OAAO,GAC7C,CACF;GAEA,MAAM,aAAa,MAAM,KAAK,SAAS,SACrC,gBAAgB,MAAM,GACtB,gBAAgB,OAAO,GACvB,KAAK,KACP;GACA,MAAM,iBAAiB,QAAQ,YAAY,cAAc;GACzD,MAAM,kBAAkB,QAAQ,YAAY,eAAe;GAC3D,IAAI,QAAQ,iBAAiB,eAAe,MAAM,cAChD,MAAM,IAAI,MAAM,sDAAsD;GACxE,MAAM,OACJ,SACC,MAAM,IAAI,eAAe;IAAE;IAAI,IAAAA;IAAI,WAAW;GAAK,CAAC,CAAA,CAAE,WAAW;GACpE,IAAI,CAAC,OAAO,KAAK,oBAAoB;GACrC,OAAO,OAAO,MAAM,KAAK,OAAO;IAC9B,UAAU,OAAO;IACjB,QAAQ,OAAO;IACf,UAAU,OAAO;IACjB,cAAc,OAAO;IACrB,eAAe,KAAK,UAAU,OAAO,KAAK;IAC1C,gBAAgB,KAAK,UAAU,OAAO;IACtC;IACA;IACA;GACF,CAAC;GACD,MAAM,cAAc,IAAI;GACxB,OAAOA;EACT,CAAC;EACD,OAAO,KAAK,IAAI,EAAE;CACpB;;;;;CAKA,MAAM,OAAO,IAAqC;EAChD,KAAK,KAAK,SAAS,EAAE;EACrB,MAAM,KAAK,SAAS,OAAO,OAAO;GAChC,MAAM,OAAO,MAAM,KAAK,KAAK,IAAI,EAAE;GACnC,IAAI,KAAK,WAAW,aAAa;GACjC,IAAI,KAAK,WAAW,QAClB,MAAM,IAAI,MAAM,yCAAyC;GAC3D,KAAK,SAAS;GACd,KAAK,8BAAc,IAAI,KAAK;GAC5B,KAAK,uBAAuB,KAAK,QAAQ;GACzC,MAAM,cAAc,IAAI;EAC1B,CAAC;EACD,OAAO,KAAK,IAAI,EAAE;CACpB;;;;;CAKA,MAAM,QAAQ,IAAqC;EACjD,MAAM,KAAK,SAAS,OAAO,OAAO;GAChC,MAAM,OAAO,MAAM,KAAK,KAAK,IAAI,EAAE;GACnC,IAAI,KAAK,WAAW,aAAa;GACjC,IAAI,KAAK,WAAW,UAAU,KAAK,QAAQ,mBACzC,MAAM,IAAI,MAAM,kDAAkD;GACpE,KAAK,SAAS;GACd,KAAK,8BAAc,IAAI,KAAK;GAC5B,KAAK,uBAAuB,KAAK,QAAQ;GACzC,MAAM,cAAc,IAAI;EAC1B,CAAC;EACD,OAAO,KAAK,IAAI,EAAE;CACpB;;;;;;;;;;;CAWA,MAAM,OACJ,IACA,OACyB;EACzB,KAAK,KAAK,SAAS,EAAE;EACrB,QAAQ;GAAE,aAAa,MAAM;GAAa,QAAQ,MAAM;EAAO;EAC/D,IAAI,CAAC,MAAM,aAAa,KAAK,KAAK,CAAC,MAAM,QAAQ,KAAK,GACpD,MAAM,IAAI,MAAM,+CAA+C;EACjE,MAAM,cAAc,SAAS;GAC3B;GACA,KAAK,MAAM;GACX,KAAK,MAAM;GACX;GACA,MAAM;EACR,CAAC;EACD,MAAM,KAAK,SAAS,OAAO,OAAO;GAEhC,MAAM,CAAC,QAAQ,OAAM,MADA,4BAA4B,OAAO,EAAE,GAAG,CAAC,EAAA,CAClC,KAAK;IAC/B,OAAO;KAAE,GAAG,KAAK;KAAO,YAAY;KAAI,IAAI;IAAY;IACxD,OAAO;GACT,CAAC;GACD,IAAI,MAAM;IACR,IACE,KAAK,WAAW,MAAM,UACtB,KAAK,mBAAmB,KAAK,QAAQ,gBAErC,MAAM,IAAI,MACR,wDACF;IACF;GACF;GACA,MAAM,OAAO,MAAM,KAAK,KAAK,IAAI,EAAE;GACnC,IAAI,KAAK,WAAW,QAClB,MAAM,IAAI,MACR,yDACF;GACF,MAAM,cAAc,OAClB,MAAM,6BAA6B,OAAO,EAAE,GAAG,CAAC,EAAA,CAChD,KAAK,EAAE,OAAO;IAAE,GAAG,KAAK;IAAO,YAAY;GAAG,EAAE,CAAC;GACnD,MAAM,UAAU,gBACd,KAAK,gBACL,aACA,gBACF;GACA,MAAM,WAAW,gBACf,KAAK,iBACL,aACA,iBACF;GACA,MAAM,MAAM,MAAM,IAAI,kBAAkB;IACtC;IACA,IAAI;IACJ,WAAW;GACb,CAAC,CAAA,CAAE,WAAW;GACd,OAAO,OAAO,KAAK,KAAK,OAAO;IAC7B,YAAY;IACZ,gBAAgB,KAAK,QAAQ;IAC7B,aAAa,MAAM;IACnB,YAAY,KAAK;IACjB,QAAQ,MAAM;IACd,eAAe,KAAK,UAAU;KAC5B,cAAc,KAAK;KACnB,MAAM;MACJ,gBAAgB,KAAK;MACrB,iBAAiB,KAAK;MACtB,cAAc,KAAK;KACrB;KACA,aAAa;MACX,OAAO,YAAY;MACnB,gBAAgB,QAAQ,UAAU,KAAK,gBAAgB,IAAI;MAC3D,iBAAiB,QAAQ,WAAW,KAAK,iBAAiB,IAAI;KAChE;KACA,WAAW;MACT,gBAAgB;MAChB,iBAAiB;MACjB,cAAc,QAAQ,UAAU,QAAQ;KAC1C;KACA,aAAa,KAAK,aAAa,YAAY,KAAK;KAChD,sBAAsB,KAAK;KAC3B,aAAa,KAAK,aAAa,YAAY,KAAK;KAChD,sBAAsB,KAAK;IAC7B,CAAC;GACH,CAAC;GACD,MAAM,cAAc,GAAG;GACvB,KAAK,SAAS;GACd,KAAK,cAAc;GACnB,KAAK,uBAAuB;GAC5B,KAAK,cAAc;GACnB,KAAK,uBAAuB;GAC5B,MAAM,cAAc,IAAI;EAC1B,CAAC;EACD,OAAO,KAAK,IAAI,EAAE;CACpB;;CAEA,MAAM,WAAW,IAA0C;EACzD,KAAK,KAAK,SAAS,EAAE;EACrB,OAAO,KAAK,MAAM,YAAY;GAC5B,MAAM,KAAK,KAAK,KAAK,IAAI,EAAE;GAC3B,QAAQ,MAAM,4BAA4B,OAAO,EAAE,IAAI,KAAK,GAAG,CAAC,EAAA,CAAG,KAAK;IACtE,OAAO;KAAE,GAAG,KAAK;KAAO,YAAY;IAAG;IACvC,SAAS;GACX,CAAC;EACH,CAAC;CACH;;CAEA,MAAM,OACJ,IACA,OAC6B;EAC7B,KAAK,KAAK,SAAS,EAAE;EACrB,QAAQ;GACN,aAAa,MAAM;GACnB,QAAQ,MAAM;GACd,gBAAgB,MAAM;GACtB,iBAAiB,MAAM;EACzB;EACA,QAAQ,MAAM,gBAAgB,IAAI;EAClC,QAAQ,MAAM,iBAAiB,IAAI;EACnC,IAAI,CAAC,MAAM,aAAa,KAAK,KAAK,CAAC,MAAM,QAAQ,KAAK,GACpD,MAAM,IAAI,MAAM,gDAAgD;EAClE,MAAM,eAAe,SAAS;GAC5B;GACA,KAAK,MAAM;GACX,KAAK,MAAM;GACX;GACA,MAAM;EACR,CAAC;EACD,MAAM,KAAK,SAAS,OAAO,OAAO;GAChC,MAAM,OAAO,MAAM,KAAK,KAAK,IAAI,EAAE;GACnC,IAAI,KAAK,WAAW,aAClB,MAAM,IAAI,MAAM,2CAA2C;GAE7D,MAAM,OAAO,OAAM,MADM,6BAA6B,OAAO,EAAE,GAAG,CAAC,EAAA,CACrC,KAAK,EACjC,OAAO;IAAE,GAAG,KAAK;IAAO,YAAY;GAAG,EACzC,CAAC;GACD,MAAM,QAAQ,KAAK,MAAM,QAAQ,IAAI,OAAO,YAAY;GACxD,IAAI,OAAO;IACT,IACE,MAAM,mBAAmB,MAAM,kBAC/B,MAAM,oBAAoB,MAAM,mBAChC,MAAM,WAAW,MAAM,UACvB,MAAM,mBAAmB,KAAK,QAAQ,gBAEtC,MAAM,IAAI,MACR,yDACF;IACF;GACF;GACA,MAAM,UAAU,gBACd,KAAK,gBACL,MACA,gBACF;GACA,MAAM,WAAW,gBACf,KAAK,iBACL,MACA,iBACF;GACA,QACE,QAAQ,UAAU,MAAM,cAAc,IACpC,QAAQ,WAAW,MAAM,eAAe,CAC5C;GACA,MAAM,aAAa,MAAM,IAAI,mBAAmB;IAC9C;IACA,IAAI;IACJ,WAAW;GACb,CAAC,CAAA,CAAE,WAAW;GACd,OAAO,OAAO,YAAY,KAAK,OAAO;IACpC,YAAY;IACZ,gBAAgB,KAAK,QAAQ;IAC7B,GAAG;GACL,CAAC;GACD,MAAM,cAAc,UAAU;EAChC,CAAC;EACD,OAAO,KAAK,MAAM,YAAY;GAI5B,MAAM,CAAC,OAAO,OAAM,MAHK,6BAA6B,OAAO,EAC3D,IAAI,KAAK,GACX,CAAC,EAAA,CAC8B,KAAK;IAClC,OAAO;KAAE,GAAG,KAAK;KAAO,IAAI;IAAa;IACzC,OAAO;GACT,CAAC;GACD,IAAI,CAAC,KAAK,MAAM,IAAI,MAAM,mCAAmC;GAC7D,OAAO;EACT,CAAC;CACH;;CAEA,MAAM,OAAO,IAIV;EACD,OAAO,KAAK,SAAS,OAAO,OAAO;GACjC,MAAM,OAAO,MAAM,KAAK,KAAK,IAAI,EAAE;GAEnC,MAAM,OAAO,OAAM,MADM,6BAA6B,OAAO,EAAE,GAAG,CAAC,EAAA,CACrC,KAAK,EACjC,OAAO;IAAE,GAAG,KAAK;IAAO,YAAY;GAAG,EACzC,CAAC;GACD,MAAM,iBAAiB,gBACrB,KAAK,gBACL,MACA,gBACF;GACA,MAAM,kBAAkB,gBACtB,KAAK,iBACL,MACA,iBACF;GACA,OAAO;IACL;IACA;IACA,cAAc,QAAQ,iBAAiB,eAAe;GACxD;EACF,CAAC;CACH;AACF"}
|