{"version":3,"sources":["../../../src/audit/events.ts"],"names":["db","model","Instance","queue","job","#createEvent","name","payload","EquippedError","context","key","ts","validBody","#processEvent","firstRun","def","event","result","asyncHandle","events","wrapQueryParams"],"mappings":"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","file":"/home/runner/work/equipped/equipped/dist/cjs/audit/events.min.cjs","sourcesContent":["import { type Pipe, type PipeInput, type PipeOutput, v } from 'valleyed'\n\nimport { Conditions, Db, type Table, wrapQueryParams } from '../dbs'\nimport { EquippedError } from '../errors'\nimport { Instance } from '../instance'\n\nexport type EventDefinition<P extends Pipe<any, any>, R> = {\n\tpipe: P\n\thandle: (payload: PipeOutput<P>, context: EventContext) => R | Promise<R>\n\tsync?: (result: R, payload: PipeOutput<P>, context: EventContext) => void\n\tasync?: (result: R, payload: PipeOutput<P>, context: EventContext) => void\n}\n\nexport type EventContext = {\n\tkey: string\n\tby: string | undefined\n\tat: Date\n\tfirstRun: boolean\n}\n\nexport type EventDoc = {\n\tkey: string\n\tname: string\n\tts: number\n\tbody: unknown\n\tsteps: { status: 'start' | 'sync' | 'async'; ts: number }[]\n\tby?: string\n}\ntype Context = Partial<Pick<EventContext, 'by' | 'at'>>\n\nfunction createStep(step: EventDoc['steps'][number]) {\n\treturn step\n}\n\nexport class EventAudit {\n\tprivate table: Table<any, EventDoc, EventDoc & { toJSON: () => Record<string, unknown> }, any>\n\tprivate definitions: Record<string, EventDefinition<any, any>> = {}\n\tprivate asyncQueue: (() => Promise<void>)[] = []\n\n\tconstructor(\n\t\tprivate db: Db<any>,\n\t\tdbName: string,\n\t) {\n\t\tthis.table = db.use({\n\t\t\tdb: dbName,\n\t\t\tcol: '__audits',\n\t\t\tmapper: (model) => ({ ...model, toJSON: () => model as Record<string, unknown> }),\n\t\t\toptions: { skipAudit: true },\n\t\t})\n\n\t\tInstance.on(\n\t\t\t'start',\n\t\t\t() => {\n\t\t\t\tsetInterval(async () => {\n\t\t\t\t\tconst queue = [...this.asyncQueue]\n\t\t\t\t\tthis.asyncQueue = []\n\t\t\t\t\tawait Promise.all(queue.map((job) => job()))\n\t\t\t\t}, 200)\n\t\t\t},\n\t\t\t4,\n\t\t)\n\t}\n\n\tasync #createEvent(name: string, payload: unknown, context: Context) {\n\t\tconst def = this.definitions[name]\n\t\tif (!def) throw new EquippedError('audit definition not found', { name, payload })\n\n\t\tconst validBody = v.assert(def.pipe, payload)\n\t\tconst ts = context.at ?? new Date()\n\t\tconst key = Instance.createId({ time: ts })\n\n\t\treturn await this.table.insertOne(\n\t\t\t{\n\t\t\t\tkey,\n\t\t\t\tname,\n\t\t\t\tts: ts.getTime(),\n\t\t\t\tbody: validBody,\n\t\t\t\tby: context.by,\n\t\t\t\tsteps: [],\n\t\t\t},\n\t\t\t{ getTime: () => ts, makeId: () => key },\n\t\t)\n\t}\n\n\tasync #processEvent<R>(event: EventDoc, firstRun: boolean) {\n\t\treturn this.db.session(async () => {\n\t\t\tconst def = this.definitions[event.name]\n\t\t\tif (!def) throw new EquippedError('audit definition not found', { event })\n\t\t\tawait this.table.updateOne({ key: event.key }, { $set: { steps: [createStep({ status: 'start', ts: Date.now() })] } })\n\t\t\tconst context: EventContext = {\n\t\t\t\tkey: event.key,\n\t\t\t\tby: event.by,\n\t\t\t\tat: new Date(event.ts),\n\t\t\t\tfirstRun,\n\t\t\t}\n\t\t\tconst result = await def.handle(event.body, context)\n\t\t\tawait def.sync?.(result, event.body, context)\n\t\t\tawait this.table.updateOne({ key: event.key }, { $push: { steps: createStep({ status: 'sync', ts: Date.now() }) } })\n\n\t\t\tconst asyncHandle = async () => {\n\t\t\t\tawait def.async?.(result, event.body, context)\n\t\t\t\tawait this.table.updateOne({ key: event.key }, { $push: { steps: createStep({ status: 'async', ts: Date.now() }) } })\n\t\t\t}\n\t\t\tif (!context.firstRun) await asyncHandle()\n\t\t\telse this.asyncQueue.push(asyncHandle)\n\t\t\treturn result as R\n\t\t})\n\t}\n\n\tasync replay(from?: Date) {\n\t\tconst { results: events } = await this.table.query(\n\t\t\twrapQueryParams({\n\t\t\t\twhere: [...(from ? [{ field: 'ts', value: from.getTime(), condition: Conditions.gte }] : [])],\n\t\t\t\tsort: [{ field: 'ts', desc: false }],\n\t\t\t\tall: true,\n\t\t\t}),\n\t\t)\n\t\tfor (const event of events) await this.#processEvent(event, false)\n\t}\n\n\tasync rerun(key: string) {\n\t\tconst event = await this.table.findOne({ key })\n\t\tif (!event) throw new EquippedError('audit event not found', { key })\n\t\tawait this.#processEvent(event, false)\n\t}\n\n\tregister<P extends Pipe<any, any>, R>(name: string, def: EventDefinition<P, R>) {\n\t\tif (this.definitions[name]) throw new EquippedError(`${name} already has a registered handler`, {})\n\t\tthis.definitions[name] = def\n\t\tv.compile(def.pipe)\n\t\treturn async (payload: PipeInput<P>, context: Context) =>\n\t\t\tthis.db.session(async () => {\n\t\t\t\tconst event = await this.#createEvent(name, payload, context)\n\t\t\t\treturn this.#processEvent<R>(event, true)\n\t\t\t})\n\t}\n}\n"]}