hierarchical-approval 1.5.0 → 1.6.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 CHANGED
@@ -7,6 +7,48 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  _Nothing yet._
9
9
 
10
+ ## [1.6.0] - 2026-09-04
11
+
12
+ ### Fixed — PostgresAdapter silently dropped most instance updates
13
+
14
+ **Anyone running the PostgreSQL adapter should upgrade and run `migrate()`.**
15
+ `MemoryAdapter` stores whole objects and was always correct, so this reproduced
16
+ only against a real database — which is exactly where it mattered.
17
+
18
+ `updateInstance` wrote just six columns (`status`, `current_level`, `version`,
19
+ `levels`, `sla_breached_at`, `updated_at`). Every other field an operation
20
+ mutates was computed, logged, emitted as an event, and then thrown away on
21
+ write:
22
+
23
+ - **`updateData()` (0.9.0) did not change the stored document.** The engine
24
+ recomputed the chain and emitted `approval:data_updated`, but `data` was never
25
+ written, so the next read returned the old values — and any later
26
+ re-evaluation ran against them.
27
+ - **`requestInfo()` (1.5.0) lost the hold entirely.** `info_request` had no
28
+ column at all, so a held instance came back unheld: `provideInfo()` then threw
29
+ "No clarification request is open", and the scheduler escalated and expired an
30
+ approval that was supposed to be paused.
31
+ - **`provideInfo()`'s deadline give-back never landed** — `expires_at` and
32
+ `sla_deadline_at` were not written, so the time an instance spent on hold was
33
+ silently forfeited.
34
+ - `metadata`, `deadline_action` and `template_snapshot` were likewise never
35
+ updated after insert.
36
+
37
+ `updateInstance` now writes every mutable column, `saveInstance` and
38
+ `rowToInstance` carry `info_request` (reviving `askedAt` as a `Date`), and
39
+ `migrate()` adds the column to existing deployments via
40
+ `ADD COLUMN IF NOT EXISTS`. Optimistic concurrency is unchanged — the update
41
+ still guards on `version`.
42
+
43
+ Regression tests assert the generated SQL and parameters for each field, so a
44
+ future column cannot be added to the type and forgotten in the writer.
45
+
46
+ ### Note on the roadmap
47
+
48
+ Attachment references were the planned 1.6.0. They are deferred: shipping a new
49
+ feature ahead of a data-loss fix in an already-published release would have been
50
+ the wrong order.
51
+
10
52
  ## [1.5.0] - 2026-09-04
11
53
 
12
54
  ### Added — request for information
@@ -115,6 +115,7 @@ var PostgresAdapter = class {
115
115
  sla_deadline_at TIMESTAMPTZ,
116
116
  sla_breached_at TIMESTAMPTZ,
117
117
  template_snapshot JSONB,
118
+ info_request JSONB,
118
119
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
119
120
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
120
121
  PRIMARY KEY (tenant_id, id),
@@ -157,6 +158,7 @@ var PostgresAdapter = class {
157
158
  ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS sla_deadline_at TIMESTAMPTZ;
158
159
  ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS sla_breached_at TIMESTAMPTZ;
159
160
  ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS template_snapshot JSONB;
161
+ ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS info_request JSONB;
160
162
  `);
161
163
  }
162
164
  /** Enable PostgreSQL Row Level Security for the instances and audit_log tables. */
@@ -216,9 +218,9 @@ var PostgresAdapter = class {
216
218
  status, current_level, version, idempotency_key,
217
219
  data, metadata, levels,
218
220
  parent_instance_id, expires_at, deadline_action,
219
- sla_deadline_at, sla_breached_at, template_snapshot,
221
+ sla_deadline_at, sla_breached_at, template_snapshot, info_request,
220
222
  created_at, updated_at)
221
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22)
223
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23)
222
224
  ON CONFLICT (tenant_id, id) DO NOTHING`,
223
225
  [
224
226
  instance.id,
@@ -241,6 +243,7 @@ var PostgresAdapter = class {
241
243
  instance.slaDeadlineAt?.toISOString() ?? null,
242
244
  instance.slaBreachedAt?.toISOString() ?? null,
243
245
  instance.templateSnapshot ? JSON.stringify(instance.templateSnapshot) : null,
246
+ instance.infoRequest ? JSON.stringify(instance.infoRequest) : null,
244
247
  instance.createdAt.toISOString(),
245
248
  instance.updatedAt.toISOString()
246
249
  ]
@@ -249,13 +252,24 @@ var PostgresAdapter = class {
249
252
  async updateInstance(instance, expectedVersion) {
250
253
  const pool = await this.getPool();
251
254
  const result = await pool.query(
255
+ // Every column an engine operation can mutate must be written here.
256
+ // Listing only a subset silently dropped writes: updateData() changed
257
+ // `data`, requestInfo() set `info_request`, and provideInfo() shifted the
258
+ // deadline columns — none of which reached the database.
252
259
  `UPDATE ${this.p}_instances SET
253
- status = $4,
254
- current_level = $5,
255
- version = $6 + 1,
256
- levels = $7,
257
- sla_breached_at = $8,
258
- updated_at = $9
260
+ status = $4,
261
+ current_level = $5,
262
+ version = $6 + 1,
263
+ levels = $7,
264
+ sla_breached_at = $8,
265
+ updated_at = $9,
266
+ data = $10,
267
+ metadata = $11,
268
+ expires_at = $12,
269
+ deadline_action = $13,
270
+ sla_deadline_at = $14,
271
+ template_snapshot = $15,
272
+ info_request = $16
259
273
  WHERE tenant_id = $1 AND id = $2 AND version = $3
260
274
  RETURNING id`,
261
275
  [
@@ -267,7 +281,14 @@ var PostgresAdapter = class {
267
281
  expectedVersion,
268
282
  JSON.stringify(instance.levels),
269
283
  instance.slaBreachedAt?.toISOString() ?? null,
270
- instance.updatedAt.toISOString()
284
+ instance.updatedAt.toISOString(),
285
+ JSON.stringify(instance.data ?? {}),
286
+ JSON.stringify(instance.metadata ?? {}),
287
+ instance.expiresAt?.toISOString() ?? null,
288
+ instance.deadlineAction ?? null,
289
+ instance.slaDeadlineAt?.toISOString() ?? null,
290
+ instance.templateSnapshot ? JSON.stringify(instance.templateSnapshot) : null,
291
+ instance.infoRequest ? JSON.stringify(instance.infoRequest) : null
271
292
  ]
272
293
  );
273
294
  if (result.rowCount === 0) throw new ApprovalConflictError(instance.id);
@@ -516,6 +537,10 @@ var PostgresAdapter = class {
516
537
  slaDeadlineAt: row["sla_deadline_at"] ? new Date(row["sla_deadline_at"]) : void 0,
517
538
  slaBreachedAt: row["sla_breached_at"] ? new Date(row["sla_breached_at"]) : void 0,
518
539
  templateSnapshot: row["template_snapshot"] ?? void 0,
540
+ infoRequest: row["info_request"] ? {
541
+ ...row["info_request"],
542
+ askedAt: new Date(row["info_request"].askedAt)
543
+ } : void 0,
519
544
  createdAt: new Date(row["created_at"]),
520
545
  updatedAt: new Date(row["updated_at"])
521
546
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/errors.ts","../../src/adapters/PostgresAdapter.ts"],"names":[],"mappings":"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","file":"PostgresAdapter.cjs","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 } 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 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 `);\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,\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)\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.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 `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 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 ],\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 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 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.cjs","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 } 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 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 `);\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,\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)\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 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 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 ],\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 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 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"]}
@@ -113,6 +113,7 @@ var PostgresAdapter = class {
113
113
  sla_deadline_at TIMESTAMPTZ,
114
114
  sla_breached_at TIMESTAMPTZ,
115
115
  template_snapshot JSONB,
116
+ info_request JSONB,
116
117
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
117
118
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
118
119
  PRIMARY KEY (tenant_id, id),
@@ -155,6 +156,7 @@ var PostgresAdapter = class {
155
156
  ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS sla_deadline_at TIMESTAMPTZ;
156
157
  ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS sla_breached_at TIMESTAMPTZ;
157
158
  ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS template_snapshot JSONB;
159
+ ALTER TABLE IF EXISTS ${this.p}_instances ADD COLUMN IF NOT EXISTS info_request JSONB;
158
160
  `);
159
161
  }
160
162
  /** Enable PostgreSQL Row Level Security for the instances and audit_log tables. */
@@ -214,9 +216,9 @@ var PostgresAdapter = class {
214
216
  status, current_level, version, idempotency_key,
215
217
  data, metadata, levels,
216
218
  parent_instance_id, expires_at, deadline_action,
217
- sla_deadline_at, sla_breached_at, template_snapshot,
219
+ sla_deadline_at, sla_breached_at, template_snapshot, info_request,
218
220
  created_at, updated_at)
219
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22)
221
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23)
220
222
  ON CONFLICT (tenant_id, id) DO NOTHING`,
221
223
  [
222
224
  instance.id,
@@ -239,6 +241,7 @@ var PostgresAdapter = class {
239
241
  instance.slaDeadlineAt?.toISOString() ?? null,
240
242
  instance.slaBreachedAt?.toISOString() ?? null,
241
243
  instance.templateSnapshot ? JSON.stringify(instance.templateSnapshot) : null,
244
+ instance.infoRequest ? JSON.stringify(instance.infoRequest) : null,
242
245
  instance.createdAt.toISOString(),
243
246
  instance.updatedAt.toISOString()
244
247
  ]
@@ -247,13 +250,24 @@ var PostgresAdapter = class {
247
250
  async updateInstance(instance, expectedVersion) {
248
251
  const pool = await this.getPool();
249
252
  const result = await pool.query(
253
+ // Every column an engine operation can mutate must be written here.
254
+ // Listing only a subset silently dropped writes: updateData() changed
255
+ // `data`, requestInfo() set `info_request`, and provideInfo() shifted the
256
+ // deadline columns — none of which reached the database.
250
257
  `UPDATE ${this.p}_instances SET
251
- status = $4,
252
- current_level = $5,
253
- version = $6 + 1,
254
- levels = $7,
255
- sla_breached_at = $8,
256
- updated_at = $9
258
+ status = $4,
259
+ current_level = $5,
260
+ version = $6 + 1,
261
+ levels = $7,
262
+ sla_breached_at = $8,
263
+ updated_at = $9,
264
+ data = $10,
265
+ metadata = $11,
266
+ expires_at = $12,
267
+ deadline_action = $13,
268
+ sla_deadline_at = $14,
269
+ template_snapshot = $15,
270
+ info_request = $16
257
271
  WHERE tenant_id = $1 AND id = $2 AND version = $3
258
272
  RETURNING id`,
259
273
  [
@@ -265,7 +279,14 @@ var PostgresAdapter = class {
265
279
  expectedVersion,
266
280
  JSON.stringify(instance.levels),
267
281
  instance.slaBreachedAt?.toISOString() ?? null,
268
- instance.updatedAt.toISOString()
282
+ instance.updatedAt.toISOString(),
283
+ JSON.stringify(instance.data ?? {}),
284
+ JSON.stringify(instance.metadata ?? {}),
285
+ instance.expiresAt?.toISOString() ?? null,
286
+ instance.deadlineAction ?? null,
287
+ instance.slaDeadlineAt?.toISOString() ?? null,
288
+ instance.templateSnapshot ? JSON.stringify(instance.templateSnapshot) : null,
289
+ instance.infoRequest ? JSON.stringify(instance.infoRequest) : null
269
290
  ]
270
291
  );
271
292
  if (result.rowCount === 0) throw new ApprovalConflictError(instance.id);
@@ -514,6 +535,10 @@ var PostgresAdapter = class {
514
535
  slaDeadlineAt: row["sla_deadline_at"] ? new Date(row["sla_deadline_at"]) : void 0,
515
536
  slaBreachedAt: row["sla_breached_at"] ? new Date(row["sla_breached_at"]) : void 0,
516
537
  templateSnapshot: row["template_snapshot"] ?? void 0,
538
+ infoRequest: row["info_request"] ? {
539
+ ...row["info_request"],
540
+ askedAt: new Date(row["info_request"].askedAt)
541
+ } : void 0,
517
542
  createdAt: new Date(row["created_at"]),
518
543
  updatedAt: new Date(row["updated_at"])
519
544
  };
@@ -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 } 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 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 `);\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,\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)\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.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 `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 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 ],\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 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 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":";AAAO,IAAM,aAAA,GAAN,cAA4B,KAAA,CAAM;AAAA,EACvC,WAAA,CACE,SACgB,IAAA,EAChB;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AAFG,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAGhB,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AAAA,EACd;AAAA,EAEA,MAAA,GAA0D;AACxD,IAAA,OAAO,EAAE,MAAM,IAAA,CAAK,IAAA,EAAM,SAAS,IAAA,CAAK,OAAA,EAAS,IAAA,EAAM,IAAA,CAAK,IAAA,EAAK;AAAA,EACnE;AAAA,EAEA,YAAA,GAAuB;AACrB,IAAA,MAAM,GAAA,GAA8B;AAAA,MAClC,SAAA,EAAW,GAAA;AAAA,MACX,QAAA,EAAU,GAAA;AAAA,MACV,SAAA,EAAW,GAAA;AAAA,MACX,UAAA,EAAY,GAAA;AAAA,MACZ,kBAAA,EAAoB;AAAA,KACtB;AACA,IAAA,OAAO,GAAA,CAAI,IAAA,CAAK,IAAI,CAAA,IAAK,GAAA;AAAA,EAC3B;AACF,CAAA;AASO,IAAM,qBAAA,GAAN,cAAoC,aAAA,CAAc;AAAA,EACvD,YAAY,UAAA,EAAoB;AAC9B,IAAA,KAAA;AAAA,MACE,iDAAiD,UAAU,CAAA,2DAAA,CAAA;AAAA,MAC3D;AAAA,KACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,uBAAA;AAAA,EACd;AACF,CAAA;AASO,IAAM,uBAAA,GAAN,cAAsC,aAAA,CAAc;AAAA,EACzD,WAAA,CACE,SACgB,KAAA,EAChB;AACA,IAAA,KAAA,CAAM,SAAS,YAAY,CAAA;AAFX,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAGhB,IAAA,IAAA,CAAK,IAAA,GAAO,yBAAA;AAAA,EACd;AACF,CAAA;;;ACnBA,IAAM,eAAA,GAAkB,mBAAA;AAWxB,SAAS,oBAAoB,QAAA,EAA8C;AACzE,EAAA,OAAO,EAAE,GAAG,QAAA,EAAU,SAAA,EAAW,IAAI,IAAA,CAAK,QAAA,CAAS,SAAS,CAAA,EAAE;AAChE;AAUA,SAAS,kBAAA,CAAmB,MAAc,QAAA,EAA0B;AAClE,EAAA,OAAO,CAAA,SAAA,EAAY,QAAQ,CAAA,YAAA,EAAe,QAAA,GAAW,CAAC,CAAA,OAAA,CAAA;AACxD;AAGA,SAAS,cAAc,IAAA,EAAsB;AAC3C,EAAA,OAAO,IAAI,IAAA,CACR,KAAA,CAAM,GAAG,CAAA,CACT,GAAA,CAAI,CAAC,GAAA,KAAQ,CAAA,CAAA,EAAI,GAAA,CAAI,OAAA,CAAQ,YAAY,MAAM,CAAC,GAAG,CAAA,CACnD,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA,CAAA;AACd;AAEO,IAAM,kBAAN,MAAiD;AAAA,EAOtD,YAA6B,IAAA,EAA8B;AAA9B,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAN7B,IAAA,IAAA,CAAQ,KAAA,GAAkC,IAAA;AAOxC,IAAA,MAAM,MAAA,GAAS,KAAK,WAAA,IAAe,IAAA;AACnC,IAAA,IAAI,CAAC,eAAA,CAAgB,IAAA,CAAK,MAAM,CAAA,EAAG;AACjC,MAAA,MAAM,IAAI,uBAAA;AAAA,QACR,iCAAiC,MAAM,CAAA,0GAAA;AAAA,OACzC;AAAA,IACF;AACA,IAAA,MAAM,MAAA,GAAS,KAAK,MAAA,IAAU,QAAA;AAC9B,IAAA,IAAI,CAAC,eAAA,CAAgB,IAAA,CAAK,MAAM,CAAA,EAAG;AACjC,MAAA,MAAM,IAAI,uBAAA;AAAA,QACR,4BAA4B,MAAM,CAAA,0GAAA;AAAA,OACpC;AAAA,IACF;AACA,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,qBAAqB,IAAA,CAAK,kBAAA;AAC/B,IAAA,IAAA,CAAK,eAAe,IAAA,CAAK,IAAA;AAAA,EAC3B;AAAA,EAEA,MAAc,OAAA,GAAsC;AAClD,IAAA,IAAI,IAAA,CAAK,YAAA,EAAc,OAAO,IAAA,CAAK,YAAA;AACnC,IAAA,IAAI,IAAA,CAAK,KAAA,EAAO,OAAO,IAAA,CAAK,KAAA;AAC5B,IAAA,MAAM,EAAE,OAAA,EAAS,EAAA,EAAG,GAAI,MAAM,OAAO,IAAI,CAAA;AACzC,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAI,EAAA,CAAG,IAAA,CAAK;AAAA,MACvB,gBAAA,EAAkB,KAAK,IAAA,CAAK,gBAAA;AAAA,MAC5B,GAAA,EAAK,KAAK,IAAA,CAAK;AAAA,KAChB,CAAA;AACD,IAAA,OAAO,IAAA,CAAK,KAAA;AAAA,EACd;AAAA,EAEA,IAAY,CAAA,GAAI;AACd,IAAA,OAAO,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,CAAA,EAAI,KAAK,MAAM,CAAA,CAAA;AAAA,EACtC;AAAA,EAEA,MAAM,OAAA,GAAyB;AAC7B,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,KAAK,KAAA,CAAM;AAAA,iCAAA,EACc,KAAK,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA,iCAAA,EAQN,KAAK,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA,iCAAA,EA4BN,KAAK,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA,iCAAA,EAmBN,KAAK,CAAC,CAAA;AAAA,WAAA,EAC5B,KAAK,CAAC,CAAA;;AAAA,iCAAA,EAEgB,KAAK,CAAC,CAAA;AAAA,WAAA,EAC5B,KAAK,CAAC,CAAA;;AAAA,iCAAA,EAEgB,KAAK,CAAC,CAAA;AAAA,WAAA,EAC5B,KAAK,CAAC,CAAA;;AAAA;AAAA,4BAAA,EAGW,KAAK,CAAC,CAAA;AAAA,4BAAA,EACN,KAAK,CAAC,CAAA;AAAA,4BAAA,EACN,KAAK,CAAC,CAAA;AAAA,4BAAA,EACN,KAAK,CAAC,CAAA;AAAA,4BAAA,EACN,KAAK,CAAC,CAAA;AAAA,4BAAA,EACN,KAAK,CAAC,CAAA;AAAA,4BAAA,EACN,KAAK,CAAC,CAAA;AAAA,4BAAA,EACN,KAAK,CAAC,CAAA;AAAA,IAAA,CAC/B,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,SAAA,GAA2B;AAC/B,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,KAAK,KAAA,CAAM;AAAA,kBAAA,EACD,KAAK,CAAC,CAAA;AAAA,kBAAA,EACN,KAAK,CAAC,CAAA;;AAAA,4BAAA,EAEI,IAAA,CAAK,CAAC,CAAA,qBAAA,EAAwB,IAAA,CAAK,CAAC,CAAA;AAAA,oBAAA,EAC5C,IAAA,CAAK,CAAC,CAAA,qBAAA,EAAwB,IAAA,CAAK,CAAC,CAAA;AAAA;;AAAA,4BAAA,EAG5B,IAAA,CAAK,CAAC,CAAA,2BAAA,EAA8B,IAAA,CAAK,CAAC,CAAA;AAAA,oBAAA,EAClD,IAAA,CAAK,CAAC,CAAA,2BAAA,EAA8B,IAAA,CAAK,CAAC,CAAA;AAAA;AAAA,IAAA,CAE3D,CAAA;AAAA,EACH;AAAA;AAAA,EAIA,MAAM,aAAa,QAAA,EAA2C;AAC5D,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,IAAA,CAAK,KAAA;AAAA,MACT,CAAA,YAAA,EAAe,KAAK,CAAC,CAAA;AAAA;AAAA,uEAAA,CAAA;AAAA,MAGrB;AAAA,QACE,QAAA,CAAS,QAAA;AAAA,QACT,QAAA,CAAS,IAAA;AAAA,QACT,IAAA,CAAK,UAAU,QAAQ,CAAA;AAAA,QACvB,QAAA,CAAS,UAAU,WAAA;AAAY;AACjC,KACF;AAAA,EACF;AAAA,EAEA,MAAM,WAAA,CAAY,QAAA,EAAkB,IAAA,EAAgD;AAClF,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,KAAA;AAAA,MACxB,CAAA,iBAAA,EAAoB,KAAK,CAAC,CAAA,6CAAA,CAAA;AAAA,MAC1B,CAAC,UAAU,IAAI;AAAA,KACjB;AACA,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA;AACzB,IAAA,OAAO,GAAA,GAAM,mBAAA,CAAoB,GAAA,CAAI,IAAI,CAAA,GAAI,IAAA;AAAA,EAC/C;AAAA,EAEA,MAAM,cAAc,QAAA,EAA+C;AACjE,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,KAAA;AAAA,MACxB,CAAA,iBAAA,EAAoB,KAAK,CAAC,CAAA,uDAAA,CAAA;AAAA,MAC1B,CAAC,QAAQ;AAAA,KACX;AACA,IAAA,OAAO,MAAA,CAAO,KAAK,GAAA,CAAI,CAAC,MAAM,mBAAA,CAAoB,CAAA,CAAE,IAAI,CAAC,CAAA;AAAA,EAC3D;AAAA;AAAA,EAIA,MAAM,aAAa,QAAA,EAA2C;AAC5D,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,IAAA,CAAK,KAAA;AAAA,MACT,CAAA,YAAA,EAAe,KAAK,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6CAAA,CAAA;AAAA,MASrB;AAAA,QACE,QAAA,CAAS,EAAA;AAAA,QACT,QAAA,CAAS,QAAA;AAAA,QACT,QAAA,CAAS,UAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,QAAA,CAAS,UAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,QAAA,CAAS,WAAA;AAAA,QACT,QAAA,CAAS,MAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,QAAA,CAAS,OAAA;AAAA,QACT,SAAS,cAAA,IAAkB,IAAA;AAAA,QAC3B,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,IAAI,CAAA;AAAA,QAC5B,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,QAAQ,CAAA;AAAA,QAChC,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,MAAM,CAAA;AAAA,QAC9B,SAAS,gBAAA,IAAoB,IAAA;AAAA,QAC7B,QAAA,CAAS,SAAA,EAAW,WAAA,EAAY,IAAK,IAAA;AAAA,QACrC,SAAS,cAAA,IAAkB,IAAA;AAAA,QAC3B,QAAA,CAAS,aAAA,EAAe,WAAA,EAAY,IAAK,IAAA;AAAA,QACzC,QAAA,CAAS,aAAA,EAAe,WAAA,EAAY,IAAK,IAAA;AAAA,QACzC,SAAS,gBAAA,GAAmB,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,gBAAgB,CAAA,GAAI,IAAA;AAAA,QACxE,SAAS,WAAA,GAAc,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,WAAW,CAAA,GAAI,IAAA;AAAA,QAC9D,QAAA,CAAS,UAAU,WAAA,EAAY;AAAA,QAC/B,QAAA,CAAS,UAAU,WAAA;AAAY;AACjC,KACF;AAAA,EACF;AAAA,EAEA,MAAM,cAAA,CAAe,QAAA,EAA4B,eAAA,EAAwC;AACvF,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,KAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAKxB,CAAA,OAAA,EAAU,KAAK,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAA,CAAA;AAAA,MAgBhB;AAAA,QACE,QAAA,CAAS,QAAA;AAAA,QACT,QAAA,CAAS,EAAA;AAAA,QACT,eAAA;AAAA,QACA,QAAA,CAAS,MAAA;AAAA,QACT,QAAA,CAAS,YAAA;AAAA,QACT,eAAA;AAAA,QACA,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,MAAM,CAAA;AAAA,QAC9B,QAAA,CAAS,aAAA,EAAe,WAAA,EAAY,IAAK,IAAA;AAAA,QACzC,QAAA,CAAS,UAAU,WAAA,EAAY;AAAA,QAC/B,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,IAAA,IAAQ,EAAE,CAAA;AAAA,QAClC,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,QAAA,IAAY,EAAE,CAAA;AAAA,QACtC,QAAA,CAAS,SAAA,EAAW,WAAA,EAAY,IAAK,IAAA;AAAA,QACrC,SAAS,cAAA,IAAkB,IAAA;AAAA,QAC3B,QAAA,CAAS,aAAA,EAAe,WAAA,EAAY,IAAK,IAAA;AAAA,QACzC,SAAS,gBAAA,GAAmB,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,gBAAgB,CAAA,GAAI,IAAA;AAAA,QACxE,SAAS,WAAA,GAAc,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,WAAW,CAAA,GAAI;AAAA;AAChE,KACF;AACA,IAAA,IAAI,OAAO,QAAA,KAAa,CAAA,QAAS,IAAI,qBAAA,CAAsB,SAAS,EAAE,CAAA;AAAA,EACxE;AAAA,EAEA,MAAM,WAAA,CAAY,QAAA,EAAkB,EAAA,EAA8C;AAChF,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,KAAA;AAAA,MACxB,CAAA,cAAA,EAAiB,KAAK,CAAC,CAAA,2CAAA,CAAA;AAAA,MACvB,CAAC,UAAU,EAAE;AAAA,KACf;AACA,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA;AACzB,IAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AACjB,IAAA,OAAO,IAAA,CAAK,cAAc,GAA8B,CAAA;AAAA,EAC1D;AAAA,EAEA,MAAM,sBAAA,CACJ,QAAA,EACA,UAAA,EACA,IAAA,EAC4C;AAC5C,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,MAAA,GAAS,MAAM,MAAA,IAAU,CAAA;AAC/B,IAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,IAAS,EAAA;AAE7B,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,KAAA;AAAA,MACxB,CAAA;AAAA,YAAA,EACQ,KAAK,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAA,CAAA;AAAA,MAUd,CAAC,UAAU,IAAA,CAAK,SAAA,CAAU,CAAC,UAAU,CAAC,CAAA,EAAG,KAAA,EAAO,MAAM;AAAA,KACxD;AAEA,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,GACvB,MAAA,CAAQ,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,CAA8B,aAAa,CAAC,CAAA,GACjE,CAAA;AACJ,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAM,IAAA,CAAK,aAAA,CAAc,CAA4B,CAAC,CAAA;AAAA,MAC9E;AAAA,KACF;AAAA,EACF;AAAA,EAEA,MAAM,oBAAA,CACJ,QAAA,EACA,MAAA,EACA,IAAA,EAC4C;AAC5C,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,UAAA,GAAuB,CAAC,gBAAgB,CAAA;AAC9C,IAAA,MAAM,MAAA,GAAoB,CAAC,QAAQ,CAAA;AACnC,IAAA,IAAI,GAAA,GAAM,CAAA;AAEV,IAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,MAAA,UAAA,CAAW,IAAA,CAAK,CAAA,UAAA,EAAa,GAAA,EAAK,CAAA,CAAE,CAAA;AACpC,MAAA,MAAA,CAAO,IAAA,CAAK,OAAO,MAAM,CAAA;AAAA,IAC3B;AACA,IAAA,IAAI,OAAO,YAAA,EAAc;AACvB,MAAA,UAAA,CAAW,IAAA,CAAK,CAAA,iBAAA,EAAoB,GAAA,EAAK,CAAA,CAAE,CAAA;AAC3C,MAAA,MAAA,CAAO,IAAA,CAAK,OAAO,YAAY,CAAA;AAAA,IACjC;AACA,IAAA,IAAI,OAAO,WAAA,EAAa;AACtB,MAAA,UAAA,CAAW,IAAA,CAAK,CAAA,gBAAA,EAAmB,GAAA,EAAK,CAAA,CAAE,CAAA;AAC1C,MAAA,MAAA,CAAO,IAAA,CAAK,OAAO,WAAW,CAAA;AAAA,IAChC;AACA,IAAA,IAAI,OAAO,YAAA,EAAc;AACvB,MAAA,UAAA,CAAW,IAAA,CAAK,CAAA,iBAAA,EAAoB,GAAA,EAAK,CAAA,CAAE,CAAA;AAC3C,MAAA,MAAA,CAAO,IAAA,CAAK,OAAO,YAAY,CAAA;AAAA,IACjC;AACA,IAAA,IAAI,OAAO,QAAA,EAAU;AACnB,MAAA,UAAA,CAAW,IAAA,CAAK,CAAA,eAAA,EAAkB,GAAA,EAAK,CAAA,CAAE,CAAA;AACzC,MAAA,MAAA,CAAO,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,WAAA,EAAa,CAAA;AAAA,IAC3C;AACA,IAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,MAAA,UAAA,CAAW,IAAA,CAAK,CAAA,eAAA,EAAkB,GAAA,EAAK,CAAA,CAAE,CAAA;AACzC,MAAA,MAAA,CAAO,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,WAAA,EAAa,CAAA;AAAA,IACzC;AACA,IAAA,IAAI,OAAO,IAAA,EAAM;AACf,MAAA,KAAA,MAAW,CAAC,MAAM,QAAQ,CAAA,IAAK,OAAO,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAA,EAAG;AAC1D,QAAA,UAAA,CAAW,IAAA,CAAK,kBAAA,CAAmB,IAAA,EAAM,GAAG,CAAC,CAAA;AAC7C,QAAA,MAAA,CAAO,IAAA,CAAK,cAAc,IAAI,CAAA,EAAG,KAAK,SAAA,CAAU,QAAA,IAAY,IAAI,CAAC,CAAA;AACjE,QAAA,GAAA,IAAO,CAAA;AAAA,MACT;AAAA,IACF;AAEA,IAAA,MAAM,MAAA,GAAS,MAAM,MAAA,IAAU,CAAA;AAC/B,IAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,IAAS,EAAA;AAC7B,IAAA,MAAA,CAAO,IAAA,CAAK,OAAO,MAAM,CAAA;AAEzB,IAAA,MAAM,GAAA,GAAM,CAAA;AAAA,sBAAA,EACQ,KAAK,CAAC,CAAA;AAAA,uBAAA,EACL,UAAA,CAAW,IAAA,CAAK,OAAO,CAAC;AAAA;AAAA,wBAAA,EAEvB,GAAA,EAAK,YAAY,GAAG,CAAA,CAAA;AAE1C,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,KAAA,CAAM,KAAK,MAAM,CAAA;AAC3C,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,GACvB,MAAA,CAAQ,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,CAA8B,aAAa,CAAC,CAAA,GACjE,CAAA;AACJ,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAM,IAAA,CAAK,aAAA,CAAc,CAA4B,CAAC,CAAA;AAAA,MAC9E;AAAA,KACF;AAAA,EACF;AAAA,EAEA,MAAM,mBAAA,CACJ,QAAA,EACA,IAAA,EACA,MAAA,GAAyB,EAAC,EACG;AAC7B,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,MAAA,GAAoB,CAAC,QAAA,EAAU,IAAA,CAAK,aAAa,CAAA;AACvD,IAAA,IAAI,KAAA,GAAQ,CAAA,cAAA,EAAiB,IAAA,CAAK,CAAC,CAAA;AAAA;AAAA,+BAAA,CAAA;AAInC,IAAA,IAAI,OAAO,YAAA,EAAc;AACvB,MAAA,MAAA,CAAO,IAAA,CAAK,OAAO,YAAY,CAAA;AAC/B,MAAA,KAAA,IAAS,CAAA,sBAAA,EAAyB,OAAO,MAAM,CAAA,CAAA;AAAA,IACjD;AACA,IAAA,IAAI,OAAO,WAAA,EAAa;AACtB,MAAA,MAAA,CAAO,IAAA,CAAK,OAAO,WAAW,CAAA;AAC9B,MAAA,KAAA,IAAS,CAAA,qBAAA,EAAwB,OAAO,MAAM,CAAA,CAAA;AAAA,IAChD;AAEA,IAAA,KAAA,IAAS,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAA,CAAA;AAsBT,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,KAAA,CAAM,OAAO,MAAM,CAAA;AAC7C,IAAA,OAAO,MAAA,CAAO,KAAK,GAAA,CAAI,CAAC,MAAM,IAAA,CAAK,aAAA,CAAc,CAA4B,CAAC,CAAA;AAAA,EAChF;AAAA,EAEA,MAAM,qBAAA,CACJ,QAAA,EACA,cAAA,EACkC;AAClC,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,KAAA;AAAA,MACxB,CAAA,cAAA,EAAiB,KAAK,CAAC,CAAA,wDAAA,CAAA;AAAA,MACvB,CAAC,UAAU,cAAc;AAAA,KAC3B;AACA,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA;AACzB,IAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AACjB,IAAA,OAAO,IAAA,CAAK,cAAc,GAA8B,CAAA;AAAA,EAC1D;AAAA;AAAA,EAIA,MAAM,gBAAA,CAAiB,QAAA,EAAkB,UAAA,EAAoB,KAAA,EAAkC;AAC7F,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,IAAA,CAAK,KAAA;AAAA,MACT,CAAA,YAAA,EAAe,KAAK,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA,kEAAA,CAAA;AAAA,MAKrB;AAAA,QACE,QAAA;AAAA,QACA,UAAA;AAAA,QACA,KAAA,CAAM,MAAA;AAAA,QACN,KAAA,CAAM,OAAA;AAAA,QACN,MAAM,SAAA,IAAa,IAAA;AAAA,QACnB,MAAM,OAAA,IAAW,IAAA;AAAA,QACjB,MAAM,cAAA,IAAkB,IAAA;AAAA,QACxB,MAAM,OAAA,IAAW,IAAA;AAAA,QACjB,KAAA,CAAM,KAAA;AAAA,QACN,KAAA,CAAM,UAAU,WAAA,EAAY;AAAA,QAC5B,MAAM,QAAA,GAAW,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,QAAQ,CAAA,GAAI,IAAA;AAAA,QAClD,MAAM,QAAA,GAAW,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,QAAQ,CAAA,GAAI,IAAA;AAAA,QAClD,MAAM,OAAA,IAAW,IAAA;AAAA,QACjB,MAAM,MAAA,IAAU,IAAA;AAAA,QAChB,MAAM,UAAA,IAAc;AAAA;AACtB,KACF;AAAA,EACF;AAAA,EAEA,MAAM,oBAAA,CACJ,QAAA,EACA,MAAA,EACA,IAAA,EACkD;AAClD,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,EAAQ;AAChC,IAAA,MAAM,UAAA,GAAuB,CAAC,gBAAgB,CAAA;AAC9C,IAAA,MAAM,MAAA,GAAoB,CAAC,QAAQ,CAAA;AACnC,IAAA,IAAI,GAAA,GAAM,CAAA;AAEV,IAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,MAAA,UAAA,CAAW,IAAA,CAAK,CAAA,UAAA,EAAa,GAAA,EAAK,CAAA,CAAE,CAAA;AACpC,MAAA,MAAA,CAAO,IAAA,CAAK,OAAO,MAAM,CAAA;AAAA,IAC3B;AACA,IAAA,IAAI,OAAO,YAAA,EAAc;AACvB,MAAA,UAAA,CAAW,IAAA,CAAK,CAAA,iBAAA,EAAoB,GAAA,EAAK,CAAA,CAAE,CAAA;AAC3C,MAAA,MAAA,CAAO,IAAA,CAAK,OAAO,YAAY,CAAA;AAAA,IACjC;AACA,IAAA,IAAI,OAAO,WAAA,EAAa;AACtB,MAAA,UAAA,CAAW,IAAA,CAAK,CAAA,gBAAA,EAAmB,GAAA,EAAK,CAAA,CAAE,CAAA;AAC1C,MAAA,MAAA,CAAO,IAAA,CAAK,OAAO,WAAW,CAAA;AAAA,IAChC;AACA,IAAA,IAAI,OAAO,YAAA,EAAc;AACvB,MAAA,UAAA,CAAW,IAAA,CAAK,CAAA,iBAAA,EAAoB,GAAA,EAAK,CAAA,CAAE,CAAA;AAC3C,MAAA,MAAA,CAAO,IAAA,CAAK,OAAO,YAAY,CAAA;AAAA,IACjC;AACA,IAAA,IAAI,OAAO,IAAA,EAAM;AACf,MAAA,KAAA,MAAW,CAAC,MAAM,QAAQ,CAAA,IAAK,OAAO,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAA,EAAG;AAC1D,QAAA,UAAA,CAAW,IAAA,CAAK,kBAAA,CAAmB,IAAA,EAAM,GAAG,CAAC,CAAA;AAC7C,QAAA,MAAA,CAAO,IAAA,CAAK,cAAc,IAAI,CAAA,EAAG,KAAK,SAAA,CAAU,QAAA,IAAY,IAAI,CAAC,CAAA;AACjE,QAAA,GAAA,IAAO,CAAA;AAAA,MACT;AAAA,IACF;AAEA,IAAA,MAAM,EAAE,MAAA,EAAQ,KAAA,EAAO,SAAA,GAAY,WAAU,GAAI,IAAA;AAEjD,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,MAAM,UAAU,MAAA,CAAO,IAAA,CAAK,QAAQ,QAAQ,CAAA,CAAE,SAAS,MAAM,CAAA;AAK7D,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,WAAA,CAAY,GAAG,CAAA;AACxC,MAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,QAAQ,CAAA;AACpC,MAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,KAAA,CAAM,QAAA,GAAW,CAAC,CAAA;AACrC,MAAA,IAAI,cAAc,SAAA,EAAW;AAC3B,QAAA,UAAA,CAAW,IAAA,CAAK,CAAA,qBAAA,EAAwB,GAAA,EAAK,CAAA,gBAAA,EAAmB,KAAK,CAAA,CAAA,CAAG,CAAA;AACxE,QAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA,MACpB,CAAA,MAAO;AACL,QAAA,UAAA,CAAW,IAAA,CAAK,CAAA,qBAAA,EAAwB,GAAA,EAAK,CAAA,gBAAA,EAAmB,KAAK,CAAA,CAAA,CAAG,CAAA;AACxE,QAAA,MAAA,CAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA,MACpB;AAAA,IACF;AAEA,IAAA,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAC,CAAA;AACrB,IAAA,MAAM,KAAA,GAAQ,SAAA,KAAc,UAAA,GAAa,MAAA,GAAS,KAAA;AAClD,IAAA,MAAM,GAAA,GAAM,CAAA,cAAA,EAAiB,IAAA,CAAK,CAAC,CAAA;AAAA,uBAAA,EACd,UAAA,CAAW,IAAA,CAAK,OAAO,CAAC;AAAA,qCAAA,EACV,KAAK,QAAQ,KAAK;AAAA,wBAAA,EAC/B,GAAG,CAAA,CAAA;AAEzB,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,KAAA,CAAM,KAAK,MAAM,CAAA;AAC3C,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,IAAA,CAAK,MAAA,GAAS,KAAA;AACrC,IAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,GAAG,KAAK,CAAA;AACvC,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,CAAI,CAAC,MAAM,IAAA,CAAK,aAAA,CAAc,CAA4B,CAAC,CAAA;AAE9E,IAAA,MAAM,UAAA,GAAa,UACf,oBAAA,CAAqB,IAAA,CAAK,KAAK,MAAA,GAAS,CAAC,CAA4B,CAAA,GACrE,MAAA;AAEJ,IAAA,OAAO,EAAE,KAAA,EAAO,UAAA,EAAY,OAAA,EAAQ;AAAA,EACtC;AAAA,EAEA,MAAM,GAAA,GAAqB;AACzB,IAAA,MAAM,IAAA,CAAK,OAAO,GAAA,EAAI;AACtB,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAA;AAAA,EACf;AAAA,EAEQ,cAAc,GAAA,EAAgD;AACpE,IAAA,OAAO;AAAA,MACL,EAAA,EAAI,IAAI,IAAI,CAAA;AAAA,MACZ,QAAA,EAAU,IAAI,WAAW,CAAA;AAAA,MACzB,UAAA,EAAa,GAAA,CAAI,aAAa,CAAA,IAAuB,EAAA;AAAA,MACrD,YAAA,EAAc,IAAI,eAAe,CAAA;AAAA,MACjC,UAAA,EAAY,IAAI,aAAa,CAAA;AAAA,MAC7B,YAAA,EAAc,IAAI,eAAe,CAAA;AAAA,MACjC,WAAA,EAAa,IAAI,cAAc,CAAA;AAAA,MAC/B,MAAA,EAAQ,IAAI,QAAQ,CAAA;AAAA,MACpB,YAAA,EAAc,IAAI,eAAe,CAAA;AAAA,MACjC,OAAA,EAAS,IAAI,SAAS,CAAA;AAAA,MACtB,cAAA,EAAiB,GAAA,CAAI,iBAAiB,CAAA,IAAuB,MAAA;AAAA,MAC7D,IAAA,EAAM,IAAI,MAAM,CAAA;AAAA,MAChB,QAAA,EAAU,IAAI,UAAU,CAAA;AAAA,MACxB,MAAA,EAAQ,IAAI,QAAQ,CAAA;AAAA,MACpB,UAAU,EAAC;AAAA,MACX,gBAAA,EAAmB,GAAA,CAAI,oBAAoB,CAAA,IAAuB,MAAA;AAAA,MAClE,SAAA,EAAW,IAAI,YAAY,CAAA,GAAI,IAAI,IAAA,CAAK,GAAA,CAAI,YAAY,CAAW,CAAA,GAAI,MAAA;AAAA,MACvE,cAAA,EAAiB,GAAA,CAAI,iBAAiB,CAAA,IAAoC,MAAA;AAAA,MAC1E,aAAA,EAAe,IAAI,iBAAiB,CAAA,GAChC,IAAI,IAAA,CAAK,GAAA,CAAI,iBAAiB,CAAW,CAAA,GACzC,MAAA;AAAA,MACJ,aAAA,EAAe,IAAI,iBAAiB,CAAA,GAChC,IAAI,IAAA,CAAK,GAAA,CAAI,iBAAiB,CAAW,CAAA,GACzC,MAAA;AAAA,MACJ,gBAAA,EACG,GAAA,CAAI,mBAAmB,CAAA,IAAqD,MAAA;AAAA,MAC/E,WAAA,EAAa,GAAA,CAAI,cAAc,CAAA,GAC3B;AAAA,QACE,GAAI,IAAI,cAAc,CAAA;AAAA,QACtB,SAAS,IAAI,IAAA,CAAM,GAAA,CAAI,cAAc,EAA0B,OAAO;AAAA,OACxE,GACA,MAAA;AAAA,MACJ,SAAA,EAAW,IAAI,IAAA,CAAK,GAAA,CAAI,YAAY,CAAW,CAAA;AAAA,MAC/C,SAAA,EAAW,IAAI,IAAA,CAAK,GAAA,CAAI,YAAY,CAAW;AAAA,KACjD;AAAA,EACF;AACF;AAWA,SAAS,qBAAqB,GAAA,EAAsC;AAClE,EAAA,MAAM,eAAe,IAAI,IAAA,CAAK,IAAI,YAAY,CAAkB,EAAE,WAAA,EAAY;AAC9E,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,CAAA,EAAG,YAAY,CAAA,CAAA,EAAI,GAAA,CAAI,IAAI,CAAC,CAAA,CAAE,CAAA,CAAE,QAAA,CAAS,QAAQ,CAAA;AACtE","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 } 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 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 `);\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,\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)\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 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 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 ],\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 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 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hierarchical-approval",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "TypeScript-first hierarchical approval engine for ERP developers",
5
5
  "keywords": [
6
6
  "approval",