@happyvertical/smrt-timesheets 0.53.4 → 0.53.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-timesheets",
3
- "version": "0.53.4",
3
+ "version": "0.53.5",
4
4
  "smrtJsdoc": "strict",
5
5
  "description": "Shared time entries for the SMRT framework: who worked how long on what, approval and correction, with immutable charge and compensation snapshots",
6
6
  "type": "module",
@@ -39,11 +39,11 @@
39
39
  }
40
40
  },
41
41
  "dependencies": {
42
- "@happyvertical/smrt-core": "0.53.4",
43
- "@happyvertical/smrt-tenancy": "0.53.4",
44
- "@happyvertical/smrt-types": "0.53.4",
45
- "@happyvertical/smrt-ui": "0.53.4",
46
- "@happyvertical/sql": "^0.98.0"
42
+ "@happyvertical/smrt-core": "0.53.5",
43
+ "@happyvertical/smrt-tenancy": "0.53.5",
44
+ "@happyvertical/smrt-types": "0.53.5",
45
+ "@happyvertical/smrt-ui": "0.53.5",
46
+ "@happyvertical/sql": "^0.100.3"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "svelte": "^5.57.0"
@@ -54,7 +54,7 @@
54
54
  }
55
55
  },
56
56
  "devDependencies": {
57
- "@happyvertical/smrt-vitest": "0.53.4",
57
+ "@happyvertical/smrt-vitest": "0.53.5",
58
58
  "@sveltejs/package": "^2.5.8",
59
59
  "@sveltejs/vite-plugin-svelte": "^7.3.0",
60
60
  "@types/node": "24.13.6",
@@ -1 +0,0 @@
1
- {"version":3,"file":"rollup-uEAMRkKb.js","names":["id"],"sources":["../../src/rollup/write.ts","../../src/rollup/models.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","import {\n crossPackageRef,\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { TimecardSource } from './types.js';\nimport { assertRollupWrite } from './write.js';\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 {\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: 'open' | 'confirmed' = 'open';\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(): 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')\n throw new Error(\n 'Confirmed timecards are immutable; append an adjustment.',\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()) 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 {\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(): 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()) as this;\n }\n override async delete(): Promise<void> {\n throw new Error('Timecard adjustments 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","import type { TimecardPeriod, TimecardSource } 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","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 { clipSource, instant, integer, validatePeriod } from './calculation.js';\nimport {\n PeriodTimecard,\n PeriodTimecardCollection,\n TimecardAdjustment,\n TimecardAdjustmentCollection,\n} from './models.js';\nimport type {\n PeriodRulesResolver,\n TimecardActor,\n TimecardAdjustmentInput,\n TimecardSource,\n} from './types.js';\nimport { 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: {\n attendance?: boolean;\n actorProfileId?: string;\n } = {},\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 (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 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 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 ));\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 const end = entry.startedAt && entry.endedAt ? entry.endedAt : start;\n const source = clipSource(\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.durationSeconds),\n },\n period,\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 /** Confirm the current snapshot; repeated confirmation is idempotent. */\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 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 /** 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 +0,0 @@
1
- {"version":3,"file":"service-time-entry-hVvWmo1p.js","names":[],"sources":["../../src/__smrt-register__.ts","../../src/models/json.ts","../../src/models/service-time-entry.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// During library builds, smrtPlugin replaces this entire module with generated\n// code that embeds the scanned manifest inline (#1506/#1507) — published dists\n// never resolve this URL, so downstream bundlers cannot break registration by\n// relocating the compiled module away from dist/manifest.json. The runtime\n// lookup below is the fallback for source-mode runs without that transform.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","/**\n * Parse a JSON value stored as text, returning `fallback` on malformed input.\n * Stored JSON may predate a schema change, so reads never throw.\n */\nexport function parseTimesheetJson<T>(value: string, fallback: T): T {\n try {\n return JSON.parse(value) as T;\n } catch {\n return fallback;\n }\n}\n","import {\n crossPackageRef,\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n SERVICE_TIME_ENTRY_STATUS_TRANSITIONS,\n type ServiceEvidence,\n type ServiceParticipantKind,\n type ServiceTimeEntrySource,\n type ServiceTimeEntryStatus,\n} from '../types.js';\nimport { parseTimesheetJson } from './json.js';\n\n/**\n * Fields that may not change once an entry is approved (or corrected). A\n * subtype that adds its own work context extends the list through\n * {@link ServiceTimeEntry.frozenFieldNames}.\n */\nexport const SERVICE_TIME_ENTRY_FROZEN_FIELDS = [\n 'tenantId',\n 'workRefType',\n 'workRefId',\n 'participantKind',\n 'participantProfileId',\n 'agentRef',\n 'source',\n 'description',\n 'startedAt',\n 'endedAt',\n 'durationSeconds',\n 'evidence',\n 'correctionOfId',\n] as const;\n\nfunction toColumn(key: string): string {\n return key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);\n}\n\nfunction frozenView(\n source: Record<string, unknown>,\n fields: readonly string[],\n): string {\n const result: Record<string, unknown> = {};\n for (const key of fields) {\n let value = source[key] ?? null;\n if ((key === 'startedAt' || key === 'endedAt') && value) {\n const date = value instanceof Date ? value : new Date(String(value));\n value = Number.isNaN(date.getTime()) ? String(value) : date.toISOString();\n }\n if (key === 'durationSeconds') value = Number(value ?? 0);\n result[key] = value === '' ? null : value;\n }\n return JSON.stringify(result);\n}\n\nconst loadedStatus = new WeakMap<ServiceTimeEntry, ServiceTimeEntryStatus>();\n\n/**\n * A person or agent worked this long on this thing, and someone approved it.\n *\n * The base entry carries a generic work reference (`workRefType` qualified\n * class name + `workRefId`) and no domain-specific foreign key; a domain adds\n * its own context by subclassing over the same `service_time_entries` table\n * (smrt-support's subtype adds `caseId` / `specialistId`). Entries carry no\n * rate: charge and compensation live on the immutable snapshot models.\n *\n * Status moves `draft → submitted → approved | rejected`; an approved entry\n * never changes, and a correction is a new row whose `correctionOfId` points\n * at the entry it corrects, which flips to `corrected`.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'service_time_entries',\n // Moved from smrt-projects in #3288; stored references keep resolving.\n previousQualifiedNames: ['@happyvertical/smrt-projects:ServiceTimeEntry'],\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n})\nexport class ServiceTimeEntry extends SmrtObject {\n @tenantId({ nullable: true }) tenantId: string | null = null;\n @field({ type: 'text', nullable: true }) workRefType: string | null = null;\n @field({ type: 'text', nullable: true }) workRefId: string | null = null;\n @field({ type: 'text' }) participantKind: ServiceParticipantKind = 'human';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n participantProfileId: string | null = null;\n @field({ type: 'text' }) agentRef: string = '';\n @field({ type: 'text' }) source: ServiceTimeEntrySource = 'manual';\n @field({ type: 'text' }) description: string = '';\n startedAt: Date | null = null;\n endedAt: Date | null = null;\n durationSeconds: number = 0;\n @field({ type: 'text' }) evidence: string = '[]';\n @field({ type: 'text' }) status: ServiceTimeEntryStatus = 'draft';\n submittedAt: Date | null = null;\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n submittedByProfileId: string | null = null;\n approvedAt: Date | null = null;\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n approvedByProfileId: string | null = null;\n @field({ type: 'text' }) approvalPath: string = '';\n rejectedAt: Date | null = null;\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n rejectedByProfileId: string | null = null;\n @field({ type: 'text' }) rejectionReason: string = '';\n @foreignKey('ServiceTimeEntry') correctionOfId: string | null = null;\n @field({ type: 'text' }) metadata: string = '{}';\n\n getEvidence(): ServiceEvidence[] {\n return parseTimesheetJson(this.evidence, []);\n }\n setEvidence(value: ServiceEvidence[]): void {\n this.evidence = JSON.stringify(value ?? []);\n }\n getMetadata(): Record<string, unknown> {\n return parseTimesheetJson(this.metadata, {});\n }\n setMetadata(value: Record<string, unknown>): void {\n this.metadata = JSON.stringify(value ?? {});\n }\n durationHours(): number {\n return this.durationSeconds / 3600;\n }\n\n /**\n * Fields frozen once the entry is approved. Subtypes that add work context\n * (e.g. a support case) return `[...super.frozenFieldNames(), ...]`.\n */\n protected frozenFieldNames(): readonly string[] {\n return SERVICE_TIME_ENTRY_FROZEN_FIELDS;\n }\n\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved()) loadedStatus.set(this, this.status);\n return this;\n }\n\n override async save(): Promise<this> {\n const prior = await this.readPrior();\n if (\n prior &&\n prior.status !== this.status &&\n !SERVICE_TIME_ENTRY_STATUS_TRANSITIONS[prior.status].includes(this.status)\n ) {\n throw new Error(\n `ServiceTimeEntry ${this.id}: illegal status transition '${prior.status}' → '${this.status}'.`,\n );\n }\n if (\n prior &&\n (prior.status === 'approved' || prior.status === 'corrected')\n ) {\n const current = frozenView(\n this as unknown as Record<string, unknown>,\n this.frozenFieldNames(),\n );\n if (current !== prior.frozen) {\n throw new Error(\n 'Approved ServiceTimeEntry evidence is immutable; create a correction instead.',\n );\n }\n }\n const result = (await super.save()) as this;\n loadedStatus.set(this, this.status);\n return result;\n }\n\n private async readPrior(): Promise<{\n status: ServiceTimeEntryStatus;\n frozen: string;\n } | null> {\n if (!this.id) return null;\n const fields = this.frozenFieldNames();\n try {\n const row = await this.db.get(this.tableName, { id: this.id });\n if (!row?.status) return null;\n const values: Record<string, unknown> = {};\n for (const key of fields) {\n values[key] = row[toColumn(key)];\n }\n return {\n status: row.status as ServiceTimeEntryStatus,\n frozen: frozenView(values, fields),\n };\n } catch {\n const status = loadedStatus.get(this);\n return status\n ? {\n status,\n frozen: frozenView(\n this as unknown as Record<string, unknown>,\n fields,\n ),\n }\n : null;\n }\n }\n}\n\n/** Collection over `service_time_entries`. */\nexport class ServiceTimeEntryCollection extends SmrtCollection<ServiceTimeEntry> {\n static readonly _itemClass = ServiceTimeEntry;\n async forWork(\n workRefType: string,\n workRefId: string,\n ): Promise<ServiceTimeEntry[]> {\n return this.list({\n where: { workRefType, workRefId },\n orderBy: 'created_at ASC',\n });\n }\n async pendingApproval(): Promise<ServiceTimeEntry[]> {\n return this.list({\n where: { status: 'submitted' },\n orderBy: 'submitted_at ASC',\n });\n }\n}\n"],"mappings":";;;;;;;ACIO,SAAS,mBAAsB,OAAe,UAAgB;CACnE,IAAI;EACF,OAAO,KAAK,MAAM,KAAK;CACzB,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;;;ACaO,IAAM,mCAAmC;CAC9C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,SAAS,KAAqB;CACrC,OAAO,IAAI,QAAQ,WAAW,MAAM,IAAI,EAAE,YAAY,GAAG;AAC3D;AAEA,SAAS,WACP,QACA,QACQ;CACR,MAAM,SAAkC,CAAC;CACzC,KAAA,MAAW,OAAO,QAAQ;EACxB,IAAI,QAAQ,OAAO,QAAQ;EAC3B,KAAK,QAAQ,eAAe,QAAQ,cAAc,OAAO;GACvD,MAAM,OAAO,iBAAiB,OAAO,QAAQ,IAAI,KAAK,OAAO,KAAK,CAAC;GACnE,QAAQ,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO,KAAK,IAAI,KAAK,YAAY;EAC1E;EACA,IAAI,QAAQ,mBAAmB,QAAQ,OAAO,SAAS,CAAC;EACxD,OAAO,OAAO,UAAU,KAAK,OAAO;CACtC;CACA,OAAO,KAAK,UAAU,MAAM;AAC9B;AAEA,IAAM,+BAAe,IAAI,QAAkD;AAwBpE,IAAM,mBAAN,cAA+B,WAAW;CACjB,WAA0B;CACf,cAA6B;CAC7B,YAA2B;CAC3C,kBAA0C;CAEnE,uBAAsC;CACb,WAAmB;CACnB,SAAiC;CACjC,cAAsB;CAC/C,YAAyB;CACzB,UAAuB;CACvB,kBAA0B;CACD,WAAmB;CACnB,SAAiC;CAC1D,cAA2B;CAE3B,uBAAsC;CACtC,aAA0B;CAE1B,sBAAqC;CACZ,eAAuB;CAChD,aAA0B;CAE1B,sBAAqC;CACZ,kBAA0B;CACnB,iBAAgC;CACvC,WAAmB;CAE5C,cAAiC;EAC/B,OAAO,mBAAmB,KAAK,UAAU,CAAC,CAAC;CAC7C;CACA,YAAY,OAAgC;EAC1C,KAAK,WAAW,KAAK,UAAU,SAAS,CAAC,CAAC;CAC5C;CACA,cAAuC;EACrC,OAAO,mBAAmB,KAAK,UAAU,CAAC,CAAC;CAC7C;CACA,YAAY,OAAsC;EAChD,KAAK,WAAW,KAAK,UAAU,SAAS,CAAC,CAAC;CAC5C;CACA,gBAAwB;EACtB,OAAO,KAAK,kBAAkB;CAChC;;;;;CAMU,mBAAsC;EAC9C,OAAO;CACT;CAEA,MAAe,aAA4B;EACzC,MAAM,MAAM,WAAW;EACvB,IAAI,MAAM,KAAK,QAAQ,GAAG,aAAa,IAAI,MAAM,KAAK,MAAM;EAC5D,OAAO;CACT;CAEA,MAAe,OAAsB;EACnC,MAAM,QAAQ,MAAM,KAAK,UAAU;EACnC,IACE,SACA,MAAM,WAAW,KAAK,UACtB,CAAC,sCAAsC,MAAM,OAAM,CAAE,SAAS,KAAK,MAAM,GAEzE,MAAM,IAAI,MACR,oBAAoB,KAAK,GAAE,+BAAgC,MAAM,OAAM,YAAQ,KAAK,OAAM,GAC5F;EAEF,IACE,UACC,MAAM,WAAW,cAAc,MAAM,WAAW;OAEjC,WACd,MACA,KAAK,iBAAiB,CAEpB,MAAY,MAAM,QACpB,MAAM,IAAI,MACR,+EACF;EAAA;EAGJ,MAAM,SAAU,MAAM,MAAM,KAAK;EACjC,aAAa,IAAI,MAAM,KAAK,MAAM;EAClC,OAAO;CACT;CAEA,MAAc,YAGJ;EACR,IAAI,CAAC,KAAK,IAAI,OAAO;EACrB,MAAM,SAAS,KAAK,iBAAiB;EACrC,IAAI;GACF,MAAM,MAAM,MAAM,KAAK,GAAG,IAAI,KAAK,WAAW,EAAE,IAAI,KAAK,GAAG,CAAC;GAC7D,IAAI,CAAC,KAAK,QAAQ,OAAO;GACzB,MAAM,SAAkC,CAAC;GACzC,KAAA,MAAW,OAAO,QAChB,OAAO,OAAO,IAAI,SAAS,GAAG;GAEhC,OAAO;IACL,QAAQ,IAAI;IACZ,QAAQ,WAAW,QAAQ,MAAM;GACnC;EACF,QAAQ;GACN,MAAM,SAAS,aAAa,IAAI,IAAI;GACpC,OAAO,SACH;IACE;IACA,QAAQ,WACN,MACA,MACF;GACF,IACA;EACN;CACF;AACF;AAtHgC,gBAAA,CAA7B,SAAS,EAAE,UAAU,KAAK,CAAC,CAAA,GADjB,iBACmB,WAAA,YAAA,CAAA;AACW,gBAAA,CAAxC,MAAM;CAAE,MAAM;CAAQ,UAAU;AAAK,CAAC,CAAA,GAF5B,iBAE8B,WAAA,eAAA,CAAA;AACA,gBAAA,CAAxC,MAAM;CAAE,MAAM;CAAQ,UAAU;AAAK,CAAC,CAAA,GAH5B,iBAG8B,WAAA,aAAA,CAAA;AAChB,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAJZ,iBAIc,WAAA,mBAAA,CAAA;AAEzB,gBAAA,CADC,gBAAgB,wCAAwC,EAAE,UAAU,KAAK,CAAC,CAAA,GALhE,iBAMX,WAAA,wBAAA,CAAA;AACyB,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAPZ,iBAOc,WAAA,YAAA,CAAA;AACA,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GARZ,iBAQc,WAAA,UAAA,CAAA;AACA,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GATZ,iBASc,WAAA,eAAA,CAAA;AAIA,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAbZ,iBAac,WAAA,YAAA,CAAA;AACA,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAdZ,iBAcc,WAAA,UAAA,CAAA;AAGzB,gBAAA,CADC,gBAAgB,wCAAwC,EAAE,UAAU,KAAK,CAAC,CAAA,GAhBhE,iBAiBX,WAAA,wBAAA,CAAA;AAGA,gBAAA,CADC,gBAAgB,wCAAwC,EAAE,UAAU,KAAK,CAAC,CAAA,GAnBhE,iBAoBX,WAAA,uBAAA,CAAA;AACyB,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GArBZ,iBAqBc,WAAA,gBAAA,CAAA;AAGzB,gBAAA,CADC,gBAAgB,wCAAwC,EAAE,UAAU,KAAK,CAAC,CAAA,GAvBhE,iBAwBX,WAAA,uBAAA,CAAA;AACyB,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GAzBZ,iBAyBc,WAAA,mBAAA,CAAA;AACO,gBAAA,CAA/B,WAAW,kBAAkB,CAAA,GA1BnB,iBA0BqB,WAAA,kBAAA,CAAA;AACP,gBAAA,CAAxB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAA,GA3BZ,iBA2Bc,WAAA,YAAA,CAAA;AA3Bd,mBAAN,gBAAA,CATN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,WAAW;CAEX,wBAAwB,CAAC,+CAA+C;CACxE,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;AAClC,CAAC,CAAA,GACY,gBAAA;AA0HN,IAAM,6BAAN,cAAyC,eAAiC;CAC/E,OAAgB,aAAa;CAC7B,MAAM,QACJ,aACA,WAC6B;EAC7B,OAAO,KAAK,KAAK;GACf,OAAO;IAAE;IAAa;GAAU;GAChC,SAAS;EACX,CAAC;CACH;CACA,MAAM,kBAA+C;EACnD,OAAO,KAAK,KAAK;GACf,OAAO,EAAE,QAAQ,YAAY;GAC7B,SAAS;EACX,CAAC;CACH;AACF"}