hierarchical-approval 2.1.0 → 2.3.0
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/CHANGELOG.md +73 -0
- package/README.md +51 -0
- package/dist/{ApprovalEngine-DqRIadYg.d.cts → ApprovalEngine-C1dUPnLM.d.cts} +101 -2
- package/dist/{ApprovalEngine-CJwnpnpL.d.ts → ApprovalEngine-Ci9urTDI.d.ts} +101 -2
- package/dist/{IStorageAdapter-DjRvHUF0.d.ts → IStorageAdapter-BU3sau5W.d.ts} +14 -0
- package/dist/{IStorageAdapter-Bk7ybd3z.d.cts → IStorageAdapter-DdHO4Rf1.d.cts} +14 -0
- package/dist/adapters/MemoryAdapter.cjs +4 -0
- package/dist/adapters/MemoryAdapter.cjs.map +1 -1
- package/dist/adapters/MemoryAdapter.d.cts +2 -1
- package/dist/adapters/MemoryAdapter.d.ts +2 -1
- package/dist/adapters/MemoryAdapter.js +4 -0
- package/dist/adapters/MemoryAdapter.js.map +1 -1
- package/dist/adapters/PostgresAdapter.cjs +12 -0
- package/dist/adapters/PostgresAdapter.cjs.map +1 -1
- package/dist/adapters/PostgresAdapter.d.cts +2 -1
- package/dist/adapters/PostgresAdapter.d.ts +2 -1
- package/dist/adapters/PostgresAdapter.js +12 -0
- package/dist/adapters/PostgresAdapter.js.map +1 -1
- package/dist/index.cjs +195 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +195 -1
- package/dist/index.js.map +1 -1
- package/dist/nestjs.cjs +190 -0
- package/dist/nestjs.cjs.map +1 -1
- package/dist/nestjs.d.cts +2 -2
- package/dist/nestjs.d.ts +2 -2
- package/dist/nestjs.js +190 -0
- package/dist/nestjs.js.map +1 -1
- package/dist/testing.cjs +194 -0
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +194 -0
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/errors.ts","../../src/adapters/PostgresAdapter.ts"],"names":[],"mappings":"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","file":"PostgresAdapter.js","sourcesContent":["export class ApprovalError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n ) {\n super(message);\n this.name = 'ApprovalError';\n }\n\n toJSON(): { code: string; message: string; name: string } {\n return { code: this.code, message: this.message, name: this.name };\n }\n\n toHttpStatus(): number {\n const map: Record<string, number> = {\n NOT_FOUND: 404,\n CONFLICT: 409,\n FORBIDDEN: 403,\n VALIDATION: 422,\n TEMPLATE_NOT_FOUND: 404,\n };\n return map[this.code] ?? 500;\n }\n}\n\nexport class ApprovalNotFoundError extends ApprovalError {\n constructor(resource: string, id: string) {\n super(`${resource} \"${id}\" not found.`, 'NOT_FOUND');\n this.name = 'ApprovalNotFoundError';\n }\n}\n\nexport class ApprovalConflictError extends ApprovalError {\n constructor(instanceId: string) {\n super(\n `Concurrent modification detected on instance \"${instanceId}\". The record was updated by another process. Please retry.`,\n 'CONFLICT',\n );\n this.name = 'ApprovalConflictError';\n }\n}\n\nexport class ApprovalForbiddenError extends ApprovalError {\n constructor(message: string) {\n super(message, 'FORBIDDEN');\n this.name = 'ApprovalForbiddenError';\n }\n}\n\nexport class ApprovalValidationError extends ApprovalError {\n constructor(\n message: string,\n public readonly cause?: unknown,\n ) {\n super(message, 'VALIDATION');\n this.name = 'ApprovalValidationError';\n }\n}\n\nexport class ApprovalTemplateNotFoundError extends ApprovalError {\n constructor(name: string) {\n super(`Template \"${name}\" not found.`, 'TEMPLATE_NOT_FOUND');\n this.name = 'ApprovalTemplateNotFoundError';\n }\n}\n","/**\n * PostgreSQL adapter for hierarchical-approval.\n *\n * Peer dependency: `pg` >= 8\n *\n * Run the bundled migration before using:\n * const adapter = new PostgresAdapter({ connectionString: '...' });\n * await adapter.migrate();\n *\n * For PostgreSQL Row Level Security, call:\n * await adapter.enableRLS();\n */\nimport type {\n IStorageAdapter,\n PaginationOpts,\n PaginatedResult,\n InstanceFilter,\n CursorPaginationOpts,\n CursorPaginatedResult,\n} from './IStorageAdapter.js';\nimport type { ApprovalTemplate, ApprovalInstance, AuditEntry, Attachment } from '../types/index.js';\nimport { ApprovalConflictError, ApprovalValidationError } from '../errors.js';\n\nexport interface PostgresAdapterOptions {\n connectionString?: string;\n /** Bring your own pre-configured pg.Pool instead of a connectionString. */\n pool?: import('pg').Pool;\n tablePrefix?: string;\n /** PostgreSQL schema to use (default: 'public'). */\n schema?: string;\n /** Statement timeout in milliseconds applied to every query (default: none). */\n statementTimeoutMs?: number;\n ssl?: import('tls').ConnectionOptions;\n}\n\n// Also used to validate `schema` — both are interpolated directly into SQL text\n// (there is no parameterized way to name a table/schema), so both must be restricted\n// to safe SQL identifiers to avoid injection via adapter construction options.\nconst TABLE_PREFIX_RE = /^[a-z][a-z0-9_]*$/;\n\n/**\n * Restores `createdAt` to a real `Date` on a template read back from JSONB.\n *\n * Templates are persisted with `JSON.stringify`, so `createdAt` comes back as an\n * ISO string even though {@link ApprovalTemplate} types it as a `Date`. Without\n * this, `TemplateRegistry.update()` threads the string into the next\n * `saveTemplate()` call, which throws `TypeError: createdAt.toISOString is not a\n * function`. Mirrors the same helper in `MemoryAdapter`.\n */\nfunction reviveTemplateDates(template: ApprovalTemplate): ApprovalTemplate {\n return { ...template, createdAt: new Date(template.createdAt) };\n}\n\n/**\n * Build the SQL fragment matching a dot-path in the JSONB `data` column.\n *\n * Uses `#>` with a text[] path so a nested path is one parameterised lookup and\n * the path segments are never interpolated into SQL. Comparison is against the\n * value as JSONB, which makes an object or array value match structurally\n * rather than by its serialised text.\n */\nfunction jsonbPathCondition(path: string, paramIdx: number): string {\n return `data #> $${paramIdx}::text[] = $${paramIdx + 1}::jsonb`;\n}\n\n/** Postgres text[] literal for a dot-path, e.g. \"vendor.id\" -> {vendor,id}. */\nfunction toPgTextArray(path: string): string {\n return `{${path\n .split('.')\n .map((seg) => `\"${seg.replace(/([\"\\\\])/g, '\\\\$1')}\"`)\n .join(',')}}`;\n}\n\nexport class PostgresAdapter implements IStorageAdapter {\n private _pool: import('pg').Pool | null = null;\n private readonly prefix: string;\n private readonly schema: string;\n private readonly statementTimeoutMs?: number;\n private readonly externalPool: import('pg').Pool | undefined;\n\n constructor(private readonly opts: PostgresAdapterOptions) {\n const prefix = opts.tablePrefix ?? 'ha';\n if (!TABLE_PREFIX_RE.test(prefix)) {\n throw new ApprovalValidationError(\n `PostgresAdapter: tablePrefix \"${prefix}\" is invalid. Must match /^[a-z][a-z0-9_]*$/. Only lowercase letters, digits, and underscores are allowed.`,\n );\n }\n const schema = opts.schema ?? 'public';\n if (!TABLE_PREFIX_RE.test(schema)) {\n throw new ApprovalValidationError(\n `PostgresAdapter: schema \"${schema}\" is invalid. Must match /^[a-z][a-z0-9_]*$/. Only lowercase letters, digits, and underscores are allowed.`,\n );\n }\n this.prefix = prefix;\n this.schema = schema;\n this.statementTimeoutMs = opts.statementTimeoutMs;\n this.externalPool = opts.pool;\n }\n\n private async getPool(): Promise<import('pg').Pool> {\n if (this.externalPool) return this.externalPool;\n if (this._pool) return this._pool;\n const { default: pg } = await import('pg');\n this._pool = new pg.Pool({\n connectionString: this.opts.connectionString,\n ssl: this.opts.ssl,\n });\n return this._pool;\n }\n\n private get p() {\n return `${this.schema}.${this.prefix}`;\n }\n\n async migrate(): Promise<void> {\n const pool = await this.getPool();\n await pool.query(`\n CREATE TABLE IF NOT EXISTS ${this.p}_templates (\n tenant_id TEXT NOT NULL,\n name TEXT NOT NULL,\n data JSONB NOT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n PRIMARY KEY (tenant_id, name)\n );\n\n CREATE TABLE IF NOT EXISTS ${this.p}_instances (\n id TEXT NOT NULL,\n tenant_id TEXT NOT NULL,\n template_id TEXT NOT NULL DEFAULT '',\n template_name TEXT NOT NULL,\n document_id TEXT NOT NULL,\n document_type TEXT NOT NULL,\n submitted_by TEXT NOT NULL,\n status TEXT NOT NULL,\n current_level INT NOT NULL,\n version INT NOT NULL DEFAULT 1,\n idempotency_key TEXT,\n data JSONB NOT NULL DEFAULT '{}',\n metadata JSONB NOT NULL DEFAULT '{}',\n levels JSONB NOT NULL DEFAULT '[]',\n parent_instance_id TEXT,\n expires_at TIMESTAMPTZ,\n deadline_action TEXT,\n sla_deadline_at TIMESTAMPTZ,\n sla_breached_at TIMESTAMPTZ,\n template_snapshot JSONB,\n info_request JSONB,\n attachments JSONB NOT NULL DEFAULT '[]',\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n PRIMARY KEY (tenant_id, id),\n UNIQUE (tenant_id, idempotency_key)\n );\n\n CREATE TABLE IF NOT EXISTS ${this.p}_audit_log (\n id BIGSERIAL PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n instance_id TEXT NOT NULL,\n action TEXT NOT NULL,\n actor_id TEXT NOT NULL,\n actor_role TEXT,\n actor_ip TEXT,\n actor_user_agent TEXT,\n trace_id TEXT,\n level INT NOT NULL,\n timestamp TIMESTAMPTZ NOT NULL,\n old_value JSONB,\n new_value JSONB,\n comment TEXT,\n reason TEXT,\n delegate_to TEXT\n );\n\n CREATE INDEX IF NOT EXISTS ${this.p}_instances_tenant_status\n ON ${this.p}_instances (tenant_id, status);\n\n CREATE INDEX IF NOT EXISTS ${this.p}_instances_tenant_updated\n ON ${this.p}_instances (tenant_id, updated_at);\n\n CREATE INDEX IF NOT EXISTS ${this.p}_audit_log_instance\n ON ${this.p}_audit_log (tenant_id, instance_id);\n\n -- Add new columns to existing tables (idempotent for upgrades)\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS template_id TEXT NOT NULL DEFAULT '';\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS parent_instance_id TEXT;\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS expires_at TIMESTAMPTZ;\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS deadline_action TEXT;\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS sla_deadline_at TIMESTAMPTZ;\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS sla_breached_at TIMESTAMPTZ;\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS template_snapshot JSONB;\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS info_request JSONB;\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS attachments JSONB NOT NULL DEFAULT '[]';\n `);\n }\n\n /** Enable PostgreSQL Row Level Security for the instances and audit_log tables. */\n async enableRLS(): Promise<void> {\n const pool = await this.getPool();\n await pool.query(`\n ALTER TABLE ${this.p}_instances ENABLE ROW LEVEL SECURITY;\n ALTER TABLE ${this.p}_audit_log ENABLE ROW LEVEL SECURITY;\n\n DROP POLICY IF EXISTS ${this.p}_tenant_isolation ON ${this.p}_instances;\n CREATE POLICY ${this.p}_tenant_isolation ON ${this.p}_instances\n USING (tenant_id = current_setting('rls.tenant_id', TRUE));\n\n DROP POLICY IF EXISTS ${this.p}_audit_tenant_isolation ON ${this.p}_audit_log;\n CREATE POLICY ${this.p}_audit_tenant_isolation ON ${this.p}_audit_log\n USING (tenant_id = current_setting('rls.tenant_id', TRUE));\n `);\n }\n\n // ─── Templates ────────────────────────────────────────────────────────────\n\n async saveTemplate(template: ApprovalTemplate): Promise<void> {\n const pool = await this.getPool();\n await pool.query(\n `INSERT INTO ${this.p}_templates (tenant_id, name, data, created_at)\n VALUES ($1, $2, $3, $4)\n ON CONFLICT (tenant_id, name) DO UPDATE SET data = EXCLUDED.data`,\n [\n template.tenantId,\n template.name,\n JSON.stringify(template),\n template.createdAt.toISOString(),\n ],\n );\n }\n\n async getTemplate(tenantId: string, name: string): Promise<ApprovalTemplate | null> {\n const pool = await this.getPool();\n const result = await pool.query<{ data: ApprovalTemplate }>(\n `SELECT data FROM ${this.p}_templates WHERE tenant_id = $1 AND name = $2`,\n [tenantId, name],\n );\n const row = result.rows[0];\n return row ? reviveTemplateDates(row.data) : null;\n }\n\n async listTemplates(tenantId: string): Promise<ApprovalTemplate[]> {\n const pool = await this.getPool();\n const result = await pool.query<{ data: ApprovalTemplate }>(\n `SELECT data FROM ${this.p}_templates WHERE tenant_id = $1 ORDER BY created_at ASC`,\n [tenantId],\n );\n return result.rows.map((r) => reviveTemplateDates(r.data));\n }\n\n // ─── Instances ────────────────────────────────────────────────────────────\n\n async saveInstance(instance: ApprovalInstance): Promise<void> {\n const pool = await this.getPool();\n await pool.query(\n `INSERT INTO ${this.p}_instances\n (id, tenant_id, template_id, template_name, document_id, document_type, submitted_by,\n status, current_level, version, idempotency_key,\n data, metadata, levels,\n parent_instance_id, expires_at, deadline_action,\n sla_deadline_at, sla_breached_at, template_snapshot, info_request, attachments,\n created_at, updated_at)\n VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24)\n ON CONFLICT (tenant_id, id) DO NOTHING`,\n [\n instance.id,\n instance.tenantId,\n instance.templateId,\n instance.templateName,\n instance.documentId,\n instance.documentType,\n instance.submittedBy,\n instance.status,\n instance.currentLevel,\n instance.version,\n instance.idempotencyKey ?? null,\n JSON.stringify(instance.data),\n JSON.stringify(instance.metadata),\n JSON.stringify(instance.levels),\n instance.parentInstanceId ?? null,\n instance.expiresAt?.toISOString() ?? null,\n instance.deadlineAction ?? null,\n instance.slaDeadlineAt?.toISOString() ?? null,\n instance.slaBreachedAt?.toISOString() ?? null,\n instance.templateSnapshot ? JSON.stringify(instance.templateSnapshot) : null,\n instance.infoRequest ? JSON.stringify(instance.infoRequest) : null,\n JSON.stringify(instance.attachments ?? []),\n instance.createdAt.toISOString(),\n instance.updatedAt.toISOString(),\n ],\n );\n }\n\n async updateInstance(instance: ApprovalInstance, expectedVersion: number): Promise<void> {\n const pool = await this.getPool();\n const result = await pool.query(\n // Every column an engine operation can mutate must be written here.\n // Listing only a subset silently dropped writes: updateData() changed\n // `data`, requestInfo() set `info_request`, and provideInfo() shifted the\n // deadline columns — none of which reached the database.\n `UPDATE ${this.p}_instances SET\n status = $4,\n current_level = $5,\n version = $6 + 1,\n levels = $7,\n sla_breached_at = $8,\n updated_at = $9,\n data = $10,\n metadata = $11,\n expires_at = $12,\n deadline_action = $13,\n sla_deadline_at = $14,\n template_snapshot = $15,\n info_request = $16,\n attachments = $17\n WHERE tenant_id = $1 AND id = $2 AND version = $3\n RETURNING id`,\n [\n instance.tenantId,\n instance.id,\n expectedVersion,\n instance.status,\n instance.currentLevel,\n expectedVersion,\n JSON.stringify(instance.levels),\n instance.slaBreachedAt?.toISOString() ?? null,\n instance.updatedAt.toISOString(),\n JSON.stringify(instance.data ?? {}),\n JSON.stringify(instance.metadata ?? {}),\n instance.expiresAt?.toISOString() ?? null,\n instance.deadlineAction ?? null,\n instance.slaDeadlineAt?.toISOString() ?? null,\n instance.templateSnapshot ? JSON.stringify(instance.templateSnapshot) : null,\n instance.infoRequest ? JSON.stringify(instance.infoRequest) : null,\n JSON.stringify(instance.attachments ?? []),\n ],\n );\n if (result.rowCount === 0) throw new ApprovalConflictError(instance.id);\n }\n\n async getInstance(tenantId: string, id: string): Promise<ApprovalInstance | null> {\n const pool = await this.getPool();\n const result = await pool.query(\n `SELECT * FROM ${this.p}_instances WHERE tenant_id = $1 AND id = $2`,\n [tenantId, id],\n );\n const row = result.rows[0];\n if (!row) return null;\n return this.rowToInstance(row as Record<string, unknown>);\n }\n\n async getInstancesByApprover(\n tenantId: string,\n approverId: string,\n opts?: PaginationOpts,\n ): Promise<PaginatedResult<ApprovalInstance>> {\n const pool = await this.getPool();\n const offset = opts?.offset ?? 0;\n const limit = opts?.limit ?? 50;\n\n const result = await pool.query(\n `SELECT *, COUNT(*) OVER() AS total_count\n FROM ${this.p}_instances\n WHERE tenant_id = $1\n AND status = 'pending'\n AND EXISTS (\n SELECT 1 FROM jsonb_array_elements(levels) AS lvl\n WHERE lvl->>'status' = 'pending'\n AND lvl->'approverIds' @> $2::jsonb\n )\n ORDER BY updated_at ASC\n LIMIT $3 OFFSET $4`,\n [tenantId, JSON.stringify([approverId]), limit, offset],\n );\n\n const total = result.rows[0]\n ? Number((result.rows[0] as Record<string, unknown>)['total_count'])\n : 0;\n return {\n items: result.rows.map((r) => this.rowToInstance(r as Record<string, unknown>)),\n total,\n };\n }\n\n async getInstancesByFilter(\n tenantId: string,\n filter: InstanceFilter,\n opts?: PaginationOpts,\n ): Promise<PaginatedResult<ApprovalInstance>> {\n const pool = await this.getPool();\n const conditions: string[] = ['tenant_id = $1'];\n const params: unknown[] = [tenantId];\n let idx = 2;\n\n if (filter.status) {\n conditions.push(`status = $${idx++}`);\n params.push(filter.status);\n }\n if (filter.documentType) {\n conditions.push(`document_type = $${idx++}`);\n params.push(filter.documentType);\n }\n if (filter.submittedBy) {\n conditions.push(`submitted_by = $${idx++}`);\n params.push(filter.submittedBy);\n }\n if (filter.templateName) {\n conditions.push(`template_name = $${idx++}`);\n params.push(filter.templateName);\n }\n if (filter.fromDate) {\n conditions.push(`created_at >= $${idx++}`);\n params.push(filter.fromDate.toISOString());\n }\n if (filter.toDate) {\n conditions.push(`created_at <= $${idx++}`);\n params.push(filter.toDate.toISOString());\n }\n if (filter.data) {\n for (const [path, expected] of Object.entries(filter.data)) {\n conditions.push(jsonbPathCondition(path, idx));\n params.push(toPgTextArray(path), JSON.stringify(expected ?? null));\n idx += 2;\n }\n }\n\n const offset = opts?.offset ?? 0;\n const limit = opts?.limit ?? 50;\n params.push(limit, offset);\n\n const sql = `SELECT *, COUNT(*) OVER() AS total_count\n FROM ${this.p}_instances\n WHERE ${conditions.join(' AND ')}\n ORDER BY created_at DESC\n LIMIT $${idx++} OFFSET $${idx}`;\n\n const result = await pool.query(sql, params);\n const total = result.rows[0]\n ? Number((result.rows[0] as Record<string, unknown>)['total_count'])\n : 0;\n return {\n items: result.rows.map((r) => this.rowToInstance(r as Record<string, unknown>)),\n total,\n };\n }\n\n async countInstances(tenantId: string, filter: InstanceFilter): Promise<number> {\n const pool = await this.getPool();\n const conditions: string[] = ['tenant_id = $1'];\n const params: unknown[] = [tenantId];\n let idx = 2;\n\n if (filter.status) {\n conditions.push(`status = $${idx++}`);\n params.push(filter.status);\n }\n if (filter.documentType) {\n conditions.push(`document_type = $${idx++}`);\n params.push(filter.documentType);\n }\n if (filter.submittedBy) {\n conditions.push(`submitted_by = $${idx++}`);\n params.push(filter.submittedBy);\n }\n if (filter.templateName) {\n conditions.push(`template_name = $${idx++}`);\n params.push(filter.templateName);\n }\n if (filter.fromDate) {\n conditions.push(`created_at >= $${idx++}`);\n params.push(filter.fromDate.toISOString());\n }\n if (filter.toDate) {\n conditions.push(`created_at <= $${idx++}`);\n params.push(filter.toDate.toISOString());\n }\n if (filter.data) {\n for (const [path, expected] of Object.entries(filter.data)) {\n conditions.push(jsonbPathCondition(path, idx));\n params.push(toPgTextArray(path), JSON.stringify(expected ?? null));\n idx += 2;\n }\n }\n\n // A bare COUNT(*): no row payload to serialise, and the planner is free to\n // satisfy it from an index rather than touching the JSONB columns at all.\n const result = await pool.query(\n `SELECT COUNT(*)::bigint AS count FROM ${this.p}_instances WHERE ${conditions.join(' AND ')}`,\n params,\n );\n const row = result.rows[0] as Record<string, unknown> | undefined;\n return row ? Number(row['count']) : 0;\n }\n\n async getOverdueInstances(\n tenantId: string,\n asOf: Date,\n filter: InstanceFilter = {},\n ): Promise<ApprovalInstance[]> {\n const pool = await this.getPool();\n const values: unknown[] = [tenantId, asOf.toISOString()];\n let query = `SELECT * FROM ${this.p}_instances\n WHERE tenant_id = $1\n AND status = 'pending'`;\n\n if (filter.documentType) {\n values.push(filter.documentType);\n query += ` AND document_type = $${values.length}`;\n }\n if (filter.submittedBy) {\n values.push(filter.submittedBy);\n query += ` AND submitted_by = $${values.length}`;\n }\n\n query += ` AND (\n EXISTS (\n SELECT 1 FROM jsonb_array_elements(levels) AS lvl\n WHERE (lvl->>'escalationDueAt') IS NOT NULL\n AND (lvl->>'escalationDueAt')::timestamptz <= $2\n )\n OR (expires_at IS NOT NULL AND expires_at <= $2)\n OR (sla_deadline_at IS NOT NULL AND sla_deadline_at <= $2 AND sla_breached_at IS NULL)\n OR EXISTS (\n SELECT 1 FROM jsonb_array_elements(levels) AS lvl\n WHERE lvl->>'status' = 'pending'\n AND (lvl->>'delegatedUntil') IS NOT NULL\n AND (lvl->>'delegatedUntil')::timestamptz <= $2\n AND (lvl->>'delegatedFrom') IS NOT NULL\n )\n OR EXISTS (\n SELECT 1 FROM jsonb_array_elements(levels) AS lvl\n WHERE lvl->>'status' = 'pending'\n AND (lvl->>'reminderDueAt') IS NOT NULL\n AND (lvl->>'reminderDueAt')::timestamptz <= $2\n )\n )`;\n const result = await pool.query(query, values);\n return result.rows.map((r) => this.rowToInstance(r as Record<string, unknown>));\n }\n\n async getIdempotentInstance(\n tenantId: string,\n idempotencyKey: string,\n ): Promise<ApprovalInstance | null> {\n const pool = await this.getPool();\n const result = await pool.query(\n `SELECT * FROM ${this.p}_instances WHERE tenant_id = $1 AND idempotency_key = $2`,\n [tenantId, idempotencyKey],\n );\n const row = result.rows[0];\n if (!row) return null;\n return this.rowToInstance(row as Record<string, unknown>);\n }\n\n // ─── Audit (append-only) ──────────────────────────────────────────────────\n\n async appendAuditEntry(tenantId: string, instanceId: string, entry: AuditEntry): Promise<void> {\n const pool = await this.getPool();\n await pool.query(\n `INSERT INTO ${this.p}_audit_log\n (tenant_id, instance_id, action, actor_id, actor_role, actor_ip,\n actor_user_agent, trace_id, level, timestamp,\n old_value, new_value, comment, reason, delegate_to)\n VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)`,\n [\n tenantId,\n instanceId,\n entry.action,\n entry.actorId,\n entry.actorRole ?? null,\n entry.actorIp ?? null,\n entry.actorUserAgent ?? null,\n entry.traceId ?? null,\n entry.level,\n entry.timestamp.toISOString(),\n entry.oldValue ? JSON.stringify(entry.oldValue) : null,\n entry.newValue ? JSON.stringify(entry.newValue) : null,\n entry.comment ?? null,\n entry.reason ?? null,\n entry.delegateTo ?? null,\n ],\n );\n }\n\n async getInstancesByCursor(\n tenantId: string,\n filter: InstanceFilter,\n opts: CursorPaginationOpts,\n ): Promise<CursorPaginatedResult<ApprovalInstance>> {\n const pool = await this.getPool();\n const conditions: string[] = ['tenant_id = $1'];\n const params: unknown[] = [tenantId];\n let idx = 2;\n\n if (filter.status) {\n conditions.push(`status = $${idx++}`);\n params.push(filter.status);\n }\n if (filter.documentType) {\n conditions.push(`document_type = $${idx++}`);\n params.push(filter.documentType);\n }\n if (filter.submittedBy) {\n conditions.push(`submitted_by = $${idx++}`);\n params.push(filter.submittedBy);\n }\n if (filter.templateName) {\n conditions.push(`template_name = $${idx++}`);\n params.push(filter.templateName);\n }\n if (filter.data) {\n for (const [path, expected] of Object.entries(filter.data)) {\n conditions.push(jsonbPathCondition(path, idx));\n params.push(toPgTextArray(path), JSON.stringify(expected ?? null));\n idx += 2;\n }\n }\n\n const { cursor, limit, direction = 'forward' } = opts;\n\n if (cursor) {\n const decoded = Buffer.from(cursor, 'base64').toString('utf8');\n // The timestamp segment is an ISO-8601 string, which itself contains colons\n // (e.g. \"2026-06-26T09:00:00.000Z\") — the LAST colon is the id separator,\n // not the first. Using indexOf() here previously truncated the timestamp\n // and corrupted the id on every cursor decode.\n const colonIdx = decoded.lastIndexOf(':');\n const ts = decoded.slice(0, colonIdx);\n const id = decoded.slice(colonIdx + 1);\n if (direction === 'forward') {\n conditions.push(`(updated_at, id) > ($${idx++}::timestamptz, $${idx++})`);\n params.push(ts, id);\n } else {\n conditions.push(`(updated_at, id) < ($${idx++}::timestamptz, $${idx++})`);\n params.push(ts, id);\n }\n }\n\n params.push(limit + 1);\n const order = direction === 'backward' ? 'DESC' : 'ASC';\n const sql = `SELECT * FROM ${this.p}_instances\n WHERE ${conditions.join(' AND ')}\n ORDER BY updated_at ${order}, id ${order}\n LIMIT $${idx}`;\n\n const result = await pool.query(sql, params);\n const hasMore = result.rows.length > limit;\n const rows = result.rows.slice(0, limit);\n const items = rows.map((r) => this.rowToInstance(r as Record<string, unknown>));\n\n const nextCursor = hasMore\n ? encodeInstanceCursor(rows[rows.length - 1] as Record<string, unknown>)\n : undefined;\n\n return { items, nextCursor, hasMore };\n }\n\n async end(): Promise<void> {\n await this._pool?.end();\n this._pool = null;\n }\n\n private rowToInstance(row: Record<string, unknown>): ApprovalInstance {\n return {\n id: row['id'] as string,\n tenantId: row['tenant_id'] as string,\n templateId: (row['template_id'] as string | null) ?? '',\n templateName: row['template_name'] as string,\n documentId: row['document_id'] as string,\n documentType: row['document_type'] as string,\n submittedBy: row['submitted_by'] as string,\n status: row['status'] as ApprovalInstance['status'],\n currentLevel: row['current_level'] as number,\n version: row['version'] as number,\n idempotencyKey: (row['idempotency_key'] as string | null) ?? undefined,\n data: row['data'] as Record<string, unknown>,\n metadata: row['metadata'] as Record<string, unknown>,\n levels: row['levels'] as ApprovalInstance['levels'],\n auditLog: [],\n parentInstanceId: (row['parent_instance_id'] as string | null) ?? undefined,\n expiresAt: row['expires_at'] ? new Date(row['expires_at'] as string) : undefined,\n deadlineAction: (row['deadline_action'] as 'cancel' | 'reject' | null) ?? undefined,\n slaDeadlineAt: row['sla_deadline_at']\n ? new Date(row['sla_deadline_at'] as string)\n : undefined,\n slaBreachedAt: row['sla_breached_at']\n ? new Date(row['sla_breached_at'] as string)\n : undefined,\n templateSnapshot:\n (row['template_snapshot'] as ApprovalInstance['templateSnapshot'] | null) ?? undefined,\n // Absent attachments map to undefined, not [], so a Postgres round trip\n // returns the same shape MemoryAdapter does for an instance that never\n // had any — the parity the round-trip test pins down.\n attachments: ((row['attachments'] as Attachment[] | null) ?? []).length\n ? (row['attachments'] as Attachment[]).map((a) => ({ ...a, addedAt: new Date(a.addedAt) }))\n : undefined,\n infoRequest: row['info_request']\n ? {\n ...(row['info_request'] as ApprovalInstance['infoRequest'] & { askedAt: string }),\n askedAt: new Date((row['info_request'] as { askedAt: string }).askedAt),\n }\n : undefined,\n createdAt: new Date(row['created_at'] as string),\n updatedAt: new Date(row['updated_at'] as string),\n };\n }\n}\n\n/**\n * Encode a cursor as base64(updatedAt_iso:id) per the IStorageAdapter contract.\n *\n * `row['updated_at']` normalizes through `new Date(...)` before `.toISOString()`\n * because node-postgres returns TIMESTAMPTZ columns as `Date` instances by default —\n * stringifying a `Date` directly (e.g. via template-literal interpolation) uses\n * `Date.prototype.toString()`, not ISO-8601, and is not guaranteed to round-trip\n * through Postgres's `::timestamptz` cast on decode.\n */\nfunction encodeInstanceCursor(row: Record<string, unknown>): string {\n const updatedAtIso = new Date(row['updated_at'] as string | Date).toISOString();\n return Buffer.from(`${updatedAtIso}:${row['id']}`).toString('base64');\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/errors.ts","../../src/adapters/PostgresAdapter.ts"],"names":[],"mappings":"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","file":"PostgresAdapter.js","sourcesContent":["export class ApprovalError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n ) {\n super(message);\n this.name = 'ApprovalError';\n }\n\n toJSON(): { code: string; message: string; name: string } {\n return { code: this.code, message: this.message, name: this.name };\n }\n\n toHttpStatus(): number {\n const map: Record<string, number> = {\n NOT_FOUND: 404,\n CONFLICT: 409,\n FORBIDDEN: 403,\n VALIDATION: 422,\n TEMPLATE_NOT_FOUND: 404,\n };\n return map[this.code] ?? 500;\n }\n}\n\nexport class ApprovalNotFoundError extends ApprovalError {\n constructor(resource: string, id: string) {\n super(`${resource} \"${id}\" not found.`, 'NOT_FOUND');\n this.name = 'ApprovalNotFoundError';\n }\n}\n\nexport class ApprovalConflictError extends ApprovalError {\n constructor(instanceId: string) {\n super(\n `Concurrent modification detected on instance \"${instanceId}\". The record was updated by another process. Please retry.`,\n 'CONFLICT',\n );\n this.name = 'ApprovalConflictError';\n }\n}\n\nexport class ApprovalForbiddenError extends ApprovalError {\n constructor(message: string) {\n super(message, 'FORBIDDEN');\n this.name = 'ApprovalForbiddenError';\n }\n}\n\nexport class ApprovalValidationError extends ApprovalError {\n constructor(\n message: string,\n public readonly cause?: unknown,\n ) {\n super(message, 'VALIDATION');\n this.name = 'ApprovalValidationError';\n }\n}\n\nexport class ApprovalTemplateNotFoundError extends ApprovalError {\n constructor(name: string) {\n super(`Template \"${name}\" not found.`, 'TEMPLATE_NOT_FOUND');\n this.name = 'ApprovalTemplateNotFoundError';\n }\n}\n","/**\n * PostgreSQL adapter for hierarchical-approval.\n *\n * Peer dependency: `pg` >= 8\n *\n * Run the bundled migration before using:\n * const adapter = new PostgresAdapter({ connectionString: '...' });\n * await adapter.migrate();\n *\n * For PostgreSQL Row Level Security, call:\n * await adapter.enableRLS();\n */\nimport type {\n IStorageAdapter,\n PaginationOpts,\n PaginatedResult,\n InstanceFilter,\n CursorPaginationOpts,\n CursorPaginatedResult,\n} from './IStorageAdapter.js';\nimport type { ApprovalTemplate, ApprovalInstance, AuditEntry, Attachment } from '../types/index.js';\nimport { ApprovalConflictError, ApprovalValidationError } from '../errors.js';\n\nexport interface PostgresAdapterOptions {\n connectionString?: string;\n /** Bring your own pre-configured pg.Pool instead of a connectionString. */\n pool?: import('pg').Pool;\n tablePrefix?: string;\n /** PostgreSQL schema to use (default: 'public'). */\n schema?: string;\n /** Statement timeout in milliseconds applied to every query (default: none). */\n statementTimeoutMs?: number;\n ssl?: import('tls').ConnectionOptions;\n}\n\n// Also used to validate `schema` — both are interpolated directly into SQL text\n// (there is no parameterized way to name a table/schema), so both must be restricted\n// to safe SQL identifiers to avoid injection via adapter construction options.\nconst TABLE_PREFIX_RE = /^[a-z][a-z0-9_]*$/;\n\n/**\n * Restores `createdAt` to a real `Date` on a template read back from JSONB.\n *\n * Templates are persisted with `JSON.stringify`, so `createdAt` comes back as an\n * ISO string even though {@link ApprovalTemplate} types it as a `Date`. Without\n * this, `TemplateRegistry.update()` threads the string into the next\n * `saveTemplate()` call, which throws `TypeError: createdAt.toISOString is not a\n * function`. Mirrors the same helper in `MemoryAdapter`.\n */\nfunction reviveTemplateDates(template: ApprovalTemplate): ApprovalTemplate {\n return { ...template, createdAt: new Date(template.createdAt) };\n}\n\n/**\n * Build the SQL fragment matching a dot-path in the JSONB `data` column.\n *\n * Uses `#>` with a text[] path so a nested path is one parameterised lookup and\n * the path segments are never interpolated into SQL. Comparison is against the\n * value as JSONB, which makes an object or array value match structurally\n * rather than by its serialised text.\n */\nfunction jsonbPathCondition(path: string, paramIdx: number): string {\n return `data #> $${paramIdx}::text[] = $${paramIdx + 1}::jsonb`;\n}\n\n/** Postgres text[] literal for a dot-path, e.g. \"vendor.id\" -> {vendor,id}. */\nfunction toPgTextArray(path: string): string {\n return `{${path\n .split('.')\n .map((seg) => `\"${seg.replace(/([\"\\\\])/g, '\\\\$1')}\"`)\n .join(',')}}`;\n}\n\nexport class PostgresAdapter implements IStorageAdapter {\n private _pool: import('pg').Pool | null = null;\n private readonly prefix: string;\n private readonly schema: string;\n private readonly statementTimeoutMs?: number;\n private readonly externalPool: import('pg').Pool | undefined;\n\n constructor(private readonly opts: PostgresAdapterOptions) {\n const prefix = opts.tablePrefix ?? 'ha';\n if (!TABLE_PREFIX_RE.test(prefix)) {\n throw new ApprovalValidationError(\n `PostgresAdapter: tablePrefix \"${prefix}\" is invalid. Must match /^[a-z][a-z0-9_]*$/. Only lowercase letters, digits, and underscores are allowed.`,\n );\n }\n const schema = opts.schema ?? 'public';\n if (!TABLE_PREFIX_RE.test(schema)) {\n throw new ApprovalValidationError(\n `PostgresAdapter: schema \"${schema}\" is invalid. Must match /^[a-z][a-z0-9_]*$/. Only lowercase letters, digits, and underscores are allowed.`,\n );\n }\n this.prefix = prefix;\n this.schema = schema;\n this.statementTimeoutMs = opts.statementTimeoutMs;\n this.externalPool = opts.pool;\n }\n\n private async getPool(): Promise<import('pg').Pool> {\n if (this.externalPool) return this.externalPool;\n if (this._pool) return this._pool;\n const { default: pg } = await import('pg');\n this._pool = new pg.Pool({\n connectionString: this.opts.connectionString,\n ssl: this.opts.ssl,\n });\n return this._pool;\n }\n\n private get p() {\n return `${this.schema}.${this.prefix}`;\n }\n\n async migrate(): Promise<void> {\n const pool = await this.getPool();\n await pool.query(`\n CREATE TABLE IF NOT EXISTS ${this.p}_templates (\n tenant_id TEXT NOT NULL,\n name TEXT NOT NULL,\n data JSONB NOT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n PRIMARY KEY (tenant_id, name)\n );\n\n CREATE TABLE IF NOT EXISTS ${this.p}_instances (\n id TEXT NOT NULL,\n tenant_id TEXT NOT NULL,\n template_id TEXT NOT NULL DEFAULT '',\n template_name TEXT NOT NULL,\n document_id TEXT NOT NULL,\n document_type TEXT NOT NULL,\n submitted_by TEXT NOT NULL,\n status TEXT NOT NULL,\n current_level INT NOT NULL,\n version INT NOT NULL DEFAULT 1,\n idempotency_key TEXT,\n data JSONB NOT NULL DEFAULT '{}',\n metadata JSONB NOT NULL DEFAULT '{}',\n levels JSONB NOT NULL DEFAULT '[]',\n parent_instance_id TEXT,\n expires_at TIMESTAMPTZ,\n deadline_action TEXT,\n sla_deadline_at TIMESTAMPTZ,\n sla_breached_at TIMESTAMPTZ,\n template_snapshot JSONB,\n info_request JSONB,\n attachments JSONB NOT NULL DEFAULT '[]',\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n PRIMARY KEY (tenant_id, id),\n UNIQUE (tenant_id, idempotency_key)\n );\n\n CREATE TABLE IF NOT EXISTS ${this.p}_audit_log (\n id BIGSERIAL PRIMARY KEY,\n tenant_id TEXT NOT NULL,\n instance_id TEXT NOT NULL,\n action TEXT NOT NULL,\n actor_id TEXT NOT NULL,\n actor_role TEXT,\n actor_ip TEXT,\n actor_user_agent TEXT,\n trace_id TEXT,\n level INT NOT NULL,\n timestamp TIMESTAMPTZ NOT NULL,\n old_value JSONB,\n new_value JSONB,\n comment TEXT,\n reason TEXT,\n delegate_to TEXT\n );\n\n CREATE INDEX IF NOT EXISTS ${this.p}_instances_tenant_status\n ON ${this.p}_instances (tenant_id, status);\n\n CREATE INDEX IF NOT EXISTS ${this.p}_instances_tenant_updated\n ON ${this.p}_instances (tenant_id, updated_at);\n\n CREATE INDEX IF NOT EXISTS ${this.p}_audit_log_instance\n ON ${this.p}_audit_log (tenant_id, instance_id);\n\n -- Add new columns to existing tables (idempotent for upgrades)\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS template_id TEXT NOT NULL DEFAULT '';\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS parent_instance_id TEXT;\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS expires_at TIMESTAMPTZ;\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS deadline_action TEXT;\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS sla_deadline_at TIMESTAMPTZ;\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS sla_breached_at TIMESTAMPTZ;\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS template_snapshot JSONB;\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS info_request JSONB;\n ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS attachments JSONB NOT NULL DEFAULT '[]';\n `);\n }\n\n /** Enable PostgreSQL Row Level Security for the instances and audit_log tables. */\n async enableRLS(): Promise<void> {\n const pool = await this.getPool();\n await pool.query(`\n ALTER TABLE ${this.p}_instances ENABLE ROW LEVEL SECURITY;\n ALTER TABLE ${this.p}_audit_log ENABLE ROW LEVEL SECURITY;\n\n DROP POLICY IF EXISTS ${this.p}_tenant_isolation ON ${this.p}_instances;\n CREATE POLICY ${this.p}_tenant_isolation ON ${this.p}_instances\n USING (tenant_id = current_setting('rls.tenant_id', TRUE));\n\n DROP POLICY IF EXISTS ${this.p}_audit_tenant_isolation ON ${this.p}_audit_log;\n CREATE POLICY ${this.p}_audit_tenant_isolation ON ${this.p}_audit_log\n USING (tenant_id = current_setting('rls.tenant_id', TRUE));\n `);\n }\n\n // ─── Templates ────────────────────────────────────────────────────────────\n\n async saveTemplate(template: ApprovalTemplate): Promise<void> {\n const pool = await this.getPool();\n await pool.query(\n `INSERT INTO ${this.p}_templates (tenant_id, name, data, created_at)\n VALUES ($1, $2, $3, $4)\n ON CONFLICT (tenant_id, name) DO UPDATE SET data = EXCLUDED.data`,\n [\n template.tenantId,\n template.name,\n JSON.stringify(template),\n template.createdAt.toISOString(),\n ],\n );\n }\n\n async getTemplate(tenantId: string, name: string): Promise<ApprovalTemplate | null> {\n const pool = await this.getPool();\n const result = await pool.query<{ data: ApprovalTemplate }>(\n `SELECT data FROM ${this.p}_templates WHERE tenant_id = $1 AND name = $2`,\n [tenantId, name],\n );\n const row = result.rows[0];\n return row ? reviveTemplateDates(row.data) : null;\n }\n\n async listTemplates(tenantId: string): Promise<ApprovalTemplate[]> {\n const pool = await this.getPool();\n const result = await pool.query<{ data: ApprovalTemplate }>(\n `SELECT data FROM ${this.p}_templates WHERE tenant_id = $1 ORDER BY created_at ASC`,\n [tenantId],\n );\n return result.rows.map((r) => reviveTemplateDates(r.data));\n }\n\n // ─── Instances ────────────────────────────────────────────────────────────\n\n async saveInstance(instance: ApprovalInstance): Promise<void> {\n const pool = await this.getPool();\n await pool.query(\n `INSERT INTO ${this.p}_instances\n (id, tenant_id, template_id, template_name, document_id, document_type, submitted_by,\n status, current_level, version, idempotency_key,\n data, metadata, levels,\n parent_instance_id, expires_at, deadline_action,\n sla_deadline_at, sla_breached_at, template_snapshot, info_request, attachments,\n created_at, updated_at)\n VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24)\n ON CONFLICT (tenant_id, id) DO NOTHING`,\n [\n instance.id,\n instance.tenantId,\n instance.templateId,\n instance.templateName,\n instance.documentId,\n instance.documentType,\n instance.submittedBy,\n instance.status,\n instance.currentLevel,\n instance.version,\n instance.idempotencyKey ?? null,\n JSON.stringify(instance.data),\n JSON.stringify(instance.metadata),\n JSON.stringify(instance.levels),\n instance.parentInstanceId ?? null,\n instance.expiresAt?.toISOString() ?? null,\n instance.deadlineAction ?? null,\n instance.slaDeadlineAt?.toISOString() ?? null,\n instance.slaBreachedAt?.toISOString() ?? null,\n instance.templateSnapshot ? JSON.stringify(instance.templateSnapshot) : null,\n instance.infoRequest ? JSON.stringify(instance.infoRequest) : null,\n JSON.stringify(instance.attachments ?? []),\n instance.createdAt.toISOString(),\n instance.updatedAt.toISOString(),\n ],\n );\n }\n\n async updateInstance(instance: ApprovalInstance, expectedVersion: number): Promise<void> {\n const pool = await this.getPool();\n const result = await pool.query(\n // Every column an engine operation can mutate must be written here.\n // Listing only a subset silently dropped writes: updateData() changed\n // `data`, requestInfo() set `info_request`, and provideInfo() shifted the\n // deadline columns — none of which reached the database.\n `UPDATE ${this.p}_instances SET\n status = $4,\n current_level = $5,\n version = $6 + 1,\n levels = $7,\n sla_breached_at = $8,\n updated_at = $9,\n data = $10,\n metadata = $11,\n expires_at = $12,\n deadline_action = $13,\n sla_deadline_at = $14,\n template_snapshot = $15,\n info_request = $16,\n attachments = $17\n WHERE tenant_id = $1 AND id = $2 AND version = $3\n RETURNING id`,\n [\n instance.tenantId,\n instance.id,\n expectedVersion,\n instance.status,\n instance.currentLevel,\n expectedVersion,\n JSON.stringify(instance.levels),\n instance.slaBreachedAt?.toISOString() ?? null,\n instance.updatedAt.toISOString(),\n JSON.stringify(instance.data ?? {}),\n JSON.stringify(instance.metadata ?? {}),\n instance.expiresAt?.toISOString() ?? null,\n instance.deadlineAction ?? null,\n instance.slaDeadlineAt?.toISOString() ?? null,\n instance.templateSnapshot ? JSON.stringify(instance.templateSnapshot) : null,\n instance.infoRequest ? JSON.stringify(instance.infoRequest) : null,\n JSON.stringify(instance.attachments ?? []),\n ],\n );\n if (result.rowCount === 0) throw new ApprovalConflictError(instance.id);\n }\n\n async getInstance(tenantId: string, id: string): Promise<ApprovalInstance | null> {\n const pool = await this.getPool();\n const result = await pool.query(\n `SELECT * FROM ${this.p}_instances WHERE tenant_id = $1 AND id = $2`,\n [tenantId, id],\n );\n const row = result.rows[0];\n if (!row) return null;\n return this.rowToInstance(row as Record<string, unknown>);\n }\n\n async getInstancesByApprover(\n tenantId: string,\n approverId: string,\n opts?: PaginationOpts,\n ): Promise<PaginatedResult<ApprovalInstance>> {\n const pool = await this.getPool();\n const offset = opts?.offset ?? 0;\n const limit = opts?.limit ?? 50;\n\n const result = await pool.query(\n `SELECT *, COUNT(*) OVER() AS total_count\n FROM ${this.p}_instances\n WHERE tenant_id = $1\n AND status = 'pending'\n AND EXISTS (\n SELECT 1 FROM jsonb_array_elements(levels) AS lvl\n WHERE lvl->>'status' = 'pending'\n AND lvl->'approverIds' @> $2::jsonb\n )\n ORDER BY updated_at ASC\n LIMIT $3 OFFSET $4`,\n [tenantId, JSON.stringify([approverId]), limit, offset],\n );\n\n const total = result.rows[0]\n ? Number((result.rows[0] as Record<string, unknown>)['total_count'])\n : 0;\n return {\n items: result.rows.map((r) => this.rowToInstance(r as Record<string, unknown>)),\n total,\n };\n }\n\n async getInstancesByFilter(\n tenantId: string,\n filter: InstanceFilter,\n opts?: PaginationOpts,\n ): Promise<PaginatedResult<ApprovalInstance>> {\n const pool = await this.getPool();\n const conditions: string[] = ['tenant_id = $1'];\n const params: unknown[] = [tenantId];\n let idx = 2;\n\n if (filter.status) {\n conditions.push(`status = $${idx++}`);\n params.push(filter.status);\n }\n if (filter.documentType) {\n conditions.push(`document_type = $${idx++}`);\n params.push(filter.documentType);\n }\n if (filter.submittedBy) {\n conditions.push(`submitted_by = $${idx++}`);\n params.push(filter.submittedBy);\n }\n if (filter.templateName) {\n conditions.push(`template_name = $${idx++}`);\n params.push(filter.templateName);\n }\n if (filter.fromDate) {\n conditions.push(`created_at >= $${idx++}`);\n params.push(filter.fromDate.toISOString());\n }\n if (filter.toDate) {\n conditions.push(`created_at <= $${idx++}`);\n params.push(filter.toDate.toISOString());\n }\n if (filter.data) {\n for (const [path, expected] of Object.entries(filter.data)) {\n conditions.push(jsonbPathCondition(path, idx));\n params.push(toPgTextArray(path), JSON.stringify(expected ?? null));\n idx += 2;\n }\n }\n\n const offset = opts?.offset ?? 0;\n const limit = opts?.limit ?? 50;\n params.push(limit, offset);\n\n const sql = `SELECT *, COUNT(*) OVER() AS total_count\n FROM ${this.p}_instances\n WHERE ${conditions.join(' AND ')}\n ORDER BY created_at DESC\n LIMIT $${idx++} OFFSET $${idx}`;\n\n const result = await pool.query(sql, params);\n const total = result.rows[0]\n ? Number((result.rows[0] as Record<string, unknown>)['total_count'])\n : 0;\n return {\n items: result.rows.map((r) => this.rowToInstance(r as Record<string, unknown>)),\n total,\n };\n }\n\n async deleteInstance(tenantId: string, id: string): Promise<boolean> {\n const pool = await this.getPool();\n // Audit rows first: if the second statement fails, orphaned audit rows are a\n // far better outcome than audit rows silently outliving nothing.\n await pool.query(`DELETE FROM ${this.p}_audit_log WHERE tenant_id = $1 AND instance_id = $2`, [\n tenantId,\n id,\n ]);\n const result = await pool.query(\n `DELETE FROM ${this.p}_instances WHERE tenant_id = $1 AND id = $2 RETURNING id`,\n [tenantId, id],\n );\n return (result.rowCount ?? 0) > 0;\n }\n\n async countInstances(tenantId: string, filter: InstanceFilter): Promise<number> {\n const pool = await this.getPool();\n const conditions: string[] = ['tenant_id = $1'];\n const params: unknown[] = [tenantId];\n let idx = 2;\n\n if (filter.status) {\n conditions.push(`status = $${idx++}`);\n params.push(filter.status);\n }\n if (filter.documentType) {\n conditions.push(`document_type = $${idx++}`);\n params.push(filter.documentType);\n }\n if (filter.submittedBy) {\n conditions.push(`submitted_by = $${idx++}`);\n params.push(filter.submittedBy);\n }\n if (filter.templateName) {\n conditions.push(`template_name = $${idx++}`);\n params.push(filter.templateName);\n }\n if (filter.fromDate) {\n conditions.push(`created_at >= $${idx++}`);\n params.push(filter.fromDate.toISOString());\n }\n if (filter.toDate) {\n conditions.push(`created_at <= $${idx++}`);\n params.push(filter.toDate.toISOString());\n }\n if (filter.data) {\n for (const [path, expected] of Object.entries(filter.data)) {\n conditions.push(jsonbPathCondition(path, idx));\n params.push(toPgTextArray(path), JSON.stringify(expected ?? null));\n idx += 2;\n }\n }\n\n // A bare COUNT(*): no row payload to serialise, and the planner is free to\n // satisfy it from an index rather than touching the JSONB columns at all.\n const result = await pool.query(\n `SELECT COUNT(*)::bigint AS count FROM ${this.p}_instances WHERE ${conditions.join(' AND ')}`,\n params,\n );\n const row = result.rows[0] as Record<string, unknown> | undefined;\n return row ? Number(row['count']) : 0;\n }\n\n async getOverdueInstances(\n tenantId: string,\n asOf: Date,\n filter: InstanceFilter = {},\n ): Promise<ApprovalInstance[]> {\n const pool = await this.getPool();\n const values: unknown[] = [tenantId, asOf.toISOString()];\n let query = `SELECT * FROM ${this.p}_instances\n WHERE tenant_id = $1\n AND status = 'pending'`;\n\n if (filter.documentType) {\n values.push(filter.documentType);\n query += ` AND document_type = $${values.length}`;\n }\n if (filter.submittedBy) {\n values.push(filter.submittedBy);\n query += ` AND submitted_by = $${values.length}`;\n }\n\n query += ` AND (\n EXISTS (\n SELECT 1 FROM jsonb_array_elements(levels) AS lvl\n WHERE (lvl->>'escalationDueAt') IS NOT NULL\n AND (lvl->>'escalationDueAt')::timestamptz <= $2\n )\n OR (expires_at IS NOT NULL AND expires_at <= $2)\n OR (sla_deadline_at IS NOT NULL AND sla_deadline_at <= $2 AND sla_breached_at IS NULL)\n OR EXISTS (\n SELECT 1 FROM jsonb_array_elements(levels) AS lvl\n WHERE lvl->>'status' = 'pending'\n AND (lvl->>'delegatedUntil') IS NOT NULL\n AND (lvl->>'delegatedUntil')::timestamptz <= $2\n AND (lvl->>'delegatedFrom') IS NOT NULL\n )\n OR EXISTS (\n SELECT 1 FROM jsonb_array_elements(levels) AS lvl\n WHERE lvl->>'status' = 'pending'\n AND (lvl->>'reminderDueAt') IS NOT NULL\n AND (lvl->>'reminderDueAt')::timestamptz <= $2\n )\n )`;\n const result = await pool.query(query, values);\n return result.rows.map((r) => this.rowToInstance(r as Record<string, unknown>));\n }\n\n async getIdempotentInstance(\n tenantId: string,\n idempotencyKey: string,\n ): Promise<ApprovalInstance | null> {\n const pool = await this.getPool();\n const result = await pool.query(\n `SELECT * FROM ${this.p}_instances WHERE tenant_id = $1 AND idempotency_key = $2`,\n [tenantId, idempotencyKey],\n );\n const row = result.rows[0];\n if (!row) return null;\n return this.rowToInstance(row as Record<string, unknown>);\n }\n\n // ─── Audit (append-only) ──────────────────────────────────────────────────\n\n async appendAuditEntry(tenantId: string, instanceId: string, entry: AuditEntry): Promise<void> {\n const pool = await this.getPool();\n await pool.query(\n `INSERT INTO ${this.p}_audit_log\n (tenant_id, instance_id, action, actor_id, actor_role, actor_ip,\n actor_user_agent, trace_id, level, timestamp,\n old_value, new_value, comment, reason, delegate_to)\n VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)`,\n [\n tenantId,\n instanceId,\n entry.action,\n entry.actorId,\n entry.actorRole ?? null,\n entry.actorIp ?? null,\n entry.actorUserAgent ?? null,\n entry.traceId ?? null,\n entry.level,\n entry.timestamp.toISOString(),\n entry.oldValue ? JSON.stringify(entry.oldValue) : null,\n entry.newValue ? JSON.stringify(entry.newValue) : null,\n entry.comment ?? null,\n entry.reason ?? null,\n entry.delegateTo ?? null,\n ],\n );\n }\n\n async getInstancesByCursor(\n tenantId: string,\n filter: InstanceFilter,\n opts: CursorPaginationOpts,\n ): Promise<CursorPaginatedResult<ApprovalInstance>> {\n const pool = await this.getPool();\n const conditions: string[] = ['tenant_id = $1'];\n const params: unknown[] = [tenantId];\n let idx = 2;\n\n if (filter.status) {\n conditions.push(`status = $${idx++}`);\n params.push(filter.status);\n }\n if (filter.documentType) {\n conditions.push(`document_type = $${idx++}`);\n params.push(filter.documentType);\n }\n if (filter.submittedBy) {\n conditions.push(`submitted_by = $${idx++}`);\n params.push(filter.submittedBy);\n }\n if (filter.templateName) {\n conditions.push(`template_name = $${idx++}`);\n params.push(filter.templateName);\n }\n if (filter.data) {\n for (const [path, expected] of Object.entries(filter.data)) {\n conditions.push(jsonbPathCondition(path, idx));\n params.push(toPgTextArray(path), JSON.stringify(expected ?? null));\n idx += 2;\n }\n }\n\n const { cursor, limit, direction = 'forward' } = opts;\n\n if (cursor) {\n const decoded = Buffer.from(cursor, 'base64').toString('utf8');\n // The timestamp segment is an ISO-8601 string, which itself contains colons\n // (e.g. \"2026-06-26T09:00:00.000Z\") — the LAST colon is the id separator,\n // not the first. Using indexOf() here previously truncated the timestamp\n // and corrupted the id on every cursor decode.\n const colonIdx = decoded.lastIndexOf(':');\n const ts = decoded.slice(0, colonIdx);\n const id = decoded.slice(colonIdx + 1);\n if (direction === 'forward') {\n conditions.push(`(updated_at, id) > ($${idx++}::timestamptz, $${idx++})`);\n params.push(ts, id);\n } else {\n conditions.push(`(updated_at, id) < ($${idx++}::timestamptz, $${idx++})`);\n params.push(ts, id);\n }\n }\n\n params.push(limit + 1);\n const order = direction === 'backward' ? 'DESC' : 'ASC';\n const sql = `SELECT * FROM ${this.p}_instances\n WHERE ${conditions.join(' AND ')}\n ORDER BY updated_at ${order}, id ${order}\n LIMIT $${idx}`;\n\n const result = await pool.query(sql, params);\n const hasMore = result.rows.length > limit;\n const rows = result.rows.slice(0, limit);\n const items = rows.map((r) => this.rowToInstance(r as Record<string, unknown>));\n\n const nextCursor = hasMore\n ? encodeInstanceCursor(rows[rows.length - 1] as Record<string, unknown>)\n : undefined;\n\n return { items, nextCursor, hasMore };\n }\n\n async end(): Promise<void> {\n await this._pool?.end();\n this._pool = null;\n }\n\n private rowToInstance(row: Record<string, unknown>): ApprovalInstance {\n return {\n id: row['id'] as string,\n tenantId: row['tenant_id'] as string,\n templateId: (row['template_id'] as string | null) ?? '',\n templateName: row['template_name'] as string,\n documentId: row['document_id'] as string,\n documentType: row['document_type'] as string,\n submittedBy: row['submitted_by'] as string,\n status: row['status'] as ApprovalInstance['status'],\n currentLevel: row['current_level'] as number,\n version: row['version'] as number,\n idempotencyKey: (row['idempotency_key'] as string | null) ?? undefined,\n data: row['data'] as Record<string, unknown>,\n metadata: row['metadata'] as Record<string, unknown>,\n levels: row['levels'] as ApprovalInstance['levels'],\n auditLog: [],\n parentInstanceId: (row['parent_instance_id'] as string | null) ?? undefined,\n expiresAt: row['expires_at'] ? new Date(row['expires_at'] as string) : undefined,\n deadlineAction: (row['deadline_action'] as 'cancel' | 'reject' | null) ?? undefined,\n slaDeadlineAt: row['sla_deadline_at']\n ? new Date(row['sla_deadline_at'] as string)\n : undefined,\n slaBreachedAt: row['sla_breached_at']\n ? new Date(row['sla_breached_at'] as string)\n : undefined,\n templateSnapshot:\n (row['template_snapshot'] as ApprovalInstance['templateSnapshot'] | null) ?? undefined,\n // Absent attachments map to undefined, not [], so a Postgres round trip\n // returns the same shape MemoryAdapter does for an instance that never\n // had any — the parity the round-trip test pins down.\n attachments: ((row['attachments'] as Attachment[] | null) ?? []).length\n ? (row['attachments'] as Attachment[]).map((a) => ({ ...a, addedAt: new Date(a.addedAt) }))\n : undefined,\n infoRequest: row['info_request']\n ? {\n ...(row['info_request'] as ApprovalInstance['infoRequest'] & { askedAt: string }),\n askedAt: new Date((row['info_request'] as { askedAt: string }).askedAt),\n }\n : undefined,\n createdAt: new Date(row['created_at'] as string),\n updatedAt: new Date(row['updated_at'] as string),\n };\n }\n}\n\n/**\n * Encode a cursor as base64(updatedAt_iso:id) per the IStorageAdapter contract.\n *\n * `row['updated_at']` normalizes through `new Date(...)` before `.toISOString()`\n * because node-postgres returns TIMESTAMPTZ columns as `Date` instances by default —\n * stringifying a `Date` directly (e.g. via template-literal interpolation) uses\n * `Date.prototype.toString()`, not ISO-8601, and is not guaranteed to round-trip\n * through Postgres's `::timestamptz` cast on decode.\n */\nfunction encodeInstanceCursor(row: Record<string, unknown>): string {\n const updatedAtIso = new Date(row['updated_at'] as string | Date).toISOString();\n return Buffer.from(`${updatedAtIso}:${row['id']}`).toString('base64');\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -915,6 +915,7 @@ function computeTimingStats(samples) {
|
|
|
915
915
|
|
|
916
916
|
// src/engine/ApprovalEngine.ts
|
|
917
917
|
var MAX_SUBWORKFLOW_DEPTH = 5;
|
|
918
|
+
var DEFAULT_PURGE_LIMIT = 1e3;
|
|
918
919
|
var DEFAULT_MAX_REMINDERS = 3;
|
|
919
920
|
var DEFAULT_MAX_ATTEMPTS = 3;
|
|
920
921
|
var DEFAULT_BASE_DELAY_MS = 50;
|
|
@@ -926,6 +927,7 @@ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
|
926
927
|
]);
|
|
927
928
|
var CYCLE_TIME_STATUSES = ["approved", "rejected", "cancelled"];
|
|
928
929
|
var CYCLE_TIME_FETCH_BATCH_SIZE = 500;
|
|
930
|
+
var TEMPLATE_BUNDLE_VERSION = 1;
|
|
929
931
|
var ApprovalEngine = class _ApprovalEngine {
|
|
930
932
|
constructor(opts) {
|
|
931
933
|
this.opts = opts;
|
|
@@ -3007,6 +3009,194 @@ var ApprovalEngine = class _ApprovalEngine {
|
|
|
3007
3009
|
oldestAgeMs: Number.isFinite(row.oldest) ? now.getTime() - row.oldest : 0
|
|
3008
3010
|
})).sort((a, b) => b.pending - a.pending || a.approverId.localeCompare(b.approverId));
|
|
3009
3011
|
}
|
|
3012
|
+
/**
|
|
3013
|
+
* Export templates as a portable bundle.
|
|
3014
|
+
*
|
|
3015
|
+
* Approval configuration is written once and then has to travel — authored in
|
|
3016
|
+
* a sandbox, reviewed, promoted to production. Reading `listTemplates()` and
|
|
3017
|
+
* re-posting the rows carried each environment's own `id`, `tenantId` and
|
|
3018
|
+
* version lineage with it, which either collided on arrival or silently
|
|
3019
|
+
* claimed a history the target never had. This strips all of it.
|
|
3020
|
+
*
|
|
3021
|
+
* @param names - Templates to include; omit for all of them.
|
|
3022
|
+
*/
|
|
3023
|
+
async exportTemplates(names) {
|
|
3024
|
+
const all = await this.registry.list();
|
|
3025
|
+
const wanted = names ? all.filter((t) => names.includes(t.name)) : all;
|
|
3026
|
+
if (names) {
|
|
3027
|
+
const missing = names.filter((n) => !all.some((t) => t.name === n));
|
|
3028
|
+
if (missing.length > 0) {
|
|
3029
|
+
throw new ApprovalTemplateNotFoundError(missing.join(", "));
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
return {
|
|
3033
|
+
bundleVersion: TEMPLATE_BUNDLE_VERSION,
|
|
3034
|
+
exportedAt: this.clock.now(),
|
|
3035
|
+
templates: wanted.map((t) => {
|
|
3036
|
+
const {
|
|
3037
|
+
id: _id,
|
|
3038
|
+
tenantId: _tenantId,
|
|
3039
|
+
createdAt: _createdAt,
|
|
3040
|
+
version: _version,
|
|
3041
|
+
previousVersionId: _previousVersionId,
|
|
3042
|
+
...config
|
|
3043
|
+
} = t;
|
|
3044
|
+
return config;
|
|
3045
|
+
})
|
|
3046
|
+
};
|
|
3047
|
+
}
|
|
3048
|
+
/**
|
|
3049
|
+
* Import a bundle produced by {@link exportTemplates}.
|
|
3050
|
+
*
|
|
3051
|
+
* **Every template is validated before any is written.** A bundle that is
|
|
3052
|
+
* half-applied is worse than one rejected outright: the tenant is left in a
|
|
3053
|
+
* state matching neither environment, and the operator has no way to tell
|
|
3054
|
+
* which half landed. Per-template failures during the write phase are still
|
|
3055
|
+
* reported individually, since a storage error can occur after validation
|
|
3056
|
+
* passes.
|
|
3057
|
+
*
|
|
3058
|
+
* @param bundle - The bundle to apply.
|
|
3059
|
+
* @param opts - `mode: 'create'` (default) refuses to touch existing
|
|
3060
|
+
* templates; `'upsert'` updates them. `dryRun` reports without writing.
|
|
3061
|
+
*/
|
|
3062
|
+
async importTemplates(bundle, opts = {}) {
|
|
3063
|
+
const mode = opts.mode ?? "create";
|
|
3064
|
+
const dryRun = opts.dryRun ?? false;
|
|
3065
|
+
if (bundle.bundleVersion !== TEMPLATE_BUNDLE_VERSION) {
|
|
3066
|
+
throw new ApprovalValidationError(
|
|
3067
|
+
`Unsupported template bundle version ${bundle.bundleVersion}; this engine reads version ${TEMPLATE_BUNDLE_VERSION}.`
|
|
3068
|
+
);
|
|
3069
|
+
}
|
|
3070
|
+
if (!Array.isArray(bundle.templates) || bundle.templates.length === 0) {
|
|
3071
|
+
throw new ApprovalValidationError("Template bundle contains no templates.");
|
|
3072
|
+
}
|
|
3073
|
+
const duplicates = bundle.templates.map((t) => t.name).filter((name, i, all) => all.indexOf(name) !== i);
|
|
3074
|
+
if (duplicates.length > 0) {
|
|
3075
|
+
throw new ApprovalValidationError(
|
|
3076
|
+
`Template bundle contains duplicate names: ${[...new Set(duplicates)].join(", ")}.`
|
|
3077
|
+
);
|
|
3078
|
+
}
|
|
3079
|
+
const invalid = [];
|
|
3080
|
+
for (const config of bundle.templates) {
|
|
3081
|
+
const result2 = this.validateTemplate(config);
|
|
3082
|
+
if (!result2.valid) {
|
|
3083
|
+
invalid.push({
|
|
3084
|
+
name: config.name,
|
|
3085
|
+
message: result2.errors[0]?.message ?? "unknown validation error"
|
|
3086
|
+
});
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
if (invalid.length > 0) {
|
|
3090
|
+
throw new ApprovalValidationError(
|
|
3091
|
+
`Template bundle failed validation and was not applied: ${invalid.map((e) => `${e.name}: ${e.message}`).join("; ")}`
|
|
3092
|
+
);
|
|
3093
|
+
}
|
|
3094
|
+
const result = { created: [], updated: [], skipped: [], errors: [], dryRun };
|
|
3095
|
+
for (const config of bundle.templates) {
|
|
3096
|
+
const existing = await this.opts.adapter.getTemplate(this.tenantId, config.name);
|
|
3097
|
+
try {
|
|
3098
|
+
if (existing && mode === "create") {
|
|
3099
|
+
result.skipped.push(config.name);
|
|
3100
|
+
continue;
|
|
3101
|
+
}
|
|
3102
|
+
if (existing) {
|
|
3103
|
+
if (!dryRun) await this.registry.update(config);
|
|
3104
|
+
result.updated.push(config.name);
|
|
3105
|
+
} else {
|
|
3106
|
+
if (!dryRun) await this.registry.define(config);
|
|
3107
|
+
result.created.push(config.name);
|
|
3108
|
+
}
|
|
3109
|
+
} catch (err) {
|
|
3110
|
+
result.errors.push({ name: config.name, message: err.message });
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
this.logger.info("importTemplates: bundle applied", {
|
|
3114
|
+
tenantId: this.tenantId,
|
|
3115
|
+
mode,
|
|
3116
|
+
dryRun,
|
|
3117
|
+
created: result.created.length,
|
|
3118
|
+
updated: result.updated.length,
|
|
3119
|
+
skipped: result.skipped.length,
|
|
3120
|
+
errors: result.errors.length
|
|
3121
|
+
});
|
|
3122
|
+
return result;
|
|
3123
|
+
}
|
|
3124
|
+
/**
|
|
3125
|
+
* Permanently remove finished approvals older than a cut-off.
|
|
3126
|
+
*
|
|
3127
|
+
* Approval tables only grow, and data-minimisation rules eventually require
|
|
3128
|
+
* old records to go. There was no way to remove one, so operators reached
|
|
3129
|
+
* around the library and deleted rows directly — which is exactly where
|
|
3130
|
+
* orphaned audit rows and half-deleted instances come from.
|
|
3131
|
+
*
|
|
3132
|
+
* **Only terminal instances are eligible.** A pending approval is live work;
|
|
3133
|
+
* deleting one would strand a document with no way to finish and no record of
|
|
3134
|
+
* why. Passing a non-terminal status is rejected rather than quietly ignored,
|
|
3135
|
+
* because a caller who asked to purge pending work has misunderstood
|
|
3136
|
+
* something and should hear about it.
|
|
3137
|
+
*
|
|
3138
|
+
* This is irreversible and removes the audit trail with the instance. In many
|
|
3139
|
+
* deployments that trail *is* the compliance record, which is why the
|
|
3140
|
+
* underlying `deleteInstance` is an optional adapter method: an adapter that
|
|
3141
|
+
* does not implement it makes the whole operation unavailable, and this
|
|
3142
|
+
* throws rather than reporting a successful purge of nothing.
|
|
3143
|
+
*
|
|
3144
|
+
* @param opts - Cut-off, optional status/type scoping, safety limit, dry run.
|
|
3145
|
+
* @returns What was removed, or would be under `dryRun`.
|
|
3146
|
+
*/
|
|
3147
|
+
async purgeInstances(opts) {
|
|
3148
|
+
const dryRun = opts.dryRun ?? false;
|
|
3149
|
+
const limit = opts.limit ?? DEFAULT_PURGE_LIMIT;
|
|
3150
|
+
if (!(opts.olderThan instanceof Date) || Number.isNaN(opts.olderThan.getTime())) {
|
|
3151
|
+
throw new ApprovalValidationError("purgeInstances requires a valid olderThan date.");
|
|
3152
|
+
}
|
|
3153
|
+
const requested = opts.statuses ?? [...TERMINAL_STATUSES];
|
|
3154
|
+
const nonTerminal = requested.filter((st) => !TERMINAL_STATUSES.has(st));
|
|
3155
|
+
if (nonTerminal.length > 0) {
|
|
3156
|
+
throw new ApprovalValidationError(
|
|
3157
|
+
`purgeInstances refuses non-terminal statuses (${nonTerminal.join(", ")}): a pending approval is live work, and removing it would strand the document it belongs to.`
|
|
3158
|
+
);
|
|
3159
|
+
}
|
|
3160
|
+
const deleteInstance = this.opts.adapter.deleteInstance?.bind(this.opts.adapter);
|
|
3161
|
+
if (!deleteInstance && !dryRun) {
|
|
3162
|
+
throw new ApprovalValidationError(
|
|
3163
|
+
"The configured storage adapter does not implement deleteInstance(), so instances cannot be purged."
|
|
3164
|
+
);
|
|
3165
|
+
}
|
|
3166
|
+
const result = { purged: [], scanned: 0, dryRun };
|
|
3167
|
+
for (const status of requested) {
|
|
3168
|
+
if (result.purged.length >= limit) break;
|
|
3169
|
+
const page = await this.opts.adapter.getInstancesByFilter(
|
|
3170
|
+
this.tenantId,
|
|
3171
|
+
{
|
|
3172
|
+
status,
|
|
3173
|
+
toDate: opts.olderThan,
|
|
3174
|
+
...opts.documentType ? { documentType: opts.documentType } : {}
|
|
3175
|
+
},
|
|
3176
|
+
{ limit, offset: 0 }
|
|
3177
|
+
);
|
|
3178
|
+
for (const instance of page.items) {
|
|
3179
|
+
if (result.purged.length >= limit) break;
|
|
3180
|
+
result.scanned++;
|
|
3181
|
+
if (!TERMINAL_STATUSES.has(instance.status)) continue;
|
|
3182
|
+
if (new Date(instance.createdAt) > opts.olderThan) continue;
|
|
3183
|
+
if (!dryRun) await deleteInstance(this.tenantId, instance.id);
|
|
3184
|
+
result.purged.push({
|
|
3185
|
+
instanceId: instance.id,
|
|
3186
|
+
documentId: instance.documentId,
|
|
3187
|
+
status: instance.status
|
|
3188
|
+
});
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
this.logger.info("purgeInstances: sweep complete", {
|
|
3192
|
+
tenantId: this.tenantId,
|
|
3193
|
+
olderThan: opts.olderThan.toISOString(),
|
|
3194
|
+
scanned: result.scanned,
|
|
3195
|
+
purged: result.purged.length,
|
|
3196
|
+
dryRun
|
|
3197
|
+
});
|
|
3198
|
+
return result;
|
|
3199
|
+
}
|
|
3010
3200
|
async getStatistics(filter = {}) {
|
|
3011
3201
|
const statuses = [
|
|
3012
3202
|
"pending",
|
|
@@ -4009,6 +4199,10 @@ var MemoryAdapter = class {
|
|
|
4009
4199
|
return hasOverdueEscalation || isExpired || hasSLABreach || hasDelegationExpiry || hasDueReminder;
|
|
4010
4200
|
}).map((i) => reviveDates(deepClone(i)));
|
|
4011
4201
|
}
|
|
4202
|
+
async deleteInstance(tenantId, id) {
|
|
4203
|
+
const key = `${tenantId}:${id}`;
|
|
4204
|
+
return this.instances.delete(key);
|
|
4205
|
+
}
|
|
4012
4206
|
async countInstances(tenantId, filter) {
|
|
4013
4207
|
let count = 0;
|
|
4014
4208
|
for (const instance of this.instances.values()) {
|
|
@@ -4106,6 +4300,7 @@ exports.ApprovalTemplateNotFoundError = ApprovalTemplateNotFoundError;
|
|
|
4106
4300
|
exports.ApprovalValidationError = ApprovalValidationError;
|
|
4107
4301
|
exports.EscalationScheduler = EscalationScheduler;
|
|
4108
4302
|
exports.MemoryAdapter = MemoryAdapter;
|
|
4303
|
+
exports.TEMPLATE_BUNDLE_VERSION = TEMPLATE_BUNDLE_VERSION;
|
|
4109
4304
|
exports.defaultIdGenerator = defaultIdGenerator;
|
|
4110
4305
|
exports.noopLogger = noopLogger;
|
|
4111
4306
|
exports.systemClock = systemClock;
|